diff options
Diffstat (limited to 'g10')
-rw-r--r-- | g10/ChangeLog | 3663 | ||||
-rw-r--r-- | g10/Makefile.am | 125 | ||||
-rw-r--r-- | g10/armor.c | 378 | ||||
-rw-r--r-- | g10/build-packet.c | 437 | ||||
-rw-r--r-- | g10/cipher.c | 109 | ||||
-rw-r--r-- | g10/comment.c | 40 | ||||
-rw-r--r-- | g10/compress.c | 75 | ||||
-rw-r--r-- | g10/dearmor.c | 8 | ||||
-rw-r--r-- | g10/decrypt.c | 62 | ||||
-rw-r--r-- | g10/delkey.c | 104 | ||||
-rw-r--r-- | g10/encode.c | 519 | ||||
-rw-r--r-- | g10/encr-data.c | 96 | ||||
-rw-r--r-- | g10/exec.c | 579 | ||||
-rw-r--r-- | g10/exec.h | 22 | ||||
-rw-r--r-- | g10/export.c | 168 | ||||
-rw-r--r-- | g10/filter.h | 47 | ||||
-rw-r--r-- | g10/free-packet.c | 225 | ||||
-rw-r--r-- | g10/g10.c (renamed from g10/gpg.c) | 1491 | ||||
-rw-r--r-- | g10/getkey.c | 1867 | ||||
-rw-r--r-- | g10/global.h (renamed from g10/basicdefs.h) | 27 | ||||
-rw-r--r-- | g10/gpgd.c | 24 | ||||
-rw-r--r-- | g10/gpgv.c | 372 | ||||
-rw-r--r-- | g10/helptext.c | 49 | ||||
-rw-r--r-- | g10/hkp.c | 503 | ||||
-rw-r--r-- | g10/hkp.h | 15 | ||||
-rw-r--r-- | g10/import.c | 801 | ||||
-rw-r--r-- | g10/kbnode.c | 56 | ||||
-rw-r--r-- | g10/kbx.h | 51 | ||||
-rw-r--r-- | g10/kbxblob.c | 895 | ||||
-rw-r--r-- | g10/kbxfile.c | 332 | ||||
-rw-r--r-- | g10/kbxio.c | 75 | ||||
-rw-r--r-- | g10/kbxutil.c | 442 | ||||
-rw-r--r-- | g10/keydb.c | 698 | ||||
-rw-r--r-- | g10/keydb.h | 156 | ||||
-rw-r--r-- | g10/keyedit.c | 1722 | ||||
-rw-r--r-- | g10/keygen.c | 1301 | ||||
-rw-r--r-- | g10/keyid.c | 436 | ||||
-rw-r--r-- | g10/keylist.c | 1046 | ||||
-rw-r--r-- | g10/keyring.c | 1550 | ||||
-rw-r--r-- | g10/keyring.h | 45 | ||||
-rw-r--r-- | g10/keyserver-internal.h | 22 | ||||
-rw-r--r-- | g10/keyserver.c | 1033 | ||||
-rw-r--r-- | g10/ks-proto.c | 2 | ||||
-rw-r--r-- | g10/ks-proto.h | 8 | ||||
-rw-r--r-- | g10/main.h | 110 | ||||
-rw-r--r-- | g10/mainproc.c | 833 | ||||
-rw-r--r-- | g10/mdfilter.c | 12 | ||||
-rw-r--r-- | g10/misc.c | 704 | ||||
-rw-r--r-- | g10/mkdtemp.c | 98 | ||||
-rw-r--r-- | g10/openfile.c | 102 | ||||
-rw-r--r-- | g10/options.h | 103 | ||||
-rw-r--r-- | g10/options.skel | 180 | ||||
-rw-r--r-- | g10/packet.h | 239 | ||||
-rw-r--r-- | g10/parse-packet.c | 748 | ||||
-rw-r--r-- | g10/passphrase.c | 1002 | ||||
-rw-r--r-- | g10/photoid.c | 320 | ||||
-rw-r--r-- | g10/photoid.h | 14 | ||||
-rw-r--r-- | g10/pipemode.c | 317 | ||||
-rw-r--r-- | g10/pkclist.c | 1100 | ||||
-rw-r--r-- | g10/plaintext.c | 135 | ||||
-rw-r--r-- | g10/pubkey-enc.c | 231 | ||||
-rw-r--r-- | g10/revoke.c | 374 | ||||
-rw-r--r-- | g10/ringedit.c | 1360 | ||||
-rw-r--r-- | g10/seckey-cert.c | 371 | ||||
-rw-r--r-- | g10/seskey.c | 104 | ||||
-rw-r--r-- | g10/sig-check.c | 370 | ||||
-rw-r--r-- | g10/sign.c | 1276 | ||||
-rw-r--r-- | g10/signal.c | 126 | ||||
-rw-r--r-- | g10/skclist.c | 133 | ||||
-rw-r--r-- | g10/status.c | 348 | ||||
-rw-r--r-- | g10/status.h | 31 | ||||
-rw-r--r-- | g10/tdbdump.c | 411 | ||||
-rw-r--r-- | g10/tdbio.c | 929 | ||||
-rw-r--r-- | g10/tdbio.h | 141 | ||||
-rw-r--r-- | g10/textfilter.c | 18 | ||||
-rw-r--r-- | g10/trustdb.c | 3848 | ||||
-rw-r--r-- | g10/trustdb.h | 58 | ||||
-rw-r--r-- | g10/verify.c | 43 |
78 files changed, 24084 insertions, 13781 deletions
diff --git a/g10/ChangeLog b/g10/ChangeLog index 9c5acd8a1..21c44df4e 100644 --- a/g10/ChangeLog +++ b/g10/ChangeLog @@ -1,429 +1,3446 @@ -2000-11-13 Werner Koch <wk@gnupg.org> +2002-06-21 Stefan Bellon <sbellon@sbellon.de> - * Makefile.am: Do not install any program + * g10.c [__riscos__]: Moved RISC OS specific stuff to util/riscos.c + and include/util.h. -2000-10-12 Werner Koch <wk@gnupg.org> + * gpgv.c [__riscos__]: Likewise. - * keygen.c (keygen_add_std_prefs): Add Rijndael to the prefs. +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. "&") 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> -2000-10-11 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. - * packet.h: Add features sig subpacket definition. - * parse-packet.c (dump_sig_subpkt,parse_one_sig_subpkt, - can_handle_critical): Implemented it here. - * build-packet.c (build_sig_subpkt): and here. - * keygen.c (keygen_add_std_prefs): Generate a features packet. +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. -2000-10-09 Werner Koch <wk@gnupg.org> +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. - * keygen.c (do_generate_keypair): Removed the keyblock locking. +2002-04-02 Werner Koch <wk@gnupg.org> - * ringedit.c (enum_keyblocks): Replaced by ... - (enum_keyblocks_begin): New. - (enum_keyblocks_next): New. - (enum_keyblocks_end): New. And changed all callers. + * 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. - * import.c (import_one): Removed keyblock locking becuase this is - now done inside of insert_keyblock(). Removed get_keyblock_handle - because insert_keyblock() now decides what is the default keyring. + * 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. - (import_one): Ditto. - -Fri Oct 6 14:29:16 CEST 2000 Werner Koch <wk@openit.de> - - Started to rework the whole getkey/ringedit stuff to make - it simpler, correcter and faster. - - * parse-packet.c (parse_packet): Add a 3rd arg to return the filepos. - Changed all callers. - * getkey.c (classify_user_id): Add new mode 21. - (find_by_fpr): Find using this new mode. - (get_seckey_byname): New arg to return the context. Changed all - callers. - * keyid.c (unified_fingerprint_from_pk): New. - (unified_fingerprint_from_sk): New. - * ringedit.c (find_keyblock_bypk): Changed to use the unified - fingerprint for lookup. I can't see a reason why we did compare - the entire public key. - (find_keyblock_bysk): Ditto. - (search,cmp_pubkey,cmp_seckey): Removed. - (keyring_search, do_kbxf_search): Removed. - (locate_keyblock_by_fpr,locate_keyblock_by_keyid): Removed. - (find_keyblock_byname): Removed use o search function. - (find_secret_keyblock_byname): Ditto. - (merge_public_with_secret): Fixed removing subkeys. - (premerge_public_with_secret): New. - - * ringedit.c: Removed all GDBM support - - * ringedit.c (read_keyblock): Removed. - * ringedit.c (find_keyblock_byname,find_secret_keyblock_byname, - find_keyblock_bypk,find_keyblock_bysk): Moved from here to .... - * getkey.c: ... here. Changed first arg to return a keyblock and - changed all callers to merge the old read_keyblock() with these - functions. - -Wed Oct 4 13:16:18 CEST 2000 Werner Koch <wk@openit.de> - - * getkey.c (merge_selfsigs_main): Fixed for v3 keys. - - * sign.c (hash_for): New arg to take packet version in account. Changed - all callers. - (write_one_sig): New. Moved the shared code from sign_file and - clearsign_file to here. - * skclist.c (build_sk_list): Fixed usage check. - * pkclist.c (build_pk_list): Ditto. - - * encode.c (encode_crypt): Removed duplicated stuff by using - encrypt_filter as sign.c already did. Removed already disabled - comment-packet code. - -Mon Sep 18 16:35:45 CEST 2000 Werner Koch <wk@openit.de> - - * parse-packet.c (dump_sig_subpkt): Dump key flags. - (parse_one_sig_subpkt,can_handle_critical): Add KeyFlags support. - * build-packet.c (build_sig_subpkt): 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. - * g10.c: New option --allow-freeform-uid. By Jeroen C. van Gelderen. - * keygen.c (ask_user_id): Implemented here. + * keydb.h: Moved KBNODE typedef and MAX_FINGERPRINT_LEN to + * global.h: this new header. + +2001-09-03 Werner Koch <wk@gnupg.org> - * parse-packet.c (dump_sig_subpkt): Print info about the ARR. + * passphrase.c (agent_get_passphrase): Changed nread to size_t. + (passphrase_clear_cache): Ditto. - * 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. + * keyid.c (mk_datestr): Avoid trigraphs. + (fingerprint_from_pk): Cache the keyid in the pk. - * getkey.c: Large parts rewritten to have a better sub key selection - and handle some meta information from signatures more correctly. - (get_primary_seckey): Removed. - * seckey_cert.c (do_check): Set main keyid from the data in the sk. - * free-packet.c (copy_public_parts_to_secret_key): New. - * sig-check.c (check_key_signature2): Enabled shortcut for already - checked signatures. - * keydb.h: New macros IS_xxx_SIG, IS_xxx_REV. - * misc.c (openpgp_pk_algo_usage): New. - * packet.h: New field req_uage and do not use pubkey_usage anymore - to request a specific usage. Changed at all places. - * keyid.c (keyid_from_sk): Cache the keyid in the sk + * 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. - * passphrase.c (hash_passphrase): Removed funny assert. Reported by - David Mathog. + * status.c, status.h: New status INV_RECP. + * pkclist.c (build_pk_list): Issue this status. - * keyedit.c (keyedit_menu): Allow "debug" on secret keys. +2001-08-31 Werner Koch <wk@gnupg.org> - * keygen.c (keygen_add_std_prefs): Changed order of preferences to - twofish, cast5, blowfish. + * 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. - * gpg.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. + * keygen.c (ask_keysize): Do not print the notes about suggested + key sizes if just a DSA key is generated. - * openfile.c (try_make_homedir): Changes for non-Posix systems. - * gpg.c (main): Take the default homedir from macro. + * trustdb.c (add_ultimate_key): s/log_error/log_info/ for + duplicated inserted trusted keys. - * encode.c (encode_simple, encode_crypt): Fix for large files. - * sign.c (sign_file): Ditto. +2001-08-07 Werner Koch <wk@gnupg.org> - * gpg.c (main): Don't set --quite along with --no-tty. By Frank Tobin. + * sign.c (sleep): Redefine for W32. - * misc.c (disable_core_dump): Don't display a warning here but a return - a status value and ... - * gpg.c (main): ...print warning here. Suggested by Sam Roberts. + * 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. - * 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. +2001-08-03 Werner Koch <wk@gnupg.org> - * ringedit.c (cmp_seckey): Fix for v4 RSA keys. - * seckey-cert.c (do_check): Workaround for PGP 7 bug. + * armor.c (armor_filter): Removed the default comment string + because it could get us in trouble due to translations using non + ascii characters. - * pkclist.c (algo_available): Removed hack to disable Twofish. +2001-08-01 Werner Koch <wk@gnupg.org> - * gpg.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. + * 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. - * gpg.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. +2001-01-03 Werner Koch <wk@gnupg.org> - * gpg.c: New option --try-all-secrets on suggestion from - Matthias Urlichs. - * pubkey-enc.c (get_session_key): Quite easy to implement here. + * status.c, status.h : New status KEY_CREATED + * keygen.c (do_generate_keypair,generate_subkeypair): Emit it. -Mon Aug 21 17:59:17 CEST 2000 Werner Koch <wk@openit.de> +2000-12-28 Werner Koch <wk@gnupg.org> - * gpg.c: New option --use-agent + * 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 wrong cached passphrases. + * seckey-cert.c (do_check): Clear cached passphrases. -Fri Aug 18 14:27:14 CEST 2000 Werner Koch <wk@openit.de> +2000-11-15 Werner Koch <wk@gnupg.org> - * status.c (do_get_from_fd): Ooops, we used fd instead of opt.command_fd. - Thanks to Michael Tokarev. + * status.c (write_status_text): Moved the big switch to ... + (get_status_string): ... new function. + (write_status_buffer): New. -Mon Jul 31 10:04:47 CEST 2000 Werner Koch <wk@openit.de> + * status.c (mywrite): New and replaced all write() by this. - * encode.c, sign.c, keygen.c, pubkey-enc.c: Replaced all - gcry_sexp_{car,cdr}_{data,mpi} by the new gcry_sexp_nth_{data,mpi} functions. + * 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> - * keygen.c (gen_dsa,gen_elg): Changed the way the factors are stored. - (factors_from_sexp): Removed. - * comment.c (make_mpi_comment_node): Removed. - (make_comment_node_from_buffer): New. + * sign.c (clearsign_file): Use LF macro to print linefeed. + +2000-11-11 Paul Eggert <eggert@twinsun.com> -Fri Jul 28 18:19:11 CEST 2000 Werner Koch <wk@openit.de> + 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". - * sig-check.c (pk_verify): Fixed the S-Exp withe the pkey. + * 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. - * gpg.c (main): Use setmode(O_BINARY) for MSDOS while generating random bytes - (print_mds): Likewise for stdin. - * plaintext.c (handle_plaintext): Likewise for stdout. + * parse-packet.c (parse): Do not use %lu to report file + offsets in error diagnostics; it's not portable. + * ringedit.c (keyring_search): Likewise. -Tue Jul 25 17:44:15 CEST 2000 Werner Koch <wk@openit.de> +2000-11-09 Werner Koch <wk@gnupg.org> - * keyedit.c (menu_expire): expire date for primary key can be set again. + * g10.c (main): New option --enable-special-filenames. - * 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. +2000-11-07 Werner Koch <wk@gnupg.org> + + * g10.c (main): New command --pipemode. + * pipemode.c: New. - * sign.c (pk_sign): Changed to use the new S-Exp interface. - * encode.c (pk_encrypt): Ditto. - * sig-check.c (pk_verify): Ditto. - * seckey-cert.c (pk_check_secret_key): Ditto. - * pubkey-enc.c (pk_decrypt): Ditto. - * misc.c (pubkey_nbits): Ditto. - * keygen.c (key_from_sexp,factors_from_sexp,gen_elg,gen_dsa): Ditto. +2000-10-23 Werner Koch <wk@gnupg.org> -Fri Jul 14 19:38:23 CEST 2000 Werner Koch <wk@> + * armor.c (armor_filter): Changed output of hdrlines, so that a CR + is emitted for DOS systems. - Replaced everything with the code from the STABLE-BRANCH-1-0 and - started to backport the changes from the 1.1 development branch - which are dated according to the ChangeLog of the 1.1 from - Sat Sep 18 12:16:08 CEST 1999 to Thu May 25 18:39:11 CEST 2000. - Here are those changes, some of them are duplicates because they - have been done on both branch simultaneously. + * keygen.c (read_parameter_file): Add a cast for isspace(). - * gpg.c (print_mds): Add arg keys as a kludge to print hmacs - (main): New option --print-hmac. + * status.c (myread): Use SIGINT instead of SIGHUP for DOS. - * trustdb.c (verify_own_keys): Do not print warning about unprotected - key when in quiet mode. +2000-10-19 Werner Koch <wk@gnupg.org> - * build-paket.c (do_user_id): Save offset where name has been stored. + * g10.c: New option --ignore-crc-error + * armor.c (invalid_crc): New. + (radix64_read): Act on new option. - * ringedit.c : Add new access method KBXF + * openfile.c (try_make_homedir): Klaus Singvogel fixed a stupid + error introduced on Sep 6th. - * kbxfile.c: New. +2000-10-18 Werner Koch <wk@gnupg.org> - * kbx.h: New. - * kbxblob.c: Started to work on the keybox stuff. + * misc.c (print_cipher_algo_note): Don't print the note for AES. + Changed wording. - * keygen.c (gen_dsa): Modified to work with gcry_pk_genkey. +2000-10-16 Werner Koch <wk@gnupg.org> - * Removed dummy-cipher.h from all files. + * 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. - * keygen.c (gen_elg): Modified to work with gcry_pk_genkey. - (key_from_sexp): New. - (factors_from_sexp): New. + * mainproc.c (check_sig_and_print): Detect multiple signatures + and don't verify them. - * g10.c : Renamed to ... - * gpg.c : ... this - * Makefile.am: And fixed it here. +2000-10-14 Werner Koch <wk@gnupg.org> - * Changed all "g10_"/"GPG_" prefixes to "gpg_"/"GPG_". + * 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. - * misc.c (mpi_read_opaque): Fixed double counting. +2000-10-13 Werner Koch <wk@gnupg.org> - * seckey-cert.c (do_check): Removed buffer and the unmotivated free - on it. + * 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. - * pubkey-enc.c (pk_decrypt): New wrapper for the gcry_ function. - * seckey-cert.c (pk_check_secret_key): Likewise. - * encode.c (pk_encrypt): Likewise. + * 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. - * parse-packet.c (parse_key): Fixed case of unencrypted secret keys. + * keyedit.c (keyedit_menu): Allow batchmode with a command_fd. + * status.c (my_read): New. + (do_get_from_fd): use it. - * misc.c (mpi_print): Use gcry_mpi_aprint. - (pubkey_nbits): Kludge to use the gcry_pk_ API. +2000-10-12 Werner Koch <wk@gnupg.org> - * seskey.c (encode_session_key): Replaced mpi_set_buffer by *_scan. - (do_encode_md): Ditto. - (encode_md_value): Ditto. - * seckey-cert.c (protect_secret_key): Ditto. - * comment.c (make_mpi_comment_node): Replaced mpi_get_buffer by _print. - * pubkey-enc.c (get_it): Ditto. - * sig-check.c (do_signature_check): Ditto. + * keygen.c (keygen_add_std_prefs): Add Rijndael to the prefs. - * keyid.c (do_fingerprint_md): Replaced mpi_get_buffer by gcry_mpi_print. - (v3_keyid): New. - (keyid_from_sk): And use it here. - (keyid_from_pk): Ditto. - (fingerprint_from_sk): Ditto. - (fingerprint_from_pk): Ditto. +2000-10-07 Werner Koch <wk@gnupg.org> - * misc.c (mpi_print): New. + * gpgv.c: Add more stubs for ununsed code to make the binary smaller. - * misc.c (checksum_mpi): Now uses gcry_mpi_print to get the data. +Wed Oct 4 15:50:18 CEST 2000 Werner Koch <wk@openit.de> - * seckey-cert.c (do_check): Replaced mpi_read_from_buffer. + * sign.c (hash_for): New arg to take packet version in account, changed + call callers. - * armor.c (armor_filter): Made the "Comment:" header translatable. + * gpgv.c: New. + * Makefile.am: Rearranged source files so that gpgv can be build with + at least files as possible. - * seckey-cert.c: Removed obsolete mpi_*_protect_flag. - * parse-packet.c: Ditto. +Mon Sep 18 12:13:52 CEST 2000 Werner Koch <wk@openit.de> - * misc.c (mpi_read): Removed the secure argumet becuase it is - never used. Changed all Callers. - (mpi_read_opaque): New. - (mpi_write_opaque): New. - * parse-packet.c (parse_key): Use the opaque method also for - v3 keys. - * build-packet.c (do_secret_key): Likewise. + * hkp.c (not_implemented): Print a notice for W32 - * g10.c (main): Check libgcrypt version. +Fri Sep 15 18:40:36 CEST 2000 Werner Koch <wk@openit.de> - * packet.h: replaced inclusion of mpi.h by a plain typeedef of the - gcry_mpi structure and removed all inclusions of "mpi.h" in all - sources. + * keygen.c (keygen_add_std_prefs): Changed order of preferences to + twofish, cast5, blowfish. - * g10.c: Add --delete-secret-key to the help page. + * pkclist.c (algo_available): Removed hack to disable Twofish. - * g10.c (main): Changed the default homedir to "~/.gnupg-test" so - that we don't mess up with the stable version. +Thu Sep 14 17:45:11 CEST 2000 Werner Koch <wk@openit.de> - * misc.c (mpi_write): New. - (mpi_write): New. + * parse-packet.c (dump_sig_subpkt): Dump key flags. Print special + warning in case of faked ARRs. - * misc.c (checksum_u16_nobug): Removed. - (checksum_mpi_counted_nbits): Renamed to ... - (checksum_mpi): ... this to superseed the old one. Changed all - callers. This is because we do not emulate the old gpg bug anymore. - * g10.c (oEmuChecksumBug): Removed. + * getkey.c (finsih_lookup): Hack so that for v4 RSA keys the subkey + is used for encryption. - * g10.c (register_extension): New... - (main): Use it here instead of register_cipher_extesnion. - (strusage): s/strusage/my_strusage/ . Made static. - (main): Use set_strusage(). +Thu Sep 14 14:20:38 CEST 2000 Werner Koch <wk@openit.de> - * tdbdump.c (HEXTOBIN): Changed the name of the argument, so that - traditional cpp don't mess up the macros. Suggested by Jos Backus. + * 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. - * armor.c (parse_header_line): Stop parsing on a only WS line too. - Suggested by Aric Cyr. + * g10.c (main): Don't set --quite along with --no-tty. By Frank Tobin. - * misc.c (pull_in_libs): Removed. + * 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. - * mainproc.c (list_node): Print the PK algo in the --with-colon mode. - * keylist.c (list_keyblock): Ditto. +Wed Sep 13 18:12:34 CEST 2000 Werner Koch <wk@openit.de> - * misc.c (pull_in_libs): Removed pull in of g10c. + * keyedit.c (keyedit_menu): Allow to use "debug" on the secret key. - * misc.c (map_gcry_rc): Removed here and chnaged all users. + * ringedit.c (cmp_seckey): Fix for v4 RSA keys. + * seckey-cert.c (do_check): Workaround for PGP 7 bug. - * getkey.c: Replaced check_pubkey_algo by openpgp_pk_test_algo. - * import.c (delete_inv_parts): Ditto. - * pkclist.c: Ditto. - * skclist.c: Ditto. - * pubkey-enc.c: Ditto. +Wed Sep 6 17:55:47 CEST 2000 Werner Koch <wk@openit.de> - * g10.c (main): Replaced the function to diable PK algos. + * 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. - * g10.c (main): Replaced get_random_bits by gcry_random_bytes. - * seskey.c (encode_session_key): Likewise. - (make_session_key): Renamed randomize_buffer to gcry_randomize - and use the GCRY_xxx_RANDOM constants. - * cipher.c (write_header): Ditto. - * passphrase.c (hash_passphrase): Ditto. - * seckey-cert.c (protect_secret_key): Ditto. + * encode.c (encode_simple, encode_crypt): Fix for large files. + * sign.c (sign_file): Ditto. - * getkey.c (find_by_name): Replaced rmd160_hash_buffer - by gcry_md_hash_buffer. - * keyedit.c (show_prefs): Ditto. - * keylist.c (list_keyblock): Ditto. - * trustdb.c (print_uid_from_keyblock): Ditto. - (make_uid_records): Ditto. +Wed Sep 6 14:59:09 CEST 2000 Werner Koch <wk@openit.de> - * skclist.c (build_sk_list): Removed the test on faked RNGs. - (is_insecure): Removed. - * g10.c (--quick-random): Removed this option. + * passphrase.c (hash_passphrase): Removed funny assert. Reported by + David Mathog. - * Replaced all PUBKEY_ALGO_xxx by GCRY_PK_xxxx. + * openfile.c (try_make_homedir): Changes for non-Posix systems. + * g10.c (main): Take the default homedir from macro. - * misc.c (pubkey_algo_npkey): New as a wrapper around the gcry fucntion. - (pubkey_algo_nskey): Ditto. - (pubkey_algo_nsig): Ditto. - (pubkey_algo_nenc): Ditto. + * 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. - * Makefile.am (basicdefs.h): Added. - (install-data-local): Removed the handling for historic gpgm. +Fri Aug 25 16:05:38 CEST 2000 Werner Koch <wk@openit.de> - * misc.c (openpgp_cipher_test_algo): New. - (openpgp_pk_test_algo): New. - (openpgp_md_test_algo): New. + * 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. - * g10.c (build_list): Changed to use the new functions from libgcrypt. +Tue Aug 1 20:06:23 CEST 2000 Werner Koch <wk@openit.de> - * ringedit.c (enum_keyblocks): Set .rt to 0 on open. + * g10.c: New opttion --try-all-secrets on suggestion from Matthias Urlichs. + * pubkey-enc.c (get_session_key): Quite easy to implement here. - * 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. +Thu Jul 27 17:33:04 CEST 2000 Werner Koch <wk@openit.de> - * 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. + * 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. - (clearsign_file): Use gcry_md_test_algo() and gcry_md_algo_name(). +Thu Jul 27 12:01:00 CEST 2000 Werner Koch <wk@openit.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 suffix instead of - appending it. + * 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. - * encr-data.c (decrypt_data): Reset error on a weak key. +Thu Jul 27 10:02:38 CEST 2000 Werner Koch <wk@openit.de> - * cipher.c: Replaced the cipher and digest functions by the gcry_ ones. - * seckey-cert.c: Ditto. - * seskey.c: Ditto. - * g10.c (print_mds): Replaced digst functions with the new gcry_ ones. - * keyid.c: Ditto. - * mainproc.c: Ditto. - * passphrase.c: Ditto. - * sig-check.c: Ditto. - * sign.c: Ditto. + * 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> - * pkclist.c (do_edit_ownertrust): Made the answer string const. + * helptext.c, pkclist.c: s/superseeded/superseded/ - * basicdefs.h: New. Move some defs and decl to this header. +Mon Jul 10 16:08:57 CEST 2000 Werner Koch <wk@openit.de> - * openfile.c (open_outfile): Fixed the 8dot3 handling. + * parse-packet.c (enum_sig_subpkt): Fixed testing on crtitical bit in case + of a NULL buffer. Reported by Peter Marschall. - * 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. +Wed Jul 5 13:28:45 CEST 2000 Werner Koch <wk@openit.de> - * filter.h: Changed cipher handle types to the the GCRY_xxx ones. - replaces include cipher by system header include gcrypt.h. - * cipher.c: replaced the cipher functions by the gcry_ ones. - Ditto for the md functions. + * keyedit.c, keyid.c: Add some _() - * misc.c (map_gcry_rc): New. + * argparse.c: Changed the flag to suppress --version handling to also + suppress --help. -Wed Jun 28 11:54:44 CEST 2000 Werner Koch <wk@> +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. @@ -461,7 +3478,7 @@ Fri Jun 9 10:09:52 CEST 2000 Werner Koch <wk@openit.de> Wed Jun 7 19:19:09 CEST 2000 Werner Koch <wk@openit.de> - * sig-check.c (do_check): Use EMULATE_MDENCODE also on v4 paclets. + * 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> @@ -3280,3 +6297,13 @@ Thu Feb 12 22:24:42 1998 Werner Koch (wk@frodo) * pubkey-enc.c (get_session_key): rewritten + + Copyright 1998,1999,2000,2001,2002 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 index 3e724512b..f59cd8b2d 100644 --- a/g10/Makefile.am +++ b/g10/Makefile.am @@ -1,84 +1,104 @@ +# 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + ## Process this file with automake to produce Makefile.in -INCLUDES = -I$(top_srcdir)/include -I$(top_srcdir)/intl -EXTRA_DIST = OPTIONS pubring.asc options.skel -OMIT_DEPENDENCIES = zlib.h zconf.h -LDFLAGS = @LDFLAGS@ $(LIBGCRYPT_LIBS) -# we need to add libutil.la a second time because we have to resolve -# gpg_log_ in some libjnlib modules. - very ugly - should be removed soon. -needed_libs = ../util/libutil.a \ - ../jnlib/libjnlib.a ../util/libutil.a +INCLUDES = -I.. -I$(top_srcdir)/include -I$(top_srcdir)/intl +EXTRA_DIST = options.skel +# it seems that we can't use this with automake 1.5 +#OMIT_DEPENDENCIES = zlib.h zconf.h +LDFLAGS = @LDFLAGS@ @DYNLINK_LDFLAGS@ +needed_libs = ../cipher/libcipher.a ../mpi/libmpi.a ../util/libutil.a #noinst_PROGRAMS = gpgd -#bin_PROGRAMS = gpg kbxutil -noinst_PROGRAMS = gpg kbxutil +bin_PROGRAMS = gpg gpgv common_source = \ + global.h \ build-packet.c \ compress.c \ - basicdefs.h \ filter.h \ free-packet.c \ getkey.c \ - keydb.h \ - delkey.c \ - pkclist.c \ - skclist.c \ - ringedit.c \ + keydb.c keydb.h \ + keyring.c keyring.h \ + seskey.c \ kbnode.c \ - kbx.h \ - kbxblob.c \ - kbxio.c \ - kbxfile.c \ main.h \ mainproc.c \ armor.c \ mdfilter.c \ textfilter.c \ - cipher.c \ misc.c \ options.h \ openfile.c \ keyid.c \ - trustdb.c \ - trustdb.h \ - tdbdump.c \ - tdbio.c \ - tdbio.h \ - hkp.h \ - hkp.c \ packet.h \ parse-packet.c \ - passphrase.c \ - pubkey-enc.c \ - seckey-cert.c \ - seskey.c \ - import.c \ - export.c \ comment.c \ status.c \ status.h \ - sign.c \ plaintext.c \ - encr-data.c \ - encode.c \ - revoke.c \ - keylist.c \ sig-check.c \ - signal.c \ - helptext.c + keylist.c \ + signal.c -gpg_SOURCES = gpg.c \ +gpg_SOURCES = g10.c \ $(common_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 \ - keygen.c + import.c \ + export.c \ + hkp.h \ + hkp.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 + -# fixme: remove unused sources from kbxutil -kbxutil_SOURCES = kbxutil.c \ - $(common_source) #gpgd_SOURCES = gpgd.c \ @@ -88,15 +108,18 @@ kbxutil_SOURCES = kbxutil.c \ # ks-db.h \ # $(common_source) - -LDADD = $(needed_libs) @ZLIBS@ @INTLLIBS@ - +LDADD = $(needed_libs) @ZLIBS@ @INTLLIBS@ +# gpg gets LIBOBJS to add in mkdtemp if the platform doesn't have it +gpg_LDADD = @LIBOBJS@ $(LDADD) @NETLIBS@ $(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 diff --git a/g10/armor.c b/g10/armor.c index 819c951dc..9c7858fe6 100644 --- a/g10/armor.c +++ b/g10/armor.c @@ -1,5 +1,5 @@ /* armor.c - Armor flter - * Copyright (C) 1998, 1999, 2000 Free Software Foundation, Inc. + * Copyright (C) 1998, 1999, 2000, 2001, 2002 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -28,7 +28,7 @@ #include "errors.h" #include "iobuf.h" -#include <gcrypt.h> +#include "memory.h" #include "util.h" #include "filter.h" #include "packet.h" @@ -151,8 +151,9 @@ initialize(void) } /**************** - * Check whether this is an armored file or not - * See also parse-packet.c for details on this code + * 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 @@ -195,6 +196,7 @@ 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 */ @@ -210,7 +212,7 @@ static void invalid_armor(void) { write_status(STATUS_BADARMOR); - gpg_exit(1); /* stop here */ + g10_exit(1); /* stop here */ } @@ -245,7 +247,9 @@ parse_hash_header( const char *line ) found |= 2; else if( !strncmp( s, "MD5", s2-s ) ) found |= 4; - else if( !strncmp( s, "TIGER", s2-s ) ) + else if( !strncmp( s, "TIGER192", s2-s ) ) + found |= 8; + else if( !strncmp( s, "TIGER", s2-s ) ) /* used by old versions */ found |= 8; else return 0; @@ -283,6 +287,14 @@ is_armor_header( byte *line, unsigned len ) return -1; save_p = p; p += 5; + + /* Some mail programs on Windows seem to add spaces to the end of + the line. This becomes strict if --openpgp is set. */ + + if(!opt.rfc2440) + while(*p==' ') + p++; + if( *p == '\r' ) p++; if( *p == '\n' ) @@ -312,19 +324,19 @@ is_armor_header( byte *line, unsigned len ) * >0: Good header line */ static int -parse_header_line( armor_filter_context_t *afx, byte *line, unsigned len ) +parse_header_line( armor_filter_context_t *afx, byte *line, unsigned int len ) { byte *p; int hashes=0; + unsigned int len2; - /* fixme: why this double check? I think the original code w/o the - * second check for an empty line was done from an early draft of - * of OpenPGP - or simply very stupid code */ - if( *line == '\n' || ( len && (*line == '\r' && line[1]=='\n') ) ) - return 0; /* empty line */ - len = trim_trailing_ws( line, len ); - if( !len ) - return 0; /* WS only same as empty line */ + 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 */ + } + len = len2; + line[len2] = 0; p = strchr( line, ':'); if( !p || !p[1] ) { @@ -399,7 +411,7 @@ check_input( armor_filter_context_t *afx, IOBUF a ) if( hdr_line == BEGIN_SIGNED_MSG_IDX ) { if( afx->in_cleartext ) { log_error(_("nested clear text signatures\n")); - rc = GPGERR_INVALID_ARMOR; + rc = G10ERR_INVALID_ARMOR; } afx->in_cleartext = 1; } @@ -429,7 +441,7 @@ check_input( armor_filter_context_t *afx, IOBUF a ) i = parse_header_line( afx, line, len ); if( i <= 0 ) { if( i ) - rc = GPGERR_INVALID_ARMOR; + rc = G10ERR_INVALID_ARMOR; break; } } @@ -502,7 +514,7 @@ fake_packet( armor_filter_context_t *afx, IOBUF a, /* 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 tupe of lineending - handling of the removed + * 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 @@ -590,6 +602,15 @@ fake_packet( armor_filter_context_t *afx, IOBUF a, } +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, @@ -636,9 +657,9 @@ radix64_read( armor_filter_context_t *afx, IOBUF a, size_t *retn, if( isxdigit(cc1) && isxdigit(cc2) && strchr( "=\n\r\t ", cc3 )) { /* well it seems to be the case - adjust */ - c = isdigit(cc1)? (cc1 - '0'): (toupper(cc1)-'A'+10); + c = isdigit(cc1)? (cc1 - '0'): (ascii_toupper(cc1)-'A'+10); c <<= 4; - c |= isdigit(cc2)? (cc2 - '0'): (toupper(cc2)-'A'+10); + c |= isdigit(cc2)? (cc2 - '0'): (ascii_toupper(cc2)-'A'+10); afx->buffer_pos += 2; afx->qp_detected = 1; goto again; @@ -728,20 +749,23 @@ radix64_read( armor_filter_context_t *afx, IOBUF a, size_t *retn, break; /* eof */ } while( ++idx < 4 ); if( c == -1 ) { - log_error(_("premature eof (in CRC)\n")); - rc = GPGERR_INVALID_ARMOR; - } + log_info(_("premature eof (in CRC)\n")); + rc = invalid_crc(); + } else if( idx != 4 ) { - log_error(_("malformed CRC\n")); - rc = GPGERR_INVALID_ARMOR; + log_info(_("malformed CRC\n")); + rc = invalid_crc(); } else if( mycrc != afx->crc ) { - log_error(_("CRC error; %06lx - %06lx\n"), + log_info (_("CRC error; %06lx - %06lx\n"), (ulong)afx->crc, (ulong)mycrc); - rc = GPGERR_INVALID_ARMOR; + 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 )*/; @@ -754,11 +778,11 @@ radix64_read( armor_filter_context_t *afx, IOBUF a, size_t *retn, rc = 0; else if( rc == 2 ) { log_error(_("premature eof (in Trailer)\n")); - rc = GPGERR_INVALID_ARMOR; + rc = G10ERR_INVALID_ARMOR; } else { log_error(_("error in trailer line\n")); - rc = GPGERR_INVALID_ARMOR; + rc = G10ERR_INVALID_ARMOR; } #endif } @@ -815,7 +839,9 @@ armor_filter( void *opaque, int control, *ret_len = n; } else if( control == IOBUFCTRL_UNDERFLOW ) { - if( size < 15+(4*15) ) /* need space for up to 4 onepass_sigs */ + /* We need some space for the faked packet. The minmum required + * size is ~18 + length of the session marker */ + if( size < 50 ) BUG(); /* supplied buffer too short */ if( afx->faked ) @@ -831,7 +857,14 @@ armor_filter( void *opaque, int control, rc = -1; } else if( afx->faked ) { - unsigned hashes = afx->hashes; + 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 */ @@ -842,36 +875,21 @@ armor_filter( void *opaque, int control, afx->pgp2mode = 1; } n=0; - do { - /* first some onepass signature packets */ - buf[n++] = 0x90; /* old format, type 4, 1 length byte */ - buf[n++] = 13; /* length */ - buf[n++] = 3; /* version */ - buf[n++] = afx->not_dash_escaped? 0:1; /* sigclass */ - if( hashes & 1 ) { - hashes &= ~1; - buf[n++] = GCRY_MD_RMD160; - } - else if( hashes & 2 ) { - hashes &= ~2; - buf[n++] = GCRY_MD_SHA1; - } - else if( hashes & 4 ) { - hashes &= ~4; - buf[n++] = GCRY_MD_MD5; - } - else if( hashes & 8 ) { - hashes &= ~8; - buf[n++] = GCRY_MD_TIGER; - } - else - buf[n++] = 0; /* (don't know) */ - - buf[n++] = 0; /* public key algo (don't know) */ - memset(buf+n, 0, 8); /* don't know the keyid */ - n += 8; - buf[n++] = !hashes; /* last one */ - } while( hashes ); + /* 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_TIGER; + buf[1] = n - 2; /* followed by a plaintext packet */ buf[n++] = 0xaf; /* old packet format, type 11, var length */ @@ -908,9 +926,8 @@ armor_filter( void *opaque, int control, PRINTABLE_OS_NAME ")" LF ); /* write the comment string or a default one */ - s = opt.comment_string ? opt.comment_string - : _("For info see http://www.gnupg.org"); - if( *s ) { + s = opt.comment_string; + if( s && *s ) { iobuf_writestr(a, "Comment: " ); for( ; *s; s++ ) { if( *s == '\n' ) @@ -925,8 +942,15 @@ armor_filter( void *opaque, int control, iobuf_writestr(a, LF ); } - if( afx->hdrlines ) - iobuf_writestr(a, afx->hdrlines); + 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, LF ); afx->status++; afx->idx = 0; @@ -1041,7 +1065,7 @@ armor_filter( void *opaque, int control, if( afx->qp_detected ) log_error(_("quoted printable character in armor - " "probably a buggy MTA has been used\n") ); - gcry_free( afx->buffer ); + m_free( afx->buffer ); afx->buffer = NULL; } else if( control == IOBUFCTRL_DESC ) @@ -1058,7 +1082,7 @@ make_radix64_string( const byte *data, size_t len ) { char *buffer, *p; - buffer = p = gcry_xmalloc( (len+2)/3*4 + 1 ); + buffer = p = m_alloc( (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]; @@ -1078,3 +1102,221 @@ make_radix64_string( const byte *data, size_t len ) 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 = m_alloc_clear (sizeof *x); + return x; +} + +void +unarmor_pump_release (UnarmorPump x) +{ + m_free (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 index 93381879c..e24ac3b2a 100644 --- a/g10/build-packet.c +++ b/g10/build-packet.c @@ -1,5 +1,5 @@ /* build-packet.c - assemble packets and write them - * Copyright (C) 1998, 1999, 2000 Free Software Foundation, Inc. + * Copyright (C) 1998, 1999, 2000, 2001, 2002 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -24,13 +24,14 @@ #include <string.h> #include <assert.h> -#include <gcrypt.h> #include "packet.h" #include "errors.h" #include "iobuf.h" +#include "mpi.h" #include "util.h" +#include "cipher.h" +#include "memory.h" #include "options.h" -#include "main.h" static int do_comment( IOBUF out, int ctb, PKT_comment *rem ); @@ -80,8 +81,8 @@ build_packet( IOBUF out, PACKET *pkt ) 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->photo ) - pkttype = PKT_PHOTO_ID; + if( pkt->pkt.user_id->attrib_data ) + pkttype = PKT_ATTRIBUTE; break; default: break; } @@ -91,7 +92,7 @@ build_packet( IOBUF out, PACKET *pkt ) else ctb = 0x80 | ((pkttype & 15)<<2); switch( pkttype ) { - case PKT_PHOTO_ID: + case PKT_ATTRIBUTE: case PKT_USER_ID: rc = do_user_id( out, ctb, pkt->pkt.user_id ); break; @@ -134,7 +135,7 @@ build_packet( IOBUF out, PACKET *pkt ) rc = do_onepass_sig( out, ctb, pkt->pkt.onepass_sig ); break; case PKT_RING_TRUST: - break; /* ignore it */ + break; /* ignore it (keyring.c does write it directly)*/ default: log_bug("invalid packet type in build_packet()\n"); break; @@ -158,7 +159,7 @@ calc_packet_length( PACKET *pkt ) n = calc_plaintext( pkt->pkt.plaintext ); new_ctb = pkt->pkt.plaintext->new_ctb; break; - case PKT_PHOTO_ID: + case PKT_ATTRIBUTE: case PKT_USER_ID: case PKT_COMMENT: case PKT_PUBLIC_KEY: @@ -195,10 +196,10 @@ write_fake_data( IOBUF out, MPI a ) static int do_comment( IOBUF out, int ctb, PKT_comment *rem ) { - if( !opt.no_comment ) { + if( opt.sk_comments ) { write_header(out, ctb, rem->len); if( iobuf_write( out, rem->data, rem->len ) ) - return GPGERR_WRITE_FILE; + return G10ERR_WRITE_FILE; } return 0; } @@ -206,19 +207,15 @@ do_comment( IOBUF out, int ctb, PKT_comment *rem ) static int do_user_id( IOBUF out, int ctb, PKT_user_id *uid ) { - if( uid->photo ) { - write_header(out, ctb, uid->photolen); - uid->stored_at = iobuf_get_temp_length ( out ); /* what a hack ... */ - /* ... and it does only work when used with a temp iobuf */ - if( iobuf_write( out, uid->photo, uid->photolen ) ) - return GPGERR_WRITE_FILE; + 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_header(out, ctb, uid->len); - uid->stored_at = iobuf_get_temp_length ( out ); /* what a hack ... */ - /* ... and it does only work when used with a temp iobuf */ if( iobuf_write( out, uid->name, uid->len ) ) - return GPGERR_WRITE_FILE; + return G10ERR_WRITE_FILE; } return 0; } @@ -252,7 +249,7 @@ do_public_key( IOBUF out, int ctb, PKT_public_key *pk ) write_header2(out, ctb, iobuf_get_temp_length(a), pk->hdrbytes, 1 ); if( iobuf_write_temp( out, a ) ) - rc = GPGERR_WRITE_FILE; + rc = G10ERR_WRITE_FILE; iobuf_close(a); return rc; @@ -263,7 +260,7 @@ do_public_key( IOBUF out, int ctb, PKT_public_key *pk ) * Make a hash value from the public key certificate */ void -hash_public_key( GCRY_MD_HD md, PKT_public_key *pk ) +hash_public_key( MD_HANDLE md, PKT_public_key *pk ) { PACKET pkt; int rc = 0; @@ -283,7 +280,7 @@ hash_public_key( GCRY_MD_HD md, PKT_public_key *pk ) pkt.pkttype = PKT_PUBLIC_KEY; pkt.pkt.public_key = pk; if( (rc = build_packet( a, &pkt )) ) - log_fatal("build public_key for hashing failed: %s\n", gpg_errstr(rc)); + log_fatal("build public_key for hashing failed: %s\n", g10_errstr(rc)); if( !(pk->version == 3 && pk->pubkey_algo == 16) ) { /* skip the constructed header but don't do this for our very old @@ -314,10 +311,10 @@ hash_public_key( GCRY_MD_HD md, PKT_public_key *pk ) } } /* hash a header */ - gcry_md_putc( md, 0x99 ); + md_putc( md, 0x99 ); pktlen &= 0xffff; /* can't handle longer packets */ - gcry_md_putc( md, pktlen >> 8 ); - gcry_md_putc( md, pktlen & 0xff ); + md_putc( md, pktlen >> 8 ); + md_putc( md, pktlen & 0xff ); } /* hash the packet body */ while( (c=iobuf_get(a)) != -1 ) { @@ -328,7 +325,7 @@ hash_public_key( GCRY_MD_HD md, PKT_public_key *pk ) i=0; } #endif - gcry_md_putc( md, c ); + md_putc( md, c ); } #if 0 putc('\n', fp); @@ -343,43 +340,64 @@ do_secret_key( IOBUF out, int ctb, PKT_secret_key *sk ) { int rc = 0; int i, nskey, npkey; - IOBUF a = iobuf_temp(); + 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, 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 { - iobuf_put(a, 0xff ); + /* 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 ) { - iobuf_put(a, 101 ); + /* These modes are not possible in OpenPGP, we use them + to implement our extesnsions, 101 can ve views 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 ); @@ -392,34 +410,41 @@ do_secret_key( IOBUF out, int ctb, PKT_secret_key *sk ) || 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 ); + iobuf_put(a, sk->protect.s2k.count ); + + /* For out special mode 1001 we do not need an IV */ if( sk->protect.s2k.mode != 1001 ) 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->is_protected && sk->version >= 4 ) { + /* The secret key is protected - write it out as it is */ byte *p; - size_t n; - assert( gcry_mpi_get_flag( sk->skey[i], GCRYMPI_FLAG_OPAQUE ) ); - p = gcry_mpi_get_opaque( sk->skey[i], &n ); - iobuf_write(a, p, (n+7)/8 ); + assert( mpi_is_opaque( sk->skey[npkey] ) ); + p = mpi_get_opaque( sk->skey[npkey], &i ); + iobuf_write(a, p, i ); } else { + /* v3 way - same code for protected and 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, 1 ); + /* And finally write it out the real stream */ if( iobuf_write_temp( out, a ) ) - rc = GPGERR_WRITE_FILE; + rc = G10ERR_WRITE_FILE; - iobuf_close(a); + iobuf_close(a); /* close the remporary buffer */ return rc; } @@ -448,7 +473,7 @@ do_symkey_enc( IOBUF out, int ctb, PKT_symkey_enc *enc ) write_header(out, ctb, iobuf_get_temp_length(a) ); if( iobuf_write_temp( out, a ) ) - rc = GPGERR_WRITE_FILE; + rc = G10ERR_WRITE_FILE; iobuf_close(a); return rc; @@ -482,7 +507,7 @@ do_pubkey_enc( IOBUF out, int ctb, PKT_pubkey_enc *enc ) write_header(out, ctb, iobuf_get_temp_length(a) ); if( iobuf_write_temp( out, a ) ) - rc = GPGERR_WRITE_FILE; + rc = G10ERR_WRITE_FILE; iobuf_close(a); return rc; @@ -511,12 +536,12 @@ do_plaintext( IOBUF out, int ctb, PKT_plaintext *pt ) for(i=0; i < pt->namelen; i++ ) iobuf_put(out, pt->name[i] ); if( write_32(out, pt->timestamp ) ) - rc = GPGERR_WRITE_FILE; + rc = G10ERR_WRITE_FILE; n = 0; while( (nbytes=iobuf_read(pt->buf, buf, 1000)) != -1 ) { if( iobuf_write(out, buf, nbytes) == -1 ) { - rc = GPGERR_WRITE_FILE; + rc = G10ERR_WRITE_FILE; break; } n += nbytes; @@ -539,7 +564,7 @@ do_encrypted( IOBUF out, int ctb, PKT_encrypted *ed ) int rc = 0; u32 n; - n = ed->len ? (ed->len + 10) : 0; + n = ed->len ? (ed->len + ed->extralen) : 0; write_header(out, ctb, n ); /* This is all. The caller has to write the real data */ @@ -555,7 +580,7 @@ do_encrypted_mdc( IOBUF out, int ctb, PKT_encrypted *ed ) assert( ed->mdc_method ); - n = ed->len ? (ed->len + 10) : 0; + n = ed->len ? (ed->len + ed->extralen) : 0; write_header(out, ctb, n ); iobuf_put(out, 1 ); /* version */ @@ -572,7 +597,7 @@ do_mdc( IOBUF out, PKT_mdc *mdc ) iobuf_put( out, 0xd3 ); /* packet ID and 1 byte length */ iobuf_put( out, 0x14 ); /* length = 20 */ if( iobuf_write( out, mdc->hash, sizeof(mdc->hash) ) ) - return GPGERR_WRITE_FILE; + return G10ERR_WRITE_FILE; return 0; } @@ -591,36 +616,36 @@ do_compressed( IOBUF out, int ctb, PKT_compressed *cd ) } - /**************** - * Find a subpacket of type REQTYPE in BUFFER and a return a pointer - * to the first byte of that subpacket data. - * And return the length of the packet in RET_N and the number of - * header bytes in RET_HLEN (length header and type byte). + * Delete all subpackets of type REQTYPE and return a bool whether a packet + * was deleted. */ -byte * -find_subpkt( byte *buffer, sigsubpkttype_t reqtype, - size_t *ret_hlen, size_t *ret_n ) +int +delete_sig_subpkt (subpktarea_t *area, sigsubpkttype_t reqtype ) { int buflen; sigsubpkttype_t type; - byte *bufstart; + byte *buffer, *bufstart; size_t n; + size_t unused = 0; + int okay = 0; - if( !buffer ) - return NULL; - buflen = (*buffer << 8) | buffer[1]; - buffer += 2; + if( !area ) + return 0; + buflen = area->len; + buffer = area->data; for(;;) { - if( !buflen ) - return NULL; /* end of packets; not found */ + 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[2] << 8) | buffer[3]; buffer += 4; buflen -= 4; } @@ -633,137 +658,175 @@ find_subpkt( byte *buffer, sigsubpkttype_t reqtype, } if( buflen < n ) break; + type = *buffer & 0x7f; if( type == reqtype ) { buffer++; + buflen--; n--; if( n > buflen ) break; - if( ret_hlen ) - *ret_hlen = buffer - bufstart; - if( ret_n ) - *ret_n = n; - return buffer; + buffer += n; /* point to next subpkt */ + buflen -= n; + memmove (bufstart, buffer, buflen); /* shift */ + unused += buffer - bufstart; + buffer = bufstart; } - buffer += n; buflen -=n; + else { + buffer += n; buflen -=n; + } } - log_error("find_subpkt: buffer shorter than subpacket\n"); - return NULL; + 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 unhased part to the hashed one. - * Note: All pointers into sig->[un]hashed are not valid after a call - * to this function. The data to but into the subpaket should be - * in buffer with a length of buflen. + * 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, +build_sig_subpkt (PKT_signature *sig, sigsubpkttype_t type, const byte *buffer, size_t buflen ) { - byte *data; - size_t hlen, dlen, nlen; - int found=0; - int critical, hashed, realloced; - size_t n, n0; + byte *p; + int critical, hashed; + subpktarea_t *oldarea, *newarea; + size_t nlen, n, n0; critical = (type & SIGSUBPKT_FLAG_CRITICAL); type &= ~SIGSUBPKT_FLAG_CRITICAL; - if( type == SIGSUBPKT_NOTATION ) - ; /* we allow multiple packets */ - else if( (data = find_subpkt( sig->hashed_data, type, &hlen, &dlen )) ) - found = 1; - else if( (data = find_subpkt( sig->unhashed_data, type, &hlen, &dlen ))) - found = 2; + /* 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: + /* 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_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; + + default: + break; + } - if( found ) - log_bug("build_sig_packet: update nyi\n"); if( (buflen+1) >= 8384 ) - nlen = 5; + nlen = 5; /* write 5 byte length header */ else if( (buflen+1) >= 192 ) - nlen = 2; + nlen = 2; /* write 2 byte length header */ else - nlen = 1; + nlen = 1; /* just a 1 byte length header */ switch( type ) { - case SIGSUBPKT_SIG_CREATED: - case SIGSUBPKT_PRIV_ADD_SIG: - case SIGSUBPKT_PREF_SYM: - case SIGSUBPKT_PREF_HASH: - case SIGSUBPKT_PREF_COMPR: - case SIGSUBPKT_KS_FLAGS: - case SIGSUBPKT_KEY_EXPIRE: - case SIGSUBPKT_NOTATION: - case SIGSUBPKT_POLICY: - case SIGSUBPKT_REVOC_REASON: - case SIGSUBPKT_KEY_FLAGS: - case SIGSUBPKT_FEATURES: - hashed = 1; break; - default: hashed = 0; break; - } - - if( hashed ) { - n0 = sig->hashed_data ? ((*sig->hashed_data << 8) - | sig->hashed_data[1]) : 0; - n = n0 + nlen + 1 + buflen; /* length, type, buffer */ - realloced = !!sig->hashed_data; - data = sig->hashed_data ? gcry_xrealloc( sig->hashed_data, n+2 ) - : gcry_xmalloc( n+2 ); - } - else { - n0 = sig->unhashed_data ? ((*sig->unhashed_data << 8) - | sig->unhashed_data[1]) : 0; - n = n0 + nlen + 1 + buflen; /* length, type, buffer */ - realloced = !!sig->unhashed_data; - data = sig->unhashed_data ? gcry_xrealloc( sig->unhashed_data, n+2 ) - : gcry_xmalloc( n+2 ); + case SIGSUBPKT_ISSUER: + case SIGSUBPKT_PRIV_VERIFY_CACHE: /*(obsolete)*/ + hashed = 0; + break; + default: + hashed = 1; + break; } if( critical ) type |= SIGSUBPKT_FLAG_CRITICAL; - data[0] = (n >> 8) & 0xff; - data[1] = n & 0xff; - if( nlen == 5 ) { - data[n0+2] = 255; - data[n0+3] = (buflen+1) >> 24; - data[n0+4] = (buflen+1) >> 16; - data[n0+5] = (buflen+1) >> 8; - data[n0+6] = (buflen+1); - data[n0+7] = type; - memcpy(data+n0+8, buffer, buflen ); + 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( nlen == 2 ) { - data[n0+2] = (buflen+1-192) / 256 + 192; - data[n0+3] = (buflen+1-192) & 256; - data[n0+4] = type; - memcpy(data+n0+5, buffer, buflen ); + else if (oldarea) { + newarea = m_realloc (oldarea, sizeof (*newarea) + n - 1); + newarea->size = n; + /*log_debug ("reallocating area for type %d\n", type );*/ } else { - data[n0+2] = buflen+1; - data[n0+3] = type; - memcpy(data+n0+4, buffer, buflen ); + newarea = m_alloc (sizeof (*newarea) + n - 1); + newarea->size = n; + /*log_debug ("allocating area for type %d\n", type );*/ } - - if( hashed ) { - if( !realloced ) - gcry_free(sig->hashed_data); - sig->hashed_data = data; + 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 { - if( !realloced ) - gcry_free(sig->unhashed_data); - sig->unhashed_data = data; + *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 ) @@ -789,8 +852,70 @@ build_sig_subpkt_from_sig( PKT_signature *sig ) buf[2] = (u >> 8) & 0xff; buf[3] = u & 0xff; build_sig_subpkt( sig, SIGSUBPKT_SIG_CREATED, buf, 4 ); + + if(sig->expiredate) + { + u = sig->expiredate-sig->timestamp; + 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,int buflen, + const void *header,int 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=m_realloc(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 */ + + memcpy(&attrib[idx],header,headerlen); + memcpy(&attrib[idx+headerlen],buf,buflen); + uid->attrib_len+=idx+headerlen+buflen; +} static int do_signature( IOBUF out, int ctb, PKT_signature *sig ) @@ -818,16 +943,14 @@ do_signature( IOBUF out, int ctb, PKT_signature *sig ) /* 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_data?((sig->hashed_data[0]<<8) - |sig->hashed_data[1]) :0; + nn = sig->hashed? sig->hashed->len : 0; write_16(a, nn); if( nn ) - iobuf_write( a, sig->hashed_data+2, nn ); - nn = sig->unhashed_data?((sig->unhashed_data[0]<<8) - |sig->unhashed_data[1]) :0; + 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+2, nn ); + iobuf_write( a, sig->unhashed->data, nn ); } iobuf_put(a, sig->digest_start[0] ); iobuf_put(a, sig->digest_start[1] ); @@ -842,7 +965,7 @@ do_signature( IOBUF out, int ctb, PKT_signature *sig ) else write_header(out, ctb, iobuf_get_temp_length(a) ); if( iobuf_write_temp( out, a ) ) - rc = GPGERR_WRITE_FILE; + rc = G10ERR_WRITE_FILE; iobuf_close(a); return rc; @@ -865,7 +988,7 @@ do_onepass_sig( IOBUF out, int ctb, PKT_onepass_sig *ops ) write_header(out, ctb, iobuf_get_temp_length(a) ); if( iobuf_write_temp( out, a ) ) - rc = GPGERR_WRITE_FILE; + rc = G10ERR_WRITE_FILE; iobuf_close(a); return rc; diff --git a/g10/cipher.c b/g10/cipher.c index cad6ff664..2af8750c8 100644 --- a/g10/cipher.c +++ b/g10/cipher.c @@ -1,5 +1,5 @@ /* cipher.c - En-/De-ciphering filter - * Copyright (C) 1998, 1999, 2000 Free Software Foundation, Inc. + * Copyright (C) 1998, 1999, 2000, 2001 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -25,9 +25,9 @@ #include <errno.h> #include <assert.h> -#include <gcrypt.h> #include "errors.h" #include "iobuf.h" +#include "memory.h" #include "util.h" #include "filter.h" #include "packet.h" @@ -45,69 +45,69 @@ write_header( cipher_filter_context_t *cfx, IOBUF a ) PACKET pkt; PKT_encrypted ed; byte temp[18]; - unsigned int blocksize; - unsigned int nprefix; - int rc; - int use_mdc = opt.force_mdc; + unsigned blocksize; + unsigned nprefix; + int use_mdc; - blocksize = gcry_cipher_get_algo_blklen( cfx->dek->algo ); + blocksize = cipher_get_blocksize( cfx->dek->algo ); if( blocksize < 8 || blocksize > 16 ) log_fatal("unsupported blocksize %u\n", blocksize ); + + use_mdc = cfx->dek->use_mdc; + if( blocksize != 8 ) - use_mdc = 1; /* enable it for all modern ciphers */ - if( opt.rfc2440 ) + use_mdc = 1; /* Hack: enable it for all modern ciphers */ + /* Note: We should remove this hack as soon as a reasonable number of keys + are carrying the MDC flag. But always keep the hack for conventional + encryption */ + + if (opt.force_mdc) + use_mdc = 1; + + if( opt.rfc2440 || opt.rfc1991 || opt.disable_mdc ) use_mdc = 0; /* override - rfc2440 does not know about MDC */ memset( &ed, 0, sizeof ed ); ed.len = cfx->datalen; + ed.extralen = blocksize+2; ed.new_ctb = !ed.len && !opt.rfc1991; if( use_mdc ) { - ed.mdc_method = GCRY_MD_SHA1; - cfx->mdc_hash = gcry_md_open( GCRY_MD_SHA1, 0 ); - if( !cfx->mdc_hash ) - BUG(); + ed.mdc_method = DIGEST_ALGO_SHA1; + cfx->mdc_hash = md_open( DIGEST_ALGO_SHA1, 0 ); if ( DBG_HASHING ) - gcry_md_start_debug( cfx->mdc_hash, "creatmdc" ); + 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 = 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; - gcry_randomize( temp, nprefix, GCRY_STRONG_RANDOM ); + randomize_buffer( temp, nprefix, 1 ); temp[nprefix] = temp[nprefix-2]; temp[nprefix+1] = temp[nprefix-1]; print_cipher_algo_note( cfx->dek->algo ); - if( !(cfx->cipher_hd = gcry_cipher_open( cfx->dek->algo, - GCRY_CIPHER_MODE_CFB, - GCRY_CIPHER_SECURE - | ((use_mdc || cfx->dek->algo >= 100) ? - 0 : GCRY_CIPHER_ENABLE_SYNC))) - ) { - /* we should never get an error here cause we already checked, that - * the algorithm is available. */ - BUG(); - } - - + cfx->cipher_hd = cipher_open( cfx->dek->algo, + use_mdc? CIPHER_MODE_CFB + : CIPHER_MODE_AUTO_CFB, 1 ); /* log_hexdump( "thekey", cfx->dek->key, cfx->dek->keylen );*/ - rc = gcry_cipher_setkey( cfx->cipher_hd, cfx->dek->key, cfx->dek->keylen ); - if( !rc ) - rc = gcry_cipher_setiv( cfx->cipher_hd, NULL, 0 ); - if( rc ) - log_fatal("set key or IV failed: %s\n", gcry_strerror(rc) ); + 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 ) - gcry_md_write( cfx->mdc_hash, temp, nprefix+2 ); - rc = gcry_cipher_encrypt( cfx->cipher_hd, temp, nprefix+2, NULL, 0 ); - if( !rc ) - rc = gcry_cipher_sync( cfx->cipher_hd ); - if( rc ) - log_fatal("encrypt failed: %s\n", gcry_strerror(rc) ); + 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; - } @@ -129,46 +129,39 @@ cipher_filter( void *opaque, int control, else if( control == IOBUFCTRL_FLUSH ) { /* encrypt */ assert(a); if( !cfx->header ) { - write_status( STATUS_BEGIN_ENCRYPTION ); write_header( cfx, a ); } if( cfx->mdc_hash ) - gcry_md_write( cfx->mdc_hash, buf, size ); - rc = gcry_cipher_encrypt( cfx->cipher_hd, buf, size, NULL, 0); - if( rc ) - log_fatal("encrypt failed: %s\n", gcry_strerror(rc) ); + md_write( cfx->mdc_hash, buf, size ); + cipher_encrypt( cfx->cipher_hd, buf, buf, size); if( iobuf_write( a, buf, size ) ) - rc = GPGERR_WRITE_FILE; + rc = G10ERR_WRITE_FILE; } else if( control == IOBUFCTRL_FREE ) { if( cfx->mdc_hash ) { byte *hash; - int hashlen = gcry_md_get_algo_dlen( gcry_md_get_algo( cfx->mdc_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; - gcry_md_putc( cfx->mdc_hash, temp[0] ); - gcry_md_putc( cfx->mdc_hash, temp[1] ); + md_putc( cfx->mdc_hash, temp[0] ); + md_putc( cfx->mdc_hash, temp[1] ); - hash = gcry_md_read( cfx->mdc_hash, 0 ); + md_final( cfx->mdc_hash ); + hash = md_read( cfx->mdc_hash, 0 ); memcpy(temp+2, hash, 20); - rc = gcry_cipher_encrypt( cfx->cipher_hd, temp, 22, NULL, 0 ); - if( rc ) - log_fatal("encrypt failed: %s\n", gcry_strerror(rc) ); - gcry_md_close( cfx->mdc_hash ); cfx->mdc_hash = NULL; + 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" ); } - gcry_cipher_close(cfx->cipher_hd); - write_status( STATUS_END_ENCRYPTION ); + cipher_close(cfx->cipher_hd); } else if( control == IOBUFCTRL_DESC ) { *(char**)buf = "cipher_filter"; } return rc; } - - diff --git a/g10/comment.c b/g10/comment.c index b1732a29f..6d27e481b 100644 --- a/g10/comment.c +++ b/g10/comment.c @@ -1,5 +1,5 @@ /* comment.c - write comment stuff - * Copyright (C) 1998, 2000 Free Software Foundation, Inc. + * Copyright (C) 1998 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -29,7 +29,7 @@ #include "packet.h" #include "errors.h" #include "iobuf.h" -#include <gcrypt.h> +#include "memory.h" #include "util.h" #include "main.h" #include "keydb.h" @@ -45,41 +45,59 @@ write_comment( IOBUF out, const char *s ) pkt.pkttype = PKT_COMMENT; if( *s != '#' ) { - pkt.pkt.comment = gcry_xmalloc( sizeof *pkt.pkt.comment + n ); + pkt.pkt.comment = m_alloc( sizeof *pkt.pkt.comment + n ); pkt.pkt.comment->len = n+1; *pkt.pkt.comment->data = '#'; strcpy(pkt.pkt.comment->data+1, s); } else { - pkt.pkt.comment = gcry_xmalloc( sizeof *pkt.pkt.comment + n - 1 ); + pkt.pkt.comment = m_alloc( sizeof *pkt.pkt.comment + n - 1 ); pkt.pkt.comment->len = n; strcpy(pkt.pkt.comment->data, s); } if( (rc = build_packet( out, &pkt )) ) - log_error("build_packet(comment) failed: %s\n", gpg_errstr(rc) ); + log_error("build_packet(comment) failed: %s\n", g10_errstr(rc) ); free_packet( &pkt ); return rc; } KBNODE -make_comment_node_from_buffer( const char *s, size_t n ) +make_comment_node( const char *s ) { PACKET *pkt; + size_t n = strlen(s); - pkt = gcry_xcalloc( 1, sizeof *pkt ); + pkt = m_alloc_clear( sizeof *pkt ); pkt->pkttype = PKT_COMMENT; - pkt->pkt.comment = gcry_xmalloc( sizeof *pkt->pkt.comment + n - 1 ); + pkt->pkt.comment = m_alloc( sizeof *pkt->pkt.comment + n - 1 ); pkt->pkt.comment->len = n; strcpy(pkt->pkt.comment->data, s); return new_kbnode( pkt ); } + KBNODE -make_comment_node( const char *s ) +make_mpi_comment_node( const char *s, MPI a ) { - return make_comment_node_from_buffer ( s, strlen (s) ); -} + PACKET *pkt; + byte *buf, *p, *pp; + unsigned n1, nb1; + size_t n = strlen(s); + nb1 = mpi_get_nbits( a ); + p = buf = mpi_get_buffer( a, &n1, NULL ); + pkt = m_alloc_clear( sizeof *pkt ); + pkt->pkttype = PKT_COMMENT; + pkt->pkt.comment = m_alloc( sizeof *pkt->pkt.comment + n + 2 + n1 ); + pkt->pkt.comment->len = n+1+2+n1; + pp = pkt->pkt.comment->data; + memcpy(pp, s, n+1); + pp[n+1] = nb1 >> 8; + pp[n+2] = nb1 ; + memcpy(pp+n+3, p, n1 ); + m_free(buf); + return new_kbnode( pkt ); +} diff --git a/g10/compress.c b/g10/compress.c index 2666e9051..6d85e0181 100644 --- a/g10/compress.c +++ b/g10/compress.c @@ -1,5 +1,5 @@ /* compress.c - compress filter - * Copyright (C) 1998, 1999, 2000 Free Software Foundation, Inc. + * Copyright (C) 1998, 1999, 2000, 2001, 2002 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -27,10 +27,11 @@ #include <errno.h> #include <zlib.h> -#include <gcrypt.h> #include "util.h" +#include "memory.h" #include "packet.h" #include "filter.h" +#include "main.h" #include "options.h" @@ -63,7 +64,7 @@ init_compress( compress_filter_context_t *zfx, z_stream *zs ) } zfx->outbufsize = 8192; - zfx->outbuf = gcry_xmalloc( zfx->outbufsize ); + zfx->outbuf = m_alloc( zfx->outbufsize ); } static int @@ -73,7 +74,11 @@ do_compress( compress_filter_context_t *zfx, z_stream *zs, int flush, IOBUF a ) 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", @@ -96,7 +101,7 @@ do_compress( compress_filter_context_t *zfx, z_stream *zs, int flush, IOBUF a ) if( iobuf_write( a, zfx->outbuf, n ) ) { log_debug("deflate: iobuf_write failed\n"); - return GPGERR_WRITE_FILE; + return G10ERR_WRITE_FILE; } } while( zs->avail_in || (flush == Z_FINISH && zrc != Z_STREAM_END) ); return 0; @@ -121,7 +126,7 @@ init_uncompress( compress_filter_context_t *zfx, z_stream *zs ) } zfx->inbufsize = 2048; - zfx->inbuf = gcry_xmalloc( zfx->inbufsize ); + zfx->inbuf = m_alloc( zfx->inbufsize ); zs->avail_in = 0; } @@ -143,7 +148,11 @@ do_uncompress( compress_filter_context_t *zfx, z_stream *zs, 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; @@ -196,12 +205,16 @@ compress_filter( void *opaque, int control, if( control == IOBUFCTRL_UNDERFLOW ) { if( !zfx->status ) { - zs = zfx->opaque = gcry_xcalloc( 1, sizeof *zs ); + zs = zfx->opaque = m_alloc_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 ); @@ -212,7 +225,9 @@ compress_filter( void *opaque, int control, PKT_compressed cd; if( !zfx->algo ) - zfx->algo = opt.def_compress_algo; + zfx->algo = DEFAULT_COMPRESS_ALGO; + if( zfx->algo != 1 && zfx->algo != 2 ) + BUG(); memset( &cd, 0, sizeof cd ); cd.len = 0; cd.algorithm = zfx->algo; @@ -221,37 +236,54 @@ compress_filter( void *opaque, int control, pkt.pkt.compressed = &cd; if( build_packet( a, &pkt )) log_bug("build_packet(PKT_COMPRESSED) failed\n"); - zs = zfx->opaque = gcry_xcalloc( 1, sizeof *zs ); + zs = zfx->opaque = m_alloc_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); - gcry_free(zs); + m_free(zs); zfx->opaque = NULL; - gcry_free(zfx->outbuf); zfx->outbuf = NULL; + m_free(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); - gcry_free(zs); + m_free(zs); zfx->opaque = NULL; - gcry_free(zfx->outbuf); zfx->outbuf = NULL; + m_free(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) +{ + m_free (ctx); +} + /**************** * Handle a compressed packet */ @@ -259,26 +291,19 @@ int handle_compressed( void *procctx, PKT_compressed *cd, int (*callback)(IOBUF, void *), void *passthru ) { - compress_filter_context_t cfx; + compress_filter_context_t *cfx; int rc; - memset( &cfx, 0, sizeof cfx ); if( cd->algorithm < 1 || cd->algorithm > 2 ) - return GPGERR_COMPR_ALGO; - cfx.algo = cd->algorithm; - - iobuf_push_filter( cd->buf, compress_filter, &cfx ); + return G10ERR_COMPR_ALGO; + cfx = m_alloc_clear (sizeof *cfx); + cfx->algo = cd->algorithm; + cfx->release = release_context; + iobuf_push_filter( cd->buf, compress_filter, cfx ); if( callback ) rc = callback(cd->buf, passthru ); else rc = proc_packets(procctx, cd->buf); - #if 0 - iobuf_pop_filter( cd->buf, compress_filter, &cfx ); - if( cd->len ) - iobuf_set_limit( cd->buf, 0 ); /* disable the readlimit */ - else - iobuf_clear_eof( cd->buf ); - #endif cd->buf = NULL; return rc; } diff --git a/g10/dearmor.c b/g10/dearmor.c index 937961e7f..4ec8fa012 100644 --- a/g10/dearmor.c +++ b/g10/dearmor.c @@ -1,5 +1,5 @@ /* dearmor.c - Armor utility - * Copyright (C) 1998, 1999, 2000 Free Software Foundation, Inc. + * Copyright (C) 1998, 1999, 2000, 2001 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -25,9 +25,9 @@ #include <errno.h> #include <assert.h> -#include <gcrypt.h> #include "errors.h" #include "iobuf.h" +#include "memory.h" #include "util.h" #include "filter.h" #include "packet.h" @@ -52,7 +52,7 @@ dearmor_file( const char *fname ) if( !(inp = iobuf_open(fname)) ) { log_error("can't open %s: %s\n", fname? fname: "[stdin]", strerror(errno) ); - rc = GPGERR_OPEN_FILE; + rc = G10ERR_OPEN_FILE; goto leave; } @@ -94,7 +94,7 @@ enarmor_file( const char *fname ) if( !(inp = iobuf_open(fname)) ) { log_error("can't open %s: %s\n", fname? fname: "[stdin]", strerror(errno) ); - rc = GPGERR_OPEN_FILE; + rc = G10ERR_OPEN_FILE; goto leave; } diff --git a/g10/decrypt.c b/g10/decrypt.c index 981275602..297ee3418 100644 --- a/g10/decrypt.c +++ b/g10/decrypt.c @@ -1,5 +1,5 @@ /* decrypt.c - verify signed data - * Copyright (C) 1998, 2000 Free Software Foundation, Inc. + * Copyright (C) 1998, 1999, 2000, 2001, 2002 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -25,14 +25,15 @@ #include <errno.h> #include <assert.h> -#include <gcrypt.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" @@ -57,7 +58,7 @@ decrypt_message( const char *filename ) fp = iobuf_open(filename); if( !fp ) { log_error(_("can't open `%s'\n"), print_fname_stdin(filename)); - return GPGERR_OPEN_FILE; + return G10ERR_OPEN_FILE; } if( !opt.no_armor ) { @@ -78,5 +79,60 @@ decrypt_message( const char *filename ) return rc; } +void +decrypt_messages(int nfiles, char **files) +{ + IOBUF fp; + armor_filter_context_t afx; + char *p, *output = NULL; + int rc = 0; + + if (opt.outfile) + { + log_error(_("--output doesn't work for this command\n")); + return; + + } + + while (nfiles--) + { + print_file_status(STATUS_FILE_START, *files, 3); + output = make_outfile_name(*files); + if (!output) + continue; + fp = iobuf_open(*files); + if (!fp) + { + log_error(_("can't open `%s'\n"), print_fname_stdin(*files)); + continue; + } + 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(*files), + g10_errstr(rc)); + p = get_last_passphrase(); + set_next_passphrase(p); + m_free (p); + files++; + m_free(output); + write_status( STATUS_FILE_DONE ); + } + set_next_passphrase(NULL); +} + + + + + + diff --git a/g10/delkey.c b/g10/delkey.c index f4bfd01e4..f9d882113 100644 --- a/g10/delkey.c +++ b/g10/delkey.c @@ -1,5 +1,5 @@ /* delkey.c - delete keys - * Copyright (C) 1998, 1999, 2000 Free Software Foundation, Inc. + * Copyright (C) 1998, 1999, 2000, 2001, 2002 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -31,7 +31,7 @@ #include "errors.h" #include "iobuf.h" #include "keydb.h" -#include <gcrypt.h> +#include "memory.h" #include "util.h" #include "main.h" #include "trustdb.h" @@ -43,34 +43,46 @@ /**************** * 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. */ -int -delete_key( const char *username, int secret ) +static int +do_delete_key( const char *username, int secret, int *r_sec_avail ) { int rc = 0; KBNODE keyblock = NULL; KBNODE node; - KBPOS kbpos; + 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; + + *r_sec_avail = 0; /* search the userid */ - rc = secret? find_secret_keyblock_byname( &keyblock, username ) - : find_keyblock_byname( &keyblock, username ); - if( rc ) { - log_error(_("%s: user not found: %s\n"), username, gpg_errstr(rc) ); + classify_user_id (username, &desc); + 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 = GPGERR_GENERAL; + rc = G10ERR_GENERAL; goto leave; } @@ -83,15 +95,12 @@ delete_key( const char *username, int secret ) keyid_from_pk( pk, keyid ); rc = seckey_available( keyid ); if( !rc ) { - log_error(_( - "there is a secret key for this public key!\n")); - log_info(_( - "use option \"--delete-secret-key\" to delete it first.\n")); - write_status_text( STATUS_DELETE_PROBLEM, "2" ); - rc = -1; + *r_sec_avail = 1; + rc = -1; + goto leave; } - else if( rc != GPGERR_NO_SECKEY ) { - log_error("%s: get secret key: %s\n", username, gpg_errstr(rc) ); + else if( rc != G10ERR_NO_SECKEY ) { + log_error("%s: get secret key: %s\n", username, g10_errstr(rc) ); } else rc = 0; @@ -113,15 +122,15 @@ delete_key( const char *username, int secret ) tty_printf("sec %4u%c/%08lX %s ", nbits_from_sk( sk ), pubkey_letter( sk->pubkey_algo ), - keyid[1], datestr_from_sk(sk) ); + (ulong)keyid[1], datestr_from_sk(sk) ); else tty_printf("pub %4u%c/%08lX %s ", nbits_from_pk( pk ), pubkey_letter( pk->pubkey_algo ), - keyid[1], datestr_from_pk(pk) ); + (ulong)keyid[1], datestr_from_pk(pk) ); p = get_user_id( keyid, &n ); tty_print_utf8_string( p, n ); - gcry_free(p); + m_free(p); tty_printf("\n\n"); yes = cpr_get_answer_is_yes( secret? "delete_key.secret.okay" @@ -142,16 +151,59 @@ delete_key( const char *username, int secret ) if( okay ) { - #warning MUST FIX THIS!!! - rc = delete_keyblock( &kbpos ); - if( rc ) { - log_error("delete_keyblock failed: %s\n", gpg_errstr(rc) ); + 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_ownertrust (pk)) { + if (opt.verbose) + log_info (_("ownertrust information cleared\n")); + } } leave: - release_kbnode( keyblock ); + 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; + + for(;names;names=names->next) { + rc = do_delete_key (names->d, secret, &avail ); + if ( rc && avail ) { + if ( allow_both ) { + rc = do_delete_key (names->d, 1, &avail ); + if ( !rc ) + rc = do_delete_key (names->d, 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 index f033c76ae..80a9039ec 100644 --- a/g10/encode.c +++ b/g10/encode.c @@ -1,5 +1,5 @@ /* encode.c - encode data - * Copyright (C) 1998, 1999, 2000 Free Software Foundation, Inc. + * Copyright (C) 1998, 1999, 2000, 2001, 2002 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -30,69 +30,18 @@ #include "errors.h" #include "iobuf.h" #include "keydb.h" -#include <gcrypt.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 ); +static int encode_simple( const char *filename, int mode, int compat ); static int write_pubkey_enc_from_list( PK_LIST pk_list, DEK *dek, IOBUF out ); -/**************** - * Emulate our old PK interface here - sometime in the future we might - * change the internal design to directly fit to libgcrypt. - */ -static int -pk_encrypt( int algo, MPI *resarr, MPI data, MPI *pkey ) -{ - GCRY_SEXP s_ciph, s_data, s_pkey; - int rc; - - /* make a sexp from pkey */ - if( algo == GCRY_PK_ELG || algo == GCRY_PK_ELG_E ) { - rc = gcry_sexp_build ( &s_pkey, NULL, - "(public-key(elg(p%m)(g%m)(y%m)))", - pkey[0], pkey[1], pkey[2] ); - } - else - return GPGERR_PUBKEY_ALGO; - - if ( rc ) - BUG (); - - /* put the data into a simple list */ - if ( gcry_sexp_build( &s_data, NULL, "%m", data ) ) - BUG (); - - /* pass it to libgcrypt */ - rc = gcry_pk_encrypt( &s_ciph, s_data, s_pkey ); - gcry_sexp_release( s_data ); - gcry_sexp_release( s_pkey ); - - if( rc ) - ; - else { /* add better error handling or make gnupg use S-Exp directly */ - GCRY_SEXP list = gcry_sexp_find_token( s_ciph, "a" , 0 ); - assert( list ); - resarr[0] = gcry_sexp_nth_mpi( list, 1, 0 ); - assert( resarr[0] ); - gcry_sexp_release ( list ); - - list = gcry_sexp_find_token( s_ciph, "b" , 0 ); - assert( list ); - resarr[1] = gcry_sexp_nth_mpi( list, 1, 0 ); - assert( resarr[1] ); - gcry_sexp_release ( list ); - } - - gcry_sexp_release( s_ciph ); - return rc; -} - /**************** * Encode FILENAME with only the symmetric cipher. Take input from @@ -101,7 +50,11 @@ pk_encrypt( int algo, MPI *resarr, MPI data, MPI *pkey ) int encode_symmetric( const char *filename ) { - return encode_simple( filename, 1 ); + int compat = 1; + + if ( opt.expert ) + compat = 0; /* PGP knows how to handle this mode. */ + return encode_simple( filename, 1, compat ); } /**************** @@ -111,19 +64,49 @@ encode_symmetric( const char *filename ) int encode_store( const char *filename ) { - return encode_simple( filename, 0 ); + return encode_simple( filename, 0, 1 ); } - +static void +encode_sesskey( DEK *dek, DEK **ret_dek, byte *enckey ) +{ + CIPHER_HANDLE hd; + DEK *c; + byte buf[33]; + + assert ( dek->keylen < 32 ); + + c = m_alloc_clear( sizeof *c ); + c->keylen = dek->keylen; + c->algo = dek->algo; + make_session_key( c ); + /*log_hexdump( "thekey", c->key, c->keylen );*/ + + buf[0] = c->algo; + memcpy( buf + 1, c->key, c->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, c->keylen + 1 ); + cipher_close( hd ); + + memcpy( enckey, buf, c->keylen + 1 ); + memset( buf, 0, sizeof buf ); /* burn key */ + *ret_dek = c; +} static int -encode_simple( const char *filename, int mode ) +encode_simple( const char *filename, int mode, int compat ) { IOBUF inp, out; PACKET pkt; + DEK *dek = NULL; 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; @@ -136,40 +119,62 @@ encode_simple( const char *filename, int mode ) memset( &zfx, 0, sizeof zfx); memset( &tfx, 0, sizeof tfx); init_packet(&pkt); + + if (opt.compress == -1 && is_file_compressed(filename, &rc)) + { + if (opt.verbose) + log_info(_("`%s' already compressed\n"), filename); + do_compress = 0; + } + if (rc) + return rc; /* prepare iobufs */ if( !(inp = iobuf_open(filename)) ) { log_error(_("%s: can't open: %s\n"), filename? filename: "[stdin]", strerror(errno) ); - return GPGERR_OPEN_FILE; + return G10ERR_OPEN_FILE; } 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 ( opt.rfc1991 && !compat ) + compat = 1; + cfx.dek = NULL; if( mode ) { - s2k = gcry_xcalloc( 1, sizeof *s2k ); + s2k = m_alloc_clear( sizeof *s2k ); s2k->mode = opt.rfc1991? 0:opt.s2k_mode; s2k->hash_algo = opt.def_digest_algo ? opt.def_digest_algo : opt.s2k_digest_algo; cfx.dek = passphrase_to_dek( NULL, 0, - opt.def_cipher_algo ? opt.def_cipher_algo - : opt.s2k_cipher_algo , s2k, 2 ); + opt.def_cipher_algo ? opt.def_cipher_algo + : opt.s2k_cipher_algo , s2k, 2, NULL ); if( !cfx.dek || !cfx.dek->keylen ) { - rc = GPGERR_PASSPHRASE; - gcry_free(cfx.dek); - gcry_free(s2k); + rc = G10ERR_PASSPHRASE; + m_free(cfx.dek); + m_free(s2k); iobuf_close(inp); - log_error(_("error creating passphrase: %s\n"), gpg_errstr(rc) ); + log_error(_("error creating passphrase: %s\n"), g10_errstr(rc) ); return rc; } + if ( !compat ) { + seskeylen = cipher_get_keylen( opt.def_cipher_algo ? + opt.def_cipher_algo: + opt.s2k_cipher_algo ) / 8; + encode_sesskey( cfx.dek, &dek, enckey ); + m_free( cfx.dek ); cfx.dek = dek; + } } if( (rc = open_outfile( filename, opt.armor? 1:0, &out )) ) { iobuf_cancel(inp); - gcry_free(cfx.dek); - gcry_free(s2k); + m_free(cfx.dek); + m_free(s2k); return rc; } @@ -184,15 +189,19 @@ encode_simple( const char *filename, int mode ) } #endif if( s2k && !opt.rfc1991 ) { - PKT_symkey_enc *enc = gcry_xcalloc( 1, sizeof *enc ); + PKT_symkey_enc *enc = m_alloc_clear( sizeof *enc + seskeylen + 1 ); enc->version = 4; enc->cipher_algo = cfx.dek->algo; enc->s2k = *s2k; + if ( !compat && 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", gpg_errstr(rc) ); - gcry_free(enc); + log_error("build symkey packet failed: %s\n", g10_errstr(rc) ); + m_free(enc); } if (!opt.no_literal) { @@ -200,30 +209,36 @@ encode_simple( const char *filename, int mode ) if( filename || opt.set_filename ) { char *s = make_basename( opt.set_filename ? opt.set_filename : filename ); - pt = gcry_xmalloc( sizeof *pt + strlen(s) - 1 ); + pt = m_alloc( sizeof *pt + strlen(s) - 1 ); pt->namelen = strlen(s); memcpy(pt->name, s, pt->namelen ); - gcry_free(s); + m_free(s); } else { /* no filename */ - pt = gcry_xmalloc( sizeof *pt - 1 ); + pt = m_alloc( sizeof *pt - 1 ); pt->namelen = 0; } } - /* pgp5 has problems to decrypt symmetrically encrypted data from - * GnuPG if the filelength is in the inner packet. It works - * when only partial length headers are use. Until we have - * tracked this problem down. We use this temporary fix - * (fixme: remove the && !mode ) - */ - if( filename && !opt.textmode && !mode ) { + /* 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( filename && !opt.textmode ) { if( !(filesize = iobuf_get_filelength(inp)) ) log_info(_("%s: WARNING: empty file\n"), filename ); /* we can't yet encode the length of very large files, - * so we switch to partial length encoding in this case */ + * so we switch to partial lengthn encoding in this case */ if ( filesize >= IOBUF_FILELENGTH_LIMIT ) filesize = 0; + } else filesize = opt.set_filesize ? opt.set_filesize : 0; /* stdin */ @@ -239,7 +254,11 @@ encode_simple( const char *filename, int mode ) cfx.datalen = filesize && !do_compress ? calc_packet_length( &pkt ) : 0; } else - cfx.datalen = filesize && !do_compress ? filesize : 0; + { + cfx.datalen = filesize && !do_compress ? filesize : 0; + pkt.pkttype = 0; + pkt.pkt.generic = NULL; + } /* register the cipher filter */ if( mode ) @@ -251,7 +270,7 @@ encode_simple( const char *filename, int mode ) /* do the work */ if (!opt.no_literal) { if( (rc = build_packet( out, &pkt )) ) - log_error("build_packet failed: %s\n", gpg_errstr(rc) ); + log_error("build_packet failed: %s\n", g10_errstr(rc) ); } else { /* user requested not to create a literal packet, @@ -260,8 +279,8 @@ encode_simple( const char *filename, int mode ) int bytes_copied; while ((bytes_copied = iobuf_read(inp, copy_buffer, 4096)) != -1) if (iobuf_write(out, copy_buffer, bytes_copied) == -1) { - rc = GPGERR_WRITE_FILE; - log_error("copying input to output failed: %s\n", gpg_errstr(rc) ); + rc = G10ERR_WRITE_FILE; + log_error("copying input to output failed: %s\n", g10_errstr(rc) ); break; } memset(copy_buffer, 0, 4096); /* burn buffer */ @@ -271,13 +290,16 @@ encode_simple( const char *filename, int mode ) iobuf_close(inp); if (rc) iobuf_cancel(out); - else + else { iobuf_close(out); /* fixme: check returncode */ + if (mode) + write_status( STATUS_END_ENCRYPTION ); + } if (pt) pt->buf = NULL; free_packet(&pkt); - gcry_free(cfx.dek); - gcry_free(s2k); + m_free(cfx.dek); + m_free(s2k); return rc; } @@ -291,123 +313,203 @@ encode_crypt( const char *filename, STRLIST remusr ) IOBUF inp = NULL, out = NULL; PACKET pkt; PKT_plaintext *pt = NULL; - int rc = 0; + 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; - encrypt_filter_context_t efx; - PK_LIST pk_list; + PK_LIST pk_list,work_list; int do_compress = opt.compress && !opt.rfc1991; + + memset( &cfx, 0, sizeof cfx); memset( &afx, 0, sizeof afx); memset( &zfx, 0, sizeof zfx); memset( &tfx, 0, sizeof tfx); - memset( &efx, 0, sizeof efx); init_packet(&pkt); - if( (rc=build_pk_list( remusr, &pk_list, GCRY_PK_USAGE_ENCR)) ) + if( (rc=build_pk_list( remusr, &pk_list, PUBKEY_USAGE_ENC)) ) return rc; + if(opt.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")); + log_info(_("this message may not be usable by PGP 2.x\n")); + opt.pgp2=0; + break; + } + } + + if (opt.compress == -1 && is_file_compressed(filename, &rc2)) + { + if (opt.verbose) + log_info(_("`%s' already compressed\n"), filename); + do_compress = 0; + } + if (rc2) + { + rc = rc2; + goto leave; + } + /* prepare iobufs */ if( !(inp = iobuf_open(filename)) ) { log_error(_("can't open %s: %s\n"), filename? filename: "[stdin]", strerror(errno) ); - rc = GPGERR_OPEN_FILE; + rc = G10ERR_OPEN_FILE; goto leave; } else if( opt.verbose ) log_info(_("reading from `%s'\n"), filename? filename: "[stdin]"); - /* If the user selected textmode, push the text filter onto the input */ if( opt.textmode ) iobuf_push_filter( inp, text_filter, &tfx ); - /* Now we can create the outputfile */ if( (rc = open_outfile( filename, opt.armor? 1:0, &out )) ) goto leave; - /* The first thing we have to push on the output stream - * is the armor filter */ + if( opt.armor ) iobuf_push_filter( out, armor_filter, &afx ); + #ifdef ENABLE_COMMENT_PACKETS + else { + write_comment( out, "#created by GNUPG v" VERSION " (" + PRINTABLE_OS_NAME ")"); + if( opt.comment_string ) + write_comment( out, opt.comment_string ); + } + #endif + /* create a session key */ + cfx.dek = m_alloc_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( opt.pgp2 ) { + log_info(_("unable to use the IDEA cipher for all of the keys " + "you are encrypting to.\n")); + log_info(_("this message may not be usable by PGP 2.x\n")); + opt.pgp2=0; + } + } + } + else { + if(!opt.expert && + select_algo_from_prefs(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); + + cfx.dek->algo = opt.def_cipher_algo; + } + cfx.dek->use_mdc = select_mdc_from_pklist (pk_list); - /* Prepare the plaintext packet */ - { - if (!opt.no_literal) { - if( filename || opt.set_filename ) { - char *s = make_basename( opt.set_filename ? - opt.set_filename : filename ); - pt = gcry_xmalloc( sizeof *pt + strlen(s) - 1 ); - pt->namelen = strlen(s); - memcpy(pt->name, s, pt->namelen ); - gcry_free(s); - } - else { /* no filename */ - pt = gcry_xmalloc( sizeof *pt - 1 ); - pt->namelen = 0; - } - } - - if( filename && !opt.textmode ) { - if( !(filesize = iobuf_get_filelength(inp)) ) - log_info(_("%s: WARNING: empty file\n"), filename ); - /* we can't yet encode the length of very large files, - * so we switch to partial lengthn encoding in this case */ - if ( filesize >= IOBUF_FILELENGTH_LIMIT ) - 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 && !opt.rfc1991; - pt->buf = inp; - pkt.pkttype = PKT_PLAINTEXT; - pkt.pkt.plaintext = pt; - efx.cfx.datalen = filesize && !do_compress? - calc_packet_length( &pkt ) : 0; - } - else - efx.cfx.datalen = filesize && !do_compress ? filesize : 0; - } /* end preparation of plaintext packet */ - - /* push in the actual encryption filter */ - efx.pk_list = pk_list; - iobuf_push_filter( out, encrypt_filter, &efx ); - - /* register the compress filter (so that it is done before encryption) */ + 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; + + if (!opt.no_literal) { + /* setup the inner packet */ + if( filename || opt.set_filename ) { + char *s = make_basename( opt.set_filename ? opt.set_filename : filename ); + pt = m_alloc( sizeof *pt + strlen(s) - 1 ); + pt->namelen = strlen(s); + memcpy(pt->name, s, pt->namelen ); + m_free(s); + } + else { /* no filename */ + pt = m_alloc( sizeof *pt - 1 ); + pt->namelen = 0; + } + } + + if( filename && !opt.textmode ) { + if( !(filesize = iobuf_get_filelength(inp)) ) + log_info(_("%s: WARNING: empty file\n"), filename ); + /* we can't yet encode the length of very large files, + * so we switch to partial length encoding in this case */ + if ( filesize >= IOBUF_FILELENGTH_LIMIT ) + 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 && !opt.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 = select_algo_from_prefs( pk_list, PREFTYPE_COMPR ); - if( !compr_algo ) - ; /* don't use compression */ - else { - if( compr_algo == 1 ) - zfx.algo = 1; /* default is 2 */ + int compr_algo = opt.def_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; + } + else if(!opt.expert && + select_algo_from_prefs(pk_list,PREFTYPE_ZIP, + compr_algo,NULL)!=compr_algo) + log_info(_("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 ) + { + zfx.algo = compr_algo; iobuf_push_filter( out, compress_filter, &zfx ); - } + } } /* do the work */ if (!opt.no_literal) { if( (rc = build_packet( out, &pkt )) ) - log_error("build_packet failed: %s\n", gpg_errstr(rc) ); + 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 */ + /* 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 = GPGERR_WRITE_FILE; - log_error("copying input to output failed: %s\n", gpg_errstr(rc) ); + rc = G10ERR_WRITE_FILE; + log_error("copying input to output failed: %s\n", g10_errstr(rc) ); break; } - memset(copy_buffer, 0, DIM(copy_buffer)); /* burn buffer */ + memset(copy_buffer, 0, 4096); /* burn buffer */ } /* finish the stuff */ @@ -415,13 +517,14 @@ encode_crypt( const char *filename, STRLIST remusr ) iobuf_close(inp); if( rc ) iobuf_cancel(out); - else + else { iobuf_close(out); /* fixme: check returncode */ + write_status( STATUS_END_ENCRYPTION ); + } if( pt ) pt->buf = NULL; free_packet(&pkt); - gcry_free(efx.cfx.dek); /* Hmmm, why does the encrypt filter does not - * take care about this? */ + m_free(cfx.dek); release_pk_list( pk_list ); return rc; } @@ -430,7 +533,7 @@ encode_crypt( const char *filename, STRLIST remusr ) /**************** - * Filter to handle the entire public key encryption. + * Filter to do a complete public key encryption. */ int encrypt_filter( void *opaque, int control, @@ -445,16 +548,32 @@ encrypt_filter( void *opaque, int control, } else if( control == IOBUFCTRL_FLUSH ) { /* encrypt */ if( !efx->header_okay ) { - efx->cfx.dek = gcry_xmalloc_secure( sizeof *efx->cfx.dek ); + efx->cfx.dek = m_alloc_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 ); - if( efx->cfx.dek->algo == -1 ) + 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; } - else - efx->cfx.dek->algo = opt.def_cipher_algo; + + efx->cfx.dek->use_mdc = select_mdc_from_pklist (efx->pk_list); + make_session_key( efx->cfx.dek ); if( DBG_CIPHER ) log_hexdump("DEK is: ", @@ -497,7 +616,7 @@ write_pubkey_enc_from_list( PK_LIST pk_list, DEK *dek, IOBUF out ) pk = pk_list->pk; print_pubkey_algo_note( pk->pubkey_algo ); - enc = gcry_xcalloc( 1, sizeof *enc ); + enc = m_alloc_clear( sizeof *enc ); enc->pubkey_algo = pk->pubkey_algo; keyid_from_pk( pk, enc->keyid ); enc->throw_keyid = opt.throw_keyid; @@ -517,17 +636,17 @@ write_pubkey_enc_from_list( PK_LIST pk_list, DEK *dek, IOBUF out ) */ frame = encode_session_key( dek, pubkey_nbits( pk->pubkey_algo, pk->pkey ) ); - rc = pk_encrypt( pk->pubkey_algo, enc->data, frame, pk->pkey ); - mpi_release( frame ); + rc = pubkey_encrypt( pk->pubkey_algo, enc->data, frame, pk->pkey ); + mpi_free( frame ); if( rc ) - log_error("pubkey_encrypt failed: %s\n", gpg_errstr(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"), - gcry_pk_algo_name(enc->pubkey_algo), - gcry_cipher_algo_name(dek->algo), ustr ); - gcry_free(ustr); + pubkey_algo_to_string(enc->pubkey_algo), + cipher_algo_to_string(dek->algo), ustr ); + m_free(ustr); } /* and write it */ init_packet(&pkt); @@ -535,7 +654,7 @@ write_pubkey_enc_from_list( PK_LIST pk_list, DEK *dek, IOBUF out ) pkt.pkt.pubkey_enc = enc; rc = build_packet( out, &pkt ); if( rc ) - log_error("build_packet(pubkey_enc) failed: %s\n", gpg_errstr(rc)); + log_error("build_packet(pubkey_enc) failed: %s\n", g10_errstr(rc)); } free_pubkey_enc(enc); if( rc ) @@ -544,3 +663,47 @@ write_pubkey_enc_from_list( PK_LIST pk_list, DEK *dek, IOBUF out ) 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)) ) + log_error("%s: encryption failed: %s\n", + print_fname_stdin(line), g10_errstr(rc) ); + write_status( STATUS_FILE_DONE ); + } + } + else + { + while (nfiles--) + { + print_file_status(STATUS_FILE_START, *files, 2); + if ( (rc = encode_crypt(*files, remusr)) ) + log_error("%s: encryption failed: %s\n", + print_fname_stdin(*files), g10_errstr(rc) ); + write_status( STATUS_FILE_DONE ); + files++; + } + } +} diff --git a/g10/encr-data.c b/g10/encr-data.c index 17d43e9d6..c8a8c85db 100644 --- a/g10/encr-data.c +++ b/g10/encr-data.c @@ -1,5 +1,5 @@ /* encr-data.c - process an encrypted data packet - * Copyright (C) 1998, 1999, 2000 Free Software Foundation, Inc. + * Copyright (C) 1998, 1999, 2000, 2001 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -23,10 +23,11 @@ #include <stdlib.h> #include <string.h> #include <assert.h> - -#include <gcrypt.h> #include "util.h" +#include "memory.h" #include "packet.h" +#include "mpi.h" +#include "cipher.h" #include "options.h" #include "i18n.h" @@ -37,8 +38,8 @@ static int decode_filter( void *opaque, int control, IOBUF a, byte *buf, size_t *ret_len); typedef struct { - GCRY_CIPHER_HD cipher_hd; - GCRY_MD_HD mdc_hash; + CIPHER_HANDLE cipher_hd; + MD_HANDLE mdc_hash; char defer[20]; int defer_filled; int eof_seen; @@ -55,20 +56,21 @@ decrypt_data( void *procctx, PKT_encrypted *ed, DEK *dek ) byte *p; int rc=0, c, i; byte temp[32]; - unsigned int blocksize; - unsigned int nprefix; + unsigned blocksize; + unsigned nprefix; memset( &dfx, 0, sizeof dfx ); - if( gcry_cipher_test_algo( dek->algo ) ) { - if( opt.verbose ) + 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 ); - rc = GPGERR_CIPHER_ALGO; - goto leave; + dek->algo_info_printed = 1; } - if( opt.verbose ) - log_info(_("%s encrypted data\n"), gcry_cipher_algo_name( dek->algo ) ); - - blocksize = gcry_cipher_get_algo_blklen( dek->algo ); + 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; @@ -76,36 +78,28 @@ decrypt_data( void *procctx, PKT_encrypted *ed, DEK *dek ) BUG(); if( ed->mdc_method ) { - dfx.mdc_hash = gcry_md_open( ed->mdc_method, 0 ); + dfx.mdc_hash = md_open( ed->mdc_method, 0 ); if ( DBG_HASHING ) - gcry_md_start_debug(dfx.mdc_hash, "checkmdc"); + md_start_debug(dfx.mdc_hash, "checkmdc"); } - if( !(dfx.cipher_hd = gcry_cipher_open( dek->algo, - GCRY_CIPHER_MODE_CFB, - GCRY_CIPHER_SECURE - | ((ed->mdc_method || dek->algo >= 100)? - 0 : GCRY_CIPHER_ENABLE_SYNC) )) - ) { - /* we should never get an error here cause we already checked, that - * the algorithm is available. What about a flag to let the function - * die in this case? */ - BUG(); - } - - + 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 = gcry_cipher_setkey( dfx.cipher_hd, dek->key, dek->keylen ); - if( rc == GCRYERR_WEAK_KEY ) { + 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", gcry_strerror(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; + } - gcry_cipher_setiv( dfx.cipher_hd, NULL, 0 ); + cipher_setiv( dfx.cipher_hd, NULL, 0 ); if( ed->len ) { for(i=0; i < (nprefix+2) && ed->len; i++, ed->len-- ) { @@ -122,17 +116,17 @@ decrypt_data( void *procctx, PKT_encrypted *ed, DEK *dek ) else temp[i] = c; } - gcry_cipher_decrypt( dfx.cipher_hd, temp, nprefix+2, NULL, 0 ); - gcry_cipher_sync( dfx.cipher_hd ); + cipher_decrypt( dfx.cipher_hd, temp, temp, nprefix+2); + cipher_sync( dfx.cipher_hd ); p = temp; /* log_hexdump( "prefix", temp, nprefix+2 ); */ if( p[nprefix-2] != p[nprefix] || p[nprefix-1] != p[nprefix+1] ) { - rc = GPGERR_BAD_KEY; + rc = G10ERR_BAD_KEY; goto leave; } if( dfx.mdc_hash ) - gcry_md_write( dfx.mdc_hash, temp, nprefix+2 ); + md_write( dfx.mdc_hash, temp, nprefix+2 ); if( ed->mdc_method ) iobuf_push_filter( ed->buf, mdc_decode_filter, &dfx ); @@ -142,21 +136,23 @@ decrypt_data( void *procctx, PKT_encrypted *ed, DEK *dek ) proc_packets( procctx, ed->buf ); ed->buf = NULL; if( ed->mdc_method && dfx.eof_seen == 2 ) - rc = GPGERR_INVALID_PACKET; + rc = G10ERR_INVALID_PACKET; else if( ed->mdc_method ) { /* check the mdc */ - int datalen = gcry_md_get_algo_dlen( ed->mdc_method ); + int datalen = md_digest_length( ed->mdc_method ); - gcry_cipher_decrypt( dfx.cipher_hd, dfx.defer, 20, NULL, 0); + cipher_decrypt( dfx.cipher_hd, dfx.defer, dfx.defer, 20); + md_final( dfx.mdc_hash ); if( datalen != 20 - || memcmp(gcry_md_read( dfx.mdc_hash, 0 ), dfx.defer, datalen) ) - rc = GPGERR_BAD_SIGN; + || memcmp(md_read( dfx.mdc_hash, 0 ), dfx.defer, datalen) ) + rc = G10ERR_BAD_SIGN; /*log_hexdump("MDC calculated:", md_read( dfx.mdc_hash, 0), datalen);*/ /*log_hexdump("MDC message :", dfx.defer, 20);*/ } + leave: - gcry_cipher_close(dfx.cipher_hd); - gcry_md_close( dfx.mdc_hash ); + cipher_close(dfx.cipher_hd); + md_close( dfx.mdc_hash ); return rc; } @@ -222,8 +218,8 @@ mdc_decode_filter( void *opaque, int control, IOBUF a, } if( n ) { - gcry_cipher_decrypt( dfx->cipher_hd, buf, n, NULL, 0); - gcry_md_write( dfx->mdc_hash, buf, n ); + cipher_decrypt( dfx->cipher_hd, buf, buf, n); + md_write( dfx->mdc_hash, buf, n ); } else { assert( dfx->eof_seen ); @@ -249,7 +245,7 @@ decode_filter( void *opaque, int control, IOBUF a, byte *buf, size_t *ret_len) n = iobuf_read( a, buf, size ); if( n == -1 ) n = 0; if( n ) - gcry_cipher_decrypt( fc->cipher_hd, buf, n, NULL, 0 ); + cipher_decrypt( fc->cipher_hd, buf, buf, n); else rc = -1; /* eof */ *ret_len = n; diff --git a/g10/exec.c b/g10/exec.c new file mode 100644 index 000000000..229c968bc --- /dev/null +++ b/g10/exec.c @@ -0,0 +1,579 @@ +/* exec.c - generic call-a-program code + * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, 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; } + +#else /* ! NO_EXEC */ + +#ifndef HAVE_MKDTEMP +char *mkdtemp(char *template); +#endif + +#if defined (__MINGW32__) || defined (__CYGWIN32__) +/* 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=m_strdup(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); + m_free(string); + + return 0; +} +#endif + +/* 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->binary?"tempin" EXTSEP_S "bin":"tempin" EXTSEP_S "txt"; + + nameout=info->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 (__MINGW32__) || defined (__CYGWIN32__) + tmp=m_alloc(256); + if(GetTempPath(256,tmp)==0) + 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=m_alloc(strlen(tmp)+strlen(DIRSEP_S)+10+1); + + sprintf(info->tempdir,"%s" DIRSEP_S "gpg-XXXXXX",tmp); + +#if defined (__MINGW32__) || defined (__CYGWIN32__) + m_free(tmp); +#endif + + if(mkdtemp(info->tempdir)==NULL) + log_error(_("%s: can't create directory: %s\n"), + info->tempdir,strerror(errno)); + else + { + info->madedir=1; + + info->tempfile_in=m_alloc(strlen(info->tempdir)+ + strlen(DIRSEP_S)+strlen(namein)+1); + sprintf(info->tempfile_in,"%s" DIRSEP_S "%s",info->tempdir,namein); + + if(!info->writeonly) + { + info->tempfile_out=m_alloc(strlen(info->tempdir)+ + strlen(DIRSEP_S)+strlen(nameout)+1); + sprintf(info->tempfile_out,"%s" DIRSEP_S "%s",info->tempdir,nameout); + } + } + + return info->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; + int size,len; + + info->use_temp_files=0; + info->keep_temp_files=0; + + if(DBG_EXTPROG) + log_debug("expanding string \"%s\"\n",args_in); + + size=100; + info->command=m_alloc(size); + len=0; + info->command[0]='\0'; + + while(*ch!='\0') + { + if(*ch=='%') + { + char *append=NULL; + + ch++; + + switch(*ch) + { + case 'O': + info->keep_temp_files=1; + /* fall through */ + + case 'o': /* out */ + if(!info->madedir) + { + if(make_tempdir(info)) + goto fail; + } + append=info->tempfile_out; + info->use_temp_files=1; + break; + + case 'I': + info->keep_temp_files=1; + /* fall through */ + + case 'i': /* in */ + if(!info->madedir) + { + if(make_tempdir(info)) + goto fail; + } + append=info->tempfile_in; + info->use_temp_files=1; + break; + + case '%': + append="%"; + break; + } + + if(append) + { + while(strlen(append)+len>size-1) + { + size+=100; + info->command=m_realloc(info->command,size); + } + + strcat(info->command,append); + len+=strlen(append); + } + } + else + { + if(len==size-1) /* leave room for the \0 */ + { + size+=100; + info->command=m_realloc(info->command,size); + } + + info->command[len++]=*ch; + info->command[len]='\0'; + } + + ch++; + } + + if(DBG_EXTPROG) + log_debug("args expanded to \"%s\", use %d, keep %d\n", + info->command,info->use_temp_files,info->keep_temp_files); + + return 0; + + fail: + + m_free(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=m_alloc_clear(sizeof(struct exec_info)); + + if(name) + (*info)->name=m_strdup(name); + (*info)->binary=binary; + (*info)->writeonly=writeonly; + + /* Expand the args, if any */ + if(args_in && expand_args(*info,args_in)) + goto fail; + +#ifdef EXEC_TEMPFILE_ONLY + if(!(*info)->use_temp_files) + { + log_error(_("this platform requires temp 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)->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)->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,NULL); + } + else + { + if(DBG_EXTPROG) + log_debug("execlp: %s -c %s\n",shell,(*info)->command); + + execlp(shell,shell,"-c",(*info)->command,NULL); + } + + /* If we get this far the exec failed. Clean up and return. */ + + log_error(_("unable to execute %s \"%s\": %s\n"), + args_in==NULL?"program":"shell", + args_in==NULL?program: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 */ + (*info)->tochild=fopen((*info)->tempfile_in,binary?"wb":"w"); + if((*info)->tochild==NULL) + { + log_error(_("%s: can't create: %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->use_temp_files) + { + if(DBG_EXTPROG) + log_debug("system() command is %s\n",info->command); + +#if defined (__MINGW32__) || defined (__CYGWIN32__) + 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->writeonly) + { + info->fromchild=iobuf_open(info->tempfile_out); + 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->madedir && !info->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)); + } + + m_free(info->command); + m_free(info->name); + m_free(info->tempdir); + m_free(info->tempfile_in); + m_free(info->tempfile_out); + m_free(info); + + return ret; +} +#endif /* ! NO_EXEC */ diff --git a/g10/exec.h b/g10/exec.h new file mode 100644 index 000000000..2e0be460b --- /dev/null +++ b/g10/exec.h @@ -0,0 +1,22 @@ +#ifndef _EXEC_H_ +#define _EXEC_H_ + +#include <unistd.h> +#include <stdio.h> +#include "iobuf.h" + +struct exec_info +{ + int progreturn,binary,writeonly,madedir,use_temp_files,keep_temp_files; + 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); + +#endif /* !_EXEC_H_ */ diff --git a/g10/export.c b/g10/export.c index 9a9cd9859..47d06e651 100644 --- a/g10/export.c +++ b/g10/export.c @@ -1,5 +1,5 @@ /* export.c - * Copyright (C) 1998, 1999, 2000 Free Software Foundation, Inc. + * Copyright (C) 1998, 1999, 2000, 2001, 2002 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -25,11 +25,11 @@ #include <errno.h> #include <assert.h> -#include <gcrypt.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" @@ -83,8 +83,10 @@ do_export( STRLIST users, int secret, int onlyrfc ) 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 ) @@ -94,6 +96,8 @@ do_export( STRLIST users, int secret, int onlyrfc ) afx.what = secret?5:1; iobuf_push_filter( out, armor_filter, &afx ); } + if( opt.compress_keys && opt.compress ) + iobuf_push_filter( out, compress_filter, &zfx ); rc = do_export_stream( out, users, secret, onlyrfc, &any ); if( rc || !any ) @@ -108,54 +112,58 @@ static int do_export_stream( IOBUF out, STRLIST users, int secret, int onlyrfc, int *any ) { int rc = 0; - compress_filter_context_t zfx; PACKET pkt; KBNODE keyblock = NULL; KBNODE kbctx, node; - KBPOS kbpos; + int ndesc; + KEYDB_SEARCH_DESC *desc = NULL; + KEYDB_HANDLE kdbhd; STRLIST sl; - int all = !users; *any = 0; - memset( &zfx, 0, sizeof zfx); init_packet( &pkt ); + kdbhd = keydb_new (secret); - if( opt.compress_keys && opt.compress ) - iobuf_push_filter( out, compress_filter, &zfx ); + if (!users) { + ndesc = 1; + desc = m_alloc_clear ( ndesc * sizeof *desc); + desc[0].mode = KEYDB_SEARCH_MODE_FIRST; + } + else { + for (ndesc=0, sl=users; sl; sl = sl->next, ndesc++) + ; + desc = m_alloc ( 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)); + } - if( all ) { - rc = enum_keyblocks_begin( &kbpos, secret ); - if( rc ) { - if( rc != -1 ) - log_error("enum_keyblocks_begin failed: %s\n", gpg_errstr(rc)); - goto leave; - } - all = 2; + /* 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 */ } - /* use the correct sequence. strlist_last,prev do work correctly with - * NULL pointers :-) */ - for( sl=strlist_last(users); sl || all ; sl=strlist_prev( users, sl )) { - if( all ) { /* get the next user */ - rc = enum_keyblocks_next( kbpos, 1, &keyblock ); - if( rc == -1 ) /* EOF */ - break; - if( rc ) { - log_error("enum_keyblocks_next failed: %s\n", gpg_errstr(rc)); - break; - } - } - else { - /* search the userid */ - rc = secret? find_secret_keyblock_byname( &keyblock, sl->d ) - : find_keyblock_byname( &keyblock, sl->d ); - if( rc ) { - log_error(_("%s: user not found: %s\n"), sl->d, gpg_errstr(rc)); - rc = 0; - continue; - } - } + while (!(rc = keydb_search (kdbhd, desc, ndesc))) { + int sha1_warned=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; + } /* do not export keys which are incompatible with rfc2440 */ if( onlyrfc && (node = find_kbnode( keyblock, PKT_PUBLIC_KEY )) ) { @@ -167,15 +175,29 @@ do_export_stream( IOBUF out, STRLIST users, int secret, int onlyrfc, int *any ) } } - /* we can't apply GNU mode 1001 on an unprotected key */ - if( secret == 2 - && (node = find_kbnode( keyblock, PKT_SECRET_KEY )) - && !node->pkt->pkt.secret_key->is_protected ) - { - log_info(_("key %08lX: not protected - skipped\n"), - (ulong)keyid_from_sk( node->pkt->pkt.secret_key, NULL) ); - continue; - } + node=find_kbnode( keyblock, PKT_SECRET_KEY ); + if(node) + { + 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 %08lX: not protected - skipped\n"), + (ulong)sk_keyid[1]); + continue; + } + + /* no v3 keys with GNU mode 1001 */ + if( secret == 2 && sk->version == 3 ) + { + log_info(_("key %08lX: PGP 2.x style key - skipped\n"), + (ulong)sk_keyid[1]); + continue; + } + } /* and write it */ for( kbctx=NULL; (node = walk_kbnode( keyblock, &kbctx, 0 )); ) { @@ -183,13 +205,30 @@ do_export_stream( IOBUF out, STRLIST users, int secret, int onlyrfc, int *any ) * secret keyring */ if( !secret && node->pkt->pkttype == PKT_COMMENT ) continue; - /* do not export packets which are marked as not exportable */ + /* make sure that ring_trust packets never get exported */ + if (node->pkt->pkttype == PKT_RING_TRUST) + continue; + if( node->pkt->pkttype == PKT_SIGNATURE ) { - const char *p; - p = parse_sig_subpkt2( node->pkt->pkt.signature, - SIGSUBPKT_EXPORTABLE, NULL ); - if( p && !*p ) - continue; /* not exportable */ + /* do not export packets which are marked as not exportable */ + if( !node->pkt->pkt.signature->flags.exportable ) + continue; /* not exportable */ + + /* do not export packets with a "sensitive" revocation + key. This will need revisiting when we start + supporting creating revocation keys and not just + reading them. */ + if( 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) + continue; + } + + /* delete our verification cache */ + delete_sig_subpkt (node->pkt->pkt.signature->unhashed, + SIGSUBPKT_PRIV_VERIFY_CACHE); } if( secret == 2 && node->pkt->pkttype == PKT_SECRET_KEY ) { @@ -202,13 +241,28 @@ do_export_stream( IOBUF out, STRLIST users, int secret, int onlyrfc, int *any ) node->pkt->pkt.secret_key->protect.s2k.mode = save_mode; } 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 %08lX does not have a " + "simple SK checksum\n",(ulong)sk_keyid[1]); + + sha1_warned=1; + } + rc = build_packet( out, node->pkt ); } if( rc ) { log_error("build_packet(%d) failed: %s\n", - node->pkt->pkttype, gpg_errstr(rc) ); - rc = GPGERR_WRITE_FILE; + node->pkt->pkttype, g10_errstr(rc) ); + rc = G10ERR_WRITE_FILE; goto leave; } } @@ -218,8 +272,8 @@ do_export_stream( IOBUF out, STRLIST users, int secret, int onlyrfc, int *any ) rc = 0; leave: - if( all == 2 ) - enum_keyblocks_end( kbpos ); + m_free(desc); + keydb_release (kdbhd); release_kbnode( keyblock ); if( !*any ) log_info(_("WARNING: nothing exported\n")); diff --git a/g10/filter.h b/g10/filter.h index a29d2aa29..b7a99e6bc 100644 --- a/g10/filter.h +++ b/g10/filter.h @@ -1,5 +1,5 @@ /* filter.h - * Copyright (C) 1998, 1999, 2000 Free Software Foundation, Inc. + * Copyright (C) 1998, 1999, 2000, 2001 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -17,18 +17,15 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA */ -#ifndef GPG_FILTER_H -#define GPG_FILTER_H - -#include <gcrypt.h> - -#include "basicdefs.h" -#include "iobuf.h" +#ifndef G10_FILTER_H +#define G10_FILTER_H +#include "types.h" +#include "cipher.h" typedef struct { - GCRY_MD_HD md; /* catch all */ - GCRY_MD_HD md2; /* if we want to calculate an alternate hash */ + MD_HANDLE md; /* catch all */ + MD_HANDLE md2; /* if we want to calculate an alternate hash */ size_t maxbuf_size; } md_filter_context_t; @@ -67,8 +64,11 @@ typedef struct { int pending_lf; /* used together with faked */ } armor_filter_context_t; +struct unarmor_pump_s; +typedef struct unarmor_pump_s *UnarmorPump; -typedef struct { + +struct compress_filter_context_s { int status; void *opaque; /* (used for z_stream) */ byte *inbuf; @@ -77,26 +77,22 @@ typedef struct { unsigned outbufsize; int algo; /* compress algo */ int algo1hack; -} compress_filter_context_t; + void (*release)(struct compress_filter_context_s*); +}; +typedef struct compress_filter_context_s compress_filter_context_t; typedef struct { DEK *dek; u32 datalen; - GCRY_CIPHER_HD cipher_hd; + CIPHER_HANDLE cipher_hd; int header; - GCRY_MD_HD mdc_hash; + MD_HANDLE mdc_hash; byte enchash[20]; int create_mdc; /* flag will be set by the cipher filter */ } cipher_filter_context_t; -typedef struct { - int header_okay; - PK_LIST pk_list; - cipher_filter_context_t cfx; -} encrypt_filter_context_t; - typedef struct { byte *buffer; /* malloced buffer */ @@ -106,12 +102,14 @@ typedef struct { int truncated; /* number of truncated lines */ int not_dash_escaped; int escape_from; - GCRY_MD_HD md; + MD_HANDLE md; int pending_lf; int pending_esc; } text_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 ); @@ -120,6 +118,9 @@ void free_md_filter_context( md_filter_context_t *mfx ); 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 --*/ int compress_filter( void *opaque, int control, @@ -132,9 +133,9 @@ int cipher_filter( void *opaque, int control, /*-- 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, GCRY_MD_HD md, +int copy_clearsig_text( IOBUF out, IOBUF inp, MD_HANDLE md, int escape_dash, int escape_from, int pgp2mode ); -#endif /*GPG_FILTER_H*/ +#endif /*G10_FILTER_H*/ diff --git a/g10/free-packet.c b/g10/free-packet.c index 2cc4e25e9..e760999be 100644 --- a/g10/free-packet.c +++ b/g10/free-packet.c @@ -1,5 +1,5 @@ /* free-packet.c - cleanup stuff for packets - * Copyright (C) 1998, 1999, 2000 Free Software Foundation, Inc. + * Copyright (C) 1998, 1999, 2000, 2001, 2002 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -24,17 +24,18 @@ #include <string.h> #include <assert.h> -#include <gcrypt.h> #include "packet.h" #include "iobuf.h" +#include "mpi.h" #include "util.h" +#include "cipher.h" +#include "memory.h" #include "options.h" -#include "main.h" void free_symkey_enc( PKT_symkey_enc *enc ) { - gcry_free(enc); + m_free(enc); } void @@ -43,24 +44,27 @@ free_pubkey_enc( PKT_pubkey_enc *enc ) int n, i; n = pubkey_get_nenc( enc->pubkey_algo ); if( !n ) - mpi_release(enc->data[0]); + mpi_free(enc->data[0]); for(i=0; i < n; i++ ) - mpi_release( enc->data[i] ); - gcry_free(enc); + mpi_free( enc->data[i] ); + m_free(enc); } void free_seckey_enc( PKT_signature *sig ) { - int n, i; - n = pubkey_get_nsig( sig->pubkey_algo ); - if( !n ) - mpi_release(sig->data[0]); - for(i=0; i < n; i++ ) - mpi_release( sig->data[i] ); - gcry_free(sig->hashed_data); - gcry_free(sig->unhashed_data); - gcry_free(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] ); + + m_free(sig->revkey); + m_free(sig->hashed); + m_free(sig->unhashed); + m_free(sig); } @@ -71,15 +75,28 @@ release_public_key_parts( PKT_public_key *pk ) int n, i; n = pubkey_get_npkey( pk->pubkey_algo ); if( !n ) - mpi_release(pk->pkey[0]); + mpi_free(pk->pkey[0]); for(i=0; i < n; i++ ) { - mpi_release( pk->pkey[i] ); + mpi_free( pk->pkey[i] ); pk->pkey[i] = NULL; } + if (pk->prefs) { + m_free (pk->prefs); + pk->prefs = NULL; + } if( pk->namehash ) { - gcry_free(pk->namehash); + m_free(pk->namehash); pk->namehash = NULL; } + if (pk->user_id) { + free_user_id (pk->user_id); + pk->user_id = NULL; + } + if (pk->revkey) { + m_free(pk->revkey); + pk->revkey=NULL; + pk->numrevkeys=0; + } } @@ -87,42 +104,60 @@ void free_public_key( PKT_public_key *pk ) { release_public_key_parts( pk ); - gcry_free(pk); + m_free(pk); } -static void * -cp_data_block( byte *s ) +static subpktarea_t * +cp_subpktarea (subpktarea_t *s ) { - byte *d; - u16 len; + subpktarea_t *d; if( !s ) return NULL; - len = (s[0] << 8) | s[1]; - d = gcry_xmalloc( len+2 ); - memcpy(d, s, len+2); + d = m_alloc (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 = m_alloc ( 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_new_namehash( PKT_public_key *d, PKT_public_key *s, - const byte *namehash ) +copy_public_key ( PKT_public_key *d, PKT_public_key *s) { int n, i; if( !d ) - d = gcry_xmalloc(sizeof *d); + d = m_alloc(sizeof *d); memcpy( d, s, sizeof *d ); - if( namehash ) { - d->namehash = gcry_xmalloc( 20 ); - memcpy(d->namehash, namehash, 20 ); - } - else if( s->namehash ) { - d->namehash = gcry_xmalloc( 20 ); - memcpy(d->namehash, s->namehash, 20 ); - } + 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]); @@ -130,16 +165,17 @@ copy_public_key_new_namehash( PKT_public_key *d, PKT_public_key *s, 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 = m_alloc(sizeof(struct revocation_key)*s->numrevkeys); + memcpy(d->revkey,s->revkey,sizeof(struct revocation_key)*s->numrevkeys); + } + else + d->revkey = NULL; return d; } -PKT_public_key * -copy_public_key( PKT_public_key *d, PKT_public_key *s ) -{ - return copy_public_key_new_namehash( d, s, NULL ); -} - - /**************** * 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 @@ -151,7 +187,6 @@ 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->created = pk->created; sk->req_usage = pk->req_usage; sk->req_algo = pk->req_algo; sk->has_expired = pk->has_expired; @@ -163,15 +198,13 @@ copy_public_parts_to_secret_key( PKT_public_key *pk, PKT_secret_key *sk ) sk->keyid[1] = pk->keyid[1]; } - - PKT_signature * copy_signature( PKT_signature *d, PKT_signature *s ) { int n, i; if( !d ) - d = gcry_xmalloc(sizeof *d); + d = m_alloc(sizeof *d); memcpy( d, s, sizeof *d ); n = pubkey_get_nsig( s->pubkey_algo ); if( !n ) @@ -180,19 +213,27 @@ copy_signature( PKT_signature *d, PKT_signature *s ) for(i=0; i < n; i++ ) d->data[i] = mpi_copy( s->data[i] ); } - d->hashed_data = cp_data_block(s->hashed_data); - d->unhashed_data = cp_data_block(s->unhashed_data); + 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 * -copy_user_id( PKT_user_id *d, PKT_user_id *s ) +scopy_user_id (PKT_user_id *s) { - if( !d ) - d = gcry_xmalloc(sizeof *d + s->len - 1 ); - memcpy( d, s, sizeof *d + s->len - 1 ); - return d; + if (s) + s->ref++; + return s; } @@ -204,9 +245,9 @@ release_secret_key_parts( PKT_secret_key *sk ) n = pubkey_get_nskey( sk->pubkey_algo ); if( !n ) - mpi_release(sk->skey[0]); + mpi_free(sk->skey[0]); for(i=0; i < n; i++ ) { - mpi_release( sk->skey[i] ); + mpi_free( sk->skey[i] ); sk->skey[i] = NULL; } } @@ -215,7 +256,7 @@ void free_secret_key( PKT_secret_key *sk ) { release_secret_key_parts( sk ); - gcry_free(sk); + m_free(sk); } PKT_secret_key * @@ -224,7 +265,7 @@ copy_secret_key( PKT_secret_key *d, PKT_secret_key *s ) int n, i; if( !d ) - d = gcry_xmalloc(sizeof *d); + d = m_alloc(sizeof *d); memcpy( d, s, sizeof *d ); n = pubkey_get_nskey( s->pubkey_algo ); if( !n ) @@ -239,15 +280,32 @@ copy_secret_key( PKT_secret_key *d, PKT_secret_key *s ) void free_comment( PKT_comment *rem ) { - gcry_free(rem); + m_free(rem); } void -free_user_id( PKT_user_id *uid ) +free_attributes(PKT_user_id *uid) { - if( uid->photo ) - gcry_free( uid->photo ); - gcry_free(uid); + m_free(uid->attribs); + m_free(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); + + if (uid->prefs) + m_free (uid->prefs); + m_free (uid); } void @@ -259,7 +317,7 @@ free_compressed( PKT_compressed *zd ) while( iobuf_read( zd->buf, NULL, 1<<30 ) != -1 ) ; } - gcry_free(zd); + m_free(zd); } void @@ -280,7 +338,7 @@ free_encrypted( PKT_encrypted *ed ) } } } - gcry_free(ed); + m_free(ed); } @@ -302,7 +360,7 @@ free_plaintext( PKT_plaintext *pt ) } } } - gcry_free(pt); + m_free(pt); } /**************** @@ -345,13 +403,14 @@ free_packet( PACKET *pkt ) 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: - gcry_free( pkt->pkt.generic ); + m_free( pkt->pkt.generic ); break; } pkt->pkt.generic = NULL; @@ -460,23 +519,29 @@ cmp_signatures( PKT_signature *a, PKT_signature *b ) } - /**************** * Returns: true if the user ids do not match */ int cmp_user_ids( PKT_user_id *a, PKT_user_id *b ) { - int res; + 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 ); + } - res = a->len - b->len; - if( !res ) - res = memcmp( a->name, b->name, a->len ); return res; } - - - - - - @@ -1,5 +1,5 @@ -/* gpg.c - The GnuPG utility (main for gpg) - * Copyright (C) 1998, 1999, 2000 Free Software Foundation, Inc. +/* g10.c - The GnuPG utility (main for gpg) + * Copyright (C) 1998,1999,2000,2001,2002 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -29,22 +29,23 @@ #include <fcntl.h> /* for setmode() */ #endif - -#include <gcrypt.h> +#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 "gnupg-defs.h" -#include "hkp.h" - +#include "g10defs.h" +#include "keyserver-internal.h" enum cmd_and_opt_values { aNull = 0, oArmor = 'a', @@ -52,6 +53,7 @@ enum cmd_and_opt_values { aNull = 0, aSym = 'c', aDecrypt = 'd', aEncr = 'e', + aEncrFiles, oInteractive = 'i', oKOption = 'k', oDryRun = 'n', @@ -65,16 +67,25 @@ enum cmd_and_opt_values { aNull = 0, oCompress = 'z', oNotation = 'N', oBatch = 500, + oSigNotation, + oCertNotation, + oShowNotation, + oNoShowNotation, + aDecryptFiles, aClearsign, aStore, aKeygen, aSignEncr, + aSignSym, aSignKey, aLSignKey, + aNRSignKey, + aNRLSignKey, aListPackets, aEditKey, - aDeleteKey, - aDeleteSecretKey, + aDeleteKeys, + aDeleteSecretKeys, + aDeleteSecretAndPublicKeys, aKMode, aKModeC, aImport, @@ -86,16 +97,17 @@ enum cmd_and_opt_values { aNull = 0, aListSecretKeys, aSendKeys, aRecvKeys, + aSearchKeys, aExport, aExportAll, aExportSecret, aExportSecretSub, aCheckKeys, aGenRevoke, + aDesigRevoke, aPrimegen, aPrintMD, aPrintMDs, - aPrintHMAC, aCheckTrustDB, aUpdateTrustDB, aFixTrustDB, @@ -106,14 +118,25 @@ enum cmd_and_opt_values { aNull = 0, aDeArmor, aEnArmor, aGenRandom, + aPipeMode, + aRebuildKeydbCaches, + aRefreshKeys, oTextmode, + oExpert, + oNoExpert, + oAskSigExpire, + oNoAskSigExpire, + oAskCertExpire, + oNoAskCertExpire, oFingerprint, oWithFingerprint, oAnswerYes, oAnswerNo, + oDefCertCheckLevel, oKeyring, oSecretKeyring, + oShowKeyring, oDefaultKey, oDefRecipient, oDefRecipientSelf, @@ -122,7 +145,15 @@ enum cmd_and_opt_values { aNull = 0, oDebug, oDebugAll, oStatusFD, - oNoComment, +#ifdef __riscos__ + oStatusFile, +#endif /* __riscos__ */ + oAttributeFD, +#ifdef __riscos__ + oAttributeFile, +#endif /* __riscos__ */ + oSKComments, + oNoSKComments, oNoVersion, oEmitVersion, oCompletesNeeded, @@ -131,14 +162,29 @@ enum cmd_and_opt_values { aNull = 0, oLoadExtension, oRFC1991, oOpenPGP, + oPGP2, + oNoPGP2, + oPGP6, + oNoPGP6, + oPGP7, + oNoPGP7, oCipherAlgo, oDigestAlgo, + oCertDigestAlgo, oCompressAlgo, oPasswdFD, +#ifdef __riscos__ + oPasswdFile, +#endif /* __riscos__ */ oCommandFD, +#ifdef __riscos__ + oCommandFile, +#endif /* __riscos__ */ + oQuickRandom, oNoVerbose, oTrustDBName, oNoSecmemWarn, + oNoPermissionWarn, oNoArmor, oNoDefKeyring, oNoGreeting, @@ -152,47 +198,100 @@ enum cmd_and_opt_values { aNull = 0, oCompressKeys, oCompressSigs, oAlwaysTrust, + oEmuChecksumBug, oRunAsShmCP, oSetFilename, + oForYourEyesOnly, + oNoForYourEyesOnly, oSetPolicyURL, + oSigPolicyURL, + oCertPolicyURL, + oShowPolicyURL, + oNoShowPolicyURL, oUseEmbeddedFilename, oComment, oDefaultComment, oThrowKeyid, + oShowPhotos, + oNoShowPhotos, + oPhotoViewer, oForceV3Sigs, + oNoForceV3Sigs, + oForceV4Certs, + oNoForceV4Certs, oForceMDC, + oNoForceMDC, + oDisableMDC, + oNoDisableMDC, oS2KMode, oS2KDigest, oS2KCipher, + oSimpleSKChecksum, oCharset, oNotDashEscaped, oEscapeFrom, + oNoEscapeFrom, oLockOnce, oLockMultiple, oLockNever, oKeyServer, + oKeyServerOptions, + oTempDir, + oExecPath, oEncryptTo, oNoEncryptTo, oLoggerFD, +#ifdef __riscos__ + oLoggerFile, +#endif /* __riscos__ */ oUtf8Strings, oNoUtf8Strings, oDisableCipherAlgo, oDisablePubkeyAlgo, oAllowNonSelfsignedUID, + oNoAllowNonSelfsignedUID, oAllowFreeformUID, + oNoAllowFreeformUID, + oAllowSecretKeyImport, + oEnableSpecialFilenames, oNoLiteral, oSetFilesize, oHonorHttpProxy, oFastListMode, oListOnly, oIgnoreTimeConflict, + oIgnoreValidFrom, + oIgnoreCrcError, + oShowSessionKey, + oOverrideSessionKey, oNoRandomSeedFile, + oAutoKeyRetrieve, oNoAutoKeyRetrieve, oUseAgent, + oNoUseAgent, + oGpgAgentInfo, oMergeOnly, oTryAllSecrets, oTrustedKey, + oNoExpensiveTrustChecks, + oFixedListMode, + oNoSigCache, + oNoSigCreateCheck, + oAutoCheckTrustDB, + oNoAutoCheckTrustDB, + oPreservePermissions, + oDefaultPreferenceList, + oPersonalCipherPreferences, + oPersonalDigestPreferences, + oPersonalCompressPreferences, + oEmu3DESS2KBug, /* will be removed in 1.1 */ oEmuMDEncodeBug, + oDisplay, + oTTYname, + oTTYtype, + oLCctype, + oLCmessages, + oGroup, aTest }; @@ -204,9 +303,11 @@ static ARGPARSE_OPTS opts[] = { { 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, N_("|[files]|encrypt files")}, { aSym, "symmetric", 256, N_("encryption only with symmetric cipher")}, { aStore, "store", 256, N_("store only")}, { aDecrypt, "decrypt", 256, N_("decrypt data (default)")}, + { aDecryptFiles, "decrypt-files", 256, N_("|[files]|decrypt files")}, { aVerify, "verify" , 256, N_("verify a signature")}, { aVerifyFiles, "verify-files" , 256, "@" }, { aListKeys, "list-keys", 256, N_("list keys")}, @@ -216,16 +317,23 @@ static ARGPARSE_OPTS opts[] = { { 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")}, - { aDeleteKey, "delete-key",256, N_("remove key from the public keyring")}, - { aDeleteSecretKey, "delete-secret-key",256, - N_("remove key from the secret keyring")}, + { 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")}, + { aNRSignKey, "nrsign-key" ,256, N_("sign a key non-revocably")}, + { aNRLSignKey, "nrlsign-key" ,256, N_("sign a key locally and non-revocably")}, { 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")}, { aExportAll, "export-all" , 256, "@" }, { aExportSecret, "export-secret-keys" , 256, "@" }, { aExportSecretSub, "export-secret-subkeys" , 256, "@" }, @@ -239,14 +347,13 @@ static ARGPARSE_OPTS opts[] = { { aUpdateTrustDB, "update-trustdb",0 , N_("update the trust database")}, { aCheckTrustDB, - "check-trustdb",0 , N_("|[NAMES]|check the trust database")}, + "check-trustdb",0 , N_("unattended trust database update")}, { aFixTrustDB, "fix-trustdb",0 , N_("fix a corrupted trust database")}, { aDeArmor, "dearmor", 256, N_("De-Armor a file or stdin") }, { aDeArmor, "dearmour", 256, "@" }, { aEnArmor, "enarmor", 256, N_("En-Armor a file or stdin") }, { aEnArmor, "enarmour", 256, "@" }, { aPrintMD, "print-md" , 256, N_("|algo [files]|print message digests")}, - { aPrintHMAC, "print-hmac" , 256, "@"}, { aPrimegen, "gen-prime" , 256, "@" }, { aGenRandom, "gen-random" , 256, "@" }, @@ -261,52 +368,92 @@ static ARGPARSE_OPTS opts[] = { { oDefRecipientSelf, "default-recipient-self" ,0, N_("use the default key as default recipient")}, { oNoDefRecipient, "no-default-recipient", 0, "@" }, + { oTempDir, "temp-directory", 2, "@" }, + { oExecPath, "exec-path", 2, "@" }, { oEncryptTo, "encrypt-to", 2, "@" }, { oNoEncryptTo, "no-encrypt-to", 0, "@" }, { oUser, "local-user",2, N_("use this user-id to sign or decrypt")}, { oCompress, NULL, 1, N_("|N|set compress level N (0 disables)") }, { oTextmodeShort, NULL, 0, "@"}, { oTextmode, "textmode", 0, N_("use canonical text mode")}, + { oExpert, "expert", 0, "@"}, + { oNoExpert, "no-expert", 0, "@"}, + { oAskSigExpire, "ask-sig-expire", 0, "@"}, + { oNoAskSigExpire, "no-ask-sig-expire", 0, "@"}, + { oAskCertExpire, "ask-cert-expire", 0, "@"}, + { oNoAskCertExpire, "no-ask-cert-expire", 0, "@"}, { oOutput, "output", 2, N_("use as output file")}, { oVerbose, "verbose", 0, N_("verbose") }, { oQuiet, "quiet", 0, N_("be somewhat more quiet") }, { oNoTTY, "no-tty", 0, N_("don't use the terminal at all") }, { oForceV3Sigs, "force-v3-sigs", 0, N_("force v3 signatures") }, + { oNoForceV3Sigs, "no-force-v3-sigs", 0, N_("do not force v3 signatures") }, + { oForceV4Certs, "force-v4-certs", 0, N_("force v4 key signatures") }, + { oNoForceV4Certs, "no-force-v4-certs", 0, N_("do not force v4 key signatures") }, { oForceMDC, "force-mdc", 0, N_("always use a MDC for encryption") }, + { oNoForceMDC, "no-force-mdc", 0, "@" }, + { oDisableMDC, "disable-mdc", 0, N_("never use a MDC for encryption") }, + { 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, N_("use the gpg-agent")}, + { oNoUseAgent, "no-use-agent",0, "@"}, + { oGpgAgentInfo, "gpg-agent-info",2, "@"}, { oBatch, "batch", 0, N_("batch mode: never ask")}, { oAnswerYes, "yes", 0, N_("assume yes on most questions")}, { oAnswerNo, "no", 0, N_("assume no on most questions")}, { oKeyring, "keyring" ,2, N_("add this keyring to the list of keyrings")}, { oSecretKeyring, "secret-keyring" ,2, N_("add this secret keyring to the list")}, + { oShowKeyring, "show-keyring", 0, N_("show which keyring a listed key is on")}, { oDefaultKey, "default-key" ,2, N_("|NAME|use NAME as default secret key")}, { oKeyServer, "keyserver",2, N_("|HOST|use this keyserver to lookup keys")}, + { oKeyServerOptions, "keyserver-options",2,"@"}, { oCharset, "charset" , 2, N_("|NAME|set terminal charset to NAME") }, { oOptions, "options" , 2, N_("read options from file")}, { oDebug, "debug" ,4|16, "@"}, { oDebugAll, "debug-all" ,0, "@"}, { oStatusFD, "status-fd" ,1, N_("|FD|write status info to this FD") }, - { oNoComment, "no-comment", 0, "@"}, +#ifdef __riscos__ + { oStatusFile, "status-file" ,2, N_("|[file]|write status info to file") }, +#endif /* __riscos__ */ + { oAttributeFD, "attribute-fd" ,1, "@" }, +#ifdef __riscos__ + { oAttributeFile, "attribute-file" ,2, "@" }, +#endif /* __riscos__ */ + { oNoSKComments, "no-comment", 0, "@"}, + { oNoSKComments, "no-sk-comments", 0, "@"}, + { oSKComments, "sk-comments", 0, "@"}, { oCompletesNeeded, "completes-needed", 1, "@"}, { oMarginalsNeeded, "marginals-needed", 1, "@"}, { oMaxCertDepth, "max-cert-depth", 1, "@" }, - { oTrustedKey, "trusted-key", 2, N_("|KEYID|ulimately trust this key")}, + { oTrustedKey, "trusted-key", 2, N_("|KEYID|ultimately trust this key")}, { oLoadExtension, "load-extension" ,2, N_("|FILE|load extension module FILE")}, { oRFC1991, "rfc1991", 0, N_("emulate the mode described in RFC1991")}, { oOpenPGP, "openpgp", 0, N_("set all packet, cipher and digest options to OpenPGP behavior")}, + { oPGP2, "pgp2", 0, N_("set all packet, cipher and digest options to PGP 2.x behavior")}, + { oNoPGP2, "no-pgp2", 0, "@"}, + { oPGP6, "pgp6", 0, "@"}, + { oNoPGP6, "no-pgp6", 0, "@"}, + { oPGP7, "pgp7", 0, "@"}, + { oNoPGP7, "no-pgp7", 0, "@"}, { oS2KMode, "s2k-mode", 1, N_("|N|use passphrase mode N")}, { oS2KDigest, "s2k-digest-algo",2, N_("|NAME|use message digest algorithm NAME for passphrases")}, { oS2KCipher, "s2k-cipher-algo",2, N_("|NAME|use cipher algorithm NAME for passphrases")}, + { oSimpleSKChecksum, "simple-sk-checksum", 0, "@"}, { oCipherAlgo, "cipher-algo", 2 , N_("|NAME|use cipher algorithm NAME")}, { oDigestAlgo, "digest-algo", 2 , N_("|NAME|use message digest algorithm NAME")}, + { oCertDigestAlgo, "cert-digest-algo", 2 , "@" }, { oCompressAlgo, "compress-algo", 1 , N_("|N|use compress algorithm N")}, { oThrowKeyid, "throw-keyid", 0, N_("throw keyid field of encrypted packets")}, - { oNotation, "notation-data", 2, N_("|NAME=VALUE|use this notation data")}, + { oShowPhotos, "show-photos", 0, N_("Show Photo IDs")}, + { oNoShowPhotos, "no-show-photos", 0, N_("Don't show Photo IDs")}, + { oPhotoViewer, "photo-viewer", 2, N_("Set command line to view Photo IDs")}, + { oNotation, "notation-data", 2, "@" }, + { 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" @@ -324,12 +471,21 @@ static ARGPARSE_OPTS opts[] = { { aPrintMDs, "print-mds" , 256, "@"}, /* old */ { aListTrustDB, "list-trustdb",0 , "@"}, { aListTrustPath, "list-trust-path",0, "@"}, + { aPipeMode, "pipemode", 0, "@" }, { oKOption, NULL, 0, "@"}, { oPasswdFD, "passphrase-fd",1, "@" }, +#ifdef __riscos__ + { oPasswdFile, "passphrase-file",2, "@" }, +#endif /* __riscos__ */ { oCommandFD, "command-fd",1, "@" }, +#ifdef __riscos__ + { oCommandFile, "command-file",2, "@" }, +#endif /* __riscos__ */ + { oQuickRandom, "quick-random", 0, "@"}, { oNoVerbose, "no-verbose", 0, "@"}, { oTrustDBName, "trustdb-name", 2, "@" }, { oNoSecmemWarn, "no-secmem-warning", 0, "@" }, /* used only by regression tests */ + { oNoPermissionWarn, "no-permission-warning", 0, "@" }, { oNoArmor, "no-armor", 0, "@"}, { oNoArmor, "no-armour", 0, "@"}, { oNoDefKeyring, "no-default-keyring", 0, "@" }, @@ -345,20 +501,34 @@ static ARGPARSE_OPTS opts[] = { { oSkipVerify, "skip-verify",0, "@" }, { oCompressKeys, "compress-keys",0, "@"}, { oCompressSigs, "compress-sigs",0, "@"}, + { oDefCertCheckLevel, "default-cert-check-level", 1, "@"}, { oAlwaysTrust, "always-trust", 0, "@"}, + { oEmuChecksumBug, "emulate-checksum-bug", 0, "@"}, { 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, "@" }, + { oShowNotation, "show-notation", 0, "@" }, + { oNoShowNotation, "no-show-notation", 0, "@" }, { oComment, "comment", 2, "@" }, { oDefaultComment, "default-comment", 0, "@" }, { oNoVersion, "no-version", 0, "@"}, { oEmitVersion, "emit-version", 0, "@"}, { 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, "@" }, +#ifdef __riscos__ + { oLoggerFile, "logger-file",2, "@" }, +#endif /* __riscos__ */ { oUseEmbeddedFilename, "use-embedded-filename", 0, "@" }, { oUtf8Strings, "utf8-strings", 0, "@" }, { oNoUtf8Strings, "no-utf8-strings", 0, "@" }, @@ -366,47 +536,73 @@ static ARGPARSE_OPTS opts[] = { { 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,"@" }, + { 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, "@"}, + { oPersonalCipherPreferences, "personal-cipher-preferences", 2, "@"}, + { oPersonalDigestPreferences, "personal-digest-preferences", 2, "@"}, + { oPersonalCompressPreferences, "personal-compress-preferences", 2, "@"}, + { oEmu3DESS2KBug, "emulate-3des-s2k-bug", 0, "@"}, { oEmuMDEncodeBug, "emulate-md-encode-bug", 0, "@"}, + { oDisplay, "display", 2, "@" }, + { oTTYname, "ttyname", 2, "@" }, + { oTTYtype, "ttytype", 2, "@" }, + { oLCctype, "lc-ctype", 2, "@" }, + { oLCmessages, "lc-messages", 2, "@" }, + { oGroup, "group", 2, "@" }, {0} }; -int gpg_errors_seen = 0; +int g10_errors_seen = 0; static int utf8_strings = 0; static int maybe_setuid = 1; -static char *build_list( const char *text, +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_hex( byte *p, size_t n ); -static void print_mds( const char *fname, int algo, const char *key ); -static void add_notation_data( const char *string ); -static int check_policy_url( const char *s ); - +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 int -our_pk_test_algo( int algo ) -{ - return openpgp_pk_test_algo( algo, 0 ); -} +#ifdef __riscos__ +RISCOS_GLOBAL_STATICS("GnuPG Heap") +#endif /* __riscos__ */ -static const char * -my_strusage( int level ) +const char * +strusage( int level ) { - static char *digests, *pubkeys, *ciphers; + static char *digests, *pubkeys, *ciphers, *zips; const char *p; switch( level ) { case 11: p = "gpg (GnuPG)"; @@ -427,56 +623,90 @@ my_strusage( int level ) 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( !ciphers ) - ciphers = build_list("Cipher: ", gcry_cipher_algo_name, - openpgp_cipher_test_algo ); - p = ciphers; - break; - case 35: if( !pubkeys ) - pubkeys = build_list("Pubkey: ", gcry_pk_algo_name, - our_pk_test_algo ); + 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: ", gcry_md_algo_name, - openpgp_md_test_algo ); + digests = build_list("Hash: ", 'H', digest_algo_to_string, + check_digest_algo ); p = digests; break; + case 37: + if( !zips ) + zips = build_list("Compress: ",'Z',compress_algo_to_string, + check_compress_algo); + p = zips; + break; - - default: p = NULL; + default: p = default_strusage(level); } return p; } static char * -build_list( const char *text, const char * (*mapf)(int), int (*chkf)(int) ) +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; - - if( maybe_setuid ) { - gcry_control( GCRYCTL_DROP_PRIVS ); /* drop setuid */ - } - - for(i=1; i < 110; i++ ) - if( !chkf(i) ) - n += strlen(mapf(i)) + 2; - list = gcry_xmalloc( 21 + n ); *list = 0; - for(p=NULL, i=1; i < 110; i++ ) { - if( !chkf(i) ) { - if( !p ) + 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 = m_alloc( 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, ", "); - p = stpcpy(p, mapf(i) ); + + if(strlen(line)>60) { + int spaces=strlen(text); + + list=m_realloc(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 ) @@ -492,52 +722,20 @@ i18n_init(void) set_gettext_file( PACKAGE ); #else #ifdef ENABLE_NLS - #ifdef HAVE_LC_MESSAGES - setlocale( LC_TIME, "" ); - setlocale( LC_MESSAGES, "" ); - #else - setlocale( LC_ALL, "" ); - #endif - bindtextdomain( PACKAGE, GNUPG_LOCALEDIR ); + setlocale( LC_ALL, "" ); + bindtextdomain( PACKAGE, G10_LOCALEDIR ); textdomain( PACKAGE ); #endif #endif } - -static void -register_extension( const char *mainpgm, const char *fname ) -{ - #warning fixme add register cipher extension - /* Before we do so, we should design a beter API for this. - * I am currently thinking about using S-Exp to pass everything we - * need from the module to gcrypt. I hope we are not going to - * implement my-own-lisp-library-no-17000 */ - #if 0 - if( *fname != '/' ) { /* do tilde expansion etc */ - char *tmp; - - if( strchr(fname, '/') ) - tmp = make_filename(fname, NULL); - else - tmp = make_filename(GNUPG_LIBDIR, fname, NULL); - register_cipher_extension( mainpgm, tmp ); - gcry_free(tmp); - } - else - register_cipher_extension( mainpgm, fname ); - #endif -} - - - static void wrong_args( const char *text) { fputs(_("usage: gpg [options] "),stderr); fputs(text,stderr); putc('\n',stderr); - gpg_exit(2); + g10_exit(2); } @@ -546,7 +744,7 @@ make_username( const char *string ) { char *p; if( utf8_strings ) - p = gcry_xstrdup(string); + p = m_strdup(string); else p = native_to_utf8( string ); return p; @@ -556,19 +754,14 @@ make_username( const char *string ) static void set_debug(void) { - #if 0 - #warning memory debugging not enabled if( opt.debug & DBG_MEMORY_VALUE ) memory_debug_mode = 1; if( opt.debug & DBG_MEMSTAT_VALUE ) memory_stat_debug_mode = 1; - #endif - if( opt.debug & DBG_MPI_VALUE ) - gcry_control( GCRYCTL_SET_DEBUG_FLAGS, 2 ); + mpi_debug_mode = 1; if( opt.debug & DBG_CIPHER_VALUE ) - gcry_control( GCRYCTL_SET_DEBUG_FLAGS, 1 ); - + g10c_debug_mode = 1; if( opt.debug & DBG_IOBUF_VALUE ) iobuf_debug_mode = 1; @@ -586,6 +779,10 @@ set_cmd( enum cmd_and_opt_values *ret_cmd, enum cmd_and_opt_values new_cmd ) 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 == aKMode && new_cmd == aSym ) cmd = aKModeC; else if( ( cmd == aSign && new_cmd == aClearsign ) @@ -593,13 +790,39 @@ set_cmd( enum cmd_and_opt_values *ret_cmd, enum cmd_and_opt_values new_cmd ) cmd = aClearsign; else { log_error(_("conflicting commands\n")); - gpg_exit(2); + g10_exit(2); } *ret_cmd = cmd; } +static void add_group(char *string) +{ + char *name,*value; + struct groupitem *item; + STRLIST values=NULL; + + /* Break off the group name */ + name=strsep(&string," "); + if(string==NULL) + { + log_error(_("no values for group \"%s\"\n"),name); + return; + } + + /* Break apart the values */ + while((value=strsep(&string," ")) && *value!='\0') + add_to_strlist2(&values,value,utf8_strings); + + item=m_alloc(sizeof(struct groupitem)); + item->name=name; + item->values=values; + item->next=opt.grouplist; + + opt.grouplist=item; +} + int main( int argc, char **argv ) @@ -611,6 +834,8 @@ main( int argc, char **argv ) char **orig_argv; const char *fname; char *username; + STRLIST unsafe_files=NULL; + STRLIST extensions=NULL; int may_coredump; STRLIST sl, remusr= NULL, locusr=NULL; STRLIST nrings=NULL, sec_nrings=NULL; @@ -620,7 +845,7 @@ main( int argc, char **argv ) char *configname = NULL; unsigned configlineno; int parse_debug = 0; - int default_config =1; + int default_config = 1; int default_keyring = 1; int greeting = 0; int nogreeting = 0; @@ -629,30 +854,33 @@ main( int argc, char **argv ) const char *trustdb_name = NULL; char *def_cipher_string = NULL; char *def_digest_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 pwfd = -1; int with_fpr = 0; /* make an option out of --fingerprint */ + int any_explicit_recipient = 0; #ifdef USE_SHM_COPROCESSING ulong requested_shm_size=0; #endif + #ifdef __riscos__ + riscos_global_defaults(); + opt.lock_once = 1; + #endif /* __riscos__ */ + trap_unaligned(); - set_strusage( my_strusage ); - gcry_control( GCRYCTL_SUSPEND_SECMEM_WARN ); + 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"); - /* check that the libraries are suitable. Do it here because - * the option parse may need services of the library */ - if ( !gcry_check_version ( "1.1.0a" ) ) { - log_fatal(_("libgcrypt is too old (need %s, have %s)\n"), - VERSION, gcry_check_version(NULL) ); - } - - gcry_control( GCRYCTL_USE_SECURE_RNDPOOL ); + secure_random_alloc(); /* put random number into secure memory */ may_coredump = disable_core_dumps(); init_signals(); create_dotlock(NULL); /* register locking cleanup */ @@ -662,20 +890,23 @@ main( int argc, char **argv ) /* note: if you change these lines, look at oOpenPGP */ opt.def_cipher_algo = 0; opt.def_digest_algo = 0; - opt.def_compress_algo = 2; + opt.cert_digest_algo = 0; + opt.def_compress_algo = -1; opt.s2k_mode = 3; /* iterated+salted */ - opt.s2k_digest_algo = GCRY_MD_SHA1; - opt.s2k_cipher_algo = GCRY_CIPHER_CAST5; + opt.s2k_digest_algo = DIGEST_ALGO_SHA1; + opt.s2k_cipher_algo = CIPHER_ALGO_CAST5; opt.completes_needed = 1; opt.marginals_needed = 3; opt.max_cert_depth = 5; opt.pgp2_workarounds = 1; - opt.auto_key_retrieve = 1; - #ifdef __MINGW32__ + opt.force_v3_sigs = 1; + opt.escape_from = 1; + opt.keyserver_options.include_subkeys=1; +#if defined (__MINGW32__) || defined (__CYGWIN32__) opt.homedir = read_w32_registry_string( NULL, "Software\\GNU\\GnuPG", "HomeDir" ); - #else +#else opt.homedir = getenv("GNUPGHOME"); - #endif +#endif if( !opt.homedir || !*opt.homedir ) { opt.homedir = GNUPG_HOMEDIR; } @@ -709,19 +940,28 @@ main( int argc, char **argv ) 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( pargs.r.ret_int ); + set_status_fd( iobuf_translate_file_handle (pargs.r.ret_int, 1) ); } #endif } - - #ifdef USE_SHM_COPROCESSING +#ifdef HAVE_DOSISH_SYSTEM + if ( strchr (opt.homedir,'\\') ) { + char *d, *buf = m_alloc (strlen (opt.homedir)+1); + const char *s = opt.homedir; + for (d=buf,s=opt.homedir; *s; s++) + *d++ = *s == '\\'? '/': *s; + *d = 0; + opt.homedir = buf; + } +#endif +#ifdef USE_SHM_COPROCESSING if( opt.shm_coprocess ) { init_shm_coprocessing(requested_shm_size, 1 ); } - #endif +#endif /* initialize the secure memory. */ - gcry_control( GCRYCTL_INIT_SECMEM, 16384, 0 ); + secmem_init( 16384 ); maybe_setuid = 0; /* Okay, we are now working under our real uid */ @@ -735,6 +975,20 @@ main( int argc, char **argv ) pargs.flags= 1; /* do not remove the args */ next_pass: if( configname ) { + + if(check_permissions(configname,0,1)) + { + add_to_strlist(&unsafe_files,configname); + + /* 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 ) { @@ -746,9 +1000,9 @@ main( int argc, char **argv ) else { log_error(_("option file `%s': %s\n"), configname, strerror(errno) ); - gpg_exit(2); + g10_exit(2); } - gcry_free(configname); configname = NULL; + m_free(configname); configname = NULL; } if( parse_debug && configname ) log_info(_("reading options from `%s'\n"), configname ); @@ -764,37 +1018,47 @@ main( int argc, char **argv ) case aFastImport: set_cmd( &cmd, aFastImport); break; case aSendKeys: set_cmd( &cmd, aSendKeys); break; case aRecvKeys: set_cmd( &cmd, aRecvKeys); break; + case aSearchKeys: set_cmd( &cmd, aSearchKeys); break; + case aRefreshKeys: set_cmd( &cmd, aRefreshKeys); break; case aExport: set_cmd( &cmd, aExport); break; case aExportAll: set_cmd( &cmd, aExportAll); 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 aDeleteSecretKey: set_cmd( &cmd, aDeleteSecretKey); + case aDeleteSecretKeys: set_cmd( &cmd, aDeleteSecretKeys); greeting=1; break; - case aDeleteKey: set_cmd( &cmd, aDeleteKey); 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 aDecrypt: set_cmd( &cmd, aDecrypt); break; + case aDecryptFiles: set_cmd( &cmd, aDecryptFiles); break; case aEncr: set_cmd( &cmd, aEncr); break; + case aEncrFiles: set_cmd( &cmd, aEncrFiles ); 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 aNRSignKey: set_cmd( &cmd, aNRSignKey); break; + case aNRLSignKey: set_cmd( &cmd, aNRLSignKey); 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 aVerify: set_cmd( &cmd, aVerify); break; case aVerifyFiles: set_cmd( &cmd, aVerifyFiles); 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 aPrintHMAC: set_cmd( &cmd, aPrintHMAC); break; case aListTrustDB: set_cmd( &cmd, aListTrustDB); break; case aCheckTrustDB: set_cmd( &cmd, aCheckTrustDB); break; case aUpdateTrustDB: set_cmd( &cmd, aUpdateTrustDB); break; @@ -804,6 +1068,8 @@ main( int argc, char **argv ) case aEnArmor: set_cmd( &cmd, aEnArmor); break; case aExportOwnerTrust: set_cmd( &cmd, aExportOwnerTrust); break; case aImportOwnerTrust: set_cmd( &cmd, aImportOwnerTrust); break; + case aPipeMode: set_cmd( &cmd, aPipeMode); break; + case aRebuildKeydbCaches: set_cmd( &cmd, aRebuildKeydbCaches); break; case oArmor: opt.armor = 1; opt.no_armor=0; break; case oOutput: opt.outfile = pargs.r.ret_str; break; @@ -811,41 +1077,75 @@ main( int argc, char **argv ) case oNoTTY: tty_no_terminal(1); break; case oDryRun: opt.dry_run = 1; break; case oInteractive: opt.interactive = 1; break; - case oVerbose: - opt.verbose++; opt.list_sigs=1; - gcry_control( GCRYCTL_SET_VERBOSITY, (int)opt.verbose ); - break; + case oVerbose: g10_opt_verbose++; + opt.verbose++; opt.list_sigs=1; break; case oKOption: set_cmd( &cmd, aKMode ); break; - case oBatch: opt.batch = 1; greeting = 0; break; - case oUseAgent: opt.use_agent = 1; break; + case oBatch: opt.batch = 1; nogreeting = 1; break; + case oUseAgent: +#ifndef __riscos__ + opt.use_agent = 1; +#else /* __riscos__ */ + opt.use_agent = 0; + 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 oShowKeyring: opt.show_keyring = 1; break; case oDebug: opt.debug |= pargs.r.ret_ulong; break; case oDebugAll: opt.debug = ~0; break; - case oStatusFD: set_status_fd( pargs.r.ret_int ); break; - case oLoggerFD: log_set_logfile( NULL, pargs.r.ret_int ); break; + case oStatusFD: + set_status_fd( iobuf_translate_file_handle (pargs.r.ret_int, 1) ); + break; +#ifdef __riscos__ + case oStatusFile: + set_status_fd( iobuf_translate_file_handle ( fdopenfile (pargs.r.ret_str, 1), 1) ); + break; +#endif /* __riscos__ */ + case oAttributeFD: + set_attrib_fd(iobuf_translate_file_handle (pargs.r.ret_int, 1)); + break; +#ifdef __riscos__ + case oAttributeFile: + set_attrib_fd(iobuf_translate_file_handle ( fdopenfile (pargs.r.ret_str, 1), 1) ); + break; +#endif /* __riscos__ */ + case oLoggerFD: + log_set_logfile( NULL, + iobuf_translate_file_handle (pargs.r.ret_int, 1) ); + break; +#ifdef __riscos__ + case oLoggerFile: + log_set_logfile( NULL, + iobuf_translate_file_handle ( fdopenfile (pargs.r.ret_str, 1), 1) ); + break; +#endif /* __riscos__ */ case oWithFingerprint: - with_fpr=1; /*fall thru*/ + 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 ) { - gcry_free(configname); - configname = gcry_xstrdup(pargs.r.ret_str); + m_free(configname); + configname = m_strdup(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 oDefCertCheckLevel: opt.def_cert_check_level=pargs.r.ret_int; break; case oNoGreeting: nogreeting = 1; break; - case oNoVerbose: - opt.verbose = 0; opt.list_sigs=0; - gcry_control( GCRYCTL_SET_VERBOSITY, (int)opt.verbose ); - break; - case oNoComment: opt.no_comment=1; break; + case oNoVerbose: g10_opt_verbose = 0; + opt.verbose = 0; opt.list_sigs=0; break; + case oQuickRandom: quick_random_gen(1); break; + case oSKComments: opt.sk_comments=1; break; + case oNoSKComments: opt.sk_comments=0; break; case oNoVersion: opt.no_version=1; break; case oEmitVersion: opt.no_version=0; break; case oCompletesNeeded: opt.completes_needed = pargs.r.ret_int; break; @@ -858,15 +1158,15 @@ main( int argc, char **argv ) opt.def_recipient = make_username(pargs.r.ret_str); break; case oDefRecipientSelf: - gcry_free(opt.def_recipient); opt.def_recipient = NULL; + m_free(opt.def_recipient); opt.def_recipient = NULL; opt.def_recipient_self = 1; break; case oNoDefRecipient: - gcry_free(opt.def_recipient); opt.def_recipient = NULL; + m_free(opt.def_recipient); opt.def_recipient = NULL; opt.def_recipient_self = 0; break; - case oNoOptions: break; /* no-options */ - case oHomedir: opt.homedir = pargs.r.ret_str; 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; @@ -877,17 +1177,26 @@ main( int argc, char **argv ) case aListSecretKeys: set_cmd( &cmd, aListSecretKeys); break; case oAlwaysTrust: opt.always_trust = 1; break; case oLoadExtension: - register_extension(orig_argc? *orig_argv:NULL, pargs.r.ret_str); +#ifndef __riscos__ + add_to_strlist(&extensions,pargs.r.ret_str); + register_cipher_extension(orig_argc? *orig_argv:NULL, + pargs.r.ret_str); +#else /* __riscos__ */ + not_implemented("load-extension"); +#endif /* __riscos__ */ break; case oRFC1991: opt.rfc1991 = 1; opt.rfc2440 = 0; - opt.no_comment = 1; + opt.force_v4_certs = 0; + opt.sk_comments = 0; opt.escape_from = 1; break; case oOpenPGP: opt.rfc1991 = 0; opt.rfc2440 = 1; + opt.allow_non_selfsigned_uid = 1; + opt.allow_freeform_uid = 1; opt.pgp2_workarounds = 0; opt.escape_from = 0; opt.force_v3_sigs = 0; @@ -896,32 +1205,63 @@ main( int argc, char **argv ) opt.not_dash_escaped = 0; opt.def_cipher_algo = 0; opt.def_digest_algo = 0; + opt.cert_digest_algo = 0; opt.def_compress_algo = 1; - opt.s2k_mode = 3; /* iterated+salted */ - opt.s2k_digest_algo = GCRY_MD_SHA1; - opt.s2k_cipher_algo = GCRY_CIPHER_CAST5; + opt.s2k_mode = 3; /* iterated+salted */ + opt.s2k_digest_algo = DIGEST_ALGO_SHA1; + opt.s2k_cipher_algo = CIPHER_ALGO_CAST5; break; + case oPGP2: opt.pgp2 = 1; break; + case oNoPGP2: opt.pgp2 = 0; break; + case oPGP6: opt.pgp6 = 1; break; + case oNoPGP6: opt.pgp6 = 0; break; + case oPGP7: opt.pgp7 = 1; break; + case oNoPGP7: opt.pgp7 = 0; break; + case oEmuChecksumBug: opt.emulate_bugs |= EMUBUG_GPGCHKSUM; break; + case oEmu3DESS2KBug: opt.emulate_bugs |= EMUBUG_3DESS2K; break; case oEmuMDEncodeBug: opt.emulate_bugs |= EMUBUG_MDENCODE; break; case oCompressSigs: opt.compress_sigs = 1; 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__ */ + not_implemented("run-as-shm-coprocess"); +#endif /* __riscos__ */ break; case oSetFilename: opt.set_filename = pargs.r.ret_str; break; - case oSetPolicyURL: opt.set_policy_url = 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: opt.show_policy_url=1; break; + case oNoShowPolicyURL: opt.show_policy_url=0; break; case oUseEmbeddedFilename: opt.use_embedded_filename = 1; break; case oComment: opt.comment_string = pargs.r.ret_str; break; case oDefaultComment: opt.comment_string = NULL; break; case oThrowKeyid: opt.throw_keyid = 1; break; + case oShowPhotos: opt.show_photos = 1; break; + case oNoShowPhotos: opt.show_photos = 0; 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 = gcry_xstrdup(pargs.r.ret_str); break; - case oS2KCipher: s2k_cipher_string = gcry_xstrdup(pargs.r.ret_str); break; - + case oS2KDigest: s2k_digest_string = m_strdup(pargs.r.ret_str); break; + case oS2KCipher: s2k_cipher_string = m_strdup(pargs.r.ret_str); 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 ); @@ -929,18 +1269,41 @@ main( int argc, char **argv ) break; case oRecipient: /* store the recipient */ add_to_strlist2( &remusr, pargs.r.ret_str, utf8_strings ); + any_explicit_recipient = 1; break; case oTextmodeShort: opt.textmode = 2; break; case oTextmode: opt.textmode=1; break; + case oExpert: opt.expert = 1; break; + case oNoExpert: opt.expert = 0; break; + case oAskSigExpire: opt.ask_sig_expire = 1; break; + case oNoAskSigExpire: opt.ask_sig_expire = 0; break; + case oAskCertExpire: opt.ask_cert_expire = 1; break; + case oNoAskCertExpire: opt.ask_cert_expire = 0; break; case oUser: /* store the local users */ add_to_strlist2( &locusr, pargs.r.ret_str, utf8_strings ); break; case oCompress: opt.compress = pargs.r.ret_int; break; - case oPasswdFD: pwfd = pargs.r.ret_int; break; - case oCommandFD: opt.command_fd = pargs.r.ret_int; break; - case oCipherAlgo: def_cipher_string = gcry_xstrdup(pargs.r.ret_str); break; - case oDigestAlgo: def_digest_string = gcry_xstrdup(pargs.r.ret_str); break; - case oNoSecmemWarn: gcry_control( GCRYCTL_DISABLE_SECMEM_WARN ); break; + case oPasswdFD: + pwfd = iobuf_translate_file_handle (pargs.r.ret_int, 0); + break; +#ifdef __riscos__ + case oPasswdFile: + pwfd = iobuf_translate_file_handle ( fdopenfile (pargs.r.ret_str, 0), 0); + break; +#endif /* __riscos__ */ + case oCommandFD: + opt.command_fd = iobuf_translate_file_handle (pargs.r.ret_int, 0); + break; +#ifdef __riscos__ + case oCommandFile: + opt.command_fd = iobuf_translate_file_handle ( fdopenfile (pargs.r.ret_str, 0), 0); + break; +#endif /* __riscos__ */ + case oCipherAlgo: def_cipher_string = m_strdup(pargs.r.ret_str); break; + case oDigestAlgo: def_digest_string = m_strdup(pargs.r.ret_str); break; + case oCertDigestAlgo: cert_digest_string = m_strdup(pargs.r.ret_str); break; + case oNoSecmemWarn: secmem_set_flags( secmem_get_flags() | 1 ); break; + case oNoPermissionWarn: opt.no_perm_warn=1; break; case oCharset: if( set_native_charset( pargs.r.ret_str ) ) log_error(_("%s is not a valid character set\n"), @@ -948,54 +1311,129 @@ main( int argc, char **argv ) 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; - #if 0 - #warning no disable_dotlock() yet case oLockNever: disable_dotlock(); break; - #endif - case oLockMultiple: opt.lock_once = 0; break; - case oKeyServer: opt.keyserver_name = pargs.r.ret_str; break; - case oNotation: add_notation_data( pargs.r.ret_str ); break; + case oLockMultiple: +#ifndef __riscos__ + opt.lock_once = 0; +#else /* __riscos__ */ + not_implemented("lock-multiple"); +#endif /* __riscos__ */ + break; + case oKeyServer: + if(parse_keyserver_uri(pargs.r.ret_str,configname,configlineno)) + log_error(_("could not parse keyserver URI\n")); + break; + case oKeyServerOptions: + parse_keyserver_options(pargs.r.ret_str); + break; + case oTempDir: opt.temp_dir=pargs.r.ret_str; break; + case oExecPath: + { + /* Notice that path is never freed. That is + intentional due to the way putenv() works. */ + char *path=m_alloc(5+strlen(pargs.r.ret_str)+1); + strcpy(path,"PATH="); + strcat(path,pargs.r.ret_str); + if(putenv(path)!=0) + log_error(_("unable to set exec-path to %s\n"),path); + } + break; + case oNotation: + 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: opt.show_notation=1; break; + case oNoShowNotation: opt.show_notation=0; break; case oUtf8Strings: utf8_strings = 1; break; case oNoUtf8Strings: utf8_strings = 0; break; - case oDisableCipherAlgo: { - int algo = gcry_cipher_map_name(pargs.r.ret_str); - gcry_cipher_ctl( NULL, GCRYCTL_DISABLE_ALGO, - &algo, sizeof algo ); - } + case oDisableCipherAlgo: + disable_cipher_algo( string_to_cipher_algo(pargs.r.ret_str) ); break; - case oDisablePubkeyAlgo: { - int algo = gcry_pk_map_name(pargs.r.ret_str); - gcry_pk_ctl( GCRYCTL_DISABLE_ALGO, - &algo, sizeof algo ); - } + 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: opt.honor_http_proxy = 1; break; + case oHonorHttpProxy: + opt.keyserver_options.honor_http_proxy = 1; + deprecated_warning(configname,configlineno, + "--honor-http-proxy", + "--keyserver-options ", + "honor-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 oNoRandomSeedFile: use_random_seed = 0; break; - case oNoAutoKeyRetrieve: opt.auto_key_retrieve = 0; break; + case oAutoKeyRetrieve: + case oNoAutoKeyRetrieve: + opt.keyserver_options.auto_key_retrieve= + (pargs.r_opt==oAutoKeyRetrieve); + 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: opt.merge_only = 1; 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 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; default : pargs.err = configfp? 1:2; break; } } + if( configfp ) { fclose( configfp ); configfp = NULL; - gcry_free(configname); configname = NULL; + m_free(configname); configname = NULL; goto next_pass; } - gcry_free( configname ); configname = NULL; + m_free( configname ); configname = NULL; if( log_get_errorcount(0) ) - gpg_exit(2); + g10_exit(2); if( nogreeting ) greeting = 0; @@ -1012,9 +1450,39 @@ main( int argc, char **argv ) } #endif + check_permissions(opt.homedir,0,0); + + if(unsafe_files) + { + STRLIST tmp; + + for(tmp=unsafe_files;tmp;tmp=tmp->next) + check_permissions(tmp->d,0,0); + + free_strlist(unsafe_files); + } + + if(extensions) + { + STRLIST tmp; + + for(tmp=extensions;tmp;tmp=tmp->next) + check_permissions(tmp->d,1,0); + + free_strlist(extensions); + } + 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) @@ -1022,51 +1490,142 @@ main( int argc, char **argv ) "--textmode", "--no-literal" ); if (opt.set_filename) log_error(_("%s makes no sense with %s!\n"), - "--set-filename", "--no-literal" ); + eyes_only?"--for-your-eyes-only":"--set-filename", + "--no-literal" ); } + if (opt.set_filesize) log_info(_("NOTE: %s is not for normal use!\n"), "--set-filesize"); if( opt.batch ) tty_batchmode( 1 ); - gcry_control( GCRYCTL_RESUME_SECMEM_WARN ); + secmem_set_flags( secmem_get_flags() & ~2 ); /* resume warnings */ set_debug(); - /* FIXME: should set filenames of libgcrypt explicitly - * gpg_opt_homedir = opt.homedir; */ + g10_opt_homedir = opt.homedir; + + /* Do these after the switch(), so they can override settings. */ + if(opt.pgp2 && (opt.pgp6 || opt.pgp7)) + log_error(_("%s not allowed with %s!\n"), + "--pgp2",opt.pgp6?"--pgp6":"--pgp7"); + else + { + if(opt.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) + { + m_free(def_cipher_string); + def_cipher_string = m_strdup("idea"); + } + } + + if(unusable) + { + log_info(_("this message may not be usable by PGP 2.x\n")); + opt.pgp2=0; + } + else + { + opt.rfc1991 = 1; + opt.rfc2440 = 0; + opt.force_mdc = 0; + opt.disable_mdc = 1; + opt.force_v4_certs = 0; + opt.sk_comments = 0; + opt.escape_from = 1; + opt.force_v3_sigs = 1; + opt.pgp2_workarounds = 1; + opt.ask_sig_expire = 0; + opt.ask_cert_expire = 0; + m_free(def_digest_string); + def_digest_string = m_strdup("md5"); + opt.def_compress_algo = 1; + } + } + + if(opt.pgp6 || opt.pgp7) + { + opt.force_mdc=0; + opt.disable_mdc=1; + opt.sk_comments=0; + opt.escape_from=1; + opt.force_v3_sigs=1; + opt.ask_sig_expire=0; + opt.def_compress_algo=1; + } + } /* must do this after dropping setuid, because string_to... * may try to load an module */ if( def_cipher_string ) { - opt.def_cipher_algo = gcry_cipher_map_name(def_cipher_string); - gcry_free(def_cipher_string); def_cipher_string = NULL; - if( openpgp_cipher_test_algo(opt.def_cipher_algo) ) + opt.def_cipher_algo = string_to_cipher_algo(def_cipher_string); + if(opt.def_cipher_algo==0 && + ascii_strcasecmp(def_cipher_string,"idea")==0) + idea_cipher_warn(1); + m_free(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 = gcry_md_map_name(def_digest_string); - gcry_free(def_digest_string); def_digest_string = NULL; - if( openpgp_md_test_algo(opt.def_digest_algo) ) + opt.def_digest_algo = string_to_digest_algo(def_digest_string); + m_free(def_digest_string); def_digest_string = NULL; + if( check_digest_algo(opt.def_digest_algo) ) log_error(_("selected digest algorithm is invalid\n")); } + if( cert_digest_string ) { + opt.cert_digest_algo = string_to_digest_algo(cert_digest_string); + m_free(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 = gcry_cipher_map_name(s2k_cipher_string); - gcry_free(s2k_cipher_string); s2k_cipher_string = NULL; - if( openpgp_cipher_test_algo(opt.s2k_cipher_algo) ) + opt.s2k_cipher_algo = string_to_cipher_algo(s2k_cipher_string); + m_free(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 = gcry_md_map_name(s2k_digest_string); - gcry_free(s2k_digest_string); s2k_digest_string = NULL; - if( openpgp_md_test_algo(opt.s2k_digest_algo) ) + opt.s2k_digest_algo = string_to_digest_algo(s2k_digest_string); + m_free(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.set_policy_url ) { - if( check_policy_url( opt.set_policy_url ) ) - log_error(_("the given policy URL is invalid\n")); - } - if( opt.def_compress_algo < 1 || opt.def_compress_algo > 2 ) - log_error(_("compress algorithm must be in range %d..%d\n"), 1, 2); + if( opt.def_compress_algo < -1 || opt.def_compress_algo > 2 ) + log_error(_("compress algorithm must be in range %d..%d\n"), 0, 2); if( opt.completes_needed < 1 ) log_error(_("completes-needed must be greater than 0\n")); if( opt.marginals_needed < 2 ) @@ -1082,18 +1641,40 @@ main( int argc, char **argv ) log_error(_("invalid S2K mode; must be 0, 1 or 3\n")); } + if(opt.def_cert_check_level<0 || opt.def_cert_check_level>3) + log_error(_("invalid default-check-level; must be 0, 1, 2, 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 */ + 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")); if( log_get_errorcount(0) ) - gpg_exit(2); + g10_exit(2); /* set the random seed file */ if( use_random_seed ) { char *p = make_filename(opt.homedir, "random_seed", NULL ); - #if 0 - #warning set_random_seed_file missing + check_permissions(p,0,0); set_random_seed_file(p); - #endif - gcry_free(p); + m_free(p); } if( !cmd && opt.fingerprint && !with_fpr ) { @@ -1112,9 +1693,12 @@ main( int argc, char **argv ) opt.list_sigs++; opt.verbose = opt.verbose > 1; - gcry_control( GCRYCTL_SET_VERBOSITY, (int)opt.verbose ); + g10_opt_verbose = opt.verbose; } + /* Compression algorithm 0 means no compression at all */ + if( opt.def_compress_algo == 0) + opt.compress = 0; /* kludge to let -sat generate a clear text signature */ if( opt.textmode == 2 && !detached_sig && opt.armor && cmd == aSign ) @@ -1123,20 +1707,27 @@ main( int argc, char **argv ) if( opt.verbose > 1 ) set_packet_list_mode(1); - /* add the keyrings, but not for some special commands and - * not in case of "-kvv userid keyring" */ + /* 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 */ if( cmd != aDeArmor && cmd != aEnArmor - && !(cmd == aKMode && argc == 2 ) ) { - - if( !sec_nrings && default_keyring ) /* add default secret rings */ - add_keyblock_resource("secring.gpg", 0, 1); - for(sl = sec_nrings; sl; sl = sl->next ) - add_keyblock_resource( sl->d, 0, 1 ); - if( !nrings && default_keyring ) /* add default ring */ - add_keyblock_resource("pubring.gpg", 0, 0); + && !(cmd == aKMode && argc == 2 ) ) + { + if (cmd != aCheckKeys && cmd != aListSigs && cmd != aListKeys + && cmd != aVerify && cmd != aVerifyFiles + && cmd != aSym) + { + if (!sec_nrings || default_keyring) /* add default secret rings */ + keydb_add_resource ("secring" EXTSEP_S "gpg", 0, 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", 0, 0); for(sl = nrings; sl; sl = sl->next ) - add_keyblock_resource( sl->d, 0, 0 ); - } + keydb_add_resource ( sl->d, 0, 0 ); + } FREE_STRLIST(nrings); FREE_STRLIST(sec_nrings); @@ -1150,7 +1741,6 @@ main( int argc, char **argv ) case aPrimegen: case aPrintMD: case aPrintMDs: - case aPrintHMAC: case aGenRandom: case aDeArmor: case aEnArmor: @@ -1168,8 +1758,22 @@ main( int argc, char **argv ) default: rc = setup_trustdb(1, trustdb_name ); break; } if( rc ) - log_error(_("failed to initialize the TrustDB: %s\n"), gpg_errstr(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 */ @@ -1177,23 +1781,27 @@ main( int argc, char **argv ) wrong_args(_("--store [filename]")); if( (rc = encode_store(fname)) ) log_error_f( print_fname_stdin(fname), - "store failed: %s\n", gpg_errstr(rc) ); + "store failed: %s\n", 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_f(print_fname_stdin(fname), - "symmetric encryption failed: %s\n",gpg_errstr(rc) ); + "symmetric encryption failed: %s\n",g10_errstr(rc) ); break; case aEncr: /* encrypt the given file */ if( argc > 1 ) wrong_args(_("--encrypt [filename]")); if( (rc = encode_crypt(fname,remusr)) ) - log_error("%s: encryption failed: %s\n", print_fname_stdin(fname), gpg_errstr(rc) ); + log_error("%s: encryption failed: %s\n", print_fname_stdin(fname), g10_errstr(rc) ); break; + case aEncrFiles: /* encrypt the given files */ + encode_crypt_files(argc, argv, remusr); + break; + case aSign: /* sign the given file */ sl = NULL; if( detached_sig ) { /* sign all files */ @@ -1204,12 +1812,12 @@ main( int argc, char **argv ) if( argc > 1 ) wrong_args(_("--sign [filename]")); if( argc ) { - sl = gcry_xcalloc( 1, sizeof *sl + strlen(fname)); + sl = m_alloc_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", gpg_errstr(rc) ); + log_error("signing failed: %s\n", g10_errstr(rc) ); free_strlist(sl); break; @@ -1217,47 +1825,60 @@ main( int argc, char **argv ) if( argc > 1 ) wrong_args(_("--sign --encrypt [filename]")); if( argc ) { - sl = gcry_xcalloc( 1, sizeof *sl + strlen(fname)); + sl = m_alloc_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), gpg_errstr(rc) ); + log_error("%s: 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), gpg_errstr(rc) ); + log_error("%s: clearsign failed: %s\n", + print_fname_stdin(fname), g10_errstr(rc) ); break; case aVerify: if( (rc = verify_signatures( argc, argv ) )) - log_error("verify signatures failed: %s\n", gpg_errstr(rc) ); + log_error("verify signatures failed: %s\n", g10_errstr(rc) ); break; case aVerifyFiles: if( (rc = verify_files( argc, argv ) )) - log_error("verify files failed: %s\n", gpg_errstr(rc) ); + log_error("verify files failed: %s\n", g10_errstr(rc) ); break; case aDecrypt: if( argc > 1 ) wrong_args(_("--decrypt [filename]")); if( (rc = decrypt_message( fname ) )) - log_error("decrypt_message failed: %s\n", gpg_errstr(rc) ); + log_error("decrypt_message failed: %s\n", g10_errstr(rc) ); break; - + case aDecryptFiles: + decrypt_messages(argc, argv); + break; + case aSignKey: /* sign the key given as argument */ if( argc != 1 ) wrong_args(_("--sign-key user-id")); username = make_username( fname ); keyedit_menu(fname, locusr, NULL, 1 ); - gcry_free(username); + m_free(username); break; case aLSignKey: @@ -1265,9 +1886,25 @@ main( int argc, char **argv ) wrong_args(_("--lsign-key user-id")); username = make_username( fname ); keyedit_menu(fname, locusr, NULL, 2 ); - gcry_free(username); + m_free(username); break; + case aNRSignKey: + if( argc != 1 ) + wrong_args(_("--nrsign-key user-id")); + username = make_username( fname ); + keyedit_menu(fname, locusr, NULL, 3 ); + m_free(username); + break; + + case aNRLSignKey: + if( argc != 1 ) + wrong_args(_("--nrlsign-key user-id")); + username = make_username( fname ); + keyedit_menu(fname, locusr, NULL, 4 ); + m_free(username); + break; + case aEditKey: /* Edit a key signature */ if( !argc ) wrong_args(_("--edit-key user-id [commands]")); @@ -1281,22 +1918,22 @@ main( int argc, char **argv ) } else keyedit_menu(username, locusr, NULL, 0 ); - gcry_free(username); + m_free(username); break; - case aDeleteSecretKey: - if( argc != 1 ) - wrong_args(_("--delete-secret-key user-id")); - case aDeleteKey: - if( argc != 1 ) - wrong_args(_("--delete-key user-id")); - username = make_username( fname ); - if( (rc = delete_key(username, cmd==aDeleteSecretKey)) ) - log_error("%s: delete key failed: %s\n", username, gpg_errstr(rc) ); - gcry_free(username); + 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: @@ -1332,7 +1969,7 @@ main( int argc, char **argv ) else { /* add keyring (default keyrings are not registered in this * special case */ - add_keyblock_resource( argv[1], 0, 0 ); + keydb_add_resource( argv[1], 0, 0 ); sl = NULL; if (**argv) add_to_strlist2( &sl, *argv, utf8_strings ); @@ -1359,7 +1996,7 @@ main( int argc, char **argv ) case aFastImport: case aImport: - import_keys( argc? argv:NULL, argc, (cmd == aFastImport) ); + import_keys( argc? argv:NULL, argc, (cmd == aFastImport), NULL ); break; case aExport: @@ -1370,14 +2007,31 @@ main( int argc, char **argv ) for( ; argc; argc--, argv++ ) add_to_strlist2( &sl, *argv, utf8_strings ); if( cmd == aSendKeys ) - hkp_export( sl ); + keyserver_export( sl ); else if( cmd == aRecvKeys ) - hkp_import( sl ); + keyserver_import( sl ); else export_pubkeys( sl, (cmd == aExport) ); free_strlist(sl); break; + case aSearchKeys: + sl = NULL; + for( ; argc; argc--, argv++ ) + append_to_strlist2( &sl, *argv, utf8_strings ); + + keyserver_search( sl ); + free_strlist(sl); + break; + + case aRefreshKeys: + sl = NULL; + for( ; argc; argc--, argv++ ) + add_to_strlist2( &sl, *argv, utf8_strings ); + keyserver_refresh(sl); + free_strlist(sl); + break; + case aExportSecret: sl = NULL; for( ; argc; argc--, argv++ ) @@ -1399,7 +2053,15 @@ main( int argc, char **argv ) wrong_args("--gen-revoke user-id"); username = make_username(*argv); gen_revoke( username ); - gcry_free( username ); + m_free( username ); + break; + + case aDesigRevoke: + if( argc != 1 ) + wrong_args("--desig-revoke user-id"); + username = make_username(*argv); + gen_desig_revoke( username ); + m_free( username ); break; case aDeArmor: @@ -1407,7 +2069,7 @@ main( int argc, char **argv ) wrong_args("--dearmor [file]"); rc = dearmor_file( argc? *argv: NULL ); if( rc ) - log_error(_("dearmoring failed: %s\n"), gpg_errstr(rc)); + log_error(_("dearmoring failed: %s\n"), g10_errstr(rc)); break; case aEnArmor: @@ -1415,17 +2077,12 @@ main( int argc, char **argv ) wrong_args("--enarmor [file]"); rc = enarmor_file( argc? *argv: NULL ); if( rc ) - log_error(_("enarmoring failed: %s\n"), gpg_errstr(rc)); + log_error(_("enarmoring failed: %s\n"), g10_errstr(rc)); break; case aPrimegen: - { - #if 1 - log_error( "command is currently not implemented\n"); - #else - /* FIXME: disabled until we have an API to create primes */ - int mode = argc < 2 ? 0 : atoi(*argv); + { int mode = argc < 2 ? 0 : atoi(*argv); if( mode == 1 && argc == 2 ) { mpi_print( stdout, generate_public_prime( atoi(argv[1]) ), 1); @@ -1450,12 +2107,11 @@ main( int argc, char **argv ) atoi(argv[2]), g, NULL ), 1); putchar('\n'); mpi_print( stdout, g, 1 ); - mpi_release(g); + mpi_free(g); } else wrong_args("--gen-prime mode bits [qbits] "); putchar('\n'); - #endif } break; @@ -1470,17 +2126,33 @@ main( int argc, char **argv ) while( endless || count ) { byte *p; - size_t n = !endless && count < 100? count : 100; + /* 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 = gcry_random_bytes( n, level ); + p = get_random_bits( n*8, level, 0); #ifdef HAVE_DOSISH_SYSTEM setmode ( fileno(stdout), O_BINARY ); #endif - fwrite( p, n, 1, stdout ); - gcry_free(p); + if (opt.armor) { + char *tmp = make_radix64_string (p, n); + fputs (tmp, stdout); + m_free (tmp); + if (n%3 == 1) + putchar ('='); + if (n%3) + putchar ('='); + } else { + fwrite( p, n, 1, stdout ); + } + m_free(p); if( !endless ) count -= n; } + if (opt.armor) + putchar ('\n'); } break; @@ -1489,53 +2161,28 @@ main( int argc, char **argv ) wrong_args("--print-md algo [files]"); { int all_algos = (**argv=='*' && !(*argv)[1]); - int algo = all_algos? 0 : gcry_md_map_name(*argv); - - if( !algo && !all_algos ) - log_error(_("invalid hash algorithm `%s'\n"), *argv ); - else { - argc--; argv++; - if( !argc ) - print_mds(NULL, algo, NULL); - else { - for(; argc; argc--, argv++ ) - print_mds(*argv, algo, NULL); - } - } - } - break; - - case aPrintHMAC: - if( argc < 2 ) - wrong_args("--print-hmac hash-algo key [files]"); - { - int all_algos = (**argv=='*' && !(*argv)[1]); - int algo = all_algos? 0 : gcry_md_map_name(*argv); + int algo = all_algos? 0 : string_to_digest_algo(*argv); if( !algo && !all_algos ) log_error(_("invalid hash algorithm `%s'\n"), *argv ); else { - const char *key; - argc--; argv++; - key = *argv; argc--; argv++; if( !argc ) - print_mds(NULL, algo, key ); + print_mds(NULL, algo); else { for(; argc; argc--, argv++ ) - print_mds(*argv, algo, key ); + print_mds(*argv, algo); } } } break; - case aPrintMDs: /* old option */ if( !argc ) - print_mds(NULL,0,NULL); + print_mds(NULL,0); else { for(; argc; argc--, argv++ ) - print_mds(*argv,0,NULL); + print_mds(*argv,0); } break; @@ -1555,15 +2202,8 @@ main( int argc, char **argv ) break; case aCheckTrustDB: - if( !argc ) - check_trustdb(NULL); - else { - for( ; argc; argc--, argv++ ) { - username = make_username( *argv ); - check_trustdb( username ); - gcry_free(username); - } - } + /* Old versions allowed for arguments - ignore them */ + check_trustdb(); break; case aFixTrustDB: @@ -1578,7 +2218,7 @@ main( int argc, char **argv ) for( ; argc; argc--, argv++ ) { username = make_username( *argv ); list_trust_path( username ); - gcry_free(username); + m_free(username); } break; @@ -1593,9 +2233,21 @@ main( int argc, char **argv ) 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 (); + break; case aListPackets: - opt.list_packets=1; + opt.list_packets=2; default: if( argc > 1 ) wrong_args(_("[filename]")); @@ -1620,7 +2272,7 @@ main( int argc, char **argv ) } rc = proc_packets(NULL, a ); if( rc ) - log_error("processing message failed: %s\n", gpg_errstr(rc) ); + log_error("processing message failed: %s\n", g10_errstr(rc) ); iobuf_close(a); } break; @@ -1629,28 +2281,24 @@ main( int argc, char **argv ) /* cleanup */ FREE_STRLIST(remusr); FREE_STRLIST(locusr); - gpg_exit(0); + g10_exit(0); return 8; /*NEVER REACHED*/ } void -gpg_exit( int rc ) +g10_exit( int rc ) { - #if 0 - #warning no update_random_seed_file update_random_seed_file(); - #endif if( opt.debug & DBG_MEMSTAT_VALUE ) { - gcry_control( GCRYCTL_DUMP_MEMORY_STATS ); - gcry_control( GCRYCTL_DUMP_RANDOM_STATS ); + m_print_stats("on exit"); + random_dump_stats(); } if( opt.debug ) - gcry_control( GCRYCTL_DUMP_SECMEM_STATS ); - gcry_control( GCRYCTL_TERM_SECMEM ); + secmem_dump_stats(); + secmem_term(); rc = rc? rc : log_get_errorcount(0)? 2 : - gpg_errors_seen? 1 : 0; - /*write_status( STATUS_LEAVE );*/ + g10_errors_seen? 1 : 0; exit(rc ); } @@ -1692,71 +2340,106 @@ print_hex( byte *p, size_t n ) } static void -print_mds( const char *fname, int algo, const char *key ) +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; - GCRY_MD_HD md; + MD_HANDLE md; char *pname; - int have_tiger = 0; if( !fname ) { fp = stdin; #ifdef HAVE_DOSISH_SYSTEM setmode ( fileno(fp) , O_BINARY ); #endif - pname = gcry_xstrdup("[stdin]: "); + pname = m_strdup("[stdin]: "); } else { - pname = gcry_xmalloc(strlen(fname)+3); + pname = m_alloc(strlen(fname)+3); strcpy(stpcpy(pname,fname),": "); fp = fopen( fname, "rb" ); } if( !fp ) { log_error("%s%s\n", pname, strerror(errno) ); - gcry_free(pname); + m_free(pname); return; } - md = gcry_md_open( 0, key? GCRY_MD_FLAG_HMAC : 0 ); + md = md_open( 0, 0 ); if( algo ) - gcry_md_enable( md, algo ); + md_enable( md, algo ); else { - /* Fixme: this does not work with hmac */ - gcry_md_enable( md, GCRY_MD_MD5 ); - gcry_md_enable( md, GCRY_MD_SHA1 ); - gcry_md_enable( md, GCRY_MD_RMD160 ); - have_tiger = !gcry_md_enable( md, GCRY_MD_TIGER ); + md_enable( md, DIGEST_ALGO_MD5 ); + md_enable( md, DIGEST_ALGO_SHA1 ); + md_enable( md, DIGEST_ALGO_RMD160 ); + if( !check_digest_algo(DIGEST_ALGO_TIGER) ) + md_enable( md, DIGEST_ALGO_TIGER ); } - if( key ) - gcry_md_setkey( md, key, strlen(key) ); while( (n=fread( buf, 1, DIM(buf), fp )) ) - gcry_md_write( md, buf, n ); + md_write( md, buf, n ); if( ferror(fp) ) log_error("%s%s\n", pname, strerror(errno) ); else { - if( algo ) { - if( fname ) - fputs( pname, stdout ); - print_hex(gcry_md_read(md, algo), gcry_md_get_algo_dlen(algo) ); - } - else { - printf( "%s MD5 = ", fname?pname:"" ); - print_hex(gcry_md_read(md, GCRY_MD_MD5), 16 ); - printf("\n%s SHA1 = ", fname?pname:"" ); - print_hex(gcry_md_read(md, GCRY_MD_SHA1), 20 ); - printf("\n%sRMD160 = ", fname?pname:"" ); - print_hex(gcry_md_read(md, GCRY_MD_RMD160), 20 ); - if( have_tiger ) { - printf("\n%s TIGER = ", fname?pname:"" ); - print_hex(gcry_md_read(md, GCRY_MD_TIGER), 24 ); - } - } - putchar('\n'); + 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 ); + if( !check_digest_algo(DIGEST_ALGO_TIGER) ) + print_hashline( md, DIGEST_ALGO_TIGER, fname ); + } + } + else { + if( algo ) { + if( fname ) + fputs( pname, stdout ); + print_hex(md_read(md, algo), md_digest_length(algo) ); + } + else { + printf( "%s MD5 = ", fname?pname:"" ); + print_hex(md_read(md, DIGEST_ALGO_MD5), 16 ); + printf("\n%s SHA1 = ", fname?pname:"" ); + print_hex(md_read(md, DIGEST_ALGO_SHA1), 20 ); + printf("\n%sRMD160 = ", fname?pname:"" ); + print_hex(md_read(md, DIGEST_ALGO_RMD160), 20 ); + if( !check_digest_algo(DIGEST_ALGO_TIGER) ) { + printf("\n%s TIGER = ", fname?pname:"" ); + print_hex(md_read(md, DIGEST_ALGO_TIGER), 24 ); + } + } + putchar('\n'); + } } - gcry_md_close(md); + md_close(md); if( fp != stdin ) fclose(fp); @@ -1765,41 +2448,35 @@ print_mds( const char *fname, int algo, const char *key ) /**************** * Check the supplied name,value string and add it to the notation - * data to be used for signatures. - */ + * data to be used for signatures. which==0 for sig notations, and 1 + * for cert notations. +*/ static void -add_notation_data( const char *string ) +add_notation_data( const char *string, int which ) { const char *s; - const char *s2; - STRLIST sl; + STRLIST sl,*notation_data; int critical=0; int highbit=0; + if(which) + notation_data=&opt.cert_notation_data; + else + notation_data=&opt.sig_notation_data; + if( *string == '!' ) { critical = 1; string++; } - s = string; - if( !*s || (*s & 0x80) || (!isalpha(*s) && *s != '_') ) { - log_error(_("the first character of a notation name " - "must be a letter or an underscore\n") ); - return; - } - for(s++; *s != '='; s++ ) { - if( !*s || (*s & 0x80) || (!isalnum(*s) && *s != '_' && *s != '.' ) ) { - log_error(_("a notation name must have only letters, " - "digits, dots or underscores and end with an '='\n") ); + for( s=string ; *s != '='; s++ ) { + if( !*s || (*s & 0x80) || (!isgraph(*s) && !isspace(*s)) ) { + log_error(_("a notation name must have only printable characters " + "or spaces, and end with an '='\n") ); return; } } - if( s[-1] == '.' || ((s2=strstr(string, "..")) && s2 < s ) ) { - log_error(_("dots in a notation name must be surrounded " - "by other characters\n") ); - return; - } - /* we do only support printabe text - therefore we enforce the use + /* we only support printable text - therefore we enforce the use * of only printable characters (an empty value is valid) */ for( s++; *s ; s++ ) { if( iscntrl(*s) ) { @@ -1812,26 +2489,44 @@ add_notation_data( const char *string ) } if( highbit ) /* must use UTF8 encoding */ - sl = add_to_strlist2( &opt.notation_data, string, utf8_strings ); + sl = add_to_strlist2( notation_data, string, utf8_strings ); else - sl = add_to_strlist( &opt.notation_data, string ); + sl = add_to_strlist( notation_data, string ); if( critical ) sl->flags |= 1; } -static int -check_policy_url( const char *s ) +static void +add_policy_url( const char *string, int which ) { - if( *s == '!' ) - s++; - if( !*s ) - return -1; - for(; *s ; s++ ) { - if( (*s & 0x80) || iscntrl(*s) ) - return -1; + int i,critical=0; + STRLIST sl; + + if(*string=='!') + { + string++; + critical=1; } - return 0; -} + for(i=0;i<strlen(string);i++) + if(string[i]&0x80 || 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; +} diff --git a/g10/getkey.c b/g10/getkey.c index 8f38c4a34..87680502a 100644 --- a/g10/getkey.c +++ b/g10/getkey.c @@ -1,5 +1,5 @@ /* getkey.c - Get a key from the database - * Copyright (C) 1998, 1999, 2000 Free Software Foundation, Inc. + * Copyright (C) 1998, 1999, 2000, 2001, 2002 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -24,10 +24,9 @@ #include <string.h> #include <assert.h> #include <ctype.h> - #include "util.h" #include "packet.h" -#include <gcrypt.h> +#include "memory.h" #include "iobuf.h" #include "keydb.h" #include "options.h" @@ -35,81 +34,26 @@ #include "trustdb.h" #include "i18n.h" +#define MAX_PK_CACHE_ENTRIES 200 +#define MAX_UID_CACHE_ENTRIES 200 -#if 0 -#define MAX_UNK_CACHE_ENTRIES 1000 /* we use a linked list - so I guess - * this is a reasonable limit */ -#define MAX_PK_CACHE_ENTRIES 50 +#if MAX_PK_CACHE_ENTRIES < 2 + #error We need the cache for key creation #endif -#define MAX_UID_CACHE_ENTRIES 50 - -/* 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 -}; -typedef struct { - int mode; - u32 keyid[2]; - byte fprint[20]; - char *namebuf; - const char *name; -} getkey_item_t; struct getkey_ctx_s { - /* make an array or a linked list from dome fields */ - int primary; + int exact; KBNODE keyblock; - KBPOS kbpos; + KBPOS kbpos; KBNODE found_key; /* pointer into some keyblock */ int last_rc; int req_usage; int req_algo; - ulong count; + KEYDB_HANDLE kr_handle; int not_allocated; int nitems; - getkey_item_t items[1]; + KEYDB_SEARCH_DESC items[1]; }; #if 0 @@ -127,12 +71,6 @@ typedef struct keyid_list { } *keyid_list_t; -#if MAX_UNK_CACHE_ENTRIES - static keyid_list_t unknown_keyids; - static int unk_cache_entries; /* number of entries in unknown keys cache */ - static int unk_cache_disabled; -#endif - #if MAX_PK_CACHE_ENTRIES typedef struct pk_cache_entry { struct pk_cache_entry *next; @@ -156,11 +94,9 @@ typedef struct user_id_db { static user_id_db_t user_id_db; static int uid_cache_entries; /* number of entries in uid cache */ - - -static char* prepare_word_match( const byte *name ); -static int lookup( GETKEY_CTX ctx, KBNODE *ret_kb, int secmode ); - +static void merge_selfsigs( KBNODE keyblock ); +static int lookup( GETKEY_CTX ctx, KBNODE *ret_keyblock, int secmode ); +static int check_revocation_keys(PKT_public_key *pk,PKT_signature *sig); #if 0 static void @@ -180,7 +116,7 @@ print_stats() #endif -static void +void cache_public_key( PKT_public_key *pk ) { #if MAX_PK_CACHE_ENTRIES @@ -190,8 +126,11 @@ cache_public_key( PKT_public_key *pk ) if( pk_cache_disabled ) return; + if( pk->dont_cache ) + return; + if( is_ELGAMAL(pk->pubkey_algo) - || pk->pubkey_algo == GCRY_PK_DSA + || pk->pubkey_algo == PUBKEY_ALGO_DSA || is_RSA(pk->pubkey_algo) ) { keyid_from_pk( pk, keyid ); } @@ -213,7 +152,7 @@ cache_public_key( PKT_public_key *pk ) return; } pk_cache_entries++; - ce = gcry_xmalloc( sizeof *ce ); + ce = m_alloc( sizeof *ce ); ce->next = pk_cache; pk_cache = ce; ce->pk = copy_public_key( NULL, pk ); @@ -222,6 +161,7 @@ cache_public_key( PKT_public_key *pk ) #endif } + /* * Return the user ID from the given keyblock. * We use the primary uid flag which has been set by the merge_selfsigs @@ -232,16 +172,21 @@ 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; } } - *uidlen = 12; - return "[No user ID]"; + /* fixme: returning translatable constants instead of a user ID is + * not good because they are probably not utf-8 encoded. */ + s = _("[User id not found]"); + *uidlen = strlen (s); + return s; } @@ -250,7 +195,7 @@ release_keyid_list ( keyid_list_t k ) { while ( k ) { keyid_list_t k2 = k->next; - gcry_free (k); + m_free (k); k = k2; } } @@ -259,7 +204,7 @@ release_keyid_list ( keyid_list_t k ) * Store the association of keyid and userid * Feed only public keys to this function. */ -void +static void cache_user_id( KBNODE keyblock ) { user_id_db_t r; @@ -271,7 +216,7 @@ cache_user_id( KBNODE keyblock ) for (k=keyblock; k; k = k->next ) { if ( k->pkt->pkttype == PKT_PUBLIC_KEY || k->pkt->pkttype == PKT_PUBLIC_SUBKEY ) { - keyid_list_t a = gcry_xcalloc ( 1, sizeof *a ); + keyid_list_t a = m_alloc_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 ); @@ -284,7 +229,7 @@ cache_user_id( KBNODE keyblock ) if( DBG_CACHE ) log_debug("cache_user_id: already in cache\n"); release_keyid_list ( keyids ); - gcry_free ( a ); + m_free ( a ); return; } } @@ -305,10 +250,10 @@ cache_user_id( KBNODE keyblock ) r = user_id_db; user_id_db = r->next; release_keyid_list ( r->keyids ); - gcry_free(r); + m_free(r); uid_cache_entries--; } - r = gcry_xmalloc( sizeof *r + uidlen-1 ); + r = m_alloc( sizeof *r + uidlen-1 ); r->keyids = keyids; r->len = uidlen; memcpy(r->name, uid, r->len); @@ -321,17 +266,6 @@ cache_user_id( KBNODE keyblock ) void getkey_disable_caches() { - #if MAX_UNK_CACHE_ENTRIES - { - keyid_list_t kl, kl2; - for( kl = unknown_keyids; kl; kl = kl2 ) { - kl2 = kl->next; - gcry_free(kl); - } - unknown_keyids = NULL; - unk_cache_disabled = 1; - } - #endif #if MAX_PK_CACHE_ENTRIES { pk_cache_entry_t ce, ce2; @@ -339,7 +273,7 @@ getkey_disable_caches() for( ce = pk_cache; ce; ce = ce2 ) { ce2 = ce->next; free_public_key( ce->pk ); - gcry_free( ce ); + m_free( ce ); } pk_cache_disabled=1; pk_cache_entries = 0; @@ -351,15 +285,14 @@ getkey_disable_caches() static void -pk_from_block ( GETKEY_CTX ctx, - PKT_public_key *pk, KBNODE keyblock, const char *namehash ) +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_new_namehash( pk, a->pkt->pkt.public_key, namehash); + copy_public_key ( pk, a->pkt->pkt.public_key ); } static void @@ -386,15 +319,6 @@ get_pubkey( PKT_public_key *pk, u32 *keyid ) int internal = 0; int rc = 0; - #if MAX_UNK_CACHE_ENTRIES - { /* let's see whether we checked the keyid already */ - keyid_list_t kl; - for( kl = unknown_keyids; kl; kl = kl->next ) - if( kl->keyid[0] == keyid[0] && kl->keyid[1] == keyid[1] ) - return GPGERR_NO_PUBKEY; /* already checked and not found */ - } - #endif - #if MAX_PK_CACHE_ENTRIES { /* Try to get it from the cache */ pk_cache_entry_t ce; @@ -409,7 +333,7 @@ get_pubkey( PKT_public_key *pk, u32 *keyid ) #endif /* more init stuff */ if( !pk ) { - pk = gcry_xcalloc( 1, sizeof *pk ); + pk = m_alloc_clear( sizeof *pk ); internal++; } @@ -418,16 +342,18 @@ get_pubkey( PKT_public_key *pk, u32 *keyid ) { 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 = 11; - ctx.items[0].keyid[0] = keyid[0]; - ctx.items[0].keyid[1] = keyid[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, NULL ); + pk_from_block ( &ctx, pk, kb ); } get_pubkey_end( &ctx ); release_kbnode ( kb ); @@ -435,26 +361,7 @@ get_pubkey( PKT_public_key *pk, u32 *keyid ) if( !rc ) goto leave; - #if MAX_UNK_CACHE_ENTRIES - /* not found: store it for future reference */ - if( unk_cache_disabled ) - ; - else if( ++unk_cache_entries > MAX_UNK_CACHE_ENTRIES ) { - unk_cache_disabled = 1; - if( opt.verbose > 1 ) - log_info(_("too many entries in unk cache - disabled\n")); - } - else { - keyid_list_t kl; - - kl = gcry_xmalloc( sizeof *kl ); - kl->keyid[0] = keyid[0]; - kl->keyid[1] = keyid[1]; - kl->next = unknown_keyids; - unknown_keyids = kl; - } - #endif - rc = GPGERR_NO_PUBKEY; + rc = G10ERR_NO_PUBKEY; leave: if( !rc ) @@ -473,11 +380,13 @@ get_pubkeyblock( u32 *keyid ) 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 = 11; - ctx.items[0].keyid[0] = keyid[0]; - ctx.items[0].keyid[1] = keyid[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 ); @@ -498,11 +407,13 @@ get_seckey( PKT_secret_key *sk, u32 *keyid ) 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 = 11; - ctx.items[0].keyid[0] = keyid[0]; - ctx.items[0].keyid[1] = keyid[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 ); @@ -524,61 +435,30 @@ get_seckey( PKT_secret_key *sk, u32 *keyid ) /**************** - * Check whether the secret key is available + * 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 - * GPGERR_NO_SECKEY := not availabe + * G10ERR_NO_SECKEY := not availabe */ int seckey_available( u32 *keyid ) { int rc; - struct getkey_ctx_s ctx; - KBNODE kb = NULL; + KEYDB_HANDLE hd = keydb_new (1); - memset( &ctx, 0, sizeof ctx ); - ctx.not_allocated = 1; - ctx.nitems = 1; - ctx.items[0].mode = 11; - ctx.items[0].keyid[0] = keyid[0]; - ctx.items[0].keyid[1] = keyid[1]; - rc = lookup( &ctx, &kb, 1 ); - get_seckey_end( &ctx ); - release_kbnode ( kb ); + rc = keydb_search_kid (hd, keyid); + if ( rc == -1 ) + rc = G10ERR_NO_SECKEY; + keydb_release (hd); return rc; } - -static int -hextobyte( const byte *s ) -{ - int c; - - if( *s >= '0' && *s <= '9' ) - c = 16 * (*s - '0'); - else if( *s >= 'A' && *s <= 'F' ) - c = 16 * (10 + *s - 'A'); - else if( *s >= 'a' && *s <= 'f' ) - c = 16 * (10 + *s - 'a'); - else - return -1; - s++; - if( *s >= '0' && *s <= '9' ) - c += *s - '0'; - else if( *s >= 'A' && *s <= 'F' ) - c += 10 + *s - 'A'; - else if( *s >= 'a' && *s <= 'f' ) - c += 10 + *s - 'a'; - else - return -1; - return c; -} - - - /**************** * 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 @@ -594,8 +474,6 @@ hextobyte( const byte *s ) * 21 = Unified fingerprint :fpr:pk_algo: * (We don't use pk_algo yet) * - * if fprint is not NULL, it should be an array of at least 20 bytes. - * * 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 @@ -620,16 +498,21 @@ hextobyte( const byte *s ) * is not case sensitive. */ -int -classify_user_id( const char *name, u32 *keyid, byte *fprint, - const char **retstr, size_t *retlen ) +static int +classify_user_id2( const char *name, + KEYDB_SEARCH_DESC *desc, + int *force_exact ) { - const char * s; - int mode = 0; - int hexprefix = 0; - int hexlength; - - /* skip leading spaces. FIXME: what is with leading spaces? */ + const char *s; + int hexprefix = 0; + int hexlength; + int mode = 0; + + /* 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); + *force_exact = 0; + /* skip leading spaces. Fixme: what is with trailing spaces? */ for(s = name; *s && isspace(*s); s++ ) ; @@ -638,42 +521,42 @@ classify_user_id( const char *name, u32 *keyid, byte *fprint, return 0; case '.': /* an email address, compare from end */ - mode = 5; + mode = KEYDB_SEARCH_MODE_MAILEND; s++; + desc->u.name = s; break; case '<': /* an email address */ - mode = 3; + mode = KEYDB_SEARCH_MODE_MAIL; + desc->u.name = s; break; case '@': /* part of an email address */ - mode = 4; + mode = KEYDB_SEARCH_MODE_MAILSUB; s++; + desc->u.name = s; break; case '=': /* exact compare */ - mode = 1; + mode = KEYDB_SEARCH_MODE_EXACT; s++; + desc->u.name = s; break; case '*': /* case insensitive substring search */ - mode = 2; + mode = KEYDB_SEARCH_MODE_SUBSTR; s++; + desc->u.name = s; break; case '+': /* compare individual words */ - mode = 6; + mode = KEYDB_SEARCH_MODE_WORDS; s++; + desc->u.name = s; break; case '#': /* local user id */ - mode = 12; - s++; - if (keyid) { - if (keyid_from_lid(strtoul(s, NULL, 10), keyid)) - keyid[0] = keyid[1] = 0; - } - break; + return 0; /* This is now obsolete and van't not be used anymore*/ case ':': /*Unified fingerprint */ { @@ -689,14 +572,12 @@ classify_user_id( const char *name, u32 *keyid, byte *fprint, } if (i != 32 && i != 40) return 0; /* invalid length of fpr*/ - if (fprint) { - for (i=0,si=s; si < se; i++, si +=2) - fprint[i] = hextobyte(si); - for ( ; i < 20; i++) - fprint[i]= 0; - } + 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 = 21; + mode = KEYDB_SEARCH_MODE_FPR; } break; @@ -707,6 +588,10 @@ classify_user_id( const char *name, u32 *keyid, byte *fprint, } hexlength = strspn(s, "0123456789abcdefABCDEF"); + if (hexlength >= 8 && s[hexlength] =='!') { + *force_exact = 1; + hexlength++; /* just for the following check */ + } /* check if a hexadecimal number is terminated by EOS or blank */ if (hexlength && s[hexlength] && !isspace(s[hexlength])) { @@ -716,16 +601,17 @@ classify_user_id( const char *name, u32 *keyid, byte *fprint, hexlength = 0; /* a hex number, but really were not. */ } + if (*force_exact) + hexlength--; + if (hexlength == 8 || (!hexprefix && hexlength == 9 && *s == '0')){ /* short keyid */ if (hexlength == 9) s++; - if (keyid) { - keyid[0] = 0; - keyid[1] = strtoul( s, NULL, 16 ); - } - mode = 10; + 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')) { @@ -734,9 +620,9 @@ classify_user_id( const char *name, u32 *keyid, byte *fprint, if (hexlength == 17) s++; mem2str(buf, s, 9 ); - keyid[0] = strtoul( buf, NULL, 16 ); - keyid[1] = strtoul( s+8, NULL, 16 ); - mode = 11; + 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')) { @@ -744,16 +630,14 @@ classify_user_id( const char *name, u32 *keyid, byte *fprint, int i; if (hexlength == 33) s++; - if (fprint) { - memset(fprint+16, 4, 0); - for (i=0; i < 16; i++, s+=2) { - int c = hextobyte(s); - if (c == -1) - return 0; - fprint[i] = c; - } - } - mode = 16; + 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')) { @@ -761,131 +645,140 @@ classify_user_id( const char *name, u32 *keyid, byte *fprint, int i; if (hexlength == 41) s++; - if (fprint) { - for (i=0; i < 20; i++, s+=2) { - int c = hextobyte(s); - if (c == -1) - return 0; - fprint[i] = c; - } - } - mode = 20; + 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 */ - mode = 2; /* Default is case insensitive substring search */ + *force_exact = 0; + desc->u.name = s; + mode = KEYDB_SEARCH_MODE_SUBSTR; /* default mode */ } } - if( retstr ) - *retstr = s; - if( retlen ) - *retlen = strlen(s); - + desc->mode = mode; return mode; } +int +classify_user_id (const char *name, KEYDB_SEARCH_DESC *desc) +{ + int dummy; + KEYDB_SEARCH_DESC dummy_desc; + if (!desc) + desc = &dummy_desc; + return classify_user_id2 (name, desc, &dummy); +} /**************** * 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. - * The caller must provide provide storage for either the pk or the sk. - * If ret_kb is not NULL the funtion will return the keyblock there. + * 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, KBNODE *ret_kb ) + PKT_public_key *pk, PKT_secret_key *sk, int secmode, + KBNODE *ret_kb, KEYDB_HANDLE *ret_kdbhd ) { int rc = 0; int n; STRLIST r; GETKEY_CTX ctx; KBNODE help_kb = NULL; + int exact; - if( retctx ) /* reset the returned context in case of error */ + 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; /* build the search context */ - /* Performance hint: Use a static buffer if there is only one name */ - /* and we don't have mode 6 */ for(n=0, r=namelist; r; r = r->next ) n++; - ctx = gcry_xcalloc( 1, sizeof *ctx + (n-1)*sizeof ctx->items ); + ctx = m_alloc_clear (sizeof *ctx + (n-1)*sizeof ctx->items ); ctx->nitems = n; for(n=0, r=namelist; r; r = r->next, n++ ) { - int mode = classify_user_id( r->d, - ctx->items[n].keyid, - ctx->items[n].fprint, - &ctx->items[n].name, - NULL ); - - /* if we don't use one of the exact key specifications, we assume that - * the primary key is requested */ - if ( mode != 10 && mode != 11 - && mode != 16 && mode == 20 && mode != 21 ) - ctx->primary = 1; - - ctx->items[n].mode = mode; - if( !ctx->items[n].mode ) { - gcry_free( ctx ); - return GPGERR_INV_USER_ID; - } - if( ctx->items[n].mode == 6 ) { - ctx->items[n].namebuf = prepare_word_match(ctx->items[n].name); - ctx->items[n].name = ctx->items[n].namebuf; + classify_user_id2 (r->d, &ctx->items[n], &exact); + + if (exact) + ctx->exact = 1; + if (!ctx->items[n].mode) { + m_free (ctx); + return G10ERR_INV_USER_ID; } } - - + ctx->kr_handle = keydb_new (secmode); if ( !ret_kb ) ret_kb = &help_kb; - if( sk ) { + 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, NULL /* FIXME need to get the namehash*/ ); + pk_from_block ( ctx, pk, *ret_kb ); } } release_kbnode ( help_kb ); - if( retctx ) /* caller wants the context */ + if (retctx) /* caller wants the context */ *retctx = ctx; else { - /* Hmmm, why not get_pubkey-end here?? */ - enum_keyblocks_end( ctx->kbpos ); ctx->kbpos = NULL; - for(n=0; n < ctx->nitems; n++ ) - gcry_free( ctx->items[n].namebuf ); - gcry_free( ctx ); + 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 returh 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. + */ int -get_pubkey_byname( GETKEY_CTX *retctx, PKT_public_key *pk, - const char *name, KBNODE *ret_keyblock ) +get_pubkey_byname (PKT_public_key *pk, + const char *name, KBNODE *ret_keyblock, + KEYDB_HANDLE *ret_kdbhd ) { int rc; STRLIST namelist = NULL; add_to_strlist( &namelist, name ); - rc = key_byname( retctx, namelist, pk, NULL, ret_keyblock ); + rc = key_byname( NULL, namelist, pk, NULL, 0, ret_keyblock, ret_kdbhd); free_strlist( namelist ); return rc; } @@ -894,7 +787,7 @@ int get_pubkey_bynames( GETKEY_CTX *retctx, PKT_public_key *pk, STRLIST names, KBNODE *ret_keyblock ) { - return key_byname( retctx, names, pk, NULL, ret_keyblock ); + return key_byname( retctx, names, pk, NULL, 0, ret_keyblock, NULL); } int @@ -904,7 +797,7 @@ get_pubkey_next( GETKEY_CTX ctx, PKT_public_key *pk, KBNODE *ret_keyblock ) rc = lookup( ctx, ret_keyblock, 0 ); if ( !rc && pk && ret_keyblock ) - pk_from_block ( ctx, pk, *ret_keyblock, NULL ); + pk_from_block ( ctx, pk, *ret_keyblock ); return rc; } @@ -914,70 +807,15 @@ void get_pubkey_end( GETKEY_CTX ctx ) { if( ctx ) { - int n; - - enum_keyblocks_end( ctx->kbpos ); ctx->kbpos = NULL; - for(n=0; n < ctx->nitems; n++ ) - gcry_free( ctx->items[n].namebuf ); + memset (&ctx->kbpos, 0, sizeof ctx->kbpos); + keydb_release (ctx->kr_handle); if( !ctx->not_allocated ) - gcry_free( ctx ); + m_free( ctx ); } } -/**************** - * Combined function to search for a username and get the position - * of the keyblock. - */ -int -find_keyblock_byname( KBNODE *retblock, const char *username ) -{ - PKT_public_key *pk = gcry_xcalloc( 1, sizeof *pk ); - int rc; - - rc = get_pubkey_byname( NULL, pk, username, retblock ); - free_public_key(pk); - return rc; -} - - -/**************** - * Combined function to search for a key and get the position - * of the keyblock. Used for merging while importing keys. - */ -int -find_keyblock_bypk( KBNODE *retblock, PKT_public_key *pk ) -{ - char ufpr[50]; - - unified_fingerprint_from_pk( pk, ufpr, sizeof ufpr ); - return find_keyblock_byname( retblock, ufpr ); -} - -int -find_kblocation_bypk( void *re_opaque, PKT_public_key *pk ) -{ - PKT_public_key *dummy_pk = gcry_xcalloc( 1, sizeof *pk ); - char ufpr[50]; - GETKEY_CTX ctx; - int rc; - - unified_fingerprint_from_pk( pk, ufpr, sizeof ufpr ); - /* FIXME: There is no need to return any informaton, we just - * wnat to know the location. Using the general lookup function - * has the problem that we might not get the key becuase it has expired - * or due to some similar probelm. A solotion would be a locate-only - * flag in the ctx */ - rc = get_pubkey_byname( &ctx, dummy_pk, ufpr, NULL ); - free_public_key(dummy_pk); - if ( !rc ) - ringedit_copy_kbpos( re_opaque, ctx->kbpos ); - get_pubkey_end( ctx ); - - return rc; -} - /**************** * Search for a key with the given fingerprint. @@ -996,18 +834,21 @@ get_pubkey_byfprint( PKT_public_key *pk, 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; - memcpy( ctx.items[0].fprint, fprint, fprint_len ); + 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, NULL ); + pk_from_block ( &ctx, pk, kb ); release_kbnode ( kb ); get_pubkey_end( &ctx ); } else - rc = GPGERR_GENERAL; /* Oops */ + rc = G10ERR_GENERAL; /* Oops */ return rc; } @@ -1026,54 +867,27 @@ get_keyblock_byfprint( KBNODE *ret_keyblock, const byte *fprint, memset( &ctx, 0, sizeof ctx ); ctx.not_allocated = 1; + ctx.kr_handle = keydb_new (0); ctx.nitems = 1; - ctx.items[0].mode = fprint_len; - memcpy( ctx.items[0].fprint, fprint, fprint_len ); + 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 = GPGERR_GENERAL; /* Oops */ + rc = G10ERR_GENERAL; /* Oops */ return rc; } - -/**************** - * Search for a key with the given lid and return the entire keyblock - */ -int -get_keyblock_bylid( KBNODE *ret_keyblock, ulong lid ) -{ - int rc; - struct getkey_ctx_s ctx; - u32 kid[2]; - - if( keyid_from_lid( lid, kid ) ) - kid[0] = kid[1] = 0; - memset( &ctx, 0, sizeof ctx ); - ctx.not_allocated = 1; - ctx.nitems = 1; - ctx.items[0].mode = 12; - ctx.items[0].keyid[0] = kid[0]; - ctx.items[0].keyid[1] = kid[1]; - rc = lookup( &ctx, ret_keyblock, 0 ); - get_pubkey_end( &ctx ); - - return rc; -} - - - - - /**************** * Get a secret key by name and store it into sk * If NAME is NULL use the default key */ -int -get_seckey_byname( GETKEY_CTX *retctx, +static int +get_seckey_byname2( GETKEY_CTX *retctx, PKT_secret_key *sk, const char *name, int unprotect, KBNODE *retblock ) { @@ -1082,7 +896,7 @@ get_seckey_byname( GETKEY_CTX *retctx, if( !name && opt.def_secret_key && *opt.def_secret_key ) { add_to_strlist( &namelist, opt.def_secret_key ); - rc = key_byname( retctx, namelist, NULL, sk, retblock ); + rc = key_byname( retctx, namelist, NULL, sk, 1, retblock, NULL ); } else if( !name ) { /* use the first one as default key */ struct getkey_ctx_s ctx; @@ -1092,9 +906,9 @@ get_seckey_byname( GETKEY_CTX *retctx, assert (!retblock); memset( &ctx, 0, sizeof ctx ); ctx.not_allocated = 1; - ctx.primary = 1; + ctx.kr_handle = keydb_new (1); ctx.nitems = 1; - ctx.items[0].mode = 15; + ctx.items[0].mode = KEYDB_SEARCH_MODE_FIRST; rc = lookup( &ctx, &kb, 1 ); if (!rc && sk ) sk_from_block ( &ctx, sk, kb ); @@ -1103,7 +917,7 @@ get_seckey_byname( GETKEY_CTX *retctx, } else { add_to_strlist( &namelist, name ); - rc = key_byname( retctx, namelist, NULL, sk, retblock ); + rc = key_byname( retctx, namelist, NULL, sk, 1, retblock, NULL ); } free_strlist( namelist ); @@ -1114,11 +928,18 @@ get_seckey_byname( GETKEY_CTX *retctx, 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, ret_keyblock ); + return key_byname( retctx, names, NULL, sk, 1, ret_keyblock, NULL ); } @@ -1142,200 +963,41 @@ get_seckey_end( GETKEY_CTX ctx ) } - -/**************** - * Combined function to search for a username and get the position - * of the keyblock. This function does not unprotect the secret key. - */ -int -find_secret_keyblock_byname( KBNODE *retblock, const char *username ) -{ - PKT_secret_key *sk = gcry_xcalloc( 1, sizeof *sk ); - int rc; - - rc = get_seckey_byname( NULL, sk, username, 0, retblock ); - free_secret_key(sk); - return rc; -} - - - /**************** - * Combined function to search for a key and get the position - * of the keyblock. + * 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 -find_keyblock_bysk( KBNODE *retblock, PKT_secret_key *sk ) -{ - char ufpr[50]; - - unified_fingerprint_from_sk( sk, ufpr, sizeof ufpr ); - return find_secret_keyblock_byname( retblock, ufpr ); -} - -int -find_kblocation_bysk( void *re_opaque, PKT_secret_key *sk ) +get_seckey_byfprint( PKT_secret_key *sk, + const byte *fprint, size_t fprint_len) { - PKT_secret_key *dummy_sk = gcry_xcalloc( 1, sizeof *sk ); - char ufpr[50]; - GETKEY_CTX ctx; int rc; - - unified_fingerprint_from_sk( sk, ufpr, sizeof ufpr ); - rc = get_seckey_byname( &ctx, dummy_sk, ufpr, 0, NULL ); - free_secret_key(dummy_sk); - if ( !rc ) - ringedit_copy_kbpos( re_opaque, &ctx->kbpos ); - get_seckey_end( ctx ); - - return rc; -} - - - - -/******************************************************* - ************** compare functions ********************** - *******************************************************/ - -/**************** - * 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 = gcry_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; -} - + if( fprint_len == 20 || fprint_len == 16 ) { + struct getkey_ctx_s ctx; + KBNODE kb = NULL; - - -static int -compare_name( const char *uid, size_t uidlen, const char *name, int mode ) -{ - int i; - const char *s, *se; - - if( mode == 1 ) { /* exact match */ - for(i=0; name[i] && uidlen; i++, uidlen-- ) - if( uid[i] != name[i] ) - break; - if( !uidlen && !name[i] ) - return 0; /* found */ - } - else if( mode == 2 ) { /* case insensitive substring */ - if( memistr( uid, uidlen, name ) ) - return 0; - } - else if( mode >= 3 && mode <= 5 ) { /* look at the email address */ - 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 == 3 ) { /* exact email address */ - if( strlen(name)-2 == i && !memicmp( s, name+1, i) ) - return 0; - } - else if( mode == 4 ) { /* email substring */ - if( memistr( s, i, name ) ) - return 0; - } - else { /* email from end */ - /* nyi */ - } - } - } + 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_pubkey_end( &ctx ); } - else if( mode == 6 ) - return word_match( uid, uidlen, name ); else - BUG(); - - return -1; /* not found */ + rc = G10ERR_GENERAL; /* Oops */ + return rc; } - - /************************************************ ************* Merging stuff ******************** @@ -1344,7 +1006,11 @@ compare_name( const char *uid, size_t uidlen, const char *name, int mode ) /**************** * merge all selfsignatures with the keys. * FIXME: replace this at least for the public key parts - * by merge_selfsigs + * 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 ) @@ -1356,6 +1022,13 @@ merge_keys_and_selfsig( KBNODE keyblock ) 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 ) { @@ -1394,7 +1067,7 @@ merge_keys_and_selfsig( KBNODE keyblock ) const byte *p; u32 ed; - p = parse_sig_subpkt( sig->hashed_data, SIGSUBPKT_KEY_EXPIRE, NULL ); + 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 ) { @@ -1410,49 +1083,71 @@ merge_keys_and_selfsig( KBNODE keyblock ) } } } + + 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; } } - +/* + * 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 ) +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; - size_t n; + const byte *p, *sym, *hash, *zip; + size_t n, nsym, nhash, nzip; uid->created = 0; /* not created == invalid */ - if ( !signode ) - return; /* no self-signature */ - if ( IS_UID_REV ( sig ) ) + if ( IS_UID_REV ( sig ) ) { + uid->is_revoked = 1; return; /* has been revoked */ + } + + 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; + uid->expiredate = sig->expiredate; - uid->created = sig->timestamp; /* this one is okay */ - - /* store the key flags in the helper variable for later processing */ uid->help_key_usage = 0; - p = parse_sig_subpkt ( sig->hashed_data, SIGSUBPKT_KEY_FLAGS, &n ); + p = parse_sig_subpkt (sig->hashed, SIGSUBPKT_KEY_FLAGS, &n ); if ( p && n ) { /* first octet of the keyflags */ if ( (*p & 3) ) - uid->help_key_usage |= GCRY_PK_USAGE_SIGN; + uid->help_key_usage |= PUBKEY_USAGE_SIG; if ( (*p & 12) ) - uid->help_key_usage |= GCRY_PK_USAGE_ENCR; + uid->help_key_usage |= PUBKEY_USAGE_ENC; + /* Note: we do not set the CERT flag here because it can be assumed + * that thre is no real policy to set it. */ } /* ditto or the key expiration */ uid->help_key_expire = 0; - p = parse_sig_subpkt ( sig->hashed_data, SIGSUBPKT_KEY_EXPIRE, NULL); - if ( p ) { - uid->help_key_expire = sig->timestamp + buffer_to_u32(p); + p = parse_sig_subpkt (sig->hashed, SIGSUBPKT_KEY_EXPIRE, NULL); + if ( p ) { + uid->help_key_expire = keycreated + buffer_to_u32(p); } /* Set the primary user ID flag - we will later wipe out some - * of them to only have one in out keyblock */ + * of them to only have one in our keyblock */ uid->is_primary = 0; - p = parse_sig_subpkt ( sig->hashed_data, SIGSUBPKT_PRIMARY_UID, NULL ); + p = parse_sig_subpkt ( sig->hashed, SIGSUBPKT_PRIMARY_UID, NULL ); if ( p && *p ) uid->is_primary = 1; /* We could also query this from the unhashed area if it is not in @@ -1460,6 +1155,46 @@ fixup_uidnode ( KBNODE uidnode, KBNODE signode ) * 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) + m_free (uid->prefs); + n = nsym + nhash + nzip; + if (!n) + uid->prefs = NULL; + else { + uid->prefs = m_alloc (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->mdc_feature = 0; + p = parse_sig_subpkt (sig->hashed, SIGSUBPKT_FEATURES, &n); + if (p && n && (p[0] & 0x01)) + uid->mdc_feature = 1; + } static void @@ -1472,29 +1207,38 @@ merge_selfsigs_main( KBNODE keyblock, int *r_revoked ) 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; if ( keyblock->pkt->pkttype != PKT_PUBLIC_KEY ) BUG (); pk = keyblock->pkt->pkt.public_key; - pk->created = 0; + 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 noway to change it. So we also use only the - * one from the key packet */ - key_expire = pk->expiredate; + /* 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 */ + m_free(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_PUBLIC_SUBKEY; k = k->next ) { @@ -1505,30 +1249,82 @@ merge_selfsigs_main( KBNODE keyblock, int *r_revoked ) ; /* 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 can stop now. - * we can't cope with expiration times for revocations - * here because we have to assumethat an attacker can - * generate all kinds of signatures. + * 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; - return; } - else if ( IS_KEY_SIG (sig) && sig->timestamp >= sigdate ) { - const byte *p; - - p = parse_sig_subpkt( sig->hashed_data, - SIGSUBPKT_SIG_EXPIRE, NULL ); - if ( p && (sig->timestamp + buffer_to_u32(p)) >= curtime ) + 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= + m_realloc(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; - } + 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=m_realloc(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. @@ -1537,40 +1333,60 @@ merge_selfsigs_main( KBNODE keyblock, int *r_revoked ) const byte *p; size_t n; - p = parse_sig_subpkt ( sig->hashed_data, SIGSUBPKT_KEY_FLAGS, &n ); + p = parse_sig_subpkt (sig->hashed, SIGSUBPKT_KEY_FLAGS, &n ); if ( p && n ) { /* first octet of the keyflags */ if ( (*p & 3) ) - key_usage |= GCRY_PK_USAGE_SIGN; + key_usage |= PUBKEY_USAGE_SIG; if ( (*p & 12) ) - key_usage |= GCRY_PK_USAGE_ENCR; + key_usage |= PUBKEY_USAGE_ENC; } - if ( pk->version > 3 ) { - p = parse_sig_subpkt ( sig->hashed_data, - SIGSUBPKT_KEY_EXPIRE, NULL); - if ( p ) { - key_expire = sig->timestamp + buffer_to_u32(p); - key_expire_seen = 1; - } + p = parse_sig_subpkt (sig->hashed, SIGSUBPKT_KEY_EXPIRE, NULL); + if ( p ) { + key_expire = keytimestamp + buffer_to_u32(p); + key_expire_seen = 1; } - /* and set the created field */ - pk->created = sigdate; - /* and mark that key as valid: one direct key signature should + + /* 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. */ + + if(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])) + { + if(check_revocation_keys(pk,sig)) + ; /* did not verify, or loop broken */ + else + *r_revoked=1; + + /* 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 */ uiddate = 0; /* and over of all user IDs */ for(k=keyblock; k && k->pkt->pkttype != PKT_PUBLIC_SUBKEY; k = k->next ) { - if ( k->pkt->pkttype == PKT_USER_ID - || k->pkt->pkttype == PKT_PHOTO_ID ) { - if ( uidnode ) - fixup_uidnode ( uidnode, signode ); + if ( k->pkt->pkttype == PKT_USER_ID ) { + if ( uidnode && signode ) + fixup_uidnode ( uidnode, signode, keytimestamp ); uidnode = k; signode = NULL; if ( sigdate > uiddate ) @@ -1582,48 +1398,89 @@ merge_selfsigs_main( KBNODE keyblock, int *r_revoked ) 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)) { - /* Note: we allow to invalidated cert revocations + 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). - */ - const byte *p; - - p = parse_sig_subpkt( sig->hashed_data, - SIGSUBPKT_SIG_EXPIRE, NULL ); - if ( p && (sig->timestamp + buffer_to_u32(p)) >= curtime ) - ; /* signature/revocation has expired - ignore it */ + */ + if(sig->flags.expired) { + /* Expired uids don't get to be primary unless + they are the only uid there is. */ + uidnode->pkt->pkt.user_id->is_primary=0; + uidnode->pkt->pkt.user_id->is_expired=1; + uidnode->pkt->pkt.user_id->expiredate=sig->expiredate; + } else { sigdate = sig->timestamp; signode = k; + if( sig->version > sigversion ) + sigversion = sig->version; } } } } } - if ( uidnode ) { - fixup_uidnode ( uidnode, signode ); + if ( uidnode && signode ) { + fixup_uidnode ( uidnode, signode, keytimestamp ); pk->is_valid = 1; } - if ( sigdate > uiddate ) - uiddate = sigdate; - /* if we do not have a direct key signature, take the key creation date - * from the latest user ID. Hmmm, another possibilty would be to take - * it from the latest primary user ID - but we don't implement it for - * now */ - if ( !pk->created ) - pk->created = uiddate; - if ( !pk->created ) { - /* oops, still no creation date: use the timestamp */ - if (DBG_CACHE) - log_debug( "merge_selfsigs_main: " - "using timestamp as creation date\n"); - pk->created = pk->timestamp; - } + + /* 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 %08lX made valid by " + "--allow-non-selfsigned-uid\n"), + (ulong)keyid_from_pk(pk,NULL)); + + 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=m_alloc_clear(sizeof(*ultimate_pk)); + + if(get_pubkey(ultimate_pk,sig->keyid)==0 && + check_key_signature(keyblock,k,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 selfsigversion so we know if this is a v3 + key through and through, or a v3 key with a v4 selfsig, which + means we can trust the preferences (if any). */ + pk->selfsigversion=sigversion; /* Now that we had a look at all user IDs we can now get some information * from those user IDs. @@ -1634,8 +1491,7 @@ merge_selfsigs_main( KBNODE keyblock, int *r_revoked ) 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 - || k->pkt->pkttype == PKT_PHOTO_ID ) { + 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; @@ -1654,16 +1510,14 @@ merge_selfsigs_main( KBNODE keyblock, int *r_revoked ) } pk->pubkey_usage = key_usage; - if ( !key_expire_seen ) { /* find the latest valid user ID with a key expiration set - * Note, that this may be a diferent one from the above because + * 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 - || k->pkt->pkttype == PKT_PHOTO_ID ) { + 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; @@ -1672,17 +1526,23 @@ merge_selfsigs_main( KBNODE keyblock, int *r_revoked ) } } } - - pk->has_expired = key_expire >= curtime? 0 : key_expire; - /* FIXME: we should see how to get rid of the expiretime fields */ + /* 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->pkttype == PKT_PHOTO_ID ) { + 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 && uid->created > uiddate ) { uiddate = uid->created; @@ -1697,8 +1557,8 @@ merge_selfsigs_main( KBNODE keyblock, int *r_revoked ) if ( uidnode ) { for(k=keyblock; k && k->pkt->pkttype != PKT_PUBLIC_SUBKEY; k = k->next ) { - if ( k->pkt->pkttype == PKT_USER_ID - || k->pkt->pkttype == PKT_PHOTO_ID ) { + 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; @@ -1709,7 +1569,23 @@ merge_selfsigs_main( KBNODE keyblock, int *r_revoked ) /* none is flagged primary - use the latest user ID we have */ uidnode2->pkt->pkt.user_id->is_primary = 1; } - + else + { + /* None of our uids were self-signed, so pick the first one to + be the primary. This is the best we can do here since + there are no self sigs to date the uids. */ + + 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=1; + break; + } + } + } } @@ -1724,6 +1600,7 @@ merge_selfsigs_subkey( KBNODE keyblock, KBNODE subnode ) KBNODE signode; u32 curtime = make_timestamp (); unsigned int key_usage = 0; + u32 keytimestamp = 0; u32 key_expire = 0; const byte *p; size_t n; @@ -1735,11 +1612,11 @@ merge_selfsigs_subkey( KBNODE keyblock, KBNODE subnode ) 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]; - if ( subpk->version < 4 ) - return; /* there are no v3 subkeys */ /* find the latest key binding self-signature. */ signode = NULL; @@ -1752,17 +1629,13 @@ merge_selfsigs_subkey( KBNODE keyblock, KBNODE subnode ) if ( check_key_signature( keyblock, k, NULL ) ) ; /* signature did not verify */ else if ( IS_SUBKEY_REV (sig) ) { - /* key has been revoked - given the fact that it is easy - * to create a new subkey, it does not make sense to - * revive a revoked key. So we can stop here. - */ subpk->is_revoked = 1; - return; + /* 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 ) { - p = parse_sig_subpkt( sig->hashed_data, - SIGSUBPKT_SIG_EXPIRE, NULL ); - if ( p && (sig->timestamp + buffer_to_u32(p)) >= curtime ) + if(sig->flags.expired) ; /* signature has expired - ignore it */ else { sigdate = sig->timestamp; @@ -1774,21 +1647,19 @@ merge_selfsigs_subkey( KBNODE keyblock, KBNODE subnode ) } if ( !signode ) { - subpk->created = subpk->timestamp; return; /* no valid key binding */ } subpk->is_valid = 1; - subpk->created = sigdate; sig = signode->pkt->pkt.signature; - p = parse_sig_subpkt ( sig->hashed_data, SIGSUBPKT_KEY_FLAGS, &n ); + p = parse_sig_subpkt (sig->hashed, SIGSUBPKT_KEY_FLAGS, &n ); if ( p && n ) { /* first octet of the keyflags */ if ( (*p & 3) ) - key_usage |= GCRY_PK_USAGE_SIGN; + key_usage |= PUBKEY_USAGE_SIG; if ( (*p & 12) ) - key_usage |= GCRY_PK_USAGE_ENCR; + key_usage |= PUBKEY_USAGE_ENC; } if ( !key_usage ) { /* no key flags at all: get it from the algo */ key_usage = openpgp_pk_algo_usage ( subpk->pubkey_algo ); @@ -1800,12 +1671,13 @@ merge_selfsigs_subkey( KBNODE keyblock, KBNODE subnode ) } subpk->pubkey_usage = key_usage; - p = parse_sig_subpkt ( sig->hashed_data, SIGSUBPKT_KEY_EXPIRE, NULL); + p = parse_sig_subpkt (sig->hashed, SIGSUBPKT_KEY_EXPIRE, NULL); if ( p ) - key_expire = sig->timestamp + 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; } @@ -1828,9 +1700,20 @@ merge_selfsigs( KBNODE keyblock ) KBNODE k; int revoked; PKT_public_key *main_pk; - - if ( keyblock->pkt->pkttype != PKT_PUBLIC_KEY ) + 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 ); main_pk = keyblock->pkt->pkt.public_key; @@ -1855,6 +1738,44 @@ merge_selfsigs( KBNODE keyblock ) merge_selfsigs_subkey ( keyblock, k ); } } + + /* If the main key is not valid, then the subkeys aren't either, + even if they have binding sigs. */ + if(!main_pk->is_valid) + for(k=keyblock; k; k=k->next) + if(k->pkt->pkttype==PKT_PUBLIC_SUBKEY) + k->pkt->pkt.public_key->is_valid=0; + + /* 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->mdc_feature; + 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) + m_free (pk->prefs); + pk->prefs = copy_prefs (prefs); + pk->mdc_feature = mdc_feature; + } + } } @@ -1865,7 +1786,7 @@ merge_selfsigs( KBNODE keyblock ) * keys at all and have a way to store just the real secret parts * from the key. */ -void +static void merge_public_with_secret ( KBNODE pubblock, KBNODE secblock ) { KBNODE pub; @@ -1898,7 +1819,7 @@ merge_public_with_secret ( KBNODE pubblock, KBNODE secblock ) if ( !cmp_public_secret_key ( pk, sk ) ) { copy_public_parts_to_secret_key ( pk, sk ); free_public_key ( pk ); - pub->pkt->pkttype = PKT_SECRET_KEY; + pub->pkt->pkttype = PKT_SECRET_SUBKEY; pub->pkt->pkt.secret_key = copy_secret_key (NULL, sk); break; } @@ -1911,11 +1832,12 @@ merge_public_with_secret ( KBNODE pubblock, KBNODE secblock ) } /* This function checks that for every public subkey a corresponding - * secret subkey is avalable and deletes the public subkey otherwise. - * We need this function becuase we can'tdelete it later when we + * 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. */ -void +static void premerge_public_with_secret ( KBNODE pubblock, KBNODE secblock ) { KBNODE last, pub; @@ -1924,6 +1846,7 @@ premerge_public_with_secret ( KBNODE pubblock, KBNODE secblock ) 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; @@ -1931,14 +1854,25 @@ premerge_public_with_secret ( KBNODE pubblock, KBNODE secblock ) 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 ( !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; + } + /* transfer flag bits 0 and 1 to the pubblock */ + pub->flag |= (sec->flag &3); break; + } } } if ( !sec ) { KBNODE next, ll; - log_error ( "no corresponding secret subkey " - "for public subkey - removing\n" ); + + log_info ( "no secret subkey " + "for public subkey %08lX - ignoring\n", + (ulong)keyid_from_pk (pk,NULL) ); /* we have to remove the subkey in this case */ assert ( last ); /* find the next subkey */ @@ -1956,108 +1890,25 @@ premerge_public_with_secret ( KBNODE pubblock, KBNODE secblock ) } } } + /* 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); } -/************************************************ - ************* Find stuff *********************** - ************************************************/ - -static int -find_by_name( KBNODE keyblock, const char *name, - int mode, byte *namehash ) -{ - KBNODE k; - - for(k=keyblock; k; k = k->next ) { - if( k->pkt->pkttype == PKT_USER_ID - && !compare_name( k->pkt->pkt.user_id->name, - k->pkt->pkt.user_id->len, name, mode)) { - /* we found a matching name, look for the key */ - if( k->pkt->pkt.user_id->photo ) { - /* oops: this can never happen */ - gcry_md_hash_buffer( GCRY_MD_RMD160, namehash, - k->pkt->pkt.user_id->photo, - k->pkt->pkt.user_id->photolen ); - } - else { - gcry_md_hash_buffer( GCRY_MD_RMD160, namehash, - k->pkt->pkt.user_id->name, - k->pkt->pkt.user_id->len ); - } - return 1; - } - } - - return 0; -} - - - -static KBNODE -find_by_keyid( KBNODE keyblock, u32 *keyid, int mode ) -{ - KBNODE k; - - for(k=keyblock; 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[1] == keyid[1] && ( mode == 10 || aki[0] == keyid[0] ) ) { - return k; /* found */ - } - } - } - return NULL; -} - - - -static KBNODE -find_by_fpr( KBNODE keyblock, const char *name, int mode ) -{ - KBNODE k; - - for(k=keyblock; k; k = k->next ) { - if( k->pkt->pkttype == PKT_PUBLIC_KEY - || k->pkt->pkttype == PKT_PUBLIC_SUBKEY ) { - byte afp[MAX_FINGERPRINT_LEN]; - size_t an; - - fingerprint_from_pk(k->pkt->pkt.public_key, afp, &an ); - if ( mode == 21 ) { - /* Unified fingerprint. The fingerprint is always 20 bytes*/ - while ( an < 20 ) - afp[an++] = 0; - if ( !memcmp( afp, name, 20 ) ) - return k; - } - else { - if( an == mode && !memcmp( afp, name, an) ) { - return k; - } - } - } - } - return NULL; -} - - - - /* See see whether the key fits * our requirements and in case we do not - * request a the primary key, we should select + * 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: + * 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. @@ -2069,35 +1920,79 @@ find_by_fpr( KBNODE keyblock, const char *name, int mode ) * 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 foundk ) +finish_lookup (GETKEY_CTX ctx) { KBNODE keyblock = ctx->keyblock; KBNODE k; - #define USAGE_MASK (GCRY_PK_USAGE_SIGN|GCRY_PK_USAGE_ENCR) + KBNODE foundk = NULL; + PKT_user_id *foundu = NULL; + #define USAGE_MASK (PUBKEY_USAGE_SIG|PUBKEY_USAGE_ENC) 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. */ + int req_prim = (ctx->req_usage & PUBKEY_USAGE_CERT) || + ((opt.pgp6 || opt.pgp7) && (ctx->req_usage & PUBKEY_USAGE_SIG)); u32 latest_date; KBNODE latest_key; + u32 curtime = make_timestamp (); - assert( !foundk || foundk->pkt->pkttype == PKT_PUBLIC_KEY - || foundk->pkt->pkttype == PKT_PUBLIC_SUBKEY ); 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 %s (req_usage=%x)\n", - foundk? "one key":"all keys", req_usage); + 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; - /* We do check the subkeys only if we either have requested a specific - * usage or have not requested to get the primary key. */ - if ( (req_usage || !ctx->primary) - && (!foundk || foundk->pkt->pkttype == PKT_PUBLIC_SUBKEY) ) { + /* 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 ) { @@ -2108,6 +2003,9 @@ finish_lookup( GETKEY_CTX ctx, KBNODE foundk ) 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"); @@ -2123,9 +2021,13 @@ finish_lookup( GETKEY_CTX ctx, KBNODE foundk ) 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 ( req_usage && - !((pk->pubkey_usage&USAGE_MASK) & req_usage) ) { + 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 ); @@ -2133,18 +2035,19 @@ finish_lookup( GETKEY_CTX ctx, KBNODE foundk ) } if (DBG_CACHE) - log_debug( "\tconsidering key %08lX\n", - (ulong)keyid_from_pk( pk, NULL)); - if ( pk->created > latest_date ) { - latest_date = pk->created; + log_debug( "\tsubkey looks fine\n"); + if ( pk->timestamp > latest_date ) { + latest_date = pk->timestamp; latest_key = k; } } } - if ( !latest_key ) { + /* 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 ) + 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 ) { @@ -2159,17 +2062,17 @@ finish_lookup( GETKEY_CTX ctx, KBNODE foundk ) if (DBG_CACHE) log_debug( "\tprimary key has expired\n"); } - else if ( req_usage - && !((pk->pubkey_usage&USAGE_MASK) & req_usage) ) { + else if ( !((pk->pubkey_usage&USAGE_MASK) & req_usage) ) { if (DBG_CACHE) - log_debug( "\tusage does not match: want=%x have=%x\n", + 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->created; + latest_date = pk->timestamp; } } @@ -2179,13 +2082,21 @@ finish_lookup( GETKEY_CTX ctx, KBNODE foundk ) 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 ) { + if (latest_key != keyblock && opt.verbose) { log_info(_("using secondary key %08lX " "instead of primary key %08lX\n"), (ulong)keyid_from_pk( latest_key->pkt->pkt.public_key, NULL), @@ -2197,109 +2108,91 @@ finish_lookup( GETKEY_CTX ctx, KBNODE foundk ) return 1; /* found */ } - + static int lookup( GETKEY_CTX ctx, KBNODE *ret_keyblock, int secmode ) { int rc; - int oldmode = set_packet_list_mode(0); - byte namehash[20]; - int use_namehash=0; KBNODE secblock = NULL; /* helper */ - - if( !ctx->count ) /* first time */ - rc = enum_keyblocks_begin( &ctx->kbpos, secmode ); - else - rc = 0; - if( !rc ) { - while( !(rc = enum_keyblocks_next( ctx->kbpos, 1, &ctx->keyblock )) ) { - int n; - getkey_item_t *item; - - 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 ) { - log_info(_("key %08lX: secret key without public key " + 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 %08lX: secret key without public key " "- skipped\n"), (ulong)aki[1] ); - goto skip; - } - secblock = ctx->keyblock; - ctx->keyblock = k; - premerge_public_with_secret ( ctx->keyblock, secblock ); + goto skip; } + secblock = ctx->keyblock; + ctx->keyblock = k; + premerge_public_with_secret ( ctx->keyblock, secblock ); + } - /* loop over all the user ids we want to look for */ - item = ctx->items; - for(n=0; n < ctx->nitems; n++, item++ ) { - KBNODE k = NULL; - int found = 0; - - if( item->mode < 10 ) { - found = find_by_name( ctx->keyblock, - item->name, item->mode, - namehash ); - use_namehash = found; - } - else if( item->mode >= 10 && item->mode <= 12 ) { - k = find_by_keyid( ctx->keyblock, - item->keyid, item->mode ); - found = !!k; - } - else if( item->mode == 15 ) { - found = 1; - } - else if( item->mode == 16 || item->mode == 20 - || item->mode == 21 ) { - k = find_by_fpr( ctx->keyblock, - item->fprint, item->mode ); - found = !!k; - } - else - BUG(); - if( found ) { - /* this keyblock looks fine - do further investigation */ - merge_selfsigs ( ctx->keyblock ); - if ( finish_lookup( ctx, k ) ) { - if ( secmode ) { - merge_public_with_secret ( ctx->keyblock, - secblock); - release_kbnode (secblock); - secblock = NULL; - } - goto found; - } - } - } - skip: - /* release resources and try the next keyblock */ + /* 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 ) { - release_kbnode( secblock ); + merge_public_with_secret ( ctx->keyblock, + secblock); + release_kbnode (secblock); secblock = NULL; } - release_kbnode( ctx->keyblock ); - ctx->keyblock = NULL; - } - found: - ; + 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("enum_keyblocks failed: %s\n", gpg_errstr(rc)); + 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 ? GPGERR_NO_SECKEY : GPGERR_NO_PUBKEY; + rc = secmode ? G10ERR_NO_SECKEY : G10ERR_NO_PUBKEY; if ( secmode ) { release_kbnode( secblock ); @@ -2307,36 +2200,14 @@ lookup( GETKEY_CTX ctx, KBNODE *ret_keyblock, int secmode ) } release_kbnode( ctx->keyblock ); ctx->keyblock = NULL; - set_packet_list_mode(oldmode); - #if 0 - if( opt.debug & DBG_MEMSTAT_VALUE ) { - static int initialized; - - if( !initialized ) { - initialized = 1; - atexit( print_stats ); - } - - assert( ctx->mode < DIM(lkup_stats) ); - lkup_stats[ctx->mode].any = 1; - if( !rc ) - lkup_stats[ctx->mode].okay_count++; - else if ( rc == GPGERR_NO_PUBKEY || rc == GPGERR_NO_SECKEY ) - lkup_stats[ctx->mode].nokey_count++; - else - lkup_stats[ctx->mode].error_count++; - } - #endif ctx->last_rc = rc; - ctx->count++; return rc; } - /**************** * FIXME: Replace by the generic function * It does not work as it is right now - it is used at @@ -2352,34 +2223,33 @@ lookup( GETKEY_CTX ctx, KBNODE *ret_keyblock, int secmode ) * 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 rc=0; - PACKET pkt; - int save_mode; struct { int eof; - int sequence; - const char *name; - IOBUF iobuf; + int first; + KEYDB_HANDLE hd; + KBNODE keyblock; + KBNODE node; } *c = *context; if( !c ) { /* make a new context */ - c = gcry_xcalloc( 1, sizeof *c ); + c = m_alloc_clear( sizeof *c ); *context = c; - c->sequence = 0; - c->name = enum_keyblock_resources( &c->sequence, 1 ); + c->hd = keydb_new (1); + c->first = 1; + c->keyblock = NULL; + c->node = NULL; } if( !sk ) { /* free the context */ - if( c->iobuf ) - iobuf_close(c->iobuf); - gcry_free( c ); + keydb_release (c->hd); + release_kbnode (c->keyblock); + m_free( c ); *context = NULL; return 0; } @@ -2387,33 +2257,33 @@ enum_secret_keys( void **context, PKT_secret_key *sk, int with_subkeys ) if( c->eof ) return -1; - /* FIXME: This assumes a plain keyring file */ - for( ; c->name; c->name = enum_keyblock_resources( &c->sequence, 1 ) ) { - if( !c->iobuf ) { - if( !(c->iobuf = iobuf_open( c->name ) ) ) { - log_error("enum_secret_keys: can't open `%s'\n", c->name ); - continue; /* try next file */ - } - } + 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) ) { + 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); - save_mode = set_packet_list_mode(0); - init_packet(&pkt); - while( (rc=parse_packet(c->iobuf, &pkt, NULL)) != -1 ) { - if( rc ) - ; /* e.g. unknown packet */ - else if( pkt.pkttype == PKT_SECRET_KEY - || ( with_subkeys && pkt.pkttype == PKT_SECRET_SUBKEY ) ) { - copy_secret_key( sk, pkt.pkt.secret_key ); - set_packet_list_mode(save_mode); - return 0; /* found */ - } - free_packet(&pkt); - } - set_packet_list_mode(save_mode); - iobuf_close(c->iobuf); c->iobuf = NULL; - } - c->eof = 1; - return -1; + return rc; /* error */ } @@ -2438,7 +2308,7 @@ get_user_id_string( u32 *keyid ) keyid_list_t a; for (a=r->keyids; a; a= a->next ) { if( a->keyid[0] == keyid[0] && a->keyid[1] == keyid[1] ) { - p = gcry_xmalloc( r->len + 10 ); + p = m_alloc( r->len + 10 ); sprintf(p, "%08lX %.*s", (ulong)keyid[1], r->len, r->name ); return p; @@ -2446,7 +2316,7 @@ get_user_id_string( u32 *keyid ) } } } while( ++pass < 2 && !get_pubkey( NULL, keyid ) ); - p = gcry_xmalloc( 15 ); + p = m_alloc( 15 ); sprintf(p, "%08lX [?]", (ulong)keyid[1] ); return p; } @@ -2456,9 +2326,9 @@ char* get_user_id_string_native( u32 *keyid ) { char *p = get_user_id_string( keyid ); - char *p2 = utf8_to_native( p, strlen(p) ); + char *p2 = utf8_to_native( p, strlen(p), 0 ); - gcry_free(p); + m_free(p); return p2; } @@ -2475,7 +2345,7 @@ get_long_user_id_string( u32 *keyid ) keyid_list_t a; for (a=r->keyids; a; a= a->next ) { if( a->keyid[0] == keyid[0] && a->keyid[1] == keyid[1] ) { - p = gcry_xmalloc( r->len + 20 ); + p = m_alloc( r->len + 20 ); sprintf(p, "%08lX%08lX %.*s", (ulong)keyid[0], (ulong)keyid[1], r->len, r->name ); @@ -2484,7 +2354,7 @@ get_long_user_id_string( u32 *keyid ) } } } while( ++pass < 2 && !get_pubkey( NULL, keyid ) ); - p = gcry_xmalloc( 25 ); + p = m_alloc( 25 ); sprintf(p, "%08lX%08lX [?]", (ulong)keyid[0], (ulong)keyid[1] ); return p; } @@ -2502,7 +2372,7 @@ get_user_id( u32 *keyid, size_t *rn ) keyid_list_t a; for (a=r->keyids; a; a= a->next ) { if( a->keyid[0] == keyid[0] && a->keyid[1] == keyid[1] ) { - p = gcry_xmalloc( r->len ); + p = m_alloc( r->len ); memcpy(p, r->name, r->len ); *rn = r->len; return p; @@ -2510,9 +2380,84 @@ get_user_id( u32 *keyid, size_t *rn ) } } } while( ++pass < 2 && !get_pubkey( NULL, keyid ) ); - p = gcry_xstrdup( _("[User id not found]") ); + p = m_strdup( _("[User id not found]") ); *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 ); + + m_free(p); + return p2; +} + +KEYDB_HANDLE +get_ctx_handle(GETKEY_CTX ctx) +{ + return ctx->kr_handle; +} + +/* 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 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 */ +/* Return 0 if pk is revoked, non-0 if not revoked */ +static int +check_revocation_keys(PKT_public_key *pk,PKT_signature *sig) +{ + static int busy=0; + int i,rc=-1; + + assert(IS_KEY_REV(sig)); + assert((sig->keyid[0]!=pk->keyid[0]) || (sig->keyid[0]!=pk->keyid[1])); + + if(busy) + { + /* return -1 (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 -1; + } + + 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); + if(signature_check(sig,md)==0) { + rc=0; + break; + } + } + } + + busy=0; + + return rc; +} diff --git a/g10/basicdefs.h b/g10/global.h index 1a7f0901a..3c4e59ec4 100644 --- a/g10/basicdefs.h +++ b/g10/global.h @@ -1,5 +1,5 @@ -/* basicdefs.h - Some definitions used at many place - * Copyright (C) 1999 Free Software Foundation, Inc. +/* global.h - Local typedefs and constants + * Copyright (C) 2001 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -18,23 +18,12 @@ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA */ -#ifndef GPG_BASICDEFS_H -#define GPG_BASICDEFS_H +#ifndef GPG_GLOBAL_H +#define GPG_GLOBAL_H -#include "types.h" +#define MAX_FINGERPRINT_LEN 20 -typedef struct { - int algo; - int keylen; - byte key[32]; /* this is the largest used keylen (256 bit) */ -} DEK; +typedef struct kbnode_struct *KBNODE; +typedef struct keydb_search_desc KEYDB_SEARCH_DESC; - -struct pk_list; -struct sk_list; -typedef struct pk_list *PK_LIST; -typedef struct sk_list *SK_LIST; - - - -#endif /* GPG_BASICDEFS_H */ +#endif /*GPG_GLOBAL_H*/ diff --git a/g10/gpgd.c b/g10/gpgd.c index 8ca37d34a..365556604 100644 --- a/g10/gpgd.c +++ b/g10/gpgd.c @@ -1,5 +1,5 @@ -/* ggpd.c - The GnuPG daemon (keyserver) - * Copyright (C) 1998 Free Software Foundation, Inc. +/* gpd.c - The GnuPG daemon (keyserver) + * Copyright (C) 1998, 1999 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -112,7 +112,7 @@ build_list( const char *text, const char * (*mapf)(int), int (*chkf)(int) ) for(i=1; i < 100; i++ ) if( !chkf(i) && (s=mapf(i)) ) n += strlen(s) + 2; - list = gcry_xmalloc( 21 + n ); *list = 0; + list = m_alloc( 21 + n ); *list = 0; for(p=NULL, i=1; i < 100; i++ ) { if( !chkf(i) && (s=mapf(i)) ) { if( !p ) @@ -201,9 +201,9 @@ main( int argc, char **argv ) else { log_error("option file `%s': %s\n", configname, strerror(errno) ); - gpg_exit(1); + g10_exit(1); } - gcry_free(configname); configname = NULL; + m_free(configname); configname = NULL; } if( parse_debug && configname ) log_info("reading options from `%s'\n", configname ); @@ -216,8 +216,8 @@ main( int argc, char **argv ) case 'v': opt.verbose++; break; case 501: if( !configfp ) { - gcry_free(configname); - configname = gcry_xstrdup(pargs.r.ret_str); + m_free(configname); + configname = m_strdup(pargs.r.ret_str); goto next_pass; } break; @@ -230,12 +230,12 @@ main( int argc, char **argv ) if( configfp ) { fclose( configfp ); configfp = NULL; - gcry_free(configname); configname = NULL; + m_free(configname); configname = NULL; goto next_pass; } - gcry_free( configname ); configname = NULL; + m_free( configname ); configname = NULL; if( log_get_errorcount(0) ) - gpg_exit(2); + g10_exit(2); fprintf(stderr, "%s %s; %s\n", strusage(11), strusage(13), strusage(14) ); fprintf(stderr, "%s\n", strusage(15) ); @@ -245,13 +245,13 @@ main( int argc, char **argv ) become_daemon(); - gpg_exit(0); + g10_exit(0); return 8; /*NEVER REACHED*/ } void -gpg_exit( int rc ) +g10_exit( int rc ) { secmem_term(); rc = rc? rc : log_get_errorcount(0)? 2:0; diff --git a/g10/gpgv.c b/g10/gpgv.c new file mode 100644 index 000000000..362f5c528 --- /dev/null +++ b/g10/gpgv.c @@ -0,0 +1,372 @@ +/* gpgv.c - The GnuPG signature verify utility + * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, 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 + +#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" + + +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; + +#ifdef __riscos__ +RISCOS_GLOBAL_STATICS("GnuPG (gpgv) Heap") +#endif /* __riscos__ */ + +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 ); + #else + #ifdef ENABLE_NLS + #ifdef HAVE_LC_MESSAGES + setlocale( LC_TIME, "" ); + setlocale( LC_MESSAGES, "" ); + #else + setlocale( LC_ALL, "" ); + #endif + 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; + + #ifdef __riscos__ + riscos_global_defaults(); + #endif /* __riscos__ */ + + log_set_name("gpgv"); + init_signals(); + i18n_init(); + opt.command_fd = -1; /* no command fd */ + opt.pgp2_workarounds = 1; + opt.keyserver_options.auto_key_retrieve = 1; + opt.always_trust = 1; + opt.batch = 1; + + #if defined (__MINGW32__) || defined (__CYGWIN32__) + opt.homedir = read_w32_registry_string( NULL, "Software\\GNU\\GnuPG", "HomeDir" ); + #else + opt.homedir = getenv("GNUPGHOME"); + #endif + if( !opt.homedir || !*opt.homedir ) { + opt.homedir = GNUPG_HOMEDIR; + } + tty_no_terminal(1); + tty_batchmode(1); + disable_dotlock(); + + 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; + 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; +} + + +/* Stub: + * We don't have the trustdb , so we have to provide some stub functions + * instead + */ +int +get_validity_info (PKT_public_key *pk, const byte *namehash ) +{ + return '?'; +} + +/* Stub: */ +int +get_ownertrust_info (PKT_public_key *pk) +{ + return '?'; +} + +unsigned int +get_ownertrust (PKT_public_key *pk) +{ + return TRUST_UNKNOWN; +} + + +/* Stub: + * Because we only work with trusted keys, it does not make sense to + * get them from a keyserver + */ +int +keyserver_import_keyid( u32 *keyid, void *dummy ) +{ + 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) +{ + return NULL; +} + +/* 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;} + +/* 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_print_string( byte *p, size_t n ) { } +void tty_print_utf8_string( byte *p, size_t n ) {} +void tty_print_utf8_string2( 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;} + +/* 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; } +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 index 4a7a14fde..0150c549c 100644 --- a/g10/helptext.c +++ b/g10/helptext.c @@ -1,5 +1,5 @@ /* helptext.c - English help texts - * Copyright (C) 1998, 1999, 2000 Free Software Foundation, Inc. + * Copyright (C) 1998, 1999, 2000, 2001, 2002 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -49,6 +49,13 @@ static struct helptexts { const char *key; const char *help; } helptexts[] = { "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" +)}, + { "revoked_key.override", N_( "If you want to use this revoked key anyway, answer \"yes\"." )}, @@ -87,6 +94,12 @@ static struct helptexts { const char *key; const char *help; } helptexts[] = { "with them are quite large and very slow to verify." )}, +{ "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" @@ -145,6 +158,29 @@ static struct helptexts { const char *key; const char *help; } helptexts[] = { "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\"" @@ -197,11 +233,16 @@ static struct helptexts { const char *key; const char *help; } helptexts[] = { "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" -" Blurb, blurb,.... " )}, @@ -231,7 +272,7 @@ static struct helptexts { const char *key; const char *help; } helptexts[] = { " \"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 superseeded\"\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" @@ -271,5 +312,3 @@ display_online_help( const char *keyword ) } tty_printf("\n"); } - - @@ -1,5 +1,5 @@ -/* hkp.c - Horrowitz Keyserver Protocol - * Copyright (C) 1999 Free Software Foundation, Inc. +/* hkp.c - Horowitz Keyserver Protocol + * Copyright (C) 1998, 1999, 2000, 2001, 2002 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -34,6 +34,7 @@ #include "filter.h" #include "http.h" #include "main.h" +#include "keyserver-internal.h" static int urlencode_filter( void *opaque, int control, IOBUF a, byte *buf, size_t *ret_len); @@ -47,95 +48,72 @@ static int urlencode_filter( void *opaque, int control, * or other error codes. */ int -hkp_ask_import( u32 *keyid ) +hkp_ask_import( KEYDB_SEARCH_DESC *desc, void *stats_handle) { - #ifdef HAVE_DOSISH_SYSTEM - return -1; - #else struct http_context hd; char *request; int rc; - unsigned int hflags = opt.honor_http_proxy? HTTP_FLAG_TRY_PROXY : 0; + unsigned int hflags = opt.keyserver_options.honor_http_proxy? HTTP_FLAG_TRY_PROXY : 0; + u32 key[2]; - if( !opt.keyserver_name ) - return -1; - log_info(_("requesting key %08lX from %s ...\n"), (ulong)keyid[1], - opt.keyserver_name ); - request = gcry_xmalloc( strlen( opt.keyserver_name ) + 100 ); + if(desc->mode==KEYDB_SEARCH_MODE_FPR20) + keyid_from_fingerprint(desc->u.fpr,MAX_FINGERPRINT_LEN,key); + else if(desc->mode==KEYDB_SEARCH_MODE_LONG_KID || + desc->mode==KEYDB_SEARCH_MODE_SHORT_KID) + { + key[0]=desc->u.kid[0]; + key[1]=desc->u.kid[1]; + } + else + return -1; /* HKP does not support v3 fingerprints */ + + log_info(_("requesting key %08lX from HKP keyserver %s\n"), + (ulong)key[1],opt.keyserver_host ); + request = m_alloc( strlen( opt.keyserver_host ) + 100 ); /* hkp does not accept the long keyid - we should really write a * nicer one :-) * FIXME: request binary mode - need to pass no_armor mode * down to the import function. Marc told that there is such a * binary mode ... how? */ - sprintf( request, "x-hkp://%s:11371/pks/lookup?op=get&search=0x%08lX", - opt.keyserver_name, (ulong)keyid[1] ); + + if(opt.keyserver_options.broken_http_proxy) + hflags |= HTTP_FLAG_NO_SHUTDOWN; + + sprintf(request,"x-hkp://%s%s%s/pks/lookup?op=get&search=0x%08lX", + opt.keyserver_host, + atoi(opt.keyserver_port)>0?":":"", + atoi(opt.keyserver_port)>0?opt.keyserver_port:"", + (ulong)key[1] ); + + if(opt.keyserver_options.verbose>2) + log_info("request is \"%s\"\n",request); + rc = http_open_document( &hd, request, hflags ); if( rc ) { log_info(_("can't get key from keyserver: %s\n"), - rc == GPGERR_NETWORK? strerror(errno) - : gpg_errstr(rc) ); + rc == G10ERR_NETWORK? strerror(errno) + : g10_errstr(rc) ); } else { - rc = import_keys_stream( hd.fp_read , 0 ); + rc = import_keys_stream( hd.fp_read, 0, stats_handle); http_close( &hd ); } - gcry_free( request ); + m_free( request ); return rc; - #endif -} - - - -int -hkp_import( STRLIST users ) -{ - #ifdef HAVE_DOSISH_SYSTEM - return -1; - #else - if( !opt.keyserver_name ) { - log_error(_("no keyserver known (use option --keyserver)\n")); - return -1; - } - - for( ; users; users = users->next ) { - u32 kid[2]; - int type = classify_user_id( users->d, kid, NULL, NULL, NULL ); - if( type != 10 && type != 11 ) { - log_info(_("%s: not a valid key ID\n"), users->d ); - continue; - } - /* because the function may use log_info in some situations, the - * errorcounter ist not increaed and the program will return - * with success - which is not good when this function is used. - */ - if( hkp_ask_import( kid ) ) - log_inc_errorcount(); - } - return 0; - #endif } - int hkp_export( STRLIST users ) { - #ifdef HAVE_DOSISH_SYSTEM - return -1; - #else int rc; armor_filter_context_t afx; IOBUF temp = iobuf_temp(); struct http_context hd; char *request; unsigned int status; - unsigned int hflags = opt.honor_http_proxy? HTTP_FLAG_TRY_PROXY : 0; - - if( !opt.keyserver_name ) { - log_error(_("no keyserver known (use option --keyserver)\n")); - return -1; - } + unsigned int hflags = opt.keyserver_options.honor_http_proxy? HTTP_FLAG_TRY_PROXY : 0; iobuf_push_filter( temp, urlencode_filter, NULL ); @@ -151,23 +129,34 @@ hkp_export( STRLIST users ) iobuf_flush_temp( temp ); - request = gcry_xmalloc( strlen( opt.keyserver_name ) + 100 ); - sprintf( request, "x-hkp://%s:11371/pks/add", opt.keyserver_name ); + request = m_alloc( strlen( opt.keyserver_host ) + 100 ); + + if(opt.keyserver_options.broken_http_proxy) + hflags |= HTTP_FLAG_NO_SHUTDOWN; + + sprintf( request, "x-hkp://%s%s%s/pks/add", + opt.keyserver_host, + atoi(opt.keyserver_port)>0?":":"", + atoi(opt.keyserver_port)>0?opt.keyserver_port:""); + + if(opt.keyserver_options.verbose>2) + log_info("request is \"%s\"\n",request); + rc = http_open( &hd, HTTP_REQ_POST, request , hflags ); if( rc ) { log_error(_("can't connect to `%s': %s\n"), - opt.keyserver_name, - rc == GPGERR_NETWORK? strerror(errno) - : gpg_errstr(rc) ); + opt.keyserver_host, + rc == G10ERR_NETWORK? strerror(errno) + : g10_errstr(rc) ); iobuf_close(temp); - gcry_free( request ); + m_free( request ); return rc; } sprintf( request, "Content-Length: %u\n", (unsigned)iobuf_get_temp_length(temp) + 9 ); iobuf_writestr( hd.fp_write, request ); - gcry_free( request ); + m_free( request ); http_start_data( &hd ); iobuf_writestr( hd.fp_write, "keytext=" ); @@ -180,26 +169,28 @@ hkp_export( STRLIST users ) rc = http_wait_response( &hd, &status ); if( rc ) { log_error(_("error sending to `%s': %s\n"), - opt.keyserver_name, gpg_errstr(rc) ); + opt.keyserver_host, g10_errstr(rc) ); } else { #if 1 if( opt.verbose ) { int c; while( (c=iobuf_get(hd.fp_read)) != EOF ) + if ( c >= 32 && c < 127 ) putchar( c ); + else + putchar ( '?' ); } #endif if( (status/100) == 2 ) log_info(_("success sending to `%s' (status=%u)\n"), - opt.keyserver_name, status ); + opt.keyserver_host, status ); else log_error(_("failed sending to `%s': status=%u\n"), - opt.keyserver_name, status ); + opt.keyserver_host, status ); } http_close( &hd ); return rc; - #endif } static int @@ -228,3 +219,373 @@ urlencode_filter( void *opaque, int control, return rc; } +static int +write_quoted(IOBUF a, const char *buf, char delim) +{ + char quoted[5]; + + sprintf(quoted,"\\x%02X",delim); + + while(*buf) + { + if(*buf==delim) + { + if(iobuf_writestr(a,quoted)) + return -1; + } + else if(*buf=='\\') + { + if(iobuf_writestr(a,"\\x5c")) + return -1; + } + else + { + if(iobuf_writebyte(a,*buf)) + return -1; + } + + buf++; + } + + return 0; +} + +/* pub 2048/<a href="/pks/lookup?op=get&search=0x3CB3B415">3CB3B415</a> 1998/04/03 David M. Shaw <<a href="/pks/lookup?op=get&search=0x3CB3B415">dshaw@jabberwocky.com</a>> */ + +/* Luckily enough, both the HKP server and NAI HKP interface to their + LDAP server are close enough in output so the same function can + parse them both. */ + +static int +parse_hkp_index(IOBUF buffer,char *line) +{ + static int open=0,revoked=0; + static char *key=NULL; +#ifdef __riscos__ + static char *uid=NULL; +#else + static unsigned char *uid=NULL; +#endif + static u32 bits,createtime; + int ret=0; + + /* printf("Open %d, LINE: %s, uid: %s\n",open,line,uid); */ + + /* Try and catch some bastardization of HKP. If we don't have + certain unchanging landmarks, we can't reliably parse the + response. */ + if(open && ascii_memcasecmp(line,"</pre>",6)!=0 && + ascii_memcasecmp(line,"pub ",5)!=0 && + ascii_memcasecmp(line," ",5)!=0) + { + m_free(key); + m_free(uid); + log_error(_("this keyserver is not fully HKP compatible\n")); + return -1; + } + + /* For multiple UIDs */ + if(open && uid!=NULL) + { + ret=0; + + if(!(revoked && !opt.keyserver_options.include_revoked)) + { + char intstr[11]; + + if(key) + write_quoted(buffer,key,':'); + iobuf_writestr(buffer,":"); + write_quoted(buffer,uid,':'); + iobuf_writestr(buffer,":"); + iobuf_writestr(buffer,revoked?"1:":":"); + sprintf(intstr,"%u",createtime); + write_quoted(buffer,intstr,':'); + iobuf_writestr(buffer,"::::"); + sprintf(intstr,"%u",bits); + write_quoted(buffer,intstr,':'); + iobuf_writestr(buffer,"\n"); + + ret=1; + } + + if(strncmp(line," ",5)!=0) + { + revoked=0; + m_free(key); + m_free(uid); + uid=NULL; + open=0; + } + } + + if(ascii_memcasecmp(line,"pub ",5)==0) + { + char *tok,*temp; + + open=1; + + line+=4; + + tok=strsep(&line,"/"); + if(tok==NULL) + return ret; + + bits=atoi(tok); + + tok=strsep(&line,">"); + if(tok==NULL) + return ret; + + tok=strsep(&line,"<"); + if(tok==NULL) + return ret; + + key=m_strdup(tok); + + tok=strsep(&line," "); + if(tok==NULL) + return ret; + + tok=strsep(&line," "); + if(tok==NULL) + return ret; + + /* The date parser wants '-' instead of '/', so... */ + temp=tok; + while(*temp!='\0') + { + if(*temp=='/') + *temp='-'; + + temp++; + } + + createtime=scan_isodatestr(tok); + } + + if(open) + { + int uidindex=0; + + if(line==NULL) + { + uid=m_strdup("Key index corrupted"); + return ret; + } + + /* All that's left is the user name. Strip off anything + <between brackets> and de-urlencode it. */ + + while(*line==' ' && *line!='\0') + line++; + + if(strncmp(line,"*** KEY REVOKED ***",19)==0) + { + revoked=1; + return ret; + } + + uid=m_alloc(strlen(line)+1); + + while(*line!='\0') + { + switch(*line) + { + case '<': + while(*line!='>' && *line!='\0') + line++; + + if(*line!='\0') + line++; + break; + + case '&': + if((*(line+1)!='\0' && tolower(*(line+1))=='l') && + (*(line+2)!='\0' && tolower(*(line+2))=='t') && + (*(line+3)!='\0' && *(line+3)==';')) + { + uid[uidindex++]='<'; + line+=4; + break; + } + else if((*(line+1)!='\0' && tolower(*(line+1))=='g') && + (*(line+2)!='\0' && tolower(*(line+2))=='t') && + (*(line+3)!='\0' && *(line+3)==';')) + { + uid[uidindex++]='>'; + line+=4; + break; + } + else if((*(line+1)!='\0' && tolower(*(line+1))=='a') && + (*(line+2)!='\0' && tolower(*(line+2))=='m') && + (*(line+3)!='\0' && tolower(*(line+3))=='p') && + (*(line+4)!='\0' && *(line+4)==';')) + { + uid[uidindex++]='&'; + line+=5; + break; + } + + default: + uid[uidindex++]=*line; + line++; + break; + } + } + + uid[uidindex]='\0'; + + /* Chop off the trailing \r, \n, or both. This is fussy as the + true HKP servers have \r\n, and the NAI HKP servers have just + \n. */ + + if(isspace(uid[uidindex-1])) + uid[uidindex-1]='\0'; + + if(isspace(uid[uidindex-2])) + uid[uidindex-2]='\0'; + } + + return ret; +} + +int hkp_search(STRLIST tokens) +{ + int rc=0,len=0,max,first=1; + unsigned int maxlen=1024,buflen=0; +#ifndef __riscos__ + unsigned char *searchstr=NULL,*searchurl; + unsigned char *request; +#else + char *searchstr=NULL,*searchurl; + char *request; +#endif + struct http_context hd; + unsigned int hflags=opt.keyserver_options.honor_http_proxy?HTTP_FLAG_TRY_PROXY:0; + byte *line=NULL; + + /* Glue the tokens together to make a search string */ + + for(;tokens;tokens=tokens->next) + { + len+=strlen(tokens->d)+1; + + searchstr=m_realloc(searchstr,len+1); + if(first) + { + searchstr[0]='\0'; + first=0; + } + + strcat(searchstr,tokens->d); + strcat(searchstr," "); + } + + if(len<=1) + { + m_free(searchstr); + return 0; + } + + searchstr[len-1]='\0'; + + log_info(_("searching for \"%s\" from HKP server %s\n"), + searchstr,opt.keyserver_host); + + /* Now make it url-ish */ + + max=0; + len=0; + searchurl=NULL; + request=searchstr; + + while(*request!='\0') + { + if(max-len<3) + { + max+=100; + searchurl=m_realloc(searchurl,max+1); /* Note +1 for \0 */ + } + + if(isalnum(*request) || *request=='-') + searchurl[len++]=*request; + else if(*request==' ') + searchurl[len++]='+'; + else + { + sprintf(&searchurl[len],"%%%02X",*request); + len+=3; + } + + request++; + } + + searchurl[len]='\0'; + + request=m_alloc(strlen(opt.keyserver_host) + 100 + strlen(searchurl)); + + if(opt.keyserver_options.broken_http_proxy) + hflags |= HTTP_FLAG_NO_SHUTDOWN; + + sprintf(request,"x-hkp://%s%s%s/pks/lookup?op=index&search=%s", + opt.keyserver_host, + atoi(opt.keyserver_port)>0?":":"", + atoi(opt.keyserver_port)>0?opt.keyserver_port:"", + searchurl); + + if(opt.keyserver_options.verbose>2) + log_info("request is \"%s\"\n",request); + + rc=http_open_document(&hd,request,hflags); + if(rc) + { + log_error(_("can't search keyserver: %s\n"), + rc==G10ERR_NETWORK?strerror(errno):g10_errstr(rc)); + } + else + { + IOBUF buffer; + int count=1; + int ret; + + buffer=iobuf_temp(); + + rc=1; + while(rc!=0) + { + /* This is a judgement call. Is it better to slurp up all + the results before prompting the user? On the one hand, + it probably makes the keyserver happier to not be blocked + on sending for a long time while the user picks a key. + On the other hand, it might be nice for the server to be + able to stop sending before a large search result page is + complete. */ + + rc=iobuf_read_line(hd.fp_read,&line,&buflen,&maxlen); + + ret=parse_hkp_index(buffer,line); + if(ret==-1) + break; + + if(rc!=0) + count+=ret; + } + + http_close(&hd); + + count--; + + if(ret>-1) + keyserver_search_prompt(buffer,count,searchstr); + + iobuf_close(buffer); + m_free(line); + } + + m_free(request); + m_free(searchurl); + m_free(searchstr); + + return rc; +} @@ -1,5 +1,5 @@ -/* hkp.h - Horrowitz Keyserver Protocol - * Copyright (C) 1999, 2000 Free Software Foundation, Inc. +/* hkp.h - Horowitz Keyserver Protocol + * Copyright (C) 1998, 1999, 2000, 2001 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -18,13 +18,12 @@ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA */ -#ifndef GPG_HKP_H -#define GPG_HKP_H 1 +#ifndef G10_HKP_H +#define G10_HKP_H 1 - -int hkp_ask_import( u32 *keyid ); +int hkp_ask_import( KEYDB_SEARCH_DESC *desc, void *stats_handle); int hkp_import( STRLIST users ); int hkp_export( STRLIST users ); +int hkp_search(STRLIST tokens); - -#endif /*GPG_HKP_H*/ +#endif /*G10_HKP_H*/ diff --git a/g10/import.c b/g10/import.c index 7e2e5bc18..ccc665145 100644 --- a/g10/import.c +++ b/g10/import.c @@ -1,5 +1,5 @@ /* import.c - * Copyright (C) 1998, 1999, 2000 Free Software Foundation, Inc. + * Copyright (C) 1998, 1999, 2000, 2001, 2002 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -29,15 +29,15 @@ #include "packet.h" #include "errors.h" #include "keydb.h" -#include <gcrypt.h> +#include "memory.h" #include "util.h" #include "trustdb.h" #include "main.h" #include "i18n.h" #include "status.h" +#include "keyserver-internal.h" - -static struct { +struct stats_s { ulong count; ulong no_user_id; ulong imported; @@ -51,18 +51,22 @@ static struct { ulong secret_imported; ulong secret_dups; ulong skipped_new_keys; -} stats; +}; -static int import( IOBUF inp, int fast, const char* fname ); -static void print_stats(void); +static int import( IOBUF inp, int fast, const char* fname, + struct stats_s *stats ); static int read_block( IOBUF a, PACKET **pending_pkt, KBNODE *ret_root ); -static int import_one( const char *fname, KBNODE keyblock, int fast ); -static int import_secret_one( const char *fname, KBNODE keyblock ); -static int import_revoke_cert( const char *fname, KBNODE node ); +static void revocation_present(KBNODE keyblock); +static void remove_bad_stuff (KBNODE keyblock); +static int import_one( const char *fname, KBNODE keyblock, int fast, + struct stats_s *stats); +static int import_secret_one( const char *fname, KBNODE keyblock, + struct stats_s *stats ); +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 ); -static void mark_non_selfsigned_uids_valid( KBNODE keyblock, u32 *kid ); static int delete_inv_parts( const char *fname, KBNODE keyblock, u32 *keyid ); static int merge_blocks( const char *fname, KBNODE keyblock_orig, KBNODE keyblock, u32 *keyid, @@ -77,6 +81,18 @@ static int merge_keysigs( KBNODE dst, KBNODE src, int *n_sigs, const char *fname, u32 *keyid ); +void * +import_new_stats_handle (void) +{ + return m_alloc_clear ( sizeof (struct stats_s) ); +} + +void +import_release_stats_handle (void *p) +{ + m_free (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 @@ -109,12 +125,13 @@ static int merge_keysigs( KBNODE dst, KBNODE src, int *n_sigs, * */ void -import_keys( char **fnames, int nnames, int fast ) +import_keys( char **fnames, int nnames, int fast, void *stats_handle ) { int i; + struct stats_s *stats = stats_handle; - /* fixme: don't use static variables */ - memset( &stats, 0, sizeof( stats ) ); + if (!stats) + stats = import_new_stats_handle (); if( !fnames && !nnames ) nnames = 1; /* Ohh what a ugly hack to jump into the loop */ @@ -127,36 +144,42 @@ import_keys( char **fnames, int nnames, int fast ) if( !inp ) log_error(_("can't open `%s': %s\n"), fname, strerror(errno) ); else { - int rc = import( inp, fast, fname ); + int rc = import( inp, fast, fname, stats ); iobuf_close(inp); if( rc ) log_error("import from `%s' failed: %s\n", fname, - gpg_errstr(rc) ); + g10_errstr(rc) ); } if( !fname ) break; } - print_stats(); - if( !fast ) - sync_trustdb(); + if (!stats_handle) { + import_print_stats (stats); + import_release_stats_handle (stats); + } + } int -import_keys_stream( IOBUF inp, int fast ) +import_keys_stream( IOBUF inp, int fast, void *stats_handle ) { int rc = 0; + struct stats_s *stats = stats_handle; + + if (!stats) + stats = import_new_stats_handle (); + + rc = import( inp, fast, "[stream]", stats); + if (!stats_handle) { + import_print_stats (stats); + import_release_stats_handle (stats); + } - /* fixme: don't use static variables */ - memset( &stats, 0, sizeof( stats ) ); - rc = import( inp, fast, "[stream]" ); - print_stats(); - if( !fast ) - sync_trustdb(); return rc; } static int -import( IOBUF inp, int fast, const char* fname ) +import( IOBUF inp, int fast, const char* fname, struct stats_s *stats ) { PACKET *pending_pkt = NULL; KBNODE keyblock; @@ -165,19 +188,20 @@ import( IOBUF inp, int fast, const char* fname ) getkey_disable_caches(); if( !opt.no_armor ) { /* armored reading is not disabled */ - armor_filter_context_t *afx = gcry_xcalloc( 1, sizeof *afx ); + armor_filter_context_t *afx = m_alloc_clear( sizeof *afx ); afx->only_keyblocks = 1; iobuf_push_filter2( inp, armor_filter, afx, 1 ); } while( !(rc = read_block( inp, &pending_pkt, &keyblock) )) { + remove_bad_stuff (keyblock); if( keyblock->pkt->pkttype == PKT_PUBLIC_KEY ) - rc = import_one( fname, keyblock, fast ); - else if( keyblock->pkt->pkttype == PKT_SECRET_KEY ) - rc = import_secret_one( fname, keyblock ); + rc = import_one( fname, keyblock, fast, stats ); + else if( keyblock->pkt->pkttype == PKT_SECRET_KEY ) + rc = import_secret_one( fname, keyblock, stats ); else if( keyblock->pkt->pkttype == PKT_SIGNATURE && keyblock->pkt->pkt.signature->sig_class == 0x20 ) - rc = import_revoke_cert( fname, keyblock ); + rc = import_revoke_cert( fname, keyblock, stats ); else { log_info( _("skipping block of type %d\n"), keyblock->pkt->pkttype ); @@ -185,67 +209,70 @@ import( IOBUF inp, int fast, const char* fname ) release_kbnode(keyblock); if( rc ) break; - if( !(++stats.count % 100) && !opt.quiet ) - log_info(_("%lu keys so far processed\n"), stats.count ); + if( !(++stats->count % 100) && !opt.quiet ) + log_info(_("%lu keys so far processed\n"), stats->count ); } if( rc == -1 ) rc = 0; - else if( rc && rc != GPGERR_INV_KEYRING ) - log_error( _("error reading `%s': %s\n"), fname, gpg_errstr(rc)); + else if( rc && rc != G10ERR_INV_KEYRING ) + log_error( _("error reading `%s': %s\n"), fname, g10_errstr(rc)); return rc; } -static void -print_stats() +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(_("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 ); + 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->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( is_status_enabled() ) { - char buf[12*20]; - sprintf(buf, "%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); + char buf[13*20]; + sprintf(buf, "%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 ); write_status_text( STATUS_IMPORT_RES, buf ); } } @@ -272,13 +299,13 @@ read_block( IOBUF a, PACKET **pending_pkt, KBNODE *ret_root ) } else in_cert = 0; - pkt = gcry_xmalloc( sizeof *pkt ); + pkt = m_alloc( sizeof *pkt ); init_packet(pkt); - while( (rc=parse_packet(a, pkt, NULL)) != -1 ) { + while( (rc=parse_packet(a, pkt)) != -1 ) { if( rc ) { /* ignore errors */ - if( rc != GPGERR_UNKNOWN_PACKET ) { - log_error("read_block: read error: %s\n", gpg_errstr(rc) ); - rc = GPGERR_INV_KEYRING; + 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 ); @@ -300,11 +327,11 @@ read_block( IOBUF a, PACKET **pending_pkt, KBNODE *ret_root ) case PKT_COMPRESSED: if( pkt->pkt.compressed->algorithm < 1 || pkt->pkt.compressed->algorithm > 2 ) { - rc = GPGERR_COMPR_ALGO; + rc = G10ERR_COMPR_ALGO; goto ready; } { - compress_filter_context_t *cfx = gcry_xcalloc( 1, sizeof *cfx ); + compress_filter_context_t *cfx = m_alloc_clear( sizeof *cfx ); cfx->algo = pkt->pkt.compressed->algorithm; pkt->pkt.compressed->buf = NULL; iobuf_push_filter2( a, compress_filter, cfx, 1 ); @@ -313,6 +340,11 @@ read_block( IOBUF a, PACKET **pending_pkt, KBNODE *ret_root ) 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: @@ -328,7 +360,7 @@ read_block( IOBUF a, PACKET **pending_pkt, KBNODE *ret_root ) root = new_kbnode( pkt ); else add_kbnode( root, new_kbnode( pkt ) ); - pkt = gcry_xmalloc( sizeof *pkt ); + pkt = m_alloc( sizeof *pkt ); } init_packet(pkt); break; @@ -343,19 +375,125 @@ read_block( IOBUF a, PACKET **pending_pkt, KBNODE *ret_root ) else *ret_root = root; free_packet( pkt ); - gcry_free( pkt ); + m_free( pkt ); return rc; } +static void +remove_bad_stuff (KBNODE keyblock) +{ + KBNODE node; + + for (node=keyblock; node; node = node->next ) { + if( node->pkt->pkttype == PKT_SIGNATURE ) { + /* delete the subpackets we used to use for the + verification cache */ + delete_sig_subpkt (node->pkt->pkt.signature->unhashed, + SIGSUBPKT_PRIV_VERIFY_CACHE); + } + } +} + +/* Clean the subkeys on a pk so that they each have at most 1 binding + sig and at most 1 revocation sig. This works based solely on the + timestamps like the rest of gpg. If the standard does get + revocation targets, this may need to be revised. */ + +static int +clean_subkeys(KBNODE keyblock,u32 *keyid) +{ + int removed=0; + KBNODE node,sknode=keyblock; + + while((sknode=find_kbnode(sknode,PKT_PUBLIC_SUBKEY))) + { + KBNODE bsnode=NULL,rsnode=NULL; + u32 bsdate=0,rsdate=0; + + sknode=sknode->next; + + for(node=sknode;node;node=node->next) + { + if(node->pkt->pkttype==PKT_SIGNATURE) + { + PKT_signature *sig=node->pkt->pkt.signature; + + /* We're only interested in valid sigs */ + if(check_key_signature(keyblock,node,NULL)) + continue; + + if(IS_SUBKEY_SIG(sig) && bsdate<=sig->timestamp) + { + bsnode=node; + bsdate=sig->timestamp; + } + else if(IS_SUBKEY_REV(sig) && rsdate<=sig->timestamp) + { + rsnode=node; + rsdate=sig->timestamp; + } + /* If it's not a subkey sig or rev, then it shouldn't be + here so ignore it. */ + } + else + break; + } + + /* We now know the most recent binding sig and revocation sig + (if any). If the binding sig is more recent than the + revocation sig, strip everything but the binding sig. If the + revocation sig is more recent than the binding sig, strip + everything but the binding sig and the revocation sig. */ + + if(bsdate>=rsdate) + { + rsnode=NULL; + rsdate=0; + } + + for(node=sknode;node;node=node->next) + { + if(node->pkt->pkttype==PKT_SIGNATURE) + { + PKT_signature *sig=node->pkt->pkt.signature; + + if(IS_SUBKEY_SIG(sig) && node!=bsnode) + { + delete_kbnode(node); + removed++; + } + else if(IS_SUBKEY_REV(sig) && node!=rsnode) + { + delete_kbnode(node); + removed++; + } + } + else + break; + } + } + + if(removed) + { + log_info(_("key %08lX: removed multiple subkey binding\n"), + (ulong)keyid[1]); + commit_kbnode(&keyblock); + } + + return removed; +} + + /**************** * 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 gpg. + * which called g10. */ static int -import_one( const char *fname, KBNODE keyblock, int fast ) +import_one( const char *fname, KBNODE keyblock, int fast, + struct stats_s *stats ) { PKT_public_key *pk; PKT_public_key *pk_orig; @@ -395,8 +533,18 @@ import_one( const char *fname, KBNODE keyblock, int fast ) if( rc ) return rc== -1? 0:rc; + /* If we allow such a thing, mark unsigned uids as valid */ if( opt.allow_non_selfsigned_uid ) - mark_non_selfsigned_uids_valid( keyblock, keyid ); + 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 %08lX: accepted non self-signed user ID '%s'\n"), + (ulong)keyid[1],user); + m_free(user); + } if( !delete_inv_parts( fname, keyblock, keyid ) ) { if( !opt.quiet ) { @@ -404,47 +552,63 @@ import_one( const char *fname, KBNODE keyblock, int fast ) (ulong)keyid[1]); log_info(_("this may be caused by a missing self-signature\n")); } - stats.no_user_id++; + stats->no_user_id++; return 0; } - /* do we have this key already in one of our pubrings ? */ - pk_orig = gcry_xcalloc( 1, sizeof *pk_orig ); + pk_orig = m_alloc_clear( sizeof *pk_orig ); rc = get_pubkey( pk_orig, keyid ); - if( rc && rc != GPGERR_NO_PUBKEY ) { + if( rc && rc != G10ERR_NO_PUBKEY && rc != G10ERR_UNU_PUBKEY ) { log_error( _("key %08lX: public key not found: %s\n"), - (ulong)keyid[1], gpg_errstr(rc)); + (ulong)keyid[1], g10_errstr(rc)); } else if ( rc && opt.merge_only ) { if( opt.verbose ) log_info( _("key %08lX: new key - skipped\n"), (ulong)keyid[1] ); rc = 0; fast = 1; /* so that we don't get into the trustdb update */ - stats.skipped_new_keys++; + stats->skipped_new_keys++; } else if( rc ) { /* insert this key */ -#if 0 /* we don't know wehre we are going to write */ + 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"), - keyblock_resource_name(kbpos) ); -#endif - if( (rc=insert_keyblock( keyblock )) ) - log_error( _("error writing key: %s\n"), gpg_errstr(rc) ); - /* we are ready */ - if( !opt.quiet ) - log_info( _("key %08lX: public key imported\n"), (ulong)keyid[1]); + log_info (_("writing to `%s'\n"), keydb_get_resource_name (hd) ); + clean_subkeys(keyblock,keyid); + 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 + revalidation_mark (); + keydb_release (hd); + + /* we are ready */ + if( !opt.quiet ) { + char *p=get_user_id_native(keyid); + log_info( _("key %08lX: public key \"%s\" imported\n"), + (ulong)keyid[1],p); + m_free(p); + } if( is_status_enabled() ) { char *us = get_long_user_id_string( keyid ); write_status_text( STATUS_IMPORTED, us ); - gcry_free(us); + m_free(us); } - stats.imported++; + stats->imported++; if( is_RSA( pk->pubkey_algo ) ) - stats.imported_rsa++; + stats->imported_rsa++; new_key = 1; } else { /* merge */ + KEYDB_HANDLE hd; int n_uids, n_sigs, n_subk; /* Compare the original against the new key; just to be sure nothing @@ -452,15 +616,31 @@ import_one( const char *fname, KBNODE keyblock, int fast ) if( cmp_public_keys( pk_orig, pk ) ) { log_error( _("key %08lX: doesn't match our copy\n"), (ulong)keyid[1]); - rc = GPGERR_GENERAL; goto leave; } /* now read the original keyblock */ - rc = find_keyblock_bypk( &keyblock_orig, pk_orig ); + 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 %08lX: can't locate original keyblock: %s\n"), - (ulong)keyid[1], gpg_errstr(rc)); + log_error (_("key %08lX: can't locate original keyblock: %s\n"), + (ulong)keyid[1], g10_errstr(rc)); + keydb_release (hd); + goto leave; + } + rc = keydb_get_keyblock (hd, &keyblock_orig ); + if (rc) { + log_error (_("key %08lX: can't read original keyblock: %s\n"), + (ulong)keyid[1], g10_errstr(rc)); + keydb_release (hd); goto leave; } @@ -471,73 +651,80 @@ import_one( const char *fname, KBNODE keyblock, int fast ) n_uids = n_sigs = n_subk = 0; rc = merge_blocks( fname, keyblock_orig, keyblock, keyid, &n_uids, &n_sigs, &n_subk ); - if( rc ) + if( rc ) { + keydb_release (hd); goto leave; + } if( n_uids || n_sigs || n_subk ) { mod_key = 1; /* keyblock_orig has been updated; write */ - if( (rc=update_keyblock( keyblock_orig )) ) - log_error( _("error writing key: %s\n"), gpg_errstr(rc) ); + n_sigs-=clean_subkeys(keyblock_orig,keyid); + 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 + revalidation_mark (); + /* we are ready */ if( !opt.quiet ) { + char *p=get_user_id_native(keyid); if( n_uids == 1 ) - log_info( _("key %08lX: 1 new user ID\n"), - (ulong)keyid[1]); + log_info( _("key %08lX: \"%s\" 1 new user ID\n"), + (ulong)keyid[1], p); else if( n_uids ) - log_info( _("key %08lX: %d new user IDs\n"), - (ulong)keyid[1], n_uids ); + log_info( _("key %08lX: \"%s\" %d new user IDs\n"), + (ulong)keyid[1], p, n_uids ); if( n_sigs == 1 ) - log_info( _("key %08lX: 1 new signature\n"), - (ulong)keyid[1]); + log_info( _("key %08lX: \"%s\" 1 new signature\n"), + (ulong)keyid[1], p); else if( n_sigs ) - log_info( _("key %08lX: %d new signatures\n"), - (ulong)keyid[1], n_sigs ); + log_info( _("key %08lX: \"%s\" %d new signatures\n"), + (ulong)keyid[1], p, n_sigs ); if( n_subk == 1 ) - log_info( _("key %08lX: 1 new subkey\n"), - (ulong)keyid[1]); + log_info( _("key %08lX: \"%s\" 1 new subkey\n"), + (ulong)keyid[1], p); else if( n_subk ) - log_info( _("key %08lX: %d new subkeys\n"), - (ulong)keyid[1], n_subk ); + log_info( _("key %08lX: \"%s\" %d new subkeys\n"), + (ulong)keyid[1], p, n_subk ); + m_free(p); } - stats.n_uids +=n_uids; - stats.n_sigs +=n_sigs; - stats.n_subk +=n_subk; + stats->n_uids +=n_uids; + stats->n_sigs +=n_sigs; + stats->n_subk +=n_subk; } else { - if( !opt.quiet ) - log_info( _("key %08lX: not changed\n"), (ulong)keyid[1] ); - stats.unchanged++; - } - } - if( !rc && !fast ) { - rc = query_trust_record( new_key? pk : pk_orig ); - if( rc && rc != -1 ) - log_error("trustdb error: %s\n", gpg_errstr(rc) ); - else if( rc == -1 ) { /* not found trustdb */ - rc = insert_trust_record( new_key? keyblock : keyblock_orig ); - if( rc ) - log_error("key %08lX: trustdb insert failed: %s\n", - (ulong)keyid[1], gpg_errstr(rc) ); + if( !opt.quiet ) { + char *p=get_user_id_native(keyid); + log_info( _("key %08lX: \"%s\" not changed\n"), + (ulong)keyid[1],p); + m_free(p); + } + stats->unchanged++; } - else if( mod_key ) - rc = update_trust_record( keyblock_orig, 1, NULL ); - else - rc = clear_trust_checked_flag( new_key? pk : pk_orig ); + keydb_release (hd); hd = NULL; } leave: release_kbnode( keyblock_orig ); free_public_key( pk_orig ); + + revocation_present(keyblock); + return rc; } /**************** * 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 ) +import_secret_one( const char *fname, KBNODE keyblock, + struct stats_s *stats) { PKT_secret_key *sk; KBNODE node, uidnode; @@ -563,7 +750,8 @@ import_secret_one( const char *fname, KBNODE keyblock ) uidnode->pkt->pkt.user_id->len ); putc('\n', stderr); } - stats.secret_read++; + stats->secret_read++; + if( !uidnode ) { log_error( _("key %08lX: no user ID\n"), (ulong)keyid[1]); return 0; @@ -573,22 +761,34 @@ import_secret_one( const char *fname, KBNODE keyblock ) /* do we have this key already in one of our secrings ? */ rc = seckey_available( keyid ); - if( rc == GPGERR_NO_SECKEY && !opt.merge_only ) { /*just insert this key*/ - if( (rc=insert_keyblock( keyblock )) ) - log_error( _("error writing key: %s\n"), gpg_errstr(rc) ); + if( rc == G10ERR_NO_SECKEY && !opt.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 %08lX: secret key imported\n"), (ulong)keyid[1]); - stats.secret_imported++; + stats->secret_imported++; } else if( !rc ) { /* we can't merge secret keys */ log_error( _("key %08lX: already in secret keyring\n"), (ulong)keyid[1]); - stats.secret_dups++; + stats->secret_dups++; } else log_error( _("key %08lX: secret key not found: %s\n"), - (ulong)keyid[1], gpg_errstr(rc)); + (ulong)keyid[1], g10_errstr(rc)); return rc; } @@ -598,10 +798,11 @@ import_secret_one( const char *fname, KBNODE keyblock ) * Import a revocation certificate; this is a single signature packet. */ static int -import_revoke_cert( const char *fname, KBNODE node ) +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; @@ -612,9 +813,9 @@ import_revoke_cert( const char *fname, KBNODE node ) keyid[0] = node->pkt->pkt.signature->keyid[0]; keyid[1] = node->pkt->pkt.signature->keyid[1]; - pk = gcry_xcalloc( 1, sizeof *pk ); + pk = m_alloc_clear( sizeof *pk ); rc = get_pubkey( pk, keyid ); - if( rc == GPGERR_NO_PUBKEY ) { + if( rc == G10ERR_NO_PUBKEY ) { log_info( _("key %08lX: no public key - " "can't apply revocation certificate\n"), (ulong)keyid[1]); rc = 0; @@ -622,15 +823,30 @@ import_revoke_cert( const char *fname, KBNODE node ) } else if( rc ) { log_error( _("key %08lX: public key not found: %s\n"), - (ulong)keyid[1], gpg_errstr(rc)); + (ulong)keyid[1], g10_errstr(rc)); goto leave; } /* read the original keyblock */ - rc = find_keyblock_bypk( &keyblock, pk ); - if( rc ) { - log_error( _("key %08lX: can't locate original keyblock: %s\n"), - (ulong)keyid[1], gpg_errstr(rc)); + 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 %08lX: can't locate original keyblock: %s\n"), + (ulong)keyid[1], g10_errstr(rc)); + goto leave; + } + rc = keydb_get_keyblock (hd, &keyblock ); + if (rc) { + log_error (_("key %08lX: can't read original keyblock: %s\n"), + (ulong)keyid[1], g10_errstr(rc)); goto leave; } @@ -641,7 +857,8 @@ import_revoke_cert( const char *fname, KBNODE node ) rc = check_key_signature( keyblock, node, NULL); if( rc ) { log_error( _("key %08lX: invalid revocation certificate" - ": %s - rejected\n"), (ulong)keyid[1], gpg_errstr(rc)); + ": %s - rejected\n"), (ulong)keyid[1], g10_errstr(rc)); + goto leave; } @@ -663,24 +880,23 @@ import_revoke_cert( const char *fname, KBNODE node ) insert_kbnode( keyblock, clone_kbnode(node), 0 ); /* and write the keyblock back */ - if( (rc=update_keyblock( keyblock )) ) - log_error( _("error writing key: %s\n"), gpg_errstr(rc) ); + 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 ) - log_info( _("key %08lX: revocation certificate imported\n"), - (ulong)keyid[1]); - stats.n_revoc++; - if( clear_trust_checked_flag( pk ) ) { - /* seems that we have to insert the record first */ - rc = insert_trust_record( keyblock ); - if( rc ) - log_error("key %08lX: trustdb insert failed: %s\n", - (ulong)keyid[1], gpg_errstr(rc) ); - else - rc = clear_trust_checked_flag( pk ); + if( !opt.quiet ) { + char *p=get_user_id_native(keyid); + log_info( _("key %08lX: \"%s\" revocation certificate imported\n"), + (ulong)keyid[1],p); + m_free(p); } + stats->n_revoc++; + revalidation_mark (); leave: + keydb_release (hd); release_kbnode( keyblock ); free_public_key( pk ); return rc; @@ -713,16 +929,25 @@ chk_self_sigs( const char *fname, KBNODE keyblock, (ulong)keyid[1]); return -1; /* the complete keyblock is invalid */ } - rc = check_key_signature( keyblock, n, NULL); - if( rc ) { - log_info( rc == GPGERR_PUBKEY_ALGO ? - _("key %08lX: unsupported public key algorithm\n"): - _("key %08lX: invalid self-signature\n"), - (ulong)keyid[1]); - unode->flag |= 2; /* mark as invalid */ + /* If it hasn't been marked valid yet, keep trying */ + if(!(unode->flag&1)) { + rc = check_key_signature( keyblock, n, NULL); + if( rc ) + { + 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 %08lX: unsupported public key " + "algorithm on user id \"%s\"\n"): + _("key %08lX: invalid self-signature " + "on user id \"%s\"\n"), + (ulong)keyid[1],p); + m_free(p); + } + else + unode->flag |= 1; /* mark that signature checked */ } - unode->flag |= 1; /* mark that signature checked */ } else if( sig->sig_class == 0x18 ) { KBNODE knode = find_prev_kbnode( keyblock, @@ -737,16 +962,17 @@ chk_self_sigs( const char *fname, KBNODE keyblock, n->flag |= 4; /* delete this */ } else { + /* If it hasn't been marked valid yet, keep trying */ + if(!(knode->flag&1)) { rc = check_key_signature( keyblock, n, NULL); - if( rc ) { - log_info( rc == GPGERR_PUBKEY_ALGO ? + if( rc ) + log_info( rc == G10ERR_PUBKEY_ALGO ? _("key %08lX: unsupported public key algorithm\n"): _("key %08lX: invalid subkey binding\n"), (ulong)keyid[1]); - - knode->flag |= 2; /* mark as invalid */ - } - knode->flag |= 1; /* mark that signature checked */ + else + knode->flag |= 1; /* mark that signature checked */ + } } } } @@ -754,30 +980,6 @@ chk_self_sigs( const char *fname, KBNODE keyblock, return 0; } - - -/**************** - * If a user ID has at least one signature, mark it as valid - */ -static void -mark_non_selfsigned_uids_valid( KBNODE keyblock, u32 *kid ) -{ - KBNODE node; - for(node=keyblock->next; node; node = node->next ) { - if( node->pkt->pkttype == PKT_USER_ID && !(node->flag & 1) ) { - if( (node->next && node->next->pkt->pkttype == PKT_SIGNATURE) - || !node->next ) { - node->flag |= 1; - log_info( _("key %08lX: accepted non self-signed user ID '"), - (ulong)kid[1]); - print_string( log_stream(), node->pkt->pkt.user_id->name, - node->pkt->pkt.user_id->len, 0 ); - fputs("'\n", log_stream() ); - } - } - } -} - /**************** * delete all parts which are invalid and those signatures whose * public key algorithm is not available in this implemenation; @@ -790,7 +992,6 @@ delete_inv_parts( const char *fname, KBNODE keyblock, u32 *keyid ) { KBNODE node; int nvalid=0, uid_seen=0; - const char *p; for(node=keyblock->next; node; node = node->next ) { if( node->pkt->pkttype == PKT_USER_ID ) { @@ -833,14 +1034,12 @@ delete_inv_parts( const char *fname, KBNODE keyblock, u32 *keyid ) } } else if( node->pkt->pkttype == PKT_SIGNATURE - && openpgp_pk_test_algo( node->pkt->pkt.signature->pubkey_algo, 0 ) - && node->pkt->pkt.signature->pubkey_algo != GCRY_PK_RSA ) + && 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 - && (p = parse_sig_subpkt2( node->pkt->pkt.signature, - SIGSUBPKT_EXPORTABLE, NULL )) - && !*p - && seckey_available( node->pkt->pkt.signature->keyid ) ) { + else if( node->pkt->pkttype == PKT_SIGNATURE && + !node->pkt->pkt.signature->flags.exportable && + 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 @@ -860,12 +1059,22 @@ delete_inv_parts( const char *fname, KBNODE keyblock, u32 *keyid ) delete_kbnode( node ); } else { - int rc = check_key_signature( keyblock, node, NULL); - if( rc ) { - log_error( _("key %08lX: invalid revocation " - "certificate: %s - skipped\n"), - (ulong)keyid[1], gpg_errstr(rc)); - delete_kbnode( node ); + /* 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 ) + { + log_error( _("key %08lX: invalid revocation " + "certificate: %s - skipped\n"), + (ulong)keyid[1], g10_errstr(rc)); + delete_kbnode( node ); + } } } } @@ -968,7 +1177,84 @@ collapse_uids( KBNODE *keyblock ) return 1; } +/* Check for a 0x20 revocation from a revocation key that is not + present. This gets 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(NULL,sig->revkey[idx]->fpr, + MAX_FINGERPRINT_LEN); + if(rc==G10ERR_NO_PUBKEY || rc==G10ERR_UNU_PUBKEY) + { + /* No, so try and get it */ + if(opt.keyserver_scheme && + opt.keyserver_options.auto_key_retrieve) + { + log_info(_("Warning: key %08lX may be revoked: " + "fetching revocation key %08lX\n"), + (ulong)keyid_from_pk(pk,NULL), + (ulong)keyid[1]); + keyserver_import_fprint(sig->revkey[idx]->fpr, + MAX_FINGERPRINT_LEN); + + /* Do we have it now? */ + rc=get_pubkey_byfprint(NULL, + sig->revkey[idx]->fpr, + MAX_FINGERPRINT_LEN); + } + + if(rc==G10ERR_NO_PUBKEY || rc==G10ERR_UNU_PUBKEY) + log_info(_("Warning: key %08lX may be revoked: " + "revocation key %08lX not present.\n"), + (ulong)keyid_from_pk(pk,NULL), + (ulong)keyid[1]); + } + } + } + } + } + } +} /**************** * compare and merge the blocks @@ -1010,16 +1296,49 @@ merge_blocks( const char *fname, KBNODE keyblock_orig, KBNODE keyblock, } } if( !found ) { + char *p=get_user_id_native(keyid); KBNODE n2 = clone_kbnode(node); insert_kbnode( keyblock_orig, n2, 0 ); n2->flag |= 1; - log_info( _("key %08lX: revocation certificate added\n"), + ++*n_sigs; + log_info(_("key %08lX: \"%s\" revocation certificate added\n"), + (ulong)keyid[1],p); + m_free(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; + log_info( _("key %08lX: direct key signature added\n"), (ulong)keyid[1]); } } } - /* 2nd: try to merge new certificates in */ + /* 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 */ @@ -1036,7 +1355,7 @@ merge_blocks( const char *fname, KBNODE keyblock_orig, KBNODE keyblock, } } - /* 3rd: add new user-ids */ + /* 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 */ @@ -1054,7 +1373,7 @@ merge_blocks( const char *fname, KBNODE keyblock_orig, KBNODE keyblock, } } - /* add new subkeys */ + /* 5th: add new subkeys */ for(node=keyblock->next; node; node=node->next ) { onode = NULL; if( node->pkt->pkttype == PKT_PUBLIC_SUBKEY ) { @@ -1087,7 +1406,7 @@ merge_blocks( const char *fname, KBNODE keyblock_orig, KBNODE keyblock, } } - /* merge subkey certificates */ + /* 6th: merge subkey certificates */ for(onode=keyblock_orig->next; onode; onode=onode->next ) { if( !(onode->flag & 1) && ( onode->pkt->pkttype == PKT_PUBLIC_SUBKEY @@ -1126,11 +1445,6 @@ append_uid( KBNODE keyblock, KBNODE node, int *n_sigs, KBNODE n, n_where=NULL; assert(node->pkt->pkttype == PKT_USER_ID ); - if( !node->next || node->next->pkt->pkttype == PKT_USER_ID ) { - log_error( _("key %08lX: our copy has no self-signature\n"), - (ulong)keyid[1]); - return GPGERR_GENERAL; - } /* find the position */ for( n = keyblock; n; n_where = n, n = n->next ) { @@ -1179,12 +1493,6 @@ merge_sigs( KBNODE dst, KBNODE src, int *n_sigs, assert(dst->pkt->pkttype == PKT_USER_ID ); assert(src->pkt->pkttype == PKT_USER_ID ); - if( !dst->next || dst->next->pkt->pkttype == PKT_USER_ID ) { - log_error( _("key %08lX: our copy has no self-signature\n"), - (ulong)keyid[1]); - return 0; - } - for(n=src->next; n && n->pkt->pkttype != PKT_USER_ID; n = n->next ) { if( n->pkt->pkttype != PKT_SIGNATURE ) @@ -1304,4 +1612,3 @@ append_key( KBNODE keyblock, KBNODE node, int *n_sigs, return 0; } - diff --git a/g10/kbnode.c b/g10/kbnode.c index 2c1e2ad3c..06d28f844 100644 --- a/g10/kbnode.c +++ b/g10/kbnode.c @@ -1,5 +1,5 @@ /* kbnode.c - keyblock node utility functions - * Copyright (C) 1998, 1999, 2000 Free Software Foundation, Inc. + * Copyright (C) 1998, 1999, 2000, 2001 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -23,9 +23,8 @@ #include <stdlib.h> #include <string.h> #include <assert.h> - -#include <gcrypt.h> #include "util.h" +#include "memory.h" #include "packet.h" #include "keydb.h" @@ -42,7 +41,7 @@ alloc_node(void) if( n ) unused_nodes = n->next; else - n = gcry_xmalloc( sizeof *n ); + n = m_alloc( sizeof *n ); n->next = NULL; n->pkt = NULL; n->flag = 0; @@ -59,7 +58,7 @@ free_node( KBNODE n ) n->next = unused_nodes; unused_nodes = n; #else - gcry_free( n ); + m_free( n ); #endif } } @@ -95,7 +94,7 @@ release_kbnode( KBNODE n ) n2 = n->next; if( !is_cloned_kbnode(n) ) { free_packet( n->pkt ); - gcry_free( n->pkt ); + m_free( n->pkt ); } free_node( n ); n = n2; @@ -165,9 +164,10 @@ 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; + for (n1=NULL; root && root != node; root = root->next ) { + if (!pkttype ||root->pkt->pkttype == pkttype) + n1 = root; + } return n1; } @@ -185,7 +185,7 @@ 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 + else if( pkttype == PKT_USER_ID && ( node->pkt->pkttype == PKT_PUBLIC_KEY || node->pkt->pkttype == PKT_SECRET_KEY ) ) return NULL; @@ -267,7 +267,7 @@ commit_kbnode( KBNODE *root ) nl->next = n->next; if( !is_cloned_kbnode(n) ) { free_packet( n->pkt ); - gcry_free( n->pkt ); + m_free( n->pkt ); } free_node( n ); changed = 1; @@ -291,7 +291,7 @@ remove_kbnode( KBNODE *root, KBNODE node ) nl->next = n->next; if( !is_cloned_kbnode(n) ) { free_packet( n->pkt ); - gcry_free( n->pkt ); + m_free( n->pkt ); } free_node( n ); } @@ -356,28 +356,44 @@ dump_kbnode( KBNODE node ) 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, node->pkt->pkt.user_id->name, - node->pkt->pkt.user_id->len, 0 ); - fputs("\"\n", 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\n", + 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->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 ) { - fprintf(stderr, " keyid=%08lX\n", (ulong) - keyid_from_pk( node->pkt->pkt.public_key, NULL )); + 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/kbx.h b/g10/kbx.h deleted file mode 100644 index 25825a4ec..000000000 --- a/g10/kbx.h +++ /dev/null @@ -1,51 +0,0 @@ -/* kbx.h - The GnuPG Keybox - * Copyright (C) 2000 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA - */ - -#ifndef GPG_KBX_H -#define GPG_KBX_H 1 - -#include "keydb.h" - -/*-- kbxblob.c */ -struct kbxblob; -typedef struct kbxblob *KBXBLOB; - -int kbx_new_blob ( KBXBLOB *r_blob, char *image, size_t imagelen ); -int kbx_create_blob ( KBXBLOB *r_blob, KBNODE keyblock ); -void kbx_release_blob ( KBXBLOB blob ); -const char *kbx_get_blob_image ( KBXBLOB blob, size_t *n ); - -int kbx_dump_blob ( FILE *fp, KBXBLOB blob ); -int kbx_blob_has_fpr ( KBXBLOB blob, const byte *fpr ); -int kbx_blob_has_kid ( KBXBLOB blob, const byte *keyidbuf, size_t keyidlen ); -int kbx_blob_has_uid ( KBXBLOB blob, - int (*cmp)(const byte *, size_t, void *), void *opaque ); - -/*-- kbxio.c --*/ -int kbx_read_blob ( KBXBLOB *r_blob, FILE *a ); - -/*-- kbxfile.c --*/ -int kbxfile_search_by_fpr( const char *filename, const byte *fpr ); -int kbxfile_search_by_kid ( const char *filename, u32 *kid, int mode ); -int kbxfile_search_by_uid ( const char *filename, const char *name ); -void print_kbxfile( const char *filename ); - - -#endif /*GPG_KBX_H*/ diff --git a/g10/kbxblob.c b/g10/kbxblob.c deleted file mode 100644 index 01d0dfe10..000000000 --- a/g10/kbxblob.c +++ /dev/null @@ -1,895 +0,0 @@ -/* kbxblob.c - KBX Blob handling - * Copyright (C) 2000 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA - */ - - -/* The keybox data formats - -The KeyBox uses an augmented OpenPGP key format. This makes random -access to a keyblock easier and also gives the opportunity to store -additional information (e.g. the fingerprint) along with the key. -All integers are stored in network byte order, offsets are counted from -the beginning of the Blob. - -The first record of a plain KBX file has a special format: - - u32 length of the first record - byte Blob type (1) - byte version number (1) - byte reserved - byte reserved - u32 magic 'KBXf' - byte marginals used for validity calculation of this file - byte completes ditto. - byte cert_depth ditto. - -The standard KBX Blob looks like this: - - u32 length of this blob (including these 4 bytes) - byte Blob type (2) - byte version number of this blob type (1) - u16 Blob flags - bit 0 = contains secret key material - - u32 offset to the OpenPGP keyblock - u32 length of the keyblock - u16 number of keys (at least 1!) - u16 size of additional key information - n times: - b20 The keys fingerprint - (fingerprints are always 20 bytes, MD5 left padded with zeroes) - u32 offset to the n-th key's keyID (a keyID is always 8 byte) - u16 special key flags - bit 0 = - u16 reserved - u16 number of user IDs - u16 size of additional user ID information - n times: - u32 offset to the n-th user ID - u32 length of this user ID. - u16 special user ID flags. - bit 0 = - byte validity - byte reserved - u16 number of signatures - u16 size of signature information (4) - u32 expiration time of signature with some special values: - 0x00000000 = not checked - 0x00000001 = missing key - 0x00000002 = bad signature - 0x10000000 = valid and expires at some date in 1978. - 0xffffffff = valid and does not expire - u8 assigned ownertrust - u8 all_validity - u16 reserved - u32 recheck_after - u32 Newest timestamp in the keyblock (useful for KS syncronsiation?) - u32 Blob created at - u32 size of reserved space (not including this field) - reserved space - - Here we might want to put other data - - Here comes the keyblock - - maybe we put a signature here later. - - b16 MD5 checksum (useful for KS syncronisation) - * - */ - - -#include <config.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <errno.h> -#include <assert.h> -#include <gcrypt.h> - -#include "iobuf.h" -#include "util.h" -#include "kbx.h" - -/* special values of the signature status */ -#define SF_NONE(a) ( !(a) ) -#define SF_NOKEY(a) ((a) & (1<<0)) -#define SF_BAD(a) ((a) & (1<<1)) -#define SF_VALID(a) ((a) & (1<<29)) - -#if MAX_FINGERPRINT_LEN < 20 - #error fingerprints are 20 bytes -#endif - -struct kbxblob_key { - char fpr[20]; - u32 off_kid; - ulong off_kid_addr; - u16 flags; -}; -struct kbxblob_uid { - ulong off_addr; - u32 len; - u16 flags; - byte validity; -}; - -struct keyid_list { - struct keyid_list *next; - int seqno; - byte kid[8]; -}; - -struct fixup_list { - struct fixup_list *next; - u32 off; - u32 val; -}; - - -struct kbxblob { - byte *blob; - size_t bloblen; - - /* stuff used only by kbx_create_blob */ - int nkeys; - struct kbxblob_key *keys; - int nuids; - struct kbxblob_uid *uids; - int nsigs; - u32 *sigs; - struct fixup_list *fixups; - - struct keyid_list *temp_kids; - IOBUF buf; /* the KBX is temporarly stored here */ -}; - -void kbx_release_blob ( KBXBLOB blob ); - -/* Note: this functions are only used for temportay iobufs and therefore - * they can't fail */ -static void -put8 ( IOBUF out, byte a ) -{ - iobuf_put ( out, a ); -} - -static void -put16 ( IOBUF out, u16 a ) -{ - iobuf_put ( out, a>>8 ); - iobuf_put ( out, a ); -} - -static void -put32 ( IOBUF out, u32 a ) -{ - iobuf_put (out, a>> 24); - iobuf_put (out, a>> 16); - iobuf_put (out, a>> 8); - iobuf_put (out, a ); -} - -static void -putn ( IOBUF out, const byte *p, size_t n ) -{ - for ( ; n; p++, n-- ) { - iobuf_put ( out, *p ); - } -} - - -/**************** - * We must store the keyid at some place because we can't calculate the - * offset yet. This is only used for v3 keyIDs. Function returns an index - * value for later fixupd; this must be a non-zero value - */ -static int -temp_store_kid ( KBXBLOB blob, PKT_public_key *pk ) -{ - struct keyid_list *k, *r; - - k = gcry_xmalloc ( sizeof *k ); - k->kid[0] = pk->keyid[0] >> 24 ; - k->kid[1] = pk->keyid[0] >> 16 ; - k->kid[2] = pk->keyid[0] >> 8 ; - k->kid[3] = pk->keyid[0] ; - k->kid[4] = pk->keyid[0] >> 24 ; - k->kid[5] = pk->keyid[0] >> 16 ; - k->kid[6] = pk->keyid[0] >> 8 ; - k->kid[7] = pk->keyid[0] ; - k->seqno = 0; - k->next = blob->temp_kids; - blob->temp_kids = k; - for ( r=k; r; r = r->next ) { - k->seqno++; - } - - return k->seqno; -} - -static void -put_stored_kid( KBXBLOB blob, int seqno ) -{ - struct keyid_list *r; - - for ( r = blob->temp_kids; r; r = r->next ) { - if( r->seqno == seqno ) { - putn ( blob->buf, r->kid, 8 ); - return; - } - } - BUG(); -} - -static void -release_kid_list ( struct keyid_list *kl ) -{ - struct keyid_list *r, *r2; - - for ( r = kl; r; r = r2 ) { - r2 = r->next; - gcry_free( r ); - } -} - - -static int -create_key_part( KBXBLOB blob, KBNODE keyblock ) -{ - KBNODE node; - size_t fprlen; - int n; - - for ( n=0, 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; - char tmp[20]; - - fingerprint_from_pk( pk, tmp , &fprlen ); - memcpy(blob->keys[n].fpr,tmp,20); - if ( fprlen != 20 ) { /*v3 fpr - shift right and fill with zeroes*/ - assert( fprlen == 16 ); - memmove( blob->keys[n].fpr+4, blob->keys[n].fpr, 16); - memset( blob->keys[n].fpr, 0, 4 ); - blob->keys[n].off_kid = temp_store_kid( blob, pk ); - } - else { - blob->keys[n].off_kid = 0; /* will be fixed up later */ - } - blob->keys[n].flags = 0; - n++; - } - else if ( node->pkt->pkttype == PKT_SECRET_KEY - || node->pkt->pkttype == PKT_SECRET_SUBKEY ) { - BUG(); /* not yet implemented */ - } - } - assert( n == blob->nkeys ); - return 0; -} - -static int -create_uid_part( KBXBLOB blob, KBNODE keyblock ) -{ - KBNODE node; - int n; - - for ( n=0, node = keyblock; node; node = node->next ) { - if ( node->pkt->pkttype == PKT_USER_ID ) { - PKT_user_id *u = node->pkt->pkt.user_id; - - blob->uids[n].len = u->len; - blob->uids[n].flags = 0; - blob->uids[n].validity = 0; - n++; - } - } - assert( n == blob->nuids ); - return 0; -} - -static int -create_sig_part( KBXBLOB blob, KBNODE keyblock ) -{ - KBNODE node; - int n; - - for ( n=0, node = keyblock; node; node = node->next ) { - if ( node->pkt->pkttype == PKT_SIGNATURE ) { - PKT_signature *sig = node->pkt->pkt.signature; - - blob->sigs[n] = 0; /* FIXME: check the signature here */ - n++; - } - } - assert( n == blob->nsigs ); - return 0; -} - - -static int -create_blob_header( KBXBLOB blob ) -{ - IOBUF a = blob->buf; - int i; - - put32 ( a, 0 ); /* blob length, needs fixup */ - put8 ( a, 2 ); /* blob type */ - put8 ( a, 1 ); /* blob type version */ - put16 ( a, 0 ); /* blob flags */ - - put32 ( a, 0 ); /* offset to the keyblock, needs fixup */ - put32 ( a, 0 ); /* length of the keyblock, needs fixup */ - - put16 ( a, blob->nkeys ); - put16 ( a, 20 + 4 + 2 + 2 ); /* size of key info */ - for ( i=0; i < blob->nkeys; i++ ) { - putn ( a, blob->keys[i].fpr, 20 ); - blob->keys[i].off_kid_addr = iobuf_get_temp_length (a); - put32 ( a, 0 ); /* offset to keyid, fixed up later */ - put16 ( a, blob->keys[i].flags ); - put16 ( a, 0 ); /* reserved */ - } - - put16 ( a, blob->nuids ); - put16 ( a, 4 + 4 + 2 + 1 + 1 ); /* size of uid info */ - for ( i=0; i < blob->nuids; i++ ) { - blob->uids[i].off_addr = iobuf_get_temp_length ( a ); - put32 ( a, 0 ); /* offset to userid, fixed up later */ - put32 ( a, blob->uids[i].len ); - put16 ( a, blob->uids[i].flags ); - put8 ( a, 0 ); /* validity */ - put8 ( a, 0 ); /* reserved */ - } - - put16 ( a, blob->nsigs ); - put16 ( a, 4 ); /* size of sig info */ - for ( i=0; i < blob->nsigs; i++ ) { - put32 ( a, blob->sigs[i] ); - } - - put8 ( a, 0 ); /* assigned ownertrust */ - put8 ( a, 0 ); /* validity of all user IDs */ - put16 ( a, 0 ); /* reserved */ - put32 ( a, 0 ); /* time of next recheck */ - put32 ( a, 0 ); /* newest timestamp (none) */ - put32 ( a, make_timestamp() ); /* creation time */ - put32 ( a, 0 ); /* size of reserved space */ - /* reserved space (which is currently of size 0) */ - - /* We need to store the keyids for all v3 keys because those key IDs are - * not part of the fingerprint. While we are doing that, we fixup all - * the keyID offsets */ - for ( i=0; i < blob->nkeys; i++ ) { - struct fixup_list *fl = gcry_xcalloc(1, sizeof *fl ); - fl->off = blob->keys[i].off_kid_addr; - fl->next = blob->fixups; - blob->fixups = fl; - - if ( blob->keys[i].off_kid ) { /* this is a v3 one */ - fl->val = iobuf_get_temp_length (a); - put_stored_kid ( blob, blob->keys[i].off_kid ); - } - else { /* the better v4 key IDs - just store an offset 8 bytes back */ - fl->val = blob->keys[i].off_kid_addr-8; - } - } - - - return 0; -} - -static int -create_blob_keyblock( KBXBLOB blob, KBNODE keyblock ) -{ - IOBUF a = blob->buf; - KBNODE node; - int rc; - int n; - u32 kbstart = iobuf_get_temp_length ( a ); - - { - struct fixup_list *fl = gcry_xcalloc(1, sizeof *fl ); - fl->off = 8; - fl->val = kbstart; - fl->next = blob->fixups; - blob->fixups = fl; - } - for ( n = 0, node = keyblock; node; node = node->next ) { - rc = build_packet ( a, node->pkt ); - if ( rc ) { - gpg_log_error("build_packet(%d) for kbxblob failed: %s\n", - node->pkt->pkttype, gpg_errstr(rc) ); - return GPGERR_WRITE_FILE; - } - if ( node->pkt->pkttype == PKT_USER_ID ) { - PKT_user_id *u = node->pkt->pkt.user_id; - /* build_packet has set the offset of the name into u ; - * now we can do the fixup */ - struct fixup_list *fl = gcry_xcalloc(1, sizeof *fl ); - fl->off = blob->uids[n].off_addr; - fl->val = u->stored_at; - fl->next = blob->fixups; - blob->fixups = fl; - n++; - } - } - assert( n == blob->nuids ); - { - struct fixup_list *fl = gcry_xcalloc(1, sizeof *fl ); - fl->off = 12; - fl->val = iobuf_get_temp_length (a) - kbstart; - fl->next = blob->fixups; - blob->fixups = fl; - } - return 0; -} - -static int -create_blob_trailer( KBXBLOB blob ) -{ - IOBUF a = blob->buf; - return 0; -} - -static int -create_blob_finish( KBXBLOB blob ) -{ - IOBUF a = blob->buf; - byte *p; - char *pp; - int i; - size_t n; - - /* write a placeholder for the checksum */ - for ( i = 0; i < 16; i++ ) - put32( a, 0 ); - /* get the memory area */ - iobuf_flush_temp ( a ); - p = iobuf_get_temp_buffer ( a ); - n = iobuf_get_temp_length ( a ); - assert( n >= 20 ); - - /* fixup the length */ - { - struct fixup_list *fl = gcry_xcalloc(1, sizeof *fl ); - fl->off = 0; - fl->val = n; - fl->next = blob->fixups; - blob->fixups = fl; - } - /* do the fixups */ - { - struct fixup_list *fl; - for ( fl = blob->fixups; fl; fl = fl->next ) { - assert( fl->off+4 <= n ); - p[fl->off+0] = fl->val >> 24 ; - p[fl->off+1] = fl->val >> 16 ; - p[fl->off+2] = fl->val >> 8 ; - p[fl->off+3] = fl->val ; - } - - } - - /* calculate and store the MD5 checksum */ - gcry_md_hash_buffer( GCRY_MD_MD5, p + n - 16, p, n - 16 ); - - pp = gcry_malloc ( n ); - if ( !pp ) - return GCRYERR_NO_MEM; - memcpy ( pp , p, n ); - blob->blob = pp; - blob->bloblen = n; - - return 0; -} - - -int -kbx_create_blob ( KBXBLOB *r_blob, KBNODE keyblock ) -{ - int rc = 0; - KBNODE node; - KBXBLOB blob; - - *r_blob = NULL; - blob = gcry_xcalloc (1, sizeof *blob ); - if( !blob ) - return GCRYERR_NO_MEM; - - /* fixme: Do some sanity checks on the keyblock */ - - /* count userids and keys so that we can allocate the arrays */ - for ( node = keyblock; node; node = node->next ) { - switch ( node->pkt->pkttype ) { - case PKT_PUBLIC_KEY: - case PKT_SECRET_KEY: - case PKT_PUBLIC_SUBKEY: - case PKT_SECRET_SUBKEY: blob->nkeys++; break; - case PKT_USER_ID: blob->nuids++; break; - case PKT_SIGNATURE: blob->nsigs++; break; - default: break; - } - } - blob->keys = gcry_xcalloc ( blob->nkeys, sizeof ( *blob->keys ) ); - blob->uids = gcry_xcalloc ( blob->nuids, sizeof ( *blob->uids ) ); - blob->sigs = gcry_xcalloc ( blob->nsigs, sizeof ( *blob->sigs ) ); - if ( !blob->keys || !blob->uids || !blob->sigs ) { - rc = GCRYERR_NO_MEM; - goto leave; - } - - rc = create_key_part ( blob, keyblock ); - if( rc ) - goto leave; - rc = create_uid_part ( blob, keyblock ); - if( rc ) - goto leave; - rc = create_sig_part ( blob, keyblock ); - if( rc ) - goto leave; - - blob->buf = iobuf_temp(); - rc = create_blob_header ( blob ); - if( rc ) - goto leave; - rc = create_blob_keyblock ( blob, keyblock ); - if( rc ) - goto leave; - rc = create_blob_trailer ( blob ); - if( rc ) - goto leave; - rc = create_blob_finish ( blob ); - if( rc ) - goto leave; - - - leave: - release_kid_list( blob->temp_kids ); - blob->temp_kids = NULL; - if ( rc ) { - kbx_release_blob ( blob ); - *r_blob = NULL; - } - else { - *r_blob = blob; - } - return rc; -} - -int -kbx_new_blob ( KBXBLOB *r_blob, char *image, size_t imagelen ) -{ - KBXBLOB blob; - - *r_blob = NULL; - blob = gcry_xcalloc (1, sizeof *blob ); - if( !blob ) - return GCRYERR_NO_MEM; - blob->blob = image; - blob->bloblen = imagelen; - *r_blob = blob; - return 0; -} - - - -const char * -kbx_get_blob_image ( KBXBLOB blob, size_t *n ) -{ - *n = blob->bloblen; - return blob->blob; -} - -void -kbx_release_blob ( KBXBLOB blob ) -{ - if( !blob ) - return; - if( blob->buf ) - iobuf_cancel( blob->buf ); - gcry_free( blob->keys ); - gcry_free( blob->uids ); - gcry_free( blob->sigs ); - - gcry_free ( blob->blob ); - - gcry_free( blob ); -} - -static ulong -get32( const byte *buffer ) -{ - ulong a; - a = *buffer << 24; - a |= buffer[1] << 16; - a |= buffer[2] << 8; - a |= buffer[3]; - return a; -} - -static ulong -get16( const byte *buffer ) -{ - ulong a; - a = *buffer << 8; - a |= buffer[1]; - return a; -} - - -int -kbx_dump_blob ( FILE *fp, KBXBLOB blob ) -{ - const byte *buffer = blob->blob; - size_t length = blob->bloblen; - ulong n, nkeys, keyinfolen; - ulong nuids, uidinfolen; - ulong nsigs, siginfolen; - ulong keyblock_off, keyblock_len; - const byte *p; - - if( length < 40 ) { - fprintf( fp, "blob too short\n"); - return -1; - } - n = get32( buffer ); - if( n > length ) { - fprintf( fp, "blob larger than length - output truncated\n"); - } - else - length = n; /* ignore the rest */ - fprintf( fp, "Length: %lu\n", n ); - fprintf( fp, "Type: %d\n", buffer[4] ); - fprintf( fp, "Version: %d\n", buffer[5] ); - if( buffer[4] != 2 ) { - fprintf( fp, "can't dump this blob type\n" ); - return 0; - } - - n = get16( buffer + 6 ); - fprintf( fp, "Blob-Flags: %04lX\n", n ); - keyblock_off = get32( buffer + 8 ); - keyblock_len = get32( buffer + 12 ); - fprintf( fp, "Keyblock-Offset: %lu\n", keyblock_off ); - fprintf( fp, "Keyblock-Length: %lu\n", keyblock_len ); - - nkeys = get16( buffer + 16 ); - fprintf( fp, "Key-Count: %lu\n", nkeys ); - keyinfolen = get16( buffer + 18 ); - fprintf( fp, "Key-Info-Length: %lu\n", keyinfolen ); - /* fixme: check bounds */ - p = buffer + 20; - for(n=0; n < nkeys; n++, p += keyinfolen ) { - int i; - ulong kidoff, kflags; - - fprintf( fp, "Key-%lu-Fpr: ", n ); - for(i=0; i < 20; i++ ) - fprintf( fp, "%02X", p[i] ); - kidoff = get32( p + 20 ); - fprintf( fp, "\nKey-%lu-Kid-Off: %lu\n", n, kidoff ); - fprintf( fp, "Key-%lu-Kid: ", n ); - /* fixme: check bounds */ - for(i=0; i < 8; i++ ) - fprintf( fp, "%02X", buffer[kidoff+i] ); - kflags = get16( p + 24 ); - fprintf( fp, "\nKey-%lu-Flags: %04lX\n", n, kflags ); - } - - - nuids = get16( p ); - fprintf( fp, "Uid-Count: %lu\n", nuids ); - uidinfolen = get16( p + 2 ); - fprintf( fp, "Uid-Info-Length: %lu\n", uidinfolen ); - /* fixme: check bounds */ - p += 4; - for(n=0; n < nuids; n++, p += uidinfolen ) { - ulong uidoff, uidlen, uflags; - - uidoff = get32( p ); - uidlen = get32( p+4 ); - fprintf( fp, "Uid-%lu-Off: %lu\n", n, uidoff ); - fprintf( fp, "Uid-%lu-Len: %lu\n", n, uidlen ); - fprintf( fp, "Uid-%lu: \"", n ); - print_string( fp, buffer+uidoff, uidlen, '\"' ); - fputs("\"\n", fp ); - uflags = get16( p + 8 ); - fprintf( fp, "Uid-%lu-Flags: %04lX\n", n, uflags ); - fprintf( fp, "Uid-%lu-Validity: %d\n", n, p[10] ); - } - - nsigs = get16( p ); - fprintf( fp, "Sig-Count: %lu\n", nsigs ); - siginfolen = get16( p + 2 ); - fprintf( fp, "Sig-Info-Length: %lu\n", siginfolen ); - /* fixme: check bounds */ - p += 4; - for(n=0; n < nsigs; n++, p += siginfolen ) { - ulong sflags; - - sflags = get32( p ); - fprintf( fp, "Sig-%lu-Expire: ", n ); - if( !sflags ) - fputs( "[not checked]", fp ); - else if( sflags == 1 ) - fputs( "[missing key]", fp ); - else if( sflags == 2 ) - fputs( "[bad signature]", fp ); - else if( sflags < 0x10000000 ) - fprintf( fp, "[bad flag %0lx]", sflags ); - else if( sflags == 0xffffffff ) - fputs( "0", fp ); - else - fputs( strtimestamp( sflags ), fp ); - putc('\n', fp ); - } - - fprintf( fp, "Ownertrust: %d\n", p[0] ); - fprintf( fp, "All-Validity: %d\n", p[1] ); - p += 4; - n = get32( p ); p += 4; - fprintf( fp, "Recheck-After: %s\n", n? strtimestamp(n) : "0" ); - n = get32( p ); p += 4; - fprintf( fp, "Latest-Timestamp: %s\n", strtimestamp(n) ); - n = get32( p ); p += 4; - fprintf( fp, "Created-At: %s\n", strtimestamp(n) ); - n = get32( p ); p += 4; - fprintf( fp, "Reserved-Space: %lu\n", n ); - - - /* check that the keyblock is at the correct offset and other bounds */ - - - fprintf( fp, "Blob-Checksum: [MD5-hash]\n" ); - return 0; -} - -/**************** - * Check whether the given fingerprint (20 bytes) is in the - * given keyblob. fpr is always 20 bytes. - * Return: 0 = found - * -1 = not found - other = error (fixme: do not always reurn gpgerr_general) - */ -int -kbx_blob_has_fpr ( KBXBLOB blob, const byte *fpr ) -{ - ulong n, nkeys, keyinfolen; - const byte *p, *pend; - byte *buffer = blob->blob; - size_t buflen = blob->bloblen; - - if ( buflen < 40 ) - return GPGERR_GENERAL; /* blob too short */ - n = get32( buffer ); - if ( n > buflen ) - return GPGERR_GENERAL; /* blob larger than announced length */ - buflen = n; /* ignore trailing stuff */ - pend = buffer + n - 1; - - if ( buffer[4] != 2 ) - return GPGERR_GENERAL; /* invalid blob type */ - if ( buffer[5] != 1 ) - return GPGERR_GENERAL; /* invalid blob format version */ - - nkeys = get16( buffer + 16 ); - keyinfolen = get16( buffer + 18 ); - p = buffer + 20; - for(n=0; n < nkeys; n++, p += keyinfolen ) { - if ( p+20 > pend ) - return GPGERR_GENERAL; /* blob shorter than required */ - if (!memcmp ( p, fpr, 20 ) ) - return 0; /* found */ - } - return -1; -} - -/**************** - * Check whether the given keyID (20 bytes) is in the - * given keyblob. - * Return: 0 = found - * -1 = not found - other = error (fixme: do not always return gpgerr_general) - */ -int -kbx_blob_has_kid ( KBXBLOB blob, const byte *keyidbuf, size_t keyidlen ) -{ - ulong n, nkeys, keyinfolen, off; - const byte *p, *pend; - byte *buffer = blob->blob; - size_t buflen = blob->bloblen; - - if ( buflen < 40 ) - return GPGERR_GENERAL; /* blob too short */ - n = get32( buffer ); - if ( n > buflen ) - return GPGERR_GENERAL; /* blob larger than announced length */ - buflen = n; /* ignore trailing stuff */ - pend = buffer + n - 1; - - if ( buffer[4] != 2 ) - return GPGERR_GENERAL; /* invalid blob type */ - if ( buffer[5] != 1 ) - return GPGERR_GENERAL; /* invalid blob format version */ - - nkeys = get16( buffer + 16 ); - keyinfolen = get16( buffer + 18 ); - p = buffer + 20; - for(n=0; n < nkeys; n++, p += keyinfolen ) { - if ( p+24 > pend ) - return GPGERR_GENERAL; /* blob shorter than required */ - off = get32 ( p + 20 ); - if (keyidlen < 8 ) /* actually keyidlen may either be 4 or 8 */ - off +=4; - if ( off+keyidlen > buflen ) - return GPGERR_GENERAL; /* offset out of bounds */ - if ( !memcmp ( buffer+off, keyidbuf, keyidlen ) ) - return 0; /* found */ - } - return -1; -} - - - -int -kbx_blob_has_uid ( KBXBLOB blob, - int (*cmp)(const byte *, size_t, void *), void *opaque ) -{ - ulong n, nuids, uidinfolen, off, len; - const byte *p, *pend; - byte *buffer = blob->blob; - size_t buflen = blob->bloblen; - - if ( buflen < 40 ) - return GPGERR_GENERAL; /* blob too short */ - n = get32( buffer ); - if ( n > buflen ) - return GPGERR_GENERAL; /* blob larger than announced length */ - buflen = n; /* ignore trailing stuff */ - pend = buffer + n - 1; - - if ( buffer[4] != 2 ) - return GPGERR_GENERAL; /* invalid blob type */ - if ( buffer[5] != 1 ) - return GPGERR_GENERAL; /* invalid blob format version */ - - p = buffer + 20 + get16( buffer + 16 ) * get16( buffer + 18 ); - if ( p+4 > pend ) - return GPGERR_GENERAL; /* blob shorter than required */ - - nuids = get16( p ); p+= 2; - uidinfolen = get16( p ); p+=2; - for(n=0; n < nuids; n++, p += uidinfolen ) { - if ( p+8 > pend ) - return GPGERR_GENERAL; /* blob shorter than required */ - off = get32 ( p ); - len = get32 ( p + 4 ); - if ( off+len > buflen ) - return GPGERR_GENERAL; /* offset out of bounds */ - if ( (*cmp) ( buffer+off, len, opaque ) ) - return 0; /* found */ - } - - return -1; -} - - diff --git a/g10/kbxfile.c b/g10/kbxfile.c deleted file mode 100644 index 94ef13f83..000000000 --- a/g10/kbxfile.c +++ /dev/null @@ -1,332 +0,0 @@ -/* kbxfile.c - KBX file handling - * Copyright (C) 2000 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA - */ - -/**************** - * We will change the whole system to use only KBX. This file here - * will implement the methods needed to operate on plain KBXfiles. - * Most stuff from getkey and ringedit will be replaced by stuff here. - * To make things even more easier we will only allow one updateable kbxfile - * and optionally some read-only files. - */ - -#include <config.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <errno.h> -#include <assert.h> -#include <gcrypt.h> - -#include "kbx.h" -#include "options.h" -#include "util.h" -#include "i18n.h" -#include "main.h" - -/**************** - * Read the blob at the current fileposition and return an allocated - * pointer nto the blob if it was found. - * Fixme: return a blob object. - */ -static int -do_search_by_fpr ( const char *filename, FILE *a, const char *fpr, - KBXBLOB *r_blob ) -{ - KBXBLOB blob; - int rc; - - *r_blob = NULL; - rc = kbx_read_blob ( &blob, a ); - if ( rc && rc != -1 ) { - log_error (_("file `%s': error reading blob\n"), filename ); - } - else if ( !rc ) { - rc = kbx_blob_has_fpr ( blob, fpr ); - } - else - log_info ("eof\n"); - - if ( !rc ) { - *r_blob = blob; - } - else { - kbx_release_blob ( blob ); - } - return rc; -} - -int -kbxfile_search_by_fpr( const char *filename, const byte *fpr ) -{ - FILE *fp; - KBXBLOB blob; - int rc; - - fp = fopen ( filename, "rb" ); - if( !fp ) { - log_error(_("can't open `%s': %s\n"), filename, strerror(errno) ); - return 1; - } - - while ( (rc=do_search_by_fpr ( filename, fp, fpr, &blob )) == -1 ) - ; - if ( !rc ) { - fputs ("FOUND\n", stderr ); - kbx_dump_blob ( stderr, blob ); - kbx_release_blob ( blob ); - } - - fclose (fp); - return -1; -} - - -/**************** - * Read the blob at the current fileposition and return an allocated - * pointer nto the blob if it was found. - * Fixme: return a blob object. - */ -static int -do_search_by_keyid ( const char *filename, FILE *a, - const byte *keyidbuf, size_t keyidlen, KBXBLOB *r_blob ) -{ - KBXBLOB blob; - int rc; - - *r_blob = NULL; - rc = kbx_read_blob ( &blob, a ); - if ( rc && rc != -1 ) { - log_error (_("file `%s': error reading blob\n"), filename ); - } - else if ( !rc ) { - rc = kbx_blob_has_kid ( blob, keyidbuf, keyidlen ); - } - else - rc = GPGERR_GENERAL; /* eof */ - - if ( !rc ) { - *r_blob = blob; - } - else { - kbx_release_blob ( blob ); - } - return rc; -} - -/**************** - * Look for a KBX described by an keyid. This function will in - * turn return each matching keyid because there may me duplicates - * (which can't happen for fingerprints) - * mode 10 = short keyid - * 11 = long keyid - */ -int -kbxfile_search_by_kid ( const char *filename, u32 *kid, int mode ) -{ - FILE *fp; - KBXBLOB blob; - int rc; - byte kbuf[8], *kbufptr; - int kbuflen; - - fp = fopen ( filename, "rb" ); - if( !fp ) { - log_error(_("can't open `%s': %s\n"), filename, strerror(errno) ); - return 1; - } - - kbuf[0] = kid[0] >> 24; - kbuf[1] = kid[0] >> 16; - kbuf[2] = kid[0] >> 8; - kbuf[3] = kid[0]; - kbuf[4] = kid[1] >> 24; - kbuf[5] = kid[1] >> 16; - kbuf[6] = kid[1] >> 8; - kbuf[7] = kid[1]; - if ( mode == 10 ) { - kbufptr=kbuf+4; - kbuflen = 4; - } - else if (mode == 11 ) { - kbufptr=kbuf; - kbuflen = 8; - } - else { - BUG(); - } - - do { - while ( (rc=do_search_by_keyid ( filename, fp, - kbufptr, kbuflen, &blob )) == -1 ) - ; - if ( !rc ) { - fputs ("FOUND:\n", stderr ); - kbx_dump_blob ( stderr, blob ); - kbx_release_blob ( blob ); - } - } while ( !rc ); - - fclose (fp); - return -1; -} - - -static int -do_search_by_uid ( const char *filename, FILE *a, - int (*cmpfnc)(const byte*,size_t,void*), void *cmpdata, - KBXBLOB *r_blob ) -{ - KBXBLOB blob; - int rc; - - *r_blob = NULL; - rc = kbx_read_blob ( &blob, a ); - if ( rc && rc != -1 ) { - log_error (_("file `%s': error reading blob\n"), filename ); - } - else if ( !rc ) { - rc = kbx_blob_has_uid ( blob, cmpfnc, cmpdata ); - } - else - rc = GPGERR_GENERAL; /* eof */ - - if ( !rc ) { - *r_blob = blob; - } - else { - kbx_release_blob ( blob ); - } - return rc; -} - - -static int -substr_compare ( const byte *buf, size_t buflen, void *opaque ) -{ - return !!memistr ( buf, buflen, opaque ); -} - - -int -kbxfile_search_by_uid ( const char *filename, const char *name ) -{ - FILE *fp; - KBXBLOB blob; - int rc; - byte kbuf[8], *kbufptr; - int kbuflen; - - fp = fopen ( filename, "rb" ); - if( !fp ) { - log_error(_("can't open `%s': %s\n"), filename, strerror(errno) ); - return 1; - } - - - do { - while ( (rc=do_search_by_uid ( filename, fp, - substr_compare, name, &blob )) == -1 ) - ; - if ( !rc ) { - fputs ("FOUND:\n", stderr ); - kbx_dump_blob ( stderr, blob ); - kbx_release_blob ( blob ); - } - } while ( !rc ); - - fclose ( fp ); - return -1; -} - - - -void -export_as_kbxfile(void) -{ - - KBPOS kbpos; - KBNODE keyblock = NULL; - int rc=0; - - rc = enum_keyblocks_begin( &kbpos, 0 ); - if( rc ) { - if( rc != -1 ) - log_error("enum_keyblocks(open) failed: %s\n", gpg_errstr(rc) ); - goto leave; - } - - while( !(rc = enum_keyblocks_next( kbpos, 1, &keyblock )) ) { - KBXBLOB blob; - const char *p; - size_t n; - - merge_keys_and_selfsig( keyblock ); - rc = kbx_create_blob ( &blob, keyblock ); - if( rc ) { - log_error("kbx_create_blob failed: %s\n", gpg_errstr(rc) ); - goto leave; - } - p = kbx_get_blob_image ( blob, &n ); - fwrite( p, n, 1, stdout ); - kbx_release_blob ( blob ); - } - - if( rc && rc != -1 ) - log_error("enum_keyblocks(read) failed: %s\n", gpg_errstr(rc)); - - leave: - enum_keyblocks_end( kbpos ); - release_kbnode( keyblock ); -} - - -static int -do_print_kbxfile( const char *filename, FILE *a ) -{ - KBXBLOB blob; - int rc; - - rc = kbx_read_blob ( &blob, a ); - if ( rc && rc != -1 ) { - log_error (_("file `%s': error reading blob\n"), filename ); - } - else if ( ! rc ) - kbx_dump_blob ( stdout, blob ); - kbx_release_blob ( blob ); - return rc; -} - -void -print_kbxfile( const char *filename ) -{ - FILE *fp; - - fp = fopen ( filename, "rb" ); - if( !fp ) { - log_error(_("can't open `%s': %s\n"), filename, strerror(errno) ); - return; - } - - while ( !do_print_kbxfile( filename, fp ) ) - ; - - fclose (fp); -} - diff --git a/g10/kbxio.c b/g10/kbxio.c deleted file mode 100644 index 6c4437bf8..000000000 --- a/g10/kbxio.c +++ /dev/null @@ -1,75 +0,0 @@ -/* kbxio.c - KBX I/O handling - * Copyright (C) 2000 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA - */ - - -#include <config.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <errno.h> -#include <assert.h> -#include <gcrypt.h> - -#include "iobuf.h" -#include "util.h" -#include "kbx.h" - - -int -kbx_read_blob ( KBXBLOB *r_blob, FILE *a ) -{ - char *image; - size_t imagelen = 0; - int c1, c2, c3, c4; - int rc; - - *r_blob = NULL; - if ( (c1 = getc ( a )) == EOF - || (c2 = getc ( a )) == EOF - || (c3 = getc ( a )) == EOF - || (c4 = getc ( a )) == EOF ) { - if ( c1 == EOF && !ferror ( a ) ) - return -1; - return GPGERR_GENERAL; - } - imagelen = (c1 << 24) | (c2 << 16) | (c3 << 8 ) | c4; - if ( imagelen > 500000 ) { /* sanity check:blob too large */ - return GPGERR_GENERAL; - } - else if ( imagelen < 4 ) { /* blobtoo short */ - return GPGERR_GENERAL; - } - image = gcry_malloc ( imagelen ); - if ( !image ) { - return GPGERR_GENERAL; - } - - image[0] = c1; image[1] = c2; image[2] = c3; image[3] = c4; - if ( fread ( image+4, imagelen-4, 1, a ) != 1 ) { - gcry_free ( image ); - return GPGERR_GENERAL; - } - - rc = kbx_new_blob ( r_blob, image, imagelen ); - return rc; -} - - - diff --git a/g10/kbxutil.c b/g10/kbxutil.c deleted file mode 100644 index 95fcb9cce..000000000 --- a/g10/kbxutil.c +++ /dev/null @@ -1,442 +0,0 @@ -/* gpg.c - The GnuPG utility (main for gpg) - * Copyright (C) 1998, 1999, 2000 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA - */ - -#include <config.h> -#include <errno.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <ctype.h> -#include <unistd.h> - -#include <gcrypt.h> - -#include "packet.h" -#include "iobuf.h" -#include "util.h" -#include "main.h" -#include "options.h" -#include "keydb.h" -#include "filter.h" -#include "ttyio.h" -#include "i18n.h" -#include "gnupg-defs.h" -#include "kbx.h" - - -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', - aSign = 's', - oTextmodeShort= 't', - oUser = 'u', - oVerbose = 'v', - oCompress = 'z', - oNotation = 'N', - oBatch = 500, - aClearsign, - aStore, - aKeygen, - aSignEncr, - aSignKey, - aLSignKey, - aListPackets, - aEditKey, - aDeleteKey, - aDeleteSecretKey, - aKMode, - aKModeC, - aImport, - aFastImport, - aVerify, - aListKeys, - aListSigs, - aListSecretKeys, - aSendKeys, - aRecvKeys, - aExport, - aExportAll, - aExportSecret, - aCheckKeys, - aGenRevoke, - aPrimegen, - aPrintMD, - aPrintHMAC, - aPrintMDs, - aCheckTrustDB, - aUpdateTrustDB, - aFixTrustDB, - aListTrustDB, - aListTrustPath, - aExportOwnerTrust, - aImportOwnerTrust, - aDeArmor, - aEnArmor, - aGenRandom, - - oTextmode, - oFingerprint, - oWithFingerprint, - oAnswerYes, - oAnswerNo, - oKeyring, - oSecretKeyring, - oDefaultKey, - oDefRecipient, - oDefRecipientSelf, - oNoDefRecipient, - oOptions, - oDebug, - oDebugAll, - oStatusFD, - oNoComment, - oNoVersion, - oEmitVersion, - oCompletesNeeded, - oMarginalsNeeded, - oMaxCertDepth, - oLoadExtension, - oRFC1991, - oOpenPGP, - oCipherAlgo, - oDigestAlgo, - oCompressAlgo, - oPasswdFD, - oNoVerbose, - oTrustDBName, - oNoSecmemWarn, - oNoArmor, - oNoDefKeyring, - oNoGreeting, - oNoTTY, - oNoOptions, - oNoBatch, - oHomedir, - oWithColons, - oWithKeyData, - oSkipVerify, - oCompressKeys, - oCompressSigs, - oAlwaysTrust, - oEmuChecksumBug, - oRunAsShmCP, - oSetFilename, - oSetPolicyURL, - oUseEmbeddedFilename, - oComment, - oDefaultComment, - oThrowKeyid, - oForceV3Sigs, - oForceMDC, - oS2KMode, - oS2KDigest, - oS2KCipher, - oCharset, - oNotDashEscaped, - oEscapeFrom, - oLockOnce, - oLockMultiple, - oKeyServer, - oEncryptTo, - oNoEncryptTo, - oLoggerFD, - oUtf8Strings, - oNoUtf8Strings, - oDisableCipherAlgo, - oDisablePubkeyAlgo, - oAllowNonSelfsignedUID, - oNoLiteral, - oSetFilesize, - oEntropyDLLName, - - aFindByFpr, - aFindByKid, - aFindByUid, -aTest }; - - -static ARGPARSE_OPTS opts[] = { - - { 300, NULL, 0, N_("@Commands:\n ") }, - - { aFindByFpr, "find-by-fpr", 0, "|FPR| find key using it's fingerprnt" }, - { aFindByKid, "find-by-kid", 0, "|KID| find key using it's keyid" }, - { aFindByUid, "find-by-uid", 0, "|NAME| find key by user name" }, - - { 301, NULL, 0, N_("@\nOptions:\n ") }, - - { oArmor, "armor", 0, N_("create ascii armored output")}, - { oArmor, "armour", 0, "@" }, - { oCompress, NULL, 1, N_("|N|set compress level N (0 disables)") }, - { oOutput, "output", 2, N_("use as output file")}, - { oVerbose, "verbose", 0, N_("verbose") }, - { oQuiet, "quiet", 0, N_("be somewhat more quiet") }, - { oDryRun, "dry-run", 0, N_("do not make any changes") }, - { oOptions, "options" , 2, N_("read options from file")}, - - { oDebug, "debug" ,4|16, N_("set debugging flags")}, - { oDebugAll, "debug-all" ,0, N_("enable full debugging")}, - - -{0} }; - - - -int gpg_errors_seen = 0; - - -static const char * -my_strusage( int level ) -{ - const char *p; - switch( level ) { - case 11: p = "kbxutil (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: kbxutil [options] [files] (-h for help)"); - break; - case 41: p = - _("Syntax: kbxutil [options] [files]\n" - "list, export, import KBX data\n"); - break; - - - default: p = NULL; - } - return p; -} - - -static void -i18n_init(void) -{ - #ifdef USE_SIMPLE_GETTEXT - set_gettext_file( PACKAGE ); - #else - #ifdef ENABLE_NLS - #ifdef HAVE_LC_MESSAGES - setlocale( LC_TIME, "" ); - setlocale( LC_MESSAGES, "" ); - #else - setlocale( LC_ALL, "" ); - #endif - bindtextdomain( PACKAGE, GNUPG_LOCALEDIR ); - textdomain( PACKAGE ); - #endif - #endif -} - - -static void -wrong_args( const char *text ) -{ - log_error("usage: kbxutil %s\n", text); - gpg_exit ( 1 ); -} - - -static int -hextobyte( const byte *s ) -{ - int c; - - if( *s >= '0' && *s <= '9' ) - c = 16 * (*s - '0'); - else if( *s >= 'A' && *s <= 'F' ) - c = 16 * (10 + *s - 'A'); - else if( *s >= 'a' && *s <= 'f' ) - c = 16 * (10 + *s - 'a'); - else - return -1; - s++; - if( *s >= '0' && *s <= '9' ) - c += *s - '0'; - else if( *s >= 'A' && *s <= 'F' ) - c += 10 + *s - 'A'; - else if( *s >= 'a' && *s <= 'f' ) - c += 10 + *s - 'a'; - else - return -1; - return c; -} - -static char * -format_fingerprint ( const char *s ) -{ - int i, c; - byte fpr[20]; - - for (i=0; i < 20 && *s; ) { - if ( *s == ' ' || *s == '\t' ) { - s++; - continue; - } - c = hextobyte(s); - if (c == -1) { - return NULL; - } - fpr[i++] = c; - s += 2; - } - return gcry_xstrdup ( fpr ); -} - -static int -format_keyid ( const char *s, u32 *kid ) -{ - char helpbuf[9]; - switch ( strlen ( s ) ) { - case 8: - kid[0] = 0; - kid[1] = strtoul( s, NULL, 16 ); - return 10; - - case 16: - mem2str( helpbuf, s, 9 ); - kid[0] = strtoul( helpbuf, NULL, 16 ); - kid[1] = strtoul( s+8, NULL, 16 ); - return 11; - } - return 0; /* error */ -} - - - -int -main( int argc, char **argv ) -{ - ARGPARSE_ARGS pargs; - enum cmd_and_opt_values cmd = 0; - - set_strusage( my_strusage ); - log_set_name("kbxutil"); - /* check that the libraries are suitable. Do it here because - * the option parse may need services of the library */ - if ( !gcry_check_version ( "1.1.0a" ) ) { - log_fatal(_("libgcrypt is too old (need %s, have %s)\n"), - VERSION, gcry_check_version(NULL) ); - } - - create_dotlock(NULL); /* register locking cleanup */ - i18n_init(); - - - pargs.argc = &argc; - pargs.argv = &argv; - pargs.flags= 1; /* do not remove the args */ - while( arg_parse( &pargs, opts) ) { - switch( pargs.r_opt ) { - case oVerbose: - opt.verbose++; - gcry_control( GCRYCTL_SET_VERBOSITY, (int)opt.verbose ); - break; - case oDebug: opt.debug |= pargs.r.ret_ulong; break; - case oDebugAll: opt.debug = ~0; break; - - case aFindByFpr: - case aFindByKid: - case aFindByUid: - cmd = pargs.r_opt; - break; - - default : pargs.err = 2; break; - } - } - if( log_get_errorcount(0) ) - gpg_exit(2); - - if ( !cmd ) { /* default is to list a KBX file */ - if( !argc ) { - print_kbxfile( NULL ); - } - else { - for ( ; argc; argc--, argv++ ) { - print_kbxfile( *argv ); - } - } - } - else if ( cmd == aFindByFpr ) { - char *fpr; - if ( argc != 2 ) - wrong_args ("kbxfile foingerprint"); - fpr = format_fingerprint ( argv[1] ); - if ( !fpr ) - log_error ("invalid formatted fingerprint\n"); - else { - kbxfile_search_by_fpr ( argv[0], fpr ); - gcry_free ( fpr ); - } - } - else if ( cmd == aFindByKid ) { - u32 kid[2]; - int mode; - - if ( argc != 2 ) - wrong_args ("kbxfile short-or-long-keyid"); - mode = format_keyid ( argv[1], kid ); - if ( !mode ) - log_error ("invalid formatted keyID\n"); - else { - kbxfile_search_by_kid ( argv[0], kid, mode ); - } - } - else if ( cmd == aFindByUid ) { - if ( argc != 2 ) - wrong_args ("kbxfile userID"); - kbxfile_search_by_uid ( argv[0], argv[1] ); - } - else - log_error ("unsupported action\n"); - - gpg_exit(0); - return 8; /*NEVER REACHED*/ -} - - -void -gpg_exit( int rc ) -{ - if( opt.debug & DBG_MEMSTAT_VALUE ) { - gcry_control( GCRYCTL_DUMP_MEMORY_STATS ); - gcry_control( GCRYCTL_DUMP_RANDOM_STATS ); - } - if( opt.debug ) - gcry_control( GCRYCTL_DUMP_SECMEM_STATS ); - rc = rc? rc : log_get_errorcount(0)? 2 : - gpg_errors_seen? 1 : 0; - exit(rc ); -} - - diff --git a/g10/keydb.c b/g10/keydb.c new file mode 100644 index 000000000..96d19105a --- /dev/null +++ b/g10/keydb.c @@ -0,0 +1,698 @@ +/* keydb.c - key database dispatcher + * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, 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 20 + +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; + +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); + + +/* + * 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. + */ +int +keydb_add_resource (const char *url, int force, int secret) +{ + static int any_secret, any_public; + const char *resname = url; + IOBUF iobuf = NULL; + char *filename = NULL; + 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 = m_strdup (resname); + + check_permissions(filename,0,0); + + 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: + if (access(filename, F_OK)) + { /* file does not exist */ + char *last_slash_in_filename; + + if (!force) + { + rc = G10ERR_OPEN_FILE; + goto leave; + } + + last_slash_in_filename = strrchr (filename, DIRSEP_C); + *last_slash_in_filename = 0; + if (access(filename, F_OK)) + { /* on the first time we try to create the default + homedir and in this case the process will be + terminated, so that on the next invocation it can + read the options file in on startup */ + try_make_homedir (filename); + rc = G10ERR_OPEN_FILE; + *last_slash_in_filename = DIRSEP_C; + goto leave; + } + *last_slash_in_filename = DIRSEP_C; + + iobuf = iobuf_create (filename); + if (!iobuf) + { + log_error ( _("error creating keyring `%s': %s\n"), + filename, strerror(errno)); + rc = G10ERR_OPEN_FILE; + goto leave; + } + +#ifndef HAVE_DOSISH_SYSTEM + if (secret && !opt.preserve_permissions) + { + if (chmod (filename, S_IRUSR | S_IWUSR) ) + { + log_error (_("changing permission of " + " `%s' failed: %s\n"), + filename, strerror(errno) ); + rc = G10ERR_WRITE_FILE; + goto leave; + } + } +#endif + if (!opt.quiet) + log_info (_("keyring `%s' created\n"), filename); + iobuf_close (iobuf); + iobuf = NULL; + /* must invalidate that ugly cache */ + iobuf_ioctl (NULL, 2, 0, (char*)filename); + } /* end file creation */ + + token = keyring_register_filename (filename, secret); + if (!token) + ; /* already registered - ignore it */ + else if (used_resources >= MAX_KEYDB_RESOURCES) + rc = G10ERR_RESOURCE_LIMIT; + else + { + 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++; + } + 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) + log_error ("keyblock resource `%s': %s\n", filename, g10_errstr(rc)); + else if (secret) + any_secret = 1; + else + any_public = 1; + m_free (filename); + return rc; +} + + + + +KEYDB_HANDLE +keydb_new (int secret) +{ + KEYDB_HANDLE hd; + int i, j; + + hd = m_alloc_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) { + m_free (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; + } + } + + m_free (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; + + 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 (void) +{ + 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); + 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_search (KEYDB_HANDLE hd, KEYDB_SEARCH_DESC *desc, size_t ndesc) +{ + 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); + 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 index c2131ada7..34d02d702 100644 --- a/g10/keydb.h +++ b/g10/keydb.h @@ -1,5 +1,5 @@ /* keydb.h - Key database - * Copyright (C) 1998, 1999, 2000 Free Software Foundation, Inc. + * Copyright (C) 1998, 1999, 2000, 2001 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -18,14 +18,18 @@ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA */ -#ifndef GPG_KEYDB_H -#define GPG_KEYDB_H +#ifndef G10_KEYDB_H +#define G10_KEYDB_H #include "types.h" -#include "basicdefs.h" +#include "global.h" #include "packet.h" +#include "cipher.h" -#define MAX_FINGERPRINT_LEN 20 +/* What qualifies as a certification (rather than a signature?) */ +#define IS_SIG(s) (((s)->sig_class==0x00) || ((s)->sig_class==0x01) || \ + ((s)->sig_class==0x02) || ((s)->sig_class==0x40)) +#define IS_CERT(s) (!IS_SIG(s)) #define IS_KEY_SIG(s) ((s)->sig_class == 0x1f) #define IS_UID_SIG(s) (((s)->sig_class & ~3) == 0x10) @@ -35,7 +39,6 @@ #define IS_SUBKEY_REV(s) ((s)->sig_class == 0x28) - struct getkey_ctx_s; typedef struct getkey_ctx_s *GETKEY_CTX; @@ -47,7 +50,6 @@ typedef struct getkey_ctx_s *GETKEY_CTX; * This structure is also used to bind arbitrary packets together. */ -typedef struct kbnode_struct *KBNODE; struct kbnode_struct { KBNODE next; PACKET *pkt; @@ -62,19 +64,28 @@ struct kbnode_struct { enum resource_type { rt_UNKNOWN = 0, - rt_RING = 1, - rt_KBXF = 2 + rt_RING = 1 }; /**************** - * A data structure to hold information about the external position + * A data structre to hold information about the external position * of a keyblock. */ -struct keyblock_pos_struct; -typedef struct keyblock_pos_struct *KBPOS; +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; @@ -82,18 +93,82 @@ struct pk_list { }; /* 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; }; +/* 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*); + void *skipfncvalue; + union { + const char *name; + char fpr[MAX_FINGERPRINT_LEN]; + u32 kid[2]; + } u; +}; + +/*-- keydb.c --*/ +int keydb_add_resource (const char *url, int force, 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 (void); +int keydb_search_reset (KEYDB_HANDLE hd); +int keydb_search (KEYDB_HANDLE hd, KEYDB_SEARCH_DESC *desc, size_t ndesc); +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 remusr, PK_LIST *ret_pk_list, unsigned use ); -int select_algo_from_prefs( PK_LIST pk_list, int preftype ); +int build_pk_list( STRLIST rcpts, PK_LIST *ret_pk_list, unsigned use ); +int select_algo_from_prefs( PK_LIST pk_list, int preftype, + int request, void *hint ); +int select_mdc_from_pklist (PK_LIST pk_list); /*-- skclist.c --*/ void release_sk_list( SK_LIST sk_list ); @@ -105,18 +180,19 @@ int have_static_passphrase(void); void read_passphrase_from_fd( int fd ); void passphrase_clear_cache ( u32 *keyid, int algo ); DEK *passphrase_to_dek( u32 *keyid, int pubkey_algo, - int cipher_algo, STRING2KEY *s2k, int mode); + int cipher_algo, STRING2KEY *s2k, int mode, + const char *tryagain_text); void set_next_passphrase( const char *s ); char *get_last_passphrase(void); /*-- getkey.c --*/ -int classify_user_id( const char *name, u32 *keyid, byte *fprint, - const char **retstr, size_t *retlen ); +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 ); KBNODE get_pubkeyblock( u32 *keyid ); -int get_pubkey_byname( GETKEY_CTX *rx, PKT_public_key *pk, - const char *name, KBNODE *ret_keyblock ); +int get_pubkey_byname( PKT_public_key *pk, const char *name, + KBNODE *ret_keyblock, KEYDB_HANDLE *ret_kdbhd); 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 ); @@ -129,25 +205,21 @@ 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( GETKEY_CTX *rx, - PKT_secret_key *sk, const char *name, int unlock, - KBNODE *retblock ); +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_byfprint( PKT_secret_key *sk, + const byte *fprint, size_t fprint_len); int get_seckey_next( GETKEY_CTX ctx, PKT_secret_key *sk, KBNODE *ret_keyblock ); void get_seckey_end( GETKEY_CTX ctx ); -int find_keyblock_byname( KBNODE *retblock, const char *username ); -int find_secret_keyblock_byname( KBNODE *retblock, const char *username ); -int find_keyblock_bypk( KBNODE *retblock, PKT_public_key *pk ); -int find_keyblock_bysk( KBNODE *retblock, PKT_secret_key *sk ); - int enum_secret_keys( void **context, PKT_secret_key *sk, int with_subkeys ); void merge_keys_and_selfsig( KBNODE keyblock ); -void merge_public_with_secret ( KBNODE pubblock, KBNODE secblock ); 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); /*-- keyid.c --*/ int pubkey_letter( int algo ); @@ -162,12 +234,16 @@ 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 *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 ); -char *unified_fingerprint_from_pk( PKT_public_key *pk, - char *buffer, size_t bufsize ); -char *unified_fingerprint_from_sk( PKT_secret_key *sk, - char *buffer, size_t bufsize ); /*-- kbnode.c --*/ KBNODE new_kbnode( PACKET *pkt ); @@ -186,18 +262,4 @@ void clear_kbnode_flags( KBNODE n ); int commit_kbnode( KBNODE *root ); void dump_kbnode( KBNODE node ); -/*-- ringedit.c --*/ -const char *enum_keyblock_resources( int *sequence, int secret ); -int add_keyblock_resource( const char *resname, int force, int secret ); -const char *keyblock_resource_name( KBPOS kbpos ); -int get_keyblock_handle( const char *filename, int secret, KBPOS kbpos ); -char *get_writable_keyblock_file( int secret ); -int enum_keyblocks_begin( KBPOS *kbpos, int mode ); -int enum_keyblocks_next( KBPOS kbpos, int mode, KBNODE *ret_root ); -void enum_keyblocks_end( KBPOS kbpos ); -int insert_keyblock( KBNODE keyblock ); -int delete_keyblock( KBNODE keyblock ); -int update_keyblock( KBNODE keyblock ); - - -#endif /*GPG_KEYDB_H*/ +#endif /*G10_KEYDB_H*/ diff --git a/g10/keyedit.c b/g10/keyedit.c index 095e43baf..db811ac0c 100644 --- a/g10/keyedit.c +++ b/g10/keyedit.c @@ -1,5 +1,5 @@ /* keyedit.c - keyedit stuff - * Copyright (C) 1998, 1999, 2000 Free Software Foundation, Inc. + * Copyright (C) 1998, 1999, 2000, 2001, 2002 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -31,7 +31,8 @@ #include "errors.h" #include "iobuf.h" #include "keydb.h" -#include <gcrypt.h> +#include "memory.h" +#include "photoid.h" #include "util.h" #include "main.h" #include "trustdb.h" @@ -40,26 +41,32 @@ #include "status.h" #include "i18n.h" -static void show_prefs( KBNODE keyblock, PKT_user_id *uid ); -static void show_key_with_all_names( KBNODE keyblock, - int only_marked, int with_fpr, int with_subkeys, int with_prefs ); +static void show_prefs( PKT_user_id *uid, int verbose ); +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 void show_fingerprint( PKT_public_key *pk ); -static int menu_adduid( KBNODE keyblock, KBNODE sec_keyblock ); +static int menu_adduid( KBNODE keyblock, KBNODE sec_keyblock, int photo ); static void menu_deluid( KBNODE pub_keyblock, KBNODE sec_keyblock ); static int menu_delsig( KBNODE pub_keyblock ); static void menu_delkey( KBNODE pub_keyblock, KBNODE sec_keyblock ); +static int menu_addrevoker( KBNODE pub_keyblock, KBNODE sec_keyblock ); static int menu_expire( 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_select_uid( KBNODE keyblock, int idx ); 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_revkey( 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) @@ -68,41 +75,19 @@ static int enable_disable_key( KBNODE keyblock, int disable ); #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; + int non_exportable,non_revocable; struct revocation_reason_info *reason; }; - - - -static int -get_keyblock_byname( KBNODE *keyblock, KBPOS *kbpos, const char *username ) -{ - int rc; - - *keyblock = NULL; - /* search the userid */ - rc = find_keyblock_byname( keyblock, username ); - if( rc ) { - log_error(_("%s: user not found: %s\n"), username, gpg_errstr(rc) ); - return rc; - } - - merge_keys_and_selfsig( *keyblock ); - - return rc; -} - - /**************** - * Print information about a signature, chek it and return true + * Print information about a signature, check it and return true * if the signature is okay. NODE must be a signature packet. */ static int @@ -119,13 +104,14 @@ print_and_check_one_sig( KBNODE keyblock, KBNODE node, node->flag &= ~(NODFLG_BADSIG|NODFLG_NOKEY|NODFLG_SIGERR); sigrc = '!'; break; - case GPGERR_BAD_SIGN: + case G10ERR_BAD_SIGN: node->flag = NODFLG_BADSIG; sigrc = '-'; if( inv_sigs ) ++*inv_sigs; break; - case GPGERR_NO_PUBKEY: + case G10ERR_NO_PUBKEY: + case G10ERR_UNU_PUBKEY: node->flag = NODFLG_NOKEY; sigrc = '?'; if( no_key ) @@ -139,11 +125,18 @@ print_and_check_one_sig( KBNODE keyblock, KBNODE node, break; } if( sigrc != '?' || print_without_key ) { - tty_printf("%s%c %08lX %s ", - is_rev? "rev":"sig", - sigrc, sig->keyid[1], datestr_from_sig(sig)); + tty_printf("%s%c%c %c%c%c%c%c %08lX %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':' ', + (ulong)sig->keyid[1], datestr_from_sig(sig)); if( sigrc == '%' ) - tty_printf("[%s] ", gpg_errstr(rc) ); + tty_printf("[%s] ", g10_errstr(rc) ); else if( sigrc == '?' ) ; else if( *is_selfsig ) { @@ -154,10 +147,17 @@ print_and_check_one_sig( KBNODE keyblock, KBNODE node, size_t n; char *p = get_user_id( sig->keyid, &n ); tty_print_utf8_string2( p, n, 40 ); - gcry_free(p); + m_free(p); } tty_printf("\n"); + + if(sig->flags.policy_url && opt.show_policy_url) + show_policy_url(sig,3); + + if(sig->flags.notation && opt.show_notation) + show_notation(sig,3); } + return (sigrc == '!'); } @@ -212,7 +212,7 @@ check_all_keysigs( KBNODE keyblock, int only_selected ) if( !has_selfsig ) mis_selfsig++; if( inv_sigs == 1 ) - tty_printf(_("1 bad signature\n"), inv_sigs ); + tty_printf(_("1 bad signature\n") ); else if( inv_sigs ) tty_printf(_("%d bad signatures\n"), inv_sigs ); if( no_key == 1 ) @@ -245,6 +245,12 @@ sign_mk_attrib( PKT_signature *sig, void *opaque ) 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 ); @@ -259,7 +265,8 @@ sign_mk_attrib( PKT_signature *sig, void *opaque ) * if some user_ids are marked those will be signed. */ static int -sign_uids( KBNODE keyblock, STRLIST locusr, int *ret_modified, int local ) +sign_uids( KBNODE keyblock, STRLIST locusr, int *ret_modified, + int local , int nonrevocable ) { int rc = 0; SK_LIST sk_list = NULL; @@ -268,18 +275,40 @@ sign_uids( KBNODE keyblock, STRLIST locusr, int *ret_modified, int local ) KBNODE node, uidnode; PKT_public_key *primary_pk=NULL; int select_all = !count_selected_uids(keyblock); - int upd_trust = 0; - - /* build a list of all signators */ - rc=build_sk_list( locusr, &sk_list, 0, 1 ); + int all_v3=1; + + /* Are there any non-v3 sigs on this key already? */ + if(opt.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_SIG|PUBKEY_USAGE_CERT); if( rc ) goto leave; - /* loop over all signaturs */ + /* loop over all signators */ for( sk_rover = sk_list; sk_rover; sk_rover = sk_rover->next ) { - u32 sk_keyid[2]; + u32 sk_keyid[2],pk_keyid[2]; size_t n; char *p; + int force_v4=0,class=0,selfsig=0; + u32 duration=0,timestamp=0; + + if(local || nonrevocable || + opt.cert_policy_url || opt.cert_notation_data) + 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 @@ -299,18 +328,129 @@ sign_uids( KBNODE keyblock, STRLIST locusr, int *ret_modified, int local ) /* reset mark for uids which are already signed */ uidnode = NULL; for( node=keyblock; node; node = node->next ) { - if( node->pkt->pkttype == PKT_USER_ID ) { + 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) + { + 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(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; + } + else + { + uidnode->flag &= ~NODFLG_MARK_A; + tty_printf(_(" Unable to sign.\n")); + } + } + else if(!uidnode->pkt->pkt.user_id->created) + { + tty_printf(_("Warning: user ID \"%s\" is not " + "self-signed.\n"),user); + } + + m_free(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; + 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; + continue; + } + } + /* Fixme: see whether there is a revocation in which * case we should allow to sign it again. */ - tty_printf(_("Already signed by key %08lX\n"), - (ulong)sk_keyid[1] ); + if (!node->pkt->pkt.signature->flags.exportable && local) + tty_printf(_( + "\"%s\" was already locally signed by key %08lX\n"), + user,(ulong)sk_keyid[1] ); + else + tty_printf(_( + "\"%s\" was already signed by key %08lX\n"), + user,(ulong)sk_keyid[1] ); + 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 */ + + m_free(user); } } } @@ -322,25 +462,186 @@ sign_uids( KBNODE keyblock, STRLIST locusr, int *ret_modified, int local ) } /* Ask whether we really should sign these user id(s) */ tty_printf("\n"); - show_key_with_all_names( keyblock, 1, 1, 0, 0 ); + show_key_with_all_names( keyblock, 1, 0, 1, 0, 0 ); tty_printf("\n"); - tty_printf(_( - "Are you really sure that you want to sign this key\n" - "with your key: \"")); + + 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 + { + char *answer; + + tty_printf(_("This key is due to expire on %s.\n"), + expirestr_from_pk(primary_pk)); + + 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(); + m_free(answer); + } + } + + /* Only ask for duration if we haven't already set it to match + the expiration of the pk */ + if(opt.ask_cert_expire && !duration && !selfsig) + duration=ask_expire_interval(1); + + 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(opt.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) + class=0x10+opt.def_cert_check_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_check_level==0?" (default)":""); + tty_printf(_(" (1) I have not checked at all.%s\n"), + opt.def_cert_check_level==1?" (default)":""); + tty_printf(_(" (2) I have done casual checking.%s\n"), + opt.def_cert_check_level==2?" (default)":""); + tty_printf(_(" (3) I have done very careful checking.%s\n"), + opt.def_cert_check_level==3?" (default)":""); + tty_printf("\n"); + + while(class==0) + { + answer = cpr_get("sign_uid.class",_("Your selection? ")); + + if(answer[0]=='\0') + class=0x10+opt.def_cert_check_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")); + + m_free(answer); + } + } + + tty_printf(_("Are you really sure that you want to sign this key\n" + "with your key: \"")); p = get_user_id( sk_keyid, &n ); tty_print_utf8_string( p, n ); - gcry_free(p); p = NULL; - tty_printf("\"\n\n"); + m_free(p); p = NULL; + tty_printf("\"\n"); + + if(selfsig) + { + tty_printf(_("\nThis will be a self-signature.\n")); + + if( local ) + tty_printf( + _("\nWarning: the signature will not be marked " + "as non-exportable.\n")); + + if( nonrevocable ) + tty_printf( + _("\nWarning: the signature will not be marked " + "as non-revocable.\n")); + } + else + { + if( local ) + tty_printf( + _("\nThe signature will be marked as non-exportable.\n")); + + if( nonrevocable ) + tty_printf( + _("\nThe signature will be marked as non-revocable.\n")); + + switch(class) + { + case 0x11: + tty_printf(_("\nI have not checked this key at all.\n")); + break; - if( local ) - tty_printf( - _("The signature will be marked as non-exportable.\n\n")); + case 0x12: + tty_printf(_("\nI have checked this key casually.\n")); + break; + case 0x13: + tty_printf(_("\nI 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? ")) ) continue; + /* now we can sign the user ids */ reloop: /* (must use this, because we are modifing the list) */ primary_pk = NULL; @@ -356,33 +657,50 @@ sign_uids( KBNODE keyblock, STRLIST locusr, int *ret_modified, int local ) assert( primary_pk ); memset( &attrib, 0, sizeof attrib ); attrib.non_exportable = local; + attrib.non_revocable = nonrevocable; node->flag &= ~NODFLG_MARK_A; - rc = make_keysig_packet( &sig, primary_pk, - node->pkt->pkt.user_id, - NULL, - sk, - 0x10, 0, - sign_mk_attrib, - &attrib ); + + /* 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"), gpg_errstr(rc)); + log_error(_("signing failed: %s\n"), g10_errstr(rc)); goto leave; } + *ret_modified = 1; /* we changed the keyblock */ - upd_trust = 1; + update_trust = 1; - pkt = gcry_xcalloc( 1, sizeof *pkt ); + pkt = m_alloc_clear( sizeof *pkt ); pkt->pkttype = PKT_SIGNATURE; pkt->pkt.signature = sig; insert_kbnode( node, new_kbnode(pkt), PKT_SIGNATURE ); goto reloop; } } - } /* end loop over signators */ - if( upd_trust && primary_pk ) { - rc = clear_trust_checked_flag( primary_pk ); - } + /* 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 ); @@ -416,7 +734,7 @@ change_passphrase( KBNODE keyblock ) switch( is_secret_key_protected( sk ) ) { case -1: - rc = GPGERR_PUBKEY_ALGO; + rc = G10ERR_PUBKEY_ALGO; break; case 0: tty_printf(_("This key is not protected.\n")); @@ -447,10 +765,11 @@ change_passphrase( KBNODE keyblock ) } if( rc ) - tty_printf(_("Can't edit this key: %s\n"), gpg_errstr(rc)); + tty_printf(_("Can't edit this key: %s\n"), g10_errstr(rc)); else { DEK *dek = NULL; - STRING2KEY *s2k = gcry_xmalloc_secure( sizeof *s2k ); + STRING2KEY *s2k = m_alloc_secure( sizeof *s2k ); + const char *errtext = NULL; tty_printf(_("Enter the new passphrase for this secret key.\n\n") ); @@ -458,9 +777,11 @@ change_passphrase( KBNODE keyblock ) for(;;) { s2k->mode = opt.s2k_mode; s2k->hash_algo = opt.s2k_digest_algo; - dek = passphrase_to_dek( NULL, 0, opt.s2k_cipher_algo, s2k, 2 ); + dek = passphrase_to_dek( NULL, 0, opt.s2k_cipher_algo, + s2k, 2, errtext); if( !dek ) { - tty_printf(_("passphrase not correctly repeated; try again.\n")); + errtext = _("passphrase not correctly repeated; try again"); + tty_printf ("%s.\n", errtext); } else if( !dek->keylen ) { rc = 0; @@ -487,18 +808,18 @@ change_passphrase( KBNODE keyblock ) } } if( rc ) - log_error("protect_secret_key failed: %s\n", gpg_errstr(rc) ); + log_error("protect_secret_key failed: %s\n", g10_errstr(rc) ); else changed++; break; } } - gcry_free(s2k); - gcry_free(dek); + m_free(s2k); + m_free(dek); } leave: - gcry_free( passphrase ); + m_free( passphrase ); set_next_passphrase( NULL ); return changed && !rc; } @@ -560,11 +881,12 @@ keyedit_menu( const char *username, STRLIST locusr, STRLIST commands, { enum cmdids { cmdNONE = 0, cmdQUIT, cmdHELP, cmdFPR, cmdLIST, cmdSELUID, cmdCHECK, cmdSIGN, - cmdLSIGN, cmdREVSIG, cmdREVKEY, cmdDELSIG, - cmdDEBUG, cmdSAVE, cmdADDUID, cmdDELUID, cmdADDKEY, cmdDELKEY, - cmdTOGGLE, cmdSELKEY, cmdPASSWD, cmdTRUST, cmdPREF, cmdEXPIRE, - cmdENABLEKEY, cmdDISABLEKEY, - cmdINVCMD, cmdNOP }; + cmdLSIGN, cmdNRSIGN, cmdNRLSIGN, cmdREVSIG, cmdREVKEY, cmdDELSIG, + cmdPRIMARY, cmdDEBUG, cmdSAVE, cmdADDUID, cmdADDPHOTO, cmdDELUID, + cmdADDKEY, cmdDELKEY, cmdADDREVOKER, cmdTOGGLE, cmdSELKEY, + cmdPASSWD, cmdTRUST, cmdPREF, cmdEXPIRE, cmdENABLEKEY, + cmdDISABLEKEY, cmdSHOWPREF, cmdSETPREF, cmdUPDPREF, cmdINVCMD, + cmdSHOWPHOTO, cmdUPDTRUST, cmdCHKTRUST, cmdNOP }; static struct { const char *name; enum cmdids id; int need_sk; @@ -587,30 +909,42 @@ keyedit_menu( const char *username, STRLIST locusr, STRLIST commands, { N_("sign") , cmdSIGN , 0,1,1, N_("sign the key") }, { N_("s") , cmdSIGN , 0,1,1, NULL }, { N_("lsign") , cmdLSIGN , 0,1,1, N_("sign the key locally") }, + { N_("nrsign") , cmdNRSIGN , 0,1,1, N_("sign the key non-revocably") }, + { N_("nrlsign") , cmdNRLSIGN , 0,1,1, N_("sign the key locally and non-revocably") }, { N_("debug") , cmdDEBUG , 0,0,0, NULL }, { N_("adduid") , cmdADDUID , 1,1,0, N_("add a user ID") }, + { N_("addphoto"), cmdADDPHOTO , 1,1,0, N_("add a photo ID") }, { N_("deluid") , cmdDELUID , 0,1,0, N_("delete user ID") }, + /* delphoto is really deluid in disguise */ + { N_("delphoto"), cmdDELUID , 0,1,0, NULL }, { N_("addkey") , cmdADDKEY , 1,1,0, N_("add a secondary key") }, { N_("delkey") , cmdDELKEY , 0,1,0, N_("delete a secondary key") }, + { N_("addrevoker"),cmdADDREVOKER,1,1,0, N_("add a revocation key") }, { N_("delsig") , cmdDELSIG , 0,1,0, N_("delete signatures") }, { N_("expire") , cmdEXPIRE , 1,1,0, N_("change the expire date") }, + { N_("primary") , cmdPRIMARY , 1,1,0, N_("flag user ID as primary")}, { N_("toggle") , cmdTOGGLE , 1,0,0, N_("toggle between secret " "and public key listing") }, { N_("t" ) , cmdTOGGLE , 1,0,0, NULL }, - { N_("pref") , cmdPREF , 0,1,0, N_("list preferences") }, + { N_("pref") , cmdPREF , 0,1,0, N_("list preferences (expert)") }, + { N_("showpref"), cmdSHOWPREF , 0,1,0, N_("list preferences (verbose)") }, + { N_("setpref") , cmdSETPREF , 1,1,0, N_("set preference list") }, + { N_("updpref") , cmdUPDPREF , 1,1,0, N_("updated preferences") }, { N_("passwd") , cmdPASSWD , 1,1,0, N_("change the passphrase") }, { N_("trust") , cmdTRUST , 0,1,0, N_("change the ownertrust") }, { N_("revsig") , cmdREVSIG , 0,1,0, N_("revoke signatures") }, { N_("revkey") , cmdREVKEY , 1,1,0, N_("revoke a secondary key") }, { N_("disable") , cmdDISABLEKEY, 0,1,0, N_("disable a key") }, { N_("enable") , cmdENABLEKEY , 0,1,0, N_("enable a key") }, + { N_("showphoto"),cmdSHOWPHOTO , 0,0,0, N_("show photo ID") }, { NULL, cmdNONE } }; enum cmdids cmd = 0; int rc = 0; KBNODE keyblock = NULL; - KBPOS keyblockpos; + KEYDB_HANDLE kdbhd = NULL; KBNODE sec_keyblock = NULL; + KEYDB_HANDLE sec_kdbhd = NULL; KBNODE cur_keyblock; char *answer = NULL; int redisplay = 1; @@ -619,34 +953,23 @@ keyedit_menu( const char *username, STRLIST locusr, STRLIST commands, int toggle; int have_commands = !!commands; - - if( opt.batch && !have_commands ) { + if ( opt.command_fd != -1 ) + ; + else if( opt.batch && !have_commands ) { log_error(_("can't do that in batchmode\n")); goto leave; } if( sign_mode ) { commands = NULL; - append_to_strlist( &commands, sign_mode == 1? "sign":"lsign" ); + append_to_strlist( &commands, sign_mode == 1? "sign": + sign_mode == 2?"lsign": + sign_mode == 3?"nrsign":"nrlsign"); have_commands = 1; } - - if( !sign_mode ) { - /* first try to locate it as secret key */ - rc = find_secret_keyblock_byname( &sec_keyblock, username ); - if( rc && rc != GPGERR_NO_SECKEY ) - log_debug("%s: secret keyblock read problem: %s\n", - username, gpg_errstr(rc)); - if( !rc ) { - merge_keys_and_selfsig( sec_keyblock ); - if( fix_keyblock( sec_keyblock ) ) - sec_modified++; - } - } - - /* and now get the public key */ - rc = get_keyblock_byname( &keyblock, &keyblockpos, username ); + /* get the public key */ + rc = get_pubkey_byname (NULL, username, &keyblock, &kdbhd); if( rc ) goto leave; if( fix_keyblock( keyblock ) ) @@ -654,44 +977,79 @@ keyedit_menu( const char *username, STRLIST locusr, STRLIST commands, if( collapse_uids( &keyblock ) ) modified++; - if( sec_keyblock ) { /* check that they match */ - /* fixme: check that they both match */ + if( !sign_mode ) {/* 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 ) { tty_printf(_("Secret key is available.\n")); } toggle = 0; cur_keyblock = keyblock; for(;;) { /* main loop */ - int i, arg_number; + 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 ) { - show_key_with_all_names( cur_keyblock, 0, 0, 1, 0 ); + show_key_with_all_names( cur_keyblock, 0, 1, 0, 1, 0 ); tty_printf("\n"); redisplay = 0; } do { - gcry_free(answer); + m_free(answer); if( have_commands ) { if( commands ) { - answer = gcry_xstrdup( commands->d ); + answer = m_strdup( commands->d ); commands = commands->next; } else if( opt.batch ) { - answer = gcry_xstrdup("quit"); + answer = m_strdup("quit"); } else have_commands = 0; } if( !have_commands ) { - answer = cpr_get("keyedit.prompt", _("Command> ")); + answer = cpr_get_no_help("keyedit.prompt", _("Command> ")); cpr_kill_prompt(); } trim_spaces(answer); } while( *answer == '#' ); - arg_number = 0; /* Yes, here is the init which egcc complains about*/ + 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 ) @@ -706,10 +1064,11 @@ keyedit_menu( const char *username, STRLIST locusr, STRLIST commands, trim_spaces(answer); trim_spaces(p); arg_number = atoi(p); + arg_string = p; } for(i=0; cmds[i].name; i++ ) { - if( !stricmp( answer, cmds[i].name ) ) + if( !ascii_strcasecmp( answer, cmds[i].name ) ) break; } if( sign_mode && !cmds[i].signmode ) @@ -764,6 +1123,27 @@ keyedit_menu( const char *username, STRLIST locusr, STRLIST commands, case cmdSIGN: /* sign (only the public key) */ case cmdLSIGN: /* sign (only the public key) */ + case cmdNRSIGN: /* sign (only the public key) */ + case cmdNRLSIGN: /* sign (only the public key) */ + 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) ) { if( !cpr_get_answer_is_yes("keyedit.sign_all.okay", _("Really sign all user IDs? ")) ) { @@ -771,14 +1151,11 @@ keyedit_menu( const char *username, STRLIST locusr, STRLIST commands, break; } } - if( !sign_uids( keyblock, locusr, &modified, cmd == cmdLSIGN ) + if( !sign_uids( keyblock, locusr, &modified, + (cmd == cmdLSIGN) || (cmd == cmdNRLSIGN), + (cmd == cmdNRSIGN) || (cmd==cmdNRLSIGN)) && sign_mode ) - goto do_cmd_save; - /* Actually we should do a update_trust_record() here so that - * the trust gets displayed correctly. however this is not possible - * because we would have to save the keyblock first - something - * we don't want to do without an explicit save command. - */ + goto do_cmd_save; break; case cmdDEBUG: @@ -791,18 +1168,23 @@ keyedit_menu( const char *username, STRLIST locusr, STRLIST commands, redisplay = 1; break; + case cmdADDPHOTO: + if (opt.rfc2440 || opt.rfc1991 || opt.pgp2) + { + tty_printf( + _("This command is not allowed while in %s mode.\n"), + opt.rfc2440?"OpenPGP":opt.pgp2?"PGP2":"RFC-1991"); + break; + } + photo=1; + /* fall through */ + case cmdADDUID: - if( menu_adduid( keyblock, sec_keyblock ) ) { + if( menu_adduid( keyblock, sec_keyblock, photo ) ) { redisplay = 1; sec_modified = modified = 1; - /* must update the trustdb already here, so that preferences - * get listed correctly */ - rc = update_trust_record( keyblock, 0, NULL ); - if( rc ) { - log_error(_("update of trustdb failed: %s\n"), - gpg_errstr(rc) ); - rc = 0; - } + merge_keys_and_selfsig( sec_keyblock ); + merge_keys_and_selfsig( keyblock ); } break; @@ -811,7 +1193,7 @@ keyedit_menu( const char *username, STRLIST locusr, STRLIST commands, if( !(n1=count_selected_uids(keyblock)) ) tty_printf(_("You must select at least one user ID.\n")); - else if( count_uids(keyblock) - n1 < 1 ) + 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", @@ -844,6 +1226,8 @@ keyedit_menu( const char *username, STRLIST locusr, STRLIST commands, if( generate_subkeypair( keyblock, sec_keyblock ) ) { redisplay = 1; sec_modified = modified = 1; + merge_keys_and_selfsig( sec_keyblock ); + merge_keys_and_selfsig( keyblock ); } break; @@ -870,6 +1254,15 @@ keyedit_menu( const char *username, STRLIST locusr, STRLIST commands, } break; + case cmdADDREVOKER: + if( menu_addrevoker( keyblock, sec_keyblock ) ) { + redisplay = 1; + sec_modified = modified = 1; + merge_keys_and_selfsig( sec_keyblock ); + merge_keys_and_selfsig( keyblock ); + } + break; + case cmdREVKEY: { int n1; @@ -902,23 +1295,62 @@ keyedit_menu( const char *username, STRLIST locusr, STRLIST commands, } 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: - show_key_with_all_names( keyblock, 0, 0, 1, 0 ); + 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->local_id, 1 ) ) + PKT_PUBLIC_KEY )->pkt->pkt.public_key, 1 ) ) { redisplay = 1; - /* we don't need to set modified here, as the trustvalues - * are updated immediately */ + /* No real need to set update_trust here as + edit_ownertrust() calls revalidation_mark() + anyway. */ + update_trust=1; + } break; case cmdPREF: - show_key_with_all_names( keyblock, 0, 0, 0, 1 ); + show_key_with_all_names( keyblock, 0, 0, 0, 0, 1 ); + break; + + case cmdSHOWPREF: + show_key_with_all_names( keyblock, 0, 0, 0, 0, 2 ); + break; + + case cmdSETPREF: + keygen_set_std_prefs ( !*arg_string? "default" : arg_string, 0); + break; + + case cmdUPDPREF: + { + p = keygen_get_std_prefs (); + tty_printf (("Current preference list: %s\n"), p); + m_free (p); + } + if (cpr_get_answer_is_yes ("keyedit.updpref.okay", + count_selected_uids (keyblock)? + _("Really update the preferences" + " for the selected user IDs? "): + _("Really update the preferences? "))){ + + if ( menu_set_preferences (keyblock, sec_keyblock) ) { + merge_keys_and_selfsig (keyblock); + modified = 1; + redisplay = 1; + } + } break; case cmdNOP: @@ -939,6 +1371,10 @@ keyedit_menu( const char *username, STRLIST locusr, STRLIST commands, } break; + case cmdSHOWPHOTO: + menu_showphoto(keyblock); + break; + case cmdQUIT: if( have_commands ) goto leave; @@ -957,32 +1393,29 @@ keyedit_menu( const char *username, STRLIST locusr, STRLIST commands, do_cmd_save: if( modified || sec_modified ) { if( modified ) { - rc = update_keyblock( keyblock ); + rc = keydb_update_keyblock (kdbhd, keyblock); if( rc ) { - log_error(_("update failed: %s\n"), gpg_errstr(rc) ); + log_error(_("update failed: %s\n"), g10_errstr(rc) ); break; } } if( sec_modified ) { - rc = update_keyblock( sec_keyblock ); + rc = keydb_update_keyblock (sec_kdbhd, sec_keyblock ); if( rc ) { - log_error(_("update secret failed: %s\n"), - gpg_errstr(rc) ); + log_error( _("update secret failed: %s\n"), + g10_errstr(rc) ); break; } } } else tty_printf(_("Key not changed so no update needed.\n")); - /* TODO: we should keep track whether we have changed - * something relevant to the trustdb */ - if( !modified && sign_mode ) - rc = 0; /* we can skip at least in this case */ - else - rc = update_trust_record( keyblock, 0, NULL ); - if( rc ) - log_error(_("update of trustdb failed: %s\n"), - gpg_errstr(rc) ); + + if( update_trust ) + { + revalidation_mark (); + update_trust=0; + } goto leave; case cmdINVCMD: @@ -996,7 +1429,8 @@ keyedit_menu( const char *username, STRLIST locusr, STRLIST commands, leave: release_kbnode( keyblock ); release_kbnode( sec_keyblock ); - gcry_free(answer); + keydb_release (kdbhd); + m_free(answer); } @@ -1004,45 +1438,246 @@ keyedit_menu( const char *username, STRLIST locusr, STRLIST commands, * show preferences of a public keyblock. */ static void -show_prefs( KBNODE keyblock, PKT_user_id *uid ) +show_prefs (PKT_user_id *uid, int verbose) { - KBNODE node = find_kbnode( keyblock, PKT_PUBLIC_KEY ); - PKT_public_key *pk; - byte *p; + const prefitem_t fake={0,0}; + const prefitem_t *prefs; int i; - size_t n; - byte namehash[20]; - - if( !node ) - return; /* is a secret keyblock */ - pk = node->pkt->pkt.public_key; - if( !pk->local_id ) { - log_error("oops: no LID\n"); - return; - } - if( uid->photo ) { - gcry_md_hash_buffer( GCRY_MD_RMD160, namehash, uid->photo, - uid->photolen ); + 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 (" 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 Hash: "); + 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 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 == 0 ) + uncomp_seen = 1; + } + } + if (!uncomp_seen) { + if (any) + tty_printf (", "); + else { + tty_printf ("%s",compress_algo_to_string(1)); + tty_printf (", "); + } + tty_printf ("%s",compress_algo_to_string(0)); + } + tty_printf ("\n Features: "); + if(uid->mdc_feature) + tty_printf ("MDC"); + tty_printf("\n"); } else { - gcry_md_hash_buffer( GCRY_MD_RMD160, namehash, uid->name, uid->len ); + 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->mdc_feature) + tty_printf (" [mdc]"); + tty_printf("\n"); } +} - p = get_pref_data( pk->local_id, namehash, &n ); - if( !p ) - return; - tty_printf(" "); - for(i=0; i < n; i+=2 ) { - if( p[i] ) - tty_printf( " %c%d", p[i] == PREFTYPE_SYM ? 'S' : - p[i] == PREFTYPE_HASH ? 'H' : - p[i] == PREFTYPE_COMPR ? 'Z' : '?', p[i+1]); +/* 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; + byte pk_version=0; + + /* 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; + int otrust=0, trust=0; + u32 keyid[2]; + + if (node->pkt->pkttype == PKT_PUBLIC_KEY) + { + trust = get_validity_info (pk, NULL); + otrust = get_ownertrust_info (pk); + pk_version = pk->version; + } + + 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 + 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 (pk->local_id) + printf ("%lu", pk->local_id); + putchar (':'); + putchar (otrust); + putchar(':'); + putchar('\n'); + + print_fingerprint (pk, NULL, 0); + + /* print the revoker record */ + if( !pk->revkey && pk->numrevkeys ) + BUG(); + else + { + 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%c:\n", pk->revkey[i].class, + (pk->revkey[i].class&0x40)? 'l':'x'); + } + } + } } - tty_printf("\n"); + + /* 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; + int trustletter = '?'; - gcry_free(p); + ++i; + if(uid->attrib_data) + { + printf ("uat:%c::::::::%u %lu", trustletter, + uid->numattribs,uid->attrib_len); + } + else + { + printf ("uid:%c::::::::", trustletter); + 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->mdc_feature) + printf (",mdc"); + } + 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'); + } + } } @@ -1051,11 +1686,19 @@ show_prefs( KBNODE keyblock, PKT_user_id *uid ) * 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, +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, rc; + int do_warn = 0; + byte pk_version=0; + + if (opt.with_colons) + { + show_key_with_all_names_colon (keyblock); + return; + } /* the keys */ for( node = keyblock; node; node = node->next ) { @@ -1067,10 +1710,43 @@ show_key_with_all_names( KBNODE keyblock, int only_marked, if( node->pkt->pkttype == PKT_PUBLIC_KEY ) { /* do it here, so that debug messages don't clutter the * output */ - trust = query_trust_info(pk, NULL); - otrust = get_ownertrust_info( pk->local_id ); + static int did_warn = 0; + + trust = get_validity_info (pk, NULL); + otrust = get_ownertrust_info (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; } + if(with_revoker) { + if( !pk->revkey && pk->numrevkeys ) + BUG(); + else + for(i=0;i<pk->numrevkeys;i++) { + u32 r_keyid[2]; + char *user; + + keyid_from_fingerprint(pk->revkey[i].fpr, + MAX_FINGERPRINT_LEN,r_keyid); + + user=get_user_id_string (r_keyid); + tty_printf (_("This key may be revoked by %s key "), + pubkey_algo_to_string (pk->revkey[i].algid)); + tty_print_utf8_string (user, strlen (user)); + if ((pk->revkey[i].class&0x40)) + tty_printf (_(" (sensitive)")); + tty_printf ("\n"); + m_free(user); + } + } + tty_printf(_("%s%c %4u%c/%08lX created: %s expires: %s"), node->pkt->pkttype == PKT_PUBLIC_KEY? "pub":"sub", (node->flag & NODFLG_SELKEY)? '*':' ', @@ -1082,14 +1758,14 @@ show_key_with_all_names( KBNODE keyblock, int only_marked, if( node->pkt->pkttype == PKT_PUBLIC_KEY ) { tty_printf(_(" trust: %c/%c"), otrust, trust ); if( node->pkt->pkttype == PKT_PUBLIC_KEY - && (get_ownertrust( pk->local_id )&TRUST_FLAG_DISABLED)) { + && (get_ownertrust (pk)&TRUST_FLAG_DISABLED)) { tty_printf("\n*** "); tty_printf(_("This key has been disabled")); } if( with_fpr ) { tty_printf("\n"); - show_fingerprint( pk ); + print_fingerprint ( pk, NULL, 2 ); } } tty_printf("\n"); @@ -1115,11 +1791,11 @@ show_key_with_all_names( KBNODE keyblock, int only_marked, if( !rc ) tty_printf( _("rev! subkey has been revoked: %s\n"), datestr_from_sig( sig ) ); - else if( rc == GPGERR_BAD_SIGN ) + else if( rc == G10ERR_BAD_SIGN ) tty_printf( _("rev- faked revocation found\n") ); else if( rc ) tty_printf( _("rev? problem checking revocation: %s\n"), - gpg_errstr(rc) ); + g10_errstr(rc) ); } } /* the user ids */ @@ -1133,15 +1809,33 @@ show_key_with_all_names( KBNODE keyblock, int only_marked, 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); + if ( uid->is_revoked ) + tty_printf ("[revoked] "); + if ( uid->is_expired ) + tty_printf ("[expired] "); tty_print_utf8_string( uid->name, uid->len ); tty_printf("\n"); if( with_prefs ) - show_prefs( keyblock, uid ); + { + if(pk_version>3 || uid->selfsigversion>3) + show_prefs (uid, with_prefs == 2); + else + tty_printf(_("There are no preferences on a " + "PGP 2.x-style key.\n")); + } } } } + + if (do_warn) + tty_printf (_("Please note that the shown key validity " + "is not necessarily correct\n" + "unless you restart the program.\n")); + } static void @@ -1167,44 +1861,18 @@ show_key_and_fingerprint( KBNODE keyblock ) } tty_printf("\n"); if( pk ) - show_fingerprint( pk ); + print_fingerprint( pk, NULL, 2 ); } -static void -show_fingerprint( PKT_public_key *pk ) -{ - byte array[MAX_FINGERPRINT_LEN], *p; - size_t i, n; - - fingerprint_from_pk( pk, array, &n ); - p = array; - tty_printf(_(" Fingerprint:")); - if( n == 20 ) { - for(i=0; i < n ; i++, i++, p += 2 ) { - if( i == 10 ) - tty_printf(" "); - tty_printf(" %02X%02X", *p, p[1] ); - } - } - else { - for(i=0; i < n ; i++, p++ ) { - if( i && !(i%8) ) - tty_printf(" "); - tty_printf(" %02X", *p ); - } - } - tty_printf("\n"); -} - /**************** - * Ask for a new user id , do the selfsignature and put it into + * 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 ) +menu_adduid( KBNODE pub_keyblock, KBNODE sec_keyblock, int photo) { PKT_user_id *uid; PKT_public_key *pk=NULL; @@ -1215,10 +1883,6 @@ menu_adduid( KBNODE pub_keyblock, KBNODE sec_keyblock ) KBNODE pub_where=NULL, sec_where=NULL; int rc; - uid = generate_user_id(); - if( !uid ) - return 0; - for( node = pub_keyblock; node; pub_where = node, node = node->next ) { if( node->pkt->pkttype == PKT_PUBLIC_KEY ) pk = node->pkt->pkt.public_key; @@ -1235,27 +1899,69 @@ menu_adduid( KBNODE pub_keyblock, KBNODE sec_keyblock ) } if( !node ) /* no subkey */ sec_where = NULL; - assert(pk && sk ); + 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. 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; + } + } - rc = make_keysig_packet( &sig, pk, uid, NULL, sk, 0x13, 0, + uid = generate_photo_id(pk); + } 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", gpg_errstr(rc) ); + log_error("signing failed: %s\n", g10_errstr(rc) ); free_user_id(uid); return 0; } /* insert/append to secret keyblock */ - pkt = gcry_xcalloc( 1, sizeof *pkt ); + pkt = m_alloc_clear( sizeof *pkt ); pkt->pkttype = PKT_USER_ID; - pkt->pkt.user_id = copy_user_id(NULL, uid); + 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 = gcry_xcalloc( 1, sizeof *pkt ); + pkt = m_alloc_clear( sizeof *pkt ); pkt->pkttype = PKT_SIGNATURE; pkt->pkt.signature = copy_signature(NULL, sig); if( sec_where ) @@ -1263,7 +1969,7 @@ menu_adduid( KBNODE pub_keyblock, KBNODE sec_keyblock ) else add_kbnode( sec_keyblock, new_kbnode(pkt) ); /* insert/append to public keyblock */ - pkt = gcry_xcalloc( 1, sizeof *pkt ); + pkt = m_alloc_clear( sizeof *pkt ); pkt->pkttype = PKT_USER_ID; pkt->pkt.user_id = uid; node = new_kbnode(pkt); @@ -1271,7 +1977,7 @@ menu_adduid( KBNODE pub_keyblock, KBNODE sec_keyblock ) insert_kbnode( pub_where, node, 0 ); else add_kbnode( pub_keyblock, node ); - pkt = gcry_xcalloc( 1, sizeof *pkt ); + pkt = m_alloc_clear( sizeof *pkt ); pkt->pkttype = PKT_SIGNATURE; pkt->pkt.signature = copy_signature(NULL, sig); if( pub_where ) @@ -1295,6 +2001,10 @@ menu_deluid( KBNODE pub_keyblock, KBNODE sec_keyblock ) 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; @@ -1353,10 +2063,16 @@ menu_delsig( KBNODE pub_keyblock ) &inv_sig, &no_key, &other_err, &selfsig, 1 ); - if( valid ) + 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", @@ -1440,9 +2156,147 @@ menu_delkey( KBNODE pub_keyblock, KBNODE sec_keyblock ) commit_kbnode( &pub_keyblock ); if( sec_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 */ } +/**************** + * 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 ) +{ + 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; + sk=copy_secret_key(NULL,sec_keyblock->pkt->pkt.secret_key); + + for(;;) + { + char *answer; + u32 keyid[2]; + char *p; + size_t n; + + if(revoker_pk) + free_public_key(revoker_pk); + + revoker_pk=m_alloc_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') + goto fail; + + rc=get_pubkey_byname(revoker_pk,answer,NULL,NULL); + + if(rc) + { + log_error (_("key `%s' not found: %s\n"),answer,g10_errstr(rc)); + continue; + } + + 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; + } + + 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(revoker_pk,keyid); + + tty_printf("\npub %4u%c/%08lX %s ", + nbits_from_pk( revoker_pk ), + pubkey_letter( revoker_pk->pubkey_algo ), + (ulong)keyid[1], datestr_from_pk(pk) ); + + p = get_user_id( keyid, &n ); + tty_print_utf8_string( p, n ); + m_free(p); + tty_printf("\n"); + 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; + + /* todo: handle 0x40 sensitive flag here */ + revkey.class=0x80; + revkey.algid=revoker_pk->pubkey_algo; + free_public_key(revoker_pk); + break; + } + + rc = make_keysig_packet( &sig, pk, NULL, NULL, sk, 0x1F, 0, 0, 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 = m_alloc_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 = m_alloc_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 ) @@ -1495,7 +2349,7 @@ menu_expire( KBNODE pub_keyblock, KBNODE sec_keyblock ) 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 ) ) { + && ( mainkey || sub_pk ) ) { PKT_signature *sig = node->pkt->pkt.signature; if( keyid[0] == sig->keyid[0] && keyid[1] == sig->keyid[1] && ( (mainkey && uid && (sig->sig_class&~3) == 0x10) @@ -1532,31 +2386,31 @@ menu_expire( KBNODE pub_keyblock, KBNODE sec_keyblock ) /* create new self signature */ if( mainkey ) rc = make_keysig_packet( &newsig, main_pk, uid, NULL, - sk, 0x13, 0, + sk, 0x13, 0, 0, 0, 0, keygen_add_std_prefs, main_pk ); else rc = make_keysig_packet( &newsig, main_pk, NULL, sub_pk, - sk, 0x18, 0, + sk, 0x18, 0, 0, 0, 0, keygen_add_key_expire, sub_pk ); if( rc ) { log_error("make_keysig_packet failed: %s\n", - gpg_errstr(rc)); + g10_errstr(rc)); free_secret_key( sk ); return 0; } /* replace the packet */ - newpkt = gcry_xcalloc( 1, sizeof *newpkt ); + newpkt = m_alloc_clear( sizeof *newpkt ); newpkt->pkttype = PKT_SIGNATURE; newpkt->pkt.signature = newsig; free_packet( node->pkt ); - gcry_free( node->pkt ); + m_free( node->pkt ); node->pkt = newpkt; if( sn ) { - newpkt = gcry_xcalloc( 1, sizeof *newpkt ); + newpkt = m_alloc_clear( sizeof *newpkt ); newpkt->pkttype = PKT_SIGNATURE; newpkt->pkt.signature = copy_signature( NULL, newsig ); free_packet( sn->pkt ); - gcry_free( sn->pkt ); + m_free( sn->pkt ); sn->pkt = newpkt; } sub_pk = NULL; @@ -1565,9 +2419,235 @@ menu_expire( KBNODE pub_keyblock, KBNODE sec_keyblock ) } free_secret_key( sk ); + update_trust=1; return 1; } +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)) { + 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); + m_free(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, + 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 = m_alloc_clear( sizeof *newpkt ); + newpkt->pkttype = PKT_SIGNATURE; + newpkt->pkt.signature = newsig; + free_packet( node->pkt ); + m_free( 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; + + 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) ) { + 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); + m_free(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, + 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 = m_alloc_clear( sizeof *newpkt ); + newpkt->pkttype = PKT_SIGNATURE; + newpkt->pkt.signature = newsig; + free_packet( node->pkt ); + m_free( node->pkt ); + node->pkt = newpkt; + modified = 1; + } + } + } + } + + free_secret_key( sk ); + return modified; +} + /**************** * Select one user id or remove all selection if index is 0. @@ -1717,6 +2797,21 @@ 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. @@ -1724,6 +2819,7 @@ count_selected_keys( KBNODE keyblock ) static void ask_revoke_sig( KBNODE keyblock, KBNODE node ) { + int doit=0; PKT_signature *sig = node->pkt->pkt.signature; KBNODE unode = find_prev_kbnode( keyblock, node, PKT_USER_ID ); @@ -1735,13 +2831,29 @@ ask_revoke_sig( KBNODE keyblock, KBNODE node ) tty_printf(_("user ID: \"")); tty_print_utf8_string( unode->pkt->pkt.user_id->name, unode->pkt->pkt.user_id->len ); - tty_printf(_("\"\nsigned with your key %08lX at %s\n"), - sig->keyid[1], datestr_from_sig(sig) ); - if( cpr_get_answer_is_yes("ask_revoke_sig.one", - _("Create a revocation certificate for this signature? (y/N)")) ) { - node->flag |= NODFLG_MARK_A; - unode->flag |= NODFLG_MARK_A; + if(sig->flags.exportable) + tty_printf(_("\"\nsigned with your key %08lX at %s\n"), + (ulong)sig->keyid[1], datestr_from_sig(sig) ); + else + tty_printf(_("\"\nlocally signed with your key %08lX at %s\n"), + (ulong)sig->keyid[1], datestr_from_sig(sig) ); + + 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; } } @@ -1758,7 +2870,6 @@ menu_revsig( KBNODE keyblock ) PKT_public_key *primary_pk; KBNODE node; int changed = 0; - int upd_trust = 0; int rc, any; struct revocation_reason_info *reason = NULL; @@ -1775,15 +2886,18 @@ menu_revsig( KBNODE keyblock ) } else if( node->pkt->pkttype == PKT_SIGNATURE && ((sig = node->pkt->pkt.signature), - !seckey_available( sig->keyid ) ) ) { + !seckey_available(sig->keyid) ) ) { if( (sig->sig_class&~3) == 0x10 ) { - tty_printf(_(" signed by %08lX at %s\n"), - sig->keyid[1], datestr_from_sig(sig) ); - node->flag |= NODFLG_SELSIG; + tty_printf(_(" signed by %08lX at %s%s%s\n"), + (ulong)sig->keyid[1], 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(_(" revoked by %08lX at %s\n"), - sig->keyid[1], datestr_from_sig(sig) ); + (ulong)sig->keyid[1], datestr_from_sig(sig) ); } } } @@ -1812,15 +2926,16 @@ menu_revsig( KBNODE keyblock ) } else if( node->pkt->pkttype == PKT_SIGNATURE ) { sig = node->pkt->pkt.signature; - tty_printf(_(" signed by %08lX at %s\n"), - sig->keyid[1], datestr_from_sig(sig) ); + tty_printf(_(" signed by %08lX at %s%s\n"), + (ulong)sig->keyid[1], 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)")) ) + _("Really create the revocation certificates? (y/N) ")) ) return 0; /* forget it */ reason = ask_revocation_reason( 0, 1, 0 ); @@ -1845,9 +2960,10 @@ menu_revsig( KBNODE keyblock ) memset( &attrib, 0, sizeof attrib ); attrib.reason = reason; + attrib.non_exportable=!node->pkt->pkt.signature->flags.exportable; node->flag &= ~NODFLG_MARK_A; - sk = gcry_xcalloc_secure( 1, sizeof *sk ); + sk = m_alloc_secure_clear( sizeof *sk ); if( get_seckey( sk, node->pkt->pkt.signature->keyid ) ) { log_info(_("no secret key\n")); continue; @@ -1856,27 +2972,25 @@ menu_revsig( KBNODE keyblock ) unode->pkt->pkt.user_id, NULL, sk, - 0x30, 0, + 0x30, 0, 0, 0, 0, sign_mk_attrib, &attrib ); free_secret_key(sk); if( rc ) { - log_error(_("signing failed: %s\n"), gpg_errstr(rc)); + log_error(_("signing failed: %s\n"), g10_errstr(rc)); release_revocation_reason_info( reason ); return changed; } changed = 1; /* we changed the keyblock */ - upd_trust = 1; + update_trust = 1; - pkt = gcry_xcalloc( 1, sizeof *pkt ); + pkt = m_alloc_clear( sizeof *pkt ); pkt->pkttype = PKT_SIGNATURE; pkt->pkt.signature = sig; insert_kbnode( unode, new_kbnode(pkt), 0 ); goto reloop; } - if( upd_trust ) - clear_trust_checked_flag( primary_pk ); release_revocation_reason_info( reason ); return changed; } @@ -1892,7 +3006,6 @@ menu_revkey( KBNODE pub_keyblock, KBNODE sec_keyblock ) PKT_public_key *mainpk; KBNODE node; int changed = 0; - int upd_trust = 0; int rc; struct revocation_reason_info *reason = NULL; @@ -1918,19 +3031,18 @@ menu_revkey( KBNODE pub_keyblock, KBNODE sec_keyblock ) 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, - sign_mk_attrib, - &attrib ); + 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"), gpg_errstr(rc)); + log_error(_("signing failed: %s\n"), g10_errstr(rc)); release_revocation_reason_info( reason ); return changed; } changed = 1; /* we changed the keyblock */ - upd_trust = 1; - pkt = gcry_xcalloc( 1, sizeof *pkt ); + pkt = m_alloc_clear( sizeof *pkt ); pkt->pkttype = PKT_SIGNATURE; pkt->pkt.signature = sig; insert_kbnode( node, new_kbnode(pkt), 0 ); @@ -1940,8 +3052,9 @@ menu_revkey( KBNODE pub_keyblock, KBNODE sec_keyblock ) commit_kbnode( &pub_keyblock ); /*commit_kbnode( &sec_keyblock );*/ - if( upd_trust ) - clear_trust_checked_flag( mainpk ); + /* 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; @@ -1951,20 +3064,67 @@ menu_revkey( KBNODE pub_keyblock, KBNODE sec_keyblock ) static int enable_disable_key( KBNODE keyblock, int disable ) { - ulong lid = find_kbnode( keyblock, PKT_PUBLIC_KEY ) - ->pkt->pkt.public_key->local_id; + PKT_public_key *pk = find_kbnode( keyblock, PKT_PUBLIC_KEY ) + ->pkt->pkt.public_key; unsigned int trust, newtrust; - /* Note: Because the keys have beed displayed, we have - * ensured that local_id has been set */ - trust = newtrust = get_ownertrust( lid ); + trust = newtrust = get_ownertrust (pk); newtrust &= ~TRUST_FLAG_DISABLED; if( disable ) newtrust |= TRUST_FLAG_DISABLED; if( trust == newtrust ) return 0; /* already in that state */ - if( !update_ownertrust( lid, newtrust ) ) - return 1; + 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; + u32 keyid[2]; + + /* 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; + keyid_from_pk(pk, keyid); + } + 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 0x%08lX (uid %d)\n"), + image_type_to_string(type,1), + (ulong)size,(ulong)keyid[1],count); + show_photos(&uid->attribs[i],1,pk,NULL); + } + } + } + } + } +} diff --git a/g10/keygen.c b/g10/keygen.c index 1e2accd0c..b8398b88a 100644 --- a/g10/keygen.c +++ b/g10/keygen.c @@ -1,5 +1,5 @@ /* keygen.c - generate a key pair - * Copyright (C) 1998, 1999, 2000 Free Software Foundation, Inc. + * Copyright (C) 1998, 1999, 2000, 2001, 2002 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -26,23 +26,31 @@ #include <errno.h> #include <assert.h> #include "util.h" -#include <gcrypt.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" +#define MAX_PREFS 30 + + enum para_name { pKEYTYPE, pKEYLENGTH, + pKEYUSAGE, pSUBKEYTYPE, pSUBKEYLENGTH, + pSUBKEYUSAGE, pNAMEREAL, pNAMEEMAIL, pNAMECOMMENT, + pPREFERENCES, + pREVOKER, pUSERID, pEXPIREDATE, pKEYEXPIRE, /* in n seconds */ @@ -57,10 +65,12 @@ struct para_data_s { int lnr; enum para_name key; union { - DEK *dek; - STRING2KEY *s2k; - u32 expire; - char value[1]; + DEK *dek; + STRING2KEY *s2k; + u32 expire; + unsigned int usage; + struct revocation_key revkey; + char value[1]; } u; }; @@ -83,6 +93,21 @@ struct output_control_s { }; +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; + static void do_generate_keypair( struct para_data_s *para, struct output_control_s *outctrl ); static int write_keyblock( IOBUF out, KBNODE node ); @@ -91,16 +116,32 @@ static int write_keyblock( IOBUF out, KBNODE node ); static void write_uid( KBNODE root, const char *s ) { - PACKET *pkt = gcry_xcalloc( 1,sizeof *pkt ); + PACKET *pkt = m_alloc_clear(sizeof *pkt ); size_t n = strlen(s); pkt->pkttype = PKT_USER_ID; - pkt->pkt.user_id = gcry_xcalloc( 1, sizeof *pkt->pkt.user_id + n - 1 ); + pkt->pkt.user_id = m_alloc_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]; + + if (!use) + return; + + buf[0] = 0; + if (use & PUBKEY_USAGE_SIG) + buf[0] |= 0x01 | 0x02; + if (use & PUBKEY_USAGE_ENC) + buf[0] |= 0x04 | 0x08; + build_sig_subpkt (sig, SIGSUBPKT_KEY_FLAGS, buf, 1); +} int @@ -123,34 +164,318 @@ keygen_add_key_expire( PKT_signature *sig, void *opaque ) 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 (ulong val, int type, int (*cf)(int), byte *buf, int *nbuf) +{ + int i; + + if (cf (val)) { + log_info (_("preference %c%lu is not valid\n"), type, val); + if(type=='S' && val==CIPHER_ALGO_IDEA) + idea_cipher_warn(1); + return -1; + } + for (i=0; i < *nbuf; i++ ) { + if (buf[i] == val) { + log_info (_("preference %c%lu duplicated\n"), type, val); + return -1; + } + } + if (*nbuf >= MAX_PREFS) { + log_info (_("too many `%c' preferences\n"), type); + return -1; + } + buf[(*nbuf)++] = val; + return 0; +} + + +/* + * Parse the supplied string and use it to set the standard preferences. + * The String is expected to be in a forma like the one printed by "prefs", + * something like: "S10 S3 H3 H2 Z2 Z1". 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, mdc=1; /* mdc defaults on */ + ulong val; + const char *s, *s2; + int rc = 0; + + if (!string || !ascii_strcasecmp (string, "default")) { + if (opt.def_preference_list) + string=opt.def_preference_list; + else if ( !check_cipher_algo(CIPHER_ALGO_IDEA) ) + string = "S7 S3 S2 S1 H2 H3 Z2 Z1"; + else + string = "S7 S3 S2 H2 H3 Z2 Z1"; + + /* 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 */ + } + else if (!ascii_strcasecmp (string, "none")) + string = ""; + + for (s=string; *s; s = s2) { + if ((*s=='s' || *s == 'S') && isdigit(s[1]) ) { + val = strtoul (++s, (char**)&s2, 10); + if (set_one_pref (val, 'S', check_cipher_algo, sym, &nsym)) + rc = -1; + } + else if ((*s=='h' || *s == 'H') && isdigit(s[1]) ) { + val = strtoul (++s, (char**)&s2, 10); + if (set_one_pref (val, 'H', check_digest_algo, hash, &nhash)) + rc = -1; + } + else if ((*s=='z' || *s == 'Z') && isdigit(s[1]) ) { + val = strtoul (++s, (char**)&s2, 10); + if (set_one_pref (val, 'Z', check_compress_algo, zip, &nzip)) + rc = -1; + } + else if (ascii_strcasecmp(s,"mdc")==0) { + mdc=1; + s2=s+3; + } + else if (ascii_strcasecmp(s,"no-mdc")==0) { + mdc=0; + s2=s+6; + } + else if (isspace (*s)) + s2 = s+1; + else { + log_info (_("invalid character in preference string\n")); + return -1; + } + } + + if (!rc) + { + if(personal) + { + if(personal==PREFTYPE_SYM) + { + m_free(opt.personal_cipher_prefs); + + if(nsym==0) + opt.personal_cipher_prefs=NULL; + else + { + int i; + + opt.personal_cipher_prefs= + m_alloc(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) + { + m_free(opt.personal_digest_prefs); + + if(nhash==0) + opt.personal_digest_prefs=NULL; + else + { + int i; + + opt.personal_digest_prefs= + m_alloc(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) + { + m_free(opt.personal_compress_prefs); + + if(nzip==0) + opt.personal_compress_prefs=NULL; + else + { + int i; + + opt.personal_compress_prefs= + m_alloc(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; + prefs_initialized = 1; + } + } + + return rc; +} + + +/* + * Return a printable list of preferences. Caller must free. + */ +char * +keygen_get_std_prefs () +{ + char *buf; + int i; + + if (!prefs_initialized) + keygen_set_std_prefs (NULL,0); + + buf = m_alloc ( MAX_PREFS*3*5 + 5 + 1); + *buf = 0; + for (i=0; i < nsym_prefs; i++ ) + sprintf (buf+strlen(buf), "S%d ", sym_prefs[i]); + for (i=0; i < nhash_prefs; i++ ) + sprintf (buf+strlen(buf), "H%d ", hash_prefs[i]); + for (i=0; i < nzip_prefs; i++ ) + sprintf (buf+strlen(buf), "Z%d ", zip_prefs[i]); + + if(mdc_available) + sprintf(buf+strlen(buf),"[mdc]"); + else if (*buf) /* trim the trailing space */ + buf[strlen(buf)-1] = 0; + + return buf; +} + + +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 = m_alloc_clear (n); + } + else { + buf = m_alloc (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); + + m_free (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); + + 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; byte buf[8]; + do_add_key_flags (sig, pk->pubkey_usage); keygen_add_key_expire( sig, opaque ); - - buf[0] = GCRY_CIPHER_RIJNDAEL; - buf[1] = GCRY_CIPHER_TWOFISH; - buf[2] = GCRY_CIPHER_CAST5; - buf[3] = GCRY_CIPHER_BLOWFISH; - build_sig_subpkt( sig, SIGSUBPKT_PREF_SYM, buf, 4 ); - - buf[0] = GCRY_MD_RMD160; - buf[1] = GCRY_MD_SHA1; - build_sig_subpkt( sig, SIGSUBPKT_PREF_HASH, buf, 2 ); - - buf[0] = 2; - buf[1] = 1; - build_sig_subpkt( sig, SIGSUBPKT_PREF_COMPR, buf, 2 ); - - buf[0] = 1; /* supports MDC packets (15 + 16) */ - build_sig_subpkt( sig, SIGSUBPKT_FEATURES, buf, 1 ); + keygen_upd_std_prefs (sig, opaque); buf[0] = 0x80; /* no modify - It is reasonable that a key holder * has the possibility to reject signatures from users @@ -163,10 +488,69 @@ keygen_add_std_prefs( PKT_signature *sig, void *opaque ) 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; +} static int -write_selfsig( KBNODE root, KBNODE pub_root, PKT_secret_key *sk ) +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 = m_alloc_clear( sizeof *pkt ); + pkt->pkttype = PKT_SIGNATURE; + pkt->pkt.signature = sig; + add_kbnode( root, new_kbnode( pkt ) ); + return rc; +} + +static int +write_selfsig( KBNODE root, KBNODE pub_root, PKT_secret_key *sk, + unsigned int use ) { PACKET *pkt; PKT_signature *sig; @@ -188,16 +572,20 @@ write_selfsig( KBNODE root, KBNODE pub_root, PKT_secret_key *sk ) 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, - keygen_add_std_prefs, pk ); + 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", gpg_errstr(rc) ); + log_error("make_keysig_packet failed: %s\n", g10_errstr(rc) ); return rc; } - pkt = gcry_xcalloc( 1, sizeof *pkt ); + pkt = m_alloc_clear( sizeof *pkt ); pkt->pkttype = PKT_SIGNATURE; pkt->pkt.signature = sig; add_kbnode( root, new_kbnode( pkt ) ); @@ -205,13 +593,15 @@ write_selfsig( KBNODE root, KBNODE pub_root, PKT_secret_key *sk ) } static int -write_keybinding( KBNODE root, KBNODE pub_root, PKT_secret_key *sk ) +write_keybinding( KBNODE root, KBNODE pub_root, PKT_secret_key *sk, + unsigned int use ) { PACKET *pkt; PKT_signature *sig; int rc=0; KBNODE node; PKT_public_key *pk, *subpk; + struct opaque_data_usage_and_pk oduap; if( opt.verbose ) log_info(_("writing key binding signature\n")); @@ -221,6 +611,10 @@ write_keybinding( KBNODE root, KBNODE pub_root, PKT_secret_key *sk ) 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); + /* find the last subkey */ subpk = NULL; for(node=pub_root; node; node = node->next ) { @@ -231,14 +625,16 @@ write_keybinding( KBNODE root, KBNODE pub_root, PKT_secret_key *sk ) BUG(); /* and make the signature */ - rc = make_keysig_packet( &sig, pk, NULL, subpk, sk, 0x18, 0, - keygen_add_key_expire, subpk ); + oduap.usage = use; + oduap.pk = subpk; + rc = make_keysig_packet( &sig, pk, NULL, subpk, sk, 0x18, 0, 0, 0, 0, + keygen_add_key_flags_and_expire, &oduap ); if( rc ) { - log_error("make_keysig_packet failed: %s\n", gpg_errstr(rc) ); + log_error("make_keysig_packet failed: %s\n", g10_errstr(rc) ); return rc; } - pkt = gcry_xcalloc( 1, sizeof *pkt ); + pkt = m_alloc_clear( sizeof *pkt ); pkt->pkttype = PKT_SIGNATURE; pkt->pkt.signature = sig; add_kbnode( root, new_kbnode( pkt ) ); @@ -246,63 +642,17 @@ write_keybinding( KBNODE root, KBNODE pub_root, PKT_secret_key *sk ) } - -static int -key_from_sexp( GCRY_MPI *array, - GCRY_SEXP sexp, const char *topname, const char *elems ) -{ - GCRY_SEXP list, l2; - const char *s; - int i, idx; - - list = gcry_sexp_find_token( sexp, topname, 0 ); - if( !list ) - return GCRYERR_INV_OBJ; - l2 = gcry_sexp_cadr( list ); - gcry_sexp_release ( list ); - list = l2; - if( !list ) - return GCRYERR_NO_OBJ; - - idx = 0; - for(s=elems; *s; s++, idx++ ) { - l2 = gcry_sexp_find_token( list, s, 1 ); - if( !l2 ) { - for(i=0; i<idx; i++) { - gcry_free( array[i] ); - array[i] = NULL; - } - gcry_sexp_release ( list ); - return GCRYERR_NO_OBJ; /* required parameter not found */ - } - array[idx] = gcry_sexp_nth_mpi( l2, 1, GCRYMPI_FMT_USG ); - gcry_sexp_release ( l2 ); - if( !array[idx] ) { - for(i=0; i<idx; i++) { - gcry_free( array[i] ); - array[i] = NULL; - } - gcry_sexp_release ( list ); - return GCRYERR_INV_OBJ; /* required parameter is invalid */ - } - } - gcry_sexp_release ( list ); - - return 0; -} - - - 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 rc; + int i; PACKET *pkt; PKT_secret_key *sk; PKT_public_key *pk; - GCRY_SEXP misc_key_info; - GCRY_SEXP s_parms, s_key; + MPI skey[4]; + MPI *factors; assert( is_ELGAMAL(algo) ); @@ -316,46 +666,31 @@ gen_elg(int algo, unsigned nbits, KBNODE pub_root, KBNODE sec_root, DEK *dek, log_info(_("keysize rounded up to %u bits\n"), nbits ); } - if ( gcry_sexp_build ( &s_parms, NULL, - "(genkey(%s(nbits %d)))", - algo == GCRY_PK_ELG_E ? "openpgp-elg" : - algo == GCRY_PK_ELG ? "elg" : "x-oops" , - (int)nbits ) ) - BUG (); - rc = gcry_pk_genkey( &s_key, s_parms ); - gcry_sexp_release( s_parms ); + rc = pubkey_generate( algo, nbits, skey, &factors ); if( rc ) { - log_error("pk_genkey failed: %s\n", gpg_errstr(rc) ); + log_error("pubkey_generate failed: %s\n", g10_errstr(rc) ); return rc; } - - sk = gcry_xcalloc( 1, sizeof *sk ); - pk = gcry_xcalloc( 1, sizeof *pk ); + sk = m_alloc_clear( sizeof *sk ); + pk = m_alloc_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; - - rc = key_from_sexp( pk->pkey, s_key, "public-key", "pgy" ); - if( rc ) { - log_error("key_from_sexp failed: rc=%d\n", rc ); - return rc; - } - rc = key_from_sexp( sk->skey, s_key, "private-key", "pgyx" ); - if( rc ) { - log_error("key_from_sexp failed: rc=%d\n", rc ); - return rc; - } - misc_key_info = gcry_sexp_find_token( s_key, "misc-key-info", 0 ); - gcry_sexp_release ( s_key ); - + 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] ); + sk->csum = checksum_mpi_counted_nbits( sk->skey[3] ); if( ret_sk ) /* not a subkey: return an unprotected version of the sk */ *ret_sk = copy_secret_key( NULL, sk ); @@ -364,39 +699,27 @@ gen_elg(int algo, unsigned nbits, KBNODE pub_root, KBNODE sec_root, DEK *dek, sk->protect.s2k = *s2k; rc = protect_secret_key( sk, dek ); if( rc ) { - log_error("protect_secret_key failed: %s\n", gpg_errstr(rc) ); + log_error("protect_secret_key failed: %s\n", g10_errstr(rc) ); free_public_key(pk); free_secret_key(sk); return rc; } } - pkt = gcry_xcalloc( 1,sizeof *pkt); + pkt = m_alloc_clear(sizeof *pkt); pkt->pkttype = ret_sk ? PKT_PUBLIC_KEY : PKT_PUBLIC_SUBKEY; 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 = gcry_xcalloc( 1,sizeof *pkt); + pkt = m_alloc_clear(sizeof *pkt); pkt->pkttype = ret_sk ? PKT_SECRET_KEY : PKT_SECRET_SUBKEY; pkt->pkt.secret_key = sk; add_kbnode(sec_root, new_kbnode( pkt )); - if ( misc_key_info ) { - size_t n; - char *buf; - - n = gcry_sexp_sprint ( misc_key_info, 0, NULL, 0 ); - buf = gcry_xmalloc ( n+4 ); - strcpy ( buf, "#::" ); - n = gcry_sexp_sprint ( misc_key_info, 0, buf+3, n ); - if ( n ) { - n += 3; - add_kbnode( sec_root, make_comment_node_from_buffer( buf, n )); - } - gcry_free ( buf ); - gcry_sexp_release (misc_key_info); - } + for(i=0; factors[i]; i++ ) + add_kbnode( sec_root, + make_mpi_comment_node("#:ELG_factor:", factors[i] )); return 0; } @@ -410,11 +733,12 @@ gen_dsa(unsigned int nbits, KBNODE pub_root, KBNODE sec_root, DEK *dek, STRING2KEY *s2k, PKT_secret_key **ret_sk, u32 expireval ) { int rc; + int i; PACKET *pkt; PKT_secret_key *sk; PKT_public_key *pk; - GCRY_SEXP misc_key_info; - GCRY_SEXP s_parms, s_key; + MPI skey[5]; + MPI *factors; if( nbits > 1024 || nbits < 512 ) { nbits = 1024; @@ -426,44 +750,33 @@ gen_dsa(unsigned int nbits, KBNODE pub_root, KBNODE sec_root, DEK *dek, log_info(_("keysize rounded up to %u bits\n"), nbits ); } - if ( gcry_sexp_build ( &s_parms, NULL, - "(genkey(dsa(nbits %d)))", (int)nbits ) ) - BUG (); - - rc = gcry_pk_genkey( &s_key, s_parms ); - gcry_sexp_release( s_parms ); + rc = pubkey_generate( PUBKEY_ALGO_DSA, nbits, skey, &factors ); if( rc ) { - log_error("pk_genkey failed: %s\n", gpg_errstr(rc) ); + log_error("pubkey_generate failed: %s\n", g10_errstr(rc) ); return rc; } - - sk = gcry_xcalloc( 1, sizeof *sk ); - pk = gcry_xcalloc( 1, sizeof *pk ); + sk = m_alloc_clear( sizeof *sk ); + pk = m_alloc_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 = GCRY_PK_DSA; - - rc = key_from_sexp( pk->pkey, s_key, "public-key", "pqgy" ); - if( rc ) { - log_error("key_from_sexp failed: rc=%d\n", rc ); - return rc; - } - rc = key_from_sexp( sk->skey, s_key, "private-key", "pqgyx" ); - if( rc ) { - log_error("key_from_sexp failed: rc=%d\n", rc ); - return rc; - } - misc_key_info = gcry_sexp_find_token( s_key, "misc-key-info", 0 ); - gcry_sexp_release ( s_key ); - + 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] ); + sk->csum = checksum_mpi_counted_nbits( sk->skey[4] ); if( ret_sk ) /* not a subkey: return an unprotected version of the sk */ *ret_sk = copy_secret_key( NULL, sk ); @@ -472,47 +785,39 @@ gen_dsa(unsigned int nbits, KBNODE pub_root, KBNODE sec_root, DEK *dek, sk->protect.s2k = *s2k; rc = protect_secret_key( sk, dek ); if( rc ) { - log_error("protect_secret_key failed: %s\n", gpg_errstr(rc) ); + log_error("protect_secret_key failed: %s\n", g10_errstr(rc) ); free_public_key(pk); free_secret_key(sk); - gcry_sexp_release (misc_key_info); return rc; } } - pkt = gcry_xcalloc( 1,sizeof *pkt); + pkt = m_alloc_clear(sizeof *pkt); pkt->pkttype = ret_sk ? PKT_PUBLIC_KEY : PKT_PUBLIC_SUBKEY; 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 = gcry_xcalloc( 1,sizeof *pkt); + pkt = m_alloc_clear(sizeof *pkt); pkt->pkttype = ret_sk ? PKT_SECRET_KEY : PKT_SECRET_SUBKEY; pkt->pkt.secret_key = sk; add_kbnode(sec_root, new_kbnode( pkt )); - if ( misc_key_info ) { - size_t n; - char *buf; - - n = gcry_sexp_sprint ( misc_key_info, 0, NULL, 0 ); - buf = gcry_xmalloc ( n+4 ); - strcpy ( buf, "#::" ); - n = gcry_sexp_sprint ( misc_key_info, 0, buf+3, n ); - if ( n ) { - n += 3; - add_kbnode( sec_root, make_comment_node_from_buffer( buf, n )); - } - gcry_free ( buf ); - gcry_sexp_release (misc_key_info); - } - /* fixme: Merge this with the elg-generate function and release - * some more stuff (memory-leak) */ + for(i=1; factors[i]; i++ ) /* the first one is q */ + add_kbnode( sec_root, + make_mpi_comment_node("#:DSA_factor:", factors[i] )); + return 0; } -#if 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 ) @@ -521,7 +826,7 @@ gen_rsa(int algo, unsigned nbits, KBNODE pub_root, KBNODE sec_root, DEK *dek, PACKET *pkt; PKT_secret_key *sk; PKT_public_key *pk; - MPI skey[4]; + MPI skey[6]; MPI *factors; assert( is_RSA(algo) ); @@ -538,12 +843,12 @@ gen_rsa(int algo, unsigned nbits, KBNODE pub_root, KBNODE sec_root, DEK *dek, rc = pubkey_generate( algo, nbits, skey, &factors ); if( rc ) { - log_error("pubkey_generate failed: %s\n", gpg_errstr(rc) ); + log_error("pubkey_generate failed: %s\n", g10_errstr(rc) ); return rc; } - sk = gcry_xcalloc( 1, sizeof *sk ); - pk = gcry_xcalloc( 1, sizeof *pk ); + sk = m_alloc_clear( sizeof *sk ); + pk = m_alloc_clear( sizeof *pk ); sk->timestamp = pk->timestamp = make_timestamp(); sk->version = pk->version = 4; if( expireval ) { @@ -573,26 +878,25 @@ gen_rsa(int algo, unsigned nbits, KBNODE pub_root, KBNODE sec_root, DEK *dek, sk->protect.s2k = *s2k; rc = protect_secret_key( sk, dek ); if( rc ) { - log_error("protect_secret_key failed: %s\n", gpg_errstr(rc) ); + log_error("protect_secret_key failed: %s\n", g10_errstr(rc) ); free_public_key(pk); free_secret_key(sk); return rc; } } - pkt = gcry_xcalloc( 1,sizeof *pkt); + pkt = m_alloc_clear(sizeof *pkt); pkt->pkttype = ret_sk ? PKT_PUBLIC_KEY : PKT_PUBLIC_SUBKEY; pkt->pkt.public_key = pk; add_kbnode(pub_root, new_kbnode( pkt )); - pkt = gcry_xcalloc( 1,sizeof *pkt); + pkt = m_alloc_clear(sizeof *pkt); pkt->pkttype = ret_sk ? PKT_SECRET_KEY : PKT_SECRET_SUBKEY; pkt->pkt.secret_key = sk; add_kbnode(sec_root, new_kbnode( pkt )); return 0; } -#endif /**************** @@ -625,55 +929,69 @@ check_valid_days( const char *s ) /**************** * 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 ) +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( addmode ) tty_printf( _(" (%d) ElGamal (encrypt only)\n"), 3 ); - tty_printf( _(" (%d) ElGamal (sign and encrypt)\n"), 4 ); - #if 0 - tty_printf( _(" (%d) RSA (sign and encrypt)\n"), 5 ); - #endif + if (opt.expert) + tty_printf( _(" (%d) ElGamal (sign and encrypt)\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 (sign and encrypt)\n"), 7 ); for(;;) { answer = cpr_get("keygen.algo",_("Your selection? ")); cpr_kill_prompt(); algo = *answer? atoi(answer): 1; - gcry_free(answer); + m_free(answer); if( algo == 1 && !addmode ) { algo = 0; /* create both keys */ break; } - #if 0 + else if( algo == 7 && opt.expert ) { + if (cpr_get_answer_is_yes ("keygen.algo.rsa_se",_( + "The use of this algorithm is deprecated - create anyway? "))){ + algo = PUBKEY_ALGO_RSA; + *r_usage = PUBKEY_USAGE_ENC | PUBKEY_USAGE_SIG; + break; + } + } + else if( algo == 6 && addmode ) { + algo = PUBKEY_ALGO_RSA; + *r_usage = PUBKEY_USAGE_ENC; + break; + } else if( algo == 5 ) { - if( cpr_get_answer_is_yes("keygen.algo.rsa_se",_( - "Do you really want to create a sign and encrypt key? "))) { - algo = GCRY_PK_RSA; - break; - } + algo = PUBKEY_ALGO_RSA; + *r_usage = PUBKEY_USAGE_SIG; + break; } - #endif - else if( algo == 4 ) { + else if( algo == 4 && opt.expert) { if( cpr_get_answer_is_yes("keygen.algo.elg_se",_( - "Do you really want to create a sign and encrypt key? "))) { - algo = GCRY_PK_ELG; + "The use of this algorithm is deprecated - create anyway? "))){ + algo = PUBKEY_ALGO_ELGAMAL; break; } } else if( algo == 3 && addmode ) { - algo = GCRY_PK_ELG_E; + algo = PUBKEY_ALGO_ELGAMAL_E; break; } else if( algo == 2 ) { - algo = GCRY_PK_DSA; + algo = PUBKEY_ALGO_DSA; break; } else @@ -689,24 +1007,28 @@ ask_keysize( int algo ) char *answer; unsigned nbits; - tty_printf(_("About to generate a new %s keypair.\n" - " minimum keysize is 768 bits\n" - " default keysize is 1024 bits\n" - " highest suggested keysize is 2048 bits\n"), - gcry_pk_algo_name(algo) ); + if (algo != PUBKEY_ALGO_DSA && algo != PUBKEY_ALGO_RSA) { + tty_printf (_("About to generate a new %s keypair.\n" + " minimum keysize is 768 bits\n" + " default keysize is 1024 bits\n" + " highest suggested keysize is 2048 bits\n"), + pubkey_algo_to_string(algo) ); + } + for(;;) { answer = cpr_get("keygen.size", _("What keysize do you want? (1024) ")); cpr_kill_prompt(); nbits = *answer? atoi(answer): 1024; - gcry_free(answer); - if( algo == GCRY_PK_DSA && (nbits < 512 || nbits > 1024) ) + m_free(answer); + if( algo == PUBKEY_ALGO_DSA && (nbits < 512 || nbits > 1024) ) tty_printf(_("DSA only allows keysizes from 512 to 1024\n")); - else if( nbits < 768 ) - tty_printf(_("keysize too small; 768 is smallest value allowed.\n")); - else if( algo == GCRY_PK_RSA && nbits < 1024 ) + else if( algo == PUBKEY_ALGO_RSA && nbits < 1024 ) tty_printf(_("keysize too small;" " 1024 is smallest value allowed for RSA.\n")); + else if( nbits < 768 ) + tty_printf(_("keysize too small;" + " 768 is smallest value allowed.\n")); else if( nbits > 4096 ) { /* It is ridiculous and an annoyance to use larger key sizes! * GnuPG can handle much larger sizes; but it takes an eternity @@ -731,16 +1053,11 @@ ask_keysize( int algo ) break; } } - else if( nbits > 1536 && !cpr_enabled() && algo != GCRY_PK_RSA ) { - if( cpr_get_answer_is_yes("keygen.size.large.okay",_( - "Do you really need such a large keysize? ")) ) - break; - } else break; } tty_printf(_("Requested keysize is %u bits\n"), nbits ); - if( algo == GCRY_PK_DSA && (nbits % 64) ) { + if( algo == PUBKEY_ALGO_DSA && (nbits % 64) ) { nbits = ((nbits + 63) / 64) * 64; tty_printf(_("rounded up to %u bits\n"), nbits ); } @@ -786,20 +1103,38 @@ parse_expire_string( const char *string ) return valid_days; } - -static u32 -ask_expire_interval(void) +/* object == 0 for a key, and 1 for a sig */ +u32 +ask_expire_interval(int object) { char *answer; int valid_days=0; u32 interval = 0; - 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")); + switch(object) + { + case 0: + 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: + 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 */ @@ -808,8 +1143,11 @@ ask_expire_interval(void) for(;;) { u32 curtime=make_timestamp(); - gcry_free(answer); - answer = cpr_get("keygen.valid",_("Key is valid for? (0) ")); + m_free(answer); + if(object==0) + answer = cpr_get("keygen.valid",_("Key is valid for? (0) ")); + else + answer = cpr_get("siggen.valid",_("Signature is valid for? (0) ")); cpr_kill_prompt(); trim_spaces(answer); valid_days = parse_expire_string( answer ); @@ -819,14 +1157,18 @@ ask_expire_interval(void) } if( !valid_days ) { - tty_printf(_("Key does not expire at all\n")); + tty_printf(_("%s does not expire at all\n"), + object==0?"Key":"Signature"); interval = 0; } else { interval = valid_days * 86400L; /* print the date when the key expires */ - tty_printf(_("Key expires at %s\n"), + tty_printf(_("%s expires at %s\n"), + object==0?"Key":"Signature", 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")); @@ -836,14 +1178,14 @@ ask_expire_interval(void) _("Is this correct (y/n)? ")) ) break; } - gcry_free(answer); + m_free(answer); return interval; } u32 ask_expiredate() { - u32 x = ask_expire_interval(); + u32 x = ask_expire_interval(0); return x? make_timestamp() + x : 0; } @@ -887,7 +1229,7 @@ ask_user_id( int mode ) if( !aname ) { for(;;) { - gcry_free(aname); + m_free(aname); aname = cpr_get("keygen.name",_("Real name: ")); trim_spaces(aname); cpr_kill_prompt(); @@ -907,7 +1249,7 @@ ask_user_id( int mode ) } if( !amail ) { for(;;) { - gcry_free(amail); + m_free(amail); amail = cpr_get("keygen.email",_("Email address: ")); trim_spaces(amail); cpr_kill_prompt(); @@ -926,7 +1268,7 @@ ask_user_id( int mode ) } if( !acomment ) { for(;;) { - gcry_free(acomment); + m_free(acomment); acomment = cpr_get("keygen.comment",_("Comment: ")); trim_spaces(acomment); cpr_kill_prompt(); @@ -940,8 +1282,8 @@ ask_user_id( int mode ) } - gcry_free(uid); - uid = p = gcry_xmalloc(strlen(aname)+strlen(amail)+strlen(acomment)+12+10); + m_free(uid); + uid = p = m_alloc(strlen(aname)+strlen(amail)+strlen(acomment)+12+10); p = stpcpy(p, aname ); if( *acomment ) p = stpcpy(stpcpy(stpcpy(p," ("), acomment),")"); @@ -950,10 +1292,8 @@ ask_user_id( int mode ) /* append a warning if we do not have dev/random * or it is switched into quick testmode */ - #if 0 if( quick_random_gen(-1) ) strcpy(p, " (INSECURE!)" ); - #endif /* print a note in case that UTF8 mapping has to be done */ for(p=uid; *p; p++ ) { @@ -973,12 +1313,12 @@ ask_user_id( int mode ) } for(;;) { - char *ansstr = _("NnCcEeOoQq"); + const char *ansstr = _("NnCcEeOoQq"); if( strlen(ansstr) != 10 ) BUG(); if( cpr_enabled() ) { - answer = gcry_xstrdup(ansstr+6); + answer = m_strdup(ansstr+6); answer[1] = 0; } else { @@ -990,15 +1330,15 @@ ask_user_id( int mode ) if( strlen(answer) > 1 ) ; else if( *answer == ansstr[0] || *answer == ansstr[1] ) { - gcry_free(aname); aname = NULL; + m_free(aname); aname = NULL; break; } else if( *answer == ansstr[2] || *answer == ansstr[3] ) { - gcry_free(acomment); acomment = NULL; + m_free(acomment); acomment = NULL; break; } else if( *answer == ansstr[4] || *answer == ansstr[5] ) { - gcry_free(amail); amail = NULL; + m_free(amail); amail = NULL; break; } else if( *answer == ansstr[6] || *answer == ansstr[7] ) { @@ -1006,29 +1346,29 @@ ask_user_id( int mode ) tty_printf(_("Please correct the error first\n")); } else { - gcry_free(aname); aname = NULL; - gcry_free(acomment); acomment = NULL; - gcry_free(amail); amail = NULL; + m_free(aname); aname = NULL; + m_free(acomment); acomment = NULL; + m_free(amail); amail = NULL; break; } } else if( *answer == ansstr[8] || *answer == ansstr[9] ) { - gcry_free(aname); aname = NULL; - gcry_free(acomment); acomment = NULL; - gcry_free(amail); amail = NULL; - gcry_free(uid); uid = NULL; + m_free(aname); aname = NULL; + m_free(acomment); acomment = NULL; + m_free(amail); amail = NULL; + m_free(uid); uid = NULL; break; } - gcry_free(answer); + m_free(answer); } - gcry_free(answer); + m_free(answer); if( !amail && !acomment && !amail ) break; - gcry_free(uid); uid = NULL; + m_free(uid); uid = NULL; } if( uid ) { char *p = native_to_utf8( uid ); - gcry_free( uid ); + m_free( uid ); uid = p; } return uid; @@ -1040,20 +1380,22 @@ 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 = gcry_xmalloc_secure( sizeof *s2k ); + s2k = m_alloc_secure( sizeof *s2k ); for(;;) { s2k->mode = opt.s2k_mode; s2k->hash_algo = opt.s2k_digest_algo; - dek = passphrase_to_dek( NULL, 0, opt.s2k_cipher_algo, s2k, 2 ); + dek = passphrase_to_dek( NULL, 0, opt.s2k_cipher_algo, s2k,2,errtext); if( !dek ) { - tty_printf(_("passphrase not correctly repeated; try again.\n")); + errtext = _("passphrase not correctly repeated; try again"); + tty_printf(_("%s.\n"), errtext); } else if( !dek->keylen ) { - gcry_free(dek); dek = NULL; - gcry_free(s2k); s2k = NULL; + m_free(dek); dek = NULL; + m_free(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" @@ -1069,7 +1411,7 @@ ask_passphrase( STRING2KEY **ret_s2k ) static int -do_create( int algo, unsigned nbits, KBNODE pub_root, KBNODE sec_root, +do_create( int algo, unsigned int nbits, KBNODE pub_root, KBNODE sec_root, DEK *dek, STRING2KEY *s2k, PKT_secret_key **sk, u32 expiredate ) { int rc=0; @@ -1081,14 +1423,12 @@ do_create( int algo, unsigned nbits, KBNODE pub_root, KBNODE sec_root, "disks) during the prime generation; this gives the random number\n" "generator a better chance to gain enough entropy.\n") ); - if( algo == GCRY_PK_ELG || algo == GCRY_PK_ELG_E ) + if( algo == PUBKEY_ALGO_ELGAMAL || algo == PUBKEY_ALGO_ELGAMAL_E ) rc = gen_elg(algo, nbits, pub_root, sec_root, dek, s2k, sk, expiredate); - else if( algo == GCRY_PK_DSA ) + else if( algo == PUBKEY_ALGO_DSA ) rc = gen_dsa(nbits, pub_root, sec_root, dek, s2k, sk, expiredate); - #if 0 - else if( algo == GCRY_PK_RSA ) + else if( algo == PUBKEY_ALGO_RSA ) rc = gen_rsa(algo, nbits, pub_root, sec_root, dek, s2k, sk, expiredate); - #endif else BUG(); @@ -1120,9 +1460,10 @@ generate_user_id() if( !p ) return NULL; n = strlen(p); - uid = gcry_xcalloc( 1, sizeof *uid + n - 1 ); + uid = m_alloc_clear( sizeof *uid + n - 1 ); uid->len = n; strcpy(uid->name, p); + uid->ref = 1; return uid; } @@ -1135,11 +1476,11 @@ release_parameter_list( struct para_data_s *r ) for( ; r ; r = r2 ) { r2 = r->next; if( r->key == pPASSPHRASE_DEK ) - gcry_free( r->u.dek ); + m_free( r->u.dek ); else if( r->key == pPASSPHRASE_S2K ) - gcry_free( r->u.s2k ); + m_free( r->u.s2k ); - gcry_free(r); + m_free(r); } } @@ -1163,12 +1504,102 @@ get_parameter_value( struct para_data_s *para, enum para_name key ) 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( isdigit( *r->u.value ) ) - return atoi( r->u.value ); - return gcry_pk_map_name( 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 { + log_error("%s:%d: invalid usage list\n", fname, r->lnr ); + return -1; /* error */ + } + } + r->u.usage = use; + return 0; +} + +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 */ } @@ -1181,6 +1612,8 @@ get_parameter_u32( struct para_data_s *para, enum para_name key ) 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 ); } @@ -1205,10 +1638,16 @@ get_parameter_s2k( struct para_data_s *para, enum para_name 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 ) + struct output_control_s *outctrl ) { struct para_data_s *r; const char *s1, *s2, *s3; @@ -1219,18 +1658,24 @@ proc_parameter_file( struct para_data_s *para, const char *fname, /* check that we have all required parameters */ assert( get_parameter( para, pKEYTYPE ) ); i = get_parameter_algo( para, pKEYTYPE ); - if( i < 1 || openpgp_pk_test_algo( i, GCRY_PK_USAGE_SIGN ) ) { + if( i < 1 || check_pubkey_algo2( i, PUBKEY_USAGE_SIG ) ) { r = get_parameter( para, pKEYTYPE ); log_error("%s:%d: invalid algorithm\n", fname, r->lnr ); return -1; } + if (parse_parameter_usage (fname, para, pKEYUSAGE)) + return -1; + i = get_parameter_algo( para, pSUBKEYTYPE ); - if( i > 1 && openpgp_pk_test_algo( i, 0 ) ) { + if( i > 0 && check_pubkey_algo( i ) ) { r = get_parameter( para, pSUBKEYTYPE ); log_error("%s:%d: invalid algorithm\n", fname, r->lnr ); return -1; } + if (i > 0 && parse_parameter_usage (fname, para, pSUBKEYUSAGE)) + return -1; + if( !get_parameter_value( para, pUSERID ) ) { /* create the formatted user ID */ @@ -1239,7 +1684,7 @@ proc_parameter_file( struct para_data_s *para, const char *fname, s3 = get_parameter_value( para, pNAMEEMAIL ); if( s1 || s2 || s3 ) { n = (s1?strlen(s1):0) + (s2?strlen(s2):0) + (s3?strlen(s3):0); - r = gcry_xcalloc( 1, sizeof *r + n + 20 ); + r = m_alloc_clear( sizeof *r + n + 20 ); r->key = pUSERID; p = r->u.value; if( s1 ) @@ -1253,6 +1698,13 @@ proc_parameter_file( struct para_data_s *para, const char *fname, } } + /* Set preferences, if any. */ + keygen_set_std_prefs(get_parameter_value( para, pPREFERENCES ), 0); + + /* 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 ) { @@ -1262,21 +1714,21 @@ proc_parameter_file( struct para_data_s *para, const char *fname, STRING2KEY *s2k; DEK *dek; - s2k = gcry_xmalloc_secure( sizeof *s2k ); + s2k = m_alloc_secure( sizeof *s2k ); s2k->mode = opt.s2k_mode; s2k->hash_algo = opt.s2k_digest_algo; set_next_passphrase( r->u.value ); - dek = passphrase_to_dek( NULL, 0, opt.s2k_cipher_algo, s2k, 2 ); + dek = passphrase_to_dek( NULL, 0, opt.s2k_cipher_algo, s2k, 2, NULL ); set_next_passphrase( NULL ); assert( dek ); memset( r->u.value, 0, strlen(r->u.value) ); - r = gcry_xcalloc( 1, sizeof *r ); + r = m_alloc_clear( sizeof *r ); r->key = pPASSPHRASE_S2K; r->u.s2k = s2k; r->next = para; para = r; - r = gcry_xcalloc( 1, sizeof *r ); + r = m_alloc_clear( sizeof *r ); r->key = pPASSPHRASE_DEK; r->u.dek = dek; r->next = para; @@ -1294,7 +1746,7 @@ proc_parameter_file( struct para_data_s *para, const char *fname, r->u.expire = i * 86400L; r->key = pKEYEXPIRE; /* change hat entry */ /* also set it for the subkey */ - r = gcry_xcalloc( 1, sizeof *r + 20 ); + r = m_alloc_clear( sizeof *r + 20 ); r->key = pSUBKEYEXPIRE; r->u.expire = i * 86400L; r->next = para; @@ -1324,13 +1776,17 @@ read_parameter_file( const char *fname ) } 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 }, { NULL, 0 } }; FILE *fp; @@ -1366,45 +1822,45 @@ read_parameter_file( const char *fname ) err = "line too long"; break; } - for( p = line; isspace(*p); p++ ) + for( p = line; isspace(*(byte*)p); p++ ) ; if( !*p || *p == '#' ) continue; keyword = p; if( *keyword == '%' ) { - for( ; !isspace(*p); p++ ) + for( ; !isspace(*(byte*)p); p++ ) ; if( *p ) *p++ = 0; - for( ; isspace(*p); p++ ) + for( ; isspace(*(byte*)p); p++ ) ; value = p; trim_trailing_ws( value, strlen(value) ); - if( !stricmp( keyword, "%echo" ) ) + if( !ascii_strcasecmp( keyword, "%echo" ) ) log_info("%s\n", value ); - else if( !stricmp( keyword, "%dry-run" ) ) + else if( !ascii_strcasecmp( keyword, "%dry-run" ) ) outctrl.dryrun = 1; - else if( !stricmp( keyword, "%commit" ) ) { + else if( !ascii_strcasecmp( keyword, "%commit" ) ) { outctrl.lnr = lnr; proc_parameter_file( para, fname, &outctrl ); release_parameter_list( para ); para = NULL; } - else if( !stricmp( keyword, "%pubring" ) ) { + else if( !ascii_strcasecmp( keyword, "%pubring" ) ) { if( outctrl.pub.fname && !strcmp( outctrl.pub.fname, value ) ) ; /* still the same file - ignore it */ else { - gcry_free( outctrl.pub.newfname ); - outctrl.pub.newfname = gcry_xstrdup( value ); + m_free( outctrl.pub.newfname ); + outctrl.pub.newfname = m_strdup( value ); outctrl.use_files = 1; } } - else if( !stricmp( keyword, "%secring" ) ) { + else if( !ascii_strcasecmp( keyword, "%secring" ) ) { if( outctrl.sec.fname && !strcmp( outctrl.sec.fname, value ) ) ; /* still the same file - ignore it */ else { - gcry_free( outctrl.sec.newfname ); - outctrl.sec.newfname = gcry_xstrdup( value ); + m_free( outctrl.sec.newfname ); + outctrl.sec.newfname = m_strdup( value ); outctrl.use_files = 1; } } @@ -1422,7 +1878,7 @@ read_parameter_file( const char *fname ) } if( *p ) *p++ = 0; - for( ; isspace(*p); p++ ) + for( ; isspace(*(byte*)p); p++ ) ; if( !*p ) { err = "missing argument"; @@ -1432,7 +1888,7 @@ read_parameter_file( const char *fname ) trim_trailing_ws( value, strlen(value) ); for(i=0; keywords[i].name; i++ ) { - if( !stricmp( keywords[i].name, keyword ) ) + if( !ascii_strcasecmp( keywords[i].name, keyword ) ) break; } if( !keywords[i].name ) { @@ -1460,7 +1916,7 @@ read_parameter_file( const char *fname ) break; } } - r = gcry_xcalloc( 1, sizeof *r + strlen( value ) ); + r = m_alloc_clear( sizeof *r + strlen( value ) ); r->lnr = lnr; r->key = keywords[i].key; strcpy( r->u.value, value ); @@ -1480,10 +1936,10 @@ read_parameter_file( const char *fname ) if( outctrl.use_files ) { /* close open streams */ iobuf_close( outctrl.pub.stream ); iobuf_close( outctrl.sec.stream ); - gcry_free( outctrl.pub.fname ); - gcry_free( outctrl.pub.newfname ); - gcry_free( outctrl.sec.fname ); - gcry_free( outctrl.sec.newfname ); + m_free( outctrl.pub.fname ); + m_free( outctrl.pub.newfname ); + m_free( outctrl.sec.fname ); + m_free( outctrl.sec.newfname ); } release_parameter_list( para ); @@ -1504,6 +1960,7 @@ generate_keypair( const char *fname ) DEK *dek; STRING2KEY *s2k; int algo; + unsigned int use; int both = 0; u32 expire; struct para_data_s *para = NULL; @@ -1517,50 +1974,61 @@ generate_keypair( const char *fname ) return; } - algo = ask_algo( 0 ); + algo = ask_algo( 0, &use ); if( !algo ) { /* default: DSA with ElG subkey of the specified size */ both = 1; - r = gcry_xcalloc( 1, sizeof *r + 20 ); + r = m_alloc_clear( sizeof *r + 20 ); r->key = pKEYTYPE; - sprintf( r->u.value, "%d", GCRY_PK_DSA ); + sprintf( r->u.value, "%d", PUBKEY_ALGO_DSA ); r->next = para; para = r; tty_printf(_("DSA keypair will have 1024 bits.\n")); - r = gcry_xcalloc( 1, sizeof *r + 20 ); + r = m_alloc_clear( sizeof *r + 20 ); r->key = pKEYLENGTH; strcpy( r->u.value, "1024" ); r->next = para; para = r; - algo = GCRY_PK_ELG_E; - r = gcry_xcalloc( 1, sizeof *r + 20 ); + algo = PUBKEY_ALGO_ELGAMAL_E; + r = m_alloc_clear( sizeof *r + 20 ); r->key = pSUBKEYTYPE; sprintf( r->u.value, "%d", algo ); r->next = para; para = r; } else { - r = gcry_xcalloc( 1, sizeof *r + 20 ); + r = m_alloc_clear( sizeof *r + 20 ); r->key = pKEYTYPE; sprintf( r->u.value, "%d", algo ); r->next = para; para = r; + + if (use) { + r = m_alloc_clear( sizeof *r + 20 ); + r->key = pKEYUSAGE; + sprintf( r->u.value, "%s%s", + (use & PUBKEY_USAGE_SIG)? "sign ":"", + (use & PUBKEY_USAGE_ENC)? "encrypt ":"" ); + r->next = para; + para = r; + } + } nbits = ask_keysize( algo ); - r = gcry_xcalloc( 1, sizeof *r + 20 ); + r = m_alloc_clear( sizeof *r + 20 ); r->key = both? pSUBKEYLENGTH : pKEYLENGTH; sprintf( r->u.value, "%u", nbits); r->next = para; para = r; - expire = ask_expire_interval(); - r = gcry_xcalloc( 1, sizeof *r + 20 ); + expire = ask_expire_interval(0); + r = m_alloc_clear( sizeof *r + 20 ); r->key = pKEYEXPIRE; r->u.expire = expire; r->next = para; para = r; - r = gcry_xcalloc( 1, sizeof *r + 20 ); + r = m_alloc_clear( sizeof *r + 20 ); r->key = pSUBKEYEXPIRE; r->u.expire = expire; r->next = para; @@ -1572,7 +2040,7 @@ generate_keypair( const char *fname ) release_parameter_list( para ); return; } - r = gcry_xcalloc( 1, sizeof *r + strlen(uid) ); + r = m_alloc_clear( sizeof *r + strlen(uid) ); r->key = pUSERID; strcpy( r->u.value, uid ); r->next = para; @@ -1580,12 +2048,12 @@ generate_keypair( const char *fname ) dek = ask_passphrase( &s2k ); if( dek ) { - r = gcry_xcalloc( 1, sizeof *r ); + r = m_alloc_clear( sizeof *r ); r->key = pPASSPHRASE_DEK; r->u.dek = dek; r->next = para; para = r; - r = gcry_xcalloc( 1, sizeof *r ); + r = m_alloc_clear( sizeof *r ); r->key = pPASSPHRASE_S2K; r->u.s2k = s2k; r->next = para; @@ -1601,13 +2069,13 @@ static void do_generate_keypair( struct para_data_s *para, struct output_control_s *outctrl ) { - char *pub_fname = NULL; - char *sec_fname = NULL; KBNODE pub_root = NULL; KBNODE sec_root = NULL; PKT_secret_key *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"); @@ -1619,7 +2087,7 @@ do_generate_keypair( struct para_data_s *para, if( outctrl->pub.newfname ) { iobuf_close(outctrl->pub.stream); outctrl->pub.stream = NULL; - gcry_free( outctrl->pub.fname ); + m_free( outctrl->pub.fname ); outctrl->pub.fname = outctrl->pub.newfname; outctrl->pub.newfname = NULL; @@ -1638,7 +2106,7 @@ do_generate_keypair( struct para_data_s *para, if( outctrl->sec.newfname ) { iobuf_close(outctrl->sec.stream); outctrl->sec.stream = NULL; - gcry_free( outctrl->sec.fname ); + m_free( outctrl->sec.fname ); outctrl->sec.fname = outctrl->sec.newfname; outctrl->sec.newfname = NULL; @@ -1654,20 +2122,14 @@ do_generate_keypair( struct para_data_s *para, &outctrl->sec.afx ); } } - pub_fname = outctrl->pub.fname; /* only for info output */ - sec_fname = outctrl->sec.fname; /* only for info output */ assert( outctrl->pub.stream ); assert( outctrl->sec.stream ); - } - else { - pub_fname = get_writable_keyblock_file( 0 ); - sec_fname = get_writable_keyblock_file( 1 ); + if( opt.verbose ) { + log_info(_("writing public key to `%s'\n"), outctrl->pub.fname ); + log_info(_("writing secret key to `%s'\n"), outctrl->sec.fname ); + } } - if( opt.verbose ) { - log_info(_("writing public key to `%s'\n"), pub_fname ); - log_info(_("writing secret key to `%s'\n"), 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 @@ -1684,14 +2146,24 @@ do_generate_keypair( struct para_data_s *para, get_parameter_s2k( para, pPASSPHRASE_S2K ), &sk, get_parameter_u32( para, pKEYEXPIRE ) ); + + if(!rc && (revkey=get_parameter_revkey(para,pREVOKER))) + { + rc=write_direct_sig(pub_root,pub_root,sk,revkey); + if(!rc) + write_direct_sig(sec_root,pub_root,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_selfsig(pub_root, pub_root, sk); + rc = write_selfsig(pub_root, pub_root, sk, + get_parameter_uint (para, pKEYUSAGE)); if( !rc ) - rc = write_selfsig(sec_root, pub_root, sk); + rc = write_selfsig(sec_root, pub_root, sk, + get_parameter_uint (para, pKEYUSAGE)); } if( get_parameter( para, pSUBKEYTYPE ) ) { @@ -1703,64 +2175,90 @@ do_generate_keypair( struct para_data_s *para, NULL, get_parameter_u32( para, pSUBKEYEXPIRE ) ); if( !rc ) - rc = write_keybinding(pub_root, pub_root, sk); + rc = write_keybinding(pub_root, pub_root, sk, + get_parameter_uint (para, pSUBKEYUSAGE)); if( !rc ) - rc = write_keybinding(sec_root, pub_root, sk); + rc = write_keybinding(sec_root, pub_root, 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", gpg_errstr(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", gpg_errstr(rc) ); + log_error("can't write secret key: %s\n", g10_errstr(rc) ); } } else if( !rc ) { /* write to the standard keyrings */ - KBPOS pub_kbpos; - KBPOS sec_kbpos; - int rc1 = -1; - int rc2 = -1; - - /* we can now write the certificates */ - if( get_keyblock_handle( pub_fname, 0, &pub_kbpos ) ) { - if( add_keyblock_resource( pub_fname, 1, 0 ) ) { - log_error("can add keyblock file `%s'\n", pub_fname ); - rc = GPGERR_CREATE_FILE; - } - else if( get_keyblock_handle( pub_fname, 0, &pub_kbpos ) ) { - log_error("can get keyblock handle for `%s'\n", pub_fname ); - rc = GPGERR_CREATE_FILE; - } - } - if( rc ) - ; - else if( get_keyblock_handle( sec_fname, 1, &sec_kbpos ) ) { - if( add_keyblock_resource( sec_fname, 1, 1 ) ) { - log_error("can add keyblock file `%s'\n", sec_fname ); - rc = GPGERR_CREATE_FILE; - } - else if( get_keyblock_handle( sec_fname, 1, &sec_kbpos ) ) { - log_error("can get keyblock handle for `%s'\n", sec_fname ); - rc = GPGERR_CREATE_FILE; - } - } + 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)); + 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; + + 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(_("key marked as ultimately trusted.\n") ); + tty_printf("\n"); + list_keyblock(pub_root,0,1,NULL); + } + - if( rc ) - ; - else if( (rc=insert_keyblock( pub_root )) ) - log_error("can't write public key: %s\n", gpg_errstr(rc) ); - else if( (rc=insert_keyblock( sec_root )) ) - log_error("can't write secret key: %s\n", gpg_errstr(rc) ); - else { - if( !opt.batch ) - tty_printf(_("public and secret key created and signed.\n") ); if( !opt.batch - && get_parameter_algo( para, pKEYTYPE ) == GCRY_PK_DSA + && ( 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 " @@ -1769,23 +2267,21 @@ do_generate_keypair( struct para_data_s *para, "secondary key for this purpose.\n") ); } } - } if( rc ) { if( opt.batch ) - log_error("key generation failed: %s\n", gpg_errstr(rc) ); + log_error("key generation failed: %s\n", g10_errstr(rc) ); else - tty_printf(_("Key generation failed: %s\n"), gpg_errstr(rc) ); + tty_printf(_("Key generation failed: %s\n"), g10_errstr(rc) ); + } + else { + write_status_text (STATUS_KEY_CREATED, did_sub? "B":"P"); } release_kbnode( pub_root ); release_kbnode( sec_root ); if( sk ) /* the unprotected secret key */ free_secret_key(sk); - if( !outctrl->use_files ) { - gcry_free(pub_fname); - gcry_free(sec_fname); - } } @@ -1800,6 +2296,7 @@ generate_subkeypair( KBNODE pub_keyblock, KBNODE sec_keyblock ) KBNODE node; PKT_secret_key *sk = NULL; /* this is the primary sk */ int algo; + unsigned int use; u32 expire; unsigned nbits; char *passphrase = NULL; @@ -1825,16 +2322,21 @@ generate_subkeypair( KBNODE pub_keyblock, KBNODE sec_keyblock ) : _("key has been created %lu seconds " "in future (time warp or clock problem)\n"), d ); if( !opt.ignore_time_conflict ) { - rc = GPGERR_TIME_CONFLICT; + rc = G10ERR_TIME_CONFLICT; goto leave; } } + if (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( sk ) ) { case -1: - rc = GPGERR_PUBKEY_ALGO; + rc = G10ERR_PUBKEY_ALGO; break; case 0: tty_printf("This key is not protected.\n"); @@ -1850,37 +2352,39 @@ generate_subkeypair( KBNODE pub_keyblock, KBNODE sec_keyblock ) goto leave; - algo = ask_algo( 1 ); + algo = ask_algo( 1, &use ); assert(algo); nbits = ask_keysize( algo ); - expire = ask_expire_interval(); + expire = ask_expire_interval(0); if( !cpr_enabled() && !cpr_get_answer_is_yes("keygen.sub.okay", _("Really create? ") ) ) goto leave; if( passphrase ) { - s2k = gcry_xmalloc_secure( sizeof *s2k ); + s2k = m_alloc_secure( sizeof *s2k ); s2k->mode = opt.s2k_mode; s2k->hash_algo = opt.s2k_digest_algo; set_next_passphrase( passphrase ); - dek = passphrase_to_dek( NULL, 0, opt.s2k_cipher_algo, s2k, 2 ); + dek = passphrase_to_dek( NULL, 0, opt.s2k_cipher_algo, s2k, 2, NULL ); } rc = do_create( algo, nbits, pub_keyblock, sec_keyblock, dek, s2k, NULL, expire ); if( !rc ) - rc = write_keybinding(pub_keyblock, pub_keyblock, sk); - if( !rc ) - rc = write_keybinding(sec_keyblock, pub_keyblock, sk); + rc = write_keybinding(pub_keyblock, pub_keyblock, sk, use); if( !rc ) + rc = write_keybinding(sec_keyblock, pub_keyblock, sk, use); + if( !rc ) { okay = 1; + write_status_text (STATUS_KEY_CREATED, "S"); + } leave: if( rc ) - log_error(_("Key generation failed: %s\n"), gpg_errstr(rc) ); - gcry_free( passphrase ); - gcry_free( dek ); - gcry_free( s2k ); + log_error(_("Key generation failed: %s\n"), g10_errstr(rc) ); + m_free( passphrase ); + m_free( dek ); + m_free( s2k ); if( sk ) /* release the copy of the (now unprotected) secret key */ free_secret_key(sk); set_next_passphrase( NULL ); @@ -1897,10 +2401,9 @@ write_keyblock( IOBUF out, KBNODE node ) int rc = build_packet( out, node->pkt ); if( rc ) { log_error("build_packet(%d) failed: %s\n", - node->pkt->pkttype, gpg_errstr(rc) ); - return GPGERR_WRITE_FILE; + node->pkt->pkttype, g10_errstr(rc) ); + return G10ERR_WRITE_FILE; } } return 0; } - diff --git a/g10/keyid.c b/g10/keyid.c index 0269a6bb0..43e531e3e 100644 --- a/g10/keyid.c +++ b/g10/keyid.c @@ -1,5 +1,5 @@ -/* keyid.c - jeyid and fingerprint handling - * Copyright (C) 1998, 1999, 2000 Free Software Foundation, Inc. +/* keyid.c - key ID and fingerprint handling + * Copyright (C) 1998, 1999, 2000, 2001 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -25,12 +25,11 @@ #include <errno.h> #include <time.h> #include <assert.h> - -#include <gcrypt.h> #include "util.h" #include "main.h" #include "packet.h" #include "options.h" +#include "mpi.h" #include "keydb.h" #include "i18n.h" @@ -39,58 +38,48 @@ int pubkey_letter( int algo ) { switch( algo ) { - case GCRY_PK_RSA: return 'R' ; - case GCRY_PK_RSA_E: return 'r' ; - case GCRY_PK_RSA_S: return 's' ; - case GCRY_PK_ELG_E: return 'g'; - case GCRY_PK_ELG: return 'G' ; - case GCRY_PK_DSA: return 'D' ; + 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 '?'; } } - -static GCRY_MD_HD +static MD_HANDLE do_fingerprint_md( PKT_public_key *pk ) { - GCRY_MD_HD md; - unsigned int n; - unsigned int nn[GNUPG_MAX_NPKEY]; - byte *pp[GNUPG_MAX_NPKEY]; + MD_HANDLE md; + unsigned n; + 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 ); - md = gcry_md_open( pk->version < 4 ? GCRY_MD_RMD160 : GCRY_MD_SHA1, 0); - if( !md ) - BUG(); + md = md_open( pk->version < 4 ? DIGEST_ALGO_RMD160 : DIGEST_ALGO_SHA1, 0); n = pk->version < 4 ? 8 : 6; for(i=0; i < npkey; i++ ) { - int rc; - size_t nbytes; - - rc = gcry_mpi_print( GCRYMPI_FMT_PGP, NULL, &nbytes, pk->pkey[i] ); - assert( !rc ); - /* fixme: we should try to allocate a buffer on the stack */ - pp[i] = gcry_xmalloc(nbytes); - rc = gcry_mpi_print( GCRYMPI_FMT_PGP, pp[i], &nbytes, pk->pkey[i] ); - assert( !rc ); - nn[i] = nbytes; - n += nn[i]; + nb[i] = mpi_get_nbits(pk->pkey[i]); + pp[i] = mpi_get_buffer( pk->pkey[i], nn+i, NULL ); + n += 2 + nn[i]; } - gcry_md_putc( md, 0x99 ); /* ctb */ - gcry_md_putc( md, n >> 8 ); /* 2 byte length header */ - gcry_md_putc( md, n ); + md_putc( md, 0x99 ); /* ctb */ + md_putc( md, n >> 8 ); /* 2 byte length header */ + md_putc( md, n ); if( pk->version < 4 ) - gcry_md_putc( md, 3 ); + md_putc( md, 3 ); else - gcry_md_putc( md, 4 ); + md_putc( md, 4 ); { u32 a = pk->timestamp; - gcry_md_putc( md, a >> 24 ); - gcry_md_putc( md, a >> 16 ); - gcry_md_putc( md, a >> 8 ); - gcry_md_putc( md, a ); + md_putc( md, a >> 24 ); + md_putc( md, a >> 16 ); + md_putc( md, a >> 8 ); + md_putc( md, a ); } if( pk->version < 4 ) { u16 a; @@ -99,20 +88,22 @@ do_fingerprint_md( PKT_public_key *pk ) a = (u16)((pk->expiredate - pk->timestamp) / 86400L); else a = 0; - gcry_md_putc( md, a >> 8 ); - gcry_md_putc( md, a ); + md_putc( md, a >> 8 ); + md_putc( md, a ); } - gcry_md_putc( md, pk->pubkey_algo ); + md_putc( md, pk->pubkey_algo ); for(i=0; i < npkey; i++ ) { - gcry_md_write( md, pp[i], nn[i] ); - gcry_free(pp[i]); + md_putc( md, nb[i]>>8); + md_putc( md, nb[i] ); + md_write( md, pp[i], nn[i] ); + m_free(pp[i]); } - gcry_md_final( md ); + md_final( md ); return md; } -static GCRY_MD_HD +static MD_HANDLE do_fingerprint_md_sk( PKT_secret_key *sk ) { PKT_public_key pk; @@ -130,30 +121,6 @@ do_fingerprint_md_sk( PKT_secret_key *sk ) } -static void -v3_keyid( MPI a, u32 *ki ) -{ - int rc; - byte *buffer; - size_t nbytes; - - rc = gcry_mpi_print( GCRYMPI_FMT_USG, NULL, &nbytes, a ); - assert( !rc ); - /* fixme: allocate it on the stack */ - buffer = gcry_xmalloc(nbytes); - rc = gcry_mpi_print( GCRYMPI_FMT_USG, buffer, &nbytes, a ); - assert( !rc ); - if( nbytes < 8 ) { /* oops */ - ki[0] = ki[1] = 0; - } - else { - memcpy( ki+0, buffer+nbytes-8, 4); - memcpy( ki+1, buffer+nbytes-4, 4); - } - gcry_free( buffer ); -} - - /**************** * 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. @@ -161,36 +128,28 @@ v3_keyid( MPI a, u32 *ki ) 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]; - } - else if( sk->version < 4 && is_RSA(sk->pubkey_algo) ) { - if( pubkey_get_npkey(sk->pubkey_algo) ) - v3_keyid( sk->skey[0], keyid ); /* take n */ - else - keyid[0] = keyid[1] = 0; - sk->keyid[0] = keyid[0]; - sk->keyid[1] = keyid[1]; + if( sk->version < 4 && is_RSA(sk->pubkey_algo) ) { + lowbits = pubkey_get_npkey(sk->pubkey_algo) ? + mpi_get_keyid( sk->skey[0], keyid ) : 0; /* take n */ } else { const byte *dp; - GCRY_MD_HD md; + MD_HANDLE md; md = do_fingerprint_md_sk(sk); - dp = gcry_md_read( md, 0 ); + 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] ; - gcry_md_close(md); - sk->keyid[0] = keyid[0]; - sk->keyid[1] = keyid[1]; + lowbits = keyid[1]; + md_close(md); } - return keyid[1]; + return lowbits; } @@ -201,6 +160,7 @@ keyid_from_sk( PKT_secret_key *sk, u32 *keyid ) u32 keyid_from_pk( PKT_public_key *pk, u32 *keyid ) { + u32 lowbits; u32 dummy_keyid[2]; if( !keyid ) @@ -209,28 +169,28 @@ keyid_from_pk( PKT_public_key *pk, u32 *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 && is_RSA(pk->pubkey_algo) ) { - if( pubkey_get_npkey(pk->pubkey_algo) ) - v3_keyid( pk->pkey[0], keyid ); /* from n */ - else - keyid[0] = keyid[1] = 0; + 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 { const byte *dp; - GCRY_MD_HD md; + MD_HANDLE md; md = do_fingerprint_md(pk); - dp = gcry_md_read( md, 0 ); + 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] ; - gcry_md_close(md); + lowbits = keyid[1]; + md_close(md); pk->keyid[0] = keyid[0]; pk->keyid[1] = keyid[1]; } - return keyid[1]; + return lowbits; } @@ -299,6 +259,21 @@ 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. @@ -308,67 +283,122 @@ const char * datestr_from_pk( PKT_public_key *pk ) { static char buffer[11+5]; - struct tm *tp; time_t atime = pk->timestamp; - tp = gmtime( &atime ); - sprintf(buffer,"%04d-%02d-%02d", 1900+tp->tm_year, tp->tm_mon+1, tp->tm_mday ); - return buffer; + return mk_datestr (buffer, atime); } const char * datestr_from_sk( PKT_secret_key *sk ) { static char buffer[11+5]; - struct tm *tp; time_t atime = sk->timestamp; - tp = gmtime( &atime ); - sprintf(buffer,"%04d-%02d-%02d", 1900+tp->tm_year, tp->tm_mon+1, tp->tm_mday ); - return buffer; + return mk_datestr (buffer, atime); } const char * datestr_from_sig( PKT_signature *sig ) { static char buffer[11+5]; - struct tm *tp; time_t atime = sig->timestamp; - tp = gmtime( &atime ); - sprintf(buffer,"%04d-%02d-%02d", 1900+tp->tm_year, tp->tm_mon+1, tp->tm_mday ); - return buffer; + return mk_datestr (buffer, atime); } - const char * expirestr_from_pk( PKT_public_key *pk ) { static char buffer[11+5]; - struct tm *tp; time_t atime; if( !pk->expiredate ) return _("never "); atime = pk->expiredate; - tp = gmtime( &atime ); - sprintf(buffer,"%04d-%02d-%02d", 1900+tp->tm_year, tp->tm_mon+1, tp->tm_mday ); - return buffer; + return mk_datestr (buffer, atime); } const char * expirestr_from_sk( PKT_secret_key *sk ) { static char buffer[11+5]; - struct tm *tp; time_t atime; if( !sk->expiredate ) return _("never "); atime = sk->expiredate; - tp = gmtime( &atime ); - sprintf(buffer,"%04d-%02d-%02d", 1900+tp->tm_year, tp->tm_mon+1, tp->tm_mday ); - return buffer; + 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 * +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); } @@ -381,184 +411,92 @@ expirestr_from_sk( PKT_secret_key *sk ) byte * fingerprint_from_pk( PKT_public_key *pk, byte *array, size_t *ret_len ) { - byte *buf; - const char *dp; + byte *p, *buf; + const byte *dp; size_t len; + unsigned int n; if( pk->version < 4 && is_RSA(pk->pubkey_algo) ) { /* RSA in version 3 packets is special */ - GCRY_MD_HD md; + MD_HANDLE md; - md = gcry_md_open( GCRY_MD_MD5, 0); - if( !md ) - BUG(); + md = md_open( DIGEST_ALGO_MD5, 0); if( pubkey_get_npkey( pk->pubkey_algo ) > 1 ) { - int rc; - size_t nbytes; - - rc = gcry_mpi_print( GCRYMPI_FMT_USG, NULL, &nbytes, pk->pkey[0] ); - assert( !rc ); - /* fixme: allocate it on the stack */ - buf = gcry_xmalloc(nbytes); - rc = gcry_mpi_print( GCRYMPI_FMT_USG, buf, &nbytes, pk->pkey[0] ); - assert( !rc ); - gcry_md_write( md, buf, nbytes ); - gcry_free(buf); - rc = gcry_mpi_print( GCRYMPI_FMT_USG, NULL, &nbytes, pk->pkey[1] ); - assert( !rc ); - /* fixme: allocate it on the stack */ - buf = gcry_xmalloc(nbytes); - rc = gcry_mpi_print( GCRYMPI_FMT_USG, buf, &nbytes, pk->pkey[1] ); - assert( !rc ); - gcry_md_write( md, buf, nbytes ); - gcry_free(buf); + p = buf = mpi_get_buffer( pk->pkey[0], &n, NULL ); + md_write( md, p, n ); + m_free(buf); + p = buf = mpi_get_buffer( pk->pkey[1], &n, NULL ); + md_write( md, p, n ); + m_free(buf); } - gcry_md_final(md); + md_final(md); if( !array ) - array = gcry_xmalloc( 16 ); + array = m_alloc( 16 ); len = 16; - memcpy(array, gcry_md_read(md, GCRY_MD_MD5), 16 ); - gcry_md_close(md); + memcpy(array, md_read(md, DIGEST_ALGO_MD5), 16 ); + md_close(md); } else { - GCRY_MD_HD md; + MD_HANDLE md; md = do_fingerprint_md(pk); - dp = gcry_md_read( md, 0 ); - len = gcry_md_get_algo_dlen( gcry_md_get_algo( md ) ); + dp = md_read( md, 0 ); + len = md_digest_length( md_get_algo( md ) ); assert( len <= MAX_FINGERPRINT_LEN ); if( !array ) - array = gcry_xmalloc( len ); + array = m_alloc( len ); memcpy(array, dp, len ); - gcry_md_close(md); + 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; } - -/* Create a unified fingerprint, that is a printable fingerprint along - * wth some other information suitable to passto get_pubkye_byname. - * Pass NULL for buffer to let this function allocate the buffer. - * This function will truncate the buffer in a way that a valid C string - * is returnd (unless bufsize is 0) - * Returns: Supplied buffer or newly allocated buffer - */ -char * -unified_fingerprint_from_pk( PKT_public_key *pk, - char *buffer, size_t bufsize ) -{ - byte fpr[MAX_FINGERPRINT_LEN]; - size_t fprlen; - int i; - - fingerprint_from_pk( pk, fpr, &fprlen ); - if ( !buffer ) { - bufsize = 1+fprlen*2+1+4+1+1; - buffer = gcry_xmalloc( bufsize ); - } - if ( bufsize < 1+fprlen*2+1+4+1+1 ) { - /* Hmmm, that should be sufficiend also not very nice */ - if ( bufsize ) - *buffer = 0; - return buffer; - } - *buffer = ':'; - for (i=0; i < fprlen; i++ ) - sprintf( buffer+1+i*2, "%02X", fpr[i] ); - sprintf( buffer+1+i*2, ":%d:", (pk->pubkey_algo & 0xff) ); - return buffer; -} - byte * fingerprint_from_sk( PKT_secret_key *sk, byte *array, size_t *ret_len ) { - byte *buf; + byte *p, *buf; const char *dp; size_t len; + unsigned n; if( sk->version < 4 && is_RSA(sk->pubkey_algo) ) { /* RSA in version 3 packets is special */ - GCRY_MD_HD md; + MD_HANDLE md; - md = gcry_md_open( GCRY_MD_MD5, 0); - if( !md ) - BUG(); + md = md_open( DIGEST_ALGO_MD5, 0); if( pubkey_get_npkey( sk->pubkey_algo ) > 1 ) { - int rc; - size_t nbytes; - - /* FIXME: Why is the hash sequence for secret keys different */ - rc = gcry_mpi_print( GCRYMPI_FMT_USG, NULL, &nbytes, sk->skey[1] ); - assert( !rc ); - /* fixme: allocate it on the stack */ - buf = gcry_xmalloc(nbytes); - rc = gcry_mpi_print( GCRYMPI_FMT_USG, buf, &nbytes, sk->skey[1] ); - assert( !rc ); - gcry_md_write( md, buf, nbytes ); - gcry_free(buf); - rc = gcry_mpi_print( GCRYMPI_FMT_USG, NULL, &nbytes, sk->skey[0] ); - assert( !rc ); - /* fixme: allocate it on the stack */ - buf = gcry_xmalloc(nbytes); - rc = gcry_mpi_print( GCRYMPI_FMT_USG, buf, &nbytes, sk->skey[0] ); - assert( !rc ); - gcry_md_write( md, buf, nbytes ); - gcry_free(buf); + p = buf = mpi_get_buffer( sk->skey[0], &n, NULL ); + md_write( md, p, n ); + m_free(buf); + p = buf = mpi_get_buffer( sk->skey[1], &n, NULL ); + md_write( md, p, n ); + m_free(buf); } - gcry_md_final(md); + md_final(md); if( !array ) - array = gcry_xmalloc( 16 ); + array = m_alloc( 16 ); len = 16; - memcpy(array, gcry_md_read(md, GCRY_MD_MD5), 16 ); - gcry_md_close(md); + memcpy(array, md_read(md, DIGEST_ALGO_MD5), 16 ); + md_close(md); } else { - GCRY_MD_HD md; + MD_HANDLE md; md = do_fingerprint_md_sk(sk); - dp = gcry_md_read( md, 0 ); - len = gcry_md_get_algo_dlen( gcry_md_get_algo( md ) ); + dp = md_read( md, 0 ); + len = md_digest_length( md_get_algo( md ) ); assert( len <= MAX_FINGERPRINT_LEN ); if( !array ) - array = gcry_xmalloc( len ); + array = m_alloc( len ); memcpy(array, dp, len ); - gcry_md_close(md); + md_close(md); } *ret_len = len; return array; } -char * -unified_fingerprint_from_sk( PKT_secret_key *sk, - char *buffer, size_t bufsize ) -{ - byte fpr[MAX_FINGERPRINT_LEN]; - size_t fprlen; - int i; - - fingerprint_from_sk( sk, fpr, &fprlen ); - if ( !buffer ) { - bufsize = 1+fprlen*2+1+4+1+1; - buffer = gcry_xmalloc( bufsize ); - } - if ( bufsize < 1+fprlen*2+1+4+1+1 ) { - /* Hmmm, that should be sufficiend also not very nice */ - if ( bufsize ) - *buffer = 0; - return buffer; - } - *buffer = ':'; - for (i=0; i < fprlen; i++ ) - sprintf( buffer+1+i*2, "%02X", fpr[i] ); - sprintf( buffer+1+i*2, ":%d:", (sk->pubkey_algo & 0xff) ); - return buffer; -} - - - - - - diff --git a/g10/keylist.c b/g10/keylist.c index 89691873a..e226ee071 100644 --- a/g10/keylist.c +++ b/g10/keylist.c @@ -1,5 +1,5 @@ /* keylist.c - * Copyright (C) 1998, 1999, 2000 Free Software Foundation, Inc. + * Copyright (C) 1998, 1999, 2000, 2001, 2002 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -29,17 +29,26 @@ #include "packet.h" #include "errors.h" #include "keydb.h" -#include <gcrypt.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 list_keyblock( KBNODE keyblock, int secret ); -static void fingerprint( PKT_public_key *pk, PKT_secret_key *sk ); +struct sig_stats +{ + int inv_sigs; + int no_key; + int oth_err; +}; + +static FILE *attrib_fp=NULL; /**************** * List the keys @@ -63,123 +72,146 @@ secret_key_list( STRLIST list ) list_one( list, 1 ); } +void +show_policy_url(PKT_signature *sig,int indent) +{ + const byte *p; + size_t len; + int seq=0,crit; + + while((p=enum_sig_subpkt(sig->hashed,SIGSUBPKT_POLICY,&len,&seq,&crit))) + { + int i; + + for(i=0;i<indent;i++) + putchar(' '); + + /* This isn't UTF8 as it is a URL(?) */ + if(crit) + printf(_("Critical signature policy: ")); + else + printf(_("Signature policy: ")); + print_string(stdout,p,len,0); + printf("\n"); + } +} + +void +show_notation(PKT_signature *sig,int indent) +{ + const byte *p; + size_t len; + int seq=0,crit; + + /* There may be multiple notations in the same sig. */ + + while((p=enum_sig_subpkt(sig->hashed,SIGSUBPKT_NOTATION,&len,&seq,&crit))) + if(len>=8) + { + int n1,n2,i; + + 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")); + return; + } + + for(i=0;i<indent;i++) + putchar(' '); + + /* This is UTF8 */ + if(crit) + printf(_("Critical signature notation: ")); + else + printf(_("Signature notation: ")); + print_utf8_string(stdout,p+8,n1); + printf("="); + + if(*p&0x80) + print_utf8_string(stdout,p+8+n1,n2); + else + printf("[ %s ]",_("not human readable")); + + printf("\n"); + } + else + log_info(_("WARNING: invalid notation data found\n")); +} + +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 ) { - KBPOS kbpos; + KEYDB_HANDLE hd; KBNODE keyblock = NULL; int rc=0; - int lastresno; + const char *lastresname, *resname; + struct sig_stats stats; + + memset(&stats,0,sizeof(stats)); - rc = enum_keyblocks_begin( &kbpos, secret ); + hd = keydb_new (secret); + if (!hd) + rc = G10ERR_GENERAL; + else + rc = keydb_search_first (hd); if( rc ) { if( rc != -1 ) - log_error("enum_keyblocks(open) failed: %s\n", gpg_errstr(rc) ); + log_error("keydb_search_first failed: %s\n", g10_errstr(rc) ); goto leave; } - lastresno = -1; - while( !(rc = enum_keyblocks_next( kbpos, 1, &keyblock )) ) { - if( 1 /*lastresno != kbpos.resno FIXME!!! */ ) { - const char *s = "foo" /*keyblock_resource_name( &kbpos ) */; + lastresname = NULL; + do { + rc = keydb_get_keyblock (hd, &keyblock); + if (rc) { + log_error ("keydb_get_keyblock failed: %s\n", g10_errstr(rc)); + goto leave; + } + resname = keydb_get_resource_name (hd); + if (lastresname != resname ) { int i; - /* FIXME lastresno = kbpos.resno*/ - printf("%s\n", s ); - for(i=strlen(s); i; 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 ); - release_kbnode( keyblock ); keyblock = NULL; - } - + 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("enum_keyblocks(read) failed: %s\n", gpg_errstr(rc)); + log_error ("keydb_search_next failed: %s\n", g10_errstr(rc)); - leave: - enum_keyblocks_end( kbpos ); - release_kbnode( keyblock ); -} + if(opt.check_sigs && !opt.with_colons) + print_signature_stats(&stats); - -/**************** - * Check whether the user ID at NODE is valid; that is it has a - * valid self-signature but no later valid revocation. - * Caller has to pass the keyID of the primary in mainkey. - * Returns: NULL = valid - * string with the reason why it is invalid - */ -static const char * -is_uid_valid ( KBNODE keyblock, KBNODE uidnode, u32 *mainkid ) -{ - KBNODE node; - PKT_signature *selfsig = NULL; /* the latest valid self signature */ - - /* The key signature verify function can's handle secret keys yet and - * becuase we are not sure whether the duplication of user IDs and - * self-signatures should be kept on secret keys we are not going to fix - * it there. */ - if ( keyblock->pkt->pkttype == PKT_SECRET_KEY ) - return NULL; - - assert ( uidnode->pkt->pkttype == PKT_USER_ID - || uidnode->pkt->pkttype == PKT_PHOTO_ID ); - - /* first find out about the latest valid self-signature */ - for ( node = uidnode->next; node; node = node->next ) { - PKT_signature *sig; - - if ( node->pkt->pkttype == PKT_USER_ID - || node->pkt->pkttype == PKT_PHOTO_ID - || node->pkt->pkttype == PKT_PUBLIC_SUBKEY - || node->pkt->pkttype == PKT_SECRET_SUBKEY ) - break; - if ( node->pkt->pkttype != PKT_SIGNATURE ) - continue; - sig = node->pkt->pkt.signature; - if ( mainkid[0] != sig->keyid[0] || mainkid[1] != sig->keyid[1] ) - continue; /* we only care about self-signatures for now */ - - if ( (sig->sig_class&~3) == 0x10 ) { /* regular self signature */ - if ( !check_key_signature( keyblock, node, NULL ) ) { - if ( !selfsig ) - selfsig = sig; /* use the first valid sig */ - else if ( sig->timestamp > selfsig->timestamp - && sig->sig_class >= selfsig->sig_class ) - selfsig = sig; /* but this one is newer */ - } - } - } - - if ( !selfsig ) - return _("invalid"); /* no valid self signature */ - - /* watch out for a newer revocation */ - for ( node = uidnode->next; node; node = node->next ) { - PKT_signature *sig; - - if ( node->pkt->pkttype == PKT_USER_ID - || node->pkt->pkttype == PKT_PHOTO_ID - || node->pkt->pkttype == PKT_PUBLIC_SUBKEY - || node->pkt->pkttype == PKT_SECRET_SUBKEY ) - break; - if ( node->pkt->pkttype != PKT_SIGNATURE ) - continue; - sig = node->pkt->pkt.signature; - if ( mainkid[0] != sig->keyid[0] || mainkid[1] != sig->keyid[1] ) - continue; /* we only care about self-signatures for now */ - - if ( sig->sig_class == 0x30 - && sig->timestamp >= selfsig->timestamp ) { - if ( !check_key_signature( keyblock, node, NULL ) ) - return _("revoked"); - } - } - - return NULL; /* UID is valid */ + leave: + release_kbnode (keyblock); + keydb_release (hd); } @@ -189,17 +221,38 @@ list_one( STRLIST names, int secret ) int rc = 0; KBNODE keyblock = NULL; GETKEY_CTX ctx; + const char *resname; + char *keyring_str = N_("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", gpg_errstr(rc) ); + log_error("error reading key: %s\n", g10_errstr(rc) ); get_seckey_end( ctx ); return; } do { - merge_keys_and_selfsig( keyblock ); - list_keyblock( keyblock, 1 ); + if (opt.show_keyring) { + 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, &stats ); release_kbnode( keyblock ); } while( !get_seckey_next( ctx, NULL, &keyblock ) ); get_seckey_end( ctx ); @@ -207,17 +260,27 @@ list_one( STRLIST names, int secret ) else { rc = get_pubkey_bynames( &ctx, NULL, names, &keyblock ); if( rc ) { - log_error("error reading key: %s\n", gpg_errstr(rc) ); + log_error("error reading key: %s\n", g10_errstr(rc) ); get_pubkey_end( ctx ); return; } do { - merge_keys_and_selfsig( keyblock ); - list_keyblock( keyblock, 0 ); + if (opt.show_keyring) { + 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 @@ -227,16 +290,103 @@ print_key_data( PKT_public_key *pk, u32 *keyid ) int i; for(i=0; i < n; i++ ) { - printf("pkd:%d:%u:", i, gcry_mpi_get_nbits( pk->pkey[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) +{ + unsigned int use = pk? pk->pubkey_usage : sk->pubkey_usage; + + if ( use & PUBKEY_USAGE_ENC ) { + putchar ('e'); + } + if ( use & PUBKEY_USAGE_SIG ) { + putchar ('s'); + putchar ('c'); + } + if ( keyblock ) { /* figure our the usable capabilities */ + KBNODE k; + int enc=0, sign=0, cert=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_valid && !pk->is_revoked && !pk->has_expired ) { + if ( pk->pubkey_usage & PUBKEY_USAGE_ENC ) + enc = 1; + if ( pk->pubkey_usage & PUBKEY_USAGE_SIG ) + sign = cert = 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 ) { + if ( sk->pubkey_usage & PUBKEY_USAGE_ENC ) + enc = 1; + if ( sk->pubkey_usage & PUBKEY_USAGE_SIG ) + sign = cert = 1; + } + } + } + if (enc) + putchar ('E'); + if (sign) + putchar ('S'); + if (cert) + putchar ('C'); + } + putchar(':'); +} + +static void dump_attribs(const PKT_user_id *uid, + PKT_public_key *pk,PKT_secret_key *sk) +{ + int i; + + if(!attrib_fp) + BUG(); + + 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", + 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( KBNODE keyblock, int secret ) +list_keyblock_print ( KBNODE keyblock, int secret, int fpr, void *opaque ) { int rc = 0; KBNODE kbctx; @@ -245,8 +395,7 @@ list_keyblock( KBNODE keyblock, int secret ) PKT_secret_key *sk; u32 keyid[2]; int any=0; - int trustletter = 0; - int ulti_hack = 0; + struct sig_stats *stats=opaque; /* get the keyid from the keyblock */ node = find_kbnode( keyblock, secret? PKT_SECRET_KEY : PKT_PUBLIC_KEY ); @@ -260,16 +409,7 @@ list_keyblock( KBNODE keyblock, int secret ) pk = NULL; sk = node->pkt->pkt.secret_key; keyid_from_sk( sk, keyid ); - if( opt.with_colons ) - printf("sec:u:%u:%d:%08lX%08lX:%s:%s:::", - nbits_from_sk( sk ), - sk->pubkey_algo, - (ulong)keyid[0],(ulong)keyid[1], - datestr_from_sk( sk ), - sk->expiredate? strtimestamp(sk->expiredate):"" - /* fixme: add LID here */ ); - else - printf("sec %4u%c/%08lX %s ", nbits_from_sk( sk ), + printf("sec %4u%c/%08lX %s ", nbits_from_sk( sk ), pubkey_letter( sk->pubkey_algo ), (ulong)keyid[1], datestr_from_sk( sk ) ); @@ -278,80 +418,309 @@ list_keyblock( KBNODE keyblock, int secret ) pk = node->pkt->pkt.public_key; sk = NULL; keyid_from_pk( pk, keyid ); - if( opt.with_colons ) { - if ( opt.fast_list_mode ) { - fputs( "pub::", stdout ); - trustletter = 0; + printf("pub %4u%c/%08lX %s ", nbits_from_pk( pk ), + pubkey_letter( pk->pubkey_algo ), + (ulong)keyid[1], + datestr_from_pk( pk ) ); + } + + for( kbctx=NULL; (node=walk_kbnode( keyblock, &kbctx, 0)) ; ) { + if( node->pkt->pkttype == PKT_USER_ID && !opt.fast_list_mode ) { + if(attrib_fp && node->pkt->pkt.user_id->attrib_data!=NULL) + dump_attribs(node->pkt->pkt.user_id,pk,sk); + /* don't list revoked UIDS unless we are in verbose mode and + * signature listing has not been requested */ + if ( !opt.verbose && !opt.list_sigs + && node->pkt->pkt.user_id->is_revoked ) + continue; + + if( any ) + printf("uid%*s", 28, ""); + + if ( node->pkt->pkt.user_id->is_revoked ) + fputs ("[revoked] ", stdout); + if ( node->pkt->pkt.user_id->is_expired ) + fputs ("[expired] ", stdout); + print_utf8_string( stdout, node->pkt->pkt.user_id->name, + node->pkt->pkt.user_id->len ); + putchar('\n'); + if( !any ) { + if( fpr ) + print_fingerprint( pk, sk, 0 ); + if( opt.with_key_data ) + print_key_data( pk, keyid ); + any = 1; + } + + if(opt.show_photos && node->pkt->pkt.user_id->attribs!=NULL) + show_photos(node->pkt->pkt.user_id->attribs, + node->pkt->pkt.user_id->numattribs,pk,sk); + } + else if( node->pkt->pkttype == PKT_PUBLIC_SUBKEY ) { + u32 keyid2[2]; + PKT_public_key *pk2 = node->pkt->pkt.public_key; + + if( !any ) { + putchar('\n'); + if( fpr ) + print_fingerprint( pk, sk, 0 ); /* of the main key */ + any = 1; + } + + keyid_from_pk( pk2, keyid2 ); + printf("sub %4u%c/%08lX %s", nbits_from_pk( pk2 ), + pubkey_letter( pk2->pubkey_algo ), + (ulong)keyid2[1], + datestr_from_pk( pk2 ) ); + if( pk2->expiredate ) { + printf(_(" [expires: %s]"), expirestr_from_pk( pk2 ) ); + } + putchar('\n'); + if( fpr > 1 ) + print_fingerprint( pk2, NULL, 0 ); + if( opt.with_key_data ) + print_key_data( pk2, keyid2 ); + } + else if( node->pkt->pkttype == PKT_SECRET_SUBKEY ) { + u32 keyid2[2]; + PKT_secret_key *sk2 = node->pkt->pkt.secret_key; + + if( !any ) { + putchar('\n'); + if( fpr ) + print_fingerprint( pk, sk, 0 ); /* of the main key */ + any = 1; + } + + keyid_from_sk( sk2, keyid2 ); + printf("ssb %4u%c/%08lX %s\n", nbits_from_sk( sk2 ), + pubkey_letter( sk2->pubkey_algo ), + (ulong)keyid2[1], + datestr_from_sk( sk2 ) ); + 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; + 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; + } } else { - trustletter = query_trust_info( pk, NULL ); - if( trustletter == 'u' ) - ulti_hack = 1; - printf("pub:%c:", trustletter ); + rc = 0; + sigrc = ' '; } - printf("%u:%d:%08lX%08lX:%s:%s:", + + if( !any ) { /* no user id, (maybe a revocation follows)*/ + /* Check if the pk is really revoked - there could be a + 0x20 sig packet there even if we are not revoked + (say, if a revocation key issued the packet, but the + revocation key isn't present to verify it.) */ + if( sig->sig_class == 0x20 && pk->is_revoked ) + puts("[revoked]"); + else if( sig->sig_class == 0x18 ) + puts("[key binding]"); + else if( sig->sig_class == 0x28 ) + puts("[subkey revoked]"); + else + 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 " + "[unexpected signature class 0x%02x]\n",sig->sig_class ); + continue; + } + + fputs( sigstr, stdout ); + printf("%c%c %c%c%c%c%c %08lX %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':' ', + (ulong)sig->keyid[1], datestr_from_sig(sig)); + 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 ); + m_free(p); + } + putchar('\n'); + + if(sig->flags.policy_url && opt.show_policy_url) + show_policy_url(sig,3); + + if(sig->flags.notation && opt.show_notation) + show_notation(sig,3); + + /* fixme: check or list other sigs here */ + } + } + putchar('\n'); +} + + +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; + + /* 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:%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 ); + trustletter = 0; + 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], - datestr_from_pk( pk ), - pk->expiredate? strtimestamp(pk->expiredate):"" ); - if( pk->local_id ) - printf("%lu", pk->local_id ); + colon_datestr_from_pk( pk ), + colon_strtime (pk->expiredate) ); + if( pk->local_id ) + printf("%lu", pk->local_id ); + putchar(':'); + if( !opt.fast_list_mode && !opt.no_expensive_trust_checks ) + putchar( get_ownertrust_info(pk) ); putchar(':'); - if( pk->local_id && !opt.fast_list_mode ) - putchar( get_ownertrust_info( pk->local_id ) ); - putchar(':'); - } - else - printf("pub %4u%c/%08lX %s ", nbits_from_pk( pk ), - pubkey_letter( pk->pubkey_algo ), - (ulong)keyid[1], - datestr_from_pk( pk ) ); + } + + if (opt.fixed_list_mode) { + /* do not merge the first uid with the primary key */ + putchar(':'); + putchar(':'); + print_capabilities (pk, sk, keyblock); + putchar('\n'); + if( fpr ) + print_fingerprint( pk, sk, 0 ); + if( opt.with_key_data ) + print_key_data( pk, keyid ); + any = 1; } + for( kbctx=NULL; (node=walk_kbnode( keyblock, &kbctx, 0)) ; ) { if( node->pkt->pkttype == PKT_USER_ID && !opt.fast_list_mode ) { + 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 ) { - if ( opt.with_colons ) { + char *str=node->pkt->pkt.user_id->attrib_data?"uat":"uid"; + if ( node->pkt->pkt.user_id->is_revoked ) + printf("%s:r::::::::",str); + else if ( node->pkt->pkt.user_id->is_expired ) + printf("%s:e::::::::",str); + else if ( opt.no_expensive_trust_checks ) { + printf("%s:::::::::",str); + } + else { byte namehash[20]; if( pk && !ulti_hack ) { - if( node->pkt->pkt.user_id->photo ) { - gcry_md_hash_buffer( GCRY_MD_RMD160, namehash, + if( node->pkt->pkt.user_id->attrib_data ) + rmd160_hash_buffer( namehash, + node->pkt->pkt.user_id->attrib_data, + node->pkt->pkt.user_id->attrib_len); + else + rmd160_hash_buffer( namehash, node->pkt->pkt.user_id->name, node->pkt->pkt.user_id->len ); - } - else { - gcry_md_hash_buffer( GCRY_MD_RMD160, namehash, - node->pkt->pkt.user_id->name, - node->pkt->pkt.user_id->len ); - } - trustletter = query_trust_info( pk, namehash ); + trustletter = get_validity_info( pk, namehash ); } else trustletter = 'u'; - printf("uid:%c::::::::", trustletter); - } - else - printf("uid%*s", 28, ""); - } - if( opt.with_colons ) { - print_string( stdout, node->pkt->pkt.user_id->name, - node->pkt->pkt.user_id->len, ':' ); - putchar(':'); + printf("%s:%c::::::::",str,trustletter); + } } - else { - const char *s = is_uid_valid ( keyblock, node, keyid ); - if ( s ) - printf ("[%s] ", s ); - print_utf8_string( stdout, node->pkt->pkt.user_id->name, - node->pkt->pkt.user_id->len ); - } - - putchar('\n'); - if( !any ) { - if( opt.fingerprint ) - fingerprint( pk, sk ); + if(node->pkt->pkt.user_id->attrib_data) + printf("%u %lu", + node->pkt->pkt.user_id->numattribs, + node->pkt->pkt.user_id->attrib_len); + else + print_string( stdout, node->pkt->pkt.user_id->name, + node->pkt->pkt.user_id->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, keyid ); any = 1; @@ -362,48 +731,46 @@ list_keyblock( KBNODE keyblock, int secret ) PKT_public_key *pk2 = node->pkt->pkt.public_key; if( !any ) { - putchar('\n'); - if( opt.fingerprint ) - fingerprint( pk, sk ); /* of the main key */ + 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 ); - if( opt.with_colons ) { - if ( opt.fast_list_mode ) { - fputs( "sub::", stdout ); - } - else { - printf("sub:%c:", trustletter ); - } - printf("%u:%d:%08lX%08lX:%s:%s:", + 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 { + printf("%c", trustletter ); + } + printf(":%u:%d:%08lX%08lX:%s:%s:", nbits_from_pk( pk2 ), pk2->pubkey_algo, (ulong)keyid2[0],(ulong)keyid2[1], - datestr_from_pk( pk2 ), - pk2->expiredate? strtimestamp(pk2->expiredate):"" + colon_datestr_from_pk( pk2 ), + colon_strtime (pk2->expiredate) /* fixme: add LID and ownertrust here */ ); - if( pk->local_id ) /* use the local_id of the main key??? */ - printf("%lu", pk->local_id ); - putchar(':'); - putchar(':'); - putchar('\n'); - } - else { - printf("sub %4u%c/%08lX %s", nbits_from_pk( pk2 ), - pubkey_letter( pk2->pubkey_algo ), - (ulong)keyid2[1], - datestr_from_pk( pk2 ) ); - if( pk2->expiredate ) { - printf(_(" [expires: %s]"), expirestr_from_pk( pk2 ) ); - } - putchar('\n'); - } - if( opt.fingerprint > 1 ) - fingerprint( pk2, NULL ); + if( pk->local_id ) /* use the local_id of the main key??? */ + printf("%lu", pk->local_id ); + putchar(':'); + putchar(':'); + putchar(':'); + putchar(':'); + print_capabilities (pk2, NULL, NULL); + putchar('\n'); + if( fpr > 1 ) + print_fingerprint( pk2, NULL, 0 ); if( opt.with_key_data ) print_key_data( pk2, keyid2 ); } @@ -412,47 +779,47 @@ list_keyblock( KBNODE keyblock, int secret ) PKT_secret_key *sk2 = node->pkt->pkt.secret_key; if( !any ) { + putchar(':'); + putchar(':'); + print_capabilities (pk, sk, keyblock); putchar('\n'); - if( opt.fingerprint ) - fingerprint( pk, sk ); /* of the main key */ + if( fpr ) + print_fingerprint( pk, sk, 0 ); /* of the main key */ any = 1; } keyid_from_sk( sk2, keyid2 ); - if( opt.with_colons ) - printf("ssb::%u:%d:%08lX%08lX:%s:%s:::\n", + printf("ssb::%u:%d:%08lX%08lX:%s:%s:::::", nbits_from_sk( sk2 ), sk2->pubkey_algo, (ulong)keyid2[0],(ulong)keyid2[1], - datestr_from_sk( sk2 ), - sk2->expiredate? strtimestamp(sk2->expiredate):"" - /* fixme: add LID */ - ); - else - printf("ssb %4u%c/%08lX %s\n", nbits_from_sk( sk2 ), - pubkey_letter( sk2->pubkey_algo ), - (ulong)keyid2[1], - datestr_from_sk( sk2 ) ); - if( opt.fingerprint > 1 ) - fingerprint( NULL, sk2 ); - + colon_datestr_from_sk( sk2 ), + colon_strtime (sk2->expiredate) + /* fixme: add LID */ ); + print_capabilities (NULL, sk2, NULL); + 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; - char *sigstr; + char *sigstr; if( !any ) { /* no user id, (maybe a revocation follows)*/ if( sig->sig_class == 0x20 ) - puts("[revoked]"); + fputs("[revoked]:", stdout); else if( sig->sig_class == 0x18 ) - puts("[key binding]"); + fputs("[key binding]:", stdout); else if( sig->sig_class == 0x28 ) - puts("[subkey revoked]"); - else - putchar('\n'); - if( opt.fingerprint ) - fingerprint( pk, sk ); + fputs("[subkey revoked]:", stdout); + else + putchar (':'); + putchar(':'); + print_capabilities (pk, sk, keyblock); + putchar('\n'); + if( fpr ) + print_fingerprint( pk, sk, 0 ); any=1; } @@ -464,11 +831,8 @@ list_keyblock( KBNODE keyblock, int secret ) else if( sig->sig_class == 0x18 ) sigstr = "sig"; else { - if( opt.with_colons ) - printf("sig::::::::::%02x:\n",sig->sig_class ); - else - printf("sig " - "[unexpected signature class 0x%02x]\n",sig->sig_class ); + printf ("sig::::::::::%02x%c:\n", + sig->sig_class, sig->flags.exportable?'x':'l'); continue; } if( opt.check_sigs ) { @@ -476,8 +840,9 @@ list_keyblock( KBNODE keyblock, int secret ) rc = check_key_signature( keyblock, node, NULL ); switch( rc ) { case 0: sigrc = '!'; break; - case GPGERR_BAD_SIGN: sigrc = '-'; break; - case GPGERR_NO_PUBKEY: sigrc = '?'; break; + case G10ERR_BAD_SIGN: sigrc = '-'; break; + case G10ERR_NO_PUBKEY: + case G10ERR_UNU_PUBKEY: sigrc = '?'; break; default: sigrc = '%'; break; } } @@ -485,80 +850,191 @@ list_keyblock( KBNODE keyblock, int secret ) rc = 0; sigrc = ' '; } - fputs( sigstr, stdout ); - if( opt.with_colons ) { - putchar(':'); - if( sigrc != ' ' ) - putchar(sigrc); - printf("::%d:%08lX%08lX:%s::::", sig->pubkey_algo, + 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], datestr_from_sig(sig)); - } - else - printf("%c %08lX %s ", - sigrc, (ulong)sig->keyid[1], datestr_from_sig(sig)); + (ulong)sig->keyid[1], colon_datestr_from_sig(sig), + colon_expirestr_from_sig(sig)); if( sigrc == '%' ) - printf("[%s] ", gpg_errstr(rc) ); + 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 ); - if( opt.with_colons ) - print_string( stdout, p, n, ':' ); - else - print_utf8_string( stdout, p, n ); - gcry_free(p); + print_string( stdout, p, n, ':' ); + m_free(p); } - if( opt.with_colons ) - printf(":%02x:", sig->sig_class ); - putchar('\n'); - /* FIXME: check or list other sigs here (subpkt PRIV_ADD_SIG)*/ + printf(":%02x%c:\n", sig->sig_class,sig->flags.exportable?'x':'l'); + /* fixme: check or list other sigs here */ } } if( !any ) {/* oops, no user id */ - if( opt.with_colons ) - putchar(':'); + putchar(':'); + putchar(':'); + print_capabilities (pk, sk, keyblock); putchar('\n'); } - else if( !opt.with_colons ) - putchar('\n'); /* separator line */ } +/* + * 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 +reorder_keyblock (KBNODE keyblock) +{ + 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 && + !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 */ -static void -fingerprint( PKT_public_key *pk, PKT_secret_key *sk ) + last->next = primary; + primary0->next = primary2->next; + primary2->next = node; +} + +void +list_keyblock( KBNODE keyblock, int secret, int fpr, void *opaque ) { - byte *array, *p; + 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 + */ +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; - p = array = pk? fingerprint_from_pk( pk, NULL, &n ) - : fingerprint_from_sk( sk, NULL, &n ); - if( opt.with_colons ) { - printf("fpr:::::::::"); - for(i=0; i < n ; i++, p++ ) - printf("%02X", *p ); - putchar(':'); + if (mode == 1) { + fp = log_stream (); + text = _("Fingerprint:"); + } + else if (mode == 2) { + fp = NULL; /* use tty */ + /* Translators: this should fit into 24 bytes to that the fingerprint + * data is properly aligned with the user ID */ + text = _(" Fingerprint:"); } else { - printf(" Key fingerprint ="); - if( n == 20 ) { - for(i=0; i < n ; i++, i++, p += 2 ) { - if( i == 10 ) - putchar(' '); - printf(" %02X%02X", *p, p[1] ); + 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( i && !(i%8) ) - putchar(' '); - printf(" %02X", *p ); + 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 ); + } } } } - putchar('\n'); - gcry_free(array); + if (fp) + putc ('\n', fp); + else + tty_printf ("\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, "w" ); + 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 000000000..f75a79dfe --- /dev/null +++ b/g10/keyring.c @@ -0,0 +1,1550 @@ +/* keyring.c - keyring file handling + * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, 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 = m_alloc_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; + m_free (k); + } +} +#endif + +static OffsetHashTable +new_offset_hash_table (void) +{ + struct off_item **tbl; + + tbl = m_alloc_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]); + m_free (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. Returns a pointer to + * be used to create a handles etc or NULL to indicate that it has + * already been registered */ +void * +keyring_register_filename (const char *fname, int secret) +{ + 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) ) + return NULL; /* already registered */ + } + + kr = m_alloc (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 (); + + return kr; +} + +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 = m_alloc_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--; + m_free (hd->word_match.name); + m_free (hd->word_match.pattern); + iobuf_close (hd->current.iobuf); + m_free (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 = m_alloc (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 = m_alloc (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); + m_free (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 = m_alloc(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) +{ + 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; + + /* 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 */ + m_free (hd->word_match.name); + m_free (hd->word_match.pattern); + hd->word_match.name = m_strdup (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; + u32 aki[2]; + + 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: + for (n=any_skip?0:ndesc; n < ndesc; n++) + { + if (desc[n].skipfnc + && desc[n].skipfnc (desc[n].skipfncvalue, aki)) + 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; + + *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 = m_alloc (strlen (template) + 1); + strcpy (bakfname, template); + strcpy (bakfname+strlen(template)-4, EXTSEP_S "bak"); + + tmpfname = m_alloc (strlen( template ) + 1 ); + strcpy (tmpfname,template); + strcpy (tmpfname+strlen(template)-4, EXTSEP_S "tmp"); + } + else + { /* file does not end with gpg; hmmm */ + bakfname = m_alloc (strlen( template ) + 5); + strcpy (stpcpy(bakfname, template), EXTSEP_S "bak"); + + tmpfname = m_alloc (strlen( template ) + 5); + strcpy (stpcpy(tmpfname, template), EXTSEP_S "tmp"); + } +# else /* Posix file names */ + bakfname = m_alloc (strlen( template ) + 2); + strcpy (stpcpy (bakfname,template),"~"); + + tmpfname = m_alloc (strlen( template ) + 5); + strcpy (stpcpy(tmpfname,template), EXTSEP_S "tmp"); +# endif /* Posix filename */ + + *r_fp = iobuf_create (tmpfname); + if (!*r_fp) { + log_error ("can't create `%s': %s\n", tmpfname, strerror(errno) ); + m_free (tmpfname); + m_free (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; + + /* restrict the permissions for secret keyrings */ +#ifndef HAVE_DOSISH_SYSTEM + if (secret && !opt.preserve_permissions) + { + if (chmod (tmpfname, S_IRUSR | S_IWUSR) ) + { + log_error ("chmod of `%s' failed: %s\n", + tmpfname, strerror(errno) ); + return G10ERR_WRITE_FILE; + } + } +#endif + + /* 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 (rename (tmpfname, fname) ) + { + log_error ("renaming `%s' to `%s' failed: %s\n", + tmpfname, fname, strerror(errno) ); + 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; + } + + 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) +{ + 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; + + while ( !(rc = keyring_search (hd, &desc, 1)) ) + { + 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; + m_free (tmpfilename); tmpfilename = NULL; + m_free (bakfilename); bakfilename = NULL; + if (rc) + goto leave; + lastresname = resname; + if (!opt.quiet) + log_info (_("checking 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) + { + if (node->pkt->pkttype == PKT_SIGNATURE) + { + 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) && !opt.quiet) + log_info(_("%lu keys so far checked (%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; + } + log_info(_("%lu keys checked (%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; + m_free (tmpfilename); tmpfilename = NULL; + m_free (bakfilename); bakfilename = NULL; + + leave: + if (tmpfp) + iobuf_cancel (tmpfp); + m_free (tmpfilename); + m_free (bakfilename); + release_kbnode (keyblock); + 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 rname, 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; + + newfp = iobuf_create (fname); + if( !newfp ) { + log_error (_("%s: can't create: %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; + } + if (chmod( fname, S_IRUSR | S_IWUSR )) { + log_error("%s: chmod failed: %s\n", fname, strerror(errno) ); + return G10ERR_WRITE_FILE; + } + return 0; /* ready */ + } + + if( !fp ) { + log_error ("%s: can't open: %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( 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); + 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); + 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); + iobuf_cancel(newfp); + goto leave; + } + } + + if( mode == 1 || mode == 3 ) { /* insert or update */ + rc = write_keyblock (newfp, root); + if (rc) { + iobuf_close(fp); + 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); + 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: + m_free(bakfname); + m_free(tmpfname); + return rc; +} diff --git a/g10/keyring.h b/g10/keyring.h new file mode 100644 index 000000000..cb8e404a4 --- /dev/null +++ b/g10/keyring.h @@ -0,0 +1,45 @@ +/* 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + +#ifndef GPG_KEYRING_H +#define GPG_KEYRING_H 1 + +#include "global.h" + + +typedef struct keyring_handle *KEYRING_HANDLE; + +void *keyring_register_filename (const char *fname, int secret); +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); +int keyring_rebuild_cache (void *); + +#endif /*GPG_KEYRING_H*/ diff --git a/g10/keyserver-internal.h b/g10/keyserver-internal.h new file mode 100644 index 000000000..9f0d2e8c0 --- /dev/null +++ b/g10/keyserver-internal.h @@ -0,0 +1,22 @@ +/* Keyserver internals */ + +#ifndef _KEYSERVER_INTERNAL_H_ +#define _KEYSERVER_INTERNAL_H_ + +#include <time.h> +#include "keyserver.h" +#include "iobuf.h" +#include "types.h" + +void parse_keyserver_options(char *options); +int parse_keyserver_uri(char *uri, + const char *configname,unsigned int configlineno); +int keyserver_export(STRLIST users); +int keyserver_import(STRLIST users); +int keyserver_import_fprint(const byte *fprint,size_t fprint_len); +int keyserver_import_keyid(u32 *keyid); +int keyserver_refresh(STRLIST users); +int keyserver_search(STRLIST tokens); +void keyserver_search_prompt(IOBUF buffer,int count,const char *searchstr); + +#endif /* !_KEYSERVER_INTERNAL_H_ */ diff --git a/g10/keyserver.c b/g10/keyserver.c new file mode 100644 index 000000000..9338bfbb7 --- /dev/null +++ b/g10/keyserver.c @@ -0,0 +1,1033 @@ +/* keyserver.c - generic keyserver code + * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + +#include <config.h> +#include <ctype.h> +#include <stdio.h> +#include <string.h> +#include <stdlib.h> + +#include "filter.h" +#include "keydb.h" +#include "status.h" +#include "exec.h" +#include "main.h" +#include "i18n.h" +#include "hkp.h" +#include "iobuf.h" +#include "memory.h" +#include "options.h" +#include "packet.h" +#include "keyserver-internal.h" +#include "util.h" + +#define KEYSERVER_PROTO_VERSION 0 + +#define GET 0 +#define SEND 1 +#define SEARCH 2 + +struct kopts +{ + char *name; + int tell; /* tell remote process about this one */ + int *flag; +} keyserver_opts[]= +{ + {"include-revoked",1,&opt.keyserver_options.include_revoked}, + {"include-disabled",1,&opt.keyserver_options.include_disabled}, + {"include-subkeys",1,&opt.keyserver_options.include_subkeys}, + {"keep-temp-files",0,&opt.keyserver_options.keep_temp_files}, + {"honor-http-proxy",1,&opt.keyserver_options.honor_http_proxy}, + {"broken-http-proxy",1,&opt.keyserver_options.broken_http_proxy}, + {"refresh-add-fake-v3-keyids",0,&opt.keyserver_options.fake_v3_keyids}, + {"auto-key-retrieve",0,&opt.keyserver_options.auto_key_retrieve}, + {NULL} +}; + +void +parse_keyserver_options(char *options) +{ + char *tok=""; + + do + { + struct kopts *kopts=keyserver_opts; + int i,hit=0; + + for(i=0,kopts=keyserver_opts;kopts[i].name;i++) + { + if(ascii_strcasecmp(tok,kopts[i].name)==0) + { + *(kopts[i].flag)=1; + hit=1; + break; + } + else if(ascii_memcasecmp("no-",tok,3)==0 && strlen(tok)>3 && + ascii_strcasecmp(&tok[3],kopts[i].name)==0) + { + *(kopts[i].flag)=0; + hit=1; + break; + } + } + + /* These options need more than just a flag */ + if(!hit) + { + if(ascii_strcasecmp(tok,"verbose")==0) + opt.keyserver_options.verbose++; + else if(ascii_strcasecmp(tok,"no-verbose")==0) + opt.keyserver_options.verbose--; +#ifdef EXEC_TEMPFILE_ONLY + else if(ascii_strcasecmp(tok,"use-temp-files")==0 || + ascii_strcasecmp(tok,"no-use-temp-files")==0) + log_info(_("Warning: keyserver option \"%s\" is not used " + "on this platform\n"),tok); +#else + else if(ascii_strcasecmp(tok,"use-temp-files")==0) + opt.keyserver_options.use_temp_files=1; + else if(ascii_strcasecmp(tok,"no-use-temp-files")==0) + opt.keyserver_options.use_temp_files=0; +#endif + else if(strlen(tok)>0) + add_to_strlist(&opt.keyserver_options.other,tok); + } + + tok=strsep(&options," ,"); + } + while(tok!=NULL); +} + +int +parse_keyserver_uri(char *uri,const char *configname,unsigned int configlineno) +{ + /* Get the scheme */ + + opt.keyserver_scheme=strsep(&uri,":"); + if(uri==NULL) + { + uri=opt.keyserver_scheme; + opt.keyserver_scheme="hkp"; + } + + if(ascii_strcasecmp(opt.keyserver_scheme,"x-broken-hkp")==0) + { + deprecated_warning(configname,configlineno,"x-broken-hkp", + "--keyserver-options ","broken-http-proxy"); + opt.keyserver_scheme="hkp"; + opt.keyserver_options.broken_http_proxy=1; + } + else if(ascii_strcasecmp(opt.keyserver_scheme,"x-hkp")==0) + { + /* Canonicalize this to "hkp" so it works with both the internal + and external keyserver interface. */ + opt.keyserver_scheme="hkp"; + } + + /* Skip the "//", if any */ + if(strlen(uri)>2 && uri[0]=='/' && uri[1]=='/') + uri+=2; + + /* Get the host */ + opt.keyserver_host=strsep(&uri,":/"); + if(uri==NULL) + opt.keyserver_port="0"; + else + { + char *ch; + + /* Get the port */ + opt.keyserver_port=strsep(&uri,"/"); + + /* Ports are digits only */ + ch=opt.keyserver_port; + while(*ch!='\0') + { + if(!isdigit(*ch)) + return G10ERR_BAD_URI; + + ch++; + } + + if(strlen(opt.keyserver_port)==0 || + atoi(opt.keyserver_port)<1 || atoi(opt.keyserver_port)>65535) + return G10ERR_BAD_URI; + } + + /* (any path part of the URI is discarded for now as no keyserver + uses it) */ + + if(opt.keyserver_scheme[0]=='\0' || opt.keyserver_host[0]=='\0') + return G10ERR_BAD_URI; + + return 0; +} + +/* Unquote only the delimiter character and backslashes (\x5C) */ +static void +printunquoted(char *string,char delim) +{ + char *ch=string; + + while(*ch) + { + if(*ch=='\\') + { + int c; + + sscanf(ch,"\\x%02x",&c); + if(c==delim) + { + printf("%c",c); + ch+=3; + } + else if(c=='\\') + { + fputc('\\',stdout); + ch+=3; + } + else + fputc(*ch,stdout); + } + else + fputc(*ch,stdout); + + ch++; + } +} + +static int +print_keyinfo(int count,char *keystring,KEYDB_SEARCH_DESC *desc) +{ + char *certid,*userid,*keytype,*tok; + int flags,keysize=0; + time_t createtime=0,expiretime=0,modifytime=0; + + if((certid=strsep(&keystring,":"))==NULL) + return -1; + + classify_user_id (certid, 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) + return -1; + + if((tok=strsep(&keystring,":"))==NULL) + return -1; + + userid=utf8_to_native(tok,strlen(tok),0); + + if((tok=strsep(&keystring,":"))==NULL) + return -1; + + flags=atoi(tok); + + if((tok=strsep(&keystring,":"))==NULL) + return -1; + + createtime=atoi(tok); + + if((tok=strsep(&keystring,":"))==NULL) + return -1; + + expiretime=atoi(tok); + + if((tok=strsep(&keystring,":"))==NULL) + return -1; + + modifytime=atoi(tok); + + if((keytype=strsep(&keystring,":"))==NULL) + return -1; + + /* The last one */ + if(keystring!=NULL) + keysize=atoi(keystring); + + printf("(%d)\t",count); + + /* No need to check for control characters, as utf8_to_native does + this for us. */ + printunquoted(userid,':'); + + if(flags&1) + printf(" (revoked)"); + if(flags&2) + printf(" (disabled)"); + + if(keytype[0]) + printf(" %s",keytype); + + if(keysize>0) + printf(" %d",keysize); + + printf("\n\t created %s,",strtimestamp(createtime)); + + if(expiretime>0) + printf(" expires %s,",strtimestamp(expiretime)); + + printf(" key %s\n",certid); + + return 0; +} + +#define KEYSERVER_ARGS_KEEP " -o \"%O\" \"%I\"" +#define KEYSERVER_ARGS_NOKEEP " -o \"%o\" \"%i\"" + +static int +keyserver_spawn(int action,STRLIST list, + KEYDB_SEARCH_DESC *desc,int count,int *prog) +{ + int ret=0,i,gotversion=0,outofband=0; + STRLIST temp; + unsigned int maxlen=256,buflen; + char *command=NULL,*searchstr=NULL; + byte *line=NULL; + struct kopts *kopts; + struct exec_info *spawn; + +#ifdef EXEC_TEMPFILE_ONLY + opt.keyserver_options.use_temp_files=1; +#endif + + /* Build the filename for the helper to execute */ + + command=m_alloc(strlen("gpgkeys_")+strlen(opt.keyserver_scheme)+1); + strcpy(command,"gpgkeys_"); + strcat(command,opt.keyserver_scheme); + + if(opt.keyserver_options.use_temp_files) + { + if(opt.keyserver_options.keep_temp_files) + { + command=m_realloc(command,strlen(command)+ + strlen(KEYSERVER_ARGS_KEEP)+1); + strcat(command,KEYSERVER_ARGS_KEEP); + } + else + { + command=m_realloc(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); + + if(ret) + return ret; + + fprintf(spawn->tochild,"# This is a gpg keyserver communications file\n"); + fprintf(spawn->tochild,"VERSION %d\n",KEYSERVER_PROTO_VERSION); + fprintf(spawn->tochild,"PROGRAM %s\n",VERSION); + fprintf(spawn->tochild,"HOST %s\n",opt.keyserver_host); + + if(atoi(opt.keyserver_port)>0) + fprintf(spawn->tochild,"PORT %s\n",opt.keyserver_port); + + /* Write options */ + + for(i=0,kopts=keyserver_opts;kopts[i].name;i++) + if(*(kopts[i].flag) && kopts[i].tell) + fprintf(spawn->tochild,"OPTION %s\n",kopts[i].name); + + for(i=0;i<opt.keyserver_options.verbose;i++) + fprintf(spawn->tochild,"OPTION verbose\n"); + + temp=opt.keyserver_options.other; + + for(;temp;temp=temp->next) + fprintf(spawn->tochild,"OPTION %s\n",temp->d); + + switch(action) + { + case GET: + { + fprintf(spawn->tochild,"COMMAND GET\n\n"); + + /* Which keys do we want? */ + + for(i=0;i<count;i++) + { + 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",(byte)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",(byte)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 + fprintf(spawn->tochild,"0x%08lX\n", + (ulong)desc[i].u.kid[1]); + } + + fprintf(spawn->tochild,"\n"); + + break; + } + + case 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(); + + temp=NULL; + add_to_strlist(&temp,key->d); + + memset(&afx,0,sizeof(afx)); + afx.what=1; + iobuf_push_filter(buffer,armor_filter,&afx); + + if(export_pubkeys_stream(buffer,temp,1)==-1) + iobuf_close(buffer); + else + { + iobuf_flush_temp(buffer); + + fprintf(spawn->tochild,"KEY %s BEGIN\n",key->d); + fwrite(iobuf_get_temp_buffer(buffer), + iobuf_get_temp_length(buffer),1,spawn->tochild); + fprintf(spawn->tochild,"KEY %s END\n",key->d); + + iobuf_close(buffer); + } + + free_strlist(temp); + } + + break; + } + + case 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=m_realloc(searchstr, + strlen(searchstr)+strlen(key->d)+2); + strcat(searchstr," "); + } + else + { + searchstr=m_alloc(strlen(key->d)+1); + searchstr[0]='\0'; + } + + strcat(searchstr,key->d); + } + + fprintf(spawn->tochild,"\n"); + + 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(;;) + { + char *ptr; + + if(iobuf_read_line(spawn->fromchild,&line,&buflen,&maxlen)==0) + { + ret=G10ERR_READ_FILE; + goto fail; /* i.e. EOF */ + } + + ptr=line; + + if(*ptr=='\r') + ptr++; + + if(*ptr=='\n') + ptr++; + + if(*ptr=='\0') + break; + + if(ascii_memcasecmp(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_memcasecmp(ptr,"PROGRAM ",8)==0) + { + if(ascii_memcasecmp(&ptr[8],VERSION,strlen(VERSION))!=0) + log_info(_("Warning: keyserver handler from a different " + "version of GnuPG (%s)\n"),&ptr[8]); + } + else if(ascii_memcasecmp(ptr,"OPTION OUTOFBAND",16)==0) + outofband=1; /* Currently the only OPTION */ + } + + m_free(line); + + if(!gotversion) + { + log_error(_("keyserver did not send VERSION\n")); + goto fail; + } + + if(!outofband) + switch(action) + { + case GET: + { + 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,0,stats_handle); + + import_print_stats(stats_handle); + import_release_stats_handle(stats_handle); + + break; + } + + /* Nothing to do here */ + case SEND: + break; + + case SEARCH: + { + line=NULL; + buflen = 0; + maxlen = 80; + /* Look for the COUNT line */ + do + { + if(iobuf_read_line(spawn->fromchild,&line,&buflen,&maxlen)==0) + { + ret=G10ERR_READ_FILE; + goto fail; /* i.e. EOF */ + } + } + while(sscanf(line,"COUNT %d\n",&i)!=1); + + keyserver_search_prompt(spawn->fromchild,i,searchstr); + + break; + } + + default: + log_fatal(_("no keyserver action!\n")); + break; + } + + fail: + *prog=exec_finish(spawn); + + return ret; +} + +static int +keyserver_work(int action,STRLIST list,KEYDB_SEARCH_DESC *desc,int count) +{ + int rc=0,ret=0; + + if(opt.keyserver_scheme==NULL || + opt.keyserver_host==NULL || + opt.keyserver_port==NULL) + { + log_error(_("no keyserver known (use option --keyserver)\n")); + return G10ERR_BAD_URI; + } + +#ifndef USE_EXTERNAL_HKP + /* Use the internal HKP code */ + if(ascii_strcasecmp(opt.keyserver_scheme,"hkp")==0) + { + void *stats_handle = import_new_stats_handle (); + + switch(action) + { + case GET: + for(count--;count>=0;count--) + if(hkp_ask_import(&desc[count],stats_handle)) + log_inc_errorcount(); + break; + case SEND: + return hkp_export(list); + case SEARCH: + return hkp_search(list); + } + + import_print_stats (stats_handle); + import_release_stats_handle (stats_handle); + + return 0; + } +#endif + + /* It's not the internal HKP code, so try and spawn a handler for it */ + + rc=keyserver_spawn(action,list,desc,count,&ret); + if(ret) + { + switch(ret) + { + case KEYSERVER_SCHEME_NOT_FOUND: + log_error(_("no handler for keyserver scheme \"%s\"\n"), + opt.keyserver_scheme); + break; + + case KEYSERVER_NOT_SUPPORTED: + log_error(_("action \"%s\" not supported with keyserver " + "scheme \"%s\"\n"), + action==GET?"get":action==SEND?"send": + action==SEARCH?"search":"unknown", + opt.keyserver_scheme); + + 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; +} + +int +keyserver_export(STRLIST users) +{ + /* We better ask for confirmation when the user entered --send-keys + without arguments. Sending all keys might not be the thing he + intended to do */ + if (users || opt.batch || opt.answer_yes) + ; + else if ( !cpr_get_answer_is_yes + ("keyserver_export.send_all", + _("Do you really want to send all your " + "public keys to the keyserver? (y/N) "))) + return -1; + + return keyserver_work(SEND,users,NULL,0); +} + +int +keyserver_import(STRLIST users) +{ + KEYDB_SEARCH_DESC *desc; + int num=100,count=0; + int rc=0; + + /* Build a list of key ids */ + desc=m_alloc(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(_("skipping invalid key ID \"%s\"\n"),users->d); + continue; + } + + count++; + if(count==num) + { + num+=100; + desc=m_realloc(desc,sizeof(KEYDB_SEARCH_DESC)*num); + } + } + + if(count>0) + rc=keyserver_work(GET,NULL,desc,count); + + m_free(desc); + + return rc; +} + +int +keyserver_import_fprint(const byte *fprint,size_t fprint_len) +{ + 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); + + return keyserver_work(GET,NULL,&desc,1); +} + +int +keyserver_import_keyid(u32 *keyid) +{ + 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(GET,NULL,&desc,1); +} + +/* 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=m_alloc(sizeof(KEYDB_SEARCH_DESC)*num); + + kdbhd=keydb_new(0); + + if(!users) + { + ndesc = 1; + desc = m_alloc_clear ( ndesc * sizeof *desc); + desc[0].mode = KEYDB_SEARCH_MODE_FIRST; + } + else + { + for (ndesc=0, sl=users; sl; sl = sl->next, ndesc++) + ; + desc = m_alloc ( 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=m_realloc(*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 key id + 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); + } + + (*count)++; + + if(*count==num) + { + num+=100; + *klist=m_realloc(*klist,sizeof(KEYDB_SEARCH_DESC)*num); + } + } + } + + if(rc==-1) + rc=0; + + leave: + m_free(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,fakev3=0; + KEYDB_SEARCH_DESC *desc; + + /* We switch merge_only on during a refresh, as 'refresh' should + never import new keys, even if their keyids match. Is it worth + preserving the old merge_only value here? */ + opt.merge_only=1; + + /* 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.fake_v3_keyids && opt.keyserver_scheme && + (ascii_strcasecmp(opt.keyserver_scheme,"hkp")==0 || + ascii_strcasecmp(opt.keyserver_scheme,"mailto")==0)) + fakev3=1; + + rc=keyidlist(users,&desc,&count,fakev3); + if(rc) + return rc; + + if(count==1) + log_info(_("%d key to refresh\n"),count); + else + log_info(_("%d keys to refresh\n"),count); + + if(count>0) + rc=keyserver_work(GET,NULL,desc,count); + + m_free(desc); + + return 0; +} + +int +keyserver_search(STRLIST tokens) +{ + if(tokens) + return keyserver_work(SEARCH,tokens,NULL,0); + else + 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. */ +void +keyserver_search_prompt(IOBUF buffer,int count,const char *searchstr) +{ + int i=0,validcount=1; + unsigned int maxlen=256,buflen=0; + KEYDB_SEARCH_DESC *desc; + byte *line=NULL; + char *answer; + + if(count==0) + goto notfound; + + if(count<0) + { + validcount=0; + count=10; + } + + desc=m_alloc(count*sizeof(KEYDB_SEARCH_DESC)); + + /* Read each line and show it to the user */ + + for(;;) + { + int rl; + + if(validcount && i%10==0) + { + printf("Keys %d-%d of %d",i+1,(i+10<count)?i+10:count,count); + if(searchstr) + printf(" for \"%s\"",searchstr); + printf("\n"); + } + + maxlen=1024; + rl=iobuf_read_line(buffer,&line,&buflen,&maxlen); + if(rl>0) + { + if(print_keyinfo(i+1,line,&desc[i])==0) + { + i++; + + if(i==count) + { + count+=10; + desc=m_realloc(desc,count*sizeof(KEYDB_SEARCH_DESC)); + validcount=0; + } + } + else + continue; + } + + if(rl==0 && i==0) + { + count=0; + break; + } + + if(i%10==0 || rl==0) + { + 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') + { + m_free(answer); + break; + } + else if(atoi(answer)>=1 && atoi(answer)<=i) + { + char *split=answer,*num; + + while((num=strsep(&split," ,"))!=NULL) + if(atoi(num)>=1 && atoi(num)<=i) + keyserver_work(GET,NULL,&desc[atoi(num)-1],1); + + m_free(answer); + break; + } + } + } + + m_free(desc); + m_free(line); + + notfound: + if(count==0) + { + if(searchstr) + log_info(_("key \"%s\" not found on keyserver\n"),searchstr); + else + log_info(_("key not found on keyserver\n")); + return; + } +} diff --git a/g10/ks-proto.c b/g10/ks-proto.c index 43abf468a..aa7dc56e0 100644 --- a/g10/ks-proto.c +++ b/g10/ks-proto.c @@ -1,5 +1,5 @@ /* ks-proto.c keyserver protocol handling - * Copyright (C) 1998 Free Software Foundation, Inc. + * Copyright (C) 1998 Free Software Foundation, Inc. * * This file is part of GnuPG. * diff --git a/g10/ks-proto.h b/g10/ks-proto.h index 6e8bbad0b..cd55b47e2 100644 --- a/g10/ks-proto.h +++ b/g10/ks-proto.h @@ -1,5 +1,5 @@ /* ks-proto.h - * Copyright (C) 1998, 1999, 2000 Free Software Foundation, Inc. + * Copyright (C) 1998 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -17,7 +17,7 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA */ -#ifndef GPG_KS_PROTO_H -#define GPG_KS_PROTO_H +#ifndef G10_KS_PROTO_H +#define G10_KS_PROTO_H -#endif /*GPG_KS_PROTO_H*/ +#endif /*G10_KS_PROTO_H*/ diff --git a/g10/main.h b/g10/main.h index d199c5484..e7153bd55 100644 --- a/g10/main.h +++ b/g10/main.h @@ -1,5 +1,5 @@ /* main.h - * Copyright (C) 1998, 1999, 2000 Free Software Foundation, Inc. + * Copyright (C) 1998, 1999, 2000, 2001, 2002 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -17,30 +17,39 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA */ -#ifndef GPG_MAIN_H -#define GPG_MAIN_H - -#include <gcrypt.h> -#include "basicdefs.h" +#ifndef G10_MAIN_H +#define G10_MAIN_H +#include "types.h" #include "iobuf.h" +#include "mpi.h" +#include "cipher.h" #include "keydb.h" -#define DEFAULT_CIPHER_ALGO GCRY_CIPHER_BLOWFISH -#define DEFAULT_PUBKEY_ALGO GCRY_PUBKEY_ELGAMAL -#define DEFAULT_DIGEST_ALGO GCRY_MD_RMD160 +#define DEFAULT_CIPHER_ALGO CIPHER_ALGO_CAST5 +#define DEFAULT_PUBKEY_ALGO PUBKEY_ALGO_ELGAMAL +#define DEFAULT_DIGEST_ALGO DIGEST_ALGO_SHA1 +#define DEFAULT_COMPRESS_ALGO 1 -#define is_RSA(a) ((a)==GCRY_PK_RSA || (a)==GCRY_PK_RSA_E \ - || (a)==GCRY_PK_RSA_S ) -#define is_ELGAMAL(a) ((a)==GCRY_PK_ELG || (a)==GCRY_PK_ELG_E) +typedef struct { + int header_okay; + PK_LIST pk_list; + cipher_filter_context_t cfx; +} encrypt_filter_context_t; +struct groupitem +{ + char *name; + STRLIST values; + struct groupitem *next; +}; -/*-- gpg.c --*/ -extern int gpg_errors_seen; +/*-- g10.c --*/ +extern int g10_errors_seen; #if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 5 ) - void gpg_exit(int rc) __attribute__ ((noreturn)); + void g10_exit(int rc) __attribute__ ((noreturn)); #else - void gpg_exit(int rc); + void g10_exit(int rc); #endif void print_pubkey_algo_note( int algo ); void print_cipher_algo_note( int algo ); @@ -55,24 +64,29 @@ int disable_core_dumps(void); u16 checksum_u16( unsigned n ); u16 checksum( byte *p, unsigned n ); u16 checksum_mpi( MPI a ); +u16 checksum_mpi_counted_nbits( MPI a ); u32 buffer_to_u32( const byte *buffer ); - -int mpi_write( IOBUF out, GCRY_MPI a ); -int mpi_write_opaque( IOBUF out, MPI a ); -GCRY_MPI mpi_read(IOBUF inp, unsigned int *ret_nread, int secure ); -GCRY_MPI mpi_read_opaque(IOBUF inp, unsigned int *ret_nread ); -int mpi_print( FILE *fp, MPI a, int mode ); - +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 ); - -int pubkey_get_npkey( int algo ); -int pubkey_get_nskey( int algo ); -int pubkey_get_nsig( int algo ); -int pubkey_get_nenc( int algo ); -unsigned int pubkey_nbits( int algo, MPI *pkey ); +int check_permissions(const char *path,int extension,int checkonly); +void idea_cipher_warn( int show ); + +struct expando_args +{ + PKT_public_key *pk; + PKT_secret_key *sk; + byte imagetype; +}; + +char *pct_expando(const char *string,struct expando_args *args); +int hextobyte( const char *s ); +void deprecated_warning(const char *configname,unsigned int configlineno, + const char *option,const char *repl1,const char *repl2); +const char *compress_algo_to_string(int algo); +int check_compress_algo(int algo); /*-- helptext.c --*/ void display_online_help( const char *keyword ); @@ -81,15 +95,17 @@ void display_online_help( const char *keyword ); int encode_symmetric( const char *filename ); int encode_store( const char *filename ); int encode_crypt( const char *filename, STRLIST remusr ); +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, GCRY_MD_HD md ); +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_key_signature( KBNODE root, KBNODE node, int *is_selfsig ); @@ -97,17 +113,22 @@ int check_key_signature2( KBNODE root, KBNODE node, int *is_selfsig, u32 *r_expiredate, int *r_expired ); /*-- delkey.c --*/ -int delete_key( const char *username, int secure ); +int delete_keys( STRLIST names, int secret, int allow_both ); /*-- keyedit.c --*/ void keyedit_menu( const char *username, STRLIST locusr, STRLIST cmds, int sign_mode ); /*-- keygen.c --*/ +u32 ask_expire_interval(int object); u32 ask_expiredate(void); void generate_keypair( const char *fname ); +int keygen_set_std_prefs (const char *string,int personal); +char *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_revkey(PKT_signature *sig, void *opaque); int generate_subkeypair( KBNODE pub_keyblock, KBNODE sec_keyblock ); /*-- openfile.c --*/ @@ -121,16 +142,20 @@ 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( int pubkey_algo, GCRY_MD_HD md, +MPI encode_md_value( int pubkey_algo, MD_HANDLE md, int hash_algo, unsigned nbits, int v3compathack ); /*-- comment.c --*/ -KBNODE make_comment_node_from_buffer( const char *s, size_t n ); KBNODE make_comment_node( const char *s ); +KBNODE make_mpi_comment_node( const char *s, MPI a ); /*-- import.c --*/ -void import_keys( char **fnames, int nnames, int fast ); -int import_keys_stream( IOBUF inp, int fast ); +void import_keys( char **fnames, int nnames, int fast, void *stats_hd ); +int import_keys_stream( IOBUF inp, int fast, void *stats_hd ); +void *import_new_stats_handle (void); +void import_release_stats_handle (void *p); +void import_print_stats (void *hd); + int collapse_uids( KBNODE *keyblock ); /*-- export.c --*/ @@ -146,6 +171,7 @@ 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 ); 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 ); @@ -154,22 +180,32 @@ 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 list_keyblock( KBNODE keyblock, int secret, int fpr, void *opaque ); +void print_fingerprint (PKT_public_key *pk, PKT_secret_key *sk, int mode); +void show_policy_url(PKT_signature *sig,int indent); +void show_notation(PKT_signature *sig,int indent); +void set_attrib_fd(int fd); /*-- 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( GCRY_MD_HD md, GCRY_MD_HD md2, +int hash_datafiles( MD_HANDLE md, MD_HANDLE md2, STRLIST files, const char *sigfilename, int textmode ); +/*-- 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); -#endif /*GPG_MAIN_H*/ +#endif /*G10_MAIN_H*/ diff --git a/g10/mainproc.c b/g10/mainproc.c index 7b04b3e6f..bcd1c1c01 100644 --- a/g10/mainproc.c +++ b/g10/mainproc.c @@ -1,5 +1,5 @@ /* mainproc.c - handle packets - * Copyright (C) 1998, 1999, 2000 Free Software Foundation, Inc. + * Copyright (C) 1998, 1999, 2000, 2001, 2002 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -25,18 +25,19 @@ #include <assert.h> #include <time.h> -#include <gcrypt.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 "hkp.h" +#include "keyserver-internal.h" struct kidlist_item { @@ -69,8 +70,11 @@ struct mainproc_context { IOBUF iobuf; /* used to get the filename etc. */ int trustletter; /* temp usage in list_node */ ulong local_id; /* ditto */ - struct kidlist_item *failed_pkenc; /* list of packets for which - we do not have a secret key */ + struct kidlist_item *pkenc_list; /* list of encryption packets */ + struct { + int op; + int stop_now; + } pipemode; }; @@ -87,13 +91,18 @@ release_list( CTX c ) return; proc_tree(c, c->list ); release_kbnode( c->list ); - while( c->failed_pkenc ) { - struct kidlist_item *tmp = c->failed_pkenc->next; - gcry_free( c->failed_pkenc ); - c->failed_pkenc = tmp; + while( c->pkenc_list ) { + struct kidlist_item *tmp = c->pkenc_list->next; + m_free( c->pkenc_list ); + c->pkenc_list = tmp; } - c->failed_pkenc = NULL; + 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; + m_free(c->dek); c->dek = NULL; } @@ -103,8 +112,14 @@ add_onepass_sig( CTX c, PACKET *pkt ) KBNODE node; if( c->list ) { /* add another packet */ - if( c->list->pkt->pkttype != PKT_ONEPASS_SIG ) { - log_error("add_onepass_sig: another packet is in the way\n"); + /* We can only append another onepass packet if the list + * does contain only onepass packets */ + for( node=c->list; node && node->pkt->pkttype == PKT_ONEPASS_SIG; + node = node->next ) + ; + if( node ) { + /* this is not the case, so we flush the current thing and + * allow this packet to start a new verification thing */ release_list( c ); c->list = new_kbnode( pkt ); } @@ -118,6 +133,48 @@ add_onepass_sig( CTX c, PACKET *pkt ) } +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 ) @@ -180,6 +237,32 @@ add_signature( CTX c, PACKET *pkt ) return 1; } +static void +symkey_decrypt_sesskey( DEK *dek, byte *sesskey, size_t slen ) +{ + CIPHER_HANDLE hd; + + if ( slen < 17 || slen > 33 ) { + log_error( "weird size for an encrypted session key (%d)\n", slen ); + return; + } + hd = cipher_open( dek->algo, CIPHER_MODE_CFB, 1 ); + cipher_setkey( hd, dek->key, dek->keylen ); + cipher_setiv( hd, NULL, 0 ); + cipher_decrypt( hd, sesskey, sesskey, slen ); + cipher_close( hd ); + /* check first byte (the cipher algo) */ + if ( sesskey[0] > 10 ) { + log_error( "invalid symkey algorithm detected (%d)\n", sesskey[0] ); + return; + } + /* now we replace the dek components with the real session key + to decrypt the contents of the sequencing packet. */ + dek->keylen = cipher_get_keylen( sesskey[0] ) / 8; + dek->algo = sesskey[0]; + memcpy( dek->key, sesskey + 1, dek->keylen ); + /*log_hexdump( "thekey", dek->key, dek->keylen );*/ +} static void proc_symkey_enc( CTX c, PACKET *pkt ) @@ -187,12 +270,28 @@ proc_symkey_enc( CTX c, PACKET *pkt ) PKT_symkey_enc *enc; enc = pkt->pkt.symkey_enc; - if( enc->seskeylen ) - log_error( "symkey_enc packet with session keys are not supported!\n"); + if (!enc) + log_error ("invalid symkey encrypted packet\n"); else { + int algo = enc->cipher_algo; + const char *s; + + s = cipher_algo_to_string (algo); + if( s ) + log_info(_("%s encrypted data\n"), s ); + else + log_info(_("encrypted with unknown algorithm %d\n"), algo ); + c->last_was_session_key = 2; - c->dek = passphrase_to_dek( NULL, 0, enc->cipher_algo, &enc->s2k, 0 ); + if ( opt.list_only ) + goto leave; + c->dek = passphrase_to_dek( NULL, 0, algo, &enc->s2k, 0, NULL ); + if (c->dek) + c->dek->algo_info_printed = 1; + if ( c->dek && enc->seskeylen ) + symkey_decrypt_sesskey( c->dek, enc->seskey, enc->seskeylen ); } +leave: free_packet(pkt); } @@ -218,42 +317,64 @@ proc_pubkey_enc( CTX c, PACKET *pkt ) write_status_text( STATUS_ENC_TO, buf ); } - - if( is_ELGAMAL(enc->pubkey_algo) - || enc->pubkey_algo == GCRY_PK_DSA + 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 = m_alloc_clear( sizeof *c->dek ); + result = get_override_session_key ( c->dek, opt.override_session_key ); + if ( result ) { + m_free(c->dek); c->dek = NULL; + } + } + else if( is_ELGAMAL(enc->pubkey_algo) + || enc->pubkey_algo == PUBKEY_ALGO_DSA || is_RSA(enc->pubkey_algo) ) { 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 = gcry_xmalloc_secure( sizeof *c->dek ); + c->dek = m_alloc_secure_clear( sizeof *c->dek ); if( (result = get_session_key( enc, c->dek )) ) { /* error: delete the DEK */ - gcry_free(c->dek); c->dek = NULL; + m_free(c->dek); c->dek = NULL; } } } else - result = GPGERR_NO_SECKEY; + result = G10ERR_NO_SECKEY; } else - result = GPGERR_PUBKEY_ALGO; + result = G10ERR_PUBKEY_ALGO; if( result == -1 ) ; - else if( !result ) { - if( opt.verbose > 1 ) - log_info( _("public key encrypted data: good DEK\n") ); - } - else { /* store it for later display */ - struct kidlist_item *x = gcry_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->failed_pkenc; - c->failed_pkenc = x; + else { + if( !result ) { + if( opt.verbose > 1 ) + log_info( _("public key encrypted data: good DEK\n") ); + if ( opt.show_session_key ) { + int i; + char *buf = m_alloc ( 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 ); + } + } + /* store it for later display */ + { + struct kidlist_item *x = m_alloc( 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; + } } free_packet(pkt); } @@ -265,11 +386,19 @@ proc_pubkey_enc( CTX c, PACKET *pkt ) * not decrypt. */ static void -print_failed_pkenc( struct kidlist_item *list ) +print_pkenc_list( struct kidlist_item *list, int failed ) { for( ; list; list = list->next ) { - PKT_public_key *pk = gcry_xcalloc( 1, sizeof *pk ); - const char *algstr = gcry_pk_algo_name( list->pubkey_algo ); + 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 = m_alloc_clear( sizeof *pk ); if( !algstr ) algstr = "[?]"; @@ -282,8 +411,8 @@ print_failed_pkenc( struct kidlist_item *list ) strtimestamp(pk->timestamp) ); fputs(" \"", log_stream() ); p = get_user_id( list->kid, &n ); - print_string( log_stream(), p, n, '"' ); - gcry_free(p); + print_utf8_string2 ( log_stream(), p, n, '"' ); + m_free(p); fputs("\"\n", log_stream() ); } else { @@ -292,8 +421,7 @@ print_failed_pkenc( struct kidlist_item *list ) } free_public_key( pk ); - if( list->reason == GPGERR_NO_SECKEY ) { - log_info(_("no secret key for decryption available\n")); + if( list->reason == G10ERR_NO_SECKEY ) { if( is_status_enabled() ) { char buf[20]; sprintf(buf,"%08lX%08lX", (ulong)list->kid[0], @@ -301,9 +429,9 @@ print_failed_pkenc( struct kidlist_item *list ) write_status_text( STATUS_NO_SECKEY, buf ); } } - else + else if (list->reason) log_error(_("public key decryption failed: %s\n"), - gpg_errstr(list->reason)); + g10_errstr(list->reason)); } } @@ -313,7 +441,10 @@ proc_encrypted( CTX c, PACKET *pkt ) { int result = 0; - print_failed_pkenc( c->failed_pkenc ); + if (!opt.quiet) { + print_pkenc_list ( c->pkenc_list, 1 ); + print_pkenc_list ( c->pkenc_list, 0 ); + } write_status( STATUS_BEGIN_DECRYPTION ); @@ -321,19 +452,43 @@ proc_encrypted( CTX c, PACKET *pkt ) if( opt.list_only ) result = -1; else if( !c->dek && !c->last_was_session_key ) { - /* assume this is old conventional encrypted data - * Actually we should use IDEA and MD5 in this case, but because - * IDEA is patented we can't do so */ - c->dek = passphrase_to_dek( NULL, 0, - opt.def_cipher_algo ? opt.def_cipher_algo - : DEFAULT_CIPHER_ALGO, NULL, 0 ); + int algo; + STRING2KEY s2kbuf, *s2k = NULL; + + /* 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.def_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 ); + if (c->dek) + c->dek->algo_info_printed = 1; } else if( !c->dek ) - result = GPGERR_NO_SECKEY; + result = G10ERR_NO_SECKEY; if( !result ) result = decrypt_data( c, pkt->pkt.encrypted, c->dek ); - gcry_free(c->dek); c->dek = NULL; + m_free(c->dek); c->dek = NULL; if( result == -1 ) ; else if( !result ) { @@ -343,13 +498,13 @@ proc_encrypted( CTX c, PACKET *pkt ) if( pkt->pkt.encrypted->mdc_method ) write_status( STATUS_GOODMDC ); } - else if( result == GPGERR_BAD_SIGN ) { + else if( result == G10ERR_BAD_SIGN ) { log_error(_("WARNING: encrypted message has been manipulated!\n")); write_status( STATUS_BADMDC ); } else { write_status( STATUS_DECRYPTION_FAILED ); - log_error(_("decryption failed: %s\n"), gpg_errstr(result)); + 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)*/ } @@ -372,9 +527,7 @@ proc_plaintext( CTX c, PACKET *pkt ) else if( opt.verbose ) log_info(_("original file name='%.*s'\n"), pt->namelen, pt->name); free_md_filter_context( &c->mfx ); - c->mfx.md = gcry_md_open( 0, 0); - if( !c->mfx.md ) - BUG(); + 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 @@ -385,9 +538,9 @@ proc_plaintext( CTX c, PACKET *pkt ) for(n=c->list; n; n = n->next ) { if( n->pkt->pkttype == PKT_ONEPASS_SIG ) { if( n->pkt->pkt.onepass_sig->digest_algo ) { - gcry_md_enable( c->mfx.md, 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 - == GCRY_MD_MD5 ) + == DIGEST_ALGO_MD5 ) only_md5 = 1; else only_md5 = 0; @@ -395,27 +548,31 @@ proc_plaintext( CTX c, PACKET *pkt ) } if( n->pkt->pkt.onepass_sig->sig_class != 0x01 ) only_md5 = 0; - - /* Check whether this is a cleartext signature. We assume that - * we have one if the sig_class is 1 and the keyid is 0, that - * are the faked packets produced by armor.c. There is a - * possibility that this fails, but there is no other easy way - * to do it. (We could use a special packet type to indicate - * this, but this may also be faked - it simply can't be verified - * and is _no_ security issue) - */ - if( n->pkt->pkt.onepass_sig->sig_class == 0x01 - && !n->pkt->pkt.onepass_sig->keyid[0] - && !n->pkt->pkt.onepass_sig->keyid[1] ) - clearsig = 1; } + else if( n->pkt->pkttype == PKT_GPG_CONTROL + && n->pkt->pkt.gpg_control->control + == CTRLPKT_CLEARSIGN_START ) { + 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; /* no pass signature pakets are expected */ + } } if( !any && !opt.skip_verify ) { /* no onepass sig packet: enable all standard algos */ - gcry_md_enable( c->mfx.md, GCRY_MD_RMD160 ); - gcry_md_enable( c->mfx.md, GCRY_MD_SHA1 ); - gcry_md_enable( c->mfx.md, GCRY_MD_MD5 ); + 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 @@ -423,25 +580,36 @@ proc_plaintext( CTX c, PACKET *pkt ) * 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 = gcry_md_open( GCRY_MD_MD5, 0); - if( !c->mfx.md2 ) - BUG(); + c->mfx.md2 = md_open( DIGEST_ALGO_MD5, 0); } if ( DBG_HASHING ) { - gcry_md_start_debug( c->mfx.md, "verify" ); + md_start_debug( c->mfx.md, "verify" ); if ( c->mfx.md2 ) - gcry_md_start_debug( c->mfx.md2, "verify2" ); + md_start_debug( c->mfx.md2, "verify2" ); } - rc = handle_plaintext( pt, &c->mfx, c->sigs_only, clearsig ); - if( rc == GPGERR_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 ( 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", gpg_errstr(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; } @@ -472,7 +640,7 @@ proc_compressed( CTX c, PACKET *pkt ) else rc = handle_compressed( c, zd, NULL, NULL ); if( rc ) - log_error("uncompressing failed: %s\n", gpg_errstr(rc)); + log_error("uncompressing failed: %s\n", g10_errstr(rc)); free_packet(pkt); c->last_was_session_key = 0; } @@ -482,11 +650,15 @@ proc_compressed( CTX c, PACKET *pkt ) * Returns: 0 = valid signature or an error code */ static int -do_check_sig( CTX c, KBNODE node, int *is_selfsig ) +do_check_sig( CTX c, KBNODE node, int *is_selfsig, int *is_expkey ) { PKT_signature *sig; - GCRY_MD_HD md = NULL, md2 = NULL; - int algo, rc; + MD_HANDLE md = NULL, md2 = NULL; + int algo, rc, dum2; + u32 dummy; + + if(!is_expkey) + is_expkey=&dum2; assert( node->pkt->pkttype == PKT_SIGNATURE ); if( is_selfsig ) @@ -494,42 +666,35 @@ do_check_sig( CTX c, KBNODE node, int *is_selfsig ) sig = node->pkt->pkt.signature; algo = sig->digest_algo; - if( (rc=openpgp_md_test_algo(algo)) ) + if( (rc=check_digest_algo(algo)) ) return rc; if( sig->sig_class == 0x00 ) { if( c->mfx.md ) - md = gcry_md_copy( c->mfx.md ); + md = md_copy( c->mfx.md ); else /* detached signature */ - md = gcry_md_open( 0, 0 ); /* signature_check() will enable the md*/ - if( !md ) - BUG(); + 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 = gcry_md_copy( c->mfx.md ); - if( !md ) - BUG(); - if( c->mfx.md2 ) { - md2 = gcry_md_copy( c->mfx.md2 ); - if( !md2 ) - BUG(); - } + 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 = gcry_md_open( 0, 0 ); /* signature_check() will enable the md*/ - md2 = gcry_md_open( 0, 0 ); - if( !md || !md2 ) - BUG(); + 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 == 0x30 ) { /* classes 0x10..0x17,0x20,0x30 */ + || 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 ); @@ -537,21 +702,21 @@ do_check_sig( CTX c, KBNODE node, int *is_selfsig ) else if( sig->sig_class == 0x20 ) { log_info(_("standalone revocation - " "use \"gpg --import\" to apply\n")); - return GPGERR_NOT_PROCESSED; + return G10ERR_NOT_PROCESSED; } else { log_error("invalid root packet for sigclass %02x\n", sig->sig_class); - return GPGERR_SIG_CLASS; + return G10ERR_SIG_CLASS; } } else - return GPGERR_SIG_CLASS; - rc = signature_check( sig, md ); - if( rc == GPGERR_BAD_SIGN && md2 ) - rc = signature_check( sig, md2 ); - gcry_md_close(md); - gcry_md_close(md2); + return G10ERR_SIG_CLASS; + rc = signature_check2( sig, md, &dummy, is_expkey ); + if( rc == G10ERR_BAD_SIGN && md2 ) + rc = signature_check2( sig, md2, &dummy, is_expkey ); + md_close(md); + md_close(md2); return rc; } @@ -567,8 +732,15 @@ print_userid( PACKET *pkt ) return; } if( opt.with_colons ) - print_string( stdout, pkt->pkt.user_id->name, - pkt->pkt.user_id->len, ':'); + { + 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 ); @@ -576,51 +748,13 @@ print_userid( PACKET *pkt ) static void -print_fingerprint( PKT_public_key *pk, PKT_secret_key *sk ) -{ - byte array[MAX_FINGERPRINT_LEN], *p; - size_t i, n; - - if( sk ) - fingerprint_from_sk( sk, array, &n ); - else - fingerprint_from_pk( pk, array, &n ); - p = array; - if( opt.with_colons ) { - printf("fpr:::::::::"); - for(i=0; i < n ; i++, p++ ) - printf("%02X", *p ); - putchar(':'); - } - else { - printf(" Key fingerprint ="); - if( n == 20 ) { - for(i=0; i < n ; i++, i++, p += 2 ) { - if( i == 10 ) - putchar(' '); - printf(" %02X%02X", *p, p[1] ); - } - } - else { - for(i=0; i < n ; i++, p++ ) { - if( i && !(i%8) ) - putchar(' '); - printf(" %02X", *p ); - } - } - } - putchar('\n'); -} - -static void print_notation_data( PKT_signature *sig ) { size_t n, n1, n2; const byte *p; int seq = 0; - while( (p = enum_sig_subpkt( sig->hashed_data, SIGSUBPKT_NOTATION, - &n, &seq )) ) { + while((p=enum_sig_subpkt(sig->hashed,SIGSUBPKT_NOTATION,&n,&seq,NULL))) { if( n < 8 ) { log_info(_("WARNING: invalid notation data found\n")); return; @@ -639,14 +773,20 @@ print_notation_data( PKT_signature *sig ) putc( '=', log_stream() ); print_string( log_stream(), p+n1, n2, 0 ); putc( '\n', log_stream() ); + write_status_buffer ( STATUS_NOTATION_NAME, p , n1, 0 ); + write_status_buffer ( STATUS_NOTATION_DATA, p+n1, n2, 50 ); } - if( (p = parse_sig_subpkt( sig->hashed_data, SIGSUBPKT_POLICY, &n ) )) { + + seq=0; + + while((p=enum_sig_subpkt(sig->hashed,SIGSUBPKT_POLICY,&n,&seq,NULL))) { log_info(_("Policy: ") ); print_string( log_stream(), p, n, 0 ); putc( '\n', log_stream() ); + write_status_buffer ( STATUS_POLICY_URL, p, n, 0 ); } - /* Now check wheter the key of this signature has some + /* Now check whether the key of this signature has some * notation data */ /* TODO */ @@ -675,7 +815,7 @@ list_node( CTX c, KBNODE node ) if( mainkey ) { c->local_id = pk->local_id; c->trustletter = opt.fast_list_mode? - 0 : query_trust_info( pk, NULL ); + 0 : get_validity_info( pk, NULL ); } printf("%s:", mainkey? "pub":"sub" ); if( c->trustletter ) @@ -684,18 +824,18 @@ list_node( CTX c, KBNODE node ) nbits_from_pk( pk ), pk->pubkey_algo, (ulong)keyid[0],(ulong)keyid[1], - datestr_from_pk( pk ), - pk->expiredate? strtimestamp(pk->expiredate):"" ); + colon_datestr_from_pk( pk ), + colon_strtime (pk->expiredate) ); if( c->local_id ) printf("%lu", c->local_id ); putchar(':'); - if( c->local_id && !opt.fast_list_mode ) - putchar( get_ownertrust_info( c->local_id ) ); + 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 ); + print_fingerprint( pk, NULL, 0 ); printf("rtv:1:%u:\n", node->next->pkt->pkt.ring_trust->trustval ); } @@ -724,7 +864,8 @@ list_node( CTX c, KBNODE node ) else if( node->pkt->pkttype == PKT_USER_ID ) { if( any ) { if( opt.with_colons ) - printf("uid:::::::::"); + printf("%s:::::::::", + node->pkt->pkt.user_id->attrib_data?"uat":"uid"); else printf( "uid%*s", 28, "" ); } @@ -733,7 +874,7 @@ list_node( CTX c, KBNODE node ) putchar(':'); putchar('\n'); if( opt.fingerprint && !any ) - print_fingerprint( pk, NULL ); + print_fingerprint( pk, NULL, 0 ); if( node->next && node->next->pkt->pkttype == PKT_RING_TRUST ) { printf("rtv:2:%u:\n", @@ -757,7 +898,7 @@ list_node( CTX c, KBNODE node ) if( !any ) putchar('\n'); if( !mainkey && opt.fingerprint > 1 ) - print_fingerprint( pk, NULL ); + print_fingerprint( pk, NULL, 0 ); } else if( (mainkey = (node->pkt->pkttype == PKT_SECRET_KEY) ) || node->pkt->pkttype == PKT_SECRET_SUBKEY ) { @@ -771,8 +912,8 @@ list_node( CTX c, KBNODE node ) nbits_from_sk( sk ), sk->pubkey_algo, (ulong)keyid[0],(ulong)keyid[1], - datestr_from_sk( sk ), - sk->expiredate? strtimestamp(sk->expiredate):"" + colon_datestr_from_sk( sk ), + colon_strtime (sk->expiredate) /* fixme: add LID */ ); } else @@ -798,7 +939,8 @@ list_node( CTX c, KBNODE node ) else if( node->pkt->pkttype == PKT_USER_ID ) { if( any ) { if( opt.with_colons ) - printf("uid:::::::::"); + printf("%s:::::::::", + node->pkt->pkt.user_id->attrib_data?"uat":"uid"); else printf( "uid%*s", 28, "" ); } @@ -807,7 +949,7 @@ list_node( CTX c, KBNODE node ) putchar(':'); putchar('\n'); if( opt.fingerprint && !any ) - print_fingerprint( NULL, sk ); + print_fingerprint( NULL, sk, 0 ); any=1; } else if( node->pkt->pkttype == PKT_SECRET_SUBKEY ) { @@ -822,7 +964,7 @@ list_node( CTX c, KBNODE node ) if( !any ) putchar('\n'); if( !mainkey && opt.fingerprint > 1 ) - print_fingerprint( NULL, sk ); + print_fingerprint( NULL, sk, 0 ); } else if( node->pkt->pkttype == PKT_SIGNATURE ) { PKT_signature *sig = node->pkt->pkt.signature; @@ -841,10 +983,11 @@ list_node( CTX c, KBNODE node ) fputs("sig", stdout); if( opt.check_sigs ) { fflush(stdout); - switch( (rc2=do_check_sig( c, node, &is_selfsig )) ) { + switch( (rc2=do_check_sig( c, node, &is_selfsig, NULL )) ) { case 0: sigrc = '!'; break; - case GPGERR_BAD_SIGN: sigrc = '-'; break; - case GPGERR_NO_PUBKEY: sigrc = '?'; break; + case G10ERR_BAD_SIGN: sigrc = '-'; break; + case G10ERR_NO_PUBKEY: + case G10ERR_UNU_PUBKEY: sigrc = '?'; break; default: sigrc = '%'; break; } } @@ -868,13 +1011,13 @@ list_node( CTX c, KBNODE node ) putchar(sigrc); printf("::%d:%08lX%08lX:%s::::", sig->pubkey_algo, (ulong)sig->keyid[0], - (ulong)sig->keyid[1], datestr_from_sig(sig)); + (ulong)sig->keyid[1], colon_datestr_from_sig(sig)); } else printf("%c %08lX %s ", sigrc, (ulong)sig->keyid[1], datestr_from_sig(sig)); if( sigrc == '%' ) - printf("[%s] ", gpg_errstr(rc2) ); + printf("[%s] ", g10_errstr(rc2) ); else if( sigrc == '?' ) ; else if( is_selfsig ) { @@ -887,10 +1030,10 @@ list_node( CTX c, KBNODE node ) else if( !opt.fast_list_mode ) { p = get_user_id( sig->keyid, &n ); print_string( stdout, p, n, opt.with_colons ); - gcry_free(p); + m_free(p); } if( opt.with_colons ) - printf(":%02x:", sig->sig_class ); + printf(":%02x%c:", sig->sig_class, sig->flags.exportable?'x':'l'); putchar('\n'); } else @@ -903,11 +1046,11 @@ int proc_packets( void *anchor, IOBUF a ) { int rc; - CTX c = gcry_xcalloc( 1, sizeof *c ); + CTX c = m_alloc_clear( sizeof *c ); c->anchor = anchor; rc = do_proc_packets( c, a ); - gcry_free( c ); + m_free( c ); return rc; } @@ -917,7 +1060,7 @@ int proc_signature_packets( void *anchor, IOBUF a, STRLIST signedfiles, const char *sigfilename ) { - CTX c = gcry_xcalloc( 1, sizeof *c ); + CTX c = m_alloc_clear( sizeof *c ); int rc; c->anchor = anchor; @@ -925,20 +1068,20 @@ proc_signature_packets( void *anchor, IOBUF a, c->signed_data = signedfiles; c->sigfilename = sigfilename; rc = do_proc_packets( c, a ); - gcry_free( c ); + m_free( c ); return rc; } int proc_encryption_packets( void *anchor, IOBUF a ) { - CTX c = gcry_xcalloc( 1, sizeof *c ); + CTX c = m_alloc_clear( sizeof *c ); int rc; c->anchor = anchor; c->encrypt_only = 1; rc = do_proc_packets( c, a ); - gcry_free( c ); + m_free( c ); return rc; } @@ -946,18 +1089,20 @@ proc_encryption_packets( void *anchor, IOBUF a ) int do_proc_packets( CTX c, IOBUF a ) { - PACKET *pkt = gcry_xmalloc( sizeof *pkt ); + PACKET *pkt = m_alloc( sizeof *pkt ); int rc=0; int any_data=0; int newpkt; c->iobuf = a; init_packet(pkt); - while( (rc=parse_packet(a, pkt, NULL)) != -1 ) { + while( (rc=parse_packet(a, pkt)) != -1 ) { any_data = 1; if( rc ) { free_packet(pkt); - if( rc == GPGERR_INVALID_PACKET ) + /* stop processing hwne an invalid packet has been encountered + * but don't do so when we are doing a --list-packet. */ + if( rc == G10ERR_INVALID_PACKET && opt.list_packets != 2 ) break; continue; } @@ -981,12 +1126,14 @@ do_proc_packets( CTX c, IOBUF a ) case PKT_PUBKEY_ENC: case PKT_ENCRYPTED: case PKT_ENCRYPTED_MDC: - rc = GPGERR_UNEXPECTED; + 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; } } @@ -995,7 +1142,8 @@ do_proc_packets( CTX c, IOBUF a ) case PKT_PUBLIC_KEY: case PKT_SECRET_KEY: case PKT_USER_ID: - rc = GPGERR_UNEXPECTED; + 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; @@ -1005,6 +1153,7 @@ do_proc_packets( CTX c, IOBUF a ) 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; } } @@ -1029,23 +1178,38 @@ do_proc_packets( CTX c, IOBUF a ) 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; } } - if( pkt->pkttype != PKT_SIGNATURE ) + /* 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 = gcry_xmalloc( sizeof *pkt ); + pkt = m_alloc( 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 == GPGERR_INVALID_PACKET ) + if( rc == G10ERR_INVALID_PACKET ) write_status_text( STATUS_NODATA, "3" ); if( any_data ) rc = 0; @@ -1055,9 +1219,9 @@ do_proc_packets( CTX c, IOBUF a ) leave: release_list( c ); - gcry_free(c->dek); + m_free(c->dek); free_packet( pkt ); - gcry_free( pkt ); + m_free( pkt ); free_md_filter_context( &c->mfx ); return rc; } @@ -1068,76 +1232,190 @@ check_sig_and_print( CTX c, KBNODE node ) { PKT_signature *sig = node->pkt->pkt.signature; const char *astr, *tstr; - int rc; + int rc, is_expkey=0; if( opt.skip_verify ) { log_info(_("signature verification suppressed\n")); return 0; } + /* It is not in all cases possible to check multiple signatures: + * PGP 2 (which is also allowed by OpenPGP), does use the packet + * sequence: sig+data, OpenPGP does use onepas+data=sig and GnuPG + * sometimes uses (because I did'nt read the specs right) data+sig. + * Because it is possible to create multiple signatures with + * different packet sequence (e.g. data+sig and sig+data) it might + * not be possible to get it right: let's say we have: + * data+sig, sig+data,sig+data and we have not yet encountered the last + * data, we could also see this a one data with 2 signatures and then + * data+sig. + * To protect against this we check that all signatures follow + * without any intermediate packets. Note, that we won't get this + * error when we use onepass packets or cleartext signatures because + * we reset the list every time + * + * FIXME: Now that we have these marker packets, we should create a + * real grammar and check against this. + */ + { + KBNODE n; + int n_sig=0; + + for (n=c->list; n; n=n->next ) { + if ( n->pkt->pkttype == PKT_SIGNATURE ) + n_sig++; + } + if (n_sig > 1) { /* more than one signature - check sequence */ + int tmp, onepass; + + for (tmp=onepass=0,n=c->list; n; n=n->next ) { + if (n->pkt->pkttype == PKT_ONEPASS_SIG) + onepass++; + else if (n->pkt->pkttype == PKT_GPG_CONTROL + && n->pkt->pkt.gpg_control->control + == CTRLPKT_CLEARSIGN_START ) { + onepass++; /* handle the same way as a onepass */ + } + else if ( (tmp && n->pkt->pkttype != PKT_SIGNATURE) ) { + log_error(_("can't handle these multiple signatures\n")); + return 0; + } + else if ( n->pkt->pkttype == PKT_SIGNATURE ) + tmp = 1; + else if (!tmp && !onepass + && n->pkt->pkttype == PKT_GPG_CONTROL + && n->pkt->pkt.gpg_control->control + == CTRLPKT_PLAINTEXT_MARK ) { + /* plaintext before signatures but no one-pass packets*/ + log_error(_("can't handle these multiple signatures\n")); + return 0; + } + } + } + } + + + tstr = asctimestamp(sig->timestamp); - astr = gcry_pk_algo_name( sig->pubkey_algo ); + astr = pubkey_algo_to_string( sig->pubkey_algo ); log_info(_("Signature made %.*s using %s key ID %08lX\n"), (int)strlen(tstr), tstr, astr? astr: "?", (ulong)sig->keyid[1] ); - rc = do_check_sig(c, node, NULL ); - if( rc == GPGERR_NO_PUBKEY && opt.keyserver_name && opt.auto_key_retrieve) { - if( !hkp_ask_import( sig->keyid ) ) - rc = do_check_sig(c, node, NULL ); + rc = do_check_sig(c, node, NULL, &is_expkey ); + if( rc == G10ERR_NO_PUBKEY && opt.keyserver_scheme && opt.keyserver_options.auto_key_retrieve) { + if( keyserver_import_keyid ( sig->keyid )==0 ) + rc = do_check_sig(c, node, NULL, &is_expkey ); } - if( !rc || rc == GPGERR_BAD_SIGN ) { + if( !rc || rc == G10ERR_BAD_SIGN ) { KBNODE un, keyblock; - char *us; - int count=0; + int count=0, statno; + char keyid_str[50]; + + if(rc) + statno=STATUS_BADSIG; + else if(sig->flags.expired) + statno=STATUS_EXPSIG; + else if(is_expkey) + statno=STATUS_EXPKEYSIG; + else + statno=STATUS_GOODSIG; keyblock = get_pubkeyblock( sig->keyid ); - us = get_long_user_id_string( sig->keyid ); - write_status_text( rc? STATUS_BADSIG : STATUS_GOODSIG, us ); - gcry_free(us); + sprintf (keyid_str, "%08lX%08lX [uncertain] ", + (ulong)sig->keyid[0], (ulong)sig->keyid[1]); - /* fixme: list only user ids which are valid and add information - * about the trustworthiness of each user id, sort them. - * Integrate this with check_signatures_trust(). */ + /* find and print the primary user ID */ for( un=keyblock; un; un = un->next ) { if( un->pkt->pkttype != PKT_USER_ID ) continue; - if( !count++ ) - log_info(rc? _("BAD signature from \"") - : _("Good signature from \"")); - else - log_info( _(" aka \"")); + if ( !un->pkt->pkt.user_id->created ) + continue; + if ( un->pkt->pkt.user_id->is_revoked ) + continue; + if ( !un->pkt->pkt.user_id->is_primary ) + continue; + + 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 ); + + log_info(rc? _("BAD signature from \"") + : sig->flags.expired ? _("Expired signature from \"") + : _("Good signature from \"")); print_utf8_string( log_stream(), un->pkt->pkt.user_id->name, un->pkt->pkt.user_id->len ); fputs("\"\n", log_stream() ); - if( rc ) - break; /* print only one id in this case */ + count++; } if( !count ) { /* just in case that we have no userid */ - log_info(rc? _("BAD signature from \"") + for( un=keyblock; un; un = un->next ) { + if( un->pkt->pkttype == PKT_USER_ID ) + break; + } + + if (opt.always_trust || !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 ); + + log_info(rc? _("BAD signature from \"") + : sig->flags.expired ? _("Expired signature from \"") : _("Good signature from \"")); - fputs("[?]\"\n", log_stream() ); + if (!opt.always_trust && un) { + fputs(_("[uncertain]"), log_stream() ); + putc(' ', log_stream() ); + } + print_utf8_string( log_stream(), + un? un->pkt->pkt.user_id->name:"[?]", + un? un->pkt->pkt.user_id->len:3 ); + 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 ) { + 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_primary ) + continue; + + log_info( _(" aka \"")); + print_utf8_string( log_stream(), un->pkt->pkt.user_id->name, + un->pkt->pkt.user_id->len ); + fputs("\"\n", log_stream() ); + } } release_kbnode( keyblock ); + if( !rc ) print_notation_data( sig ); if( !rc && is_status_enabled() ) { /* print a status response with the fingerprint */ - PKT_public_key *pk = gcry_xcalloc( 1, sizeof *pk ); + PKT_public_key *pk = m_alloc_clear( sizeof *pk ); if( !get_pubkey( pk, sig->keyid ) ) { byte array[MAX_FINGERPRINT_LEN], *p; - char buf[MAX_FINGERPRINT_LEN*2+61]; + char buf[MAX_FINGERPRINT_LEN*2+72]; size_t i, n; fingerprint_from_pk( pk, array, &n ); p = array; for(i=0; i < n ; i++, p++ ) sprintf(buf+2*i, "%02X", *p ); - sprintf(buf+strlen(buf), " %s %lu", + sprintf(buf+strlen(buf), " %s %lu %lu", strtimestamp( sig->timestamp ), - (ulong)sig->timestamp ); + (ulong)sig->timestamp, + (ulong)sig->expiredate ); write_status_text( STATUS_VALIDSIG, buf ); } free_public_key( pk ); @@ -1145,10 +1423,19 @@ check_sig_and_print( CTX c, KBNODE node ) if( !rc ) 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( rc ) - gpg_errors_seen = 1; + g10_errors_seen = 1; if( opt.batch && rc ) - gpg_exit(1); + g10_exit(1); } else { char buf[50]; @@ -1157,12 +1444,12 @@ check_sig_and_print( CTX c, KBNODE node ) sig->pubkey_algo, sig->digest_algo, sig->sig_class, (ulong)sig->timestamp, rc ); write_status_text( STATUS_ERRSIG, buf ); - if( rc == GPGERR_NO_PUBKEY ) { + if( rc == G10ERR_NO_PUBKEY ) { buf[16] = 0; write_status_text( STATUS_NO_PUBKEY, buf ); } - if( rc != GPGERR_NOT_PROCESSED ) - log_error(_("Can't check signature: %s\n"), gpg_errstr(rc) ); + if( rc != G10ERR_NOT_PROCESSED ) + log_error(_("Can't check signature: %s\n"), g10_errstr(rc) ); } return rc; } @@ -1180,6 +1467,18 @@ proc_tree( CTX c, KBNODE node ) 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->local_id = 0; c->trustletter = ' '; if( node->pkt->pkttype == PKT_PUBLIC_KEY @@ -1196,12 +1495,11 @@ proc_tree( CTX c, KBNODE node ) if( !c->have_data ) { free_md_filter_context( &c->mfx ); /* prepare to create all requested message digests */ - if ( !(c->mfx.md = gcry_md_open(0, 0)) ) - BUG(); + c->mfx.md = md_open(0, 0); /* fixme: why looking for the signature packet and not 1passpacket*/ for( n1 = node; (n1 = find_next_kbnode(n1, PKT_SIGNATURE )); ) { - gcry_md_enable( c->mfx.md, n1->pkt->pkt.signature->digest_algo); + md_enable( c->mfx.md, n1->pkt->pkt.signature->digest_algo); } /* ask for file and hash it */ if( c->sigs_only ) { @@ -1211,18 +1509,38 @@ proc_tree( CTX c, KBNODE node ) } else { rc = ask_for_detached_datafile( c->mfx.md, c->mfx.md2, - iobuf_get_fname(c->iobuf), + 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", gpg_errstr(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; @@ -1232,26 +1550,20 @@ proc_tree( CTX c, KBNODE node ) else if( !c->have_data ) { /* detached signature */ free_md_filter_context( &c->mfx ); - c->mfx.md = gcry_md_open(sig->digest_algo, 0); - if ( !c->mfx.md ) - BUG(); + c->mfx.md = md_open(sig->digest_algo, 0); if( !opt.pgp2_workarounds ) ; - else if( sig->digest_algo == GCRY_MD_MD5 + else if( sig->digest_algo == DIGEST_ALGO_MD5 && is_RSA( sig->pubkey_algo ) ) { /* enable a workaround for a pgp2 bug */ - c->mfx.md2 = gcry_md_open( GCRY_MD_MD5, 0 ); - if ( !c->mfx.md2 ) - BUG(); + c->mfx.md2 = md_open( DIGEST_ALGO_MD5, 0 ); } - else if( sig->digest_algo == GCRY_MD_SHA1 - && sig->pubkey_algo == GCRY_PK_DSA + 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 = gcry_md_open( sig->digest_algo, 0 ); - if ( !c->mfx.md2 ) - BUG(); + 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 @@ -1263,6 +1575,11 @@ proc_tree( CTX c, KBNODE node ) */ /* 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, @@ -1270,23 +1587,33 @@ proc_tree( CTX c, KBNODE node ) } else { rc = ask_for_detached_datafile( c->mfx.md, c->mfx.md2, - iobuf_get_fname(c->iobuf), + iobuf_get_real_fname(c->iobuf), (sig->sig_class == 0x01) ); } if( rc ) { - log_error("can't hash datafile: %s\n", gpg_errstr(rc)); + log_error("can't hash datafile: %s\n", g10_errstr(rc)); return; } } - else + 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")); - check_sig_and_print( c, node ); + for( n1 = node; n1; (n1 = find_next_kbnode(n1, PKT_SIGNATURE )) ) + check_sig_and_print( c, n1 ); } - else + 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 index c41ad857e..d6ccacecf 100644 --- a/g10/mdfilter.c +++ b/g10/mdfilter.c @@ -1,5 +1,5 @@ /* mdfilter.c - filter data and calculate a message digest - * Copyright (C) 1998, 1999, 2000 Free Software Foundation, Inc. + * Copyright (C) 1998, 1999, 2000, 2001 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -25,9 +25,9 @@ #include <errno.h> #include <assert.h> -#include <gcrypt.h> #include "errors.h" #include "iobuf.h" +#include "memory.h" #include "util.h" #include "filter.h" @@ -50,9 +50,9 @@ md_filter( void *opaque, int control, i = iobuf_read( a, buf, size ); if( i == -1 ) i = 0; if( i ) { - gcry_md_write(mfx->md, buf, i ); + md_write(mfx->md, buf, i ); if( mfx->md2 ) - gcry_md_write(mfx->md2, buf, i ); + md_write(mfx->md2, buf, i ); } else rc = -1; /* eof */ @@ -67,8 +67,8 @@ md_filter( void *opaque, int control, void free_md_filter_context( md_filter_context_t *mfx ) { - gcry_md_close(mfx->md); - gcry_md_close(mfx->md2); + 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 index 2348e46f0..c2330d959 100644 --- a/g10/misc.c +++ b/g10/misc.c @@ -1,5 +1,5 @@ /* misc.c - miscellaneous functions - * Copyright (C) 1998, 1999, 2000 Free Software Foundation, Inc. + * Copyright (C) 1998, 1999, 2000, 2001, 2002 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -22,28 +22,44 @@ #include <stdio.h> #include <stdlib.h> #include <string.h> +#include <unistd.h> #include <errno.h> +#ifdef HAVE_STAT +#include <sys/stat.h> +#endif #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 -#include <assert.h> - -#include <gcrypt.h> #include "util.h" #include "main.h" +#include "photoid.h" #include "options.h" #include "i18n.h" -#define MAX_EXTERN_MPI_BITS 16384 +const char *g10m_revision_string(int); +const char *g10c_revision_string(int); +const char *g10u_revision_string(int); + +#ifdef __GNUC__ +volatile +#endif + void +pull_in_libs(void) +{ + g10m_revision_string(0); + g10c_revision_string(0); + g10u_revision_string(0); +} + #if defined(__linux__) && defined(__alpha__) && __GLIBC__ < 2 -#warning using trap_unaligned static int setsysinfo(unsigned long op, void *buffer, unsigned long size, int *start, void *arg, unsigned long flag) @@ -68,7 +84,6 @@ trap_unaligned(void) #endif - int disable_core_dumps() { @@ -91,166 +106,23 @@ disable_core_dumps() -/**************** - * write an mpi to out. - */ -int -mpi_write( IOBUF out, MPI a ) -{ - char buffer[(MAX_EXTERN_MPI_BITS+7)/8]; - size_t nbytes; - int rc; - - nbytes = (MAX_EXTERN_MPI_BITS+7)/8; - rc = gcry_mpi_print( GCRYMPI_FMT_PGP, buffer, &nbytes, a ); - if( !rc ) - rc = iobuf_write( out, buffer, nbytes ); - - return rc; -} - -/**************** - * Writye a MPI to out, but in this case it is an opaque one, - * s used vor v3 protected keys. - */ -int -mpi_write_opaque( IOBUF out, MPI a ) -{ - size_t nbytes, nbits; - int rc; - char *p; - - assert( gcry_mpi_get_flag( a, GCRYMPI_FLAG_OPAQUE ) ); - p = gcry_mpi_get_opaque( a, &nbits ); - nbytes = (nbits+7) / 8; - iobuf_put( out, nbits >> 8 ); - iobuf_put( out, nbits ); - rc = iobuf_write( out, p, nbytes ); - return rc; -} - - -/**************** - * Read an external representation of an mpi and return the MPI - * The external format is a 16 bit unsigned value stored in network byte order, - * giving the number of bits for the following integer. The integer is stored - * with MSB first (left padded with zeroes to align on a byte boundary). - */ -MPI -mpi_read(IOBUF inp, unsigned int *ret_nread, int secure) +u16 +checksum_u16( unsigned n ) { - int c, c1, c2, i; - unsigned int nbits, nbytes, nread=0; - MPI a = NULL; - byte *buf = NULL; - byte *p; - - if( (c = c1 = iobuf_get(inp)) == -1 ) - goto leave; - nbits = c << 8; - if( (c = c2 = iobuf_get(inp)) == -1 ) - goto leave; - nbits |= c; - if( nbits > MAX_EXTERN_MPI_BITS ) { - log_error("mpi too large (%u bits)\n", nbits); - goto leave; - } - nread = 2; - nbytes = (nbits+7) / 8; - buf = secure? gcry_xmalloc_secure( nbytes+2 ) : gcry_xmalloc( nbytes+2 ); - p = buf; - p[0] = c1; - p[1] = c2; - for( i=0 ; i < nbytes; i++ ) { - p[i+2] = iobuf_get(inp) & 0xff; - nread++; - } - nread += nbytes; - if( gcry_mpi_scan( &a, GCRYMPI_FMT_PGP, buf, &nread ) ) - a = NULL; - - leave: - gcry_free(buf); - if( nread > *ret_nread ) - log_bug("mpi larger than packet"); - else - *ret_nread = nread; - return a; -} + u16 a; -/**************** - * Same as mpi_read but the value is stored as an opaque MPI. - * This function is used to read encrypted MPI of v3 packets. - */ -GCRY_MPI -mpi_read_opaque(IOBUF inp, unsigned *ret_nread ) -{ - int c, c1, c2, i; - unsigned nbits, nbytes, nread=0; - GCRY_MPI a = NULL; - byte *buf = NULL; - byte *p; - - if( (c = c1 = iobuf_get(inp)) == -1 ) - goto leave; - nbits = c << 8; - if( (c = c2 = iobuf_get(inp)) == -1 ) - goto leave; - nbits |= c; - if( nbits > MAX_EXTERN_MPI_BITS ) { - log_error("mpi too large (%u bits)\n", nbits); - goto leave; - } - nread = 2; - nbytes = (nbits+7) / 8; - buf = gcry_xmalloc( nbytes ); - p = buf; - for( i=0 ; i < nbytes; i++ ) { - p[i] = iobuf_get(inp) & 0xff; + a = (n >> 8) & 0xff; + if( opt.emulate_bugs & EMUBUG_GPGCHKSUM ) { + a |= n & 0xff; + log_debug("csum_u16 emulated for n=%u\n", n); } - nread += nbytes; - a = gcry_mpi_set_opaque(NULL, buf, nbits ); - buf = NULL; - - leave: - gcry_free(buf); - if( nread > *ret_nread ) - log_bug("mpi larger than packet"); else - *ret_nread = nread; + a += n & 0xff; return a; } - -int -mpi_print( FILE *fp, MPI a, int mode ) -{ - int n=0; - - if( !a ) - return fprintf(fp, "[MPI_NULL]"); - if( !mode ) { - unsigned int n1; - n1 = gcry_mpi_get_nbits(a); - n += fprintf(fp, "[%u bits]", n1); - } - else { - int rc; - char *buffer; - - rc = gcry_mpi_aprint( GCRYMPI_FMT_HEX, (void **)&buffer, NULL, a ); - assert( !rc ); - fputs( buffer, fp ); - n += strlen(buffer); - gcry_free( buffer ); - } - return n; -} - - - -u16 -checksum_u16( unsigned n ) +static u16 +checksum_u16_nobug( unsigned n ) { u16 a; @@ -272,22 +144,47 @@ checksum( byte *p, unsigned n ) u16 checksum_mpi( MPI a ) { - int rc; u16 csum; byte *buffer; - size_t nbytes; - - rc = gcry_mpi_print( GCRYMPI_FMT_PGP, NULL, &nbytes, a ); - assert( !rc ); - /* fixme: for numbers not in the suecre memory we - * should use a stack based buffer and only allocate - * a larger one when the mpi_print return an error + unsigned nbytes; + unsigned nbits; + + buffer = mpi_get_buffer( a, &nbytes, NULL ); + /* some versions of gpg encode wrong values for the length of an mpi + * so that mpi_get_nbits() which counts the mpi yields another (shorter) + * value than the one store with the mpi. mpi_get_nbit_info() returns + * this stored value if it is still available. */ - buffer = gcry_is_secure(a)? gcry_xmalloc_secure(nbytes) : gcry_xmalloc(nbytes); - rc = gcry_mpi_print( GCRYMPI_FMT_PGP, buffer, &nbytes, a ); - assert( !rc ); - csum = checksum( buffer, nbytes ); - gcry_free( buffer ); + + if( opt.emulate_bugs & EMUBUG_GPGCHKSUM ) + nbits = 0; + else + nbits = mpi_get_nbit_info(a); + if( !nbits ) + nbits = mpi_get_nbits(a); + csum = checksum_u16( nbits ); + csum += checksum( buffer, nbytes ); + m_free( buffer ); + return csum; +} + +/**************** + * This is the correct function + */ +u16 +checksum_mpi_counted_nbits( MPI a ) +{ + u16 csum; + byte *buffer; + unsigned nbytes; + unsigned nbits; + + buffer = mpi_get_buffer( a, &nbytes, NULL ); + nbits = mpi_get_nbits(a); + mpi_set_nbit_info(a,nbits); + csum = checksum_u16_nobug( nbits ); + csum += checksum( buffer, nbytes ); + m_free( buffer ); return csum; } @@ -327,11 +224,13 @@ print_cipher_algo_note( int algo ) { if( algo >= 100 && algo <= 110 ) no_exp_algo(); - else if( algo == GCRY_CIPHER_3DES - || algo == GCRY_CIPHER_CAST5 - || algo == GCRY_CIPHER_BLOWFISH - || algo == GCRY_CIPHER_RIJNDAEL - || algo == GCRY_CIPHER_TWOFISH + else if( algo == CIPHER_ALGO_3DES + || algo == CIPHER_ALGO_CAST5 + || algo == CIPHER_ALGO_BLOWFISH + || algo == CIPHER_ALGO_TWOFISH + || algo == CIPHER_ALGO_RIJNDAEL + || algo == CIPHER_ALGO_RIJNDAEL192 + || algo == CIPHER_ALGO_RIJNDAEL256 ) ; else { @@ -339,7 +238,7 @@ print_cipher_algo_note( int algo ) if( !did_note ) { did_note = 1; - log_info(_("this cipher algorithm is depreciated; " + log_info(_("this cipher algorithm is deprecated; " "please use a more standard one!\n")); } } @@ -353,6 +252,32 @@ print_digest_algo_note( int 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 @@ -362,122 +287,405 @@ int openpgp_cipher_test_algo( int algo ) { if( algo < 0 || algo > 110 ) - return GCRYERR_INV_CIPHER_ALGO; - return gcry_cipher_test_algo(algo); + return G10ERR_CIPHER_ALGO; + return check_cipher_algo(algo); } int openpgp_pk_test_algo( int algo, unsigned int usage_flags ) { - size_t n = usage_flags; - if( algo < 0 || algo > 110 ) - return GCRYERR_INV_PK_ALGO; - return gcry_pk_algo_info( algo, GCRYCTL_TEST_ALGO, NULL, &n ); + return G10ERR_PUBKEY_ALGO; + return check_pubkey_algo2( algo, usage_flags ); } int openpgp_pk_algo_usage ( int algo ) { - int usage = 0; + int use = 0; - /* some are hardwired */ + /* they are hardwired in gpg 1.0 */ switch ( algo ) { - case GCRY_PK_RSA: - usage = GCRY_PK_USAGE_SIGN | GCRY_PK_USAGE_ENCR; + case PUBKEY_ALGO_RSA: + use = PUBKEY_USAGE_SIG | PUBKEY_USAGE_ENC; break; - case GCRY_PK_RSA_E: - usage = GCRY_PK_USAGE_ENCR; + case PUBKEY_ALGO_RSA_E: + use = PUBKEY_USAGE_ENC; break; - case GCRY_PK_RSA_S: - usage = GCRY_PK_USAGE_SIGN; + case PUBKEY_ALGO_RSA_S: + use = PUBKEY_USAGE_SIG; break; - case GCRY_PK_ELG_E: - usage = GCRY_PK_USAGE_ENCR; + case PUBKEY_ALGO_ELGAMAL_E: + use = PUBKEY_USAGE_ENC; break; - case GCRY_PK_DSA: - usage = GCRY_PK_USAGE_SIGN; + case PUBKEY_ALGO_DSA: + use = PUBKEY_USAGE_SIG; break; - case GCRY_PK_ELG: - usage = GCRY_PK_USAGE_SIGN | GCRY_PK_USAGE_ENCR; + case PUBKEY_ALGO_ELGAMAL: + use = PUBKEY_USAGE_SIG | PUBKEY_USAGE_ENC; break; default: - usage = gcry_pk_algo_info ( algo, GCRYCTL_GET_ALGO_USAGE, - NULL, NULL); + break; } - return usage; - + return use; } - - int openpgp_md_test_algo( int algo ) { if( algo < 0 || algo > 110 ) - return GCRYERR_INV_MD_ALGO; - return gcry_md_test_algo(algo); + return G10ERR_DIGEST_ALGO; + return check_digest_algo(algo); } - int -pubkey_get_npkey( int algo ) +check_permissions(const char *path,int extension,int checkonly) { - int n = gcry_pk_algo_info( algo, GCRYCTL_GET_ALGO_NPKEY, NULL, 0 ); - return n > 0? n : 0; +#if defined(HAVE_STAT) && !defined(HAVE_DOSISH_SYSTEM) + char *tmppath; + struct stat statbuf; + int ret=1; + int isdir=0; + + if(opt.no_perm_warn) + return 0; + + if(extension && path[0]!=DIRSEP_C) + { + if(strchr(path,DIRSEP_C)) + tmppath=make_filename(path,NULL); + else + tmppath=make_filename(GNUPG_LIBDIR,path,NULL); + } + else + tmppath=m_strdup(path); + + /* It's okay if the file doesn't exist */ + if(stat(tmppath,&statbuf)!=0) + { + ret=0; + goto end; + } + + isdir=S_ISDIR(statbuf.st_mode); + + /* We may have to revisit this if we start piping keyrings to gpg + over a named pipe or keyserver character device :) */ + if(!isdir && !S_ISREG(statbuf.st_mode)) + { + ret=0; + goto end; + } + + /* Per-user files must be owned by the user. Extensions must be + owned by the user or root. */ + if((!extension && statbuf.st_uid != getuid()) || + (extension && statbuf.st_uid!=0 && statbuf.st_uid!=getuid())) + { + if(!checkonly) + log_info(_("Warning: unsafe ownership on %s \"%s\"\n"), + isdir?"directory":extension?"extension":"file",path); + goto end; + } + + /* This works for both directories and files - basically, we don't + care what the owner permissions are, so long as the group and + other permissions are 0 for per-user files, and non-writable for + extensions. */ + if((extension && (statbuf.st_mode & (S_IWGRP|S_IWOTH)) !=0) || + (!extension && (statbuf.st_mode & (S_IRWXG|S_IRWXO)) != 0)) + { + char *dir; + + /* However, if the directory the directory/file is in is owned + by the user and is 700, then this is not a problem. + 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,&statbuf)==0 && statbuf.st_uid==getuid() && + S_ISDIR(statbuf.st_mode) && (statbuf.st_mode & (S_IRWXG|S_IRWXO))==0) + { + m_free(dir); + ret=0; + goto end; + } + + m_free(dir); + + if(!checkonly) + log_info(_("Warning: unsafe permissions on %s \"%s\"\n"), + isdir?"directory":extension?"extension":"file",path); + goto end; + } + + ret=0; + + end: + m_free(tmppath); + + return ret; + +#endif /* HAVE_STAT && !HAVE_DOSISH_SYSTEM */ + + return 0; } -int -pubkey_get_nskey( int algo ) +/* Special warning for the IDEA cipher */ +void +idea_cipher_warn(int show) { - int n = gcry_pk_algo_info( algo, GCRYCTL_GET_ALGO_NSKEY, NULL, 0 ); - return n > 0? n : 0; + static int warned=0; + + if(!warned || show) + { + log_info(_("the IDEA cipher plugin is not present\n")); + log_info(_("please see http://www.gnupg.org/why-not-idea.html " + "for more information\n")); + warned=1; + } } -int -pubkey_get_nsig( int algo ) +/* Expand %-strings. Returns a string which must be m_freed. Returns + NULL if the string cannot be expanded (too large). */ +char * +pct_expando(const char *string,struct expando_args *args) { - int n = gcry_pk_algo_info( algo, GCRYCTL_GET_ALGO_NSIGN, NULL, 0 ); - return n > 0? n : 0; + 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); + + 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=m_realloc(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 'f': /* fingerprint */ + { + byte array[MAX_FINGERPRINT_LEN]; + size_t len; + int i; + + if(args->pk) + fingerprint_from_pk(args->pk,array,&len); + else + memset(array,0,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: + m_free(ret); + return NULL; } int -pubkey_get_nenc( int algo ) +hextobyte( const char *s ) { - int n = gcry_pk_algo_info( algo, GCRYCTL_GET_ALGO_NENCR, NULL, 0 ); - return n > 0? n : 0; + int c; + + if( *s >= '0' && *s <= '9' ) + c = 16 * (*s - '0'); + else if( *s >= 'A' && *s <= 'F' ) + c = 16 * (10 + *s - 'A'); + else if( *s >= 'a' && *s <= 'f' ) + c = 16 * (10 + *s - 'a'); + else + return -1; + s++; + if( *s >= '0' && *s <= '9' ) + c += *s - '0'; + else if( *s >= 'A' && *s <= 'F' ) + c += 10 + *s - 'A'; + else if( *s >= 'a' && *s <= 'f' ) + c += 10 + *s - 'a'; + else + return -1; + return c; } - -unsigned int -pubkey_nbits( int algo, MPI *key ) +void +deprecated_warning(const char *configname,unsigned int configlineno, + const char *option,const char *repl1,const char *repl2) { - int rc, nbits; - GCRY_SEXP sexp; + if(configname) + { + if(strncmp("--",option,2)==0) + option+=2; - if( algo == GCRY_PK_DSA ) { - rc = gcry_sexp_build ( &sexp, NULL, - "(public-key(dsa(p%m)(q%m)(g%m)(y%m)))", - key[0], key[1], key[2], key[3] ); - } - else if( algo == GCRY_PK_ELG || algo == GCRY_PK_ELG_E ) { - rc = gcry_sexp_build ( &sexp, NULL, - "(public-key(elg(p%m)(g%m)(y%m)))", - key[0], key[1], key[2] ); - } - else if( algo == GCRY_PK_RSA ) { - rc = gcry_sexp_build ( &sexp, NULL, - "(public-key(rsa(n%m)(e%m)))", - key[0], key[1] ); + if(strncmp("--",repl1,2)==0) + repl1+=2; + + log_info(_("%s:%d: deprecated option \"%s\"\n"), + configname,configlineno,option); } - else - return 0; + else + log_info(_("WARNING: \"%s\" is a deprecated option\n"),option); + + log_info(_("please use \"%s%s\" instead\n"),repl1,repl2); +} + +const char * +compress_algo_to_string(int algo) +{ + const char *s="?"; - if ( rc ) - BUG (); + switch(algo) + { + case 0: + s="Uncompressed"; + break; - nbits = gcry_pk_get_nbits( sexp ); - gcry_sexp_release( sexp ); - return nbits; + case 1: + s="ZIP"; + break; + + case 2: + s="ZLIB"; + break; + } + + return s; } +int +check_compress_algo(int algo) +{ + if(algo>=0 && algo<=2) + return 0; + + return G10ERR_COMPR_ALGO; +} diff --git a/g10/mkdtemp.c b/g10/mkdtemp.c new file mode 100644 index 000000000..0323486a3 --- /dev/null +++ b/g10/mkdtemp.c @@ -0,0 +1,98 @@ +/* mkdtemp.c - libc replacement function + * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + +/* This is a replacement function for mkdtemp in case the platform + we're building on (like mine!) doesn't have it. */ + +#include <config.h> +#include <string.h> +#include <errno.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <fcntl.h> +#include <unistd.h> +#include "types.h" +#include "cipher.h" + +#ifdef MKDIR_TAKES_ONE_ARG +# undef mkdir +# define mkdir(a,b) mkdir(a) +#endif + +char *mkdtemp(char *template) +{ + int attempts,idx,count=0; + byte *ch; + + idx=strlen(template); + + /* Walk backwards to count all the Xes */ + while(idx>0 && template[idx-1]=='X') + { + count++; + idx--; + } + + if(count==0) + { + errno=EINVAL; + return NULL; + } + + ch=&template[idx]; + + /* Try 4 times to make the temp directory */ + for(attempts=0;attempts<4;attempts++) + { + int remaining=count; + char *marker=ch; + byte *randombits; + + idx=0; + + /* Using really random bits is probably overkill here. The + worst thing that can happen with a directory name collision + is that the function will return an error. */ + + randombits=get_random_bits(4*remaining,0,0); + + while(remaining>1) + { + sprintf(marker,"%02X",randombits[idx++]); + marker+=2; + remaining-=2; + } + + /* Any leftover Xes? get_random_bits rounds up to full bytes, + so this is safe. */ + if(remaining>0) + sprintf(marker,"%X",randombits[idx]&0xF); + + m_free(randombits); + + if(mkdir(template,0700)==0) + break; + } + + if(attempts==4) + return NULL; /* keeps the errno from mkdir, whatever it is */ + + return template; +} diff --git a/g10/openfile.c b/g10/openfile.c index 340cfd6fa..1bc4cf04c 100644 --- a/g10/openfile.c +++ b/g10/openfile.c @@ -1,5 +1,5 @@ /* openfile.c - * Copyright (C) 1998, 1999, 2000 Free Software Foundation, Inc. + * Copyright (C) 1998, 1999, 2000, 2001 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -29,7 +29,7 @@ #include <fcntl.h> #include <unistd.h> #include "util.h" -#include <gcrypt.h> +#include "memory.h" #include "ttyio.h" #include "options.h" #include "main.h" @@ -39,11 +39,11 @@ #ifdef USE_ONLY_8DOT3 #define SKELEXT ".skl" #else - #define SKELEXT ".skel" + #define SKELEXT EXTSEP_S "skel" #endif -#ifdef HAVE_DRIVE_LETTERS - #define CMP_FILENAME(a,b) stricmp( (a), (b) ) +#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 @@ -99,24 +99,23 @@ make_outfile_name( const char *iname ) size_t n; if( (!iname || (*iname=='-' && !iname[1]) )) - return gcry_xstrdup("-"); + return m_strdup("-"); n = strlen(iname); - if( n > 4 && ( !CMP_FILENAME(iname+n-4,".gpg") - || !CMP_FILENAME(iname+n-4,".pgp") - || !CMP_FILENAME(iname+n-4,".sig") - || !CMP_FILENAME(iname+n-4,".asc") ) ) { - char *buf = gcry_xstrdup( 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 = m_strdup( iname ); buf[n-4] = 0; return buf; } - else if( n > 5 && !CMP_FILENAME(iname+n-5,".sign") ) { - char *buf = gcry_xstrdup( iname ); + else if( n > 5 && !CMP_FILENAME(iname+n-5, EXTSEP_S "sign") ) { + char *buf = m_strdup( iname ); buf[n-5] = 0; return buf; } - log_info(_("%s: unknown suffix\n"), iname ); return NULL; } @@ -143,19 +142,21 @@ ask_outfile_name( const char *name, size_t namelen ) n = strlen(s) + namelen + 10; defname = name && namelen? make_printable_string( name, namelen, 0): NULL; - prompt = gcry_xmalloc(n); + prompt = m_alloc(n); if( defname ) sprintf(prompt, "%s [%s]: ", s, defname ); else sprintf(prompt, "%s: ", s ); fname = cpr_get("openfile.askoutname", prompt ); cpr_kill_prompt(); - gcry_free(prompt); + m_free(prompt); if( !*fname ) { - gcry_free( fname ); fname = NULL; + m_free( fname ); fname = NULL; fname = defname; defname = NULL; } - gcry_free(defname); + m_free(defname); + if (fname) + trim_spaces (fname); return fname; } @@ -177,7 +178,7 @@ open_outfile( const char *iname, int mode, IOBUF *a ) if( (!iname || (*iname=='-' && !iname[1])) && !opt.outfile ) { if( !(*a = iobuf_create(NULL)) ) { log_error(_("%s: can't open: %s\n"), "[stdout]", strerror(errno) ); - rc = GPGERR_CREATE_FILE; + rc = G10ERR_CREATE_FILE; } else if( opt.verbose ) log_info(_("writing to stdout\n")); @@ -203,7 +204,7 @@ open_outfile( const char *iname, int mode, IOBUF *a ) const char *newsfx = mode==1 ? ".asc" : mode==2 ? ".sig" : ".gpg"; - buf = gcry_xmalloc(strlen(iname)+4+1); + buf = m_alloc(strlen(iname)+4+1); strcpy(buf,iname); dot = strchr(buf, '.' ); if( dot && dot > buf && dot[1] && strlen(dot) <= 4 @@ -215,24 +216,34 @@ open_outfile( const char *iname, int mode, IOBUF *a ) else strcat( buf, newsfx ); #else - buf = gcry_xmalloc(strlen(iname)+4+1); - strcpy(stpcpy(buf,iname), mode==1 ? ".asc" : - mode==2 ? ".sig" : ".gpg"); + buf = m_alloc(strlen(iname)+4+1); + strcpy(stpcpy(buf,iname), mode==1 ? EXTSEP_S "asc" : + mode==2 ? EXTSEP_S "sig" : EXTSEP_S "gpg"); #endif name = buf; } - if( overwrite_filep( name ) ) { + rc = 0; + while( !overwrite_filep (name) ) { + char *tmp = ask_outfile_name (NULL, 0); + if ( !tmp || !*tmp ) { + m_free (tmp); + rc = G10ERR_FILE_EXISTS; + break; + } + m_free (buf); + name = buf = tmp; + } + + if( !rc ) { if( !(*a = iobuf_create( name )) ) { log_error(_("%s: can't create: %s\n"), name, strerror(errno) ); - rc = GPGERR_CREATE_FILE; + rc = G10ERR_CREATE_FILE; } else if( opt.verbose ) log_info(_("writing to `%s'\n"), name ); } - else - rc = GPGERR_FILE_EXISTS; - gcry_free(buf); + m_free(buf); } return rc; } @@ -251,16 +262,16 @@ open_sigfile( const char *iname ) if( iname && !(*iname == '-' && !iname[1]) ) { len = strlen(iname); - if( len > 4 && ( !strcmp(iname + len - 4, ".sig") - || ( len > 5 && !strcmp(iname + len - 5, ".sign") ) - || !strcmp(iname + len - 4, ".asc")) ) { + 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 = gcry_xstrdup(iname); - buf[len-4] = 0 ; + buf = m_strdup(iname); + buf[len-(buf[len-1]=='n'?5:4)] = 0 ; a = iobuf_open( buf ); - if( opt.verbose ) + if( a && opt.verbose ) log_info(_("assuming signed data in `%s'\n"), buf ); - gcry_free(buf); + m_free(buf); } } return a; @@ -282,20 +293,20 @@ copy_options_file( const char *destdir ) if( opt.dry_run ) return; - fname = gcry_xmalloc( strlen(datadir) + strlen(destdir) + 15 ); - strcpy(stpcpy(fname, datadir), "/options" SKELEXT ); + fname = m_alloc( strlen(datadir) + strlen(destdir) + 15 ); + strcpy(stpcpy(fname, datadir), DIRSEP_S "options" SKELEXT ); src = fopen( fname, "r" ); if( !src ) { log_error(_("%s: can't open: %s\n"), fname, strerror(errno) ); - gcry_free(fname); + m_free(fname); return; } - strcpy(stpcpy(fname, destdir), "/options" ); + strcpy(stpcpy(fname, destdir), DIRSEP_S "options" ); dst = fopen( fname, "w" ); if( !dst ) { log_error(_("%s: can't create: %s\n"), fname, strerror(errno) ); fclose( src ); - gcry_free(fname); + m_free(fname); return; } @@ -310,7 +321,7 @@ copy_options_file( const char *destdir ) fclose( dst ); fclose( src ); log_info(_("%s: new options file created\n"), fname ); - gcry_free(fname); + m_free(fname); } @@ -325,12 +336,12 @@ try_make_homedir( const char *fname ) * 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 ) + if( opt.dry_run || opt.no_homedir_creation ) return; if ( ( *defhome == '~' && ( strlen(fname) >= strlen (defhome+1) - && !strcmp(fname+strlen(defhome+1)-strlen(defhome+1), + && !strcmp(fname+strlen(fname)-strlen(defhome+1), defhome+1 ) )) || ( *defhome != '~' && !compare_filenames( fname, defhome ) ) @@ -343,9 +354,6 @@ try_make_homedir( const char *fname ) copy_options_file( fname ); log_info(_("you have to start GnuPG again, " "so it can read the new options file\n") ); - gpg_exit(1); + g10_exit(1); } } - - - diff --git a/g10/options.h b/g10/options.h index b7ef09fe7..74cebe575 100644 --- a/g10/options.h +++ b/g10/options.h @@ -1,5 +1,5 @@ /* options.h - * Copyright (C) 1998, 1999, 2000 Free Software Foundation, Inc. + * Copyright (C) 1998, 1999, 2000, 2001, 2002 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -17,52 +17,82 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA */ -#ifndef GPG_OPTIONS_H -#define GPG_OPTIONS_H +#ifndef G10_OPTIONS_H +#define G10_OPTIONS_H #include <types.h> +#include "main.h" +#include "packet.h" #undef ENABLE_COMMENT_PACKETS /* don't create comment packets */ +#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 int debug; + unsigned debug; int armor; int compress; char *outfile; int dry_run; int list_only; int textmode; + int expert; + int ask_sig_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 */ + 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 def_compress_algo; const char *def_secret_key; char *def_recipient; int def_recipient_self; - int no_comment; + int def_cert_check_level; + int sk_comments; 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; int always_trust; + int pgp2; + int pgp6; + int pgp7; /* if we get any more of these, it's time to look at a + special emulate_pgp variable... */ int rfc1991; int rfc2440; int pgp2_workarounds; @@ -71,33 +101,77 @@ struct { const char *set_filename; const char *comment_string; int throw_keyid; + int show_photos; + const char *photo_viewer; int s2k_mode; int s2k_digest_algo; int s2k_cipher_algo; + int simple_sk_checksum; /* create the deprecated rfc2440 secret + key protection*/ int not_dash_escaped; int escape_from; int lock_once; - const char *keyserver_name; + char *keyserver_scheme; + char *keyserver_host; + char *keyserver_port; + struct + { + int verbose; + int include_revoked; + int include_disabled; + int include_subkeys; + int honor_http_proxy; + int broken_http_proxy; + int use_temp_files; + int keep_temp_files; + int fake_v3_keyids; + int auto_key_retrieve; + STRLIST other; + } keyserver_options; + int exec_disable; + char *def_preference_list; + prefitem_t *personal_cipher_prefs, + *personal_digest_prefs, + *personal_compress_prefs; + int no_perm_warn; + char *temp_dir; int no_encrypt_to; int interactive; - STRLIST notation_data; - const char *set_policy_url; + STRLIST sig_notation_data; + STRLIST cert_notation_data; + int show_notation; + STRLIST sig_policy_url; + STRLIST cert_policy_url; + int show_policy_url; int use_embedded_filename; int allow_non_selfsigned_uid; int allow_freeform_uid; int no_literal; ulong set_filesize; - int honor_http_proxy; int fast_list_mode; + int fixed_list_mode; int ignore_time_conflict; + int ignore_valid_from; + int ignore_crc_error; int command_fd; - int auto_key_retrieve; + const char *override_session_key; + int show_session_key; int use_agent; + const char *gpg_agent_info; int merge_only; 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; + int show_keyring; + struct groupitem *grouplist; } opt; +#define EMUBUG_GPGCHKSUM 1 #define EMUBUG_3DESS2K 2 #define EMUBUG_MDENCODE 4 @@ -112,14 +186,15 @@ struct { #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_PACKET (opt.debug & DBG_PACKET_VALUE) #define DBG_FILTER (opt.debug & DBG_FILTER_VALUE) -#define DBG_MEMORY (opt.debug & DBG_MEMORY_VALUE) #define DBG_CACHE (opt.debug & DBG_CACHE_VALUE) #define DBG_TRUST (opt.debug & DBG_TRUST_VALUE) -#define DBG_CIPHER (opt.debug & DBG_CIPHER_VALUE) #define DBG_HASHING (opt.debug & DBG_HASHING_VALUE) +#define DBG_EXTPROG (opt.debug & DBG_EXTPROG_VALUE) + -#endif /*GPG_OPTIONS_H*/ +#endif /*G10_OPTIONS_H*/ diff --git a/g10/options.skel b/g10/options.skel index 646e0152b..93bcfcd57 100644 --- a/g10/options.skel +++ b/g10/options.skel @@ -2,6 +2,15 @@ These first three lines are not copied to the options file in the users home directory. $Id$ # Options for GnuPG +# Copyright 1998, 1999, 2000, 2001 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 you specify which option file to use (with the # commandline option "--options filename"), GnuPG uses the @@ -22,6 +31,7 @@ $Id$ #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. @@ -30,31 +40,32 @@ $Id$ #default-recipient some-user-id #default-recipient-self - -# The next option is enabled because this one is needed for interoperation -# with PGP 5 users. To enable full OpenPGP compliance you have to remove -# this option. - -force-v3-sigs +# By default GnuPG creates version 3 signatures for data files. This +# is not OpenPGP compliant but PGP 6 requires them. To disable it, +# 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 it this way too. -# To enable full OpenPGP compliance you have to remove this option. - -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. -#charset koi8-r - - -# You may define aliases like this: -# alias mynames -u 0x12345678 -u 0x456789ab -z 9 -# everytime you use --mynames, it will be expanded to the options -# in the above defintion. The name of the alias may not be abbreviated. -# NOTE: This is not yet implemented +# 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 +# Meta data 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. +#charset utf-8 + +# Group names may be defined like this: +# group mynames paige 0x12345678 joe patti +# +# Any time "mynames" is a receipient (-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. # lock the file only once for the lifetime of a process. # if you do not define this, the lock will be obtained and released @@ -70,17 +81,122 @@ lock-once #load-extension rndunix #load-extension rndegd +# 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: +# x-hkp://keyserver.cryptnet.net +# +# Example email keyserver: +# mailto:pgp-public-keys@keys.nl.pgp.net +# +# Example LDAP keyserver: +# ldap://pgp.surfnet.nl:11370 +# +# Regular URL syntax applies, and you can set an alternate port +# through the usual method: +# x-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. +# Most servers do synchronize with each other and DNS round-robin may +# give you a quasi-random server each time. -# GnuPG can import a key from a HKP keyerver if one is missing -# for sercain operations. Is you set this option to a keyserver -# you will be asked in such a case whether GnuPG should try to -# import the key from that server (server do syncronize with each -# others and DNS Round-Robin may give you a random server each time). -# Use "host -l pgp.net | grep www" to figure out a keyserver. -#keyserver wwwkeys.eu.pgp.net +#keyserver x-hkp://keyserver.cryptnet.net +#keyserver mailto:pgp-public-keys@keys.nl.pgp.net +#keyserver ldap://pgp.surfnet.nl:11370 -# The environment variable http_proxy is only used when the -# this option is set. +# Options for keyserver functions +# +# include-disabled = when searching, include keys marked as "disabled" +# on the keyserver (not all keyservers support this). +# +# include-revoked = when searching, 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. + +#keyserver-options auto-key-retrieve include-disabled include-revoked -honor-http-proxy +# Uncomment this line to display photo user IDs in key listings +#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. +# +# The default program is "xloadimage -fork -quiet -title 'KeyID 0x%k' stdin" +# +# Some other viewers: +# photo-viewer "qiv %i" +# photo-viewer "ee %i" +# photo-viewer "display -title 'KeyID 0x%k'" +# +# 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" +# +# Use the Win32 registry to pick a viewer for you: +# On Win95/98/Me (also the default on Win32): +# photo-viewer "start /w" +# On NT/2k/XP: +# photo-viewer "cmd /c start /w" + + +# 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. diff --git a/g10/packet.h b/g10/packet.h index 2f7f16f8c..023680b9e 100644 --- a/g10/packet.h +++ b/g10/packet.h @@ -1,5 +1,5 @@ -/* packet.h - packet read/write stuff - * Copyright (C) 1998, 1999, 2000 Free Software Foundation, Inc. +/* packet.h - packet definitions + * Copyright (C) 1998, 1999, 2000, 2001, 2002 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -18,22 +18,15 @@ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA */ -#ifndef GPG_PACKET_H -#define GPG_PACKET_H +#ifndef G10_PACKET_H +#define G10_PACKET_H #include "types.h" #include "iobuf.h" +#include "mpi.h" +#include "cipher.h" #include "filter.h" - -#ifndef DID_MPI_TYPEDEF - typedef struct gcry_mpi *MPI; - #define DID_MPI_TYPEDEF -#endif - -#define GNUPG_MAX_NPKEY 4 -#define GNUPG_MAX_NSKEY 6 -#define GNUPG_MAX_NSIG 2 -#define GNUPG_MAX_NENC 2 +#include "global.h" #define DEBUG_PARSE_PACKET 1 @@ -54,14 +47,34 @@ typedef enum { 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_PHOTO_ID =17, /* PGP's photo ID */ + PKT_ATTRIBUTE =17, /* PGP's attribute packet */ PKT_ENCRYPTED_MDC =18, /* integrity protected encrypted data */ PKT_MDC =19, /* manipulaion 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; @@ -82,7 +95,7 @@ typedef struct { byte version; byte pubkey_algo; /* algorithm used for public key scheme */ byte throw_keyid; - MPI data[GNUPG_MAX_NENC]; + MPI data[PUBKEY_MAX_NENC]; } PKT_pubkey_enc; @@ -96,25 +109,74 @@ typedef struct { 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]; +}; + +typedef struct { ulong local_id; /* internal use, valid if > 0 */ struct { unsigned checked:1; /* signature has been checked */ unsigned valid:1; /* signature is good (if checked is set) */ unsigned unknown_critical:1; + unsigned exportable:1; + unsigned revocable:1; + unsigned policy_url:1; /* Policy URL is present */ + unsigned notation:1; /* At least one notation is present */ + unsigned expired:1; } flags; u32 keyid[2]; /* 64 bit keyid */ u32 timestamp; /* signature made */ + 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 */ - /* (GCRY_PK_xxx) */ - byte digest_algo; /* algorithm used for digest (DIGEST_ALGO_xxxx) */ - byte *hashed_data; /* all subpackets with hashed data (v4 only) */ - byte *unhashed_data; /* ditto for unhashed data */ + /* (PUBKEY_ALGO_xxx) */ + byte digest_algo; /* algorithm used for digest (DIGEST_ALGO_xxxx) */ + struct revocation_key **revkey; + int numrevkeys; + 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[GNUPG_MAX_NSIG]; + 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; + unsigned long 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; + int help_key_usage; + u32 help_key_expire; + int is_primary; + 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)*/ + int mdc_feature; + u32 created; /* according to the self-signature */ + byte selfsigversion; + char name[1]; +} PKT_user_id; + /**************** * Note about the pkey/skey elements: We assume that the secret keys @@ -125,31 +187,38 @@ typedef struct { 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 selfsigversion; /* highest version of all of the self-sigs */ byte pubkey_algo; /* algorithm used for public key scheme */ - byte pubkey_usage; /* the actual allowed usage as set by getkey() */ - u32 created; /* according to the self-signature */ + 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 */ int is_valid; /* key (especially subkey) is valid */ + int dont_cache; /* do not cache this */ ulong local_id; /* internal use, valid if > 0 */ u32 main_keyid[2]; /* keyid of the primary key */ u32 keyid[2]; /* calculated by keyid_from_pk() */ + prefitem_t *prefs; /* list of preferences (may be NULL) */ + int mdc_feature; /* mdc feature set */ byte *namehash; /* if != NULL: found by this name */ - MPI pkey[GNUPG_MAX_NPKEY]; + PKT_user_id *user_id; /* if != NULL: found by that uid */ + struct revocation_key *revkey; + int numrevkeys; + MPI pkey[PUBKEY_MAX_NPKEY]; } PKT_public_key; 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; - u32 created; /* according to the self-signature */ byte req_usage; byte req_algo; u32 has_expired; /* set to the expiration date if expired */ @@ -164,11 +233,12 @@ typedef struct { /* 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[GNUPG_MAX_NSKEY]; + MPI skey[PUBKEY_MAX_NSKEY]; u16 csum; /* checksum */ } PKT_secret_key; @@ -179,19 +249,6 @@ typedef struct { } PKT_comment; typedef struct { - ulong stored_at; /* the stream offset where it was stored - * by build-packet */ - int len; /* length of the name */ - char *photo; /* if this is not NULL, the packet is a photo ID */ - int photolen; /* and the length of the photo */ - int help_key_usage; - u32 help_key_expire; - int is_primary; - u32 created; /* according to the self-signature */ - char name[1]; -} PKT_user_id; - -typedef struct { u32 len; /* reserved */ byte new_ctb; byte algorithm; @@ -200,6 +257,7 @@ typedef struct { typedef struct { u32 len; /* length of encrypted data */ + int extralen; /* this is (blocksize+2) */ byte new_ctb; /* uses a new CTB */ byte mdc_method; /* > 0: integrity protected encrypted data packet */ IOBUF buf; /* IOBUF reference */ @@ -211,18 +269,25 @@ typedef struct { 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 { @@ -242,6 +307,7 @@ struct packet_struct { 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; }; @@ -276,7 +342,7 @@ typedef enum { SIGSUBPKT_SIGNERS_UID =28, /* signer's user id */ SIGSUBPKT_REVOC_REASON =29, /* reason for revocation */ SIGSUBPKT_FEATURES =30, /* feature flags */ - SIGSUBPKT_PRIV_ADD_SIG =101,/* signatur is also valid for this uid */ + SIGSUBPKT_PRIV_VERIFY_CACHE =101, /* cache verification result (obsolete)*/ SIGSUBPKT_FLAG_CRITICAL=128 } sigsubpkttype_t; @@ -293,39 +359,62 @@ int list_packets( IOBUF a ); int set_packet_list_mode( int mode ); #if DEBUG_PARSE_PACKET -int dbg_search_packet( IOBUF inp, PACKET *pkt, int pkttype, ulong *retpos, const char* file, int lineno ); -int dbg_parse_packet( IOBUF inp, PACKET *ret_pkt, ulong *pos, +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, ulong 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, c ) dbg_parse_packet( (a), (b), (c), __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__ ) +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, int pkttype, ulong *retpos ); -int parse_packet( IOBUF inp, PACKET *ret_pkt, ulong *retpos); +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, ulong stopoff ); +int copy_some_packets( IOBUF inp, IOBUF out, off_t stopoff ); int skip_some_packets( IOBUF inp, unsigned n ); #endif -const byte *enum_sig_subpkt( const byte *buffer, sigsubpkttype_t reqtype, - size_t *ret_n, int *start ); -const byte *parse_sig_subpkt( const byte *buffer, sigsubpkttype_t reqtype, - size_t *ret_n ); -const byte *parse_sig_subpkt2( PKT_signature *sig, - sigsubpkttype_t reqtype, size_t *ret_n ); +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); +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 hash_public_key( GCRY_MD_HD md, PKT_public_key *pk ); +void hash_public_key( MD_HANDLE md, PKT_public_key *pk ); 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,int buflen, + const void *header,int headerlen); /*-- free-packet.c --*/ void free_symkey_enc( PKT_symkey_enc *enc ); @@ -336,18 +425,16 @@ 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 ); -PKT_public_key *copy_public_key_new_namehash( PKT_public_key *d, - PKT_public_key *s, - const byte *namehash ); -void copy_public_parts_to_secret_key( PKT_public_key *pk, - PKT_secret_key *sk ); +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 *copy_user_id( PKT_user_id *d, PKT_user_id *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 ); @@ -356,7 +443,9 @@ int cmp_user_ids( PKT_user_id *a, PKT_user_id *b ); /*-- sig-check.c --*/ -int signature_check( PKT_signature *sig, GCRY_MD_HD digest ); +int signature_check( PKT_signature *sig, MD_HANDLE digest ); +int signature_check2( PKT_signature *sig, MD_HANDLE digest, + u32 *r_expiredate, int *r_expired ); /*-- seckey-cert.c --*/ int is_secret_key_protected( PKT_secret_key *sk ); @@ -365,6 +454,7 @@ 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, @@ -376,7 +466,7 @@ 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( GCRY_MD_HD md, GCRY_MD_HD md2, +int ask_for_detached_datafile( MD_HANDLE md, MD_HANDLE md2, const char *inname, int textmode ); /*-- comment.c --*/ @@ -385,12 +475,19 @@ int write_comment( IOBUF out, const char *s ); /*-- 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, + 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_secret_key *sk, + int (*mksubpkt)(PKT_signature *, void *), + void *opaque ); /*-- keygen.c --*/ PKT_user_id *generate_user_id(void); -#endif /*GPG_PACKET_H*/ +#endif /*G10_PACKET_H*/ diff --git a/g10/parse-packet.c b/g10/parse-packet.c index 691be6662..d57659b6b 100644 --- a/g10/parse-packet.c +++ b/g10/parse-packet.c @@ -1,5 +1,5 @@ /* parse-packet.c - read packets - * Copyright (C) 1998, 1999, 2000 Free Software Foundation, Inc. + * Copyright (C) 1998, 1999, 2000, 2001, 2002 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -24,11 +24,14 @@ #include <string.h> #include <assert.h> -#include <gcrypt.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" @@ -36,8 +39,8 @@ static int mpi_print_mode = 0; static int list_mode = 0; -static int parse( IOBUF inp, PACKET *pkt, int reqtype, - ulong *retpos, int *skip, IOBUF out, int do_skip +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 @@ -59,7 +62,7 @@ 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_photo_id( IOBUF inp, int pkttype, unsigned long pktlen, +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 ); @@ -73,6 +76,8 @@ static int parse_encrypted( IOBUF inp, int pkttype, unsigned long pktlen, PACKET *packet, int new_ctb); 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 ); static unsigned short read_16(IOBUF inp) @@ -100,7 +105,7 @@ set_packet_list_mode( int mode ) { int old = list_mode; list_mode = mode; - mpi_print_mode = (opt.debug & DBG_MPI_VALUE); + mpi_print_mode = DBG_MPI; return old; } @@ -127,53 +132,51 @@ unknown_pubkey_warning( int algo ) */ #ifdef DEBUG_PARSE_PACKET int -dbg_parse_packet( IOBUF inp, PACKET *pkt, ulong *retpos, - const char *dbg_f, int dbg_l ) +dbg_parse_packet( IOBUF inp, PACKET *pkt, const char *dbg_f, int dbg_l ) { int skip, rc; do { - rc = parse( inp, pkt, 0, retpos, - &skip, NULL, 0, "parse", dbg_f, dbg_l ); + 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, ulong *retpos ) +parse_packet( IOBUF inp, PACKET *pkt ) { int skip, rc; do { - rc = parse( inp, pkt, 0, retpos, &skip, NULL, 0 ); + rc = parse( inp, pkt, 0, NULL, &skip, NULL, 0 ); } while( skip ); return rc; } #endif /**************** - * Like parse packet, but only return packets of the given type. + * Like parse packet, but only return secret or public (sub)key packets. */ #ifdef DEBUG_PARSE_PACKET int -dbg_search_packet( IOBUF inp, PACKET *pkt, int pkttype, ulong *retpos, +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, pkttype, retpos, &skip, NULL, 0, "search", dbg_f, dbg_l ); + 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, int pkttype, ulong *retpos ) +search_packet( IOBUF inp, PACKET *pkt, off_t *retpos, int with_uid ) { int skip, rc; do { - rc = parse( inp, pkt, pkttype, retpos, &skip, NULL, 0 ); + rc = parse( inp, pkt, with_uid?2:1, retpos, &skip, NULL, 0 ); } while( skip ); return rc; } @@ -213,7 +216,7 @@ copy_all_packets( IOBUF inp, IOBUF out ) */ #ifdef DEBUG_PARSE_PACKET int -dbg_copy_some_packets( IOBUF inp, IOBUF out, ulong stopoff, +dbg_copy_some_packets( IOBUF inp, IOBUF out, off_t stopoff, const char *dbg_f, int dbg_l ) { PACKET pkt; @@ -228,7 +231,7 @@ dbg_copy_some_packets( IOBUF inp, IOBUF out, ulong stopoff, } #else int -copy_some_packets( IOBUF inp, IOBUF out, ulong stopoff ) +copy_some_packets( IOBUF inp, IOBUF out, off_t stopoff ) { PACKET pkt; int skip, rc=0; @@ -275,14 +278,14 @@ skip_some_packets( IOBUF inp, unsigned n ) /**************** - * Parse packet. Set the variable skip points to to 1 if the packet - * should be skipped; this is the case if either there is a - * requested packet type and the parsed packet doesn't match or the + * 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 reqtype, ulong *retpos, +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 @@ -294,6 +297,7 @@ parse( IOBUF inp, PACKET *pkt, int reqtype, ulong *retpos, byte hdr[8]; int hdrlen; int new_ctb = 0; + int with_uid = (onlykeypkts == 2); *skip = 0; assert( !pkt->pkt.generic ); @@ -307,9 +311,8 @@ parse( IOBUF inp, PACKET *pkt, int reqtype, ulong *retpos, hdrlen=0; hdr[hdrlen++] = ctb; if( !(ctb & 0x80) ) { - log_error("%s: invalid packet (ctb=%02x) near %lu\n", - iobuf_where(inp), ctb, iobuf_tell(inp) ); - rc = GPGERR_INVALID_PACKET; + log_error("%s: invalid packet (ctb=%02x)\n", iobuf_where(inp), ctb ); + rc = G10ERR_INVALID_PACKET; goto leave; } pktlen = 0; @@ -318,7 +321,7 @@ parse( IOBUF inp, PACKET *pkt, int reqtype, ulong *retpos, pkttype = ctb & 0x3f; if( (c = iobuf_get(inp)) == -1 ) { log_error("%s: 1st length byte missing\n", iobuf_where(inp) ); - rc = GPGERR_INVALID_PACKET; + rc = G10ERR_INVALID_PACKET; goto leave; } hdr[hdrlen++] = c; @@ -328,7 +331,7 @@ parse( IOBUF inp, PACKET *pkt, int reqtype, ulong *retpos, pktlen = (c - 192) * 256; if( (c = iobuf_get(inp)) == -1 ) { log_error("%s: 2nd length byte missing\n", iobuf_where(inp) ); - rc = GPGERR_INVALID_PACKET; + rc = G10ERR_INVALID_PACKET; goto leave; } hdr[hdrlen++] = c; @@ -340,7 +343,7 @@ parse( IOBUF inp, PACKET *pkt, int reqtype, ulong *retpos, 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 = GPGERR_INVALID_PACKET; + rc = G10ERR_INVALID_PACKET; goto leave; } pktlen |= (hdr[hdrlen++] = c ); @@ -366,15 +369,30 @@ parse( IOBUF inp, PACKET *pkt, int reqtype, ulong *retpos, } } + 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 = GPGERR_WRITE_FILE; + rc = G10ERR_WRITE_FILE; else rc = copy_packet(inp, out, pkttype, pktlen ); goto leave; } - if( do_skip || !pkttype || (reqtype && pkttype != reqtype) ) { + 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 ) ) { skip_rest(inp, pktlen); *skip = 1; rc = 0; @@ -392,16 +410,16 @@ parse( IOBUF inp, PACKET *pkt, int reqtype, ulong *retpos, #endif } pkt->pkttype = pkttype; - rc = GPGERR_UNKNOWN_PACKET; /* default error */ + rc = G10ERR_UNKNOWN_PACKET; /* default error */ switch( pkttype ) { case PKT_PUBLIC_KEY: case PKT_PUBLIC_SUBKEY: - pkt->pkt.public_key = gcry_xcalloc( 1,sizeof *pkt->pkt.public_key ); + pkt->pkt.public_key = m_alloc_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 = gcry_xcalloc( 1,sizeof *pkt->pkt.secret_key ); + pkt->pkt.secret_key = m_alloc_clear(sizeof *pkt->pkt.secret_key ); rc = parse_key(inp, pkttype, pktlen, hdr, hdrlen, pkt ); break; case PKT_SYMKEY_ENC: @@ -411,19 +429,19 @@ parse( IOBUF inp, PACKET *pkt, int reqtype, ulong *retpos, rc = parse_pubkeyenc(inp, pkttype, pktlen, pkt ); break; case PKT_SIGNATURE: - pkt->pkt.signature = gcry_xcalloc( 1,sizeof *pkt->pkt.signature ); + pkt->pkt.signature = m_alloc_clear(sizeof *pkt->pkt.signature ); rc = parse_signature(inp, pkttype, pktlen, pkt->pkt.signature ); break; case PKT_ONEPASS_SIG: - pkt->pkt.onepass_sig = gcry_xcalloc( 1,sizeof *pkt->pkt.onepass_sig ); + pkt->pkt.onepass_sig = m_alloc_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_PHOTO_ID: - pkt->pkttype = pkttype = PKT_USER_ID; /* must fix it */ - rc = parse_photo_id(inp, pkttype, pktlen, pkt); + 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: @@ -446,6 +464,9 @@ parse( IOBUF inp, PACKET *pkt, int reqtype, ulong *retpos, 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 ); + break; default: skip_packet(inp, pkttype, pktlen); break; @@ -453,7 +474,7 @@ parse( IOBUF inp, PACKET *pkt, int reqtype, ulong *retpos, leave: if( !rc && iobuf_error(inp) ) - rc = GPGERR_INV_KEYRING; + rc = G10ERR_INV_KEYRING; return rc; } @@ -483,23 +504,23 @@ copy_packet( IOBUF inp, IOBUF out, int pkttype, unsigned long pktlen ) if( iobuf_in_block_mode(inp) ) { while( (n = iobuf_read( inp, buf, 100 )) != -1 ) if( iobuf_write(out, buf, n ) ) - return GPGERR_WRITE_FILE; /* write error */ + 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 GPGERR_WRITE_FILE; /* write error */ + 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 GPGERR_READ_FILE; + return G10ERR_READ_FILE; if( iobuf_write(out, buf, n ) ) - return GPGERR_WRITE_FILE; /* write error */ + return G10ERR_WRITE_FILE; /* write error */ } } return 0; @@ -559,7 +580,7 @@ read_rest( IOBUF inp, size_t pktlen ) p = NULL; } else { - p = gcry_xmalloc( pktlen ); + p = m_alloc( pktlen ); for(i=0; pktlen; pktlen--, i++ ) p[i] = iobuf_get(inp); } @@ -572,19 +593,23 @@ 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--; @@ -606,10 +631,11 @@ parse_symkeyenc( IOBUF inp, int pkttype, unsigned long pktlen, PACKET *packet ) } 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 = gcry_xcalloc( 1, sizeof *packet->pkt.symkey_enc + k = packet->pkt.symkey_enc = m_alloc_clear( sizeof *packet->pkt.symkey_enc + seskeylen - 1 ); k->version = version; k->cipher_algo = cipher_algo; @@ -642,24 +668,27 @@ parse_symkeyenc( IOBUF inp, int pkttype, unsigned long pktlen, PACKET *packet ) leave: skip_rest(inp, pktlen); - return 0; + return rc; } static int parse_pubkeyenc( IOBUF inp, int pkttype, unsigned long pktlen, PACKET *packet ) { - unsigned n; + unsigned int n; + int rc = 0; int i, ndata; PKT_pubkey_enc *k; - k = packet->pkt.pubkey_enc = gcry_xcalloc( 1,sizeof *packet->pkt.pubkey_enc); + k = packet->pkt.pubkey_enc = m_alloc_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; @@ -686,12 +715,14 @@ parse_pubkeyenc( IOBUF inp, int pkttype, unsigned long pktlen, PACKET *packet ) mpi_print(stdout, k->data[i], mpi_print_mode ); putchar('\n'); } + if (!k->data[i]) + rc = G10ERR_INVALID_PACKET; } } leave: skip_rest(inp, pktlen); - return 0; + return rc; } @@ -713,6 +744,7 @@ dump_sig_subpkt( int hashed, int type, int critical, type, (unsigned)length ); } + printf("\t%s%ssubpkt %d len %u (", /*)*/ critical ? "critical ":"", hashed ? "hashed ":"", type, (unsigned)length ); @@ -737,13 +769,20 @@ dump_sig_subpkt( int hashed, int type, int critical, printf("%sexportable", *buffer? "":"not "); break; case SIGSUBPKT_TRUST: - p = "trust signature"; + if(length!=2) + p="[invalid trust signature]"; + else + printf("trust signature of level %d, amount %d",buffer[0],buffer[1]); break; case SIGSUBPKT_REGEXP: - p = "regular expression"; + if(!length) + p="[invalid regexp]"; + else + printf("regular expression: \"%s\"",buffer); break; case SIGSUBPKT_REVOCABLE: - p = "revocable"; + if( length ) + printf("%srevocable", *buffer? "":"not "); break; case SIGSUBPKT_KEY_EXPIRE: if( length >= 4 ) @@ -806,7 +845,9 @@ dump_sig_subpkt( int hashed, int type, int critical, printf(" %d", buffer[i] ); break; case SIGSUBPKT_KS_FLAGS: - p = "key server preferences"; + fputs("key server preferences:",stdout); + for(i=0;i<length;i++) + printf(" %02X", buffer[i]); break; case SIGSUBPKT_PREF_KS: p = "preferred key server"; @@ -822,12 +863,12 @@ dump_sig_subpkt( int hashed, int type, int critical, fputs ( "key flags:", stdout ); for( i=0; i < length; i++ ) printf(" %02X", buffer[i] ); - break; + break; case SIGSUBPKT_SIGNERS_UID: p = "signer's user ID"; break; case SIGSUBPKT_REVOC_REASON: - if( length ) { + if( length ) { printf("revocation reason 0x%02x (", *buffer ); print_string( stdout, buffer+1, length-1, ')' ); p = ")"; @@ -846,10 +887,10 @@ dump_sig_subpkt( int hashed, int type, int critical, case SIGSUBPKT_FEATURES: fputs ( "features:", stdout ); for( i=0; i < length; i++ ) - printf(" %02X", buffer[i] ); - break; - case SIGSUBPKT_PRIV_ADD_SIG: /* gnupg private - to be removed */ - p = "signs additional user ID"; + printf(" %02x", buffer[i] ); + break; + case SIGSUBPKT_PRIV_VERIFY_CACHE: + p = "obsolete verification cache"; break; default: p = "?"; break; } @@ -863,10 +904,14 @@ dump_sig_subpkt( int hashed, int type, int critical, * -2 unsupported type * -3 subpacket too short */ -static int +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: @@ -874,12 +919,19 @@ parse_one_sig_subpkt( const byte *buffer, size_t n, int type ) break; return 0; case SIGSUBPKT_KEY_FLAGS: - return 0; + case SIGSUBPKT_KS_FLAGS: + case SIGSUBPKT_PREF_SYM: + case SIGSUBPKT_PREF_HASH: + case SIGSUBPKT_PREF_COMPR: + case SIGSUBPKT_POLICY: + case SIGSUBPKT_FEATURES: + return 0; case SIGSUBPKT_EXPORTABLE: + case SIGSUBPKT_REVOCABLE: if( !n ) break; return 0; - case SIGSUBPKT_ISSUER:/* issuer key ID */ + case SIGSUBPKT_ISSUER: /* issuer key ID */ if( n < 8 ) break; return 0; @@ -891,23 +943,23 @@ parse_one_sig_subpkt( const byte *buffer, size_t n, int type ) if( !n ) break; return 0; - case SIGSUBPKT_PREF_SYM: - case SIGSUBPKT_PREF_HASH: - case SIGSUBPKT_PREF_COMPR: - case SIGSUBPKT_FEATURES: - case SIGSUBPKT_POLICY: - return 0; case SIGSUBPKT_PRIMARY_UID: if ( n != 1 ) break; return 0; - case SIGSUBPKT_PRIV_ADD_SIG: - /* because we use private data, we check the GNUPG marker */ - if( n < 24 ) + case SIGSUBPKT_PRIV_VERIFY_CACHE: + /* We used this in gpg 1.0.5 and 1.0.6 to cache signature + * verification results - it is no longer used. + * "GPG" 0x00 <mode> <stat> + * where mode == 1: valid data, stat == 0: invalid signature + * stat == 1: valid signature + * (because we use private data, we check our marker) */ + if( n < 6 ) break; - if( buffer[0] != 'G' || buffer[1] != 'P' || buffer[2] != 'G' ) + if( buffer[0] != 'G' || buffer[1] != 'P' + || buffer[2] != 'G' || buffer[3] ) return -2; - return 3; + return 4; default: return -1; } return -3; @@ -927,15 +979,18 @@ can_handle_critical( const byte *buffer, size_t n, int type ) 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_FEATURES: case SIGSUBPKT_KEY_FLAGS: + case SIGSUBPKT_PRIMARY_UID: + case SIGSUBPKT_FEATURES: + case SIGSUBPKT_POLICY: /* Is it enough to show the policy? */ return 1; - case SIGSUBPKT_POLICY: /* Is it enough to show the policy? */ default: return 0; } @@ -943,37 +998,40 @@ can_handle_critical( const byte *buffer, size_t n, int type ) const byte * -enum_sig_subpkt( const byte *buffer, sigsubpkttype_t reqtype, - size_t *ret_n, int *start ) +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; + int critical_dummy; int offset; size_t n; int seq = 0; int reqseq = start? *start: 0; - if( !buffer || reqseq == -1 ) { + if(!critical) + critical=&critical_dummy; + + if( !pktbuf || reqseq == -1 ) { /* return some value different from NULL to indicate that - * there is no crtitical bit we do not understand. The caller + * 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*)&buffer : NULL; + return reqtype == SIGSUBPKT_TEST_CRITICAL? (const byte*)&pktbuf : NULL; } - buflen = (*buffer << 8) | buffer[1]; - buffer += 2; + buffer = pktbuf->data; + buflen = pktbuf->len; while( buflen ) { n = *buffer++; buflen--; - if( n == 255 ) { + 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[2] << 8) | buffer[3]; buffer += 4; buflen -= 4; - } - else if( n >= 192 ) { + else if( n >= 192 ) { /* 2 byte special encoded length header */ if( buflen < 2 ) goto too_short; n = (( n - 192 ) << 8) + *buffer + 192; @@ -985,14 +1043,14 @@ enum_sig_subpkt( const byte *buffer, sigsubpkttype_t reqtype, type = *buffer; if( type & 0x80 ) { type &= 0x7f; - critical = 1; + *critical = 1; } else - critical = 0; + *critical = 0; if( !(++seq > reqseq) ) ; else if( reqtype == SIGSUBPKT_TEST_CRITICAL ) { - if( critical ) { + if( *critical ) { if( n-1 > buflen+1 ) goto too_short; if( !can_handle_critical(buffer+1, n-1, type ) ) { @@ -1006,7 +1064,7 @@ enum_sig_subpkt( const byte *buffer, sigsubpkttype_t reqtype, } else if( reqtype < 0 ) /* list packets */ dump_sig_subpkt( reqtype == SIGSUBPKT_LIST_HASHED, - type, critical, buffer, buflen, n ); + type, *critical, buffer, buflen, n ); else if( type == reqtype ) { /* found */ buffer++; n--; @@ -1048,23 +1106,49 @@ enum_sig_subpkt( const byte *buffer, sigsubpkttype_t reqtype, const byte * -parse_sig_subpkt( const byte *buffer, sigsubpkttype_t reqtype, size_t *ret_n ) +parse_sig_subpkt (const subpktarea_t *buffer, sigsubpkttype_t reqtype, + size_t *ret_n) { - return enum_sig_subpkt( buffer, reqtype, ret_n, NULL ); + 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 ) +parse_sig_subpkt2 (PKT_signature *sig, sigsubpkttype_t reqtype, + size_t *ret_n ) { const byte *p; - p = parse_sig_subpkt( sig->hashed_data, reqtype, ret_n ); + p = parse_sig_subpkt (sig->hashed, reqtype, ret_n ); if( !p ) - p = parse_sig_subpkt( sig->unhashed_data, reqtype, ret_n ); + 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=m_realloc(sig->revkey, + sizeof(struct revocation_key *)*(sig->numrevkeys+1)); + sig->revkey[sig->numrevkeys]=revkey; + sig->numrevkeys++; + } + } +} static int parse_signature( IOBUF inp, int pkttype, unsigned long pktlen, @@ -1085,6 +1169,7 @@ parse_signature( IOBUF inp, int pkttype, unsigned long pktlen, 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; } @@ -1099,19 +1184,22 @@ parse_signature( IOBUF inp, int pkttype, unsigned long pktlen, } 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 = GPGERR_INVALID_PACKET; + rc = G10ERR_INVALID_PACKET; goto leave; } if( n ) { - sig->hashed_data = gcry_xmalloc( n + 2 ); - sig->hashed_data[0] = n >> 8; - sig->hashed_data[1] = n; - if( iobuf_read(inp, sig->hashed_data+2, n ) != n ) { - log_error("premature eof while reading hashed signature data\n"); + sig->hashed = m_alloc (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; } @@ -1120,15 +1208,19 @@ parse_signature( IOBUF inp, int pkttype, unsigned long pktlen, n = read_16(inp); pktlen -= 2; /* length of unhashed data */ if( n > 10000 ) { log_error("signature packet: unhashed data too long\n"); - rc = GPGERR_INVALID_PACKET; + rc = G10ERR_INVALID_PACKET; goto leave; } if( n ) { - sig->unhashed_data = gcry_xmalloc( n + 2 ); - sig->unhashed_data[0] = n >> 8; - sig->unhashed_data[1] = n; - if( iobuf_read(inp, sig->unhashed_data+2, n ) != n ) { - log_error("premature eof while reading unhashed signature data\n"); + /* we add 8 extra bytes so that we have space for the signature + * status cache. Well we are wastin this if there is a cache + * packet already, but in the other case it avoids an realloc */ + sig->unhashed = m_alloc (sizeof(*sig->unhashed) + n + 8 - 1 ); + sig->unhashed->size = n + 8; + 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; } @@ -1138,7 +1230,7 @@ parse_signature( IOBUF inp, int pkttype, unsigned long pktlen, if( pktlen < 5 ) { /* sanity check */ log_error("packet(%d) too short\n", pkttype); - rc = GPGERR_INVALID_PACKET; + rc = G10ERR_INVALID_PACKET; goto leave; } @@ -1150,14 +1242,14 @@ parse_signature( IOBUF inp, int pkttype, unsigned long pktlen, /* 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_data, SIGSUBPKT_TEST_CRITICAL, NULL) - || !parse_sig_subpkt( sig->unhashed_data, SIGSUBPKT_TEST_CRITICAL, + 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_data, SIGSUBPKT_SIG_CREATED, NULL ); + p = parse_sig_subpkt (sig->hashed, SIGSUBPKT_SIG_CREATED, NULL ); if( !p ) log_error("signature packet without timestamp\n"); else @@ -1169,6 +1261,37 @@ parse_signature( IOBUF inp, int pkttype, unsigned long pktlen, sig->keyid[0] = buffer_to_u32(p); sig->keyid[1] = buffer_to_u32(p+4); } + + p=parse_sig_subpkt(sig->hashed,SIGSUBPKT_SIG_EXPIRE,NULL); + if(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_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; + + /* 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 ) { @@ -1181,8 +1304,8 @@ parse_signature( IOBUF inp, int pkttype, unsigned long pktlen, sig->digest_algo, sig->digest_start[0], sig->digest_start[1] ); if( is_v4 ) { - parse_sig_subpkt( sig->hashed_data, SIGSUBPKT_LIST_HASHED, NULL ); - parse_sig_subpkt( sig->unhashed_data,SIGSUBPKT_LIST_UNHASHED, NULL); + parse_sig_subpkt (sig->hashed, SIGSUBPKT_LIST_HASHED, NULL ); + parse_sig_subpkt (sig->unhashed, SIGSUBPKT_LIST_UNHASHED, NULL); } } @@ -1206,6 +1329,8 @@ parse_signature( IOBUF inp, int pkttype, unsigned long pktlen, mpi_print(stdout, sig->data[i], mpi_print_mode ); putchar('\n'); } + if (!sig->data[i]) + rc = G10ERR_INVALID_PACKET; } } @@ -1220,14 +1345,17 @@ 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--; @@ -1246,7 +1374,7 @@ parse_onepass_sig( IOBUF inp, int pkttype, unsigned long pktlen, leave: skip_rest(inp, pktlen); - return 0; + return rc; } @@ -1258,14 +1386,14 @@ parse_key( IOBUF inp, int pkttype, unsigned long pktlen, { int i, version, algorithm; unsigned n; - unsigned long timestamp, expiredate; + 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 gpg use old PGP comments packets; + /* early versions of G10 use old PGP comments packets; * luckily all those comments are started by a hash */ if( list_mode ) { printf(":rfc1991 comment packet: \"" ); @@ -1286,17 +1414,21 @@ parse_key( IOBUF inp, int pkttype, unsigned long pktlen, 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 ) + 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; @@ -1304,6 +1436,8 @@ parse_key( IOBUF inp, int pkttype, unsigned long pktlen, expiredate = timestamp + ndays * 86400L; else expiredate = 0; + + max_expiredate=expiredate; } algorithm = iobuf_get_noeof(inp); pktlen--; if( list_mode ) @@ -1320,23 +1454,25 @@ parse_key( IOBUF inp, int pkttype, unsigned long pktlen, 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; /* will be set by getkey functions */ + 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->pubkey_algo = algorithm; - pk->req_usage = 0; - pk->pubkey_usage = 0; /* will be set bey getkey functions */ + pk->req_usage = 0; + pk->pubkey_usage = 0; /* not yet used */ pk->is_revoked = 0; pk->keyid[0] = 0; pk->keyid[1] = 0; @@ -1355,8 +1491,8 @@ parse_key( IOBUF inp, int pkttype, unsigned long pktlen, byte temp[16]; if( !npkey ) { - sk->skey[0] = gcry_mpi_set_opaque( NULL, - read_rest(inp, pktlen), pktlen*8 ); + sk->skey[0] = mpi_set_opaque( NULL, + read_rest(inp, pktlen), pktlen ); pktlen = 0; goto leave; } @@ -1368,16 +1504,22 @@ parse_key( IOBUF inp, int pkttype, unsigned long pktlen, mpi_print(stdout, sk->skey[i], mpi_print_mode ); putchar('\n'); } + 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 == 255 ) { + if( sk->protect.algo == 254 || sk->protect.algo == 255 ) { if( pktlen < 3 ) { - rc = GPGERR_INVALID_PACKET; + rc = G10ERR_INVALID_PACKET; goto leave; } + sk->protect.sha1chk = (sk->protect.algo == 254); sk->protect.algo = iobuf_get_noeof(inp); pktlen--; sk->protect.s2k.mode = iobuf_get_noeof(inp); pktlen--; sk->protect.s2k.hash_algo = iobuf_get_noeof(inp); pktlen--; @@ -1389,7 +1531,7 @@ parse_key( IOBUF inp, int pkttype, unsigned long pktlen, if( list_mode ) printf( "\tunknown S2K %d\n", sk->protect.s2k.mode ); - rc = GPGERR_INVALID_PACKET; + rc = G10ERR_INVALID_PACKET; goto leave; } /* here we know that it is a gnu extension @@ -1421,13 +1563,15 @@ parse_key( IOBUF inp, int pkttype, unsigned long pktlen, printf( "\tunknown %sS2K %d\n", sk->protect.s2k.mode < 1000? "":"GNU ", sk->protect.s2k.mode ); - rc = GPGERR_INVALID_PACKET; + rc = G10ERR_INVALID_PACKET; goto leave; } if( list_mode ) { - printf(", algo: %d, hash: %d", + printf(", 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 ) { @@ -1440,7 +1584,7 @@ parse_key( IOBUF inp, int pkttype, unsigned long pktlen, if( sk->protect.s2k.mode == 3 ) { if( pktlen < 1 ) { - rc = GPGERR_INVALID_PACKET; + rc = G10ERR_INVALID_PACKET; goto leave; } sk->protect.s2k.count = iobuf_get(inp); @@ -1452,7 +1596,7 @@ parse_key( IOBUF inp, int pkttype, unsigned long pktlen, } else { /* old version; no S2K, so we set mode to 0, hash MD5 */ sk->protect.s2k.mode = 0; - sk->protect.s2k.hash_algo = GCRY_MD_MD5; + sk->protect.s2k.hash_algo = DIGEST_ALGO_MD5; if( list_mode ) printf( "\tprotect algo: %d (hash algo: %d)\n", sk->protect.algo, sk->protect.s2k.hash_algo ); @@ -1477,7 +1621,7 @@ parse_key( IOBUF inp, int pkttype, unsigned long pktlen, sk->protect.ivlen = 0; if( pktlen < sk->protect.ivlen ) { - rc = GPGERR_INVALID_PACKET; + rc = G10ERR_INVALID_PACKET; goto leave; } for(i=0; i < sk->protect.ivlen && pktlen; i++, pktlen-- ) @@ -1498,26 +1642,25 @@ parse_key( IOBUF inp, int pkttype, unsigned long pktlen, * So we put the key into secure memory when we unprotect it. */ if( sk->protect.s2k.mode == 1001 ) { /* better set some dummy stuff here */ - sk->skey[npkey] = mpi_set_opaque(NULL, gcry_xstrdup("dummydata"), 10); + sk->skey[npkey] = mpi_set_opaque(NULL, m_strdup("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] = gcry_mpi_set_opaque(NULL, - read_rest(inp, pktlen), pktlen*8 ); + sk->skey[npkey] = mpi_set_opaque(NULL, + read_rest(inp, pktlen), pktlen ); pktlen = 0; if( list_mode ) { printf("\tencrypted stuff follows\n"); } } - else { /* unencrypted v4 or v3 method (where length is not encrypted) */ + else { /* v3 method: the mpi length is not encrypted */ for(i=npkey; i < nskey; i++ ) { - n = pktlen; - sk->skey[i] = sk->is_protected ? mpi_read_opaque(inp, &n ) - : mpi_read( inp, &n, 1 ); - pktlen -=n; + n = pktlen; sk->skey[i] = mpi_read(inp, &n, 0 ); pktlen -=n; + if( sk->is_protected && sk->skey[i] ) + mpi_set_protect_flag(sk->skey[i]); if( list_mode ) { printf( "\tskey[%d]: ", i); if( sk->is_protected ) @@ -1527,7 +1670,11 @@ parse_key( IOBUF inp, int pkttype, unsigned long pktlen, putchar('\n'); } } + if (!sk->skey[i]) + rc = G10ERR_INVALID_PACKET; } + if (rc) + goto leave; sk->csum = read_16(inp); pktlen -= 2; if( list_mode ) { @@ -1539,8 +1686,8 @@ parse_key( IOBUF inp, int pkttype, unsigned long pktlen, PKT_public_key *pk = pkt->pkt.public_key; if( !npkey ) { - pk->pkey[0] = gcry_mpi_set_opaque( NULL, - read_rest(inp, pktlen), pktlen*8 ); + pk->pkey[0] = mpi_set_opaque( NULL, + read_rest(inp, pktlen), pktlen ); pktlen = 0; goto leave; } @@ -1552,7 +1699,11 @@ parse_key( IOBUF inp, int pkttype, unsigned long pktlen, mpi_print(stdout, pk->pkey[i], mpi_print_mode ); putchar('\n'); } + if (!pk->pkey[i]) + rc = G10ERR_INVALID_PACKET; } + if (rc) + goto leave; } leave: @@ -1560,16 +1711,95 @@ parse_key( IOBUF inp, int pkttype, unsigned long pktlen, 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; + + m_free(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=m_realloc(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: + log_error("buffer shorter than attribute subpacket\n"); + uid->attribs=attribs; + uid->numattribs=count; + return count; +} + +static void setup_user_id(PACKET *packet) +{ + packet->pkt.user_id->ref = 1; + packet->pkt.user_id->attribs = NULL; + packet->pkt.user_id->attrib_data = NULL; + packet->pkt.user_id->attrib_len = 0; + packet->pkt.user_id->is_primary = 0; + packet->pkt.user_id->is_revoked = 0; + packet->pkt.user_id->is_expired = 0; + packet->pkt.user_id->expiredate = 0; + packet->pkt.user_id->created = 0; + packet->pkt.user_id->help_key_usage = 0; + packet->pkt.user_id->help_key_expire = 0; + packet->pkt.user_id->prefs = NULL; +} static int parse_user_id( IOBUF inp, int pkttype, unsigned long pktlen, PACKET *packet ) { byte *p; - packet->pkt.user_id = gcry_xmalloc(sizeof *packet->pkt.user_id + pktlen); + packet->pkt.user_id = m_alloc(sizeof *packet->pkt.user_id + pktlen); packet->pkt.user_id->len = pktlen; - packet->pkt.user_id->photo = NULL; - packet->pkt.user_id->photolen = 0; + + setup_user_id(packet); + p = packet->pkt.user_id->name; for( ; pktlen; pktlen--, p++ ) *p = iobuf_get_noeof(inp); @@ -1578,7 +1808,7 @@ parse_user_id( IOBUF inp, int pkttype, unsigned long pktlen, PACKET *packet ) if( list_mode ) { int n = packet->pkt.user_id->len; printf(":user ID packet: \""); - /* fixme: Hey why don't we replace this wioth print_string?? */ + /* 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' ) putchar(*p); @@ -1590,28 +1820,60 @@ parse_user_id( IOBUF inp, int pkttype, unsigned long pktlen, PACKET *packet ) return 0; } +void +make_attribute_uidname(PKT_user_id *uid) +{ + 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,"[%s 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]",uid->attribs->len); + } + + uid->len = strlen(uid->name); +} -/**************** - * PGP generates a packet of type 17. We assume this is a photo ID and - * simply store it here as a comment packet. - */ static int -parse_photo_id( IOBUF inp, int pkttype, unsigned long pktlen, PACKET *packet ) +parse_attribute( IOBUF inp, int pkttype, unsigned long pktlen, PACKET *packet ) { byte *p; - packet->pkt.user_id = gcry_xmalloc(sizeof *packet->pkt.user_id + 30); - sprintf( packet->pkt.user_id->name, "[image of size %lu]", pktlen ); - packet->pkt.user_id->len = strlen(packet->pkt.user_id->name); + packet->pkt.user_id = m_alloc(sizeof *packet->pkt.user_id + 70); + + setup_user_id(packet); - packet->pkt.user_id->photo = gcry_xmalloc(sizeof *packet->pkt.user_id + pktlen); - packet->pkt.user_id->photolen = pktlen; - p = packet->pkt.user_id->photo; + packet->pkt.user_id->attrib_data = m_alloc(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); + if( list_mode ) { - printf(":photo_id packet: %s\n", packet->pkt.user_id->name ); + printf(":attribute packet: %s\n", packet->pkt.user_id->name ); } return 0; } @@ -1622,7 +1884,7 @@ parse_comment( IOBUF inp, int pkttype, unsigned long pktlen, PACKET *packet ) { byte *p; - packet->pkt.comment = gcry_xmalloc(sizeof *packet->pkt.comment + pktlen - 1); + packet->pkt.comment = m_alloc(sizeof *packet->pkt.comment + pktlen - 1); packet->pkt.comment->len = pktlen; p = packet->pkt.comment->data; for( ; pktlen; pktlen--, p++ ) @@ -1647,13 +1909,34 @@ parse_comment( IOBUF inp, int pkttype, unsigned long pktlen, PACKET *packet ) static void parse_trust( IOBUF inp, int pkttype, unsigned long pktlen, PACKET *pkt ) { - int c; - - c = iobuf_get_noeof(inp); - pkt->pkt.ring_trust = gcry_xmalloc( sizeof *pkt->pkt.ring_trust ); - pkt->pkt.ring_trust->trustval = c; - if( list_mode ) - printf(":trust packet: flag=%02x\n", c ); + int c; + + if (pktlen) + { + c = iobuf_get_noeof(inp); + pktlen--; + pkt->pkt.ring_trust = m_alloc( 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 ) + printf(":trust packet: flag=%02x sigcache=%02x\n", + pkt->pkt.ring_trust->trustval, + pkt->pkt.ring_trust->sigcache); + } + else + { + if( list_mode ) + printf(":trust packet: empty\n"); + } + skip_rest (inp, pktlen); } @@ -1661,21 +1944,29 @@ static int parse_plaintext( IOBUF inp, int pkttype, unsigned long pktlen, PACKET *pkt, int new_ctb ) { - int mode, namelen; + int rc = 0; + int mode, namelen, partial=0; PKT_plaintext *pt; byte *p; int c, i; if( pktlen && pktlen < 6 ) { log_error("packet(%d) too short (%lu)\n", pkttype, (ulong)pktlen); + rc = G10ERR_INVALID_PACKET; goto leave; } + /* A packet length of zero indicates partial body length. A zero + data length isn't a zero length packet due to the header (mode, + name, etc), so this is accurate. */ + if(pktlen==0) + partial=1; mode = iobuf_get_noeof(inp); if( pktlen ) pktlen--; namelen = iobuf_get_noeof(inp); if( pktlen ) pktlen--; - pt = pkt->pkt.plaintext = gcry_xmalloc(sizeof *pkt->pkt.plaintext + namelen -1); + pt = pkt->pkt.plaintext = m_alloc(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); @@ -1707,7 +1998,7 @@ parse_plaintext( IOBUF inp, int pkttype, unsigned long pktlen, } leave: - return 0; + return rc; } @@ -1721,7 +2012,7 @@ parse_compressed( IOBUF inp, int pkttype, unsigned long pktlen, * (this should be the last object in a file or * the compress algorithm should know the length) */ - zd = pkt->pkt.compressed = gcry_xmalloc(sizeof *pkt->pkt.compressed ); + zd = pkt->pkt.compressed = m_alloc(sizeof *pkt->pkt.compressed ); zd->len = 0; /* not yet used */ zd->algorithm = iobuf_get_noeof(inp); zd->new_ctb = new_ctb; @@ -1736,10 +2027,18 @@ static int parse_encrypted( IOBUF inp, int pkttype, unsigned long pktlen, PACKET *pkt, int new_ctb ) { + int rc = 0; PKT_encrypted *ed; + unsigned long orig_pktlen = pktlen; - ed = pkt->pkt.encrypted = gcry_xmalloc(sizeof *pkt->pkt.encrypted ); + ed = pkt->pkt.encrypted = m_alloc(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 somesanity + checks on the pkacet length - it doesn't matter that we can't + do it */ + ed->extralen = 0; ed->buf = NULL; ed->new_ctb = new_ctb; ed->mdc_method = 0; @@ -1747,22 +2046,27 @@ parse_encrypted( IOBUF inp, int pkttype, unsigned long pktlen, /* fixme: add some pktlen sanity checks */ int version; - version = iobuf_get_noeof(inp); pktlen--; + 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 = GCRY_MD_SHA1; + ed->mdc_method = DIGEST_ALGO_SHA1; } - if( pktlen && pktlen < 10 ) { /* actually this is blocksize+2 */ + if( orig_pktlen && pktlen < 10 ) { /* actually this is blocksize+2 */ log_error("packet(%d) too short\n", pkttype); + rc = G10ERR_INVALID_PACKET; skip_rest(inp, pktlen); goto leave; } if( list_mode ) { - if( pktlen ) - printf(":encrypted data packet:\n\tlength: %lu\n", pktlen); + if( orig_pktlen ) + printf(":encrypted data packet:\n\tlength: %lu\n", orig_pktlen); else printf(":encrypted data packet:\n\tlength: unknown\n"); if( ed->mdc_method ) @@ -1773,7 +2077,7 @@ parse_encrypted( IOBUF inp, int pkttype, unsigned long pktlen, pktlen = 0; leave: - return 0; + return rc; } @@ -1781,14 +2085,16 @@ 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= gcry_xmalloc(sizeof *pkt->pkt.mdc ); + mdc = pkt->pkt.mdc= m_alloc(sizeof *pkt->pkt.mdc ); if( list_mode ) printf(":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; @@ -1796,6 +2102,90 @@ parse_mdc( IOBUF inp, int pkttype, unsigned long pktlen, *p = iobuf_get_noeof(inp); leave: + return rc; +} + + +/* + * This packet is internally generated by PGG (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 tehre 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 ) +{ + byte *p; + const byte *sesmark; + size_t sesmarklen; + int i; + + if ( list_mode ) + printf(":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 ( list_mode ) + puts ("- gpg control packet"); + + packet->pkt.gpg_control = m_alloc(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; + printf("- private (rest length %lu)\n", pktlen); + if( iobuf_in_block_mode(inp) ) { + while( (c=iobuf_get(inp)) != -1 ) + dump_hex_line(c, &i); + } + else { + for( ; pktlen; pktlen-- ) + dump_hex_line(iobuf_get(inp), &i); + } + putchar('\n'); + } + skip_rest(inp,pktlen); + 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 = m_alloc( sizeof *packet ); + init_packet(packet); + packet->pkttype = PKT_GPG_CONTROL; + packet->pkt.gpg_control = m_alloc(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 index 6b06df72e..c8ebad620 100644 --- a/g10/passphrase.c +++ b/g10/passphrase.c @@ -1,5 +1,5 @@ /* passphrase.c - Get a passphrase - * Copyright (C) 1998, 1999, 2000 Free Software Foundation, Inc. + * Copyright (C) 1998, 1999, 2000, 2001, 2002 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -19,26 +19,60 @@ */ #include <config.h> +#include <stddef.h> #include <stdio.h> #include <stdlib.h> -#include <stddef.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> -#include <unistd.h> +#endif +#if defined (__MINGW32__) || defined (__CYGWIN32__) +# include <windows.h> +#endif #include <errno.h> +#ifdef HAVE_LOCALE_H +#include <locale.h> +#endif -#include <gcrypt.h> #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" -#include "gpga-prot.h" + + +enum gpga_protocol_codes { + /* Request codes */ + GPGA_PROT_GET_VERSION = 1, + GPGA_PROT_GET_PASSPHRASE = 2, + GPGA_PROT_CLEAR_PASSPHRASE= 3, + GPGA_PROT_SHUTDOWN = 4, + GPGA_PROT_FLUSH = 5, + + /* Reply codes */ + GPGA_PROT_REPLY_BASE = 0x10000, + GPGA_PROT_OKAY = 0x10001, + GPGA_PROT_GOT_PASSPHRASE = 0x10002, + + /* Error codes */ + GPGA_PROT_ERROR_BASE = 0x20000, + GPGA_PROT_PROTOCOL_ERROR = 0x20001, + GPGA_PROT_INVALID_REQUEST= 0x20002, + GPGA_PROT_CANCELED = 0x20003, + GPGA_PROT_NO_PASSPHRASE = 0x20004, + GPGA_PROT_BAD_PASSPHRASE = 0x20005, + GPGA_PROT_INVALID_DATA = 0x20006, + GPGA_PROT_NOT_IMPLEMENTED= 0x20007, + GPGA_PROT_UI_PROBLEM = 0x20008 +}; + #define buftou32( p ) ((*(byte*)(p) << 24) | (*((byte*)(p)+1)<< 16) | \ (*((byte*)(p)+2) << 8) | (*((byte*)(p)+3))) @@ -49,11 +83,25 @@ ((byte*)p)[3] = (byte)((a) ); \ } while(0) +#define digitp(p) (*(p) >= '0' && *(p) <= '9') +#define hexdigitp(a) (digitp (a) \ + || (*(a) >= 'A' && *(a) <= 'F') \ + || (*(a) >= 'a' && *(a) <= 'f')) +#define xtoi_1(p) (*(p) <= '9'? (*(p)- '0'): \ + *(p) <= 'F'? (*(p)-'A'+10):(*(p)-'a'+10)) +#define xtoi_2(p) ((xtoi_1(p) * 16) + xtoi_1((p)+1)) + + static char *fd_passwd = NULL; static char *next_pw = NULL; static char *last_pw = NULL; +#if defined (__MINGW32__) || defined (__CYGWIN32__) +static int read_fd = 0; +static int write_fd = 0; +#endif + static void hash_passphrase( DEK *dek, char *pw, STRING2KEY *s2k, int create ); int @@ -71,10 +119,10 @@ have_static_passphrase() void set_next_passphrase( const char *s ) { - gcry_free(next_pw); + m_free(next_pw); next_pw = NULL; if( s ) { - next_pw = gcry_xmalloc_secure( strlen(s)+1 ); + next_pw = m_alloc_secure( strlen(s)+1 ); strcpy(next_pw, s ); } } @@ -101,7 +149,6 @@ read_passphrase_from_fd( int fd ) if ( opt.use_agent ) return; /* not used here */ - if( !opt.batch ) tty_printf("Reading passphrase from file descriptor %d ...", fd ); @@ -109,7 +156,7 @@ read_passphrase_from_fd( int fd ) if( i >= len-1 ) { char *pw2 = pw; len += 100; - pw = gcry_xmalloc_secure( len ); + pw = m_alloc_secure( len ); if( pw2 ) memcpy(pw, pw2, i ); else @@ -122,16 +169,33 @@ read_passphrase_from_fd( int fd ) if( !opt.batch ) tty_printf("\b\b\b \n" ); - gcry_free( fd_passwd ); + m_free( fd_passwd ); fd_passwd = pw; } - static int writen ( int fd, const void *buf, size_t nbytes ) { +#if defined (__MINGW32__) || defined (__CYGWIN32__) + DWORD nwritten, nleft = nbytes; + + while (nleft > 0) { + if ( !WriteFile( (HANDLE)write_fd, buf, nleft, &nwritten, NULL) ) { + log_error("write failed: ec=%d\n", (int)GetLastError()); + return -1; + } + /*log_info("** WriteFile fd=%d nytes=%d nwritten=%d\n", + write_fd, nbytes, (int)nwritten);*/ + Sleep(100); + + nleft -= nwritten; + buf = (const BYTE *)buf + nwritten; + } +#elif defined(HAVE_DOSISH_SYSTEM) || defined(__riscos__) + /* not implemented */ +#else size_t nleft = nbytes; - ssize_t nwritten; + int nwritten; while( nleft > 0 ) { nwritten = write( fd, buf, nleft ); @@ -139,13 +203,15 @@ writen ( int fd, const void *buf, size_t nbytes ) if ( errno == EINTR ) nwritten = 0; else { - log_error ( "writen() failed: %s\n", strerror (errno) ); + log_error ( "write() failed: %s\n", strerror (errno) ); return -1; } } nleft -= nwritten; buf = (const char*)buf + nwritten; } +#endif + return 0; } @@ -153,6 +219,29 @@ writen ( int fd, const void *buf, size_t nbytes ) static int readn ( int fd, void *buf, size_t buflen, size_t *ret_nread ) { +#if defined (__MINGW32__) || defined (__CYGWIN32__) + DWORD nread, nleft = buflen; + + while (nleft > 0) { + if ( !ReadFile( (HANDLE)read_fd, buf, nleft, &nread, NULL) ) { + log_error("read() error: ec=%d\n", (int)GetLastError()); + return -1; + } + if (!nread || GetLastError() == ERROR_BROKEN_PIPE) + break; + /*log_info("** ReadFile fd=%d buflen=%d nread=%d\n", + read_fd, buflen, (int)nread);*/ + Sleep(100); + + nleft -= nread; + buf = (BYTE *)buf + nread; + } + if (ret_nread) + *ret_nread = buflen - nleft; + +#elif defined(HAVE_DOSISH_SYSTEM) || defined(__riscos__) + /* not implemented */ +#else size_t nleft = buflen; int nread; char *p; @@ -175,9 +264,161 @@ readn ( int fd, void *buf, size_t buflen, size_t *ret_nread ) } if( ret_nread ) *ret_nread = buflen - nleft; +#endif + return 0; } +/* read an entire line */ +static int +readline (int fd, char *buf, size_t buflen) +{ + size_t nleft = buflen; + char *p; + int nread = 0; + + while (nleft > 0) + { + int n = read (fd, buf, nleft); + if (n < 0) + { + if (errno == EINTR) + continue; + return -1; /* read error */ + } + else if (!n) + { + return -1; /* incomplete line */ + } + p = buf; + nleft -= n; + buf += n; + nread += n; + + for (; n && *p != '\n'; n--, p++) + ; + if (n) + { + break; /* at least one full line available - that's enough. + This function is just a temporary hack until we use + the assuna lib in gpg. So it is okay to forget + about pending bytes */ + } + } + + return nread; +} + + + +#if !defined (__riscos__) + +#if !defined (__MINGW32__) && !defined (__CYGWIN32__) +/* For the new Assuan protocol we may have to send options */ +static int +agent_send_option (int fd, const char *name, const char *value) +{ + char buf[200]; + int nread; + char *line; + int i; + + line = m_alloc (7 + strlen (name) + 1 + strlen (value) + 2); + strcpy (stpcpy (stpcpy (stpcpy ( + stpcpy (line, "OPTION "), name), "="), value), "\n"); + i = writen (fd, line, strlen (line)); + m_free (line); + if (i) + return -1; + + /* get response */ + nread = readline (fd, buf, DIM(buf)-1); + if (nread < 3) + return -1; + + if (buf[0] == 'O' && buf[1] == 'K' && (buf[2] == ' ' || buf[2] == '\n')) + return 0; /* okay */ + + return -1; +} + +static int +agent_send_all_options (int fd) +{ + char *dft_display = NULL; + 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 (fd, "display", + opt.display ? opt.display : dft_display)) + return -1; + } + + if (!opt.ttyname && ttyname (1)) + dft_ttyname = ttyname (1); + if (opt.ttyname || dft_ttyname) + { + if (agent_send_option (fd, "ttyname", + opt.ttyname ? opt.ttyname : dft_ttyname)) + return -1; + } + + dft_ttytype = getenv ("TERM"); + if (opt.ttytype || (dft_ttyname && dft_ttytype)) + { + if (agent_send_option (fd, "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 = m_strdup (old_lc); + dft_lc = setlocale (LC_CTYPE, ""); +#endif + if (opt.lc_ctype || (dft_ttyname && dft_lc)) + { + rc = agent_send_option (fd, "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); + m_free (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 = m_strdup (old_lc); + dft_lc = setlocale (LC_MESSAGES, ""); +#endif + if (opt.lc_messages || (dft_ttyname && dft_lc)) + { + rc = agent_send_option (fd, "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); + m_free (old_lc); + } +#endif + return rc; +} +#endif /*!__MINGW32__ && !__CYGWIN32__*/ /* @@ -186,33 +427,101 @@ readn ( int fd, void *buf, size_t buflen, size_t *ret_nread ) */ static int -agent_open () +agent_open (int *ret_prot) { +#if defined (__MINGW32__) || defined (__CYGWIN32__) + int fd; + char *infostr, *p; + HANDLE h; + char pidstr[128]; + + *ret_prot = 0; + if ( !(infostr = read_w32_registry_string(NULL, "Software\\GNU\\GnuPG", + "agentPID")) + || *infostr == '0') { + log_error( _("gpg-agent is not available in this session\n")); + return -1; + } + free(infostr); + + sprintf(pidstr, "%u", (unsigned int)GetCurrentProcessId()); + if (write_w32_registry_string(NULL, "Software\\GNU\\GnuPG", + "agentCID", pidstr)) { + log_error( _("can't set client pid for the agent\n") ); + return -1; + } + h = OpenEvent(EVENT_ALL_ACCESS, FALSE, "gpg_agent"); + SetEvent(h); + Sleep(50); /* some time for the server */ + if ( !(p = read_w32_registry_string(NULL, "Software\\GNU\\GnuPG", + "agentReadFD")) ) { + log_error( _("can't get server read FD for the agent\n") ); + return -1; + } + read_fd = atol(p); + free(p); + if ( !(p = read_w32_registry_string(NULL, "Software\\GNU\\GnuPG", + "agentWriteFD")) ) { + log_error ( _("can't get server write FD for the agent\n") ); + return -1; + } + write_fd = atol(p); + free(p); + fd = 0; + + if ( writen ( fd, "GPGA\0\0\0\x01", 8 ) ) { + fd = -1; + } +#else /* Posix */ + int fd; char *infostr, *p; struct sockaddr_un client_addr; size_t len; + int prot; + + if (opt.gpg_agent_info) + infostr = m_strdup (opt.gpg_agent_info); + else + { + infostr = getenv ( "GPG_AGENT_INFO" ); + if ( !infostr ) { + log_error (_("gpg-agent is not available in this session\n")); + opt.use_agent = 0; + return -1; + } + infostr = m_strdup ( infostr ); + } - infostr = getenv ( "GPG_AGENT_INFO" ); - if ( !infostr ) { - log_error (_("gpg-agent is not available in this session\n")); - return -1; - } - infostr = gcry_xstrdup ( infostr ); if ( !(p = strchr ( infostr, ':')) || p == infostr || (p-infostr)+1 >= sizeof client_addr.sun_path ) { - log_error (_("malformed GPG_AGENT_INFO environment variable\n")); - gcry_free (infostr ); + log_error( _("malformed GPG_AGENT_INFO environment variable\n")); + m_free (infostr ); + opt.use_agent = 0; return -1; } - *p = 0; - + *p++ = 0; + /* See whether this is the new gpg-agent using the Assuna protocl. + This agent identifies itself by have an info string with a + version number in the 3rd field. */ + while (*p && *p != ':') + p++; + prot = *p? atoi (p+1) : 0; + if ( prot < 0 || prot > 1) { + log_error (_("gpg-agent protocol version %d is not supported\n"),prot); + m_free (infostr ); + opt.use_agent = 0; + return -1; + } + *ret_prot = prot; + if( (fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1 ) { log_error ("can't create socket: %s\n", strerror(errno) ); - gcry_free (infostr ); + m_free (infostr ); + opt.use_agent = 0; return -1; } - + memset( &client_addr, 0, sizeof client_addr ); client_addr.sun_family = AF_UNIX; strcpy( client_addr.sun_path, infostr ); @@ -222,24 +531,57 @@ agent_open () if( connect( fd, (struct sockaddr*)&client_addr, len ) == -1 ) { log_error ( _("can't connect to `%s': %s\n"), infostr, strerror (errno) ); - gcry_free (infostr ); + m_free (infostr ); close (fd ); + opt.use_agent = 0; return -1; } - gcry_free (infostr); + m_free (infostr); - if ( writen ( fd, "GPGA\0\0\0\x01", 8 ) ) { + if (!prot) { + if ( writen ( fd, "GPGA\0\0\0\x01", 8 ) ) { + close (fd); + fd = -1; + } + } + else { /* assuan based gpg-agent */ + char line[200]; + int nread; + + nread = readline (fd, line, DIM(line)); + if (nread < 3 || !(line[0] == 'O' && line[1] == 'K' + && (line[2] == '\n' || line[2] == ' ')) ) { + log_error ( _("communication problem with gpg-agent\n")); + close (fd ); + opt.use_agent = 0; + return -1; + } + + if (agent_send_all_options (fd)) { + log_error (_("problem with the agent - disabling agent use\n")); close (fd); - fd = -1; + opt.use_agent = 0; + return -1; + } + } +#endif + return fd; } + static void agent_close ( int fd ) { +#if defined (__MINGW32__) || defined (__CYGWIN32__) + HANDLE h = OpenEvent(EVENT_ALL_ACCESS, FALSE, "gpg_agent"); + ResetEvent(h); +#else close (fd); +#endif } +#endif /* !__riscos__ */ @@ -250,210 +592,345 @@ agent_close ( int fd ) * 2: Ditto, but change the text to "repeat entry" */ static char * -agent_get_passphrase ( u32 *keyid, int mode ) +agent_get_passphrase ( u32 *keyid, int mode, const char *tryagain_text ) { - size_t n; - char *atext; - char buf[50]; - int fd = -1; - int nread; - u32 reply; - char *pw = NULL; - PKT_public_key *pk = gcry_xcalloc( 1, sizeof *pk ); - byte fpr[MAX_FINGERPRINT_LEN]; +#if defined(__riscos__) + return NULL; +#else + size_t n; + char *atext; + char buf[50]; + int fd = -1; + int nread; + u32 reply; + char *pw = NULL; + PKT_public_key *pk = m_alloc_clear( sizeof *pk ); + byte fpr[MAX_FINGERPRINT_LEN]; + int prot; #if MAX_FINGERPRINT_LEN < 20 - #error agent needs a 20 byte fingerprint +#error agent needs a 20 byte fingerprint #endif - memset (fpr, 0, MAX_FINGERPRINT_LEN ); - if( keyid && get_pubkey( pk, keyid ) ) - pk = NULL; /* oops: no key for some reason */ - - if ( !mode && pk ) { - char *uid; - size_t uidlen; - const char *algo_name = gcry_pk_algo_name( pk->pubkey_algo ); - const char *timestr; - char *maink; - const char *fmtstr; - - if ( !algo_name ) - algo_name = "?"; - - fmtstr = _(" (main key ID %08lX)"); - maink = gcry_xmalloc ( strlen (fmtstr) + 20 ); - if( keyid[2] && keyid[3] && keyid[0] != keyid[2] - && keyid[1] != keyid[3] ) - sprintf( maink, fmtstr, (ulong)keyid[3] ); - else - *maink = 0; - - uid = get_user_id( keyid, &uidlen ); - timestr = strtimestamp (pk->timestamp); - fmtstr = _("You need a passphrase to unlock the" - " secret key for user:\n" - "\"%.*s\"\n" - "%u-bit %s key, ID %08lX, created %s%s\n" ); - atext = gcry_xmalloc ( 100 + strlen (fmtstr) - + uidlen + 15 + strlen(algo_name) + 8 - + strlen (timestr) + strlen (maink) ); - sprintf (atext, fmtstr, - uidlen, uid, - nbits_from_pk (pk), algo_name, (ulong)keyid[1], timestr, - maink ); - gcry_free (uid); - gcry_free (maink); - - { - size_t dummy; - fingerprint_from_pk( pk, fpr, &dummy ); - } - + memset (fpr, 0, MAX_FINGERPRINT_LEN ); + if( keyid && get_pubkey( pk, keyid ) ) + pk = NULL; /* oops: no key for some reason */ + + if ( !mode && pk ) + { + char *uid; + size_t uidlen; + const char *algo_name = pubkey_algo_to_string ( pk->pubkey_algo ); + const char *timestr; + char *maink; + const char *fmtstr; + + if ( !algo_name ) + algo_name = "?"; + + fmtstr = _(" (main key ID %08lX)"); + maink = m_alloc ( strlen (fmtstr) + 20 ); + if( keyid[2] && keyid[3] && keyid[0] != keyid[2] + && keyid[1] != keyid[3] ) + sprintf( maink, fmtstr, (ulong)keyid[3] ); + else + *maink = 0; + + uid = get_user_id( keyid, &uidlen ); + timestr = strtimestamp (pk->timestamp); + fmtstr = _("You need a passphrase to unlock the" + " secret key for user:\n" + "\"%.*s\"\n" + "%u-bit %s key, ID %08lX, created %s%s\n" ); + atext = m_alloc ( 100 + strlen (fmtstr) + + uidlen + 15 + strlen(algo_name) + 8 + + strlen (timestr) + strlen (maink) ); + sprintf (atext, fmtstr, + uidlen, uid, + nbits_from_pk (pk), algo_name, (ulong)keyid[1], timestr, + maink ); + m_free (uid); + m_free (maink); + + { + size_t dummy; + fingerprint_from_pk( pk, fpr, &dummy ); + } + } - else if (mode == 1 ) - atext = gcry_xstrdup ( _("Enter passphrase\n") ); - else - atext = gcry_xstrdup ( _("Repeat passphrase\n") ); - - - - if ( (fd = agent_open ()) == -1 ) + else if (mode == 1 ) + atext = m_strdup ( _("Enter passphrase\n") ); + else + atext = m_strdup ( _("Repeat passphrase\n") ); + + if ( (fd = agent_open (&prot)) == -1 ) + goto failure; + + if (!prot) + { /* old style protocol */ + n = 4 + 20 + strlen (atext); + u32tobuf (buf, n ); + u32tobuf (buf+4, GPGA_PROT_GET_PASSPHRASE ); + memcpy (buf+8, fpr, 20 ); + if ( writen ( fd, buf, 28 ) || writen ( fd, atext, strlen (atext) ) ) goto failure; - - - n = 4 + 20 + strlen (atext); - u32tobuf (buf, n ); - u32tobuf (buf+4, GPGA_PROT_GET_PASSPHRASE ); - memcpy (buf+8, fpr, 20 ); - if ( writen ( fd, buf, 28 ) || writen ( fd, atext, strlen (atext) ) ) - goto failure; - gcry_free (atext); atext = NULL; - - /* get response */ - if ( readn ( fd, buf, 12, &nread ) ) + m_free (atext); atext = NULL; + + /* get response */ + if ( readn ( fd, buf, 12, &nread ) ) goto failure; - - if ( nread < 8 ) { - log_error ( _("response from agent too short\n") ); - goto failure; - } - n = buftou32 ( buf ); - reply = buftou32 ( buf + 4 ); - if ( reply == GPGA_PROT_GOT_PASSPHRASE ) { - size_t pwlen; - size_t nn; - - if ( nread < 12 || n < 8 ) { - log_error ( _("response from agent too short\n") ); - goto failure; + + if ( nread < 8 ) + { + log_error ( "response from agent too short\n" ); + goto failure; } - pwlen = buftou32 ( buf + 8 ); - nread -= 12; - n -= 8; - if ( pwlen > n || n > 1000 ) { - log_error (_("passphrase too long\n")); - /* or protocol error */ - goto failure; - } - /* we read the whole block in one chunk to give no hints - * on how long the passhrase actually is - this wastes some bytes - * but because we already have this padding we should not loosen - * the by issuing 2 read calls */ - pw = gcry_xmalloc_secure ( n+1 ); - if ( readn ( fd, pw, n, &nn ) ) + n = buftou32 ( buf ); + reply = buftou32 ( buf + 4 ); + if ( reply == GPGA_PROT_GOT_PASSPHRASE ) + { + size_t pwlen; + size_t nn; + + if ( nread < 12 || n < 8 ) + { + log_error ( "response from agent too short\n" ); + goto failure; + } + pwlen = buftou32 ( buf + 8 ); + nread -= 12; + n -= 8; + if ( pwlen > n || n > 1000 ) + { + log_error (_("passphrase too long\n")); + /* or protocol error */ + goto failure; + } + /* we read the whole block in one chunk to give no hints + * on how long the passhrase actually is - this wastes some bytes + * but because we already have this padding we should not loosen + * this by issuing 2 read calls */ + pw = m_alloc_secure ( n+1 ); + if ( readn ( fd, pw, n, &nn ) ) goto failure; - if ( n != nn ) { - log_error (_("invalid response from agent\n")); - goto failure; + if ( n != nn ) + { + log_error (_("invalid response from agent\n")); + goto failure; + } + pw[pwlen] = 0; /* make a C String */ + agent_close (fd); + free_public_key( pk ); + return pw; } - pw[pwlen] = 0; /* make a C String */ - agent_close (fd); - free_public_key( pk ); - return pw; - } - else if ( reply == GPGA_PROT_CANCELED ) { + else if ( reply == GPGA_PROT_CANCELED ) log_info ( _("cancelled by user\n") ); - } - else { + else log_error ( _("problem with the agent: agent returns 0x%lx\n"), - (ulong)reply ); + (ulong)reply ); } + else + { /* The new Assuan protocol */ + char *line, *p; + int i; + + if (!tryagain_text) + tryagain_text = "X"; + + /* We allocate 2 time the needed space for atext so that there + is nenough space for escaping */ + line = m_alloc (15 + 46 + + 3*strlen (tryagain_text) + 3*strlen (atext) + 2); + strcpy (line, "GET_PASSPHRASE "); + p = line+15; + if (!mode) + { + for (i=0; i < 20; i++, p +=2 ) + sprintf (p, "%02X", fpr[i]); + } + else + *p++ = 'X'; /* no caching */ + *p++ = ' '; + for (i=0; tryagain_text[i]; i++) + { + if (tryagain_text[i] < ' ' || tryagain_text[i] == '+') + { + sprintf (p, "%%%02X", tryagain_text[i]); + p += 3; + } + else if (tryagain_text[i] == ' ') + *p++ = '+'; + else + *p++ = tryagain_text[i]; + } + *p++ = ' '; + *p++ = 'X'; /* Use the standard prompt */ + *p++ = ' '; + /* copy description */ + for (i=0; atext[i]; i++) + { + if (atext[i] < ' ' || atext[i] == '+') + { + sprintf (p, "%%%02X", atext[i]); + p += 3; + } + else if (atext[i] == ' ') + *p++ = '+'; + else + *p++ = atext[i]; + } + *p++ = '\n'; + i = writen (fd, line, p - line); + m_free (line); + if (i) + goto failure; + m_free (atext); atext = NULL; + + /* get response */ + pw = m_alloc_secure (500); + nread = readline (fd, pw, 499); + if (nread < 3) + goto failure; + + if (pw[0] == 'O' && pw[1] == 'K' && pw[2] == ' ') + { /* we got a passphrase - convert it back from hex */ + size_t pwlen = 0; + + for (i=3; i < nread && hexdigitp (pw+i); i+=2) + pw[pwlen++] = xtoi_2 (pw+i); + pw[pwlen] = 0; /* make a C String */ + agent_close (fd); + free_public_key( pk ); + return pw; + } + else if (nread > 7 && !memcmp (pw, "ERR 111", 7) + && (pw[7] == ' ' || pw[7] == '\n') ) + log_info (_("cancelled by user\n") ); + else + { + log_error (_("problem with the agent - disabling agent use\n")); + opt.use_agent = 0; + } + } + - - failure: - gcry_free (atext); - if ( fd != -1 ) - agent_close (fd); - gcry_free (pw ); - free_public_key( pk ); - - return NULL; + failure: + m_free (atext); + if ( fd != -1 ) + agent_close (fd); + m_free (pw ); + free_public_key( pk ); + + return NULL; +#endif /* Posix or W32 */ } - /* - * Reste the cached passphrase + * Clear the cached passphrase */ void passphrase_clear_cache ( u32 *keyid, int algo ) { - size_t n; - char buf[50]; - int fd = -1; - int nread; - u32 reply; - PKT_public_key *pk = gcry_xcalloc( 1, sizeof *pk ); - byte fpr[MAX_FINGERPRINT_LEN]; - +#if defined(__riscos__) + return ; +#else + size_t n; + char buf[200]; + int fd = -1; + size_t nread; + u32 reply; + PKT_public_key *pk; + byte fpr[MAX_FINGERPRINT_LEN]; + int prot; + #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 ) ) { - log_debug ("oops, no key in passphrase_clear_cache\n"); - goto failure; /* oops: no key for some reason */ - } - + if (!opt.use_agent) + return; + + pk = m_alloc_clear ( sizeof *pk ); + memset (fpr, 0, MAX_FINGERPRINT_LEN ); + if( !keyid || get_pubkey( pk, keyid ) ) { - size_t dummy; - fingerprint_from_pk( pk, fpr, &dummy ); + log_debug ("oops, no key in passphrase_clear_cache\n"); + goto failure; /* oops: no key for some reason */ } + + { + size_t dummy; + fingerprint_from_pk( pk, fpr, &dummy ); + } - if ( (fd = agent_open ()) == -1 ) + if ( (fd = agent_open (&prot)) == -1 ) + goto failure; + + if (!prot) + { + n = 4 + 20; + u32tobuf (buf, n ); + u32tobuf (buf+4, GPGA_PROT_CLEAR_PASSPHRASE ); + memcpy (buf+8, fpr, 20 ); + if ( writen ( fd, buf, 28 ) ) goto failure; - - n = 4 + 20; - u32tobuf (buf, n ); - u32tobuf (buf+4, GPGA_PROT_CLEAR_PASSPHRASE ); - memcpy (buf+8, fpr, 20 ); - if ( writen ( fd, buf, 28 ) ) + + /* get response */ + if ( readn ( fd, buf, 8, &nread ) ) goto failure; - - /* get response */ - if ( readn ( fd, buf, 8, &nread ) ) - goto failure; - - if ( nread < 8 ) { - log_error ( _("response from agent too short\n") ); + + if ( nread < 8 ) { + log_error ( "response from agent too short\n" ); goto failure; + } + + reply = buftou32 ( buf + 4 ); + if ( reply != GPGA_PROT_OKAY && reply != GPGA_PROT_NO_PASSPHRASE ) + { + log_error ( _("problem with the agent: agent returns 0x%lx\n"), + (ulong)reply ); + } } - - reply = buftou32 ( buf + 4 ); - if ( reply != GPGA_PROT_OKAY && reply != GPGA_PROT_NO_PASSPHRASE ) { - log_error ( _("problem with the agent: agent returns 0x%lx\n"), - (ulong)reply ); + else + { /* The assuan protocol */ + char *line, *p; + int i; + + line = m_alloc (17 + 40 + 2); + strcpy (line, "CLEAR_PASSPHRASE "); + p = line+17; + for (i=0; i < 20; i++, p +=2 ) + sprintf (p, "%02X", fpr[i]); + *p++ = '\n'; + i = writen (fd, line, p - line); + m_free (line); + if (i) + goto failure; + + /* get response */ + nread = readline (fd, buf, DIM(buf)-1); + if (nread < 3) + goto failure; + + if (buf[0] == 'O' && buf[1] == 'K' && (buf[2] == ' ' || buf[2] == '\n')) + ; + else + { + log_error (_("problem with the agent - disabling agent use\n")); + opt.use_agent = 0; + } } - - failure: - if ( fd != -1 ) - agent_close (fd); - free_public_key( pk ); + failure: + if (fd != -1) + agent_close (fd); + free_public_key( pk ); +#endif /* Posix or W32 */ } + + /**************** * Get a passphrase for the secret key with KEYID, display TEXT * if the user needs to enter the passphrase. @@ -467,31 +944,49 @@ passphrase_clear_cache ( u32 *keyid, int algo ) */ DEK * passphrase_to_dek( u32 *keyid, int pubkey_algo, - int cipher_algo, STRING2KEY *s2k, int mode ) + int cipher_algo, STRING2KEY *s2k, int mode, + const char *tryagain_text) { char *pw = NULL; DEK *dek; STRING2KEY help_s2k; if( !s2k ) { + /* This is used for the old rfc1991 mode + * Note: This must match the code in encode.c with opt.rfc1991 set */ + int algo = opt.def_digest_algo ? opt.def_digest_algo + : opt.s2k_digest_algo; + s2k = &help_s2k; s2k->mode = 0; - /* this should be MD5 if cipher is IDEA, but because we do - * not have IDEA, we use the default one, the user - * can select it from the commandline - */ - s2k->hash_algo = opt.def_digest_algo?opt.def_digest_algo - :DEFAULT_DIGEST_ALGO; + s2k->hash_algo = algo; } if( !next_pw && is_status_enabled() ) { char buf[50]; + if( keyid ) { - sprintf( buf, "%08lX%08lX", (ulong)keyid[0], (ulong)keyid[1] ); - if( keyid[2] && keyid[3] && keyid[0] != keyid[2] - && keyid[1] != keyid[3] ) - sprintf( buf+strlen(buf), " %08lX%08lX %d 0", - (ulong)keyid[2], (ulong)keyid[3], pubkey_algo ); + 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 ); + m_free(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 { @@ -501,7 +996,7 @@ passphrase_to_dek( u32 *keyid, int pubkey_algo, } if( keyid && !opt.batch && !next_pw ) { - PKT_public_key *pk = gcry_xcalloc( 1, sizeof *pk ); + PKT_public_key *pk = m_alloc_clear( sizeof *pk ); size_t n; char *p; @@ -509,11 +1004,11 @@ passphrase_to_dek( u32 *keyid, int pubkey_algo, "user: \"") ); p = get_user_id( keyid, &n ); tty_print_utf8_string( p, n ); - gcry_free(p); + m_free(p); tty_printf("\"\n"); if( !get_pubkey( pk, keyid ) ) { - const char *s = gcry_pk_algo_name( pk->pubkey_algo ); + const char *s = pubkey_algo_to_string( pk->pubkey_algo ); tty_printf( _("%u-bit %s key, ID %08lX, created %s"), nbits_from_pk( pk ), s?s:"?", (ulong)keyid[1], strtimestamp(pk->timestamp) ); @@ -527,33 +1022,46 @@ passphrase_to_dek( u32 *keyid, int pubkey_algo, free_public_key( pk ); } + agent_died: if( next_pw ) { pw = next_pw; next_pw = NULL; } else if ( opt.use_agent ) { - pw = agent_get_passphrase ( keyid, mode == 2? 1: 0 ); - if ( !pw ) - pw = gcry_xstrdup (""); + pw = agent_get_passphrase ( keyid, mode == 2? 1: 0, tryagain_text ); + if (!pw) + { + if (!opt.use_agent) + goto agent_died; + pw = m_strdup (""); + } if( *pw && mode == 2 ) { - char *pw2 = agent_get_passphrase ( keyid, 2 ); - if ( !pw2 ) - pw2 = gcry_xstrdup (""); + char *pw2 = agent_get_passphrase ( keyid, 2, NULL ); + if (!pw2) + { + if (!opt.use_agent) + { + m_free (pw); + pw = NULL; + goto agent_died; + } + pw2 = m_strdup (""); + } if( strcmp(pw, pw2) ) { - gcry_free(pw2); - gcry_free(pw); + m_free(pw2); + m_free(pw); return NULL; } - gcry_free(pw2); + m_free(pw2); } } else if( fd_passwd ) { - pw = gcry_xmalloc_secure( strlen(fd_passwd)+1 ); + pw = m_alloc_secure( strlen(fd_passwd)+1 ); strcpy( pw, fd_passwd ); } else if( opt.batch ) { log_error(_("can't query password in batchmode\n")); - pw = gcry_xstrdup( "" ); /* return an empty passphrase */ + pw = m_strdup( "" ); /* return an empty passphrase */ } else { pw = cpr_get_hidden("passphrase.enter", _("Enter passphrase: ") ); @@ -563,24 +1071,24 @@ passphrase_to_dek( u32 *keyid, int pubkey_algo, _("Repeat passphrase: ") ); tty_kill_prompt(); if( strcmp(pw, pw2) ) { - gcry_free(pw2); - gcry_free(pw); + m_free(pw2); + m_free(pw); return NULL; } - gcry_free(pw2); + m_free(pw2); } } if( !pw || !*pw ) write_status( STATUS_MISSING_PASSPHRASE ); - dek = gcry_xmalloc_secure( sizeof *dek ); + dek = m_alloc_secure_clear ( sizeof *dek ); dek->algo = cipher_algo; if( !*pw && mode == 2 ) dek->keylen = 0; else hash_passphrase( dek, pw, s2k, mode==2 ); - gcry_free(last_pw); + m_free(last_pw); last_pw = pw; return dek; } @@ -594,24 +1102,28 @@ passphrase_to_dek( u32 *keyid, int pubkey_algo, static void hash_passphrase( DEK *dek, char *pw, STRING2KEY *s2k, int create ) { - GCRY_MD_HD md; + MD_HANDLE md; int pass, i; int used = 0; int pwlen = strlen(pw); assert( s2k->hash_algo ); - dek->keylen = gcry_cipher_get_algo_keylen( dek->algo ); + dek->keylen = cipher_get_keylen( dek->algo ) / 8; if( !(dek->keylen > 0 && dek->keylen <= DIM(dek->key)) ) BUG(); - if( !(md = gcry_md_open( s2k->hash_algo, GCRY_MD_FLAG_SECURE )) ) - BUG(); - + md = md_open( s2k->hash_algo, 1); for(pass=0; used < dek->keylen ; pass++ ) { if( pass ) { - gcry_md_reset(md); + if( (opt.emulate_bugs & EMUBUG_3DESS2K)) { + int tmp = md->finalized; + md_reset( md ); + md->finalized = tmp; + } + else + md_reset(md); for(i=0; i < pass; i++ ) /* preset the hash context */ - gcry_md_putc(md, 0 ); + md_putc(md, 0 ); } if( s2k->mode == 1 || s2k->mode == 3 ) { @@ -619,7 +1131,7 @@ hash_passphrase( DEK *dek, char *pw, STRING2KEY *s2k, int create ) ulong count = len2; if( create && !pass ) { - gcry_randomize(s2k->salt, 8, GCRY_STRONG_RANDOM ); + randomize_buffer(s2k->salt, 8, 1); if( s2k->mode == 3 ) s2k->count = 96; /* 65536 iterations */ } @@ -631,27 +1143,27 @@ hash_passphrase( DEK *dek, char *pw, STRING2KEY *s2k, int create ) } /* a little bit complicated because we need a ulong for count */ while( count > len2 ) { /* maybe iterated+salted */ - gcry_md_write( md, s2k->salt, 8 ); - gcry_md_write( md, pw, pwlen ); + md_write( md, s2k->salt, 8 ); + md_write( md, pw, pwlen ); count -= len2; } if( count < 8 ) - gcry_md_write( md, s2k->salt, count ); + md_write( md, s2k->salt, count ); else { - gcry_md_write( md, s2k->salt, 8 ); + md_write( md, s2k->salt, 8 ); count -= 8; - gcry_md_write( md, pw, count ); + md_write( md, pw, count ); } } else - gcry_md_write( md, pw, pwlen ); - gcry_md_final( md ); - i = gcry_md_get_algo_dlen( s2k->hash_algo ); + 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, gcry_md_read(md, s2k->hash_algo), i ); + memcpy( dek->key+used, md_read(md, s2k->hash_algo), i ); used += i; } - gcry_md_close(md); + md_close(md); } diff --git a/g10/photoid.c b/g10/photoid.c new file mode 100644 index 000000000..66240ecc2 --- /dev/null +++ b/g10/photoid.c @@ -0,0 +1,320 @@ +/* photoid.c - photo ID handling code + * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + +#include <config.h> +#include <errno.h> +#include <stdio.h> +#include <string.h> +#ifdef HAVE_DOSISH_SYSTEM +#include <windows.h> +#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" + +/* Generate a new photo id packet, or return NULL if canceled */ +PKT_user_id *generate_photo_id(PKT_public_key *pk) +{ + PKT_user_id *uid; + int error=1,i; + unsigned int len; + char *filename=NULL; + byte *photo=NULL; + byte header[16]; + IOBUF file; + + 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; + + uid=m_alloc_clear(sizeof(*uid)+50); + + 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")); + + while(photo==NULL) + { + printf("\n"); + + m_free(filename); + + filename=cpr_get("photoid.jpeg.add", + _("Enter JPEG filename for photo ID: ")); + + if(strlen(filename)==0) + goto scram; + + file=iobuf_open(filename); + if(!file) + { + log_error(_("Unable to open photo \"%s\": %s\n"), + filename,strerror(errno)); + continue; + } + + len=iobuf_get_filelength(file); + if(len>6144) + { + 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); + continue; + } + } + + photo=m_alloc(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); + m_free(photo); + photo=NULL; + continue; + } + + /* Build the packet */ + build_attribute_subpkt(uid,1,photo,len,header,16); + parse_attribute_subpkts(uid); + make_attribute_uidname(uid); + + /* 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); + m_free(photo); + photo=NULL; + continue; + } + } + } + + error=0; + uid->ref=1; + + scram: + m_free(filename); + m_free(photo); + + if(error) + { + free_attributes(uid); + m_free(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) +{ + int 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). */ +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; +} + +static const char *get_default_photo_command(void) +{ +#if defined(HAVE_DOSISH_SYSTEM) + 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 + return "xloadimage -fork -quiet -title 'KeyID 0x%k' stdin"; +#endif +} + +void show_photos(const struct user_attribute *attrs, + int count,PKT_public_key *pk,PKT_secret_key *sk) +{ + 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; + + if(!opt.photo_viewer) + opt.photo_viewer=get_default_photo_command(); + + /* make command grow */ + command=pct_expando(opt.photo_viewer,&args); + if(!command) + goto fail; + + name=m_alloc(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 partcular 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) + { + m_free(name); + goto fail; + } + +#ifdef __riscos__ + riscos_set_filetype(spawn->tempfile_in, + image_type_to_string(args.imagetype,2)); +#endif + + m_free(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"); +} diff --git a/g10/photoid.h b/g10/photoid.h new file mode 100644 index 000000000..45d104f8c --- /dev/null +++ b/g10/photoid.h @@ -0,0 +1,14 @@ +/* Photo ID functions */ + +#ifndef _PHOTOID_H_ +#define _PHOTOID_H_ + +#include "packet.h" + +PKT_user_id *generate_photo_id(PKT_public_key *pk); +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 000000000..f3351277e --- /dev/null +++ b/g10/pipemode.c @@ -0,0 +1,317 @@ +/* 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, 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 index 1170c4088..671b56879 100644 --- a/g10/pkclist.c +++ b/g10/pkclist.c @@ -1,5 +1,5 @@ /* pkclist.c - * Copyright (C) 1998, 1999, 2000 Free Software Foundation, Inc. + * Copyright (C) 1998, 1999, 2000, 2001, 2002 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -25,77 +25,21 @@ #include <errno.h> #include <assert.h> -#include <gcrypt.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 "i18n.h" -#include "main.h" #define CONTROL_D ('D' - 'A' + 1) -/* fixme: we have nearly the same code in keyedit.c */ -static void -print_fpr( PKT_public_key *pk ) -{ - byte array[MAX_FINGERPRINT_LEN], *p; - size_t i, n; - - fingerprint_from_pk( pk, array, &n ); - p = array; - /* Translators: this shoud fit into 24 bytes to that the fingerprint - * data is properly aligned with the user ID */ - tty_printf(_(" Fingerprint:")); - if( n == 20 ) { - for(i=0; i < n ; i++, i++, p += 2 ) { - if( i == 10 ) - tty_printf(" "); - tty_printf(" %02X%02X", *p, p[1] ); - } - } - else { - for(i=0; i < n ; i++, p++ ) { - if( i && !(i%8) ) - tty_printf(" "); - tty_printf(" %02X", *p ); - } - } - tty_printf("\n"); -} - -static void -fpr_info( PKT_public_key *pk ) -{ - byte array[MAX_FINGERPRINT_LEN], *p; - size_t i, n; - FILE *fp = log_stream(); - - fingerprint_from_pk( pk, array, &n ); - p = array; - log_info(_("Fingerprint:")); - if( n == 20 ) { - for(i=0; i < n ; i++, i++, p += 2 ) { - if( i == 10 ) - putc(' ', fp); - fprintf(fp, " %02X%02X", *p, p[1] ); - } - } - else { - for(i=0; i < n ; i++, p++ ) { - if( i && !(i%8) ) - putc(' ', fp); - fprintf(fp, " %02X", *p ); - } - } - putc('\n', fp ); -} - /**************** * Show the revocation reason as it is stored with the given signature @@ -108,15 +52,15 @@ do_show_revocation_reason( PKT_signature *sig ) int seq = 0; const char *text; - while( (p = enum_sig_subpkt( sig->hashed_data, SIGSUBPKT_REVOC_REASON, - &n, &seq )) ) { + 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 superseeded"); + text = _("Key is superseded"); else if( *p == 0x02 ) text = _("Key has been compromised"); else if( *p == 0x03 ) @@ -126,7 +70,7 @@ do_show_revocation_reason( PKT_signature *sig ) else text = NULL; - log_info( _("Reason for revocation: ") ); + log_info( _("reason for revocation: ") ); if( text ) fputs( text, log_stream() ); else @@ -143,7 +87,7 @@ do_show_revocation_reason( PKT_signature *sig ) if( n ) { pp = memchr( p, '\n', n ); nn = pp? pp - p : n; - log_info( _("Revocation comment: ") ); + log_info( _("revocation comment: ") ); print_string( log_stream(), p, nn, 0 ); putc( '\n', log_stream() ); p += nn; n -= nn; @@ -152,18 +96,20 @@ do_show_revocation_reason( PKT_signature *sig ) } } +/* 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 */ -static void -show_revocation_reason( PKT_public_key *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 toldus, taht we have a revoked key, we could - * simplylook 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. - */ + * 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; @@ -178,9 +124,10 @@ show_revocation_reason( PKT_public_key *pk ) } for( node=keyblock; node; node = node->next ) { - if( ( node->pkt->pkttype == PKT_PUBLIC_KEY + 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 ) ) + && !cmp_public_keys( node->pkt->pkt.public_key, pk ) ) ) break; } if( !node ) { @@ -197,16 +144,23 @@ show_revocation_reason( PKT_public_key *pk ) || 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 ); } static void -show_paths( ulong lid, int only_first ) +show_paths (const PKT_public_key *pk, int only_first ) { + log_debug("not yet implemented\n"); +#if 0 void *context = NULL; unsigned otrust, validity; int last_level, level; @@ -224,16 +178,17 @@ show_paths( ulong lid, int only_first ) last_level = level; rc = keyid_from_lid( lid, keyid ); + if( rc ) { log_error("ooops: can't get keyid for lid %lu\n", lid); return; } - pk = gcry_xcalloc( 1, sizeof *pk ); + pk = m_alloc_clear( sizeof *pk ); rc = get_pubkey( pk, keyid ); if( rc ) { log_error("key %08lX: public key not found: %s\n", - (ulong)keyid[1], gpg_errstr(rc) ); + (ulong)keyid[1], g10_errstr(rc) ); return; } @@ -257,11 +212,12 @@ show_paths( ulong lid, int only_first ) p = get_user_id( keyid, &n ); tty_print_utf8_string( p, n ), - gcry_free(p); + m_free(p); tty_printf("\"\n"); free_public_key( pk ); } enum_cert_paths( &context, NULL, NULL, NULL ); /* release context */ +#endif tty_printf("\n"); } @@ -269,207 +225,192 @@ show_paths( ulong lid, int only_first ) /**************** - * Returns true if an ownertrust has changed. + * 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( ulong lid, int mode, unsigned *new_trust, int defer_help ) +do_edit_ownertrust (PKT_public_key *pk, int mode, + unsigned *new_trust, int defer_help ) { - char *p; - int rc; - size_t n; - u32 keyid[2]; - PKT_public_key *pk ; - int changed=0; - int quit=0; - int show=0; - int did_help=defer_help; - - rc = keyid_from_lid( lid, keyid ); - if( rc ) { - log_error("ooops: can't get keyid for lid %lu\n", lid); - return 0; - } - - pk = gcry_xcalloc( 1, sizeof *pk ); - rc = get_pubkey( pk, keyid ); - if( rc ) { - log_error("key %08lX: public key not found: %s\n", - (ulong)keyid[1], gpg_errstr(rc) ); - return 0; - } - - - for(;;) { - /* a string with valid answers */ - const char *ans = _("sSmMqQ"); - - if( !did_help ) { - if( !mode ) { - tty_printf(_("No trust value assigned to %lu:\n" - "%4u%c/%08lX %s \""), lid, - nbits_from_pk( pk ), pubkey_letter( pk->pubkey_algo ), - (ulong)keyid[1], datestr_from_pk( pk ) ); - p = get_user_id( keyid, &n ); - tty_print_utf8_string( p, n ), - gcry_free(p); - tty_printf("\"\n"); - print_fpr( pk ); - tty_printf("\n"); - } - tty_printf(_( -"Please decide how far you trust this user to correctly\n" -"verify other users' keys (by looking at passports,\n" -"checking fingerprints from different sources...)?\n\n" -" 1 = Don't know\n" -" 2 = I do NOT trust\n" -" 3 = I trust marginally\n" -" 4 = I trust fully\n" -" s = please show me more information\n") ); - if( mode ) - tty_printf(_(" m = back to the main menu\n")); - else - tty_printf(_(" q = quit\n")); - tty_printf("\n"); - did_help = 1; - } - if( strlen(ans) != 6 ) - 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 >= '1' && *p <= '4') ) { - unsigned 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; - default: BUG(); - } - *new_trust = trust; - changed = 1; - break; - } - else if( *p == ans[0] || *p == ans[1] ) { - tty_printf(_( - "Certificates leading to an ultimately trusted key:\n")); - show = 1; - break; - } - else if( mode && (*p == ans[2] || *p == ans[3] || *p == CONTROL_D ) ) { - break ; /* back to the menu */ - } - else if( !mode && (*p == ans[4] || *p == ans[5] ) ) { - quit = 1; - break ; /* back to the menu */ - } - gcry_free(p); p = NULL; - } - gcry_free(p); - gcry_free(pk); - return show? -2: quit? -1 : changed; + char *p; + size_t n; + u32 keyid[2]; + int changed=0; + int quit=0; + int show=0; + int did_help=defer_help; + + keyid_from_pk (pk, keyid); + for(;;) { + /* a string with valid answers */ + const char *ans = _("iImMqQsS"); + + if( !did_help ) + { + if( !mode ) + { + tty_printf(_("No trust value assigned to:\n" + "%4u%c/%08lX %s \""), + nbits_from_pk( pk ), pubkey_letter( pk->pubkey_algo ), + (ulong)keyid[1], datestr_from_pk( pk ) ); + p = get_user_id( keyid, &n ); + tty_print_utf8_string( p, n ), + m_free(p); + tty_printf("\"\n"); + print_fingerprint (pk, NULL, 2); + tty_printf("\n"); + } + tty_printf (_( + "Please decide how far you trust this user to correctly\n" + "verify other users' keys (by looking at passports,\n" + "checking fingerprints from different sources...)?\n\n")); + tty_printf (_(" %d = Don't know\n"), 1); + tty_printf (_(" %d = I do NOT trust\n"), 2); + tty_printf (_(" %d = I trust marginally\n"), 3); + tty_printf (_(" %d = I trust fully\n"), 4); + if (mode) + tty_printf (_(" %d = I trust ultimately\n"), 5); + tty_printf (_(" i = please show me more information\n") ); + 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"); + 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 >= '1' && *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? "))) + ; /* no */ + else + { + *new_trust = trust; + changed = 1; + break; + } + } + else if( *p == ans[0] || *p == ans[1] ) + { + tty_printf(_("Certificates leading to an ultimately trusted key:\n")); + show = 1; + break; + } + 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 */ + } + m_free(p); p = NULL; + } + m_free(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( ulong lid, int mode ) +edit_ownertrust (PKT_public_key *pk, int mode ) { - unsigned int trust; - int no_help = 0; - - for(;;) { - switch( do_edit_ownertrust( lid, mode, &trust, no_help ) ) { - case -1: - return 0; - case -2: - show_paths( lid, 1 ); - no_help = 1; - break; - case 1: - trust &= ~TRUST_FLAG_DISABLED; - trust |= get_ownertrust( lid ) & TRUST_FLAG_DISABLED; - if( !update_ownertrust( lid, trust ) ) - return 1; - return 0; - default: - return 0; - } + unsigned int trust; + int no_help = 0; + + for(;;) + { + switch ( do_edit_ownertrust (pk, mode, &trust, no_help ) ) + { + case -1: /* quit */ + return -1; + case -2: /* show info */ + show_paths(pk, 1); + 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; + } } } -static int -add_ownertrust_cb( ulong lid ) -{ - unsigned trust; - int rc = do_edit_ownertrust( lid, 0, &trust, 0 ); - - if( rc == 1 ) - return trust & TRUST_MASK; - return rc > 0? 0 : rc; -} - -/**************** - * Try to add some more owner trusts (interactive) - * This function presents all the signator in a certificate - * chain who have no ownertrust value assigned. - * Returns: -1 if no ownertrust were added. - */ -static int -add_ownertrust( PKT_public_key *pk, int *quit, unsigned *trustlevel ) -{ - int rc; - unsigned flags = 0; - - *quit = 0; - *trustlevel = 0; - tty_printf( -_("Could not find a valid trust path to the key. Let's see whether we\n" - "can assign some missing owner trust values.\n\n")); - - rc = check_trust( pk, trustlevel, NULL, add_ownertrust_cb, &flags ); - - if( !(flags & 1) ) - tty_printf(_("No path leading to one of our keys found.\n\n") ); - else if( !(flags & 2) ) - tty_printf(_("No certificates with undefined trust found.\n\n") ); - else if( !(flags & 4) ) - tty_printf(_("No trust values changed.\n\n") ); - - return (flags & 4)? 0:-1; -} /**************** * Check whether we can trust this pk which has a trustlevel of TRUSTLEVEL - * Returns: true if we trust. Might change the trustlevel + * Returns: true if we trust. */ static int -do_we_trust( PKT_public_key *pk, int *trustlevel ) +do_we_trust( PKT_public_key *pk, unsigned int *trustlevel ) { - int rc; - int did_add = 0; - int trustmask = 0; - - retry: + unsigned int trustmask = 0; + + /* FIXME: get_pubkey_byname already checks the validity and won't + * return keys which are either expired or revoked - so these + * question here won't get triggered. We have to find a solution + * for this. It might make sense to have a function in getkey.c + * which does only the basic checks and returns even revoked and + * expired keys. This fnction could then also returhn a list of + * keys if the speicified name is ambiguous + */ if( (*trustlevel & TRUST_FLAG_REVOKED) ) { log_info(_("key %08lX: key has been revoked!\n"), (ulong)keyid_from_pk( pk, NULL) ); - show_revocation_reason( pk ); + show_revocation_reason( pk, 0 ); if( opt.batch ) - return 0; + return 0; /* no */ if( !cpr_get_answer_is_yes("revoked_key.override", _("Use this key anyway? ")) ) - return 0; + return 0; /* no */ trustmask |= TRUST_FLAG_REVOKED; } - else if( (*trustlevel & TRUST_FLAG_SUB_REVOKED) ) { + if( (*trustlevel & TRUST_FLAG_SUB_REVOKED) ) { log_info(_("key %08lX: subkey has been revoked!\n"), (ulong)keyid_from_pk( pk, NULL) ); - show_revocation_reason( pk ); + show_revocation_reason( pk, 0 ); if( opt.batch ) return 0; @@ -483,52 +424,25 @@ do_we_trust( PKT_public_key *pk, int *trustlevel ) if( opt.always_trust) { if( opt.verbose ) log_info("No trust check due to --always-trust option\n"); - /* The problem with this, is that EXPIRE can't be checked as - * this needs to insert a ne key into the trustdb first and - * we don't want that */ return 1; } - switch( (*trustlevel & TRUST_MASK) ) { - case TRUST_UNKNOWN: /* No pubkey in trustDB: Insert and check again */ - rc = insert_trust_record_by_pk( pk ); - if( rc ) { - log_error("failed to insert it into the trustdb: %s\n", - gpg_errstr(rc) ); - return 0; /* no */ - } - rc = check_trust( pk, trustlevel, NULL, NULL, NULL ); - *trustlevel &= ~trustmask; - if( rc ) - log_fatal("trust check after insert failed: %s\n", - gpg_errstr(rc) ); - if( *trustlevel == TRUST_UNKNOWN || *trustlevel == TRUST_EXPIRED ) { - log_debug("do_we_trust: oops at %d\n", __LINE__ ); - return 0; - } - return do_we_trust( pk, trustlevel ); - case TRUST_EXPIRED: log_info(_("%08lX: key has expired\n"), (ulong)keyid_from_pk( pk, NULL) ); return 0; /* no */ + default: + log_error ("invalid trustlevel %u returned from validation layer\n", + *trustlevel); + /* fall thru */ + case TRUST_UNKNOWN: case TRUST_UNDEFINED: - if( opt.batch || opt.answer_no ) - log_info(_("%08lX: no info to calculate a trust probability\n"), - (ulong)keyid_from_pk( pk, NULL) ); - else { - int quit; - - rc = add_ownertrust( pk, &quit, trustlevel ); - *trustlevel &= ~trustmask; - if( !rc && !did_add && !quit ) { - did_add = 1; - goto retry; - } - } - return 0; + log_info(_("%08lX: There is no indication that this key " + "really belongs to the owner\n"), + (ulong)keyid_from_pk( pk, NULL) ); + return 0; /* no */ case TRUST_NEVER: log_info(_("%08lX: We do NOT trust this key\n"), @@ -550,8 +464,6 @@ do_we_trust( PKT_public_key *pk, int *trustlevel ) if( opt.verbose ) log_info(_("This key belongs to us\n")); return 1; /* yes */ - - default: BUG(); } return 1; /* yes */ @@ -564,7 +476,7 @@ do_we_trust( PKT_public_key *pk, int *trustlevel ) * key anyway. */ static int -do_we_trust_pre( PKT_public_key *pk, int trustlevel ) +do_we_trust_pre( PKT_public_key *pk, unsigned int trustlevel ) { int rc; @@ -574,7 +486,8 @@ do_we_trust_pre( PKT_public_key *pk, int trustlevel ) return 0; if( (trustlevel & TRUST_FLAG_SUB_REVOKED) && !rc ) return 0; - else if( !opt.batch && !rc ) { + + if( !opt.batch && !rc ) { char *p; u32 keyid[2]; size_t n; @@ -585,9 +498,9 @@ do_we_trust_pre( PKT_public_key *pk, int trustlevel ) (ulong)keyid[1], datestr_from_pk( pk ) ); p = get_user_id( keyid, &n ); tty_print_utf8_string( p, n ), - gcry_free(p); + m_free(p); tty_printf("\"\n"); - print_fpr( pk ); + print_fingerprint (pk, NULL, 2); tty_printf("\n"); tty_printf(_( @@ -599,7 +512,7 @@ do_we_trust_pre( PKT_public_key *pk, int trustlevel ) _("Use this key anyway? ")) ) rc = 1; - /* Hmmm: Should we set a flag to tell the user the user about + /* Hmmm: Should we set a flag to tell the user about * his decision the next time he encrypts for this recipient? */ } @@ -620,121 +533,102 @@ do_we_trust_pre( PKT_public_key *pk, int trustlevel ) int check_signatures_trust( PKT_signature *sig ) { - PKT_public_key *pk = gcry_xcalloc( 1, sizeof *pk ); - int trustlevel; - int did_add = 0; - int rc=0; - - - if( opt.always_trust ) { - if( !opt.quiet ) - log_info(_("WARNING: Using untrusted key!\n")); - return 0; + PKT_public_key *pk = m_alloc_clear( sizeof *pk ); + unsigned int trustlevel; + int rc=0; + + if ( opt.always_trust) + { + if( !opt.quiet ) + log_info(_("WARNING: Using untrusted key!\n")); + if (opt.with_fingerprint) + print_fingerprint (pk, NULL, 1); + goto leave; } - - 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 = GPGERR_NO_PUBKEY; - goto leave; + 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; } - rc = check_trust( pk, &trustlevel, NULL, NULL, NULL ); - if( rc ) { - log_error("check trust failed: %s\n", gpg_errstr(rc)); - goto leave; - } + trustlevel = get_validity (pk, NULL); - retry: - if( (trustlevel & TRUST_FLAG_REVOKED) ) { - write_status( STATUS_KEYREVOKED ); - log_info(_("WARNING: This key has been revoked by its owner!\n")); - log_info(_(" This could mean that the signature is forgery.\n")); - show_revocation_reason( pk ); + if ( (trustlevel & TRUST_FLAG_REVOKED) ) + { + write_status( STATUS_KEYREVOKED ); + log_info(_("WARNING: This key has been revoked by its owner!\n")); + log_info(_(" This could mean that the signature is forgery.\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 ); + 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 ); } - - - switch( (trustlevel & TRUST_MASK) ) { - case TRUST_UNKNOWN: /* No pubkey in trustDB: Insert and check again */ - rc = insert_trust_record_by_pk( pk ); - if( rc ) { - log_error("failed to insert it into the trustdb: %s\n", - gpg_errstr(rc) ); - goto leave; - } - rc = check_trust( pk, &trustlevel, NULL, NULL, NULL ); - if( rc ) - log_fatal("trust check after insert failed: %s\n", - gpg_errstr(rc) ); - if( trustlevel == TRUST_UNKNOWN || trustlevel == TRUST_EXPIRED ) - BUG(); - goto retry; - - case TRUST_EXPIRED: - log_info(_("Note: This key has expired!\n")); - fpr_info( pk ); - break; - - case TRUST_UNDEFINED: - if( did_add || opt.batch || opt.answer_no ) { - 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" )); - fpr_info( pk ); - } - else { - int quit; - rc = add_ownertrust( pk, &quit, &trustlevel ); - if( rc || quit ) { - did_add = 1; - rc = 0; - } - goto retry; - } - break; - - case TRUST_NEVER: - write_status( STATUS_TRUST_NEVER ); - log_info(_("WARNING: We do NOT trust this key!\n")); - log_info(_(" The signature is probably a FORGERY.\n")); - rc = GPGERR_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" - )); - fpr_info( pk ); - break; - - case TRUST_FULLY: - write_status( STATUS_TRUST_FULLY ); - break; - - case TRUST_ULTIMATE: - write_status( STATUS_TRUST_ULTIMATE ); - break; - - default: BUG(); + + if ((trustlevel & TRUST_FLAG_DISABLED)) + log_info (_("Note: This key has been disabled.\n")); + + 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; + leave: + free_public_key( pk ); + return rc; } @@ -746,7 +640,7 @@ release_pk_list( PK_LIST pk_list ) for( ; pk_list; pk_list = pk_rover ) { pk_rover = pk_list->next; free_public_key( pk_list->pk ); - gcry_free( pk_list ); + m_free( pk_list ); } } @@ -775,11 +669,11 @@ default_recipient(void) int i; if( opt.def_recipient ) - return gcry_xstrdup( opt.def_recipient ); + return m_strdup( opt.def_recipient ); if( !opt.def_recipient_self ) return NULL; - sk = gcry_xcalloc( 1, sizeof *sk ); - i = get_seckey_byname( NULL, sk, NULL, 0, NULL ); + sk = m_alloc_clear( sizeof *sk ); + i = get_seckey_byname( sk, NULL, 0 ); if( i ) { free_secret_key( sk ); return NULL; @@ -787,7 +681,7 @@ default_recipient(void) n = MAX_FINGERPRINT_LEN; fingerprint_from_sk( sk, fpr, &n ); free_secret_key( sk ); - p = gcry_xmalloc( 2*n+3 ); + p = m_alloc( 2*n+3 ); *p++ = '0'; *p++ = 'x'; for(i=0; i < n; i++ ) @@ -796,31 +690,82 @@ default_recipient(void) 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; +} int -build_pk_list( STRLIST remusr, PK_LIST *ret_pk_list, unsigned use ) +build_pk_list( STRLIST rcpts, PK_LIST *ret_pk_list, unsigned use ) { PK_LIST pk_list = NULL; PKT_public_key *pk=NULL; int rc=0; int any_recipients=0; - STRLIST rov; + STRLIST rov,remusr; char *def_rec = NULL; + 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) ) any_recipients = 1; - else if( (use & GCRY_PK_USAGE_ENCR) && !opt.no_encrypt_to ) { - pk = gcry_xcalloc( 1, sizeof *pk ); + else if( (use & PUBKEY_USAGE_ENC) && !opt.no_encrypt_to ) { + pk = m_alloc_clear( sizeof *pk ); pk->req_usage = use; - if( (rc = get_pubkey_byname( NULL, pk, rov->d, NULL )) ) { + if( (rc = get_pubkey_byname( pk, rov->d, NULL, NULL )) ) { free_public_key( pk ); pk = NULL; - log_error(_("%s: skipped: %s\n"), rov->d, gpg_errstr(rc) ); - } - else if( !(rc=openpgp_pk_test_algo(pk->pubkey_algo, - pk->pubkey_usage)) ) { + 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); + } + else if( !(rc=check_pubkey_algo2(pk->pubkey_algo, use )) ) { /* Skip the actual key if the key is already present * in the list */ if (key_present_in_pk_list(pk_list, pk) == 0) { @@ -830,7 +775,7 @@ build_pk_list( STRLIST remusr, PK_LIST *ret_pk_list, unsigned use ) } else { PK_LIST r; - r = gcry_xmalloc( sizeof *r ); + r = m_alloc( sizeof *r ); r->pk = pk; pk = NULL; r->next = pk_list; r->mark = 0; @@ -839,44 +784,53 @@ build_pk_list( STRLIST remusr, PK_LIST *ret_pk_list, unsigned use ) } else { free_public_key( pk ); pk = NULL; - log_error(_("%s: skipped: %s\n"), rov->d, gpg_errstr(rc) ); + 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); } } } if( !any_recipients && !opt.batch ) { /* ask */ - char *answer=NULL; int have_def_rec; + char *answer=NULL; + STRLIST backlog=NULL; 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\n")); + "You did not specify a user ID. (you may use \"-r\")\n")); for(;;) { rc = 0; - gcry_free(answer); + m_free(answer); if( have_def_rec ) { answer = def_rec; def_rec = NULL; } + else if(backlog) { + answer=pop_strlist(&backlog); + } else { answer = cpr_get_utf8("pklist.user_id.enter", - _("Enter the user ID: ")); + _("\nEnter the user ID. End with an empty line: ")); trim_spaces(answer); cpr_kill_prompt(); } - if( !*answer ) + if( !answer || !*answer ) { + m_free(answer); break; + } + if(expand_id(answer,&backlog,0)) + continue; if( pk ) free_public_key( pk ); - pk = gcry_xcalloc( 1, sizeof *pk ); + pk = m_alloc_clear( sizeof *pk ); pk->req_usage = use; - rc = get_pubkey_byname( NULL, pk, answer, NULL ); + rc = get_pubkey_byname( pk, answer, NULL, NULL ); if( rc ) tty_printf(_("No such user ID.\n")); - else if( !(rc=openpgp_pk_test_algo(pk->pubkey_algo, - pk->pubkey_usage)) ) { + else if( !(rc=check_pubkey_algo2(pk->pubkey_algo, use)) ) { if( have_def_rec ) { if (key_present_in_pk_list(pk_list, pk) == 0) { free_public_key(pk); pk = NULL; @@ -884,25 +838,20 @@ build_pk_list( STRLIST remusr, PK_LIST *ret_pk_list, unsigned use ) "already set as default recipient\n") ); } else { - PK_LIST r = gcry_xmalloc( sizeof *r ); + PK_LIST r = m_alloc( sizeof *r ); r->pk = pk; pk = NULL; r->next = pk_list; r->mark = 0; pk_list = r; } any_recipients = 1; - break; + continue; } else { int trustlevel; - rc = check_trust( pk, &trustlevel, pk->namehash, - NULL, NULL ); - if( rc ) { - log_error("error checking pk of `%s': %s\n", - answer, gpg_errstr(rc) ); - } - else if( (trustlevel & TRUST_FLAG_DISABLED) ) { + trustlevel = get_validity (pk, NULL); + if( (trustlevel & TRUST_FLAG_DISABLED) ) { tty_printf(_("Public key is disabled.\n") ); } else if( do_we_trust_pre( pk, trustlevel ) ) { @@ -910,52 +859,71 @@ build_pk_list( STRLIST remusr, PK_LIST *ret_pk_list, unsigned use ) * in the list */ if (key_present_in_pk_list(pk_list, pk) == 0) { free_public_key(pk); pk = NULL; - log_info(_("skipped: public key " - "already set with --encrypt-to\n") ); + log_info(_("skipped: public key already set\n") ); } else { PK_LIST r; - - r = gcry_xmalloc( sizeof *r ); + char *p; + size_t n; + u32 keyid[2]; + + keyid_from_pk( pk, keyid); + tty_printf("Added %4u%c/%08lX %s \"", + nbits_from_pk( pk ), + pubkey_letter( pk->pubkey_algo ), + (ulong)keyid[1], + datestr_from_pk( pk ) ); + p = get_user_id( keyid, &n ); + tty_print_utf8_string( p, n ); + m_free(p); + tty_printf("\"\n"); + + r = m_alloc( sizeof *r ); r->pk = pk; pk = NULL; r->next = pk_list; r->mark = 0; pk_list = r; } any_recipients = 1; - break; + continue; } } } - gcry_free(def_rec); def_rec = NULL; + m_free(def_rec); def_rec = NULL; have_def_rec = 0; } - gcry_free(answer); if( pk ) { free_public_key( pk ); pk = NULL; } } else if( !any_recipients && (def_rec = default_recipient()) ) { - pk = gcry_xcalloc( 1, sizeof *pk ); + pk = m_alloc_clear( sizeof *pk ); pk->req_usage = use; - rc = get_pubkey_byname( NULL, pk, def_rec, NULL ); + rc = get_pubkey_byname( pk, def_rec, NULL, NULL ); if( rc ) log_error(_("unknown default recipient `%s'\n"), def_rec ); - else if( !(rc=openpgp_pk_test_algo(pk->pubkey_algo, - pk->pubkey_usage)) ) { - PK_LIST r = gcry_xmalloc( sizeof *r ); + 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) == 0) + log_info(_("skipped: public key already set as default recipient\n")); + else { + PK_LIST r = m_alloc( sizeof *r ); r->pk = pk; pk = NULL; r->next = pk_list; r->mark = 0; pk_list = r; - any_recipients = 1; + } } if( pk ) { free_public_key( pk ); pk = NULL; } - gcry_free(def_rec); def_rec = NULL; + m_free(def_rec); def_rec = NULL; } else { any_recipients = 0; @@ -963,26 +931,27 @@ build_pk_list( STRLIST remusr, PK_LIST *ret_pk_list, unsigned use ) if( (remusr->flags & 1) ) continue; /* encrypt-to keys are already handled */ - pk = gcry_xcalloc( 1, sizeof *pk ); + pk = m_alloc_clear( sizeof *pk ); pk->req_usage = use; - if( (rc = get_pubkey_byname( NULL, pk, remusr->d, NULL )) ) { + if( (rc = get_pubkey_byname( pk, remusr->d, NULL, NULL )) ) { free_public_key( pk ); pk = NULL; - log_error(_("%s: skipped: %s\n"), remusr->d, gpg_errstr(rc) ); + 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); } - else if( !(rc=openpgp_pk_test_algo(pk->pubkey_algo, - pk->pubkey_usage)) ) { + else if( !(rc=check_pubkey_algo2(pk->pubkey_algo, use )) ) { int trustlevel; - rc = check_trust( pk, &trustlevel, pk->namehash, NULL, NULL ); - if( rc ) { - free_public_key( pk ); pk = NULL; - log_error(_("%s: error checking key: %s\n"), - remusr->d, gpg_errstr(rc) ); - } - else if( (trustlevel & TRUST_FLAG_DISABLED) ) { + trustlevel = get_validity (pk, pk->namehash); + if( (trustlevel & TRUST_FLAG_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); } else if( do_we_trust_pre( pk, trustlevel ) ) { /* note: do_we_trust may have changed the trustlevel */ @@ -1000,7 +969,7 @@ build_pk_list( STRLIST remusr, PK_LIST *ret_pk_list, unsigned use ) } else { PK_LIST r; - r = gcry_xmalloc( sizeof *r ); + r = m_alloc( sizeof *r ); r->pk = pk; pk = NULL; r->next = pk_list; r->mark = 0; @@ -1009,55 +978,103 @@ build_pk_list( STRLIST remusr, PK_LIST *ret_pk_list, unsigned use ) } else { /* we don't trust this pk */ free_public_key( pk ); pk = NULL; + write_status_text_and_buffer (STATUS_INV_RECP, "0 ", + remusr->d, + strlen (remusr->d), + -1); } } else { free_public_key( pk ); pk = NULL; - log_error(_("%s: skipped: %s\n"), remusr->d, gpg_errstr(rc) ); + 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) ); } } } if( !rc && !any_recipients ) { log_error(_("no valid addressees\n")); - rc = GPGERR_NO_USER_ID; + write_status_text (STATUS_NO_RECP, "0"); + rc = G10ERR_NO_USER_ID; } 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 mode + expands the cipher list to include AES128 (7), AES192 (8), AES256 + (9), and TWOFISH (10). 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 */ static int -algo_available( int preftype, int algo ) +algo_available( int preftype, int algo, void *hint ) { if( preftype == PREFTYPE_SYM ) { - return algo && !openpgp_cipher_test_algo( algo ); + if( opt.pgp6 && ( algo != 1 && algo != 2 && algo != 3) ) + return 0; + + if( opt.pgp7 && (algo != 1 && algo != 2 && algo != 3 && + algo != 7 && algo != 8 && algo != 9 && algo != 10) ) + return 0; + + return algo && !check_cipher_algo( algo ); } else if( preftype == PREFTYPE_HASH ) { - return algo && !openpgp_md_test_algo( algo ); + int bits=0; + + if(hint) + bits=*(int *)hint; + + if(bits && (bits != md_digest_length(algo))) + return 0; + + if( (opt.pgp6 || opt.pgp7 ) && ( algo != 1 && algo != 2 && algo != 3) ) + return 0; + + return algo && !check_digest_algo( algo ); } - else if( preftype == PREFTYPE_COMPR ) { - return !algo || algo == 1 || algo == 2; + else if( preftype == PREFTYPE_ZIP ) { + if ( ( opt.pgp6 || opt.pgp7 ) && ( algo !=0 && algo != 1) ) + return 0; + + 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 ) +select_algo_from_prefs(PK_LIST pk_list, int preftype, int request, void *hint) { PK_LIST pkr; u32 bits[8]; - byte *pref = NULL; - size_t npref; + const prefitem_t *prefs; int i, j; int compr_hack=0; int any; @@ -1070,43 +1087,65 @@ select_algo_from_prefs( PK_LIST pk_list, int preftype ) u32 mask[8]; memset( mask, 0, 8 * sizeof *mask ); - if( !pkr->pk->local_id ) { /* try to set the local id */ - query_trust_info( pkr->pk, NULL ); - if( !pkr->pk->local_id ) { - log_debug("select_algo_from_prefs: can't get LID\n"); - continue; - } + if( preftype == PREFTYPE_SYM ) { + if( opt.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 */ } - if( preftype == PREFTYPE_SYM ) - mask[0] |= (1<<2); /* 3DES is implicitly there */ - gcry_free(pref); - pref = get_pref_data( pkr->pk->local_id, pkr->pk->namehash, &npref); + 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( opt.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( pref ) { - #if 0 - log_hexdump("raw: ", pref, npref ); - #endif - for(i=0; i+1 < npref; i+=2 ) { - if( pref[i] == preftype ) { - mask[pref[i+1]/32] |= 1 << (pref[i+1]%32); + 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( (!pref || !any) && preftype == PREFTYPE_COMPR ) { + + if( (!prefs || !any) && preftype == PREFTYPE_ZIP ) { mask[0] |= 3; /* asume no_compression and old pgp */ compr_hack = 1; } #if 0 - log_debug("mask=%08lX%08lX%08lX%08lX%08lX%08lX%08lX%08lX\n", + 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("bits=%08lX%08lX%08lX%08lX%08lX%08lX%08lX%08lX\n", + 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 @@ -1119,28 +1158,42 @@ select_algo_from_prefs( PK_LIST pk_list, int preftype ) */ i = -1; any = 0; - if( pref ) { - for(j=0; j+1 < npref; j+=2 ) { - if( pref[j] == preftype ) { - if( (bits[pref[j+1]/32] & (1<<(pref[j+1]%32))) ) { - if( algo_available( preftype, pref[j+1] ) ) { + + /* 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 = pref[j+1]; + i = prefs[j].value; break; } } } } } - if( !pref || !any ) { + if( !prefs || !any ) { for(j=0; j < 256; j++ ) if( (bits[j/32] & (1<<(j%32))) ) { - if( algo_available( preftype, j ) ) { + if( algo_available( preftype, j, hint ) ) { i = j; break; } } } + + /* Can we use the requested algorithm? */ + if(request>-1 && request==i) + return i; + #if 0 log_debug("prefs of type %d: selected %d\n", preftype, i ); #endif @@ -1152,8 +1205,53 @@ select_algo_from_prefs( PK_LIST pk_list, int preftype ) i = 1; /* yep; we can use compression algo 1 */ } - gcry_free(pref); + /* "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 intentinally 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->mdc_feature; + 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 index 555dd1636..b12fb0f11 100644 --- a/g10/plaintext.c +++ b/g10/plaintext.c @@ -1,5 +1,5 @@ -/* plaintext.c - process an plaintext packet - * Copyright (C) 1998, 1999, 2000 Free Software Foundation, Inc. +/* plaintext.c - process plaintext packets + * Copyright (C) 1998, 1999, 2000, 2001, 2002 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -29,7 +29,7 @@ #endif #include "util.h" -#include <gcrypt.h> +#include "memory.h" #include "options.h" #include "packet.h" #include "ttyio.h" @@ -60,7 +60,7 @@ handle_plaintext( PKT_plaintext *pt, md_filter_context_t *mfx, if( nooutput ) ; else if( opt.outfile ) { - fname = gcry_xmalloc( strlen( opt.outfile ) + 1); + fname = m_alloc( strlen( opt.outfile ) + 1); strcpy(fname, opt.outfile ); } else if( pt->namelen == 8 && !memcmp( pt->name, "_CONSOLE", 8 ) ) { @@ -72,7 +72,7 @@ handle_plaintext( PKT_plaintext *pt, md_filter_context_t *mfx, if( !fname ) fname = ask_outfile_name( pt->name, pt->namelen ); if( !fname ) { - rc = GPGERR_CREATE_FILE; + rc = G10ERR_CREATE_FILE; goto leave; } } @@ -89,31 +89,44 @@ handle_plaintext( PKT_plaintext *pt, md_filter_context_t *mfx, setmode ( fileno(fp) , O_BINARY ); #endif } - else if( !overwrite_filep( fname ) ) { - rc = GPGERR_CREATE_FILE; - goto leave; + else { + while( !overwrite_filep (fname) ) { + char *tmp = ask_outfile_name (NULL, 0); + if ( !tmp || !*tmp ) { + m_free (tmp); + rc = G10ERR_CREATE_FILE; + goto leave; + } + m_free (fname); + fname = tmp; + } } if( fp || nooutput ) ; else if( !(fp = fopen(fname,"wb")) ) { - log_error("Error creating `%s': %s\n", fname, strerror(errno) ); - rc = GPGERR_CREATE_FILE; + log_error(_("error creating `%s': %s\n"), fname, strerror(errno) ); + rc = G10ERR_CREATE_FILE; +#ifdef __riscos__ + if (errno == 106) + log_info("perhaps the output file has the same name as the input file?\n"); +#endif /* __riscos__ */ goto leave; } - if( pt->len ) { + if( !pt->is_partial ) { + /* we have an actual length (which might be zero). */ assert( !clearsig ); 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 = GPGERR_READ_FILE; + rc = G10ERR_READ_FILE; goto leave; } if( mfx->md ) - gcry_md_putc(mfx->md, c ); + 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 */ @@ -122,45 +135,45 @@ handle_plaintext( PKT_plaintext *pt, md_filter_context_t *mfx, if( putc( c, fp ) == EOF ) { log_error("Error writing to `%s': %s\n", fname, strerror(errno) ); - rc = GPGERR_WRITE_FILE; + rc = G10ERR_WRITE_FILE; goto leave; } } } } else { /* binary mode */ - byte *buffer = gcry_xmalloc( 32768 ); + byte *buffer = m_alloc( 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 = GPGERR_READ_FILE; - gcry_free( buffer ); + rc = G10ERR_READ_FILE; + m_free( buffer ); goto leave; } if( mfx->md ) - gcry_md_write( mfx->md, buffer, len ); + md_write( mfx->md, buffer, len ); if( fp ) { if( fwrite( buffer, 1, len, fp ) != len ) { log_error("Error writing to `%s': %s\n", fname, strerror(errno) ); - rc = GPGERR_WRITE_FILE; - gcry_free( buffer ); + rc = G10ERR_WRITE_FILE; + m_free( buffer ); goto leave; } } pt->len -= len; } - gcry_free( buffer ); + m_free( buffer ); } } else if( !clearsig ) { if( convert ) { /* text mode */ while( (c = iobuf_get(pt->buf)) != -1 ) { if( mfx->md ) - gcry_md_putc(mfx->md, c ); + md_putc(mfx->md, c ); #ifndef HAVE_DOSISH_SYSTEM if( convert && c == '\r' ) continue; /* fixme: this hack might be too simple */ @@ -169,14 +182,14 @@ handle_plaintext( PKT_plaintext *pt, md_filter_context_t *mfx, if( putc( c, fp ) == EOF ) { log_error("Error writing to `%s': %s\n", fname, strerror(errno) ); - rc = GPGERR_WRITE_FILE; + rc = G10ERR_WRITE_FILE; goto leave; } } } } else { /* binary mode */ - byte *buffer = gcry_xmalloc( 32768 ); + byte *buffer = m_alloc( 32768 ); int eof; for( eof=0; !eof; ) { /* Why do we check for len < 32768: @@ -191,18 +204,18 @@ handle_plaintext( PKT_plaintext *pt, md_filter_context_t *mfx, if( len < 32768 ) eof = 1; if( mfx->md ) - gcry_md_write( mfx->md, buffer, len ); + md_write( mfx->md, buffer, len ); if( fp ) { if( fwrite( buffer, 1, len, fp ) != len ) { log_error("Error writing to `%s': %s\n", fname, strerror(errno) ); - rc = GPGERR_WRITE_FILE; - gcry_free( buffer ); + rc = G10ERR_WRITE_FILE; + m_free( buffer ); goto leave; } } } - gcry_free( buffer ); + m_free( buffer ); } pt->buf = NULL; } @@ -214,15 +227,15 @@ handle_plaintext( PKT_plaintext *pt, md_filter_context_t *mfx, if( putc( c, fp ) == EOF ) { log_error("Error writing to `%s': %s\n", fname, strerror(errno) ); - rc = GPGERR_WRITE_FILE; + rc = G10ERR_WRITE_FILE; goto leave; } } if( !mfx->md ) continue; if( state == 2 ) { - gcry_md_putc(mfx->md, '\r' ); - gcry_md_putc(mfx->md, '\n' ); + md_putc(mfx->md, '\r' ); + md_putc(mfx->md, '\n' ); state = 0; } if( !state ) { @@ -231,18 +244,18 @@ handle_plaintext( PKT_plaintext *pt, md_filter_context_t *mfx, else if( c == '\n' ) state = 2; else - gcry_md_putc(mfx->md, c ); + md_putc(mfx->md, c ); } else if( state == 1 ) { if( c == '\n' ) state = 2; else { - gcry_md_putc(mfx->md, '\r' ); + md_putc(mfx->md, '\r' ); if( c == '\r' ) state = 1; else { state = 0; - gcry_md_putc(mfx->md, c ); + md_putc(mfx->md, c ); } } } @@ -253,7 +266,7 @@ handle_plaintext( PKT_plaintext *pt, md_filter_context_t *mfx, if( fp && fp != stdout && fclose(fp) ) { log_error("Error closing `%s': %s\n", fname, strerror(errno) ); fp = NULL; - rc = GPGERR_WRITE_FILE; + rc = G10ERR_WRITE_FILE; goto leave; } fp = NULL; @@ -261,12 +274,12 @@ handle_plaintext( PKT_plaintext *pt, md_filter_context_t *mfx, leave: if( fp && fp != stdout ) fclose(fp); - gcry_free(fname); + m_free(fname); return rc; } static void -do_hash( GCRY_MD_HD md, GCRY_MD_HD md2, IOBUF fp, int textmode ) +do_hash( MD_HANDLE md, MD_HANDLE md2, IOBUF fp, int textmode ) { text_filter_context_t tfx; int c; @@ -280,27 +293,27 @@ do_hash( GCRY_MD_HD md, GCRY_MD_HD md2, IOBUF fp, int textmode ) int lc = -1; while( (c = iobuf_get(fp)) != -1 ) { if( c == '\n' && lc == '\r' ) - gcry_md_putc(md2, c); + md_putc(md2, c); else if( c == '\n' ) { - gcry_md_putc(md2, '\r'); - gcry_md_putc(md2, c); + md_putc(md2, '\r'); + md_putc(md2, c); } else if( c != '\n' && lc == '\r' ) { - gcry_md_putc(md2, '\n'); - gcry_md_putc(md2, c); + md_putc(md2, '\n'); + md_putc(md2, c); } else - gcry_md_putc(md2, c); + md_putc(md2, c); if( md ) - gcry_md_putc(md, c ); + md_putc(md, c ); lc = c; } } else { while( (c = iobuf_get(fp)) != -1 ) { if( md ) - gcry_md_putc(md, c ); + md_putc(md, c ); } } } @@ -311,7 +324,7 @@ do_hash( GCRY_MD_HD md, GCRY_MD_HD md2, IOBUF fp, int textmode ) * INFILE is the name of the input file. */ int -ask_for_detached_datafile( GCRY_MD_HD md, GCRY_MD_HD md2, +ask_for_detached_datafile( MD_HANDLE md, MD_HANDLE md2, const char *inname, int textmode ) { char *answer = NULL; @@ -323,12 +336,12 @@ ask_for_detached_datafile( GCRY_MD_HD md, GCRY_MD_HD md2, int any=0; tty_printf(_("Detached signature.\n")); do { - gcry_free(answer); + m_free(answer); answer = cpr_get("detached_signature.filename", _("Please enter name of data file: ")); cpr_kill_prompt(); if( any && !*answer ) { - rc = GPGERR_READ_FILE; + rc = G10ERR_READ_FILE; goto leave; } fp = iobuf_open(answer); @@ -338,7 +351,7 @@ ask_for_detached_datafile( GCRY_MD_HD md, GCRY_MD_HD md2, } else if( !fp ) { log_error("can't open `%s': %s\n", answer, strerror(errno) ); - rc = GPGERR_READ_FILE; + rc = G10ERR_READ_FILE; goto leave; } } while( !fp ); @@ -355,7 +368,7 @@ ask_for_detached_datafile( GCRY_MD_HD md, GCRY_MD_HD md2, leave: - gcry_free(answer); + m_free(answer); return rc; } @@ -366,11 +379,11 @@ ask_for_detached_datafile( GCRY_MD_HD md, GCRY_MD_HD md2, * If FILES is NULL, hash stdin. */ int -hash_datafiles( GCRY_MD_HD md, GCRY_MD_HD md2, STRLIST files, +hash_datafiles( MD_HANDLE md, MD_HANDLE md2, STRLIST files, const char *sigfilename, int textmode ) { IOBUF fp; - STRLIST sl=NULL; + STRLIST sl; if( !files ) { /* check whether we can open the signed material */ @@ -380,28 +393,26 @@ hash_datafiles( GCRY_MD_HD md, GCRY_MD_HD md2, STRLIST files, iobuf_close(fp); return 0; } - /* no we can't (no sigfile) - read signed stuff from stdin */ - add_to_strlist( &sl, "-"); + log_error (_("no signed data\n")); + return G10ERR_OPEN_FILE; } - else - sl = files; - for( ; sl; sl = sl->next ) { + + for (sl=files; sl; sl = sl->next ) { fp = iobuf_open( sl->d ); if( !fp ) { log_error(_("can't open signed data `%s'\n"), print_fname_stdin(sl->d)); - if( !files ) - free_strlist(sl); - return GPGERR_OPEN_FILE; + return G10ERR_OPEN_FILE; } do_hash( md, md2, fp, textmode ); iobuf_close(fp); } - if( !files ) - free_strlist(sl); return 0; } + + + diff --git a/g10/pubkey-enc.c b/g10/pubkey-enc.c index 646aca017..2c8771c27 100644 --- a/g10/pubkey-enc.c +++ b/g10/pubkey-enc.c @@ -1,5 +1,5 @@ /* pubkey-enc.c - public key encoded packet handling - * Copyright (C) 1998, 1999, 2000 Free Software Foundation, Inc. + * Copyright (C) 1998, 1999, 2000, 2001, 2002 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -23,12 +23,13 @@ #include <stdlib.h> #include <string.h> #include <assert.h> - -#include <gcrypt.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" @@ -38,66 +39,31 @@ static int get_it( PKT_pubkey_enc *k, DEK *dek, PKT_secret_key *sk, u32 *keyid ); -/**************** - * Emulate our old PK interface here - sometime in the future we might - * change the internal design to directly fit to libgcrypt. - */ +/* check that the given algo is mentioned in one of the valid user IDs */ static int -pk_decrypt( int algo, MPI *result, MPI *data, MPI *skey ) +is_algo_in_prefs ( KBNODE keyblock, preftype_t type, int algo ) { - GCRY_SEXP s_skey, s_data, s_plain; - int rc; - - *result = NULL; - /* make a sexp from skey */ - if( algo == GCRY_PK_ELG || algo == GCRY_PK_ELG_E ) { - rc = gcry_sexp_build ( &s_skey, NULL, - "(private-key(elg(p%m)(g%m)(y%m)(x%m)))", - skey[0], skey[1], skey[2], skey[3] ); - } - else if( algo == GCRY_PK_RSA ) { - rc = gcry_sexp_build ( &s_skey, NULL, - "(private-key(rsa(n%m)(e%m)(d%m)(p%m)(q%m)(u%m)))", - skey[0], skey[1], skey[2], skey[3], skey[4], skey[5] ); - } - else - return GPGERR_PUBKEY_ALGO; - - if ( rc ) - BUG (); - - /* put data into a S-Exp s_data */ - if( algo == GCRY_PK_ELG || algo == GCRY_PK_ELG_E ) { - rc = gcry_sexp_build ( &s_data, NULL, - "(enc-val(elg(a%m)(b%m)))", data[0], data[1] ); + 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; + } + } } - else if( algo == GCRY_PK_RSA ) { - rc = gcry_sexp_build ( &s_data, NULL, - "(enc-val(rsa(a%m)))", data[0] ); - } - else - BUG(); - - if ( rc ) - BUG (); - - rc = gcry_pk_decrypt( &s_plain, s_data, s_skey ); - gcry_sexp_release( s_skey ); - gcry_sexp_release( s_data); - if( rc ) - return rc; - - *result = gcry_sexp_nth_mpi( s_plain, 0, 0 ); - gcry_sexp_release( s_plain ); - if( !*result ) - return -1; /* oops */ - return 0; } /**************** - * Get the session key from a pubkey enc paket and return + * Get the session key from a pubkey enc packet and return * it in DEK, which should have been allocated in secure memory. */ int @@ -106,12 +72,12 @@ get_session_key( PKT_pubkey_enc *k, DEK *dek ) PKT_secret_key *sk = NULL; int rc; - rc = openpgp_pk_test_algo( k->pubkey_algo, 0 ); + 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 = gcry_xcalloc( 1, sizeof *sk ); + sk = m_alloc_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 ); @@ -123,17 +89,17 @@ get_session_key( PKT_pubkey_enc *k, DEK *dek ) for(;;) { if( sk ) free_secret_key( sk ); - sk = gcry_xcalloc( 1, sizeof *sk ); + sk = m_alloc_clear( sizeof *sk ); rc=enum_secret_keys( &enum_context, sk, 1); if( rc ) { - rc = GPGERR_NO_SECKEY; + rc = G10ERR_NO_SECKEY; break; } if( sk->pubkey_algo != k->pubkey_algo ) continue; keyid_from_sk( sk, keyid ); - log_info(_("anonymous receiver; trying secret key %08lX ...\n"), - (ulong)keyid[1] ); + log_info(_("anonymous recipient; trying secret key %08lX ...\n"), + (ulong)keyid[1] ); rc = check_secret_key( sk, 1 ); /* ask only once */ if( !rc ) rc = get_it( k, dek, sk, keyid ); @@ -153,22 +119,19 @@ get_session_key( PKT_pubkey_enc *k, DEK *dek ) static int -get_it( PKT_pubkey_enc *k, DEK *dek, PKT_secret_key *sk, u32 *keyid ) +get_it( PKT_pubkey_enc *enc, DEK *dek, PKT_secret_key *sk, u32 *keyid ) { int rc; MPI plain_dek = NULL; byte *frame = NULL; - unsigned int n; - size_t nframe; + unsigned n, nframe; u16 csum, csum2; - rc = pk_decrypt(sk->pubkey_algo, &plain_dek, k->data, sk->skey ); + rc = pubkey_decrypt(sk->pubkey_algo, &plain_dek, enc->data, sk->skey ); if( rc ) goto leave; - if( gcry_mpi_aprint( GCRYMPI_FMT_USG, &frame, &nframe, plain_dek ) ) - BUG(); - - mpi_release( plain_dek ); plain_dek = NULL; + 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 * @@ -180,8 +143,7 @@ get_it( PKT_pubkey_enc *k, DEK *dek, PKT_secret_key *sk, u32 *keyid ) * * 0 2 RND(n bytes) 0 A DEK(k bytes) CSUM(2 bytes) * - * (mpi_get_buffer already removed the leading zero - still true - * for gcry_mpi_aprint(0 which is used now?) + * (mpi_get_buffer already removed the leading zero). * * RND are non-zero randow bytes. * A is the cipher algorithm @@ -192,35 +154,37 @@ get_it( PKT_pubkey_enc *k, DEK *dek, PKT_secret_key *sk, u32 *keyid ) log_hexdump("DEK frame:", frame, nframe ); n=0; if( n + 7 > nframe ) - { rc = GPGERR_WRONG_SECKEY; goto leave; } + { 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 = GPGERR_CIPHER_ALGO; + rc = G10ERR_CIPHER_ALGO; goto leave; } if( frame[n] != 2 ) /* somethink is wrong */ - { rc = GPGERR_WRONG_SECKEY; goto leave; } + { 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 = GPGERR_WRONG_SECKEY; goto leave; } + { rc = G10ERR_WRONG_SECKEY; goto leave; } dek->keylen = nframe - (n+1) - 2; dek->algo = frame[n++]; - if( dek->algo == GCRY_CIPHER_IDEA ) + if( dek->algo == CIPHER_ALGO_IDEA ) write_status(STATUS_RSA_OR_IDEA); - rc = openpgp_cipher_test_algo( dek->algo ); + rc = check_cipher_algo( dek->algo ); if( rc ) { - if( !opt.quiet && rc == GPGERR_CIPHER_ALGO ) { - log_info(_("cipher algorithm %d is unknown or disabled\n"), - dek->algo); + 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 != gcry_cipher_get_algo_keylen( dek->algo ) ) { - rc = GPGERR_WRONG_SECKEY; + if( (dek->keylen*8) != cipher_get_keylen( dek->algo ) ) { + rc = G10ERR_WRONG_SECKEY; goto leave; } @@ -231,53 +195,102 @@ get_it( PKT_pubkey_enc *k, DEK *dek, PKT_secret_key *sk, u32 *keyid ) for( csum2=0, n=0; n < dek->keylen; n++ ) csum2 += dek->key[n]; if( csum != csum2 ) { - rc = GPGERR_WRONG_SECKEY; + 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 = gcry_xcalloc( 1, sizeof *pk ); - if( (rc = get_pubkey( pk, keyid )) ) - log_error("public key problem: %s\n", gpg_errstr(rc) ); - else if( !pk->local_id && query_trust_record(pk) ) - log_error("can't check algorithm against preferences\n"); - else if( dek->algo != GCRY_CIPHER_3DES - && !is_algo_in_prefs( pk->local_id, PREFTYPE_SYM, dek->algo ) ) { + 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 + && !is_algo_in_prefs( pkb, PREFTYPE_SYM, dek->algo ) ) { /* Don't print a note while we are not on verbose mode, * the cipher is blowfish and the preferences have twofish * listed */ - if( opt.verbose || dek->algo != GCRY_CIPHER_BLOWFISH - || !is_algo_in_prefs( pk->local_id, PREFTYPE_SYM, - GCRY_CIPHER_TWOFISH ) ) + if( opt.verbose || dek->algo != CIPHER_ALGO_BLOWFISH + || !is_algo_in_prefs( pkb, PREFTYPE_SYM, CIPHER_ALGO_TWOFISH)) log_info(_( "NOTE: cipher algorithm %d not found in preferences\n"), dek->algo ); } - - if( !rc && pk->expiredate && pk->expiredate <= make_timestamp() ) { - log_info(_("NOTE: secret key %08lX expired at %s\n"), - (ulong)keyid[1], asctimestamp( pk->expiredate) ); - } - - /* FIXME: check wheter the key has been revoked and display - * the revocation reason. Actually the user should know this himself, - * but the sender might not know already and therefor the user - * should get a notice that an revoked key has been used to decode - * the message. The user can than watch out for snakes send by - * one of those Eves outside his paradise :-) - */ - free_public_key( pk ); + 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 %08lX expired at %s\n"), + (ulong)keyid[1], asctimestamp( pk->expiredate) ); + } + } + + if ( 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_release(plain_dek); - gcry_free(frame); + mpi_free(plain_dek); + m_free(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 index e988e5e62..ca67d80e5 100644 --- a/g10/revoke.c +++ b/g10/revoke.c @@ -1,5 +1,5 @@ /* revoke.c - * Copyright (C) 1998, 1999, 2000 Free Software Foundation, Inc. + * Copyright (C) 1998, 1999, 2000, 2001, 2002 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -30,7 +30,7 @@ #include "packet.h" #include "errors.h" #include "keydb.h" -#include <gcrypt.h> +#include "memory.h" #include "util.h" #include "main.h" #include "ttyio.h" @@ -52,25 +52,274 @@ revocation_reason_build_cb( PKT_signature *sig, void *opaque ) byte *buffer; size_t buflen = 1; + if(!reason) + return 0; + if( reason->desc ) { ud = native_to_utf8( reason->desc ); buflen += strlen(ud); } - buffer = gcry_xmalloc( buflen ); + buffer = m_alloc( buflen ); *buffer = reason->code; if( ud ) { memcpy(buffer+1, ud, strlen(ud) ); - gcry_free( ud ); + m_free( ud ); } build_sig_subpkt( sig, SIGSUBPKT_REVOC_REASON, buffer, buflen ); - gcry_free( buffer ); + m_free( buffer ); return 0; } /**************** + * Generate a revocation certificate for UNAME via a designated revoker + */ +int +gen_desig_revoke( const char *uname ) +{ + int rc = 0; + armor_filter_context_t afx; + PACKET pkt; + 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; + + if( opt.batch ) { + log_error(_("sorry, 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); + + /* Are we a designated revoker for this key? */ + + if(!pk->revkey && pk->numrevkeys) + BUG(); + + for(i=0;i<pk->numrevkeys;i++) + { + if(sk) + free_secret_key(sk); + + sk=m_alloc_clear(sizeof(*sk)); + + rc=get_seckey_byfprint(sk,pk->revkey[i].fpr,MAX_FINGERPRINT_LEN); + + /* We have the revocation key */ + if(!rc) + { + size_t n; + char *p; + u32 sk_keyid[2]; + PKT_user_id *uid=NULL; + PKT_signature *selfsig=NULL; + + any=1; + keyid_from_sk(sk,sk_keyid); + + tty_printf("\npub %4u%c/%08lX %s ", + nbits_from_pk( pk ), + pubkey_letter( pk->pubkey_algo ), + (ulong)keyid[1], datestr_from_pk(pk) ); + + p = get_user_id( keyid, &n ); + tty_print_utf8_string( p, n ); + m_free(p); + tty_printf("\n\n"); + + tty_printf(_("To be revoked by:\n")); + + tty_printf("\nsec %4u%c/%08lX %s ", + nbits_from_sk( sk ), + pubkey_letter( sk->pubkey_algo ), + (ulong)sk_keyid[1], datestr_from_sk(sk) ); + + p = get_user_id( sk_keyid, &n ); + tty_print_utf8_string( p, n ); + m_free(p); + tty_printf("\n\n"); + + if( !cpr_get_answer_is_yes("gen_desig_revoke.okay", + _("Create a revocation certificate for this key? ")) ) + 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 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. */ + + node=find_kbnode(keyblock,PKT_PUBLIC_KEY); + if(!node) + { + rc=G10ERR_GENERAL; + log_error(_("key %08lX incomplete\n"),(ulong)keyid[1]); + goto leave; + } + + pkt = *node->pkt; + rc=build_packet(out,&pkt); + if( rc ) { + log_error(_("build_packet failed: %s\n"), g10_errstr(rc) ); + goto leave; + } + + 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; + } + + 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 + { + rc=G10ERR_GENERAL; + log_error(_("key %08lX incomplete\n"),(ulong)keyid[1]); + goto leave; + } + } + + 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) ); + goto leave; + } + + 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) ); + 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 ); + + if( rc ) + iobuf_cancel(out); + else + iobuf_close(out); + release_revocation_reason_info( reason ); + return rc; +} + + +/**************** * Generate a revocation certificate for UNAME */ int @@ -78,7 +327,6 @@ gen_revoke( const char *uname ) { int rc = 0; armor_filter_context_t afx; - compress_filter_context_t zfx; PACKET pkt; PKT_secret_key *sk; /* used as pointer into a kbnode */ PKT_public_key *pk = NULL; @@ -87,34 +335,40 @@ gen_revoke( const char *uname ) IOBUF out = NULL; KBNODE keyblock = NULL; KBNODE node; + KEYDB_HANDLE kdbhd; struct revocation_reason_info *reason = NULL; + KEYDB_SEARCH_DESC desc; if( opt.batch ) { log_error(_("sorry, can't do this in batch mode\n")); - return GPGERR_GENERAL; + return G10ERR_GENERAL; } - memset( &afx, 0, sizeof afx); - memset( &zfx, 0, sizeof zfx); 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; + } - /* search the userid */ - rc = find_secret_keyblock_byname( &keyblock, uname ); + rc = keydb_get_keyblock (kdbhd, &keyblock ); if( rc ) { - log_error(_("secret key for user `%s' not found: %s\n"), - uname, gpg_errstr(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 ) { /* maybe better to use log_bug ? */ - log_error(_("Oops; secret key not found anymore!\n")); - rc = GPGERR_GENERAL; - goto leave; - } + if( !node ) + BUG (); /* fixme: should make a function out of this stuff, * it's used all over the source */ @@ -123,23 +377,25 @@ gen_revoke( const char *uname ) tty_printf("\nsec %4u%c/%08lX %s ", nbits_from_sk( sk ), pubkey_letter( sk->pubkey_algo ), - sk_keyid[1], datestr_from_sk(sk) ); + (ulong)sk_keyid[1], datestr_from_sk(sk) ); { size_t n; char *p = get_user_id( sk_keyid, &n ); tty_print_utf8_string( p, n ); - gcry_free(p); + m_free(p); tty_printf("\n"); } - pk = gcry_xcalloc( 1, sizeof *pk ); + pk = m_alloc_clear( sizeof *pk ); + + /* FIXME: We should get the public key direct from the secret one */ rc = get_pubkey( pk, sk_keyid ); if( rc ) { - log_error(_("no corresponding public key: %s\n"), gpg_errstr(rc) ); + log_error(_("no corresponding public key: %s\n"), g10_errstr(rc) ); goto leave; } if( cmp_public_secret_key( pk, sk ) ) { log_error(_("public key does not match secret key!\n") ); - rc = GPGERR_GENERAL; + rc = G10ERR_GENERAL; goto leave; } @@ -150,17 +406,19 @@ gen_revoke( const char *uname ) goto leave; } - /* get the reason for the revocation */ - reason = ask_revocation_reason( 1, 0, 1 ); - if( !reason ) { /* user decided to cancel */ + 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 = GPGERR_PUBKEY_ALGO; + rc = G10ERR_PUBKEY_ALGO; break; case 0: tty_printf(_("NOTE: This key is not protected!\n")); @@ -185,10 +443,10 @@ gen_revoke( const char *uname ) /* create it */ rc = make_keysig_packet( &sig, pk, NULL, NULL, sk, 0x20, 0, - revocation_reason_build_cb, - reason ); + opt.force_v4_certs?4:0, 0, 0, + revocation_reason_build_cb, reason ); if( rc ) { - log_error(_("make_keysig_packet failed: %s\n"), gpg_errstr(rc)); + log_error(_("make_keysig_packet failed: %s\n"), g10_errstr(rc)); goto leave; } init_packet( &pkt ); @@ -197,7 +455,7 @@ gen_revoke( const char *uname ) rc = build_packet( out, &pkt ); if( rc ) { - log_error(_("build_packet failed: %s\n"), gpg_errstr(rc) ); + log_error(_("build_packet failed: %s\n"), g10_errstr(rc) ); goto leave; } @@ -217,6 +475,7 @@ gen_revoke( const char *uname ) if( sig ) free_seckey_enc( sig ); release_kbnode( keyblock ); + keydb_release (kdbhd); if( rc ) iobuf_cancel(out); else @@ -230,20 +489,22 @@ gen_revoke( const char *uname ) struct revocation_reason_info * ask_revocation_reason( int key_rev, int cert_rev, int hint ) { - int code; + 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 non longer valid"); + const char *text_4 = _("User ID is no longer valid"); const char *code_text = NULL; do { - gcry_free(description); + m_free(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 ) @@ -252,29 +513,31 @@ ask_revocation_reason( int key_rev, int cert_rev, int hint ) tty_printf(" 3 = %s\n", text_3 ); if( cert_rev ) tty_printf(" 4 = %s\n", text_4 ); - tty_printf( " 0 = %s\n", _("Cancel") ); + tty_printf( " Q = %s\n", _("Cancel") ); if( hint ) tty_printf(_("(Probably you want to select %d here)\n"), hint ); - for(code = 0; !code;) { + 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' ) - n = 0; - else if( !isdigit( *answer ) ) - n = -1; - else if( hint && !*answer ) + if( *answer == 'q' || *answer == 'Q') + return NULL; /* cancel */ + if( hint && !*answer ) n = hint; + else if(!isdigit( *answer ) ) + n = -1; else n = atoi(answer); - gcry_free(answer); - if( !n ) - return NULL; /* cancel */ + m_free(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 = 0x02; /* key has been compromised */ code_text = text_1; } else if( key_rev && n == 2 ) { @@ -286,7 +549,7 @@ ask_revocation_reason( int key_rev, int cert_rev, int hint ) code_text = text_3; } else if( cert_rev && n == 4 ) { - code = 0x20; /* uid is non longer valid */ + code = 0x20; /* uid is no longer valid */ code_text = text_4; } else @@ -300,25 +563,25 @@ ask_revocation_reason( int key_rev, int cert_rev, int hint ) trim_trailing_ws( answer, strlen(answer) ); cpr_kill_prompt(); if( !*answer ) { - gcry_free(answer); + m_free(answer); break; } { char *p = make_printable_string( answer, strlen(answer), 0 ); - gcry_free(answer); + m_free(answer); answer = p; } if( !description ) - description = gcry_xstrdup(answer); + description = m_strdup(answer); else { - char *p = gcry_xmalloc( strlen(description) + strlen(answer) + 2 ); + char *p = m_alloc( strlen(description) + strlen(answer) + 2 ); strcpy(stpcpy(stpcpy( p, description),"\n"),answer); - gcry_free(description); + m_free(description); description = p; } - gcry_free(answer); + m_free(answer); } tty_printf(_("Reason for revocation: %s\n"), code_text ); @@ -330,7 +593,7 @@ ask_revocation_reason( int key_rev, int cert_rev, int hint ) } while( !cpr_get_answer_is_yes("ask_revocation_reason.okay", _("Is this okay? ")) ); - reason = gcry_xmalloc( sizeof *reason ); + reason = m_alloc( sizeof *reason ); reason->code = code; reason->desc = description; return reason; @@ -340,8 +603,7 @@ void release_revocation_reason_info( struct revocation_reason_info *reason ) { if( reason ) { - gcry_free( reason->desc ); - gcry_free( reason ); + m_free( reason->desc ); + m_free( reason ); } } - diff --git a/g10/ringedit.c b/g10/ringedit.c deleted file mode 100644 index 6d5b3e0e4..000000000 --- a/g10/ringedit.c +++ /dev/null @@ -1,1360 +0,0 @@ -/* ringedit.c - Function for key ring editing - * Copyright (C) 1998, 2000 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA - */ - - -/**************** - * This module supplies function for: - * - * - Search for a key block (pubkey and all other stuff) and return a - * handle for it. - * - * - Lock/Unlock a key block - * - * - Read a key block into a tree - * - * - Update a key block - * - * - Insert a new key block - * - * - Delete a key block - * - */ - - - -#include <config.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <errno.h> -#include <sys/types.h> -#include <sys/stat.h> -#include <unistd.h> /* for truncate */ -#include <assert.h> - -#include <gcrypt.h> -#include "util.h" -#include "packet.h" -#include "iobuf.h" -#include "keydb.h" -#include "host2net.h" -#include "options.h" -#include "main.h" -#include "i18n.h" -#include "kbx.h" - - - - -struct resource_table_struct { - int used; - int secret; /* this is a secret keyring */ - char *fname; - IOBUF iobuf; - enum resource_type rt; - DOTLOCK lockhd; - int is_locked; -}; -typedef struct resource_table_struct RESTBL; - - -struct keyblock_pos_struct { - int resno; /* resource number */ - enum resource_type rt; - ulong 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; - ulong save_offset; -}; - - - - -#define MAX_RESOURCES 10 -static RESTBL resource_table[MAX_RESOURCES]; -static int default_public_resource; -static int default_secret_resource; - -static int keyring_enum( KBPOS kbpos, KBNODE *ret_root, int skipsigs ); -static int keyring_copy( KBPOS kbpos, int mode, KBNODE root ); - -static int do_kbxf_enum( KBPOS kbpos, KBNODE *ret_root, int skipsigs ); -static int do_kbxf_copy( KBPOS kbpos, int mode, KBNODE root ); - - -static RESTBL * -check_pos( KBPOS kbpos ) -{ - if( kbpos->resno < 0 || kbpos->resno >= MAX_RESOURCES ) - return NULL; - if( !resource_table[kbpos->resno].used ) - return NULL; - return resource_table + kbpos->resno; -} - - -/**************** - * Hmmm, how to avoid deadlock? They should not happen if everyone - * locks the key resources in the same order; but who knows. - * A solution is to use only one lock file in the gnupg homedir but - * what will happen with key resources which normally don't belong - * to the gpg homedir? - */ -static void -lock_rentry( RESTBL *rentry ) -{ - if( !rentry->lockhd ) { - rentry->lockhd = create_dotlock( rentry->fname ); - if( !rentry->lockhd ) - log_fatal("can't allocate lock for `%s'\n", rentry->fname ); - rentry->is_locked = 0; - } - if( !rentry->is_locked ) { - if( make_dotlock( rentry->lockhd, -1 ) ) - log_fatal("can't lock `%s'\n", rentry->fname ); - rentry->is_locked = 1; - } -} - -static void -unlock_rentry( RESTBL *rentry ) -{ - if( opt.lock_once ) - return; - if( !release_dotlock( rentry->lockhd ) ) - rentry->is_locked = 0; -} - - -/**************************************************************** - ****************** public functions **************************** - ****************************************************************/ - -/**************** - * Get the name of the keyrings, start with a sequence number pointing to a 0. - */ -const char * -enum_keyblock_resources( int *sequence, int secret ) -{ - int i = *sequence; - const char *name = NULL; - - for(; i < MAX_RESOURCES; i++ ) - if( resource_table[i].used && !resource_table[i].secret == !secret ) { - if( resource_table[i].fname ) { - name = resource_table[i].fname; - break; - } - } - *sequence = ++i; - return name; -} - - -/**************** - * 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. - */ -int -add_keyblock_resource( const char *url, int force, int secret ) -{ - static int any_secret, any_public; - const char *resname = url; - IOBUF iobuf = NULL; - int i; - char *filename = NULL; - int rc = 0; - enum resource_type rt = rt_UNKNOWN; - - - /* Do we have an URL? - * gnupg-kbxf:filename := this is a KBX file resource - * 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 = rt_RING; - resname += 11; - } - else if( !strncmp( resname, "gnupg-kbxf:", 11 ) ) { - rt = rt_KBXF; - resname += 11; - } - #ifndef HAVE_DRIVE_LETTERS - else if( strchr( resname, ':' ) ) { - log_error("%s: invalid URL\n", url ); - rc = GPGERR_GENERAL; - goto leave; - } - #endif - } - - if( *resname != '/' ) { /* do tilde expansion etc */ - if( strchr(resname, '/') ) - filename = make_filename(resname, NULL); - else - filename = make_filename(opt.homedir, resname, NULL); - } - else - filename = gcry_xstrdup( resname ); - - if( !force ) - force = secret? !any_secret : !any_public; - - for(i=0; i < MAX_RESOURCES; i++ ) - if( !resource_table[i].used ) - break; - if( i == MAX_RESOURCES ) { - rc = GPGERR_RESOURCE_LIMIT; - goto leave; - } - - /* see whether we can determine the filetype */ - if( rt == rt_UNKNOWN ) { - FILE *fp = fopen( filename, "rb" ); - - if( fp ) { - u32 magic; - - if( fread( &magic, 4, 1, fp) == 1 ) { - char buf[8]; - - rt = rt_RING; - if( fread( buf, 8, 1, fp) == 1 ) { - if( !memcmp( buf+4, "KBXf", 4 ) - && buf[0] == 1 && buf[1] == 1 ) { - rt = rt_KBXF; - } - } - } - else /* maybe empty: assume ring */ - rt = rt_RING; - fclose( fp ); - } - else /* no file yet: create ring */ - rt = rt_RING; - } - - switch( rt ) { - case rt_UNKNOWN: - log_error("%s: unknown resource type\n", url ); - rc = GPGERR_GENERAL; - goto leave; - - case rt_RING: - case rt_KBXF: - iobuf = iobuf_open( filename ); - if( !iobuf && !force ) { - rc = GPGERR_OPEN_FILE; - goto leave; - } - - if( !iobuf ) { - char *last_slash_in_filename; - - last_slash_in_filename = strrchr(filename, '/'); - *last_slash_in_filename = 0; - - if( access(filename, F_OK) ) { - /* on the first time we try to create the default homedir and - * in this case the process will be terminated, so that on the - * next invocation it can read the options file in on startup - */ - try_make_homedir( filename ); - rc = GPGERR_OPEN_FILE; - goto leave; - } - - *last_slash_in_filename = '/'; - - iobuf = iobuf_create( filename ); - if( !iobuf ) { - log_error(_("%s: can't create keyring: %s\n"), - filename, strerror(errno)); - rc = GPGERR_OPEN_FILE; - goto leave; - } - else { - #ifndef HAVE_DOSISH_SYSTEM - if( secret ) { - if( chmod( filename, S_IRUSR | S_IWUSR ) ) { - log_error("%s: chmod failed: %s\n", - filename, strerror(errno) ); - rc = GPGERR_WRITE_FILE; - goto leave; - } - } - #endif - if( !opt.quiet ) - log_info(_("%s: keyring created\n"), filename ); - } - } - #if HAVE_DOSISH_SYSTEM || 1 - iobuf_close( iobuf ); - iobuf = NULL; - /* must close it again */ - #endif - break; - - - default: - log_error("%s: unsupported resource type\n", url ); - rc = GPGERR_GENERAL; - goto leave; - } - - #ifndef HAVE_DOSISH_SYSTEM - #if 0 /* fixme: check directory permissions and print a warning */ - if( secret ) { - } - #endif - #endif - - /* fixme: avoid duplicate resources */ - resource_table[i].used = 1; - resource_table[i].secret = !!secret; - resource_table[i].fname = gcry_xstrdup(filename); - resource_table[i].iobuf = iobuf; - resource_table[i].rt = rt; - if( secret ) - default_secret_resource = i; - else - default_public_resource = i; - - leave: - if( rc ) - log_error("keyblock resource `%s': %s\n", filename, gpg_errstr(rc) ); - else if( secret ) - any_secret = 1; - else - any_public = 1; - gcry_free( filename ); - return rc; -} - -/**************** - * Return the resource name of the keyblock associated with KBPOS. - */ -const char * -keyblock_resource_name( KBPOS kbpos ) -{ - RESTBL *rentry; - - if( !(rentry = check_pos( kbpos )) || !rentry->fname ) - log_bug("no name for keyblock resource %d\n", kbpos->resno ); - return rentry->fname; -} - - -/**************** - * Get a keyblock handle KBPOS from a filename. This can be used - * to get a handle for insert_keyblock for a new keyblock. - * Using a filename of NULL returns the default resource - */ -int -get_keyblock_handle( const char *filename, int secret, KBPOS kbpos ) -{ - int i = 0; - - if( !filename ) - i = secret? default_secret_resource : default_public_resource; - - for(; i < MAX_RESOURCES; i++ ) { - if( resource_table[i].used && !resource_table[i].secret == !secret ) { - /* fixme: dos needs case insensitive file compare */ - if( !filename || !strcmp( resource_table[i].fname, filename ) ) { - memset( kbpos, 0, sizeof *kbpos ); - kbpos->resno = i; - kbpos->rt = resource_table[i].rt; - return 0; - } - } - } - return -1; /* not found */ -} - - -/**************** - * Return the filename of the firstkeyblock resource which is intended - * for write access. This will either be the default resource or in - * case this is not writable one of the others. If no writable is found, - * the default filename in the homedirectory will be returned. - * Caller must free, will never return NULL. - */ -char * -get_writable_keyblock_file( int secret ) -{ - int i = secret? default_secret_resource : default_public_resource; - - if( resource_table[i].used && !resource_table[i].secret == !secret ) { - if( !access( resource_table[i].fname, R_OK|W_OK ) ) { - return gcry_xstrdup( resource_table[i].fname ); - } - } - for(i=0; i < MAX_RESOURCES; i++ ) { - if( resource_table[i].used && !resource_table[i].secret == !secret ) { - if( !access( resource_table[i].fname, R_OK|W_OK ) ) { - return gcry_xstrdup( resource_table[i].fname ); - } - } - } - /* Assume the home dir is always writable */ - return make_filename(opt.homedir, secret? "secring.gpg" - : "pubring.gpg", NULL ); -} - - -void -ringedit_copy_kbpos ( KBPOS d, KBPOS s ) -{ - *d = *s; -} - - -/**************** - * Lock the keyblock; wait until it's available - * This function may change the internal data in kbpos, in cases - * when the keyblock to be locked has been modified. - * fixme: remove this function and add an option to search()? - */ -static int -lock_keyblock( KBPOS kbpos ) -{ - if( !check_pos(kbpos) ) - return GPGERR_GENERAL; - return 0; -} - -/**************** - * Release a lock on a keyblock - */ -static void -unlock_keyblock( KBPOS kbpos ) -{ - if( !check_pos(kbpos) ) - BUG(); -} - - -static int -enum_keyrings_open_helper( KBPOS kbpos, int where ) -{ - int i = where; - RESTBL *rentry; - - for(; i < MAX_RESOURCES; i++ ) - if( resource_table[i].used - && !resource_table[i].secret == !kbpos->secret ) - break; - if( i == MAX_RESOURCES ) - return -1; /* no resources */ - kbpos->resno = i; - rentry = check_pos( kbpos ); - kbpos->rt = resource_table[i].rt; - kbpos->valid = 0; - switch( kbpos->rt ) { - case rt_RING: - case rt_KBXF: - kbpos->fp = iobuf_open( rentry->fname ); - if ( !kbpos->fp ) { - log_error("can't open `%s'\n", rentry->fname ); - return GPGERR_OPEN_FILE; - } - break; - - default: BUG(); - } - kbpos->pkt = NULL; - return 0; -} - - -/**************** - * This set of functions is used to scan over all keyrings. - * The mode in enum_keyblocks_next() is used liek this: - * Mode is: 1 = read - * 11 = read but skip signature and comment packets. - */ -int -enum_keyblocks_begin( KBPOS *rkbpos, int use_secret ) -{ - int rc, i; - KBPOS kbpos; - - *rkbpos = NULL; - - kbpos = gcry_xcalloc( 1, sizeof *kbpos ); - kbpos->fp = NULL; - kbpos->rt = rt_UNKNOWN; - if( !use_secret ) { - kbpos->secret = 0; - i = 0; - } - else { - kbpos->secret = 1; - i = 0; - } - - rc = enum_keyrings_open_helper( kbpos, i ); - if ( rc ) { - gcry_free( kbpos ); - return rc; - } - /* return the handle */ - *rkbpos = kbpos; - return 0; -} - -void -enum_keyblocks_end( KBPOS kbpos ) -{ - if ( !kbpos ) - return; - switch( kbpos->rt ) { - case rt_RING: - case rt_KBXF: - if( kbpos->fp ) { - iobuf_close( kbpos->fp ); - kbpos->fp = NULL; - } - break; - case rt_UNKNOWN: - /* this happens when we have no keyring at all */ - gcry_free( kbpos ); - return; - - default: - BUG(); - } - /* release pending packet */ - free_packet( kbpos->pkt ); - gcry_free( kbpos->pkt ); - gcry_free( kbpos ); -} - -int -enum_keyblocks_next( KBPOS kbpos, int mode, KBNODE *ret_root ) -{ - int cont, rc = 0; - RESTBL *rentry; - - if( mode != 1 && mode != 11 ) - return GPGERR_INV_ARG; - - do { - cont = 0; - switch( kbpos->rt ) { - case rt_RING: - if( !kbpos->fp ) - return GPGERR_GENERAL; - rc = keyring_enum( kbpos, ret_root, mode == 11 ); - break; - case rt_KBXF: - if( !kbpos->fp ) - return GPGERR_GENERAL; - rc = do_kbxf_enum( kbpos, ret_root, mode == 11 ); - break; - default: BUG(); - } - - if( rc == -1 ) { - RESTBL *rentry; - int i; - - assert( !kbpos->pkt ); - rentry = check_pos( kbpos ); - assert(rentry); - i = kbpos->resno+1; - /* first close */ - if( kbpos->fp ) { - iobuf_close( kbpos->fp ); - kbpos->fp = NULL; - } - free_packet( kbpos->pkt ); - gcry_free( kbpos->pkt ); - kbpos->pkt = NULL; - /* and then open the next one */ - rc = enum_keyrings_open_helper( kbpos, i ); - if ( !rc ) - cont = 1; - /* hmm, that is not really correct: if we got an error kbpos - * might be not well anymore */ - } - } while(cont); - - return rc; -} - - - - -/**************** - * Insert the keyblock described by ROOT into the keyring described - * by KBPOS. This actually appends the data to the keyfile. - */ -int -insert_keyblock( KBNODE root ) -{ - int rc; -#if 0 - if( !check_pos(kbpos) ) - return GPGERR_GENERAL; - - switch( kbpos->rt ) { - case rt_RING: - rc = keyring_copy( kbpos, 1, root ); - break; - case rt_KBXF: - rc = do_kbxf_copy( kbpos, 1, root ); - break; - default: BUG(); - } -#endif - return rc; -} - -/**************** - * Delete the keyblock described by KBPOS. - * The current code simply changes the keyblock in the keyring - * to packet of type 0 with the correct length. To help detect errors, - * zero bytes are written. - */ -int -delete_keyblock( KBNODE keyblock ) -{ - int rc; - #if 0 - if( !check_pos(kbpos) ) - return GPGERR_GENERAL; - - switch( kbpos->rt ) { - case rt_RING: - rc = keyring_copy( kbpos, 2, NULL ); - break; - case rt_KBXF: - rc = do_kbxf_copy( kbpos, 2, NULL ); - break; - default: BUG(); - } - #endif - return rc; -} - - -/**************** - * Update the keyblock in the ring (or whatever resource) one in ROOT. - */ -int -update_keyblock( KBNODE root ) -{ - int rc; - struct keyblock_pos_struct kbpos; - - /* We need to get the file position of original keyblock first */ - if ( root->pkt->pkttype == PKT_PUBLIC_KEY ) - rc = find_kblocation_bypk( &kbpos, root->pkt->pkt.public_key ); - else if ( root->pkt->pkttype == PKT_SECRET_KEY ) - rc = find_kblocation_bysk( &kbpos, root->pkt->pkt.secret_key ); - else - BUG(); - - if ( rc ) - return rc; - - if( !check_pos(&kbpos) ) - return GPGERR_GENERAL; - - switch( kbpos.rt ) { - case rt_RING: - rc = keyring_copy( &kbpos, 3, root ); - break; - case rt_KBXF: - rc = do_kbxf_copy( &kbpos, 3, root ); - break; - default: BUG(); - } - - return rc; -} - - - -/**************************************************************** - ********** Functions which operates on regular keyrings ******** - ****************************************************************/ - -static int -keyring_enum( KBPOS kbpos, KBNODE *ret_root, int skipsigs ) -{ - PACKET *pkt; - int rc; - RESTBL *rentry; - KBNODE root = NULL; - ulong offset, first_offset=0; - - if( !(rentry=check_pos(kbpos)) ) - return GPGERR_GENERAL; - - if( kbpos->pkt ) { - root = new_kbnode( kbpos->pkt ); - first_offset = kbpos->save_offset; - kbpos->pkt = NULL; - } - kbpos->valid = 0; - - pkt = gcry_xmalloc( sizeof *pkt ); - init_packet(pkt); - while( (rc=parse_packet(kbpos->fp, pkt, &offset )) != -1 ) { - if( rc ) { /* ignore errors */ - if( rc != GPGERR_UNKNOWN_PACKET ) { - log_error("keyring_enum: read error: %s\n", gpg_errstr(rc) ); - rc = GPGERR_INV_KEYRING; - goto ready; - } - free_packet( pkt ); - init_packet( pkt ); - continue; - } - /* make a linked list of all packets */ - switch( pkt->pkttype ) { - case PKT_COMPRESSED: - log_error("skipped compressed packet in keyring\n" ); - free_packet(pkt); - init_packet(pkt); - break; - - case PKT_PUBLIC_KEY: - case PKT_SECRET_KEY: - if( root ) { /* save this packet */ - kbpos->pkt = pkt; - kbpos->save_offset = offset; - pkt = NULL; - goto ready; - } - root = new_kbnode( pkt ); - first_offset = offset; - pkt = gcry_xmalloc( sizeof *pkt ); - init_packet(pkt); - break; - - default: - /* skip pakets at the beginning of a keyring, until we find - * a start packet; issue a warning if it is not a comment */ - if( !root && pkt->pkttype != PKT_COMMENT - && pkt->pkttype != PKT_OLD_COMMENT ) { - break; - } - if( !root || (skipsigs && ( pkt->pkttype == PKT_SIGNATURE - ||pkt->pkttype == PKT_COMMENT - ||pkt->pkttype == PKT_OLD_COMMENT )) ) { - init_packet(pkt); - break; - } - add_kbnode( root, new_kbnode( pkt ) ); - pkt = gcry_xmalloc( sizeof *pkt ); - init_packet(pkt); - break; - } - } - ready: - if( rc == -1 && root ) - rc = 0; - - if( rc ) - release_kbnode( root ); - else { - if ( root ) { - kbpos->offset = first_offset; - kbpos->valid = 1; - } - *ret_root = root; - } - free_packet( pkt ); - gcry_free( pkt ); - - return rc; -} - - -/**************** - * Perform insert/delete/update operation. - * mode 1 = insert - * 2 = delete - * 3 = update - */ -static int -keyring_copy( KBPOS kbpos, int mode, KBNODE root ) -{ - RESTBL *rentry; - IOBUF fp, newfp; - int rc=0; - char *bakfname = NULL; - char *tmpfname = NULL; -#warning We need to lock the keyring while we are editing it. - /* rethink this whole module */ - - if( !(rentry = check_pos( kbpos )) ) - return GPGERR_GENERAL; - - if( opt.dry_run ) - return 0; - - lock_rentry( rentry ); - - /* open the source file */ - if( kbpos->fp ) { - /* BUG(); not allowed with such a handle */ - log_debug("keyring_copy: closing fp %p\n", kbpos->fp ); - iobuf_close (kbpos->fp); - kbpos->fp = NULL; - kbpos->valid = 0; - } - fp = iobuf_open( rentry->fname ); - if( mode == 1 && !fp && errno == ENOENT ) { /* no file yet */ - KBNODE kbctx, node; - - /* insert: create a new file */ - newfp = iobuf_create( rentry->fname ); - if( !newfp ) { - log_error(_("%s: can't create: %s\n"), rentry->fname, strerror(errno)); - unlock_rentry( rentry ); - return GPGERR_OPEN_FILE; - } - else if( !opt.quiet ) - log_info(_("%s: keyring created\n"), rentry->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, gpg_errstr(rc) ); - iobuf_cancel(newfp); - unlock_rentry( rentry ); - return GPGERR_WRITE_FILE; - } - } - if( iobuf_close(newfp) ) { - log_error("%s: close failed: %s\n", rentry->fname, strerror(errno)); - unlock_rentry( rentry ); - return GPGERR_CLOSE_FILE; - } - if( chmod( rentry->fname, S_IRUSR | S_IWUSR ) ) { - log_error("%s: chmod failed: %s\n", - rentry->fname, strerror(errno) ); - unlock_rentry( rentry ); - return GPGERR_WRITE_FILE; - } - return 0; - } - if( !fp ) { - log_error("%s: can't open: %s\n", rentry->fname, strerror(errno) ); - rc = GPGERR_OPEN_FILE; - goto leave; - } - - /* create the new file */ - #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(rentry->fname) > 4 - && !strcmp(rentry->fname+strlen(rentry->fname)-4, ".gpg") ) { - bakfname = gcry_xmalloc( strlen( rentry->fname ) + 1 ); - strcpy(bakfname,rentry->fname); - strcpy(bakfname+strlen(rentry->fname)-4, ".bak"); - tmpfname = gcry_xmalloc( strlen( rentry->fname ) + 1 ); - strcpy(tmpfname,rentry->fname); - strcpy(tmpfname+strlen(rentry->fname)-4, ".tmp"); - } - else { /* file does not end with gpg; hmmm */ - bakfname = gcry_xmalloc( strlen( rentry->fname ) + 5 ); - strcpy(stpcpy(bakfname,rentry->fname),".bak"); - tmpfname = gcry_xmalloc( strlen( rentry->fname ) + 5 ); - strcpy(stpcpy(tmpfname,rentry->fname),".tmp"); - } - #else - bakfname = gcry_xmalloc( strlen( rentry->fname ) + 2 ); - strcpy(stpcpy(bakfname,rentry->fname),"~"); - tmpfname = gcry_xmalloc( strlen( rentry->fname ) + 5 ); - strcpy(stpcpy(tmpfname,rentry->fname),".tmp"); - #endif - newfp = iobuf_create( tmpfname ); - if( !newfp ) { - log_error("%s: can't create: %s\n", tmpfname, strerror(errno) ); - iobuf_close(fp); - rc = GPGERR_OPEN_FILE; - goto leave; - } - - 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", - rentry->fname, tmpfname, gpg_errstr(rc) ); - iobuf_close(fp); - 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, kbpos->offset ); - if( rc ) { /* should never get EOF here */ - log_error("%s: copy to %s failed: %s\n", - rentry->fname, tmpfname, gpg_errstr(rc) ); - iobuf_close(fp); - iobuf_cancel(newfp); - goto leave; - } - /* skip this keyblock */ - assert( kbpos->count ); - rc = skip_some_packets( fp, kbpos->count ); - if( rc ) { - log_error("%s: skipping %u packets failed: %s\n", - rentry->fname, kbpos->count, gpg_errstr(rc)); - iobuf_close(fp); - iobuf_cancel(newfp); - goto leave; - } - } - - if( mode == 1 || mode == 3 ) { /* insert or update */ - KBNODE kbctx, node; - - /* append the new data */ - 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, gpg_errstr(rc) ); - iobuf_close(fp); - iobuf_cancel(newfp); - rc = GPGERR_WRITE_FILE; - goto leave; - } - } - kbpos->valid = 0; - } - - 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", - rentry->fname, tmpfname, gpg_errstr(rc) ); - iobuf_close(fp); - iobuf_cancel(newfp); - goto leave; - } - rc = 0; - } - - /* close both files */ - if( iobuf_close(fp) ) { - log_error("%s: close failed: %s\n", rentry->fname, strerror(errno) ); - rc = GPGERR_CLOSE_FILE; - goto leave; - } - if( iobuf_close(newfp) ) { - log_error("%s: close failed: %s\n", tmpfname, strerror(errno) ); - rc = GPGERR_CLOSE_FILE; - goto leave; - } - /* if the new file is a secring, restrict the permissions */ - #ifndef HAVE_DOSISH_SYSTEM - if( rentry->secret ) { - if( chmod( tmpfname, S_IRUSR | S_IWUSR ) ) { - log_error("%s: chmod failed: %s\n", - tmpfname, strerror(errno) ); - rc = GPGERR_WRITE_FILE; - goto leave; - } - } - #endif - - /* rename and make backup file */ - if( !rentry->secret ) { /* but not for secret keyrings */ - #ifdef HAVE_DOSISH_SYSTEM - remove( bakfname ); - #endif - if( rename( rentry->fname, bakfname ) ) { - log_error("%s: rename to %s failed: %s\n", - rentry->fname, bakfname, strerror(errno) ); - rc = GPGERR_RENAME_FILE; - goto leave; - } - } - #ifdef HAVE_DOSISH_SYSTEM - remove( rentry->fname ); - #endif - if( rename( tmpfname, rentry->fname ) ) { - log_error("%s: rename to %s failed: %s\n", - tmpfname, rentry->fname,strerror(errno) ); - rc = GPGERR_RENAME_FILE; - if( rentry->secret ) { - log_info(_( - "WARNING: 2 files with confidential information exists.\n")); - log_info(_("%s is the unchanged one\n"), rentry->fname ); - log_info(_("%s is the new one\n"), tmpfname ); - log_info(_("Please fix this possible security flaw\n")); - } - goto leave; - } - - leave: - unlock_rentry( rentry ); - gcry_free(bakfname); - gcry_free(tmpfname); - return rc; -} - - -/**************************************************************** - ********** Functions which operate on KBX files **************** - ****************************************************************/ - -static int -do_kbxf_enum( KBPOS kbpos, KBNODE *ret_root, int skipsigs ) -{ - PACKET *pkt; - int rc; - RESTBL *rentry; - KBNODE root = NULL; - - if( !(rentry=check_pos(kbpos)) ) - return GPGERR_GENERAL; - - if( kbpos->pkt ) { - root = new_kbnode( kbpos->pkt ); - kbpos->pkt = NULL; - } - - pkt = gcry_xmalloc( sizeof *pkt ); - init_packet(pkt); - while( (rc=parse_packet(kbpos->fp, pkt, NULL)) != -1 ) { - if( rc ) { /* ignore errors */ - if( rc != GPGERR_UNKNOWN_PACKET ) { - log_error("do_kbxf_enum: read error: %s\n", gpg_errstr(rc) ); - rc = GPGERR_INV_KEYRING; - goto ready; - } - free_packet( pkt ); - init_packet( pkt ); - continue; - } - /* make a linked list of all packets */ - switch( pkt->pkttype ) { - case PKT_COMPRESSED: - log_error("skipped compressed packet in keyring\n" ); - free_packet(pkt); - init_packet(pkt); - break; - - case PKT_PUBLIC_KEY: - case PKT_SECRET_KEY: - if( root ) { /* store this packet */ - kbpos->pkt = pkt; - pkt = NULL; - goto ready; - } - root = new_kbnode( pkt ); - pkt = gcry_xmalloc( sizeof *pkt ); - init_packet(pkt); - break; - - default: - /* skip pakets at the beginning of a keyring, until we find - * a start packet; issue a warning if it is not a comment */ - if( !root && pkt->pkttype != PKT_COMMENT - && pkt->pkttype != PKT_OLD_COMMENT ) { - break; - } - if( !root || (skipsigs && ( pkt->pkttype == PKT_SIGNATURE - ||pkt->pkttype == PKT_COMMENT - ||pkt->pkttype == PKT_OLD_COMMENT )) ) { - init_packet(pkt); - break; - } - add_kbnode( root, new_kbnode( pkt ) ); - pkt = gcry_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 ); - gcry_free( pkt ); - - return rc; -} - - -/**************** - * Perform insert/delete/update operation. - * mode 1 = insert - * 2 = delete - * 3 = update - */ -static int -do_kbxf_copy( KBPOS kbpos, int mode, KBNODE root ) -{ - RESTBL *rentry; - IOBUF fp, newfp; - int rc=0; - char *bakfname = NULL; - char *tmpfname = NULL; - - if( !(rentry = check_pos( kbpos )) ) - return GPGERR_GENERAL; - if( kbpos->fp ) - BUG(); /* not allowed with such a handle */ - - if( opt.dry_run ) - return 0; - - lock_rentry( rentry ); - - /* open the source file */ - fp = iobuf_open( rentry->fname ); - if( mode == 1 && !fp && errno == ENOENT ) { /* no file yet */ - KBNODE kbctx, node; - - /* insert: create a new file */ - newfp = iobuf_create( rentry->fname ); - if( !newfp ) { - log_error(_("%s: can't create: %s\n"), rentry->fname, strerror(errno)); - unlock_rentry( rentry ); - return GPGERR_OPEN_FILE; - } - else if( !opt.quiet ) - log_info(_("%s: keyring created\n"), rentry->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, gpg_errstr(rc) ); - iobuf_cancel(newfp); - unlock_rentry( rentry ); - return GPGERR_WRITE_FILE; - } - } - if( iobuf_close(newfp) ) { - log_error("%s: close failed: %s\n", rentry->fname, strerror(errno)); - unlock_rentry( rentry ); - return GPGERR_CLOSE_FILE; - } - if( chmod( rentry->fname, S_IRUSR | S_IWUSR ) ) { - log_error("%s: chmod failed: %s\n", - rentry->fname, strerror(errno) ); - unlock_rentry( rentry ); - return GPGERR_WRITE_FILE; - } - return 0; - } - if( !fp ) { - log_error("%s: can't open: %s\n", rentry->fname, strerror(errno) ); - rc = GPGERR_OPEN_FILE; - goto leave; - } - - /* create the new file */ - #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(rentry->fname) > 4 - && !strcmp(rentry->fname+strlen(rentry->fname)-4, ".gpg") ) { - bakfname = gcry_xmalloc( strlen( rentry->fname ) + 1 ); - strcpy(bakfname,rentry->fname); - strcpy(bakfname+strlen(rentry->fname)-4, ".bak"); - tmpfname = gcry_xmalloc( strlen( rentry->fname ) + 1 ); - strcpy(tmpfname,rentry->fname); - strcpy(tmpfname+strlen(rentry->fname)-4, ".tmp"); - } - else { /* file does not end with gpg; hmmm */ - bakfname = gcry_xmalloc( strlen( rentry->fname ) + 5 ); - strcpy(stpcpy(bakfname,rentry->fname),".bak"); - tmpfname = gcry_xmalloc( strlen( rentry->fname ) + 5 ); - strcpy(stpcpy(tmpfname,rentry->fname),".tmp"); - } - #else - bakfname = gcry_xmalloc( strlen( rentry->fname ) + 2 ); - strcpy(stpcpy(bakfname,rentry->fname),"~"); - tmpfname = gcry_xmalloc( strlen( rentry->fname ) + 5 ); - strcpy(stpcpy(tmpfname,rentry->fname),".tmp"); - #endif - newfp = iobuf_create( tmpfname ); - if( !newfp ) { - log_error("%s: can't create: %s\n", tmpfname, strerror(errno) ); - iobuf_close(fp); - rc = GPGERR_OPEN_FILE; - goto leave; - } - - 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", - rentry->fname, tmpfname, gpg_errstr(rc) ); - iobuf_close(fp); - 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, kbpos->offset ); - if( rc ) { /* should never get EOF here */ - log_error("%s: copy to %s failed: %s\n", - rentry->fname, tmpfname, gpg_errstr(rc) ); - iobuf_close(fp); - iobuf_cancel(newfp); - goto leave; - } - /* skip this keyblock */ - assert( kbpos->count ); - rc = skip_some_packets( fp, kbpos->count ); - if( rc ) { - log_error("%s: skipping %u packets failed: %s\n", - rentry->fname, kbpos->count, gpg_errstr(rc)); - iobuf_close(fp); - iobuf_cancel(newfp); - goto leave; - } - } - - if( mode == 1 || mode == 3 ) { /* insert or update */ - KBNODE kbctx, node; - - /* append the new data */ - 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, gpg_errstr(rc) ); - iobuf_close(fp); - iobuf_cancel(newfp); - rc = GPGERR_WRITE_FILE; - goto leave; - } - } - kbpos->valid = 0; - } - - 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", - rentry->fname, tmpfname, gpg_errstr(rc) ); - iobuf_close(fp); - iobuf_cancel(newfp); - goto leave; - } - rc = 0; - } - - /* close both files */ - if( iobuf_close(fp) ) { - log_error("%s: close failed: %s\n", rentry->fname, strerror(errno) ); - rc = GPGERR_CLOSE_FILE; - goto leave; - } - if( iobuf_close(newfp) ) { - log_error("%s: close failed: %s\n", tmpfname, strerror(errno) ); - rc = GPGERR_CLOSE_FILE; - goto leave; - } - /* if the new file is a secring, restrict the permissions */ - #ifndef HAVE_DOSISH_SYSTEM - if( rentry->secret ) { - if( chmod( tmpfname, S_IRUSR | S_IWUSR ) ) { - log_error("%s: chmod failed: %s\n", - tmpfname, strerror(errno) ); - rc = GPGERR_WRITE_FILE; - goto leave; - } - } - #endif - - /* rename and make backup file */ - if( !rentry->secret ) { /* but not for secret keyrings */ - #ifdef HAVE_DOSISH_SYSTEM - remove( bakfname ); - #endif - if( rename( rentry->fname, bakfname ) ) { - log_error("%s: rename to %s failed: %s\n", - rentry->fname, bakfname, strerror(errno) ); - rc = GPGERR_RENAME_FILE; - goto leave; - } - } - #ifdef HAVE_DOSISH_SYSTEM - remove( rentry->fname ); - #endif - if( rename( tmpfname, rentry->fname ) ) { - log_error("%s: rename to %s failed: %s\n", - tmpfname, rentry->fname,strerror(errno) ); - rc = GPGERR_RENAME_FILE; - if( rentry->secret ) { - log_info(_( - "WARNING: 2 files with confidential information exists.\n")); - log_info(_("%s is the unchanged one\n"), rentry->fname ); - log_info(_("%s is the new one\n"), tmpfname ); - log_info(_("Please fix this possible security flaw\n")); - } - goto leave; - } - - leave: - unlock_rentry( rentry ); - gcry_free(bakfname); - gcry_free(tmpfname); - return rc; -} - - - - diff --git a/g10/seckey-cert.c b/g10/seckey-cert.c index 2adb9ef4c..01f4c7b2f 100644 --- a/g10/seckey-cert.c +++ b/g10/seckey-cert.c @@ -1,5 +1,5 @@ /* seckey-cert.c - secret key certificate packet handling - * Copyright (C) 1998, 1999, 2000 Free Software Foundation, Inc. + * Copyright (C) 1998, 1999, 2000, 2001, 2002 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -23,58 +23,22 @@ #include <stdlib.h> #include <string.h> #include <assert.h> - -#include <gcrypt.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" -/**************** - * Emulate our old PK interface here - sometime in the future we might - * change the internal design to directly fit to libgcrypt. - */ -static int -pk_check_secret_key( int algo, MPI *skey ) -{ - GCRY_SEXP s_skey; - int rc; - - /* make a sexp from skey */ - if( algo == GCRY_PK_DSA ) { - rc = gcry_sexp_build ( &s_skey, NULL, - "(private-key(dsa(p%m)(q%m)(g%m)(y%m)(x%m)))", - skey[0], skey[1], skey[2], skey[3], skey[4] ); - } - else if( algo == GCRY_PK_ELG || algo == GCRY_PK_ELG_E ) { - rc = gcry_sexp_build ( &s_skey, NULL, - "(private-key(elg(p%m)(g%m)(y%m)(x%m)))", - skey[0], skey[1], skey[2], skey[3] ); - } - else if( algo == GCRY_PK_RSA ) { - rc = gcry_sexp_build ( &s_skey, NULL, - "(private-key(rsa(n%m)(e%m)(d%m)(p%m)(q%m)(u%m)))", - skey[0], skey[1], skey[2], skey[3], skey[4], skey[5] ); - } - else - return GPGERR_PUBKEY_ALGO; - - if ( rc ) - BUG (); - - rc = gcry_pk_testkey( s_skey ); - gcry_sexp_release( s_skey ); - return rc; -} - - static int -do_check( PKT_secret_key *sk ) +do_check( PKT_secret_key *sk, const char *tryagain_text ) { + byte *buffer; u16 csum=0; int i, res; unsigned nbytes; @@ -82,19 +46,20 @@ do_check( PKT_secret_key *sk ) if( sk->is_protected ) { /* remove the protection */ DEK *dek = NULL; u32 keyid[4]; /* 4! because we need two of them */ - GCRY_CIPHER_HD cipher_hd=NULL; + 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 GPGERR_GENERAL; + return G10ERR_GENERAL; } - if( sk->protect.algo == GCRY_CIPHER_NONE ) + if( sk->protect.algo == CIPHER_ALGO_NONE ) BUG(); - if( openpgp_cipher_test_algo( sk->protect.algo ) ) { - log_info(_("protection algorithm %d is not supported\n"), - sk->protect.algo ); - return GPGERR_CIPHER_ALGO; + 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)":"" ); + idea_cipher_warn(0); + return G10ERR_CIPHER_ALGO; } keyid_from_sk( sk, keyid ); keyid[2] = keyid[3] = 0; @@ -103,110 +68,115 @@ do_check( PKT_secret_key *sk ) keyid[3] = sk->main_keyid[1]; } dek = passphrase_to_dek( keyid, sk->pubkey_algo, sk->protect.algo, - &sk->protect.s2k, 0 ); - /* Hmmm: Do we use sync mode here even for Twofish? */ - if( !(cipher_hd = gcry_cipher_open( sk->protect.algo, - GCRY_CIPHER_MODE_CFB, - GCRY_CIPHER_SECURE - | (sk->protect.algo >= 100 ? - 0 : GCRY_CIPHER_ENABLE_SYNC) ) ) - ) { - BUG(); - } - - if( gcry_cipher_setkey( cipher_hd, dek->key, dek->keylen ) ) - log_fatal("set key failed: %s\n", gcry_strerror(-1) ); - gcry_free(dek); + &sk->protect.s2k, 0, tryagain_text ); + cipher_hd = cipher_open( sk->protect.algo, + CIPHER_MODE_AUTO_CFB, 1); + cipher_setkey( cipher_hd, dek->key, dek->keylen ); + m_free(dek); save_sk = copy_secret_key( NULL, sk ); - if( gcry_cipher_setiv( cipher_hd, sk->protect.iv, sk->protect.ivlen )) - log_fatal("set IV failed: %s\n", gcry_strerror(-1) ); + cipher_setiv( cipher_hd, sk->protect.iv, sk->protect.ivlen ); csum = 0; if( sk->version >= 4 ) { - size_t ndata; - unsigned int ndatabits; + int ndata; byte *p, *data; u16 csumc = 0; i = pubkey_get_npkey(sk->pubkey_algo); - assert( gcry_mpi_get_flag( sk->skey[i], GCRYMPI_FLAG_OPAQUE ) ); - p = gcry_mpi_get_opaque( sk->skey[i], &ndatabits ); - ndata = (ndatabits+7)/8; + 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 = gcry_xmalloc_secure( ndata ); - gcry_cipher_decrypt( cipher_hd, data, ndata, p, ndata ); - mpi_release( sk->skey[i] ); sk->skey[i] = NULL ; + data = m_alloc_secure( ndata ); + cipher_decrypt( cipher_hd, data, p, ndata ); + mpi_free( sk->skey[i] ); sk->skey[i] = NULL ; p = data; - 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 */ + 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); } - } - /* 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; - assert( gcry_is_secure( p ) ); - res = gcry_mpi_scan( &sk->skey[i], GCRYMPI_FMT_PGP, - p, &nbytes); - if( res ) - log_bug("gcry_mpi_scan failed in do_check: rc=%d\n", res); - - ndata -= nbytes; - p += nbytes; - } - } - gcry_free(data); + } + 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 ); + ndata -= nbytes; + p += nbytes; + } + /* Note: at this point ndata should be 2 for a simple + checksum or 20 for the sha1 digest */ + } + m_free(data); } else { for(i=pubkey_get_npkey(sk->pubkey_algo); i < pubkey_get_nskey(sk->pubkey_algo); i++ ) { - size_t ndata; - unsigned int ndatabits; - byte *p, *data; - - assert( gcry_mpi_get_flag( sk->skey[i], GCRYMPI_FLAG_OPAQUE ) ); - p = gcry_mpi_get_opaque( sk->skey[i], &ndatabits ); - ndata = (ndatabits+7)/8; - data = gcry_xmalloc_secure( ndata ); - gcry_cipher_sync( cipher_hd ); - gcry_cipher_decrypt( cipher_hd, data, ndata, p, ndata ); - mpi_release( sk->skey[i] ); sk->skey[i] = NULL ; - - res = gcry_mpi_scan( &sk->skey[i], GCRYMPI_FMT_USG, - data, &ndata ); - if( res ) - log_bug("gcry_mpi_scan failed in do_check: rc=%d\n", res); - + buffer = mpi_get_secure_buffer( sk->skey[i], &nbytes, NULL ); + cipher_sync( cipher_hd ); + assert( mpi_is_protected(sk->skey[i]) ); + cipher_decrypt( cipher_hd, buffer, buffer, nbytes ); + mpi_set_buffer( sk->skey[i], buffer, nbytes, 0 ); + mpi_clear_protect_flag( sk->skey[i] ); csum += checksum_mpi( sk->skey[i] ); - gcry_free( data ); + m_free( buffer ); + } + if( opt.emulate_bugs & EMUBUG_GPGCHKSUM ) { + csum = sk->csum; } } - gcry_cipher_close( cipher_hd ); + 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, sk->pubkey_algo ); free_secret_key( save_sk ); - return GPGERR_BAD_PASS; + return G10ERR_BAD_PASS; } - /* the checksum may be correct in some cases, - * so we also check the key itself */ - res = pk_check_secret_key( sk->pubkey_algo, sk->skey ); + /* 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, sk->pubkey_algo ); free_secret_key( save_sk ); - return GPGERR_BAD_PASS; + return G10ERR_BAD_PASS; } free_secret_key( save_sk ); sk->is_protected = 0; @@ -215,11 +185,10 @@ do_check( PKT_secret_key *sk ) csum = 0; for(i=pubkey_get_npkey(sk->pubkey_algo); i < pubkey_get_nskey(sk->pubkey_algo); i++ ) { - assert( !gcry_mpi_get_flag( sk->skey[i], GCRYMPI_FLAG_OPAQUE ) ); csum += checksum_mpi( sk->skey[i] ); } if( csum != sk->csum ) - return GPGERR_CHECKSUM; + return G10ERR_CHECKSUM; } return 0; @@ -234,17 +203,20 @@ do_check( PKT_secret_key *sk ) int check_secret_key( PKT_secret_key *sk, int n ) { - int rc = GPGERR_BAD_PASS; + int rc = G10ERR_BAD_PASS; int i; if( n < 1 ) - n = opt.batch? 1 : 3; /* use the default value */ - - for(i=0; i < n && rc == GPGERR_BAD_PASS; i++ ) { - if( i ) - log_info(_("Invalid passphrase; please try again ...\n")); - rc = do_check( sk ); - if( rc == GPGERR_BAD_PASS && is_status_enabled() ) { + n = (opt.batch && !opt.use_agent)? 1 : 3; /* use the default value */ + + for(i=0; i < n && rc == G10ERR_BAD_PASS; i++ ) { + const char *tryagain = NULL; + if (i) { + tryagain = _("Invalid passphrase; please try again"); + log_info (_("%s ...\n"), tryagain); + } + rc = do_check( sk, tryagain ); + if( rc == G10ERR_BAD_PASS && is_status_enabled() ) { u32 kid[2]; char buf[50]; @@ -289,114 +261,103 @@ protect_secret_key( PKT_secret_key *sk, DEK *dek ) return 0; if( !sk->is_protected ) { /* okay, apply the protection */ - GCRY_CIPHER_HD cipher_hd=NULL; + CIPHER_HANDLE cipher_hd=NULL; - if( openpgp_cipher_test_algo( sk->protect.algo ) ) - rc = GPGERR_CIPHER_ALGO; /* unsupport protection algorithm */ + if( check_cipher_algo( sk->protect.algo ) ) + rc = G10ERR_CIPHER_ALGO; /* unsupport protection algorithm */ else { print_cipher_algo_note( sk->protect.algo ); - if( !(cipher_hd = gcry_cipher_open( sk->protect.algo, - GCRY_CIPHER_MODE_CFB, - GCRY_CIPHER_SECURE - | (sk->protect.algo >= 100 ? - 0 : GCRY_CIPHER_ENABLE_SYNC) )) - ) { - BUG(); - } - rc = gcry_cipher_setkey( cipher_hd, dek->key, dek->keylen ); - if( rc == GCRYERR_WEAK_KEY ) { + 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")); - rc = 0; - } - else if( rc ) - BUG(); - - /* set the IV length */ - { int blocksize = gcry_cipher_get_algo_blklen( sk->protect.algo ); - if( blocksize != 8 && blocksize != 16 ) - log_fatal("unsupported blocksize %d\n", blocksize ); - sk->protect.ivlen = blocksize; - assert( sk->protect.ivlen <= DIM(sk->protect.iv) ); - } - gcry_randomize(sk->protect.iv, sk->protect.ivlen, - GCRY_STRONG_RANDOM); - gcry_cipher_setiv( cipher_hd, sk->protect.iv, sk->protect.ivlen ); - - /* FIXME: replace set/get buffer */ + 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[GNUPG_MAX_NSKEY]; - unsigned narr[GNUPG_MAX_NSKEY]; - unsigned nbits[GNUPG_MAX_NSKEY]; + 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( !gcry_mpi_get_flag( sk->skey[i], GCRYMPI_FLAG_OPAQUE ) ); - - if( gcry_mpi_aprint( GCRYMPI_FMT_USG, (void**)bufarr+j, - narr+j, sk->skey[i])) - BUG(); - - nbits[j] = gcry_mpi_get_nbits( sk->skey[i] ); + 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 < GNUPG_MAX_NSKEY; j++ ) + for( ; j < PUBKEY_MAX_NSKEY; j++ ) bufarr[j] = NULL; - ndata += 2; /* for checksum */ + ndata += opt.simple_sk_checksum? 2 : 20; /* for checksum */ - data = gcry_xmalloc_secure( ndata ); + data = m_alloc_secure( ndata ); p = data; - for(j=0; j < GNUPG_MAX_NSKEY && bufarr[j]; j++ ) { + 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]; - gcry_free(bufarr[j]); + m_free(bufarr[j]); } - csum = checksum( data, ndata-2); - sk->csum = csum; - *p++ = csum >> 8; - *p++ = csum; - assert( p == data+ndata ); - gcry_cipher_encrypt( cipher_hd, data, ndata, NULL, 0 ); + + 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_release( sk->skey[i] ); + mpi_free( sk->skey[i] ); sk->skey[i] = NULL; } i = pubkey_get_npkey(sk->pubkey_algo); - sk->skey[i] = gcry_mpi_set_opaque(NULL, data, ndata*8 ); + sk->skey[i] = mpi_set_opaque(NULL, data, ndata ); } else { /* NOTE: we always recalculate the checksum because there * are some test releases which calculated it wrong */ - /* FIXME: Replace this code -- Hmmm: why */ 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( gcry_mpi_aprint( GCRYMPI_FMT_USG, - &buffer, &nbytes, sk->skey[i] ) ) - BUG(); - - gcry_cipher_sync( cipher_hd ); - assert( !gcry_mpi_get_flag( sk->skey[i], GCRYMPI_FLAG_OPAQUE ) ); - gcry_cipher_encrypt( cipher_hd, buffer, nbytes, NULL, 0 ); - gcry_mpi_release( sk->skey[i] ); - if( gcry_mpi_scan( &sk->skey[i], GCRYMPI_FMT_USG, - buffer,&nbytes ) ) - BUG(); - - gcry_free( buffer ); + csum += checksum_mpi_counted_nbits( sk->skey[i] ); + buffer = mpi_get_buffer( sk->skey[i], &nbytes, NULL ); + cipher_sync( cipher_hd ); + assert( !mpi_is_protected(sk->skey[i]) ); + cipher_encrypt( cipher_hd, buffer, buffer, nbytes ); + mpi_set_buffer( sk->skey[i], buffer, nbytes, 0 ); + mpi_set_protect_flag( sk->skey[i] ); + m_free( buffer ); } sk->csum = csum; } sk->is_protected = 1; - gcry_cipher_close( cipher_hd ); + cipher_close( cipher_hd ); } } return rc; diff --git a/g10/seskey.c b/g10/seskey.c index aa2a1511c..fc912eeb5 100644 --- a/g10/seskey.c +++ b/g10/seskey.c @@ -1,5 +1,5 @@ /* seskey.c - make sesssion keys etc. - * Copyright (C) 1998, 1999, 2000 Free Software Foundation, Inc. + * Copyright (C) 1998, 1999, 2000, 2001 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -23,9 +23,9 @@ #include <stdlib.h> #include <string.h> #include <assert.h> - -#include <gcrypt.h> #include "util.h" +#include "cipher.h" +#include "mpi.h" #include "main.h" #include "i18n.h" @@ -36,31 +36,22 @@ void make_session_key( DEK *dek ) { - GCRY_CIPHER_HD chd; + CIPHER_HANDLE chd; int i, rc; - dek->keylen = gcry_cipher_get_algo_keylen( dek->algo ); - - if( !(chd = gcry_cipher_open( dek->algo, GCRY_CIPHER_MODE_CFB, - GCRY_CIPHER_SECURE - | (dek->algo >= 100 ? - 0 : GCRY_CIPHER_ENABLE_SYNC) )) - ) { - BUG(); - } + dek->keylen = cipher_get_keylen( dek->algo ) / 8; - gcry_randomize( dek->key, dek->keylen, GCRY_STRONG_RANDOM ); + chd = cipher_open( dek->algo, CIPHER_MODE_AUTO_CFB, 1 ); + randomize_buffer( dek->key, dek->keylen, 1 ); for(i=0; i < 16; i++ ) { - rc = gcry_cipher_setkey( chd, dek->key, dek->keylen ); + rc = cipher_setkey( chd, dek->key, dek->keylen ); if( !rc ) { - gcry_cipher_close( chd ); + cipher_close( chd ); return; } - if( rc != GCRYERR_WEAK_KEY ) - BUG(); log_info(_("weak key created - retrying\n") ); /* Renew the session key until we get a non-weak key. */ - gcry_randomize( dek->key, dek->keylen, GCRY_STRONG_RANDOM ); + randomize_buffer( dek->key, dek->keylen, 1 ); } log_fatal(_( "cannot avoid weak key for symmetric cipher; tried %d times!\n"), @@ -108,13 +99,13 @@ encode_session_key( DEK *dek, unsigned nbits ) for( p = dek->key, i=0; i < dek->keylen; i++ ) csum += *p++; - frame = gcry_xmalloc_secure( nframe ); + frame = m_alloc_secure( nframe ); n = 0; frame[n++] = 0; frame[n++] = 2; i = nframe - 6 - dek->keylen; assert( i > 0 ); - p = gcry_random_bytes_secure( i, GCRY_STRONG_RANDOM ); + p = get_random_bits( i*8, 1, 1 ); /* replace zero bytes by new values */ for(;;) { int j, k; @@ -127,14 +118,14 @@ encode_session_key( DEK *dek, unsigned nbits ) if( !k ) break; /* okay: no zero bytes */ k += k/128; /* better get some more */ - pp = gcry_random_bytes_secure( k, GCRY_STRONG_RANDOM); + pp = get_random_bits( k*8, 1, 1); for(j=0; j < i && k ; j++ ) if( !p[j] ) p[j] = pp[--k]; - gcry_free(pp); + m_free(pp); } memcpy( frame+n, p, i ); - gcry_free(p); + m_free(p); n += i; frame[n++] = 0; frame[n++] = dek->algo; @@ -142,16 +133,15 @@ encode_session_key( DEK *dek, unsigned nbits ) frame[n++] = csum >>8; frame[n++] = csum; assert( n == nframe ); - if( gcry_mpi_scan( &a, GCRYMPI_FMT_USG, frame, &nframe ) ) - BUG(); - gcry_free(frame); - + a = mpi_alloc_secure( (nframe+BYTES_PER_MPI_LIMB-1) / BYTES_PER_MPI_LIMB ); + mpi_set_buffer( a, frame, nframe, 0 ); + m_free(frame); return a; } static MPI -do_encode_md( GCRY_MD_HD md, int algo, size_t len, unsigned nbits, +do_encode_md( MD_HANDLE md, int algo, size_t len, unsigned nbits, const byte *asn, size_t asnlen, int v3compathack ) { int nframe = (nbits+7) / 8; @@ -165,12 +155,11 @@ do_encode_md( GCRY_MD_HD md, int algo, size_t len, unsigned nbits, /* We encode the MD in this way: * - * 0 1 PAD(n bytes) 0 ASN(asnlen bytes) MD(len bytes) + * 0 A PAD(n bytes) 0 ASN(asnlen bytes) MD(len bytes) * * PAD consists of FF bytes. */ - frame = gcry_md_is_secure(md)? gcry_xmalloc_secure( nframe ) - : gcry_xmalloc( nframe ); + frame = md_is_secure(md)? m_alloc_secure( nframe ) : m_alloc( nframe ); n = 0; frame[n++] = 0; frame[n++] = v3compathack? algo : 1; /* block type */ @@ -179,11 +168,13 @@ do_encode_md( GCRY_MD_HD md, int algo, size_t len, unsigned nbits, memset( frame+n, 0xff, i ); n += i; frame[n++] = 0; memcpy( frame+n, asn, asnlen ); n += asnlen; - memcpy( frame+n, gcry_md_read(md, algo), len ); n += len; + memcpy( frame+n, md_read(md, algo), len ); n += len; assert( n == nframe ); - if( gcry_mpi_scan( &a, GCRYMPI_FMT_USG, frame, &nframe ) ) - BUG(); - gcry_free(frame); + 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 ); + m_free(frame); return a; } @@ -192,36 +183,35 @@ do_encode_md( GCRY_MD_HD md, int algo, size_t len, unsigned nbits, * Encode a message digest into an MPI. * v3compathack is used to work around a bug in old GnuPG versions * which did put the algo identifier inseatd of the block type 1 into - * the encoded value. setting this vare force the old behaviour. + * the encoded value. Setting this flag forces the old behaviour. */ MPI -encode_md_value( int pubkey_algo, GCRY_MD_HD md, int hash_algo, +encode_md_value( int pubkey_algo, MD_HANDLE md, int hash_algo, unsigned nbits, int v3compathack ) { - int algo = hash_algo? hash_algo : gcry_md_get_algo(md); + int algo = hash_algo? hash_algo : md_get_algo(md); + const byte *asn; + size_t asnlen, mdlen; MPI frame; - if( pubkey_algo == GCRY_PK_DSA ) { - size_t n = gcry_md_get_algo_dlen(hash_algo); - if( gcry_mpi_scan( &frame, GCRYMPI_FMT_USG, - gcry_md_read(md, hash_algo), &n ) ) - BUG(); + if( pubkey_algo == PUBKEY_ALGO_DSA ) { + mdlen = md_digest_length (hash_algo); + if (mdlen != 20) { + log_error (_("DSA requires the use of a 160 bit hash algorithm\n")); + return NULL; + } + + frame = md_is_secure(md)? mpi_alloc_secure((md_digest_length(hash_algo) + +BYTES_PER_MPI_LIMB-1) / BYTES_PER_MPI_LIMB ) + : mpi_alloc((md_digest_length(hash_algo) + +BYTES_PER_MPI_LIMB-1) / BYTES_PER_MPI_LIMB ); + mpi_set_buffer( frame, md_read(md, hash_algo), + md_digest_length(hash_algo), 0 ); } else { - byte *asn; - size_t asnlen; - - if( gcry_md_algo_info( algo, GCRYCTL_GET_ASNOID, NULL, &asnlen ) ) - log_fatal("can't get OID of algo %d: %s\n", - algo, gcry_strerror(-1)); - asn = gcry_xmalloc( asnlen ); - if( gcry_md_algo_info( algo, GCRYCTL_GET_ASNOID, asn, &asnlen ) ) - BUG(); - frame = do_encode_md( md, algo, gcry_md_get_algo_dlen( algo ), - nbits, asn, asnlen, v3compathack ); - gcry_free( asn ); + asn = md_asn_oid( algo, &asnlen, &mdlen ); + frame = do_encode_md( md, algo, mdlen, nbits, asn, asnlen, v3compathack); } return frame; } - diff --git a/g10/sig-check.c b/g10/sig-check.c index a3946a1e0..c9c19aad4 100644 --- a/g10/sig-check.c +++ b/g10/sig-check.c @@ -1,5 +1,5 @@ /* sig-check.c - Check a signature - * Copyright (C) 1998, 1999, 2000 Free Software Foundation, Inc. + * Copyright (C) 1998, 1999, 2000, 2001, 2002 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -23,11 +23,12 @@ #include <stdlib.h> #include <string.h> #include <assert.h> - -#include <gcrypt.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" @@ -35,108 +36,35 @@ struct cmp_help_context_s { PKT_signature *sig; - GCRY_MD_HD md; + MD_HANDLE md; }; - -static int do_signature_check( PKT_signature *sig, GCRY_MD_HD digest, - u32 *r_expiredate, int *r_expired ); static int do_check( PKT_public_key *pk, PKT_signature *sig, - GCRY_MD_HD digest, int *r_expired ); - - - -/**************** - * Emulate our old PK interface here - sometime in the future we might - * change the internal design to directly fit to libgcrypt. - */ -static int -pk_verify( int algo, MPI hash, MPI *data, MPI *pkey, - int (*cmp)(void *, MPI), void *opaque ) -{ - GCRY_SEXP s_sig, s_hash, s_pkey; - int rc; - - /* forget about cmp and opaque - we never used it */ - - /* make a sexp from pkey */ - if( algo == GCRY_PK_DSA ) { - rc = gcry_sexp_build ( &s_pkey, NULL, - "(public-key(dsa(p%m)(q%m)(g%m)(y%m)))", - pkey[0], pkey[1], pkey[2], pkey[3] ); - } - else if( algo == GCRY_PK_ELG || algo == GCRY_PK_ELG_E ) { - rc = gcry_sexp_build ( &s_pkey, NULL, - "(public-key(elg(p%m)(g%m)(y%m)))", - pkey[0], pkey[1], pkey[2] ); - } - else if( algo == GCRY_PK_RSA ) { - rc = gcry_sexp_build ( &s_pkey, NULL, - "(public-key(rsa(n%m)(e%m)))", - pkey[0], pkey[1] ); - } - else - return GPGERR_PUBKEY_ALGO; - - if ( rc ) - BUG (); - - /* put hash into a S-Exp s_hash */ - if ( gcry_sexp_build( &s_hash, NULL, "%m", hash ) ) - BUG (); - - /* put data into a S-Exp s_sig */ - if( algo == GCRY_PK_DSA ) { - rc = gcry_sexp_build ( &s_sig, NULL, - "(sig-val(dsa(r%m)(s%m)))", data[0], data[1] ); - } - else if( algo == GCRY_PK_ELG || algo == GCRY_PK_ELG_E ) { - rc = gcry_sexp_build ( &s_sig, NULL, - "(sig-val(elg(r%m)(s%m)))", data[0], data[1] ); - } - else if( algo == GCRY_PK_RSA ) { - rc = gcry_sexp_build ( &s_sig, NULL, - "(sig-val(rsa(s%m)))", data[0] ); - } - else - BUG(); - - if ( rc ) - BUG (); - - - rc = gcry_pk_verify( s_sig, s_hash, s_pkey ); - gcry_sexp_release( s_sig ); - gcry_sexp_release( s_hash ); - gcry_sexp_release( s_pkey ); - return rc; -} - - + MD_HANDLE digest, int *r_expired ); /**************** * Check the signature which is contained in SIG. - * The GCRY_MD_HD should be currently open, so that this function + * 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, GCRY_MD_HD digest ) +signature_check( PKT_signature *sig, MD_HANDLE digest ) { u32 dummy; int dum2; - return do_signature_check( sig, digest, &dummy, &dum2 ); + return signature_check2( sig, digest, &dummy, &dum2 ); } -static int -do_signature_check( PKT_signature *sig, GCRY_MD_HD digest, - u32 *r_expiredate, int *r_expired ) +int +signature_check2( PKT_signature *sig, MD_HANDLE digest, + u32 *r_expiredate, int *r_expired ) { - PKT_public_key *pk = gcry_xcalloc( 1, sizeof *pk ); + PKT_public_key *pk = m_alloc_clear( sizeof *pk ); int rc=0; *r_expiredate = 0; if( get_pubkey( pk, sig->keyid ) ) - rc = GPGERR_NO_PUBKEY; + rc = G10ERR_NO_PUBKEY; else { *r_expiredate = pk->expiredate; rc = do_check( pk, sig, digest, r_expired ); @@ -150,40 +78,38 @@ do_signature_check( PKT_signature *sig, GCRY_MD_HD digest, * 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 bacth processing applications might + * one second. Some remote batch processing applications might * like this feature here */ - GCRY_MD_HD md; + MD_HANDLE md; u32 a = sig->timestamp; int i, nsig = pubkey_get_nsig( sig->pubkey_algo ); byte *p, *buffer; - if( !(md = gcry_md_open( GCRY_MD_RMD160, 0)) ) - BUG(); - gcry_md_putc( digest, sig->pubkey_algo ); - gcry_md_putc( digest, sig->digest_algo ); - gcry_md_putc( digest, (a >> 24) & 0xff ); - gcry_md_putc( digest, (a >> 16) & 0xff ); - gcry_md_putc( digest, (a >> 8) & 0xff ); - gcry_md_putc( digest, a & 0xff ); + 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++ ) { - size_t n = gcry_mpi_get_nbits( sig->data[i]); - - gcry_md_putc( md, n>>8); - gcry_md_putc( md, n ); - if( gcry_mpi_aprint( GCRYMPI_FMT_USG, &p, &n, sig->data[i] ) ) - BUG(); - gcry_md_write( md, p, n ); - gcry_free(p); + 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 ); + m_free(p); } - gcry_md_final( md ); - p = make_radix64_string( gcry_md_read( md, 0 ), 20 ); - buffer = gcry_xmalloc( strlen(p) + 60 ); + md_final( md ); + p = make_radix64_string( md_read( md, 0 ), 20 ); + buffer = m_alloc( strlen(p) + 60 ); sprintf( buffer, "%s %s %lu", p, strtimestamp( sig->timestamp ), (ulong)sig->timestamp ); write_status_text( STATUS_SIG_ID, buffer ); - gcry_free(buffer); - gcry_free(p); - gcry_md_close(md); + m_free(buffer); + m_free(p); + md_close(md); } return rc; @@ -203,7 +129,7 @@ cmp_help( void *opaque, MPI result ) size_t mdlen, asnlen; struct cmp_help_context_s *ctx = opaque; PKT_signature *sig = ctx->sig; - GCRY_MD_HD digest = ctx->md; + MD_HANDLE digest = ctx->md; old_enc = 0; for(i=j=0; (c=mpi_getbyte(result, i)) != -1; i++ ) { @@ -226,7 +152,7 @@ cmp_help( void *opaque, MPI result ) } if( old_enc ) { log_error("old encoding scheme is not supported\n"); - return GPGERR_GENERAL; + return G10ERR_GENERAL; } if( (rc=check_digest_algo(sig->digest_algo)) ) @@ -238,25 +164,25 @@ cmp_help( void *opaque, MPI result ) if( asn[j] != c ) break; if( j != -1 || mpi_getbyte(result, i) ) - return GPGERR_BAD_PUBKEY; /* ASN is wrong */ + return G10ERR_BAD_PUBKEY; /* ASN is wrong */ for(i++; (c=mpi_getbyte(result, i)) != -1; i++ ) if( c != 0xff ) break; i++; if( c != sig->digest_algo || mpi_getbyte(result, i) ) { /* Padding or leading bytes in signature is wrong */ - return GPGERR_BAD_PUBKEY; + return G10ERR_BAD_PUBKEY; } if( mpi_getbyte(result, mdlen-1) != sig->digest_start[0] || mpi_getbyte(result, mdlen-2) != sig->digest_start[1] ) { /* Wrong key used to check the signature */ - return GPGERR_BAD_PUBKEY; + return G10ERR_BAD_PUBKEY; } dp = md_read( digest, sig->digest_algo ); for(i=mdlen-1; i >= 0; i--, dp++ ) { if( mpi_getbyte( result, i ) != *dp ) - return GPGERR_BAD_SIGN; + return G10ERR_BAD_SIGN; } return 0; #else @@ -266,7 +192,7 @@ cmp_help( void *opaque, MPI result ) static int -do_check( PKT_public_key *pk, PKT_signature *sig, GCRY_MD_HD digest, +do_check( PKT_public_key *pk, PKT_signature *sig, MD_HANDLE digest, int *r_expired ) { MPI result = NULL; @@ -275,10 +201,10 @@ do_check( PKT_public_key *pk, PKT_signature *sig, GCRY_MD_HD digest, u32 cur_time; *r_expired = 0; - if( pk->version == 4 && pk->pubkey_algo == GCRY_PK_ELG_E ) { + if( pk->version == 4 && pk->pubkey_algo == PUBKEY_ALGO_ELGAMAL_E ) { log_info(_("this is a PGP generated " - "ElGamal key which is NOT secure for signatures!\n")); - return GPGERR_PUBKEY_ALGO; + "ElGamal key which is NOT secure for signatures!\n")); + return G10ERR_PUBKEY_ALGO; } if( pk->timestamp > sig->timestamp ) { @@ -288,7 +214,7 @@ do_check( PKT_public_key *pk, PKT_signature *sig, GCRY_MD_HD digest, : _("public key is %lu seconds newer than the signature\n"), d ); if( !opt.ignore_time_conflict ) - return GPGERR_TIME_CONFLICT; /* pubkey newer than signature */ + return G10ERR_TIME_CONFLICT; /* pubkey newer than signature */ } cur_time = make_timestamp(); @@ -299,44 +225,55 @@ do_check( PKT_public_key *pk, PKT_signature *sig, GCRY_MD_HD digest, : _("key has been created %lu seconds " "in future (time warp or clock problem)\n"), d ); if( !opt.ignore_time_conflict ) - return GPGERR_TIME_CONFLICT; + return G10ERR_TIME_CONFLICT; } if( pk->expiredate && pk->expiredate < cur_time ) { - log_info(_("NOTE: signature key expired %s\n"), - asctimestamp( pk->expiredate ) ); + char buf[11]; + if (opt.verbose) { + u32 tmp_kid[2]; + + keyid_from_pk( pk, tmp_kid ); + log_info(_("NOTE: signature key %08lX expired %s\n"), + (ulong)tmp_kid[1], asctimestamp( pk->expiredate ) ); + } + /* SIGEXPIRED is deprecated. Use KEYEXPIRED. */ + sprintf(buf,"%lu",(ulong)pk->expiredate); + write_status_text(STATUS_KEYEXPIRED,buf); write_status(STATUS_SIGEXPIRED); *r_expired = 1; } - if( (rc=openpgp_md_test_algo(sig->digest_algo)) ) + if( (rc=check_digest_algo(sig->digest_algo)) ) return rc; - if( (rc=openpgp_pk_test_algo(sig->pubkey_algo, 0)) ) + if( (rc=check_pubkey_algo(sig->pubkey_algo)) ) return rc; /* make sure the digest algo is enabled (in case of a detached signature)*/ - gcry_md_enable( digest, sig->digest_algo ); + md_enable( digest, sig->digest_algo ); /* complete the digest */ if( sig->version >= 4 ) - gcry_md_putc( digest, sig->version ); - gcry_md_putc( digest, sig->sig_class ); + md_putc( digest, sig->version ); + md_putc( digest, sig->sig_class ); if( sig->version < 4 ) { u32 a = sig->timestamp; - gcry_md_putc( digest, (a >> 24) & 0xff ); - gcry_md_putc( digest, (a >> 16) & 0xff ); - gcry_md_putc( digest, (a >> 8) & 0xff ); - gcry_md_putc( digest, a & 0xff ); + 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; - gcry_md_putc( digest, sig->pubkey_algo ); - gcry_md_putc( digest, sig->digest_algo ); - if( sig->hashed_data ) { - n = (sig->hashed_data[0] << 8) | sig->hashed_data[1]; - gcry_md_write( digest, sig->hashed_data, n+2 ); + 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 @@ -348,57 +285,61 @@ do_check( PKT_public_key *pk, PKT_signature *sig, GCRY_MD_HD digest, buf[3] = n >> 16; buf[4] = n >> 8; buf[5] = n; - gcry_md_write( digest, buf, 6 ); + md_write( digest, buf, 6 ); } - gcry_md_final( digest ); + md_final( digest ); result = encode_md_value( pk->pubkey_algo, digest, sig->digest_algo, - gcry_mpi_get_nbits(pk->pkey[0]), 0); + mpi_get_nbits(pk->pkey[0]), 0 ); + if (!result) + return G10ERR_GENERAL; ctx.sig = sig; ctx.md = digest; - rc = pk_verify( pk->pubkey_algo, result, sig->data, pk->pkey, + rc = pubkey_verify( pk->pubkey_algo, result, sig->data, pk->pkey, cmp_help, &ctx ); - mpi_release( result ); + mpi_free( result ); if( (opt.emulate_bugs & EMUBUG_MDENCODE) - && rc == GPGERR_BAD_SIGN && is_ELGAMAL(pk->pubkey_algo) ) { + && rc == G10ERR_BAD_SIGN && is_ELGAMAL(pk->pubkey_algo) ) { /* In this case we try again because old GnuPG versions didn't encode * the hash right. There is no problem with DSA however */ result = encode_md_value( pk->pubkey_algo, digest, sig->digest_algo, - gcry_mpi_get_nbits(pk->pkey[0]), (sig->version < 5) ); - ctx.sig = sig; - ctx.md = digest; - rc = pk_verify( pk->pubkey_algo, result, sig->data, pk->pkey, - cmp_help, &ctx ); + mpi_get_nbits(pk->pkey[0]), (sig->version < 5) ); + if (!result) + rc = G10ERR_GENERAL; + else { + ctx.sig = sig; + ctx.md = digest; + rc = pubkey_verify( pk->pubkey_algo, result, sig->data, pk->pkey, + cmp_help, &ctx ); + } } if( !rc && sig->flags.unknown_critical ) { log_info(_("assuming bad signature due to an unknown critical bit\n")); - rc = GPGERR_BAD_SIGN; + rc = G10ERR_BAD_SIGN; } - sig->flags.checked = 1; - sig->flags.valid = !rc; return rc; } static void -hash_uid_node( KBNODE unode, GCRY_MD_HD md, PKT_signature *sig ) +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->photo ) { + if( uid->attrib_data ) { if( sig->version >=4 ) { byte buf[5]; - buf[0] = 0xd1; /* packet of type 17 */ - buf[1] = uid->photolen >> 24; /* always use 4 length bytes */ - buf[2] = uid->photolen >> 16; - buf[3] = uid->photolen >> 8; - buf[4] = uid->photolen; - gcry_md_write( md, 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 ); } - gcry_md_write( md, uid->photo, uid->photolen ); + md_write( md, uid->attrib_data, uid->attrib_len ); } else { if( sig->version >=4 ) { @@ -408,9 +349,26 @@ hash_uid_node( KBNODE unode, GCRY_MD_HD md, PKT_signature *sig ) buf[2] = uid->len >> 16; buf[3] = uid->len >> 8; buf[4] = uid->len; - gcry_md_write( md, buf, 5 ); + md_write( md, buf, 5 ); } - gcry_md_write( md, uid->name, uid->len ); + 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; } } @@ -431,7 +389,7 @@ int check_key_signature2( KBNODE root, KBNODE node, int *is_selfsig, u32 *r_expiredate, int *r_expired ) { - GCRY_MD_HD md; + MD_HANDLE md; PKT_public_key *pk; PKT_signature *sig; int algo; @@ -448,47 +406,49 @@ check_key_signature2( KBNODE root, KBNODE node, int *is_selfsig, sig = node->pkt->pkt.signature; algo = sig->digest_algo; - #if 0 - if( sig->flags.checked ) { - log_debug("check_key_signature: already checked: %s\n", - sig->flags.valid? "good":"bad" ); - if ( sig->flags.valid ) - return 0; /* shortcut already checked signatures */ - /* FIXME: We should also do this with bad signatures but here we - * have to distinguish between several reasons; e.g. for a missing - * public key. the key may now be available. - * For now we simply don't shortcut bad signatures - */ + /* check whether we have cached the result of a previous signature check.*/ + 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; + } + return sig->flags.valid? 0 : G10ERR_BAD_SIGN; + } } - #endif - if( (rc=openpgp_md_test_algo(algo)) ) + if( (rc=check_digest_algo(algo)) ) return rc; - if( sig->sig_class == 0x20 ) { - if( !(md = gcry_md_open( algo, 0 )) ) - BUG(); + if( sig->sig_class == 0x20 ) { /* key revocation */ + md = md_open( algo, 0 ); hash_public_key( md, pk ); rc = do_check( pk, sig, md, r_expired ); - gcry_md_close(md); + 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 ) { - if( !(md = gcry_md_open( algo, 0 )) ) - BUG(); + 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 ); - gcry_md_close(md); + cache_sig_result ( sig, rc ); + md_close(md); } else { - log_error("no subkey for subkey revocation packet\n"); - rc = GPGERR_SIG_CLASS; + if (!opt.quiet) + log_info ("key %08lX: no subkey for subkey revocation packet\n", + (ulong)keyid_from_pk (pk, NULL)); + rc = G10ERR_SIG_CLASS; } } - else if( sig->sig_class == 0x18 ) { + else if( sig->sig_class == 0x18 ) { /* key binding */ KBNODE snode = find_prev_kbnode( root, node, PKT_PUBLIC_SUBKEY ); if( snode ) { @@ -499,27 +459,35 @@ check_key_signature2( KBNODE root, KBNODE node, int *is_selfsig, if( keyid[0] == sig->keyid[0] && keyid[1] == sig->keyid[1] ) *is_selfsig = 1; } - if( !(md = gcry_md_open( algo, 0 )) ) - BUG(); + 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 ); - gcry_md_close(md); + cache_sig_result ( sig, rc ); + md_close(md); } else { - log_error("no subkey for key signature packet\n"); - rc = GPGERR_SIG_CLASS; + if (!opt.quiet) + log_info ("key %08lX: no subkey for subkey binding packet\n", + (ulong)keyid_from_pk (pk, NULL)); + rc = G10ERR_SIG_CLASS; } } - else { + 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 ); + 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 ); - if( !(md = gcry_md_open( algo, 0 )) ) - BUG(); + 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] ) { @@ -528,13 +496,17 @@ check_key_signature2( KBNODE root, KBNODE node, int *is_selfsig, rc = do_check( pk, sig, md, r_expired ); } else { - rc = do_signature_check( sig, md, r_expiredate, r_expired ); + rc = signature_check2( sig, md, r_expiredate, r_expired ); } - gcry_md_close(md); + cache_sig_result ( sig, rc ); + md_close(md); } else { - log_error("no user ID for key signature packet\n"); - rc = GPGERR_SIG_CLASS; + if (!opt.quiet) + log_info ("key %08lX: no user ID for key signature packet " + "of class %02x\n", + (ulong)keyid_from_pk (pk, NULL), sig->sig_class ); + rc = G10ERR_SIG_CLASS; } } diff --git a/g10/sign.c b/g10/sign.c index bdc5b8afe..6a8ce2991 100644 --- a/g10/sign.c +++ b/g10/sign.c @@ -1,5 +1,5 @@ /* sign.c - sign data - * Copyright (C) 1998, 1999, 2000 Free Software Foundation, Inc. + * Copyright (C) 1998, 1999, 2000, 2001, 2002 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -24,13 +24,14 @@ #include <string.h> #include <errno.h> #include <assert.h> +#include <unistd.h> /* need sleep() */ -#include <gcrypt.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" @@ -40,125 +41,212 @@ #include "i18n.h" -#define ENABLE_BETTER_PGP2_COMPAT 1 - #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; + /**************** - * Emulate our old PK interface here - sometime in the future we might - * change the internal design to directly fit to libgcrypt. + * Create a notation. It is assumed that the stings in STRLIST + * are already checked to contain only printable data and have a valid + * NAME=VALUE format. */ -static int -pk_sign( int algo, MPI *data, MPI hash, MPI *skey ) +static void +mk_notation_and_policy( PKT_signature *sig, + PKT_public_key *pk, PKT_secret_key *sk ) { - GCRY_SEXP s_sig, s_hash, s_skey, list; - int rc; + const char *string; + char *s=NULL; + byte *buf; + unsigned n1, n2; + STRLIST nd=NULL,pu=NULL; + struct expando_args args; - /* make a sexp from skey */ - if( algo == GCRY_PK_DSA ) { - rc = gcry_sexp_build ( &s_skey, NULL, - "(private-key(dsa(p%m)(q%m)(g%m)(y%m)(x%m)))", - skey[0], skey[1], skey[2], skey[3], skey[4] ); - } - else if( algo == GCRY_PK_ELG || algo == GCRY_PK_ELG_E ) { - rc = gcry_sexp_build ( &s_skey, NULL, - "(private-key(elg(p%m)(g%m)(y%m)(x%m)))", - skey[0], skey[1], skey[2], skey[3] ); - } - else - return GPGERR_PUBKEY_ALGO; + memset(&args,0,sizeof(args)); + args.pk=pk; + args.sk=sk; - if ( rc ) - BUG (); + /* notation data */ + if(IS_SIG(sig) && opt.sig_notation_data) + { + if(sig->version<4) + log_info("can't put notation data into v3 signatures\n"); + else + nd=opt.sig_notation_data; + } + else if( IS_CERT(sig) && opt.cert_notation_data ) + { + if(sig->version<4) + log_info("can't put notation data into v3 key signatures\n"); + else + nd=opt.cert_notation_data; + } + + for( ; nd; nd = nd->next ) { + char *expanded; + + string = nd->d; + s = strchr( string, '=' ); + if( !s ) + BUG(); /* we have already parsed this */ + n1 = s - string; + s++; + + expanded=pct_expando(s,&args); + if(!expanded) + { + log_error(_("WARNING: unable to %%-expand notation " + "(too large). Using unexpanded.\n")); + expanded=m_strdup(s); + } + + n2 = strlen(expanded); + buf = m_alloc( 8 + n1 + n2 ); + buf[0] = 0x80; /* human readable */ + buf[1] = buf[2] = buf[3] = 0; + buf[4] = n1 >> 8; + buf[5] = n1; + buf[6] = n2 >> 8; + buf[7] = n2; + memcpy(buf+8, string, n1 ); + memcpy(buf+8+n1, expanded, n2 ); + build_sig_subpkt( sig, SIGSUBPKT_NOTATION + | ((nd->flags & 1)? SIGSUBPKT_FLAG_CRITICAL:0), + buf, 8+n1+n2 ); + m_free(expanded); + m_free(buf); + } - /* put hash into a S-Exp s_hash */ - if ( gcry_sexp_build( &s_hash, NULL, "%m", hash ) ) - BUG (); + if(opt.show_notation) + show_notation(sig,0); - rc = gcry_pk_sign( &s_sig, s_hash, s_skey ); - gcry_sexp_release( s_hash ); - gcry_sexp_release( s_skey ); + /* set policy URL */ + if( IS_SIG(sig) && opt.sig_policy_url ) + { + if(sig->version<4) + log_info("can't put a policy URL into v3 signatures\n"); + else + pu=opt.sig_policy_url; + } + else if( IS_CERT(sig) && opt.cert_policy_url ) + { + if(sig->version<4) + log_info("can't put a policy URL into v3 key 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=m_strdup(string); + } + + build_sig_subpkt(sig,SIGSUBPKT_POLICY| + ((pu->flags & 1)?SIGSUBPKT_FLAG_CRITICAL:0), + s,strlen(s)); + + m_free(s); + } + + if(opt.show_policy_url) + show_policy_url(sig,0); +} - if( rc ) - ; - else { - list = gcry_sexp_find_token( s_sig, "r" , 0 ); - assert( list ); - data[0] = gcry_sexp_nth_mpi( list, 1, 0 ); - assert( data[0] ); - gcry_sexp_release (list); - list = gcry_sexp_find_token( s_sig, "s" , 0 ); - assert( list ); - data[1] = gcry_sexp_nth_mpi( list, 1, 0 ); - assert( data[1] ); - gcry_sexp_release (list); +/* + * 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 ); } - - gcry_sexp_release( s_sig ); - return rc; + if(uid->attrib_data) + md_write (md, uid->attrib_data, uid->attrib_len ); + else + md_write (md, uid->name, uid->len ); } -/**************** - * Create a notation. It is assumed that the stings in STRLIST - * are already checked to contain only printable data and have a valid - * NAME=VALUE format. + +/* + * Helper to hash some parts from the signature */ static void -mk_notation_and_policy( PKT_signature *sig ) +hash_sigversion_to_magic (MD_HANDLE md, const PKT_signature *sig) { - const char *string, *s; - byte *buf; - unsigned n1, n2; - - /* notation data */ - if( opt.notation_data && sig->version < 4 ) - log_info("can't put notation data into v3 signatures\n"); - else if( opt.notation_data ) { - STRLIST nd = opt.notation_data; - - for( ; nd; nd = nd->next ) { - string = nd->d; - s = strchr( string, '=' ); - if( !s ) - BUG(); /* we have already parsed this */ - n1 = s - string; - s++; - n2 = strlen(s); - buf = gcry_xmalloc( 8 + n1 + n2 ); - buf[0] = 0x80; /* human readable */ - buf[1] = buf[2] = buf[3] = 0; - buf[4] = n1 >> 8; - buf[5] = n1; - buf[6] = n2 >> 8; - buf[7] = n2; - memcpy(buf+8, string, n1 ); - memcpy(buf+8+n1, s, n2 ); - build_sig_subpkt( sig, SIGSUBPKT_NOTATION - | ((nd->flags & 1)? SIGSUBPKT_FLAG_CRITICAL:0), - buf, 8+n1+n2 ); - } + 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 ); } - - /* set policy URL */ - if( (s=opt.set_policy_url) ) { - if( *s == '!' ) - build_sig_subpkt( sig, SIGSUBPKT_POLICY | SIGSUBPKT_FLAG_CRITICAL, - s+1, strlen(s+1) ); - else - build_sig_subpkt( sig, SIGSUBPKT_POLICY, s, strlen(s) ); + 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, - GCRY_MD_HD md, int digest_algo ) + MD_HANDLE md, int digest_algo ) { MPI frame; byte *dp; @@ -171,32 +259,59 @@ do_sign( PKT_secret_key *sk, PKT_signature *sig, : _("key has been created %lu seconds " "in future (time warp or clock problem)\n"), d ); if( !opt.ignore_time_conflict ) - return GPGERR_TIME_CONFLICT; + return G10ERR_TIME_CONFLICT; } print_pubkey_algo_note(sk->pubkey_algo); if( !digest_algo ) - digest_algo = gcry_md_get_algo(md); + digest_algo = md_get_algo(md); print_digest_algo_note( digest_algo ); - dp = gcry_md_read( md, 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]; frame = encode_md_value( sk->pubkey_algo, md, - digest_algo, gcry_mpi_get_nbits(sk->skey[0]), 0 ); - rc = pk_sign( sk->pubkey_algo, sig->data, frame, sk->skey ); - mpi_release(frame); + digest_algo, mpi_get_nbits(sk->skey[0]), 0 ); + 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 = m_alloc_clear (sizeof *pk); + + if( get_pubkey( pk, sig->keyid ) ) + rc = G10ERR_NO_PUBKEY; + else { + frame = encode_md_value (pk->pubkey_algo, md, + sig->digest_algo, + mpi_get_nbits(pk->pkey[0]), 0); + if (!frame) + rc = G10ERR_GENERAL; + else + rc = pubkey_verify (pk->pubkey_algo, frame, + sig->data, pk->pkey, + NULL, NULL ); + 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"), gpg_errstr(rc) ); + log_error(_("signing failed: %s\n"), g10_errstr(rc) ); else { if( opt.verbose ) { char *ustr = get_user_id_string( sig->keyid ); log_info(_("%s signature from: %s\n"), - gcry_pk_algo_name(sk->pubkey_algo), ustr ); - gcry_free(ustr); + pubkey_algo_to_string(sk->pubkey_algo), ustr ); + m_free(ustr); } } return rc; @@ -205,16 +320,12 @@ do_sign( PKT_secret_key *sk, PKT_signature *sig, int -complete_sig( PKT_signature *sig, PKT_secret_key *sk, GCRY_MD_HD md ) +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 ); - - /* fixme: should we check whether the signature is okay? - * maybe by using an option */ - return rc; } @@ -223,10 +334,12 @@ hash_for(int pubkey_algo, int packet_version ) { if( opt.def_digest_algo ) return opt.def_digest_algo; - if( pubkey_algo == GCRY_PK_DSA ) - return GCRY_MD_SHA1; - if( pubkey_algo == GCRY_PK_RSA && packet_version < 4 ) - return GCRY_MD_MD5; + if( recipient_digest_algo ) + return recipient_digest_algo; + if( pubkey_algo == PUBKEY_ALGO_DSA ) + return DIGEST_ALGO_SHA1; + if( pubkey_algo == PUBKEY_ALGO_RSA && packet_version < 4 ) + return DIGEST_ALGO_MD5; return DEFAULT_DIGEST_ALGO; } @@ -239,7 +352,7 @@ only_old_style( SK_LIST sk_list ) /* 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 == GCRY_PK_RSA && sk->version < 4 ) + if( sk->pubkey_algo == PUBKEY_ALGO_RSA && sk->version < 4 ) old_style = 1; else return 0; @@ -267,94 +380,210 @@ print_status_sig_created ( PKT_secret_key *sk, PKT_signature *sig, int what ) 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_one_signature( IOBUF out, PKT_secret_key *sk, int old_style, - const char *outfile, - GCRY_MD_HD datamd, - int sig_class, - int status_char ) +write_onepass_sig_packets (SK_LIST sk_list, IOBUF out, int sigclass ) { - PKT_signature *sig; - GCRY_MD_HD md; - int rc; + int skcount; + SK_LIST sk_rover; - /* build the signature packet */ - /* fixme: this code is partly duplicated in make_keysig_packet */ - sig = gcry_xcalloc( 1, sizeof *sig ); - sig->version = old_style || opt.force_v3_sigs ? 3 : sk->version; - keyid_from_sk( sk, sig->keyid ); - sig->digest_algo = hash_for(sk->pubkey_algo, sk->version); - sig->pubkey_algo = sk->pubkey_algo; - sig->timestamp = make_timestamp(); - sig->sig_class = sig_class; + for (skcount=0, sk_rover=sk_list; sk_rover; sk_rover = sk_rover->next) + skcount++; - md = gcry_md_copy( datamd ); - if( !md ) - BUG(); - if( sig->version >= 4 ) { - build_sig_subpkt_from_sig( sig ); - gcry_md_putc( md, sig->version ); - } - - mk_notation_and_policy( sig ); - - gcry_md_putc( md, sig->sig_class ); - if( sig->version < 4 ) { - u32 a = sig->timestamp; - gcry_md_putc( md, (a >> 24) & 0xff ); - gcry_md_putc( md, (a >> 16) & 0xff ); - gcry_md_putc( md, (a >> 8) & 0xff ); - gcry_md_putc( md, a & 0xff ); - } - else { - byte buf[6]; - size_t n; + for (; skcount; skcount--) { + PKT_secret_key *sk; + PKT_onepass_sig *ops; + PACKET pkt; + int i, rc; - gcry_md_putc( md, sig->pubkey_algo ); - gcry_md_putc( md, sig->digest_algo ); - if( sig->hashed_data ) { - n = (sig->hashed_data[0] << 8) | sig->hashed_data[1]; - gcry_md_write( md, sig->hashed_data, n+2 ); - n += 6; + for (i=0, sk_rover = sk_list; sk_rover; sk_rover = sk_rover->next ) { + if (++i == skcount) + break; } - else { - gcry_md_putc( md, 0 );/* always hash the length of the subpacket*/ - gcry_md_putc( md, 0 ); - n = 6; + + sk = sk_rover->sk; + ops = m_alloc_clear (sizeof *ops); + ops->sig_class = sigclass; + ops->digest_algo = hash_for (sk->pubkey_algo, sk->version); + 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; } - /* 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; - gcry_md_write( md, buf, 6 ); } - gcry_md_final( md ); - rc = do_sign( sk, sig, md, hash_for(sig->pubkey_algo, sk->version) ); - gcry_md_close( md ); - /* Hmmm: Do we release sig in case of rc != 0? */ - - if( !rc ) { /* and write it */ - PACKET pkt; + return 0; +} - 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_char ); +/* + * 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) { + if (fname || opt.set_filename) { + char *s = make_basename (opt.set_filename? opt.set_filename + : fname); + pt = m_alloc (sizeof *pt + strlen(s) - 1); + pt->namelen = strlen (s); + memcpy (pt->name, s, pt->namelen); + m_free (s); + } + else { /* no filename */ + pt = m_alloc (sizeof *pt - 1); + pt->namelen = 0; } - free_packet( &pkt ); - if( rc ) - log_error("build signature packet failed: %s\n", gpg_errstr(rc) ); } + /* try to calculate the length of the data */ + if (fname) { + if( !(filesize = iobuf_get_filelength(inp)) ) + log_info (_("WARNING: `%s' is an empty file\n"), fname); + + /* we can't yet encode the length of very large files, + * so we switch to partial length encoding in this case */ + if (filesize >= IOBUF_FILELENGTH_LIMIT) + 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 && !opt.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; + } + memset(copy_buffer, 0, 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 = m_alloc_clear (sizeof *sig); + if(opt.force_v3_sigs || opt.rfc1991) + sig->version=3; + else if(duration || opt.sig_policy_url || opt.sig_notation_data) + sig->version=4; + else + sig->version=sk->version; + keyid_from_sk (sk, sig->keyid); + sig->digest_algo = hash_for (sk->pubkey_algo, sk->version); + 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_and_policy (sig, NULL, sk); + + hash_sigversion_to_magic (md, sig); + md_final (md); + + rc = do_sign( sk, sig, md, hash_for (sig->pubkey_algo, sk->version) ); + 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. @@ -362,7 +591,7 @@ write_one_signature( IOBUF out, PKT_secret_key *sk, int old_style, * 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 ENCRYPT is true, use REMUSER (or ask if it is NULL) to encrypt the + * 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 @@ -370,7 +599,7 @@ write_one_signature( IOBUF out, PKT_secret_key *sk, int old_style, */ int sign_file( STRLIST filenames, int detached, STRLIST locusr, - int encrypt, STRLIST remusr, const char *outfile ) + int encryptflag, STRLIST remusr, const char *outfile ) { const char *fname; armor_filter_context_t afx; @@ -380,16 +609,12 @@ sign_file( STRLIST filenames, int detached, STRLIST locusr, encrypt_filter_context_t efx; IOBUF inp = NULL, out = NULL; PACKET pkt; - PKT_plaintext *pt = NULL; - u32 filesize; int rc = 0; PK_LIST pk_list = NULL; SK_LIST sk_list = NULL; SK_LIST sk_rover = NULL; int multifile = 0; - int old_style = opt.rfc1991; - int compr_algo = -1; /* unknown */ - + u32 timestamp=0,duration=0; memset( &afx, 0, sizeof afx); memset( &zfx, 0, sizeof zfx); @@ -405,20 +630,25 @@ sign_file( STRLIST filenames, int detached, STRLIST locusr, else fname = NULL; - if( fname && filenames->next && (!detached || encrypt) ) + if( fname && filenames->next && (!detached || encryptflag) ) log_bug("multiple files can only be detached signed"); - if( (rc=build_sk_list( locusr, &sk_list, 1, GCRY_PK_USAGE_SIGN )) ) + if(opt.ask_sig_expire && !opt.force_v3_sigs && !opt.batch && !opt.rfc1991) + duration=ask_expire_interval(1); + + if( (rc=build_sk_list( locusr, &sk_list, 1, PUBKEY_USAGE_SIG )) ) goto leave; - if( !old_style ) - old_style = only_old_style( sk_list ); - if( encrypt ) { - if( (rc=build_pk_list( remusr, &pk_list, GCRY_PK_USAGE_ENCR )) ) - goto leave; - if( !old_style ) - compr_algo = select_algo_from_prefs( pk_list, PREFTYPE_COMPR ); - } + if(opt.pgp2 && !only_old_style(sk_list)) + { + log_info(_("you can only detach-sign with PGP 2.x style keys " + "while in --pgp2 mode\n")); + log_info(_("this message may not be usable by PGP 2.x\n")); + opt.pgp2=0; + } + + if(encryptflag && (rc=build_pk_list( remusr, &pk_list, PUBKEY_USAGE_ENC ))) + goto leave; /* prepare iobufs */ if( multifile ) /* have list of filenames */ @@ -426,14 +656,14 @@ sign_file( STRLIST filenames, int detached, STRLIST locusr, else if( !(inp = iobuf_open(fname)) ) { log_error("can't open %s: %s\n", fname? fname: "[stdin]", strerror(errno) ); - rc = GPGERR_OPEN_FILE; + rc = G10ERR_OPEN_FILE; goto leave; } if( outfile ) { if( !(out = iobuf_create( outfile )) ) { log_error(_("can't create %s: %s\n"), outfile, strerror(errno) ); - rc = GPGERR_CREATE_FILE; + rc = G10ERR_CREATE_FILE; goto leave; } else if( opt.verbose ) @@ -445,92 +675,108 @@ sign_file( STRLIST filenames, int detached, STRLIST locusr, /* prepare to calculate the MD over the input */ if( opt.textmode && !outfile ) iobuf_push_filter( inp, text_filter, &tfx ); - if( !(mfx.md = gcry_md_open(0, 0))) - BUG(); + mfx.md = md_open(0, 0); + + /* If we're encrypting and signing, it is reasonable to pick the + hash algorithm to use out of the recepient key prefs. */ + 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(_("forcing digest algorithm %s (%d) " + "violates recipient preferences\n"), + digest_algo_to_string(opt.def_digest_algo), + opt.def_digest_algo); + } + else + { + int hashlen=0,algo; + + /* Of course, if the recipient asks for something + unreasonable (like a non-160-bit hash for DSA, for + example), then don't do it. Check all sk's - if any + are DSA, then the hash must be 160-bit. In the future + this can be more complex with different hashes for each + sk, but so long as there is only one signing algorithm + with hash restrictions, this is ok. -dms */ + + for( sk_rover = sk_list; sk_rover; sk_rover = sk_rover->next ) + if(sk_rover->sk->pubkey_algo==PUBKEY_ALGO_DSA) + hashlen=20; + + if((algo= + select_algo_from_prefs(pk_list,PREFTYPE_HASH,-1,&hashlen))>0) + recipient_digest_algo=algo; + } + } for( sk_rover = sk_list; sk_rover; sk_rover = sk_rover->next ) { PKT_secret_key *sk = sk_rover->sk; - gcry_md_enable(mfx.md, hash_for(sk->pubkey_algo, sk->version )); + md_enable(mfx.md, hash_for(sk->pubkey_algo, sk->version )); } if( !multifile ) iobuf_push_filter( inp, md_filter, &mfx ); - if( detached && !encrypt && !opt.rfc1991 ) + if( detached && !encryptflag && !opt.rfc1991 ) afx.what = 2; if( opt.armor && !outfile ) iobuf_push_filter( out, armor_filter, &afx ); - #ifdef ENABLE_COMMENT_PACKETS - else { - write_comment( out, "#created by GNUPG v" VERSION " (" - PRINTABLE_OS_NAME ")"); - if( opt.comment_string ) - write_comment( out, opt.comment_string ); - } - #endif - if( encrypt ) { + + if( encryptflag ) { efx.pk_list = pk_list; /* fixme: set efx.cfx.datalen if known */ iobuf_push_filter( out, encrypt_filter, &efx ); } - /* Select a compress algorithm */ - if( opt.compress && !outfile && ( !detached || opt.compress_sigs) ) { - if( !compr_algo ) - ; /* don't use compression */ - else { - if( old_style - || compr_algo == 1 - || (compr_algo == -1 && !encrypt) ) - zfx.algo = 1; /* use the non optional algorithm */ + if( opt.compress && !outfile && ( !detached || opt.compress_sigs) ) + { + int compr_algo=opt.def_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 && + select_algo_from_prefs(pk_list,PREFTYPE_ZIP, + compr_algo,NULL)!=compr_algo) + log_info(_("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 ) + { + zfx.algo = compr_algo; iobuf_push_filter( out, compress_filter, &zfx ); - } - } - - /* Build one-pass signature packets when needed */ - if( !detached && !old_style ) { - int skcount=0; - /* loop over the secret certificates and build headers - * The specs now say that the data should be bracket by - * the onepass-sig and signature-packet; so we must build it - * here in reverse order */ - for( sk_rover = sk_list; sk_rover; sk_rover = sk_rover->next ) - skcount++; - for( ; skcount; skcount-- ) { - PKT_secret_key *sk; - PKT_onepass_sig *ops; - int i = 0; - - for( sk_rover = sk_list; sk_rover; sk_rover = sk_rover->next ) - if( ++i == skcount ) - break; - - sk = sk_rover->sk; - ops = gcry_xcalloc( 1, sizeof *ops ); - ops->sig_class = opt.textmode && !outfile ? 0x01 : 0x00; - ops->digest_algo = hash_for(sk->pubkey_algo, sk->version); - 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", - gpg_errstr(rc)); - goto leave; - } - } + } + } + + /* Write the one-pass signature packets if needed */ + if (!detached && !opt.rfc1991) { + rc = write_onepass_sig_packets (sk_list, out, + opt.textmode && !outfile ? 0x01:0x00); + if (rc) + goto leave; } /* setup the inner packet */ if( detached ) { - /* this is pretty much the same for old and new PGP. So no - * need to cope with different packet ordering */ if( multifile ) { STRLIST sl; @@ -542,7 +788,7 @@ sign_file( STRLIST filenames, int detached, STRLIST locusr, if( !(inp = iobuf_open(sl->d)) ) { log_error(_("can't open %s: %s\n"), sl->d, strerror(errno) ); - rc = GPGERR_OPEN_FILE; + rc = G10ERR_OPEN_FILE; goto leave; } if( opt.verbose ) @@ -562,92 +808,35 @@ sign_file( STRLIST filenames, int detached, STRLIST locusr, } } else { - /* get the filename to be stored into the literal datapacket */ - if (!opt.no_literal) { - if( fname || opt.set_filename ) { - char *s = make_basename( opt.set_filename ? - opt.set_filename : fname ); - pt = gcry_xmalloc( sizeof *pt + strlen(s) - 1 ); - pt->namelen = strlen(s); - memcpy(pt->name, s, pt->namelen ); - gcry_free(s); - } - else { /* no filename */ - pt = gcry_xmalloc( sizeof *pt - 1 ); - pt->namelen = 0; - } - } - - if( fname ) { - if( !(filesize = iobuf_get_filelength(inp)) ) - log_info(_("WARNING: `%s' is an empty file\n"), fname ); - /* we can't yet encode the length of very large files, - * so we switch to partial length encoding in this case */ - if ( filesize >= IOBUF_FILELENGTH_LIMIT ) - 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( opt.textmode && !outfile ) - filesize = 0; - } - else - filesize = opt.set_filesize ? opt.set_filesize : 0; /* stdin */ - - if (!opt.no_literal) { - pt->timestamp = make_timestamp(); - pt->mode = opt.textmode && !outfile ? 't':'b'; - pt->len = filesize; - pt->new_ctb = !pt->len && !opt.rfc1991; - pt->buf = inp; - 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", - gpg_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 = GPGERR_WRITE_FILE; - log_error("copying input to output failed: %s\n", - gpg_errstr(rc)); - break; - } - memset(copy_buffer, 0, 4096); /* burn buffer */ - } + rc = write_plaintext_packet (out, inp, fname, + opt.textmode && !outfile ? 't':'b'); } /* catch errors from above */ if (rc) goto leave; - /* write all the signature packets */ - for( sk_rover = sk_list; sk_rover && !rc ; sk_rover = sk_rover->next ) { - rc = write_one_signature( out, sk_rover->sk, - old_style, outfile, mfx.md, + /* write the signatures */ + rc = write_signature_packets (sk_list, out, mfx.md, opt.textmode && !outfile? 0x01 : 0x00, - detached ? 'D':'S' ); - } + timestamp, duration, detached ? 'D':'S'); + if( rc ) + goto leave; leave: if( rc ) iobuf_cancel(out); - else + else { iobuf_close(out); + if (encryptflag) + write_status( STATUS_END_ENCRYPTION ); + } iobuf_close(inp); - gcry_md_close( mfx.md ); + md_close( mfx.md ); release_sk_list( sk_list ); release_pk_list( pk_list ); - /* FIXME: Did we release the efx.cfx.dek ? */ + recipient_digest_algo=0; return rc; } @@ -660,7 +849,7 @@ int clearsign_file( const char *fname, STRLIST locusr, const char *outfile ) { armor_filter_context_t afx; - GCRY_MD_HD textmd = NULL; + MD_HANDLE textmd = NULL; IOBUF inp = NULL, out = NULL; PACKET pkt; int rc = 0; @@ -668,27 +857,40 @@ clearsign_file( const char *fname, STRLIST locusr, const char *outfile ) SK_LIST sk_rover = NULL; int old_style = opt.rfc1991; int only_md5 = 0; + u32 timestamp=0,duration=0; memset( &afx, 0, sizeof afx); init_packet( &pkt ); - if( (rc=build_sk_list( locusr, &sk_list, 1, GCRY_PK_USAGE_SIGN )) ) + if(opt.ask_sig_expire && !opt.force_v3_sigs && !opt.batch && !opt.rfc1991) + duration=ask_expire_interval(1); + + if( (rc=build_sk_list( locusr, &sk_list, 1, PUBKEY_USAGE_SIG )) ) goto leave; - if( !old_style ) + + if( !old_style && !duration ) old_style = only_old_style( sk_list ); + if(!old_style && opt.pgp2) + { + log_info(_("you can only clearsign with PGP 2.x style keys " + "while in --pgp2 mode\n")); + log_info(_("this message may not be usable by PGP 2.x\n")); + opt.pgp2=0; + } + /* prepare iobufs */ if( !(inp = iobuf_open(fname)) ) { log_error("can't open %s: %s\n", fname? fname: "[stdin]", strerror(errno) ); - rc = GPGERR_OPEN_FILE; + rc = G10ERR_OPEN_FILE; goto leave; } if( outfile ) { if( !(out = iobuf_create( outfile )) ) { log_error(_("can't create %s: %s\n"), outfile, strerror(errno) ); - rc = GPGERR_CREATE_FILE; + rc = G10ERR_CREATE_FILE; goto leave; } else if( opt.verbose ) @@ -701,7 +903,7 @@ clearsign_file( const char *fname, STRLIST locusr, const char *outfile ) for( sk_rover = sk_list; sk_rover; sk_rover = sk_rover->next ) { PKT_secret_key *sk = sk_rover->sk; - if( hash_for(sk->pubkey_algo, sk->version) == GCRY_MD_MD5 ) + if( hash_for(sk->pubkey_algo, sk->version) == DIGEST_ALGO_MD5 ) only_md5 = 1; else { only_md5 = 0; @@ -709,9 +911,8 @@ clearsign_file( const char *fname, STRLIST locusr, const char *outfile ) } } - if( old_style && only_md5 ) - iobuf_writestr(out, "\n" ); - else { + if( !(old_style && only_md5) ) { + const char *s; int any = 0; byte hashs_seen[256]; @@ -722,33 +923,32 @@ clearsign_file( const char *fname, STRLIST locusr, const char *outfile ) int i = hash_for(sk->pubkey_algo, sk->version); if( !hashs_seen[ i & 0xff ] ) { - if( !openpgp_md_test_algo( i ) ) { + s = digest_algo_to_string( i ); + if( s ) { hashs_seen[ i & 0xff ] = 1; if( any ) iobuf_put(out, ',' ); - iobuf_writestr(out, gcry_md_algo_name( i ) ); + iobuf_writestr(out, s ); any = 1; } } } assert(any); - iobuf_writestr(out, "\n" ); - if( opt.not_dash_escaped ) - iobuf_writestr( out, - "NotDashEscaped: You need GnuPG to verify this message\n" ); - iobuf_writestr(out, "\n" ); + 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 = gcry_md_open(0, 0); - if( !textmd ) - BUG(); + textmd = md_open(0, 0); for( sk_rover = sk_list; sk_rover; sk_rover = sk_rover->next ) { PKT_secret_key *sk = sk_rover->sk; - gcry_md_enable(textmd, hash_for(sk->pubkey_algo, sk->version)); + md_enable(textmd, hash_for(sk->pubkey_algo, sk->version)); } if ( DBG_HASHING ) - gcry_md_start_debug( textmd, "clearsign" ); + md_start_debug( textmd, "clearsign" ); copy_clearsig_text( out, inp, textmd, !opt.not_dash_escaped, opt.escape_from, old_style ); /* fixme: check for read errors */ @@ -757,13 +957,11 @@ clearsign_file( const char *fname, STRLIST locusr, const char *outfile ) afx.what = 2; iobuf_push_filter( out, armor_filter, &afx ); - /* write all the signature packets */ - for( sk_rover = sk_list; sk_rover && !rc ; sk_rover = sk_rover->next ) { - rc = write_one_signature( out, sk_rover->sk, - old_style, outfile, textmd, - 0x01, - 'C' ); - } + /* write the signatures */ + rc = write_signature_packets (sk_list, out, textmd, 0x01, + timestamp, duration, 'C'); + if( rc ) + goto leave; leave: if( rc ) @@ -771,68 +969,249 @@ clearsign_file( const char *fname, STRLIST locusr, const char *outfile ) else iobuf_close(out); iobuf_close(inp); - gcry_md_close( textmd ); + 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; + 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 timestamp=0,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.ask_sig_expire && !opt.force_v3_sigs && !opt.batch && !opt.rfc1991) + duration=ask_expire_interval(1); + + rc = build_sk_list (locusr, &sk_list, 1, PUBKEY_USAGE_SIG); + if (rc) + goto leave; + + /* prepare iobufs */ + inp = iobuf_open(fname); + if( !inp ) { + log_error("can't open %s: %s\n", fname? fname: "[stdin]", + strerror(errno) ); + rc = G10ERR_OPEN_FILE; + goto leave; + } + + /* prepare key */ + s2k = m_alloc_clear( sizeof *s2k ); + s2k->mode = opt.rfc1991? 0:opt.s2k_mode; + s2k->hash_algo = opt.def_digest_algo ? opt.def_digest_algo + : opt.s2k_digest_algo; + + algo = opt.def_cipher_algo ? opt.def_cipher_algo : opt.s2k_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 ); + + if (!cfx.dek || !cfx.dek->keylen) { + rc = G10ERR_PASSPHRASE; + log_error(_("error creating passphrase: %s\n"), g10_errstr(rc) ); + goto leave; + } + + /* 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); + + 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->pubkey_algo, sk->version )); + } + + 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 (!opt.rfc1991) { + PKT_symkey_enc *enc = m_alloc_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) ); + m_free(enc); + } + + /* Push the encryption filter */ + iobuf_push_filter( out, cipher_filter, &cfx ); + + /* Push the Zip filter */ + if (opt.compress) + { + int compr_algo=opt.def_compress_algo; + + /* Default */ + if(compr_algo==-1) + compr_algo=DEFAULT_COMPRESS_ALGO; + + if (compr_algo) + { + zfx.algo = compr_algo; + iobuf_push_filter( out, compress_filter, &zfx ); + } + } + + /* Write the one-pass signature packets */ + /*(current filters: zip - encrypt - armor)*/ + if (!opt.rfc1991) { + rc = write_onepass_sig_packets (sk_list, out, + opt.textmode? 0x01:0x00); + if (rc) + goto leave; + } + + /* 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, + timestamp, 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 ); + m_free(cfx.dek); + m_free(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. - */ + * 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; - GCRY_MD_HD md; + MD_HANDLE md; - assert( (sigclass >= 0x10 && sigclass <= 0x13) + assert( (sigclass >= 0x10 && sigclass <= 0x13) || sigclass == 0x1F || sigclass == 0x20 || sigclass == 0x18 || sigclass == 0x30 || sigclass == 0x28 ); - if( !digest_algo ) { - switch( sk->pubkey_algo ) { - case GCRY_PK_DSA: digest_algo = GCRY_MD_SHA1; break; - case GCRY_PK_RSA_S: - case GCRY_PK_RSA: digest_algo = GCRY_MD_MD5; break; - default: digest_algo = GCRY_MD_RMD160; break; - } - } - if( !(md = gcry_md_open( digest_algo, 0 ))) - BUG(); + + 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. */ + 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). They still must use a + 160-bit hash with 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 || + sk->pubkey_algo==PUBKEY_ALGO_RSA_S) && + pk->version<4 && sigversion < 4) + digest_algo = DIGEST_ALGO_MD5; + else + digest_algo = DIGEST_ALGO_SHA1; + } + + md = md_open( digest_algo, 0 ); /* hash the public key certificate and the user id */ hash_public_key( md, pk ); if( sigclass == 0x18 || sigclass == 0x28 ) { /* subkey binding/revocation*/ hash_public_key( md, subpk ); } - else if( sigclass != 0x20 ) { - if( sk->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; - gcry_md_write( md, buf, 5 ); - } - gcry_md_write( md, uid->name, uid->len ); + else if( sigclass != 0x1F && sigclass != 0x20 ) { + hash_uid (md, sigversion, uid); } /* and make the signature packet */ - sig = gcry_xcalloc( 1, sizeof *sig ); - sig->version = sk->version; + sig = m_alloc_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; - sig->timestamp = make_timestamp(); + 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 ); @@ -841,49 +1220,14 @@ make_keysig_packet( PKT_signature **ret_sig, PKT_public_key *pk, rc = (*mksubpkt)( sig, opaque ); if( !rc ) { - mk_notation_and_policy( sig ); - if( sig->version >= 4 ) - gcry_md_putc( md, sig->version ); - gcry_md_putc( md, sig->sig_class ); - if( sig->version < 4 ) { - u32 a = sig->timestamp; - gcry_md_putc( md, (a >> 24) & 0xff ); - gcry_md_putc( md, (a >> 16) & 0xff ); - gcry_md_putc( md, (a >> 8) & 0xff ); - gcry_md_putc( md, a & 0xff ); - } - else { - byte buf[6]; - size_t n; - - gcry_md_putc( md, sig->pubkey_algo ); - gcry_md_putc( md, sig->digest_algo ); - if( sig->hashed_data ) { - n = (sig->hashed_data[0] << 8) | sig->hashed_data[1]; - gcry_md_write( md, sig->hashed_data, n+2 ); - n += 6; - } - else { - gcry_md_putc( md, 0 ); /* always hash the length of the subpacket*/ - gcry_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; - gcry_md_write( md, buf, 6 ); - - } - gcry_md_final(md); + mk_notation_and_policy( sig, pk, sk ); + hash_sigversion_to_magic (md, sig); + md_final(md); rc = complete_sig( sig, sk, md ); } - gcry_md_close( md ); + md_close( md ); if( rc ) free_seckey_enc( sig ); else @@ -892,3 +1236,63 @@ make_keysig_packet( PKT_signature **ret_sig, PKT_public_key *pk, } + +/**************** + * 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_secret_key *sk, + int (*mksubpkt)(PKT_signature *, void *), + void *opaque + ) +{ + PKT_signature *sig; + int rc=0; + MD_HANDLE md; + + if (!orig_sig || !pk || !uid || !sk) + return G10ERR_GENERAL; + if (orig_sig->sig_class < 0x10 || orig_sig->sig_class > 0x13 ) + 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 ); + hash_uid (md, orig_sig->version, uid); + + /* create a new signature packet */ + sig = copy_signature (NULL, orig_sig); + if ( sig->version >= 4 && mksubpkt) + rc = (*mksubpkt)(sig, opaque); + + /* we increase the timestamp by one second so that a future import + of this key will replace the existing one. We also make sure that + we don't create a timestamp in the future */ + sig->timestamp++; + while (sig->timestamp >= make_timestamp()) + sleep (1); + /* put the updated timestamp back into the data */ + if( sig->version >= 4 ) + build_sig_subpkt_from_sig( sig ); + + 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 index f61b0a8f8..0517ba648 100644 --- a/g10/signal.c +++ b/g10/signal.c @@ -1,5 +1,5 @@ /* signal.c - signal handling - * Copyright (C) 1998, 1999, 2000 Free Software Foundation, Inc. + * Copyright (C) 1998, 1999, 2000, 2001 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -27,9 +27,9 @@ #include <errno.h> #include <assert.h> -#include <gcrypt.h> #include "options.h" #include "errors.h" +#include "memory.h" #include "util.h" #include "main.h" #include "ttyio.h" @@ -38,6 +38,36 @@ 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 ) +{ + #ifndef HAVE_DOSISH_SYSTEM + #ifdef HAVE_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 + #endif /*!HAVE_DOSISH_SYSTEM*/ +} + static const char * get_signal_name( int signum ) { @@ -58,23 +88,20 @@ got_fatal_signal( int sig ) raise( sig ); caught_fatal_sig = 1; - gcry_control( GCRYCTL_TERM_SECMEM ); + secmem_term(); /* better don't transtale these messages */ write(2, "\n", 1 ); s = log_get_name(); if( s ) write(2, s, strlen(s) ); write(2, ": ", 2 ); s = get_signal_name(sig); write(2, s, strlen(s) ); - write(2, " caught ... exiting\n", 21 ); - - #ifndef HAVE_DOSISH_SYSTEM - { /* reset action to default action and raise signal again */ - struct sigaction nact; - nact.sa_handler = SIG_DFL; - sigemptyset( &nact.sa_mask ); - nact.sa_flags = 0; - sigaction( sig, &nact, NULL); - } - #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__ + close_fds (); +#endif /* __riscos__ */ raise( sig ); } @@ -85,37 +112,18 @@ got_usr_signal( int sig ) caught_sigusr1 = 1; } -#ifndef HAVE_DOSISH_SYSTEM -static void -do_sigaction( int sig, struct sigaction *nact ) -{ - struct sigaction oact; - - sigaction( sig, NULL, &oact ); - if( oact.sa_handler != SIG_IGN ) - sigaction( sig, nact, NULL); -} -#endif void init_signals() { #ifndef HAVE_DOSISH_SYSTEM - struct sigaction nact; - - nact.sa_handler = got_fatal_signal; - sigemptyset( &nact.sa_mask ); - nact.sa_flags = 0; - - do_sigaction( SIGINT, &nact ); - do_sigaction( SIGHUP, &nact ); - do_sigaction( SIGTERM, &nact ); - do_sigaction( SIGQUIT, &nact ); - do_sigaction( SIGSEGV, &nact ); - nact.sa_handler = got_usr_signal; - sigaction( SIGUSR1, &nact, NULL ); - nact.sa_handler = SIG_IGN; - sigaction( SIGPIPE, &nact, NULL ); + 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 ); #endif } @@ -124,6 +132,7 @@ void pause_on_sigusr( int which ) { #ifndef HAVE_DOSISH_SYSTEM + #ifdef HAVE_SIGPROCMASK sigset_t mask, oldmask; assert( which == 1 ); @@ -135,6 +144,14 @@ pause_on_sigusr( int which ) 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*/ #endif } @@ -142,12 +159,13 @@ pause_on_sigusr( int which ) static void do_block( int block ) { - #ifndef HAVE_DOSISH_SYSTEM + #ifndef HAVE_DOSISH_SYSTEM static int is_blocked; + #ifdef HAVE_SIGPROCMASK static sigset_t oldmask; if( block ) { - sigset_t newmask; + sigset_t newmask; if( is_blocked ) log_bug("signals are already blocked\n"); @@ -161,7 +179,28 @@ do_block( int block ) sigprocmask( SIG_SETMASK, &oldmask, NULL ); is_blocked = 0; } - #endif /*HAVE_DOSISH_SYSTEM*/ + #else /*!HAVE_SIGPROCMASK*/ + static void (*disposition[MAXSIG])(); + int sig; + + if( block ) { + if( is_blocked ) + log_bug("signals are already blocked\n"); + for (sig=1; sig < MAXSIG; 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 < MAXSIG; sig++) { + sigset (sig, disposition[sig]); + } + is_blocked = 0; + } + #endif /*!HAVE_SIGPROCMASK*/ + #endif /*HAVE_DOSISH_SYSTEM*/ } @@ -176,4 +215,3 @@ unblock_all_signals() { do_block(0); } - diff --git a/g10/skclist.c b/g10/skclist.c index bc325cd58..1f7a3919a 100644 --- a/g10/skclist.c +++ b/g10/skclist.c @@ -1,5 +1,5 @@ /* skclist.c - * Copyright (C) 1998, 1999, 2000 Free Software Foundation, Inc. + * Copyright (C) 1998, 1999, 2000, 2001 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -25,14 +25,14 @@ #include <errno.h> #include <assert.h> -#include <gcrypt.h> #include "options.h" #include "packet.h" #include "errors.h" #include "keydb.h" +#include "memory.h" #include "util.h" #include "i18n.h" -#include "main.h" +#include "cipher.h" void @@ -43,14 +43,66 @@ release_sk_list( SK_LIST sk_list ) for( ; sk_list; sk_list = sk_rover ) { sk_rover = sk_list->next; free_secret_key( sk_list->sk ); - gcry_free( sk_list ); + m_free( 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" ) ) { + 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 ) +build_sk_list( STRLIST locusr, SK_LIST *ret_sk_list, + int unlock, unsigned int use ) { SK_LIST sk_list = NULL; int rc; @@ -58,24 +110,28 @@ build_sk_list( STRLIST locusr, SK_LIST *ret_sk_list, int unlock, if( !locusr ) { /* use the default one */ PKT_secret_key *sk; - sk = gcry_xcalloc( 1, sizeof *sk ); + sk = m_alloc_clear( sizeof *sk ); sk->req_usage = use; - if( (rc = get_seckey_byname( NULL, sk, NULL, unlock, NULL )) ) { + if( (rc = get_seckey_byname( sk, NULL, unlock )) ) { free_secret_key( sk ); sk = NULL; - log_error("no default secret key: %s\n", gpg_errstr(rc) ); + log_error("no default secret key: %s\n", g10_errstr(rc) ); } - else if( !(rc=openpgp_pk_test_algo(sk->pubkey_algo, - sk->pubkey_usage)) ) { + else if( !(rc=check_pubkey_algo2(sk->pubkey_algo, use)) ) { SK_LIST r; - - if( sk->version == 4 && (sk->pubkey_usage & GCRY_PK_USAGE_SIGN ) - && sk->pubkey_algo == GCRY_PK_ELG_E ) { + + if( sk->version == 4 && (use & PUBKEY_USAGE_SIG) + && sk->pubkey_algo == PUBKEY_ALGO_ELGAMAL_E ) { log_info("this is a PGP generated " "ElGamal key which is NOT secure for signatures!\n"); 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 = gcry_xmalloc( sizeof *r ); + r = m_alloc( sizeof *r ); r->sk = sk; sk = NULL; r->next = sk_list; r->mark = 0; @@ -84,31 +140,54 @@ build_sk_list( STRLIST locusr, SK_LIST *ret_sk_list, int unlock, } else { free_secret_key( sk ); sk = NULL; - log_error("invalid default secret key: %s\n", gpg_errstr(rc) ); + 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; - - sk = gcry_xcalloc( 1, sizeof *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 = m_alloc_clear( sizeof *sk ); sk->req_usage = use; - if( (rc = get_seckey_byname( NULL, sk, locusr->d, unlock, NULL))) { + if( (rc = get_seckey_byname( sk, locusr->d, 0 )) ) { free_secret_key( sk ); sk = NULL; - log_error(_("skipped `%s': %s\n"), locusr->d, gpg_errstr(rc) ); + log_error(_("skipped `%s': %s\n"), locusr->d, g10_errstr(rc) ); } - else if( !(rc=openpgp_pk_test_algo(sk->pubkey_algo, - sk->pubkey_usage)) ) { + 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 && (sk->pubkey_usage & GCRY_PK_USAGE_SIGN) - && sk->pubkey_algo == GCRY_PK_ELG_E ) { + + if( sk->version == 4 && (use & PUBKEY_USAGE_SIG) + && sk->pubkey_algo == PUBKEY_ALGO_ELGAMAL_E ) { log_info(_("skipped `%s': this is a PGP generated " "ElGamal key which is not secure for signatures!\n"), locusr->d ); 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 = gcry_xmalloc( sizeof *r ); + r = m_alloc( sizeof *r ); r->sk = sk; sk = NULL; r->next = sk_list; r->mark = 0; @@ -117,7 +196,7 @@ build_sk_list( STRLIST locusr, SK_LIST *ret_sk_list, int unlock, } else { free_secret_key( sk ); sk = NULL; - log_error("skipped `%s': %s\n", locusr->d, gpg_errstr(rc) ); + log_error("skipped `%s': %s\n", locusr->d, g10_errstr(rc) ); } } } @@ -125,7 +204,7 @@ build_sk_list( STRLIST locusr, SK_LIST *ret_sk_list, int unlock, if( !rc && !sk_list ) { log_error("no valid signators\n"); - rc = GPGERR_NO_USER_ID; + rc = G10ERR_NO_USER_ID; } if( rc ) diff --git a/g10/status.c b/g10/status.c index d336ae3b0..e0b126b78 100644 --- a/g10/status.c +++ b/g10/status.c @@ -1,5 +1,5 @@ /* status.c - * Copyright (C) 1998, 1999, 2000 Free Software Foundation, Inc. + * Copyright (C) 1998, 1999, 2000, 2001, 2002 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -24,11 +24,13 @@ #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 @@ -38,16 +40,20 @@ #include <sys/mman.h> #endif #endif - -#include <gcrypt.h> #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; -static int fd = -1; #ifdef USE_SHM_COPROCESSING static int shm_id = -1; static volatile char *shm_area; @@ -68,26 +74,118 @@ progress_cb ( void *ctx, int 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_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_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_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_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_ATTRIBUTE : s = "ATTRIBUTE"; break; + default: s = "?"; break; + } + return s; +} void -set_status_fd ( int newfd ) +set_status_fd ( int fd ) { - fd = newfd; - if ( fd != -1 ) { - #warning fixme - progress functions - /* Has to be fixed in libgcrypt */ - #if 0 - register_primegen_progress ( progress_cb, "primegen" ); - register_pk_dsa_progress ( progress_cb, "pk_dsa" ); - register_pk_elg_progress ( progress_cb, "pk_elg" ); - #endif + 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 fd != -1; + return !!statusfp; } void @@ -99,77 +197,97 @@ write_status ( int no ) void write_status_text ( int no, const char *text) { - const char *s; - - if( fd == -1 ) + if( !statusfp ) return; /* not enabled */ - switch( no ) { - case STATUS_ENTER : s = "ENTER\n"; break; - case STATUS_LEAVE : s = "LEAVE\n"; break; - case STATUS_ABORT : s = "ABORT\n"; break; - case STATUS_GOODSIG: s = "GOODSIG\n"; break; - case STATUS_SIGEXPIRED: s = "SIGEXPIRED\n"; break; - case STATUS_KEYREVOKED: s = "KEYREVOKED\n"; break; - case STATUS_BADSIG : s = "BADSIG\n"; break; - case STATUS_ERRSIG : s = "ERRSIG\n"; break; - case STATUS_BADARMOR : s = "BADARMOR\n"; break; - case STATUS_RSA_OR_IDEA : s= "RSA_OR_IDEA\n"; break; - case STATUS_TRUST_UNDEFINED: s = "TRUST_UNDEFINED\n"; break; - case STATUS_TRUST_NEVER : s = "TRUST_NEVER\n"; break; - case STATUS_TRUST_MARGINAL : s = "TRUST_MARGINAL\n"; break; - case STATUS_TRUST_FULLY : s = "TRUST_FULLY\n"; break; - case STATUS_TRUST_ULTIMATE : s = "TRUST_ULTIMATE\n"; break; - case STATUS_GET_BOOL : s = "GET_BOOL\n"; break; - case STATUS_GET_LINE : s = "GET_LINE\n"; break; - case STATUS_GET_HIDDEN : s = "GET_HIDDEN\n"; break; - case STATUS_GOT_IT : s = "GOT_IT\n"; break; - case STATUS_SHM_INFO : s = "SHM_INFO\n"; break; - case STATUS_SHM_GET : s = "SHM_GET\n"; break; - case STATUS_SHM_GET_BOOL : s = "SHM_GET_BOOL\n"; break; - case STATUS_SHM_GET_HIDDEN : s = "SHM_GET_HIDDEN\n"; break; - case STATUS_NEED_PASSPHRASE: s = "NEED_PASSPHRASE\n"; break; - case STATUS_VALIDSIG : s = "VALIDSIG\n"; break; - case STATUS_SIG_ID : s = "SIG_ID\n"; break; - case STATUS_ENC_TO : s = "ENC_TO\n"; break; - case STATUS_NODATA : s = "NODATA\n"; break; - case STATUS_BAD_PASSPHRASE : s = "BAD_PASSPHRASE\n"; break; - case STATUS_NO_PUBKEY : s = "NO_PUBKEY\n"; break; - case STATUS_NO_SECKEY : s = "NO_SECKEY\n"; break; - case STATUS_NEED_PASSPHRASE_SYM: s = "NEED_PASSPHRASE_SYM\n"; break; - case STATUS_DECRYPTION_FAILED: s = "DECRYPTION_FAILED\n"; break; - case STATUS_DECRYPTION_OKAY: s = "DECRYPTION_OKAY\n"; break; - case STATUS_MISSING_PASSPHRASE: s = "MISSING_PASSPHRASE\n"; break; - case STATUS_GOOD_PASSPHRASE : s = "GOOD_PASSPHRASE\n"; break; - case STATUS_GOODMDC : s = "GOODMDC\n"; break; - case STATUS_BADMDC : s = "BADMDC\n"; break; - case STATUS_ERRMDC : s = "ERRMDC\n"; break; - case STATUS_IMPORTED : s = "IMPORTED\n"; break; - case STATUS_IMPORT_RES : s = "IMPORT_RES\n"; break; - case STATUS_FILE_START : s = "FILE_START\n"; break; - case STATUS_FILE_DONE : s = "FILE_DONE\n"; break; - case STATUS_FILE_ERROR : s = "FILE_ERROR\n"; break; - case STATUS_BEGIN_DECRYPTION:s = "BEGIN_DECRYPTION\n"; break; - case STATUS_END_DECRYPTION : s = "END_DECRYPTION\n"; break; - case STATUS_BEGIN_ENCRYPTION:s = "BEGIN_ENCRYPTION\n"; break; - case STATUS_END_ENCRYPTION : s = "END_ENCRYPTION\n"; break; - case STATUS_DELETE_PROBLEM : s = "DELETE_PROBLEM\n"; break; - case STATUS_PROGRESS : s = "PROGRESS\n"; break; - case STATUS_SIG_CREATED : s = "SIG_CREATED\n"; break; - default: s = "?\n"; break; + 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); + fflush (statusfp); +} - write( fd, "[GNUPG:] ", 9 ); - if( text ) { - write( fd, s, strlen(s)-1 ); - write( fd, " ", 1 ); - write( fd, text, strlen(text) ); - write( fd, "\n", 1 ); + +/* + * 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 ) + return; /* not enabled */ + + if (wrap == -1) { + lower_limit--; + wrap = 0; } - else - write( fd, s, strlen(s) ); + + 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); + fflush (statusfp); } +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 @@ -333,7 +451,7 @@ do_shm_get( const char *keyword, int hidden, int bool ) if( bool ) return p[0]? "" : NULL; - string = hidden? gcry_xmalloc_secure( n+1 ) : gcry_xmalloc( n+1 ); + string = hidden? m_alloc_secure( n+1 ) : m_alloc( n+1 ); memcpy(string, p, n ); string[n] = 0; /* make sure it is a string */ if( hidden ) /* invalidate the memory */ @@ -344,6 +462,32 @@ do_shm_get( const char *keyword, int hidden, int bool ) #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 @@ -362,15 +506,21 @@ do_get_from_fd( const char *keyword, int hidden, int bool ) if( i >= len-1 ) { char *save = string; len += 100; - string = hidden? gcry_xmalloc_secure ( len ) : gcry_malloc ( len ); + string = hidden? m_alloc_secure ( len ) : m_alloc ( len ); if( save ) memcpy(string, save, i ); else i=0; } /* Hmmm: why not use our read_line function here */ - if( read( opt.command_fd, string+i, 1) != 1 || string[i] == '\n' ) - break; + 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; @@ -397,6 +547,23 @@ cpr_enabled() } 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; @@ -410,7 +577,7 @@ cpr_get( const char *keyword, const char *prompt ) for(;;) { p = tty_get( prompt ); if( *p=='?' && !p[1] && !(keyword && !*keyword)) { - gcry_free(p); + m_free(p); display_online_help( keyword ); } else @@ -418,6 +585,7 @@ cpr_get( const char *keyword, const char *prompt ) } } + char * cpr_get_utf8( const char *keyword, const char *prompt ) { @@ -425,7 +593,7 @@ cpr_get_utf8( const char *keyword, const char *prompt ) p = cpr_get( keyword, prompt ); if( p ) { char *utf8 = native_to_utf8( p ); - gcry_free( p ); + m_free( p ); p = utf8; } return p; @@ -445,7 +613,7 @@ cpr_get_hidden( const char *keyword, const char *prompt ) for(;;) { p = tty_get_hidden( prompt ); if( *p == '?' && !p[1] ) { - gcry_free(p); + m_free(p); display_online_help( keyword ); } else @@ -482,13 +650,13 @@ cpr_get_answer_is_yes( const char *keyword, const char *prompt ) p = tty_get( prompt ); trim_spaces(p); /* it is okay to do this here */ if( *p == '?' && !p[1] ) { - gcry_free(p); + m_free(p); display_online_help( keyword ); } else { tty_kill_prompt(); yes = answer_is_yes(p); - gcry_free(p); + m_free(p); return yes; } } @@ -510,13 +678,13 @@ cpr_get_answer_yes_no_quit( const char *keyword, const char *prompt ) p = tty_get( prompt ); trim_spaces(p); /* it is okay to do this here */ if( *p == '?' && !p[1] ) { - gcry_free(p); + m_free(p); display_online_help( keyword ); } else { tty_kill_prompt(); yes = answer_is_yes_no_quit(p); - gcry_free(p); + m_free(p); return yes; } } diff --git a/g10/status.h b/g10/status.h index f9cce5b6b..86e232719 100644 --- a/g10/status.h +++ b/g10/status.h @@ -1,5 +1,5 @@ /* status.h - * Copyright (C) 1998, 1999, 2000 Free Software Foundation, Inc. + * Copyright (C) 1998, 1999, 2000, 2001 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -17,8 +17,8 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA */ -#ifndef GPG_STATUS_H -#define GPG_STATUS_H +#ifndef G10_STATUS_H +#define G10_STATUS_H #define STATUS_ENTER 1 @@ -33,7 +33,7 @@ #define STATUS_BADARMOR 7 #define STATUS_RSA_OR_IDEA 8 -#define STATUS_SIGEXPIRED 9 +#define STATUS_KEYEXPIRED 9 #define STATUS_KEYREVOKED 10 #define STATUS_TRUST_UNDEFINED 11 @@ -81,12 +81,32 @@ #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 /*-- 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 ); @@ -94,6 +114,7 @@ void write_status_text ( int no, const char *text ); 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); @@ -101,4 +122,4 @@ 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 ); -#endif /*GPG_STATUS_H*/ +#endif /*G10_STATUS_H*/ diff --git a/g10/tdbdump.c b/g10/tdbdump.c index 6729d4e56..cd46f1f5a 100644 --- a/g10/tdbdump.c +++ b/g10/tdbdump.c @@ -1,5 +1,5 @@ /* tdbdump.c - * Copyright (C) 1999, 2000 Free Software Foundation, Inc. + * Copyright (C) 1998, 1999, 2000, 2001 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -33,7 +33,7 @@ #include "errors.h" #include "iobuf.h" #include "keydb.h" -#include <gcrypt.h> +#include "memory.h" #include "util.h" #include "trustdb.h" #include "options.h" @@ -46,22 +46,7 @@ #define HEXTOBIN(x) ( (x) >= '0' && (x) <= '9' ? ((x)-'0') : \ (x) >= 'A' && (x) <= 'F' ? ((x)-'A'+10) : ((x)-'a'+10)) -/**************** - * Read a record but die if it does not exist - * fixme: duplicate: remove it - */ -#if 0 -static void -read_record( ulong recno, TRUSTREC *rec, int rectype ) -{ - int rc = tdbio_read_record( recno, rec, rectype ); - if( !rc ) - return; - log_error(_("trust record %lu, req type %d: read failed: %s\n"), - recno, rectype, gpg_errstr(rc) ); - tdbio_invalid(); -} -#endif + /**************** * Wirte a record but die on error */ @@ -72,263 +57,13 @@ write_record( TRUSTREC *rec ) if( !rc ) return; log_error(_("trust record %lu, type %d: write failed: %s\n"), - rec->recnum, rec->rectype, gpg_errstr(rc) ); + rec->recnum, rec->rectype, g10_errstr(rc) ); tdbio_invalid(); } /**************** - * sync the db - */ -static void -do_sync(void) -{ - int rc = tdbio_sync(); - if( !rc ) - return; - log_error(_("trustdb: sync failed: %s\n"), gpg_errstr(rc) ); - gpg_exit(2); -} - -#if 0 -static int -print_sigflags( FILE *fp, unsigned flags ) -{ - if( flags & SIGF_CHECKED ) { - fprintf(fp,"%c%c%c", - (flags & SIGF_VALID) ? 'V':'-', - (flags & SIGF_EXPIRED) ? 'E':'-', - (flags & SIGF_REVOKED) ? 'R':'-'); - } - else if( flags & SIGF_NOPUBKEY) - fputs("?--", fp); - else - fputs("---", fp); - return 3; -} -#endif - - -/**************** - * Walk through the signatures of a public key. - * The caller must provide a context structure, with all fields set - * to zero, but the local_id field set to the requested key; - * This function does not change this field. On return the context - * is filled with the local-id of the signature and the signature flag. - * No fields should be changed (clearing all fields and setting - * pubkeyid is okay to continue with an other pubkey) - * Returns: 0 - okay, -1 for eof (no more sigs) or any other errorcode - * FIXME: Do we really need this large and complicated function? - */ -#if 0 -static int -walk_sigrecs( SIGREC_CONTEXT *c ) -{ - TRUSTREC *r; - ulong rnum; - - if( c->ctl.eof ) - return -1; - r = &c->ctl.rec; - if( !c->ctl.init_done ) { - c->ctl.init_done = 1; - read_record( c->lid, r, 0 ); - if( r->rectype != RECTYPE_DIR ) { - c->ctl.eof = 1; - return -1; /* return eof */ - } - c->ctl.nextuid = r->r.dir.uidlist; - /* force a read */ - c->ctl.index = SIGS_PER_RECORD; - r->r.sig.next = 0; - } - - /* need a loop to skip over deleted sigs */ - do { - if( c->ctl.index >= SIGS_PER_RECORD ) { /* read the record */ - rnum = r->r.sig.next; - if( !rnum && c->ctl.nextuid ) { /* read next uid record */ - read_record( c->ctl.nextuid, r, RECTYPE_UID ); - c->ctl.nextuid = r->r.uid.next; - rnum = r->r.uid.siglist; - } - if( !rnum ) { - c->ctl.eof = 1; - return -1; /* return eof */ - } - read_record( rnum, r, RECTYPE_SIG ); - if( r->r.sig.lid != c->lid ) { - log_error(_("chained sigrec %lu has a wrong owner\n"), rnum ); - c->ctl.eof = 1; - tdbio_invalid(); - } - c->ctl.index = 0; - } - } while( !r->r.sig.sig[c->ctl.index++].lid ); - - c->sig_lid = r->r.sig.sig[c->ctl.index-1].lid; - c->sig_flag = r->r.sig.sig[c->ctl.index-1].flag; - return 0; -} -#endif - -#if 0 -static int -do_list_sigs( ulong root, ulong pk_lid, int depth, - LOCAL_ID_TABLE lids, unsigned *lineno ) -{ - SIGREC_CONTEXT sx; - int rc; - u32 keyid[2]; - - memset( &sx, 0, sizeof sx ); - sx.lid = pk_lid; - for(;;) { - rc = walk_sigrecs( &sx ); /* should we replace it and use */ - if( rc ) - break; - rc = keyid_from_lid( sx.sig_lid, keyid ); - if( rc ) { - printf("%6u: %*s????????.%lu:", *lineno, depth*4, "", sx.sig_lid ); - print_sigflags( stdout, sx.sig_flag ); - putchar('\n'); - ++*lineno; - } - else { - printf("%6u: %*s%08lX.%lu:", *lineno, depth*4, "", - (ulong)keyid[1], sx.sig_lid ); - print_sigflags( stdout, sx.sig_flag ); - putchar(' '); - /* check whether we already checked this pk_lid */ - if( !qry_lid_table_flag( ultikey_table, sx.sig_lid, NULL ) ) { - print_user_id("[ultimately trusted]", keyid); - ++*lineno; - } - else if( sx.sig_lid == pk_lid ) { - printf("[self-signature]\n"); - ++*lineno; - } - else if( sx.sig_lid == root ) { - printf("[closed]\n"); - ++*lineno; - } - else if( ins_lid_table_item( lids, sx.sig_lid, *lineno ) ) { - unsigned refline; - qry_lid_table_flag( lids, sx.sig_lid, &refline ); - printf("[see line %u]\n", refline); - ++*lineno; - } - else if( depth+1 >= MAX_LIST_SIGS_DEPTH ) { - print_user_id( "[too deeply nested]", keyid ); - ++*lineno; - } - else { - print_user_id( "", keyid ); - ++*lineno; - rc = do_list_sigs( root, sx.sig_lid, depth+1, lids, lineno ); - if( rc ) - break; - } - } - } - return rc==-1? 0 : rc; -} -#endif -/**************** - * List all signatures of a public key - */ -static int -list_sigs( ulong pubkey_id ) -{ - int rc=0; - #if 0 - u32 keyid[2]; - LOCAL_ID_TABLE lids; - unsigned lineno = 1; - - rc = keyid_from_lid( pubkey_id, keyid ); - if( rc ) - return rc; - printf("Signatures of %08lX.%lu ", (ulong)keyid[1], pubkey_id ); - print_user_id("", keyid); - printf("----------------------\n"); - - lids = new_lid_table(); - rc = do_list_sigs( pubkey_id, pubkey_id, 0, lids, &lineno ); - putchar('\n'); - release_lid_table(lids); - #endif - return rc; -} - -/**************** - * List all records of a public key - */ -static int -list_records( ulong lid ) -{ - int rc; - TRUSTREC dr, ur, rec; - ulong recno; - - rc = tdbio_read_record( lid, &dr, RECTYPE_DIR ); - if( rc ) { - log_error(_("lid %lu: read dir record failed: %s\n"), - lid, gpg_errstr(rc)); - return rc; - } - tdbio_dump_record( &dr, stdout ); - - for( recno=dr.r.dir.keylist; recno; recno = rec.r.key.next ) { - rc = tdbio_read_record( recno, &rec, 0 ); - if( rc ) { - log_error(_("lid %lu: read key record failed: %s\n"), - lid, gpg_errstr(rc)); - return rc; - } - tdbio_dump_record( &rec, stdout ); - } - - for( recno=dr.r.dir.uidlist; recno; recno = ur.r.uid.next ) { - rc = tdbio_read_record( recno, &ur, RECTYPE_UID ); - if( rc ) { - log_error(_("lid %lu: read uid record failed: %s\n"), - lid, gpg_errstr(rc)); - return rc; - } - tdbio_dump_record( &ur, stdout ); - /* preference records */ - for(recno=ur.r.uid.prefrec; recno; recno = rec.r.pref.next ) { - rc = tdbio_read_record( recno, &rec, RECTYPE_PREF ); - if( rc ) { - log_error(_("lid %lu: read pref record failed: %s\n"), - lid, gpg_errstr(rc)); - return rc; - } - tdbio_dump_record( &rec, stdout ); - } - /* sig records */ - for(recno=ur.r.uid.siglist; recno; recno = rec.r.sig.next ) { - rc = tdbio_read_record( recno, &rec, RECTYPE_SIG ); - if( rc ) { - log_error(_("lid %lu: read sig record failed: %s\n"), - lid, gpg_errstr(rc)); - return rc; - } - tdbio_dump_record( &rec, stdout ); - } - } - - /* add cache record dump here */ - - - - return rc; -} - - -/**************** - * Dump the complte trustdb or only the entries of one key. + * Dump the entire trustdb or only the entries of one key. */ void list_trustdb( const char *username ) @@ -336,38 +71,8 @@ list_trustdb( const char *username ) TRUSTREC rec; init_trustdb(); - - if( username && *username == '#' ) { - int rc; - ulong lid = atoi(username+1); - - if( (rc = list_records( lid)) ) - log_error(_("user '%s' read problem: %s\n"), - username, gpg_errstr(rc)); - else if( (rc = list_sigs( lid )) ) - log_error(_("user '%s' list problem: %s\n"), - username, gpg_errstr(rc)); - } - else if( username ) { - PKT_public_key *pk = gcry_xcalloc( 1, sizeof *pk ); - int rc; - - if( (rc = get_pubkey_byname( NULL, pk, username, NULL )) ) - log_error(_("user '%s' not found: %s\n"), username, gpg_errstr(rc) ); - else if( (rc=tdbio_search_dir_bypk( pk, &rec )) && rc != -1 ) - log_error(_("problem finding '%s' in trustdb: %s\n"), - username, gpg_errstr(rc)); - else if( rc == -1 ) - log_error(_("user '%s' not in trustdb\n"), username); - else if( (rc = list_records( pk->local_id)) ) - log_error(_("user '%s' read problem: %s\n"), - username, gpg_errstr(rc)); - else if( (rc = list_sigs( pk->local_id )) ) - log_error(_("user '%s' list problem: %s\n"), - username, gpg_errstr(rc)); - free_public_key( pk ); - } - else { + /* for now we ignore the user ID */ + if (1) { ulong recnum; int i; @@ -391,33 +96,22 @@ void export_ownertrust() { TRUSTREC rec; - TRUSTREC rec2; ulong recnum; int i; byte *p; - int rc; 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_DIR ) { - if( !rec.r.dir.keylist ) { - log_error(_("directory record w/o primary key\n")); - continue; - } - if( !rec.r.dir.ownertrust ) + if( rec.rectype == RECTYPE_TRUST ) { + if( !rec.r.trust.ownertrust ) continue; - rc = tdbio_read_record( rec.r.dir.keylist, &rec2, RECTYPE_KEY); - if( rc ) { - log_error(_("error reading key record: %s\n"), gpg_errstr(rc)); - continue; - } - p = rec2.r.key.fingerprint; - for(i=0; i < rec2.r.key.fingerprint_len; i++, p++ ) + p = rec.r.trust.fingerprint; + for(i=0; i < 20; i++, p++ ) printf("%02X", *p ); - printf(":%u:\n", (unsigned)rec.r.dir.ownertrust ); + printf(":%u:\n", (unsigned int)rec.r.trust.ownertrust ); } } } @@ -431,7 +125,10 @@ import_ownertrust( const char *fname ) char line[256]; char *p; size_t n, fprlen; - unsigned otrust; + unsigned int otrust; + byte fpr[20]; + int any = 0; + int rc; init_trustdb(); if( !fname || (*fname == '-' && !fname[1]) ) { @@ -446,7 +143,6 @@ import_ownertrust( const char *fname ) while( fgets( line, DIM(line)-1, fp ) ) { TRUSTREC rec; - int rc; if( !*line || *line == '#' ) continue; @@ -475,51 +171,52 @@ import_ownertrust( const char *fname ) if( !otrust ) continue; /* no otrust defined - no need to update or insert */ /* convert the ascii fingerprint to binary */ - for(p=line, fprlen=0; *p != ':'; p += 2 ) - line[fprlen++] = HEXTOBIN(p[0]) * 16 + HEXTOBIN(p[1]); - line[fprlen] = 0; - - repeat: - rc = tdbio_search_dir_byfpr( line, fprlen, 0, &rec ); + 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.dir.ownertrust ) - log_info("LID %lu: changing trust from %u to %u\n", - rec.r.dir.lid, rec.r.dir.ownertrust, otrust ); - else - log_info("LID %lu: setting trust to %u\n", - rec.r.dir.lid, otrust ); - rec.r.dir.ownertrust = otrust; - write_record( &rec ); + 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; get the key from the ring */ - PKT_public_key *pk = gcry_xcalloc( 1, sizeof *pk ); - - log_info_f(fname, _("key not in trustdb, searching ring.\n")); - rc = get_pubkey_byfprint( pk, line, fprlen ); - if( rc ) - log_info_f(fname, _("key not in ring: %s\n"), gpg_errstr(rc)); - else { - rc = query_trust_record( pk ); /* only as assertion */ - if( rc != -1 ) - log_error_f(fname, _("Oops: key is now in trustdb???\n")); - else { - rc = insert_trust_record_by_pk( pk ); - if( !rc ) - goto repeat; /* update the ownertrust */ - log_error_f(fname, _("insert trust record failed: %s\n"), - gpg_errstr(rc) ); - } - } + 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_f(fname, _("error finding dir record: %s\n"), - gpg_errstr(rc)); + log_error_f(fname, _("error finding trust record: %s\n"), + g10_errstr(rc)); } if( ferror(fp) ) log_error_f(fname, _("read error: %s\n"), strerror(errno) ); if( !is_stdin ) fclose(fp); - do_sync(); - sync_trustdb(); + + 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 index 669f66ffc..537e4c0d4 100644 --- a/g10/tdbio.c +++ b/g10/tdbio.c @@ -1,5 +1,5 @@ /* tdbio.c - * Copyright (C) 1998, 1999, 2000 Free Software Foundation, Inc. + * Copyright (C) 1998, 1999, 2000, 2001, 2002 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -31,7 +31,7 @@ #include "errors.h" #include "iobuf.h" -#include <gcrypt.h> +#include "memory.h" #include "util.h" #include "options.h" #include "main.h" @@ -39,6 +39,9 @@ #include "trustdb.h" #include "tdbio.h" +#if defined(HAVE_DOSISH_SYSTEM) && !defined(__CYGWIN32__) +#define ftruncate chsize +#endif /**************** * Yes, this is a very simple implementation. We should really @@ -83,6 +86,8 @@ static int db_fd = -1; static int in_transaction; static void open_db(void); +static void migrate_from_v2 (void); + /************************************* @@ -115,13 +120,13 @@ write_cache_item( CACHE_CTRL r ) 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 GPGERR_WRITE_FILE; + 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 GPGERR_WRITE_FILE; + return G10ERR_WRITE_FILE; } r->flags.dirty = 0; return 0; @@ -175,7 +180,7 @@ put_record_into_cache( ulong recno, const char *data ) } /* see whether we reached the limit */ if( cache_entries < MAX_CACHE_ENTRIES_SOFT ) { /* no */ - r = gcry_xmalloc( sizeof *r ); + r = m_alloc( sizeof *r ); r->flags.used = 1; r->recno = recno; memcpy( r->data, data, TRUST_RECORD_LEN ); @@ -218,7 +223,7 @@ put_record_into_cache( ulong recno, const char *data ) if( cache_entries < MAX_CACHE_ENTRIES_HARD ) { /* no */ if( opt.debug && !(cache_entries % 100) ) log_debug("increasing tdbio cache size\n"); - r = gcry_xmalloc( sizeof *r ); + r = m_alloc( sizeof *r ); r->flags.used = 1; r->recno = recno; memcpy( r->data, data, TRUST_RECORD_LEN ); @@ -230,7 +235,7 @@ put_record_into_cache( ulong recno, const char *data ) return 0; } log_info(_("trustdb transaction too large\n")); - return GPGERR_RESOURCE_LIMIT; + return G10ERR_RESOURCE_LIMIT; } if( dirty_count ) { int n = dirty_count / 5; /* discard some dirty entries */ @@ -406,6 +411,28 @@ cleanup(void) } } +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; + 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 ) { @@ -416,20 +443,23 @@ tdbio_set_dbname( const char *new_dbname, int create ) atexit( cleanup ); initialized = 1; } - fname = new_dbname? gcry_xstrdup( new_dbname ) - : make_filename(opt.homedir, "trustdb.gpg", NULL ); + fname = new_dbname? m_strdup( new_dbname ) + : make_filename(opt.homedir, + "trustdb" EXTSEP_S "gpg", NULL ); + + check_permissions(fname,0,0); if( access( fname, R_OK ) ) { if( errno != ENOENT ) { log_error( _("%s: can't access: %s\n"), fname, strerror(errno) ); - gcry_free(fname); - return GPGERR_TRUSTDB; + m_free(fname); + return G10ERR_TRUSTDB; } if( create ) { FILE *fp; TRUSTREC rec; int rc; - char *p = strrchr( fname, '/' ); + char *p = strrchr( fname, DIRSEP_C ); assert(p); *p = 0; @@ -437,14 +467,22 @@ tdbio_set_dbname( const char *new_dbname, int create ) try_make_homedir( fname ); log_fatal( _("%s: directory does not exist!\n"), fname ); } - *p = '/'; + *p = DIRSEP_C; + m_free(db_name); + db_name = fname; +#ifdef __riscos__ + if( !lockhandle ) + lockhandle = create_dotlock( db_name ); + if( !lockhandle ) + log_fatal( _("%s: can't create lock\n"), db_name ); + if( make_dotlock( lockhandle, -1 ) ) + log_fatal( _("%s: can't make lock\n"), db_name ); +#endif /* __riscos__ */ fp =fopen( fname, "wb" ); if( !fp ) log_fatal( _("%s: can't create: %s\n"), fname, strerror(errno) ); fclose(fp); - gcry_free(db_name); - db_name = fname; #ifdef HAVE_DOSISH_SYSTEM db_fd = open( db_name, O_RDWR | O_BINARY ); #else @@ -453,25 +491,17 @@ tdbio_set_dbname( const char *new_dbname, int create ) if( db_fd == -1 ) log_fatal( _("%s: can't open: %s\n"), db_name, strerror(errno) ); +#ifndef __riscos__ if( !lockhandle ) lockhandle = create_dotlock( db_name ); if( !lockhandle ) log_fatal( _("%s: can't create lock\n"), db_name ); +#endif /* !__riscos__ */ - memset( &rec, 0, sizeof rec ); - rec.r.ver.version = 2; - 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.rectype = RECTYPE_VER; - rec.recnum = 0; - rc = tdbio_write_record( &rec ); - if( !rc ) - tdbio_sync(); + rc = create_version_record (); if( rc ) log_fatal( _("%s: failed to create version record: %s"), - fname, gpg_errstr(rc)); + 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 ); @@ -482,7 +512,7 @@ tdbio_set_dbname( const char *new_dbname, int create ) return 0; } } - gcry_free(db_name); + m_free(db_name); db_name = fname; return 0; } @@ -499,27 +529,45 @@ tdbio_get_dbname() static void open_db() { - TRUSTREC rec; - assert( db_fd == -1 ); - - if( !lockhandle ) - lockhandle = create_dotlock( db_name ); - if( !lockhandle ) - log_fatal( _("%s: can't create lock\n"), db_name ); - #ifdef HAVE_DOSISH_SYSTEM - db_fd = open( db_name, O_RDWR | O_BINARY ); - #else - db_fd = open( db_name, O_RDWR ); - #endif - if( db_fd == -1 ) - log_fatal( _("%s: can't open: %s\n"), db_name, strerror(errno) ); - if( tdbio_read_record( 0, &rec, RECTYPE_VER ) ) - log_fatal( _("%s: invalid trustdb\n"), db_name ); + byte buf[10]; + int n; + TRUSTREC rec; + + assert( db_fd == -1 ); + + if (!lockhandle ) + lockhandle = create_dotlock( db_name ); + if (!lockhandle ) + log_fatal( _("%s: can't create lock\n"), db_name ); +#ifdef __riscos__ + if (make_dotlock( lockhandle, -1 ) ) + log_fatal( _("%s: can't make lock\n"), db_name ); +#endif /* __riscos__ */ +#ifdef HAVE_DOSISH_SYSTEM + db_fd = open (db_name, O_RDWR | O_BINARY ); +#else + db_fd = open (db_name, O_RDWR ); +#endif + if ( db_fd == -1 ) + log_fatal( _("%s: can't open: %s\n"), db_name, strerror(errno) ); + + /* 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 = key hash, 1 = sdir hash + * Make a hashtable: type 0 = trust hash */ static void create_hashtable( TRUSTREC *vr, int type ) @@ -536,9 +584,8 @@ create_hashtable( TRUSTREC *vr, int type ) assert(recnum); /* this is will never be the first record */ if( !type ) - vr->r.ver.keyhashtbl = recnum; - else - vr->r.ver.sdirhashtbl = recnum; + 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++ ) { @@ -548,7 +595,7 @@ create_hashtable( TRUSTREC *vr, int type ) rc = tdbio_write_record( &rec ); if( rc ) log_fatal( _("%s: failed to create hashtable: %s\n"), - db_name, gpg_errstr(rc)); + db_name, g10_errstr(rc)); } /* update the version record */ rc = tdbio_write_record( vr ); @@ -556,7 +603,7 @@ create_hashtable( TRUSTREC *vr, int type ) rc = tdbio_sync(); if( rc ) log_fatal( _("%s: error updating version record: %s\n"), - db_name, gpg_errstr(rc)); + db_name, g10_errstr(rc)); } @@ -572,7 +619,7 @@ tdbio_db_matches_options() rc = tdbio_read_record( 0, &vr, RECTYPE_VER ); if( rc ) log_fatal( _("%s: error reading version record: %s\n"), - db_name, gpg_errstr(rc) ); + db_name, g10_errstr(rc) ); if( !vr.r.ver.marginals && !vr.r.ver.completes && !vr.r.ver.cert_depth ) @@ -585,7 +632,7 @@ tdbio_db_matches_options() rc = tdbio_sync(); if( rc ) log_error( _("%s: error writing version record: %s\n"), - db_name, gpg_errstr(rc) ); + db_name, g10_errstr(rc) ); } yes_no = vr.r.ver.marginals == opt.marginals_needed @@ -597,105 +644,70 @@ tdbio_db_matches_options() /**************** - * Return the modifiy stamp. - * if modify_down is true, the modify_down stamp will be - * returned, otherwise the modify_up stamp. + * Return the nextstamp value. */ ulong -tdbio_read_modify_stamp( int modify_down ) +tdbio_read_nextcheck () { TRUSTREC vr; int rc; - ulong mod; rc = tdbio_read_record( 0, &vr, RECTYPE_VER ); if( rc ) log_fatal( _("%s: error reading version record: %s\n"), - db_name, gpg_errstr(rc) ); - - mod = modify_down? vr.r.ver.mod_down : vr.r.ver.mod_up; - - /* Always return at least 1 to make comparison easier; - * this is still far back in history (before Led Zeppelin III :-) */ - return mod ? mod : 1; + db_name, g10_errstr(rc) ); + return vr.r.ver.nextcheck; } -void -tdbio_write_modify_stamp( int up, int down ) +/* Return true when the stamp was actually changed. */ +int +tdbio_write_nextcheck (ulong stamp) { TRUSTREC vr; int rc; - ulong stamp; - - if( !(up || down) ) - return; rc = tdbio_read_record( 0, &vr, RECTYPE_VER ); if( rc ) log_fatal( _("%s: error reading version record: %s\n"), - db_name, gpg_errstr(rc) ); + db_name, g10_errstr(rc) ); - stamp = make_timestamp(); - if( down ) - vr.r.ver.mod_down = stamp; - if( up ) - vr.r.ver.mod_up = stamp; + 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, gpg_errstr(rc) ); + db_name, g10_errstr(rc) ); + return 1; } + /**************** - * Return the record number of the keyhash tbl or create a new one. + * Return the record number of the trusthash tbl or create a new one. */ static ulong -get_keyhashrec(void) +get_trusthashrec(void) { - static ulong keyhashtbl; /* record number of the key hashtable */ + static ulong trusthashtbl; /* record number of the trust hashtable */ - if( !keyhashtbl ) { + 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, gpg_errstr(rc) ); - if( !vr.r.ver.keyhashtbl ) + db_name, g10_errstr(rc) ); + if( !vr.r.ver.trusthashtbl ) create_hashtable( &vr, 0 ); - keyhashtbl = vr.r.ver.keyhashtbl; + trusthashtbl = vr.r.ver.trusthashtbl; } - return keyhashtbl; + return trusthashtbl; } -/**************** - * Return the record number of the shadow direcory hash table - * or create a new one. - */ -static ulong -get_sdirhashrec(void) -{ - static ulong sdirhashtbl; /* record number of the hashtable */ - - if( !sdirhashtbl ) { - 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, gpg_errstr(rc) ); - if( !vr.r.ver.sdirhashtbl ) - create_hashtable( &vr, 1 ); - - sdirhashtbl = vr.r.ver.sdirhashtbl; - } - return sdirhashtbl; -} /**************** @@ -719,7 +731,7 @@ upd_hashtable( ulong table, byte *key, int keylen, ulong newrecnum ) rc = tdbio_read_record( hashrec, &rec, RECTYPE_HTBL ); if( rc ) { log_error( db_name, "upd_hashtable: read failed: %s\n", - gpg_errstr(rc) ); + g10_errstr(rc) ); return rc; } @@ -729,7 +741,7 @@ upd_hashtable( ulong table, byte *key, int keylen, ulong newrecnum ) rc = tdbio_write_record( &rec ); if( rc ) { log_error( db_name, "upd_hashtable: write htbl failed: %s\n", - gpg_errstr(rc) ); + g10_errstr(rc) ); return rc; } } @@ -738,7 +750,7 @@ upd_hashtable( ulong table, byte *key, int keylen, ulong newrecnum ) rc = tdbio_read_record( item, &rec, 0 ); if( rc ) { log_error( "upd_hashtable: read item failed: %s\n", - gpg_errstr(rc) ); + g10_errstr(rc) ); return rc; } @@ -747,7 +759,7 @@ upd_hashtable( ulong table, byte *key, int keylen, ulong newrecnum ) level++; if( level >= keylen ) { log_error( "hashtable has invalid indirections.\n"); - return GPGERR_TRUSTDB; + return G10ERR_TRUSTDB; } goto next_level; } @@ -763,8 +775,8 @@ upd_hashtable( ulong table, byte *key, int keylen, ulong newrecnum ) rc = tdbio_read_record( rec.r.hlst.next, &rec, RECTYPE_HLST); if( rc ) { - log_error( "scan keyhashtbl read hlst failed: %s\n", - gpg_errstr(rc) ); + log_error( "upd_hashtable: read hlst failed: %s\n", + g10_errstr(rc) ); return rc; } } @@ -779,7 +791,7 @@ upd_hashtable( ulong table, byte *key, int keylen, ulong newrecnum ) rc = tdbio_write_record( &rec ); if( rc ) log_error( "upd_hashtable: write hlst failed: %s\n", - gpg_errstr(rc) ); + g10_errstr(rc) ); return rc; /* done */ } } @@ -788,7 +800,7 @@ upd_hashtable( ulong table, byte *key, int keylen, ulong newrecnum ) &rec, RECTYPE_HLST ); if( rc ) { log_error( "upd_hashtable: read hlst failed: %s\n", - gpg_errstr(rc) ); + g10_errstr(rc) ); return rc; } } @@ -797,7 +809,7 @@ upd_hashtable( ulong table, byte *key, int keylen, ulong newrecnum ) rc = tdbio_write_record( &rec ); if( rc ) { log_error( "upd_hashtable: write hlst failed: %s\n", - gpg_errstr(rc) ); + g10_errstr(rc) ); return rc; } memset( &rec, 0, sizeof rec ); @@ -807,14 +819,12 @@ upd_hashtable( ulong table, byte *key, int keylen, ulong newrecnum ) rc = tdbio_write_record( &rec ); if( rc ) log_error( "upd_hashtable: write ext hlst failed: %s\n", - gpg_errstr(rc) ); + g10_errstr(rc) ); return rc; /* done */ } } /* end loop over hlst slots */ } - else if( rec.rectype == RECTYPE_KEY - || rec.rectype == RECTYPE_DIR - || rec.rectype == RECTYPE_SDIR ) { /* insert a list record */ + else if( rec.rectype == RECTYPE_TRUST ) { /* insert a list record */ if( rec.recnum == newrecnum ) { return 0; } @@ -827,7 +837,7 @@ upd_hashtable( ulong table, byte *key, int keylen, ulong newrecnum ) rc = tdbio_write_record( &rec ); if( rc ) { log_error( "upd_hashtable: write new hlst failed: %s\n", - gpg_errstr(rc) ); + g10_errstr(rc) ); return rc; } /* update the hashtable record */ @@ -835,14 +845,14 @@ upd_hashtable( ulong table, byte *key, int keylen, ulong newrecnum ) rc = tdbio_write_record( &lastrec ); if( rc ) log_error( "upd_hashtable: update htbl failed: %s\n", - gpg_errstr(rc) ); + 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 GPGERR_TRUSTDB; + return G10ERR_TRUSTDB; } } @@ -870,7 +880,7 @@ drop_from_hashtable( ulong table, byte *key, int keylen, ulong recnum ) rc = tdbio_read_record( hashrec, &rec, RECTYPE_HTBL ); if( rc ) { log_error( db_name, "drop_from_hashtable: read failed: %s\n", - gpg_errstr(rc) ); + g10_errstr(rc) ); return rc; } @@ -883,14 +893,14 @@ drop_from_hashtable( ulong table, byte *key, int keylen, ulong recnum ) rc = tdbio_write_record( &rec ); if( rc ) log_error( db_name, "drop_from_hashtable: write htbl failed: %s\n", - gpg_errstr(rc) ); + g10_errstr(rc) ); return rc; } rc = tdbio_read_record( item, &rec, 0 ); if( rc ) { log_error( "drop_from_hashtable: read item failed: %s\n", - gpg_errstr(rc) ); + g10_errstr(rc) ); return rc; } @@ -899,7 +909,7 @@ drop_from_hashtable( ulong table, byte *key, int keylen, ulong recnum ) level++; if( level >= keylen ) { log_error( "hashtable has invalid indirections.\n"); - return GPGERR_TRUSTDB; + return G10ERR_TRUSTDB; } goto next_level; } @@ -912,7 +922,7 @@ drop_from_hashtable( ulong table, byte *key, int keylen, ulong recnum ) rc = tdbio_write_record( &rec ); if( rc ) log_error( db_name, "drop_from_hashtable: write htbl failed: %s\n", - gpg_errstr(rc) ); + g10_errstr(rc) ); return rc; } } @@ -920,8 +930,8 @@ drop_from_hashtable( ulong table, byte *key, int keylen, ulong recnum ) rc = tdbio_read_record( rec.r.hlst.next, &rec, RECTYPE_HLST); if( rc ) { - log_error( "scan keyhashtbl read hlst failed: %s\n", - gpg_errstr(rc) ); + log_error( "drop_from_hashtable: read hlst failed: %s\n", + g10_errstr(rc) ); return rc; } } @@ -932,7 +942,7 @@ drop_from_hashtable( ulong table, byte *key, int keylen, ulong recnum ) log_error( "hashtbl %lu: %lu/%d points to wrong record %lu\n", table, hashrec, (msb % ITEMS_PER_HTBL_RECORD), item); - return GPGERR_TRUSTDB; + return G10ERR_TRUSTDB; } @@ -958,7 +968,7 @@ lookup_hashtable( ulong table, const byte *key, size_t keylen, hashrec += msb / ITEMS_PER_HTBL_RECORD; rc = tdbio_read_record( hashrec, rec, RECTYPE_HTBL ); if( rc ) { - log_error( db_name, "lookup_hashtable failed: %s\n", gpg_errstr(rc) ); + log_error( db_name, "lookup_hashtable failed: %s\n", g10_errstr(rc) ); return rc; } @@ -968,7 +978,7 @@ lookup_hashtable( ulong table, const byte *key, size_t keylen, rc = tdbio_read_record( item, rec, 0 ); if( rc ) { - log_error( db_name, "hashtable read failed: %s\n", gpg_errstr(rc) ); + log_error( db_name, "hashtable read failed: %s\n", g10_errstr(rc) ); return rc; } if( rec->rectype == RECTYPE_HTBL ) { @@ -976,7 +986,7 @@ lookup_hashtable( ulong table, const byte *key, size_t keylen, level++; if( level >= keylen ) { log_error( db_name, "hashtable has invalid indirections\n"); - return GPGERR_TRUSTDB; + return G10ERR_TRUSTDB; } goto next_level; } @@ -991,7 +1001,7 @@ lookup_hashtable( ulong table, const byte *key, size_t keylen, rc = tdbio_read_record( rec->r.hlst.rnum[i], &tmp, 0 ); if( rc ) { log_error( "lookup_hashtable: read item failed: %s\n", - gpg_errstr(rc) ); + g10_errstr(rc) ); return rc; } if( (*cmpfnc)( cmpdata, &tmp ) ) { @@ -1004,7 +1014,7 @@ lookup_hashtable( ulong table, const byte *key, size_t keylen, rc = tdbio_read_record( rec->r.hlst.next, rec, RECTYPE_HLST ); if( rc ) { log_error( "lookup_hashtable: read hlst failed: %s\n", - gpg_errstr(rc) ); + g10_errstr(rc) ); return rc; } } @@ -1021,57 +1031,16 @@ lookup_hashtable( ulong table, const byte *key, size_t keylen, } - - /**************** - * Update the key hashtbl or create the table if it does not exist + * Update the trust hashtbl or create the table if it does not exist */ static int -update_keyhashtbl( TRUSTREC *kr ) +update_trusthashtbl( TRUSTREC *tr ) { - return upd_hashtable( get_keyhashrec(), - kr->r.key.fingerprint, - kr->r.key.fingerprint_len, kr->recnum ); + return upd_hashtable( get_trusthashrec(), + tr->r.trust.fingerprint, 20, tr->recnum ); } -/**************** - * Update the shadow dir hashtbl or create the table if it does not exist - */ -static int -update_sdirhashtbl( TRUSTREC *sr ) -{ - byte key[8]; - - u32tobuf( key , sr->r.sdir.keyid[0] ); - u32tobuf( key+4 , sr->r.sdir.keyid[1] ); - return upd_hashtable( get_sdirhashrec(), key, 8, sr->recnum ); -} - -/**************** - * Drop the records from the key-hashtbl - */ -static int -drop_from_keyhashtbl( TRUSTREC *kr ) -{ - return drop_from_hashtable( get_keyhashrec(), - kr->r.key.fingerprint, - kr->r.key.fingerprint_len, kr->recnum ); -} - -/**************** - * Drop record drom the shadow dir hashtbl - */ -static int -drop_from_sdirhashtbl( TRUSTREC *sr ) -{ - byte key[8]; - - u32tobuf( key , sr->r.sdir.keyid[0] ); - u32tobuf( key+4 , sr->r.sdir.keyid[1] ); - return drop_from_hashtable( get_sdirhashrec(), key, 8, sr->recnum ); -} - - void @@ -1079,7 +1048,6 @@ tdbio_dump_record( TRUSTREC *rec, FILE *fp ) { int i; ulong rnum = rec->recnum; - byte *p; fprintf(fp, "rec %5lu, ", rnum ); @@ -1087,116 +1055,18 @@ tdbio_dump_record( TRUSTREC *rec, FILE *fp ) case 0: fprintf(fp, "blank\n"); break; case RECTYPE_VER: fprintf(fp, - "version, kd=%lu, sd=%lu, free=%lu, m/c/d=%d/%d/%d down=%s", - rec->r.ver.keyhashtbl, rec->r.ver.sdirhashtbl, + "version, td=%lu, f=%lu, m/c/d=%d/%d/%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, - strtimestamp(rec->r.ver.mod_down) ); - fprintf(fp, ", up=%s\n", strtimestamp(rec->r.ver.mod_up) ); + 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_DIR: - fprintf(fp, "dir %lu, keys=%lu, uids=%lu, t=%02x", - rec->r.dir.lid, - rec->r.dir.keylist, - rec->r.dir.uidlist, - rec->r.dir.ownertrust ); - if( rec->r.dir.valcheck ) - fprintf( fp, ", v=%02x/%s", rec->r.dir.validity, - strtimestamp(rec->r.dir.valcheck) ); - if( rec->r.dir.checkat ) - fprintf( fp, ", a=%s", strtimestamp(rec->r.dir.checkat) ); - if( rec->r.dir.dirflags & DIRF_CHECKED ) { - if( rec->r.dir.dirflags & DIRF_VALID ) - fputs(", valid", fp ); - if( rec->r.dir.dirflags & DIRF_EXPIRED ) - fputs(", expired", fp ); - if( rec->r.dir.dirflags & DIRF_REVOKED ) - fputs(", revoked", fp ); - if( rec->r.dir.dirflags & DIRF_NEWKEYS ) - fputs(", newkeys", fp ); - } - putc('\n', fp); - break; - case RECTYPE_KEY: - fprintf(fp, "key %lu, n=%lu a=%d ", - rec->r.key.lid, - rec->r.key.next, - rec->r.key.pubkey_algo ); - for(i=0; i < rec->r.key.fingerprint_len; i++ ) - fprintf(fp, "%02X", rec->r.key.fingerprint[i] ); - if( rec->r.key.keyflags & KEYF_CHECKED ) { - if( rec->r.key.keyflags & KEYF_VALID ) - fputs(", valid", fp ); - if( rec->r.key.keyflags & KEYF_EXPIRED ) - fputs(", expired", fp ); - if( rec->r.key.keyflags & KEYF_REVOKED ) - fputs(", revoked", fp ); - } - putc('\n', fp); - break; - case RECTYPE_UID: - fprintf(fp, "uid %lu, next=%lu, pref=%lu, sig=%lu, hash=%02X%02X", - rec->r.uid.lid, - rec->r.uid.next, - rec->r.uid.prefrec, - rec->r.uid.siglist, - rec->r.uid.namehash[18], rec->r.uid.namehash[19]); - fprintf( fp, ", v=%02x", rec->r.uid.validity ); - if( rec->r.uid.uidflags & UIDF_CHECKED ) { - if( rec->r.uid.uidflags & UIDF_VALID ) - fputs(", valid", fp ); - if( rec->r.uid.uidflags & UIDF_REVOKED ) - fputs(", revoked", fp ); - } - putc('\n', fp); - break; - case RECTYPE_PREF: - fprintf(fp, "pref %lu, next=%lu,", - rec->r.pref.lid, rec->r.pref.next); - for(i=0,p=rec->r.pref.data; i < ITEMS_PER_PREF_RECORD; i+=2,p+=2 ) { - if( *p ) - fprintf(fp, " %c%d", *p == PREFTYPE_SYM ? 'S' : - *p == PREFTYPE_HASH ? 'H' : - *p == PREFTYPE_COMPR ? 'Z' : '?', p[1]); - } - putc('\n', fp); - break; - case RECTYPE_SIG: - fprintf(fp, "sig %lu, next=%lu,", - rec->r.sig.lid, rec->r.sig.next ); - for(i=0; i < SIGS_PER_RECORD; i++ ) { - if( rec->r.sig.sig[i].lid ) { - fprintf(fp, " %lu:", rec->r.sig.sig[i].lid ); - if( rec->r.sig.sig[i].flag & SIGF_CHECKED ) { - fprintf(fp,"%c%c%c", - (rec->r.sig.sig[i].flag & SIGF_VALID) ? 'V': - (rec->r.sig.sig[i].flag & SIGF_IGNORED) ? 'I':'-', - (rec->r.sig.sig[i].flag & SIGF_EXPIRED) ? 'E':'-', - (rec->r.sig.sig[i].flag & SIGF_REVOKED) ? 'R':'-'); - } - else if( rec->r.sig.sig[i].flag & SIGF_NOPUBKEY) - fputs("?--", fp); - else - fputs("---", fp); - } - } - putc('\n', fp); - break; - case RECTYPE_SDIR: - fprintf(fp, "sdir %lu, keyid=%08lX%08lX, algo=%d, hint=%lu\n", - rec->r.sdir.lid, - (ulong)rec->r.sdir.keyid[0], - (ulong)rec->r.sdir.keyid[1], - rec->r.sdir.pubkey_algo, - (ulong)rec->r.sdir.hintlist ); - break; - case RECTYPE_CACH: - fprintf(fp, "cach\n"); - break; case RECTYPE_HTBL: fprintf(fp, "htbl,"); for(i=0; i < ITEMS_PER_HTBL_RECORD; i++ ) @@ -1209,6 +1079,20 @@ tdbio_dump_record( TRUSTREC *rec, FILE *fp ) 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; @@ -1233,7 +1117,7 @@ tdbio_read_record( ulong recnum, TRUSTREC *rec, int expected ) if( !buf ) { if( lseek( db_fd, recnum * TRUST_RECORD_LEN, SEEK_SET ) == -1 ) { log_error(_("trustdb: lseek failed: %s\n"), strerror(errno) ); - return GPGERR_READ_FILE; + return G10ERR_READ_FILE; } n = read( db_fd, readbuf, TRUST_RECORD_LEN); if( !n ) { @@ -1242,7 +1126,7 @@ tdbio_read_record( ulong recnum, TRUSTREC *rec, int expected ) else if( n != TRUST_RECORD_LEN ) { log_error(_("trustdb: read failed (n=%d): %s\n"), n, strerror(errno) ); - return GPGERR_READ_FILE; + return G10ERR_READ_FILE; } buf = readbuf; } @@ -1253,7 +1137,7 @@ tdbio_read_record( ulong recnum, TRUSTREC *rec, int expected ) if( expected && rec->rectype != expected ) { log_error("%lu: read expected rec type %d, got %d\n", recnum, expected, rec->rectype ); - return GPGERR_TRUSTDB; + return G10ERR_TRUSTDB; } p++; /* skip reserved byte */ switch( rec->rectype ) { @@ -1262,123 +1146,35 @@ tdbio_read_record( ulong recnum, TRUSTREC *rec, int expected ) case RECTYPE_VER: /* version record */ if( memcmp(buf+1, "gpg", 3 ) ) { log_error( _("%s: not a trustdb file\n"), db_name ); - rc = GPGERR_TRUSTDB; + rc = G10ERR_TRUSTDB; } - p += 2; /* skip "pgp" */ + 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++; p += 4; /* lock flags */ rec->r.ver.created = buftoulong(p); p += 4; - rec->r.ver.mod_down = buftoulong(p); p += 4; - rec->r.ver.mod_up = buftoulong(p); p += 4; - rec->r.ver.keyhashtbl=buftoulong(p); p += 4; + rec->r.ver.nextcheck = buftoulong(p); p += 4; + p += 4; + p += 4; rec->r.ver.firstfree =buftoulong(p); p += 4; - rec->r.ver.sdirhashtbl =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 = GPGERR_TRUSTDB; + rc = G10ERR_TRUSTDB; } - else if( rec->r.ver.version != 2 ) { + else if( rec->r.ver.version != 3 ) { log_error( _("%s: invalid file version %d\n"), db_name, rec->r.ver.version ); - rc = GPGERR_TRUSTDB; + rc = G10ERR_TRUSTDB; } break; case RECTYPE_FREE: rec->r.free.next = buftoulong(p); p += 4; break; - case RECTYPE_DIR: /*directory record */ - rec->r.dir.lid = buftoulong(p); p += 4; - rec->r.dir.keylist = buftoulong(p); p += 4; - rec->r.dir.uidlist = buftoulong(p); p += 4; - rec->r.dir.cacherec = buftoulong(p); p += 4; - rec->r.dir.ownertrust = *p++; - rec->r.dir.dirflags = *p++; - rec->r.dir.validity = *p++; - rec->r.dir.valcheck = buftoulong(p); p += 4; - rec->r.dir.checkat = buftoulong(p); p += 4; - switch( rec->r.dir.validity ) { - case 0: - case TRUST_UNDEFINED: - case TRUST_NEVER: - case TRUST_MARGINAL: - case TRUST_FULLY: - case TRUST_ULTIMATE: - break; - default: - log_info("lid %lu: invalid validity value - cleared\n", recnum); - } - if( rec->r.dir.lid != recnum ) { - log_error( "%s: dir LID != recnum (%lu,%lu)\n", - db_name, rec->r.dir.lid, (ulong)recnum ); - rc = GPGERR_TRUSTDB; - } - break; - case RECTYPE_KEY: /* public key record */ - rec->r.key.lid = buftoulong(p); p += 4; - rec->r.key.next = buftoulong(p); p += 4; - p += 7; - rec->r.key.keyflags = *p++; - rec->r.key.pubkey_algo = *p++; - rec->r.key.fingerprint_len = *p++; - if( rec->r.key.fingerprint_len < 1 || rec->r.key.fingerprint_len > 20 ) - rec->r.key.fingerprint_len = 20; - memcpy( rec->r.key.fingerprint, p, 20); - break; - case RECTYPE_UID: /* user id record */ - rec->r.uid.lid = buftoulong(p); p += 4; - rec->r.uid.next = buftoulong(p); p += 4; - rec->r.uid.prefrec = buftoulong(p); p += 4; - rec->r.uid.siglist = buftoulong(p); p += 4; - rec->r.uid.uidflags = *p++; - rec->r.uid.validity = *p++; - switch( rec->r.uid.validity ) { - case 0: - case TRUST_UNDEFINED: - case TRUST_NEVER: - case TRUST_MARGINAL: - case TRUST_FULLY: - case TRUST_ULTIMATE: - break; - default: - log_info("lid %lu: invalid validity value - cleared\n", recnum); - } - memcpy( rec->r.uid.namehash, p, 20); - break; - case RECTYPE_PREF: /* preference record */ - rec->r.pref.lid = buftoulong(p); p += 4; - rec->r.pref.next = buftoulong(p); p += 4; - memcpy( rec->r.pref.data, p, 30 ); - break; - case RECTYPE_SIG: - rec->r.sig.lid = buftoulong(p); p += 4; - rec->r.sig.next = buftoulong(p); p += 4; - for(i=0; i < SIGS_PER_RECORD; i++ ) { - rec->r.sig.sig[i].lid = buftoulong(p); p += 4; - rec->r.sig.sig[i].flag = *p++; - } - break; - case RECTYPE_SDIR: /* shadow directory record */ - rec->r.sdir.lid = buftoulong(p); p += 4; - rec->r.sdir.keyid[0]= buftou32(p); p += 4; - rec->r.sdir.keyid[1]= buftou32(p); p += 4; - rec->r.sdir.pubkey_algo = *p++; - p += 3; - rec->r.sdir.hintlist = buftoulong(p); - if( rec->r.sdir.lid != recnum ) { - log_error( "%s: sdir LID != recnum (%lu,%lu)\n", - db_name, rec->r.sdir.lid, (ulong)recnum ); - rc = GPGERR_TRUSTDB; - } - break; - case RECTYPE_CACH: /* cache record */ - rec->r.cache.lid = buftoulong(p); p += 4; - memcpy(rec->r.cache.blockhash, p, 20); p += 20; - rec->r.cache.trustlevel = *p++; - break; case RECTYPE_HTBL: for(i=0; i < ITEMS_PER_HTBL_RECORD; i++ ) { rec->r.htbl.item[i] = buftoulong(p); p += 4; @@ -1390,10 +1186,22 @@ tdbio_read_record( ulong recnum, TRUSTREC *rec, int expected ) 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++; + p += 2; + 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; + break; default: log_error( "%s: invalid record type %d at recnum %lu\n", db_name, rec->rectype, (ulong)recnum ); - rc = GPGERR_TRUSTDB; + rc = G10ERR_TRUSTDB; break; } @@ -1430,79 +1238,18 @@ tdbio_write_record( TRUSTREC *rec ) *p++ = rec->r.ver.cert_depth; p += 4; /* skip lock flags */ ulongtobuf(p, rec->r.ver.created); p += 4; - ulongtobuf(p, rec->r.ver.mod_down); p += 4; - ulongtobuf(p, rec->r.ver.mod_up); p += 4; - ulongtobuf(p, rec->r.ver.keyhashtbl); p += 4; + ulongtobuf(p, rec->r.ver.nextcheck); p += 4; + p += 4; + p += 4; ulongtobuf(p, rec->r.ver.firstfree ); p += 4; - ulongtobuf(p, rec->r.ver.sdirhashtbl ); 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_DIR: /*directory record */ - ulongtobuf(p, rec->r.dir.lid); p += 4; - ulongtobuf(p, rec->r.dir.keylist); p += 4; - ulongtobuf(p, rec->r.dir.uidlist); p += 4; - ulongtobuf(p, rec->r.dir.cacherec); p += 4; - *p++ = rec->r.dir.ownertrust; - *p++ = rec->r.dir.dirflags; - *p++ = rec->r.dir.validity; - ulongtobuf(p, rec->r.dir.valcheck); p += 4; - ulongtobuf(p, rec->r.dir.checkat); p += 4; - assert( rec->r.dir.lid == recnum ); - break; - - case RECTYPE_KEY: - ulongtobuf(p, rec->r.key.lid); p += 4; - ulongtobuf(p, rec->r.key.next); p += 4; - p += 7; - *p++ = rec->r.key.keyflags; - *p++ = rec->r.key.pubkey_algo; - *p++ = rec->r.key.fingerprint_len; - memcpy( p, rec->r.key.fingerprint, 20); p += 20; - break; - - case RECTYPE_UID: /* user id record */ - ulongtobuf(p, rec->r.uid.lid); p += 4; - ulongtobuf(p, rec->r.uid.next); p += 4; - ulongtobuf(p, rec->r.uid.prefrec); p += 4; - ulongtobuf(p, rec->r.uid.siglist); p += 4; - *p++ = rec->r.uid.uidflags; - *p++ = rec->r.uid.validity; - memcpy( p, rec->r.uid.namehash, 20 ); p += 20; - break; - - case RECTYPE_PREF: - ulongtobuf(p, rec->r.pref.lid); p += 4; - ulongtobuf(p, rec->r.pref.next); p += 4; - memcpy( p, rec->r.pref.data, 30 ); - break; - - case RECTYPE_SIG: - ulongtobuf(p, rec->r.sig.lid); p += 4; - ulongtobuf(p, rec->r.sig.next); p += 4; - for(i=0; i < SIGS_PER_RECORD; i++ ) { - ulongtobuf(p, rec->r.sig.sig[i].lid); p += 4; - *p++ = rec->r.sig.sig[i].flag; - } - break; - - case RECTYPE_SDIR: - ulongtobuf( p, rec->r.sdir.lid); p += 4; - u32tobuf( p, rec->r.sdir.keyid[0] ); p += 4; - u32tobuf( p, rec->r.sdir.keyid[1] ); p += 4; - *p++ = rec->r.sdir.pubkey_algo; - p += 3; - ulongtobuf( p, rec->r.sdir.hintlist ); - break; - - case RECTYPE_CACH: - ulongtobuf(p, rec->r.cache.lid); p += 4; - memcpy(p, rec->r.cache.blockhash, 20); p += 20; - *p++ = rec->r.cache.trustlevel; - break; case RECTYPE_HTBL: for(i=0; i < ITEMS_PER_HTBL_RECORD; i++ ) { @@ -1517,6 +1264,20 @@ tdbio_write_record( TRUSTREC *rec ) } break; + case RECTYPE_TRUST: + memcpy( p, rec->r.trust.fingerprint, 20); p += 20; + *p++ = rec->r.trust.ownertrust; + *p++ = rec->r.trust.depth; + p += 2; + 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; + break; + default: BUG(); } @@ -1524,10 +1285,8 @@ tdbio_write_record( TRUSTREC *rec ) rc = put_record_into_cache( recnum, buf ); if( rc ) ; - else if( rec->rectype == RECTYPE_KEY ) - rc = update_keyhashtbl( rec ); - else if( rec->rectype == RECTYPE_SDIR ) - rc = update_sdirhashtbl( rec ); + else if( rec->rectype == RECTYPE_TRUST ) + rc = update_trusthashtbl( rec ); return rc; } @@ -1542,10 +1301,10 @@ tdbio_delete_record( ulong recnum ) rc = tdbio_read_record( recnum, &rec, 0 ); if( rc ) ; - else if( rec.rectype == RECTYPE_KEY ) - rc = drop_from_keyhashtbl( &rec ); - else if( rec.rectype == RECTYPE_SDIR ) - rc = drop_from_sdirhashtbl( &rec ); + else if( rec.rectype == RECTYPE_TRUST ) { + rc = drop_from_hashtable( get_trusthashrec(), + rec.r.trust.fingerprint, 20, rec.recnum ); + } if( rc ) return rc; @@ -1554,7 +1313,7 @@ tdbio_delete_record( ulong recnum ) rc = tdbio_read_record( 0, &vr, RECTYPE_VER ); if( rc ) log_fatal( _("%s: error reading version record: %s\n"), - db_name, gpg_errstr(rc) ); + db_name, g10_errstr(rc) ); rec.recnum = recnum; rec.rectype = RECTYPE_FREE; @@ -1581,13 +1340,13 @@ tdbio_new_recnum() rc = tdbio_read_record( 0, &vr, RECTYPE_VER ); if( rc ) log_fatal( _("%s: error reading version record: %s\n"), - db_name, gpg_errstr(rc) ); + 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, gpg_errstr(rc) ); + db_name, g10_errstr(rc) ); return rc; } /* update dir record */ @@ -1595,7 +1354,7 @@ tdbio_new_recnum() rc = tdbio_write_record( &vr ); if( rc ) { log_error( _("%s: error writing dir record: %s\n"), - db_name, gpg_errstr(rc) ); + db_name, g10_errstr(rc) ); return rc; } /*zero out the new record */ @@ -1605,7 +1364,7 @@ tdbio_new_recnum() rc = tdbio_write_record( &rec ); if( rc ) log_fatal(_("%s: failed to zero a record: %s\n"), - db_name, gpg_errstr(rc)); + db_name, g10_errstr(rc)); } else { /* not found, append a new record */ offset = lseek( db_fd, 0, SEEK_END ); @@ -1622,131 +1381,193 @@ tdbio_new_recnum() if( lseek( db_fd, recnum * TRUST_RECORD_LEN, SEEK_SET ) == -1 ) { log_error(_("trustdb rec %lu: lseek failed: %s\n"), recnum, strerror(errno) ); - rc = GPGERR_WRITE_FILE; + 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 = GPGERR_WRITE_FILE; + rc = G10ERR_WRITE_FILE; } } if( rc ) log_fatal(_("%s: failed to append a record: %s\n"), - db_name, gpg_errstr(rc)); + db_name, g10_errstr(rc)); } return recnum ; } -/**************** - * Search the trustdb for a key which matches PK and return the dir record - * The local_id of PK is set to the correct value - */ -int -tdbio_search_dir_bypk( PKT_public_key *pk, TRUSTREC *rec ) -{ - byte fingerprint[MAX_FINGERPRINT_LEN]; - size_t fingerlen; - u32 keyid[2]; - int rc; - - keyid_from_pk( pk, keyid ); - fingerprint_from_pk( pk, fingerprint, &fingerlen ); - rc = tdbio_search_dir_byfpr( fingerprint, fingerlen, - pk->pubkey_algo, rec ); - - if( !rc ) { - if( pk->local_id && pk->local_id != rec->recnum ) - log_error("%s: found record, but LID from memory does " - "not match recnum (%lu,%lu)\n", - db_name, pk->local_id, rec->recnum ); - pk->local_id = rec->recnum; - } - return rc; -} - - static int -cmp_krec_fpr( void *dataptr, const TRUSTREC *rec ) +cmp_trec_fpr ( void *fpr, const TRUSTREC *rec ) { - const struct cmp_krec_fpr_struct *d = dataptr; - - return rec->rectype == RECTYPE_KEY - && ( !d->pubkey_algo || rec->r.key.pubkey_algo == d->pubkey_algo ) - && rec->r.key.fingerprint_len == d->fprlen - && !memcmp( rec->r.key.fingerprint, d->fpr, d->fprlen ); + return rec->rectype == RECTYPE_TRUST + && !memcmp( rec->r.trust.fingerprint, fpr, 20); } + int -tdbio_search_dir_byfpr( const byte *fingerprint, size_t fingerlen, - int pubkey_algo, TRUSTREC *rec ) +tdbio_search_trust_byfpr( const byte *fingerprint, TRUSTREC *rec ) { - struct cmp_krec_fpr_struct cmpdata; - ulong recnum; int rc; - assert( fingerlen == 20 || fingerlen == 16 ); - - /* locate the key using the hash table */ - cmpdata.pubkey_algo = pubkey_algo; - cmpdata.fpr = fingerprint; - cmpdata.fprlen = fingerlen; - rc = lookup_hashtable( get_keyhashrec(), fingerprint, fingerlen, - cmp_krec_fpr, &cmpdata, rec ); - if( !rc ) { - recnum = rec->r.key.lid; - /* Now read the dir record */ - rc = tdbio_read_record( recnum, rec, RECTYPE_DIR); - if( rc ) - log_error("%s: can't read dirrec %lu: %s\n", - db_name, recnum, gpg_errstr(rc) ); - } + /* locate the trust record using the hash table */ + rc = lookup_hashtable( get_trusthashrec(), fingerprint, 20, + cmp_trec_fpr, (void*)fingerprint, rec ); return rc; } - - -static int -cmp_sdir( void *dataptr, const TRUSTREC *rec ) +int +tdbio_search_trust_bypk (PKT_public_key *pk, TRUSTREC *rec) { - const struct cmp_xdir_struct *d = dataptr; + byte fingerprint[MAX_FINGERPRINT_LEN]; + size_t fingerlen; - return rec->rectype == RECTYPE_SDIR - && ( !d->pubkey_algo || rec->r.sdir.pubkey_algo == d->pubkey_algo ) - && rec->r.sdir.keyid[0] == d->keyid[0] - && rec->r.sdir.keyid[1] == d->keyid[1]; + fingerprint_from_pk( pk, fingerprint, &fingerlen ); + for (; fingerlen < 20; fingerlen++ ) + fingerprint[fingerlen] = 0; + return tdbio_search_trust_byfpr (fingerprint, rec); } -int -tdbio_search_sdir( u32 *keyid, int pubkey_algo, TRUSTREC *rec ) -{ - struct cmp_xdir_struct cmpdata; - int rc; - byte key[8]; - - /* locate the shadow dir record using the hash table */ - u32tobuf( key , keyid[0] ); - u32tobuf( key+4 , keyid[1] ); - cmpdata.pubkey_algo = pubkey_algo; - cmpdata.keyid[0] = keyid[0]; - cmpdata.keyid[1] = keyid[1]; - rc = lookup_hashtable( get_sdirhashrec(), key, 8, - cmp_sdir, &cmpdata, rec ); - return rc; -} - void tdbio_invalid(void) { log_error(_( "the trustdb is corrupted; please run \"gpg --fix-trustdb\".\n") ); - gpg_exit(2); + 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 = m_alloc (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 = m_realloc (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); + m_free (ottable); } + diff --git a/g10/tdbio.h b/g10/tdbio.h index a2e5404f6..f2c6bec1b 100644 --- a/g10/tdbio.h +++ b/g10/tdbio.h @@ -1,5 +1,5 @@ /* tdbio.h - Trust database I/O functions - * Copyright (C) 1998, 1999, 2000 Free Software Foundation, Inc. + * Copyright (C) 1998, 1999, 2000, 2001, 2002 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -18,8 +18,8 @@ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA */ -#ifndef GPG_TDBIO_H -#define GPG_TDBIO_H +#ifndef G10_TDBIO_H +#define G10_TDBIO_H #include "host2net.h" @@ -35,41 +35,13 @@ #define RECTYPE_VER 1 -#define RECTYPE_DIR 2 -#define RECTYPE_KEY 3 -#define RECTYPE_UID 4 -#define RECTYPE_PREF 5 -#define RECTYPE_SIG 6 -#define RECTYPE_SDIR 8 -#define RECTYPE_CACH 9 #define RECTYPE_HTBL 10 #define RECTYPE_HLST 11 +#define RECTYPE_TRUST 12 +#define RECTYPE_VALID 13 #define RECTYPE_FREE 254 -#define DIRF_CHECKED 1 /* has been checked - bits 1,2,3 are valid */ -#define DIRF_VALID 2 /* This key is valid: There is at least */ - /* one uid with a selfsignature or an revocation */ -#define DIRF_EXPIRED 4 /* the complete key has expired */ -#define DIRF_REVOKED 8 /* the complete key has been revoked */ -#define DIRF_NEWKEYS 128 /* new keys are available: we can check the sigs */ - -#define KEYF_CHECKED 1 /* This key has been checked */ -#define KEYF_VALID 2 /* This is a valid (sub)key */ -#define KEYF_EXPIRED 4 /* this key is expired */ -#define KEYF_REVOKED 8 /* this key has been revoked */ - -#define UIDF_CHECKED 1 /* user id has been checked - other bits are valid */ -#define UIDF_VALID 2 /* this is a valid user id */ -#define UIDF_REVOKED 8 /* this user id has been revoked */ - -#define SIGF_CHECKED 1 /* signature has been checked - bits 0..6 are valid */ -#define SIGF_VALID 2 /* the signature is valid */ -#define SIGF_EXPIRED 4 /* the key of this signature has expired */ -#define SIGF_REVOKED 8 /* this signature has been revoked */ -#define SIGF_IGNORED 64 /* this signature is ignored by the system */ -#define SIGF_NOPUBKEY 128 /* there is no pubkey for this sig */ - struct trust_record { int rectype; int mark; @@ -78,73 +50,21 @@ struct trust_record { ulong recnum; union { struct { /* version record: */ - byte version; /* should be 2 */ + byte version; /* should be 3 */ byte marginals; byte completes; byte cert_depth; ulong created; /* timestamp of trustdb creation */ - ulong mod_down; /* timestamp of last modification downward */ - ulong mod_up; /* timestamp of last modification upward */ - ulong keyhashtbl; + ulong nextcheck; /* timestamp of next scheduled check */ + ulong reserved; + ulong reserved2; ulong firstfree; - ulong sdirhashtbl; + ulong reserved3; + ulong trusthashtbl; } ver; struct { /* free record */ ulong next; } free; - struct { /* directory record */ - ulong lid; - ulong keylist; /* List of keys (the first is the primary key)*/ - ulong uidlist; /* list of uid records */ - ulong cacherec; /* the cache record */ - byte ownertrust; - byte dirflags; - byte validity; /* calculated trustlevel over all uids */ - ulong valcheck; /* timestamp of last validation check */ - ulong checkat; /* Check key when this time has been reached*/ - } dir; - struct { /* primary public key record */ - ulong lid; - ulong next; /* next key */ - byte keyflags; - byte pubkey_algo; - byte fingerprint_len; - byte fingerprint[20]; - } key; - struct { /* user id reord */ - ulong lid; /* point back to the directory record */ - ulong next; /* points to next user id record */ - ulong prefrec; /* recno of preference record */ - ulong siglist; /* list of valid signatures (w/o self-sig)*/ - byte uidflags; - byte validity; /* calculated trustlevel of this uid */ - byte namehash[20]; /* ripemd hash of the username */ - } uid; - struct { /* preference record */ - ulong lid; /* point back to the directory record */ - /* or 0 for a global pref record */ - ulong next; /* points to next pref record */ - byte data[ITEMS_PER_PREF_RECORD]; - } pref; - struct { /* signature record */ - ulong lid; - ulong next; /* recnno of next record or NULL for last one */ - struct { - ulong lid; /* of pubkey record of signator (0=unused) */ - byte flag; /* SIGF_xxxxx */ - } sig[SIGS_PER_RECORD]; - } sig; - struct { - ulong lid; - u32 keyid[2]; - byte pubkey_algo; - u32 hintlist; - } sdir; - struct { /* cache record */ - ulong lid; - byte blockhash[20]; - byte trustlevel; /* calculated trustlevel */ - } cache; struct { ulong item[ITEMS_PER_HTBL_RECORD]; } htbl; @@ -152,25 +72,21 @@ struct trust_record { ulong next; ulong rnum[ITEMS_PER_HLST_RECORD]; /* of another record */ } hlst; + struct { + byte fingerprint[20]; + byte ownertrust; + byte depth; + ulong validlist; + } trust; + struct { + byte namehash[20]; + ulong next; + byte validity; + } valid; } r; }; typedef struct trust_record TRUSTREC; -typedef struct { - ulong lid; /* localid */ - ulong sigrec; - ulong sig_lid; /* returned signatures LID */ - unsigned sig_flag; /* returned signature record flag */ - struct { /* internal data */ - int init_done; - int eof; - TRUSTREC rec; - ulong nextuid; - int index; - } ctl; -} SIGREC_CONTEXT; - - /*-- tdbio.c --*/ int tdbio_set_dbname( const char *new_dbname, int create ); const char *tdbio_get_dbname(void); @@ -178,8 +94,8 @@ 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); -ulong tdbio_read_modify_stamp( int modify_down ); -void tdbio_write_modify_stamp( int up, int down ); +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); @@ -187,12 +103,9 @@ int tdbio_end_transaction(void); int tdbio_cancel_transaction(void); int tdbio_delete_record( ulong recnum ); ulong tdbio_new_recnum(void); -int tdbio_search_dir_bypk( PKT_public_key *pk, TRUSTREC *rec ); -int tdbio_search_dir_byfpr( const byte *fingerprint, size_t fingerlen, - int pubkey_algo, TRUSTREC *rec ); -int tdbio_search_dir( u32 *keyid, int pubkey_algo, TRUSTREC *rec ); -int tdbio_search_sdir( u32 *keyid, int pubkey_algo, TRUSTREC *rec ); +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 /*GPG_TDBIO_H*/ +#endif /*G10_TDBIO_H*/ diff --git a/g10/textfilter.c b/g10/textfilter.c index a360ffccb..ded030d79 100644 --- a/g10/textfilter.c +++ b/g10/textfilter.c @@ -1,5 +1,5 @@ /* textfilter.c - * Copyright (C) 1998, 1999, 2000 Free Software Foundation, Inc. + * Copyright (C) 1998, 1999, 2000, 2001 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -25,9 +25,9 @@ #include <errno.h> #include <assert.h> -#include <gcrypt.h> #include "errors.h" #include "iobuf.h" +#include "memory.h" #include "util.h" #include "filter.h" #include "i18n.h" @@ -133,7 +133,7 @@ text_filter( void *opaque, int control, if( tfx->truncated ) log_error(_("can't handle text lines longer than %d characters\n"), MAX_LINELEN ); - gcry_free( tfx->buffer ); + m_free( tfx->buffer ); tfx->buffer = NULL; } else if( control == IOBUFCTRL_DESC ) @@ -147,7 +147,7 @@ text_filter( void *opaque, int control, * md is updated as required by rfc2440 */ int -copy_clearsig_text( IOBUF out, IOBUF inp, GCRY_MD_HD md, +copy_clearsig_text( IOBUF out, IOBUF inp, MD_HANDLE md, int escape_dash, int escape_from, int pgp2mode ) { unsigned maxlen; @@ -175,15 +175,15 @@ copy_clearsig_text( IOBUF out, IOBUF inp, GCRY_MD_HD md, /* update the message digest */ if( escape_dash ) { if( pending_lf ) { - gcry_md_putc( md, '\r' ); - gcry_md_putc( md, '\n' ); + md_putc( md, '\r' ); + md_putc( md, '\n' ); } - gcry_md_write( md, buffer, + md_write( md, buffer, len_without_trailing_chars( buffer, n, pgp2mode? " \r\n":" \t\r\n")); } else - gcry_md_write( md, buffer, n ); + md_write( md, buffer, n ); pending_lf = buffer[n-1] == '\n'; /* write the output */ @@ -224,7 +224,7 @@ copy_clearsig_text( IOBUF out, IOBUF inp, GCRY_MD_HD md, if( !pending_lf ) { /* make sure that the file ends with a LF */ iobuf_writestr( out, LF ); if( !escape_dash ) - gcry_md_putc( md, '\n' ); + md_putc( md, '\n' ); } if( truncated ) diff --git a/g10/trustdb.c b/g10/trustdb.c index 1fd2383c0..85b7dbcb9 100644 --- a/g10/trustdb.c +++ b/g10/trustdb.c @@ -1,5 +1,5 @@ /* trustdb.c - * Copyright (C) 1998, 1999, 2000 Free Software Foundation, Inc. + * Copyright (C) 1998, 1999, 2000, 2001, 2002 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -22,599 +22,353 @@ #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 <gcrypt.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" -#include "ttyio.h" - -#if MAX_FINGERPRINT_LEN > 20 - #error Must change structure of trustdb -#endif - -struct keyid_list { - struct keyid_list *next; - u32 keyid[2]; -}; +#include "trustdb.h" -struct local_id_item { - struct local_id_item *next; - ulong lid; - unsigned flag; -}; -struct local_id_table { - struct local_id_table *next; /* only used to keep a list of unused tables */ - struct local_id_item *items[16]; +/* + * A structure to store key identification as well as some stuff needed + * for validation + */ +struct key_item { + struct key_item *next; + unsigned int ownertrust; + u32 kid[2]; }; -typedef struct local_id_table *LOCAL_ID_TABLE; - - -struct enum_cert_paths_ctx { - int init; - int idx; -}; - +typedef struct key_item **KeyHashTable; /* see new_key_hash_table() */ -struct recno_list_struct { - struct recno_list_struct *next; - ulong recno; - int type; -}; -typedef struct recno_list_struct *RECNO_LIST; - - - -typedef struct trust_node *TN; -struct trust_node { - TN back; /* parent */ - TN list; /* list of other node (should all be of the same type)*/ - TN next; /* used to build the list */ - int is_uid; /* set if this is an uid node */ - ulong lid; /* key or uid recordnumber */ - union { - struct { - int ownertrust; - int validity; - /* helper */ - int buckstop; - } k; - struct { - int marginal_count; - int fully_count; - int validity; - } u; - } n; +/* + * 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; }; -static TN used_tns; -static int alloced_tns; -static int max_alloced_tns; - -static struct keyid_list *trusted_key_list; - -static LOCAL_ID_TABLE new_lid_table(void); -static int ins_lid_table_item( LOCAL_ID_TABLE tbl, ulong lid, unsigned flag ); -static int qry_lid_table_flag( LOCAL_ID_TABLE tbl, ulong lid, unsigned *flag ); - - -static int propagate_validity( TN root, TN node, - int (*add_fnc)(ulong), unsigned *retflgs ); - -static void print_user_id( FILE *fp, const char *text, u32 *keyid ); -static int do_check( TRUSTREC *drec, unsigned *trustlevel, - const char *nhash, int (*add_fnc)(ulong), - unsigned *retflgs); -static int get_dir_record( PKT_public_key *pk, TRUSTREC *rec ); -static int do_update_trust_record( KBNODE keyblock, TRUSTREC *drec, - int sigs_only, int *modified ); -static int check_trust_record( TRUSTREC *drec, int sigs_only ); -static void mark_fresh_keys(void); - -/* a table used to keep track of ultimately trusted keys - * which are the ones from our secrings and the trusted keys */ -static LOCAL_ID_TABLE ultikey_table; - - -/* a table to keep track of newly importted keys. This one is - * create by the insert_trust_record function and from time to time - * used to verify key signature which have been done with these new keys */ -static LOCAL_ID_TABLE fresh_imported_keys; -static int fresh_imported_keys_count; -#define FRESH_KEY_CHECK_THRESHOLD 200 - -/* list of unused lid items and tables */ -static LOCAL_ID_TABLE unused_lid_tables; -static struct local_id_item *unused_lid_items; - +/* control information for the trust DB */ static struct { int init; int level; char *dbname; } trustdb_args; - -/********************************************** - *********** record read write ************** - **********************************************/ - - -/**************** - * 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 ) - return; - log_error(_("trust record %lu, req type %d: read failed: %s\n"), - recno, rectype, gpg_errstr(rc) ); - tdbio_invalid(); -} +/* 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; -/**************** - * 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, gpg_errstr(rc) ); - tdbio_invalid(); -} - -/**************** - * Delete a record but die on error - */ -static void -delete_record( ulong recno ) -{ - int rc = tdbio_delete_record( recno ); - if( !rc ) - return; - log_error(_("trust record %lu: delete failed: %s\n"), - recno, gpg_errstr(rc) ); - tdbio_invalid(); -} - -/**************** - * sync the db - */ -static void -do_sync(void) -{ - int rc = tdbio_sync(); - if( !rc ) - return; - log_error(_("trustdb: sync failed: %s\n"), gpg_errstr(rc) ); - gpg_exit(2); -} - +static int validate_keys (int interactive); /********************************************** - ***************** helpers ****************** + ************* some helpers ******************* **********************************************/ - -static LOCAL_ID_TABLE -new_lid_table(void) +static struct key_item * +new_key_item (void) { - LOCAL_ID_TABLE a; - - a = unused_lid_tables; - if( a ) { - unused_lid_tables = a->next; - memset( a, 0, sizeof *a ); - } - else - a = gcry_xcalloc( 1, sizeof *a ); - return a; + struct key_item *k; + + k = m_alloc_clear (sizeof *k); + return k; } -#if 0 static void -release_lid_table( LOCAL_ID_TABLE tbl ) +release_key_items (struct key_item *k) { - struct local_id_item *a, *a2; - int i; - - for(i=0; i < 16; i++ ) { - for(a=tbl->items[i]; a; a = a2 ) { - a2 = a->next; - a->next = unused_lid_items; - unused_lid_items = a; - } - } - tbl->next = unused_lid_tables; - unused_lid_tables = tbl; -} -#endif - + struct key_item *k2; -/**************** - * Remove all items from a LID table - */ -static void -clear_lid_table( LOCAL_ID_TABLE tbl ) -{ - struct local_id_item *a, *a2; - int i; - - for(i=0; i < 16; i++ ) { - for(a=tbl->items[i]; a; a = a2 ) { - a2 = a->next; - a->next = unused_lid_items; - unused_lid_items = a; - } - tbl->items[i] = NULL; + for (; k; k = k2) + { + k2 = k->next; + m_free (k); } } - -/**************** - * Add a new item to the table or return 1 if we already have this item +/* + * 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 int -ins_lid_table_item( LOCAL_ID_TABLE tbl, ulong lid, unsigned flag ) +static KeyHashTable +new_key_hash_table (void) { - struct local_id_item *a; - - for( a = tbl->items[lid & 0x0f]; a; a = a->next ) - if( a->lid == lid ) - return 1; - a = unused_lid_items; - if( a ) - unused_lid_items = a->next; - else - a = gcry_xmalloc( sizeof *a ); - a->lid = lid; - a->flag = flag; - a->next = tbl->items[lid & 0x0f]; - tbl->items[lid & 0x0f] = a; - return 0; + struct key_item **tbl; + + tbl = m_alloc_clear (1024 * sizeof *tbl); + return tbl; } -static int -qry_lid_table_flag( LOCAL_ID_TABLE tbl, ulong lid, unsigned *flag ) +static void +release_key_hash_table (KeyHashTable tbl) { - struct local_id_item *a; + int i; - for( a = tbl->items[lid & 0x0f]; a; a = a->next ) - if( a->lid == lid ) { - if( flag ) - *flag = a->flag; - return 0; - } - return -1; + if (!tbl) + return; + for (i=0; i < 1024; i++) + release_key_items (tbl[i]); + m_free (tbl); } - -static TN -new_tn(void) +/* + * Returns: True if the keyID is in the given hash table + */ +static int +test_key_hash_table (KeyHashTable tbl, u32 *kid) { - TN t; + struct key_item *k; - if( used_tns ) { - t = used_tns; - used_tns = t->next; - memset( t, 0, sizeof *t ); - } - else - t = gcry_xcalloc( 1, sizeof *t ); - if( ++alloced_tns > max_alloced_tns ) - max_alloced_tns = alloced_tns; - return t; + 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 -release_tn( TN t ) +add_key_hash_table (KeyHashTable tbl, u32 *kid) { - if( t ) { - t->next = used_tns; - used_tns = t; - alloced_tns--; - } -} + 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_tn_tree( TN kr ) +release_key_array ( struct key_array *keys ) { - TN kr2; + struct key_array *k; - for( ; kr; kr = kr2 ) { - release_tn_tree( kr->list ); - kr2 = kr->next; - release_tn( kr ); + if (keys) { + for (k=keys; k->keyblock; k++) + release_kbnode (k->keyblock); + m_free (keys); } } + +/********************************************* + ********** Initialization ***************** + *********************************************/ - -/********************************************** - ****** access by LID and other helpers ******* - **********************************************/ -/**************** - * Return the keyid from the primary key identified by LID. +/* + * 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. */ -int -keyid_from_lid( ulong lid, u32 *keyid ) +void +register_trusted_key( const char *string ) { - TRUSTREC rec; - int rc; - - init_trustdb(); - keyid[0] = keyid[1] = 0; - rc = tdbio_read_record( lid, &rec, 0 ); - if( rc ) { - log_error(_("error reading dir record for LID %lu: %s\n"), - lid, gpg_errstr(rc)); - return GPGERR_TRUSTDB; - } - if( rec.rectype == RECTYPE_SDIR ) - return 0; - if( rec.rectype != RECTYPE_DIR ) { - log_error(_("lid %lu: expected dir record, got type %d\n"), - lid, rec.rectype ); - return GPGERR_TRUSTDB; - } - if( !rec.r.dir.keylist ) { - log_error(_("no primary key for LID %lu\n"), lid ); - return GPGERR_TRUSTDB; - } - rc = tdbio_read_record( rec.r.dir.keylist, &rec, RECTYPE_KEY ); - if( rc ) { - log_error(_("error reading primary key for LID %lu: %s\n"), - lid, gpg_errstr(rc)); - return GPGERR_TRUSTDB; - } - keyid_from_fingerprint( rec.r.key.fingerprint, rec.r.key.fingerprint_len, - keyid ); - - return 0; -} + KEYDB_SEARCH_DESC desc; + struct key_item *k; + if (classify_user_id (string, &desc) != KEYDB_SEARCH_MODE_LONG_KID ) { + log_error(_("`%s' is not a valid long keyID\n"), string ); + return; + } -ulong -lid_from_keyblock( KBNODE keyblock ) -{ - KBNODE node = find_kbnode( keyblock, PKT_PUBLIC_KEY ); - PKT_public_key *pk; - if( !node ) - BUG(); - pk = node->pkt->pkt.public_key; - if( !pk->local_id ) { - TRUSTREC rec; - init_trustdb(); - - get_dir_record( pk, &rec ); - } - return pk->local_id; + k = new_key_item (); + k->kid[0] = desc.u.kid[0]; + k->kid[1] = desc.u.kid[1]; + k->next = user_utk_list; + user_utk_list = k; } - +/* + * Helper to add a key to the global list of ultimately trusted keys. + * Retruns: true = inserted, false = already in in list. + */ static int -get_dir_record( PKT_public_key *pk, TRUSTREC *rec ) +add_utk (u32 *kid) { - int rc=0; + struct key_item *k; - if( pk->local_id ) { - read_record( pk->local_id, rec, RECTYPE_DIR ); - } - else { /* no local_id: scan the trustdb */ - if( (rc=tdbio_search_dir_bypk( pk, rec )) && rc != -1 ) - log_error(_("get_dir_record: search_record failed: %s\n"), - gpg_errstr(rc)); + for (k = utk_list; k; k = k->next) + { + if (k->kid[0] == kid[0] && k->kid[1] == kid[1]) + { + return 0; + } } - return rc; -} -static ulong -lid_from_keyid_no_sdir( u32 *keyid ) -{ - PKT_public_key *pk = gcry_xcalloc( 1, sizeof *pk ); - TRUSTREC rec; - ulong lid = 0; - int rc; - - rc = get_pubkey( pk, keyid ); - if( !rc ) { - if( pk->local_id ) - lid = pk->local_id; - else { - rc = tdbio_search_dir_bypk( pk, &rec ); - if( !rc ) - lid = rec.recnum; - } - } - free_public_key( pk ); - return lid; + 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 %08lX: accepted as trusted key\n"), (ulong)kid[1]); + return 1; } - -/*********************************************** - ************* Initialization **************** - ***********************************************/ - -void -register_trusted_key( const char *string ) +/**************** + * Verify that all our secret keys are usable and put them into the utk_list. + */ +static void +verify_own_keys(void) { - u32 keyid[2]; - struct keyid_list *r; + TRUSTREC rec; + ulong recnum; + int rc; + struct key_item *k; + int hint_shown = 0; + + if (utk_list) + return; - if( classify_user_id( string, keyid, NULL, NULL, NULL ) != 11 ) { - log_error(_("'%s' is not a valid long keyID\n"), string ); - 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 %08lX occurs more than once in the trustdb\n"), + (ulong)kid[1]); + } + } + + /* the --trusted-key option is again deprecated; however we automagically + * add those keys to 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 %08lX: no public key for trusted key - skipped\n"), + (ulong)k->kid[1] ); + } + else { + update_ownertrust (&pk, + ((get_ownertrust (&pk) & ~TRUST_MASK) + | TRUST_ULTIMATE )); + release_public_key_parts (&pk); + } + if (!hint_shown) + { + log_info ("the --trusted-key option is now obsolete; " + "use the --edit command instead.\n"); + log_info ("given keys will be marked as trusted\n"); + hint_shown = 1; + } + log_info ("key %08lX marked as ultimately trusted\n", + (ulong)k->kid[1]); + } } - for( r = trusted_key_list; r; r = r->next ) - if( r->keyid[0] == keyid[0] && r->keyid[1] == keyid[1] ) - return; - r = gcry_xmalloc( sizeof *r ); - r->keyid[0] = keyid[0]; - r->keyid[1] = keyid[1]; - r->next = trusted_key_list; - trusted_key_list = r; -} + /* 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 -add_ultimate_key( PKT_public_key *pk, u32 *keyid ) +read_record (ulong recno, TRUSTREC *rec, int rectype ) { - int rc; - - /* first make sure that the pubkey is in the trustdb */ - rc = query_trust_record( pk ); - if( rc == -1 && opt.dry_run ) - return; - if( rc == -1 ) { /* put it into the trustdb */ - rc = insert_trust_record_by_pk( pk ); - if( rc ) { - log_error(_("key %08lX: can't put it into the trustdb\n"), - (ulong)keyid[1] ); - return; - } + 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(); } - else if( rc ) { - log_error(_("key %08lX: query record failed\n"), (ulong)keyid[1] ); - return; + if (rectype != rec->rectype) + { + log_error(_("trust record %lu is not of requested type %d\n"), + rec->recnum, rectype); + tdbio_invalid(); } - - if( DBG_TRUST ) - log_debug("key %08lX.%lu: stored into ultikey_table\n", - (ulong)keyid[1], pk->local_id ); - - if( ins_lid_table_item( ultikey_table, pk->local_id, 0 ) ) - log_error(_("key %08lX: already in trusted key table\n"), - (ulong)keyid[1]); - else if( opt.verbose > 1 ) - log_info(_("key %08lX: accepted as trusted key.\n"), - (ulong)keyid[1]); - } -/**************** - * Verify that all our public keys are in the trustdb. +/* + * Write a record and die on error */ -static int -verify_own_keys(void) +static void +write_record (TRUSTREC *rec) { - int rc; - void *enum_context = NULL; - PKT_secret_key *sk = gcry_xcalloc( 1, sizeof *sk ); - PKT_public_key *pk = gcry_xcalloc( 1, sizeof *pk ); - u32 keyid[2]; - struct keyid_list *kl; - - - /* put the trusted keys into the ultikey table */ - for( kl = trusted_key_list; kl; kl = kl->next ) { - keyid[0] = kl->keyid[0]; - keyid[1] = kl->keyid[1]; - /* get the public key */ - memset( pk, 0, sizeof *pk ); - rc = get_pubkey( pk, keyid ); - if( rc ) { - log_info(_("key %08lX: no public key for trusted key - skipped\n"), - (ulong)keyid[1] ); - } - else { - add_ultimate_key( pk, keyid ); - release_public_key_parts( pk ); - } + 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(); } - - /* And now add all secret keys to the ultikey table */ - while( !(rc=enum_secret_keys( &enum_context, sk, 0 ) ) ) { - int have_pk = 0; - - keyid_from_sk( sk, keyid ); - - if( DBG_TRUST ) - log_debug("key %08lX: checking secret key\n", (ulong)keyid[1] ); - - if( !opt.quiet && is_secret_key_protected( sk ) < 1 ) - log_info(_("NOTE: secret key %08lX is NOT protected.\n"), - (ulong)keyid[1] ); - - for( kl = trusted_key_list; kl; kl = kl->next ) { - if( kl->keyid[0] == keyid[0] && kl->keyid[1] == keyid[1] ) - goto skip; /* already in trusted key table */ - } - - /* see whether we can access the public key of this secret key */ - memset( pk, 0, sizeof *pk ); - rc = get_pubkey( pk, keyid ); - if( rc ) { - log_info(_("key %08lX: secret key without public key - skipped\n"), - (ulong)keyid[1] ); - goto skip; - } - have_pk=1; - - if( cmp_public_secret_key( pk, sk ) ) { - log_info(_("key %08lX: secret and public key don't match\n"), - (ulong)keyid[1] ); - goto skip; - } - - add_ultimate_key( pk, keyid ); - - skip: - release_secret_key_parts( sk ); - if( have_pk ) - release_public_key_parts( pk ); - } - if( rc != -1 ) - log_error(_("enumerate secret keys failed: %s\n"), gpg_errstr(rc) ); - else - rc = 0; - - /* release the trusted keyid table */ - { struct keyid_list *kl2; - for( kl = trusted_key_list; kl; kl = kl2 ) { - kl2 = kl->next; - gcry_free( kl ); - } - trusted_key_list = NULL; - } - - enum_secret_keys( &enum_context, NULL, 0 ); /* free context */ - free_secret_key( sk ); - free_public_key( pk ); - return rc; } - +/* + * 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); + } +} /**************** @@ -629,2198 +383,1268 @@ setup_trustdb( int level, const char *dbname ) if( trustdb_args.init ) return 0; trustdb_args.level = level; - trustdb_args.dbname = dbname? gcry_xstrdup(dbname): NULL; + trustdb_args.dbname = dbname? m_strdup(dbname): NULL; return 0; } void init_trustdb() { - int rc=0; - int level = trustdb_args.level; - const char* dbname = trustdb_args.dbname; - - if( trustdb_args.init ) - return; + int rc=0; + int level = trustdb_args.level; + const char* dbname = trustdb_args.dbname; - trustdb_args.init = 1; - - if( !ultikey_table ) - ultikey_table = new_lid_table(); - - if( !level || level==1 ) { - rc = tdbio_set_dbname( dbname, !!level ); - if( !rc ) { - if( !level ) - return; + if( trustdb_args.init ) + return; - /* verify that our own keys are in the trustDB - * or move them to the trustdb. */ - rc = verify_own_keys(); + trustdb_args.init = 1; - /* should we check whether there is no other ultimately trusted - * key in the database? */ - } + if ( !level || level==1) + { + rc = tdbio_set_dbname( dbname, !!level ); + if( !rc ) + { + if( !level ) + return; + + /* verify that our own keys are in the trustDB + * or move them to the trustdb. */ + verify_own_keys(); + + /* should we check whether there is no other ultimately trusted + * key in the database? */ + } } - else - BUG(); - if( rc ) - log_fatal("can't init trustdb: %s\n", gpg_errstr(rc) ); + else + BUG(); + if( rc ) + log_fatal("can't init trustdb: %s\n", g10_errstr(rc) ); } -/**************** - * This function should be called in certain cases to sync the internal state - * of the trustdb with the file image. Currently it is needed after - * a sequence of insert_trust_record() calls. - */ -void -sync_trustdb() -{ - if( fresh_imported_keys && fresh_imported_keys_count ) - mark_fresh_keys(); -} - - /*********************************************** ************* Print helpers **************** ***********************************************/ -static void -print_user_id( FILE *fp, const char *text, u32 *keyid ) -{ - char *p; - size_t n; - - p = get_user_id( keyid, &n ); - if( fp ) { - fprintf( fp, "%s \"", text ); - print_utf8_string( fp, p, n ); - putc('\"', fp); - putc('\n', fp); - } - else { - tty_printf( "%s \"", text ); - tty_print_utf8_string( p, n ); - tty_printf( "\"\n" ); - } - gcry_free(p); -} - - /**************** * This function returns a letter for a trustvalue Trust flags * are ignore. */ int -trust_letter( unsigned value ) +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 0 ; + 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 0; } } -#if 0 -static void -print_path( int pathlen, TN ME .........., FILE *fp, ulong highlight ) -{ - int rc, c, i; - u32 keyid[2]; - char *p; - size_t n; - - for( i = 0; i < pathlen; i++ ) { - if( highlight ) - fputs(highlight == path[i].lid? "* ":" ", fp ); - rc = keyid_from_lid( path[i].lid, keyid ); - if( rc ) - fprintf(fp, "????????.%lu:", path[i].lid ); - else - fprintf(fp,"%08lX.%lu:", (ulong)keyid[1], path[i].lid ); - c = trust_letter(path[i].otrust); - if( c ) - putc( c, fp ); - else - fprintf( fp, "%02x", path[i].otrust ); - putc('/', fp); - c = trust_letter(path[i].trust); - if( c ) - putc( c, fp ); - else - fprintf( fp, "%02x", path[i].trust ); - putc(' ', fp); - p = get_user_id( keyid, &n ); - putc(' ', fp); - putc('\"', fp); - print_utf8_string( fp, p, n > 40? 40:n ); - putc('\"', fp); - gcry_free(p); - putc('\n', fp ); - } -} -#endif - - -static void -print_default_uid( FILE *fp, ulong lid ) -{ - u32 keyid[2]; - - if( !keyid_from_lid( lid, keyid ) ) - print_user_id( fp, "", keyid ); -} - +/**************** + * 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.batch && !opt.answer_yes) + { + ulong scheduled; + + scheduled = tdbio_read_nextcheck (); + if (!scheduled) + { + log_info (_("no need for a trustdb check\n")); + return; + } -static void -print_uid_from_keyblock( FILE *fp, KBNODE keyblock, ulong urecno ) -{ - TRUSTREC urec; - KBNODE node; - byte uhash[20]; - - read_record( urecno, &urec, RECTYPE_UID ); - for( node=keyblock; node; node = node->next ) { - if( node->pkt->pkttype == PKT_USER_ID ) { - PKT_user_id *uidpkt = node->pkt->pkt.user_id; - - if( uidpkt->photo ) { - gcry_md_hash_buffer( GCRY_MD_RMD160, uhash, - uidpkt->photo, uidpkt->photolen ); - } - else { - gcry_md_hash_buffer( GCRY_MD_RMD160, uhash, - uidpkt->name, uidpkt->len ); - } - if( !memcmp( uhash, urec.r.uid.namehash, 20 ) ) { - print_string( fp, uidpkt->name, uidpkt->len, ':' ); - return; - } - } + if (scheduled > make_timestamp ()) + { + log_info (_("next trustdb check due at %s\n"), + strtimestamp (scheduled)); + return; + } } - fputs("[?]", fp ); + validate_keys (0); } - -static void -dump_tn_tree( FILE *fp, int level, TN tree ) +/* + * Recreate the WoT. + */ +void +update_trustdb() { - TN kr, ur; - - for( kr=tree; kr; kr = kr->next ) { - if( fp ) { - fprintf( fp, "%*s", level*4, "" ); - fprintf( fp, "K%lu(ot=%d,val=%d) ", kr->lid, - kr->n.k.ownertrust, - kr->n.k.validity ); - } - else { - tty_printf("%*s", level*4, "" ); - tty_printf("K%lu(ot=%d,val=%d) ", kr->lid, - kr->n.k.ownertrust, - kr->n.k.validity ); - } - print_default_uid( fp, kr->lid ); - for( ur=kr->list; ur; ur = ur->next ) { - if( fp ) { - fprintf(fp, "%*s ", level*4, "" ); - fprintf(fp, "U%lu(mc=%d,fc=%d,val=%d)\n", ur->lid, - ur->n.u.marginal_count, - ur->n.u.fully_count, - ur->n.u.validity - ); - } - else { - tty_printf("%*s ", level*4, "" ); - tty_printf("U%lu(mc=%d,fc=%d,val=%d)\n", ur->lid, - ur->n.u.marginal_count, - ur->n.u.fully_count, - ur->n.u.validity - ); - } - dump_tn_tree( fp, level+1, ur->list ); - } - } + init_trustdb(); + validate_keys (1); } -/**************** - * Special version of dump_tn_tree, which prints it colon delimited. - * Format: - * level:keyid:type:recno:ot:val:mc:cc:name: - * With TYPE = U for a user ID - * K for a key - * The RECNO is either the one of the dir record or the one of the uid record. - * OT is the the usual trust letter and only availabel on K lines. - * VAL is the calcualted validity - * MC is the marginal trust counter and only available on U lines - * CC is the same for the complete count - * NAME ist the username and only printed on U lines - */ -static void -dump_tn_tree_with_colons( int level, TN tree ) +void +revalidation_mark (void) { - TN kr, ur; - - for( kr=tree; kr; kr = kr->next ) { - KBNODE kb = NULL; - u32 kid[2]; - - keyid_from_lid( kr->lid, kid ); - get_keyblock_bylid( &kb, kr->lid ); - - printf( "%d:%08lX%08lX:K:%lu:%c:%c::::\n", - level, (ulong)kid[0], (ulong)kid[1], kr->lid, - trust_letter( kr->n.k.ownertrust ), - trust_letter( kr->n.k.validity ) ); - for( ur=kr->list; ur; ur = ur->next ) { - printf( "%d:%08lX%08lX:U:%lu::%c:%d:%d:", - level, (ulong)kid[0], (ulong)kid[1], ur->lid, - trust_letter( kr->n.u.validity ), - ur->n.u.marginal_count, - ur->n.u.fully_count ); - print_uid_from_keyblock( stdout, kb, ur->lid ); - putchar(':'); - putchar('\n'); - dump_tn_tree_with_colons( level+1, ur->list ); - } - release_kbnode( kb ); - } + 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; } - /*********************************************** - ************* trustdb maintenance *********** + *********** Ownertrust et al. **************** ***********************************************/ -/**************** - * Create or update shadow dir record and return the LID of the record - */ -static ulong -create_shadow_dir( PKT_signature *sig ) -{ - TRUSTREC sdir; - int rc; - - /* first see whether we already have such a record */ - rc = tdbio_search_sdir( sig->keyid, sig->pubkey_algo, &sdir ); - if( rc && rc != -1 ) { - log_error("tdbio_search_sdir failed: %s\n", gpg_errstr(rc)); - tdbio_invalid(); - } - if( rc == -1 ) { /* not found: create */ - memset( &sdir, 0, sizeof sdir ); - sdir.recnum = tdbio_new_recnum(); - sdir.rectype= RECTYPE_SDIR; - sdir.r.sdir.lid = sdir.recnum; - sdir.r.sdir.keyid[0] = sig->keyid[0]; - sdir.r.sdir.keyid[1] = sig->keyid[1]; - sdir.r.sdir.pubkey_algo = sig->pubkey_algo; - write_record( &sdir ); - } - return sdir.recnum; -} - - -static ulong -find_or_create_lid( PKT_signature *sig ) -{ - ulong lid; - - lid = lid_from_keyid_no_sdir( sig->keyid ); - if( !lid ) - lid = create_shadow_dir( sig ); - return lid; +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; } - /**************** - * Check the validity of a key and calculate the keyflags - * keynode points to - * a node with a [sub]key. mainkid has the key ID of the primary key - * keyblock is the complete keyblock which is needed for signature - * checking. LID and PK is only used in verbose mode. + * Return the assigned ownertrust value for the given public key. + * The key should be the primary key. */ -static unsigned int -check_keybinding( KBNODE keyblock, KBNODE keynode, u32 *mainkid, - ulong lid, PKT_public_key *pk ) +unsigned int +get_ownertrust ( PKT_public_key *pk) { - KBNODE node; - int keybind_seen = 0; - int revoke_seen = 0; - unsigned int keyflags=0; - int is_main = (keynode->pkt->pkttype == PKT_PUBLIC_KEY); - int rc; - - if( DBG_TRUST ) - log_debug("check_keybinding: %08lX.%lu\n", - (ulong)mainkid[1], lid ); - - if( is_main ) { - /* a primary key is always valid (user IDs are handled elsewhere)*/ - keyflags = KEYF_CHECKED | KEYF_VALID; - } - - for( node=keynode->next; node; node = node->next ) { - PKT_signature *sig; - - if( node->pkt->pkttype == PKT_PUBLIC_SUBKEY ) - break; /* ready */ - if( node->pkt->pkttype != PKT_SIGNATURE ) - continue; /* don't care about other packets */ - - sig = node->pkt->pkt.signature; - - if( mainkid[0] != sig->keyid[0] || mainkid[1] != sig->keyid[1] ) - continue; /* we only care about self-signatures */ - - if( sig->sig_class == 0x18 && !keybind_seen && !is_main ) { - /* check until we find a valid keybinding */ - rc = check_key_signature( keyblock, node, NULL ); - if( !rc ) { - if( opt.verbose ) - log_info(_("key %08lX.%lu: Good subkey binding\n"), - (ulong)keyid_from_pk(pk,NULL), lid ); - keyflags |= KEYF_CHECKED | KEYF_VALID; - } - else { - log_info(_( - "key %08lX.%lu: Invalid subkey binding: %s\n"), - (ulong)keyid_from_pk(pk,NULL), lid, gpg_errstr(rc) ); - keyflags |= KEYF_CHECKED; - keyflags &= ~KEYF_VALID; - } - keybind_seen = 1; - } - else if( sig->sig_class == 0x20 && !revoke_seen ) { - /* this is a key revocation certificate: check it */ - rc = check_key_signature( keyblock, node, NULL ); - if( !rc ) { - if( opt.verbose ) - log_info(_("key %08lX.%lu: Valid key revocation\n"), - (ulong)keyid_from_pk(pk, NULL), lid ); - keyflags |= KEYF_REVOKED; - } - else { - log_info(_( - "key %08lX.%lu: Invalid key revocation: %s\n"), - (ulong)keyid_from_pk(pk,NULL), lid, gpg_errstr(rc) ); - } - revoke_seen = 1; - } - else if( sig->sig_class == 0x28 && !revoke_seen && !is_main ) { - /* this is a subkey revocation certificate: check it */ - rc = check_key_signature( keyblock, node, NULL ); - if( !rc ) { - if( opt.verbose ) - log_info(_( - "key %08lX.%lu: Valid subkey revocation\n"), - (ulong)keyid_from_pk(pk,NULL), lid ); - keyflags |= KEYF_REVOKED; - } - else { - log_info(_( - "key %08lX.%lu: Invalid subkey binding: %s\n"), - (ulong)keyid_from_pk(pk,NULL), lid, gpg_errstr(rc) ); - } - revoke_seen = 1; - } - /* Hmmm: should we handle direct key signatures here? */ + 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 keyflags; + + return rec.r.trust.ownertrust; } - -static ulong -make_key_records( KBNODE keyblock, ulong lid, u32 *keyid, int *mainrev ) +/* + * Same as get_ownertrust but return a trust letter instead of an value. + */ +int +get_ownertrust_info (PKT_public_key *pk) { - TRUSTREC *krecs, **kend, *k, *k2; - KBNODE node; - PKT_public_key *pk; - byte fpr[MAX_FINGERPRINT_LEN]; - size_t fprlen; - ulong keyrecno; - - *mainrev = 0; - krecs = NULL; kend = &krecs; - for( node=keyblock; node; node = node->next ) { - if( node->pkt->pkttype != PKT_PUBLIC_KEY - && node->pkt->pkttype != PKT_PUBLIC_SUBKEY ) - continue; - pk = node->pkt->pkt.public_key; - fingerprint_from_pk( pk, fpr, &fprlen ); - - /* create the key record */ - k = gcry_xcalloc( 1, sizeof *k ); - k->rectype = RECTYPE_KEY; - k->r.key.lid = lid; - k->r.key.pubkey_algo = pk->pubkey_algo; - k->r.key.fingerprint_len = fprlen; - memcpy(k->r.key.fingerprint, fpr, fprlen ); - k->recnum = tdbio_new_recnum(); - *kend = k; - kend = &k->next; - - k->r.key.keyflags = check_keybinding( keyblock, node, keyid, lid, pk ); - if( (k->r.key.keyflags & KEYF_REVOKED) - && node->pkt->pkttype == PKT_PUBLIC_KEY ) - *mainrev = 1; - } + unsigned int otrust; + int c; - keyrecno = krecs? krecs->recnum : 0; - /* write the keylist and release the memory */ - for( k = krecs; k ; k = k2 ) { - if( k->next ) - k->r.key.next = k->next->recnum; - write_record( k ); - k2 = k->next; - gcry_free( k ); - } - return keyrecno; + otrust = get_ownertrust (pk); + c = trust_letter( (otrust & TRUST_MASK) ); + if( !c ) + c = '?'; + return c; } - -/**************** - * Check the validity of a user ID and calculate the uidflags - * keynode points to a node with a user ID. - * mainkid has the key ID of the primary key, keyblock is the complete - * keyblock which is needed for signature checking. - * Returns: The uid flags and the self-signature which is considered to - * be the most current. +/* + * Set the trust value of the given public key to the new value. + * The key should be a primary one. */ -static unsigned int -check_uidsigs( KBNODE keyblock, KBNODE keynode, u32 *mainkid, ulong lid, - PKT_signature **bestsig ) -{ - KBNODE node; - unsigned int uidflags = 0; - PKT_signature *sig; - PKT_signature *selfsig = NULL; /* the latest valid self signature */ - int rc; - - if( DBG_TRUST ) { - PKT_user_id *uid; - log_debug("check_uidsigs: %08lX.%lu \"", - (ulong)mainkid[1], lid ); - assert(keynode->pkt->pkttype == PKT_USER_ID ); - uid = keynode->pkt->pkt.user_id; - print_string( log_stream(), uid->name, uid->len, '\"' ); - fputs("\"\n", log_stream()); +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; - /* first we check only the selfsignatures */ - for( node=keynode->next; node; node = node->next ) { - if( node->pkt->pkttype == PKT_USER_ID - || node->pkt->pkttype == PKT_PUBLIC_SUBKEY ) - break; /* ready */ - if( node->pkt->pkttype != PKT_SIGNATURE ) - continue; /* don't care about other packets */ - sig = node->pkt->pkt.signature; - if( mainkid[0] != sig->keyid[0] || mainkid[1] != sig->keyid[1] ) - continue; /* we only care about self-signatures for now */ - - if( (sig->sig_class&~3) == 0x10 ) { /* regular self signature */ - rc = check_key_signature( keyblock, node, NULL ); - if( !rc ) { - if( opt.verbose ) - log_info( "uid %08lX.%lu: %s\n", - (ulong)mainkid[1], lid, _("Good self-signature") ); - uidflags |= UIDF_CHECKED | UIDF_VALID; - if( !selfsig ) - selfsig = sig; /* use the first valid sig */ - else if( sig->timestamp > selfsig->timestamp - && sig->sig_class >= selfsig->sig_class ) - selfsig = sig; /* but this one is newer */ - } - else { - log_info( "uid %08lX: %s: %s\n", - (ulong)mainkid[1], _("Invalid self-signature"), - gpg_errstr(rc) ); - uidflags |= UIDF_CHECKED; - } - } - } + if (DBG_TRUST) + log_debug ("insert ownertrust %u\n", new_trust ); - /* and now check for revocations - we must do this after the - * self signature check because a self-signature which is newer - * than a revocation makes the revocation invalid. - * RFC2440 is quiet about tis but I feel this is reasonable for - * non-primary-key revocations. */ - for( node=keynode->next; node; node = node->next ) { - if( node->pkt->pkttype == PKT_USER_ID - || node->pkt->pkttype == PKT_PUBLIC_SUBKEY ) - break; /* ready */ - if( node->pkt->pkttype != PKT_SIGNATURE ) - continue; /* don't care about other packets */ - sig = node->pkt->pkt.signature; - if( mainkid[0] != sig->keyid[0] || mainkid[1] != sig->keyid[1] ) - continue; /* we only care about self-signatures for now */ - - if( sig->sig_class == 0x30 ) { /* cert revocation */ - rc = check_key_signature( keyblock, node, NULL ); - if( !rc && selfsig && selfsig->timestamp > sig->timestamp ) { - log_info( "uid %08lX.%lu: %s\n", - (ulong)mainkid[1], lid, - _("Valid user ID revocation skipped " - "due to a newer self signature") ); - } - else if( !rc ) { - if( opt.verbose ) - log_info( "uid %08lX.%lu: %s\n", - (ulong)mainkid[1], lid, _("Valid user ID revocation") ); - uidflags |= UIDF_CHECKED | UIDF_VALID | UIDF_REVOKED; - } - else { - log_info("uid %08lX: %s: %s\n", - (ulong)mainkid[1], _("Invalid user ID revocation"), - gpg_errstr(rc) ); - } - } + 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 (); } - - *bestsig = selfsig; - return uidflags; } - -static unsigned int -check_sig_record( KBNODE keyblock, KBNODE signode, - ulong siglid, int sigidx, u32 *keyid, ulong lid, - u32 *r_expiretime, int *mod_down, int *mod_up ) -{ - PKT_signature *sig = signode->pkt->pkt.signature; - unsigned int sigflag = 0; - TRUSTREC tmp; - int revocation=0, expired=0, rc; - - if( DBG_TRUST ) - log_debug("check_sig_record: %08lX.%lu %lu[%d]\n", - (ulong)keyid[1], lid, siglid, sigidx ); - *r_expiretime = 0; - if( (sig->sig_class&~3) == 0x10 ) /* regular certification */ - ; - else if( sig->sig_class == 0x30 ) /* cert revocation */ - revocation = 1; - else - return SIGF_CHECKED | SIGF_IGNORED; - - read_record( siglid, &tmp, 0 ); - if( tmp.rectype == RECTYPE_DIR ) { - /* the public key is in the trustdb: check sig */ - rc = check_key_signature2( keyblock, signode, NULL, - r_expiretime, &expired ); - if( !rc ) { /* valid signature */ - if( opt.verbose ) - log_info("sig %08lX.%lu/%lu[%d]/%08lX: %s\n", - (ulong)keyid[1], lid, siglid, sigidx, - (ulong)sig->keyid[1], - revocation? _("Valid certificate revocation") - : _("Good certificate") ); - sigflag |= SIGF_CHECKED | SIGF_VALID; - if( expired ) { - sigflag |= SIGF_EXPIRED; - /* We have to reset the expiretime, so that this signature - * does not get checked over and over due to the reached - * expiretime */ - *r_expiretime = 0; - } - if( revocation ) { - sigflag |= SIGF_REVOKED; - *mod_down = 1; - } - else - *mod_up = 1; - } - else if( rc == GPGERR_NO_PUBKEY ) { - /* This may happen if the key is still in the trustdb - * but not available in the keystorage */ - sigflag |= SIGF_NOPUBKEY; - *mod_down = 1; - if( revocation ) - sigflag |= SIGF_REVOKED; - } - else { - log_info("sig %08lX.%lu/%lu[%d]/%08lX: %s: %s\n", - (ulong)keyid[1], lid, siglid, sigidx, - (ulong)sig->keyid[1], - revocation? _("Invalid certificate revocation") - : _("Invalid certificate"), - gpg_errstr(rc)); - sigflag |= SIGF_CHECKED; - if( revocation ) { - sigflag |= SIGF_REVOKED; - *mod_down = 1; - } - } - } - else if( tmp.rectype == RECTYPE_SDIR ) { - /* better check that it is the right one */ - if( tmp.r.sdir.keyid[0] == sig->keyid[0] - && tmp.r.sdir.keyid[1] == sig->keyid[1] - && (!tmp.r.sdir.pubkey_algo - || tmp.r.sdir.pubkey_algo == sig->pubkey_algo )) - sigflag |= SIGF_NOPUBKEY; - else - log_error(_("sig record %lu[%d] points to wrong record.\n"), - siglid, sigidx ); +/* Clear the ownertrust value. Return true if a changed actually happend. */ +int +clear_ownertrust (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); + if (rec.r.trust.ownertrust) + { + rec.r.trust.ownertrust = 0; + write_record( &rec ); + revalidation_mark (); + do_sync (); + return 1; + } } - else { - log_error(_("sig record %lu[%d] points to wrong record.\n"), - siglid, sigidx ); - tdbio_invalid(); + else if (rc != -1) + { + tdbio_invalid (); } - - return sigflag; + return 0; } -/**************** - * Make the sig records for the given uid record - * We don't set flags here or even check the signatures; this will - * happen latter. +/* + * Note: Caller has to do a sync */ -static ulong -make_sig_records( KBNODE keyblock, KBNODE uidnode, - ulong lid, u32 *mainkid, u32 *min_expire, - int *mod_down, int *mod_up ) -{ - TRUSTREC *srecs, **s_end, *s=NULL, *s2; - KBNODE node; - PKT_signature *sig; - ulong sigrecno, siglid; - int i, sigidx = 0; - u32 expiretime; - - srecs = NULL; s_end = &srecs; - for( node=uidnode->next; node; node = node->next ) { - if( node->pkt->pkttype == PKT_USER_ID - || node->pkt->pkttype == PKT_PUBLIC_SUBKEY ) - break; /* ready */ - if( node->pkt->pkttype != PKT_SIGNATURE ) - continue; /* don't care about other packets */ - sig = node->pkt->pkt.signature; - if( mainkid[0] == sig->keyid[0] && mainkid[1] == sig->keyid[1] ) - continue; /* we don't care about self-signatures here */ - - siglid = find_or_create_lid( sig ); - /* smash dups */ - /* FIXME: Here we have a problem: - * We can't distinguish between a certification and a certification - * revocation without looking at class of the signature - we have - * to see how we can store the sigclass in the sigrecord.. - * Argg- I hope I can get rid of this ugly trustdb ASAP. - */ - for( s2 = s; s2 ; s2 = s2->next ) { - for(i=0; i < sigidx; i++ ) { - if( s2->r.sig.sig[i].lid == siglid ) - goto leaveduptest; - } - } - for( s2 = srecs; s2 ; s2 = s2->next ) { - for(i=0; i < SIGS_PER_RECORD; i++ ) { - if( s2->r.sig.sig[i].lid == siglid ) - goto leaveduptest; - } - } - leaveduptest: - if( s2 ) { - log_info( "sig %08lX.%lu: %s\n", (ulong)mainkid[1], lid, - _("duplicated certificate - deleted") ); - continue; - } +static void +update_validity (PKT_public_key *pk, const byte *namehash, + int depth, int validity) +{ + TRUSTREC trec, vrec; + int rc; + ulong recno; + + 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, 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, namehash, 20); + vrec.r.valid.next = trec.r.trust.validlist; + } + vrec.r.valid.validity = validity; + write_record (&vrec); + trec.r.trust.depth = depth; + trec.r.trust.validlist = vrec.recnum; + write_record (&trec); +} + + +/* reset validity for all user IDs. Caller must sync. */ +static int +clear_validity (PKT_public_key *pk) +{ + TRUSTREC trec, vrec; + int rc; + ulong recno; + int any = 0; + + rc = read_trust_record (pk, &trec); + if (rc && rc != -1) + { + tdbio_invalid (); + return 0; + } + if (rc == -1) /* no record yet - no need to clerar it then ;-) */ + return 0; - /* create the sig record */ - if( !sigidx ) { - s = gcry_xcalloc( 1, sizeof *s ); - s->rectype = RECTYPE_SIG; - s->r.sig.lid = lid; - } - s->r.sig.sig[sigidx].lid = siglid; - s->r.sig.sig[sigidx].flag= check_sig_record( keyblock, node, - siglid, sigidx, - mainkid, lid, &expiretime, - mod_down, mod_up ); - - sigidx++; - if( sigidx == SIGS_PER_RECORD ) { - s->recnum = tdbio_new_recnum(); - *s_end = s; - s_end = &s->next; - sigidx = 0; - } - /* keep track of signers pk expire time */ - if( expiretime && (!*min_expire || *min_expire > expiretime ) ) - *min_expire = expiretime; - } - if( sigidx ) { - s->recnum = tdbio_new_recnum(); - *s_end = s; - s_end = &s->next; + /* reset validity for all user IDs */ + recno = trec.r.trust.validlist; + while (recno) + { + read_record (recno, &vrec, RECTYPE_VALID); + if ((vrec.r.valid.validity & TRUST_MASK)) + { + vrec.r.valid.validity &= ~TRUST_MASK; + write_record (&vrec); + any = 1; + } + recno = vrec.r.valid.next; } - sigrecno = srecs? srecs->recnum : 0; - /* write the keylist and release the memory */ - for( s = srecs; s ; s = s2 ) { - if( s->next ) - s->r.sig.next = s->next->recnum; - write_record( s ); - s2 = s->next; - gcry_free( s ); - } - return sigrecno; + return any; } + +/*********************************************** + ********* Query trustdb values ************** + ***********************************************/ -/**************** - * Make a preference record (or a list of them) according to the supplied - * signature. - * Returns: The record number of the first pref record. +/* + * 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. */ -static ulong -make_pref_record( PKT_signature *sig, ulong lid ) -{ - static struct { - sigsubpkttype_t subpkttype; - int preftype; - } ptable[] = { - { SIGSUBPKT_PREF_SYM, PREFTYPE_SYM }, - { SIGSUBPKT_PREF_HASH, PREFTYPE_HASH }, - { SIGSUBPKT_PREF_COMPR, PREFTYPE_COMPR }, - { 0, 0 } - }; - TRUSTREC *precs, **p_end, *p=NULL, *p2; - ulong precno; - int k, idx=0; - const byte *s; - size_t n; - - #if (ITEMS_PER_PREF_RECORD % 2) != 0 - #error ITEMS_PER_PREF_RECORD must have an even value - #endif - - precs = NULL; p_end = &precs; - for(k=0; ptable[k].subpkttype; k++ ) { - s = parse_sig_subpkt2( sig, ptable[k].subpkttype, &n ); - if( !s ) - continue; - for( ; n; n--, s++ ) { - if( !idx ) { - p = gcry_xcalloc( 1, sizeof *p ); - p->rectype = RECTYPE_PREF; - p->r.pref.lid = lid; - } - p->r.pref.data[idx++] = ptable[k].preftype; - p->r.pref.data[idx++] = *s; - if( idx >= ITEMS_PER_PREF_RECORD ) { - p->recnum = tdbio_new_recnum(); - *p_end = p; - p_end = &p->next; - idx = 0; - } - } - } - if( idx ) { - p->recnum = tdbio_new_recnum(); - *p_end = p; - p_end = &p->next; - } - - precno = precs? precs->recnum : 0; - /* write the precs and release the memory */ - for( p = precs; p ; p = p2 ) { - if( p->next ) - p->r.pref.next = p->next->recnum; - write_record( p ); - p2 = p->next; - gcry_free( p ); +unsigned int +get_validity (PKT_public_key *pk, const byte *namehash) +{ + static int did_nextcheck; + TRUSTREC trec, vrec; + int rc; + ulong recno; + unsigned int validity; + u32 kid[2]; + PKT_public_key *main_pk; + + init_trustdb (); + if (!did_nextcheck) + { + 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 precno; -} - - -static ulong -make_uid_records( KBNODE keyblock, ulong lid, u32 *keyid, u32 *min_expire, - int *mod_down, int *mod_up ) -{ - TRUSTREC *urecs, **uend, *u, *u2; - KBNODE node; - PKT_user_id *uid; - byte uidhash[20]; - ulong uidrecno; - - urecs = NULL; uend = &urecs; - for( node=keyblock; node; node = node->next ) { - PKT_signature *bestsig; - - if( node->pkt->pkttype != PKT_USER_ID ) - continue; - uid = node->pkt->pkt.user_id; - if( uid->photo ) { - gcry_md_hash_buffer( GCRY_MD_RMD160, uidhash, - uid->photo, uid->photolen ); - } - else { - gcry_md_hash_buffer( GCRY_MD_RMD160, uidhash, - uid->name, uid->len ); - } - /* create the uid record */ - u = gcry_xcalloc( 1, sizeof *u ); - u->rectype = RECTYPE_UID; - u->r.uid.lid = lid; - memcpy(u->r.uid.namehash, uidhash, 20 ); - u->recnum = tdbio_new_recnum(); - *uend = u; - uend = &u->next; - - u->r.uid.uidflags = check_uidsigs( keyblock, node, keyid, - lid, &bestsig ); - if( (u->r.uid.uidflags & UIDF_CHECKED) - && (u->r.uid.uidflags & UIDF_VALID) ) { - u->r.uid.prefrec = bestsig? make_pref_record( bestsig, lid ) : 0; + 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 = m_alloc_clear (sizeof *main_pk); + rc = get_pubkey (main_pk, pk->main_keyid); + if (rc) + { + log_error ("error getting main key %08lX of subkey %08lX: %s\n", + (ulong)pk->main_keyid[1], (ulong)kid[1], g10_errstr(rc)); + validity = TRUST_UNKNOWN; + goto leave; } - - /* the next test is really bad because we should modify - * out modification timestamps only if we really have a change. - * But because we are deleting the uid records first it is somewhat - * difficult to track those changes. fixme */ - if( !( u->r.uid.uidflags & UIDF_VALID ) - || ( u->r.uid.uidflags & UIDF_REVOKED ) ) - *mod_down=1; - else - *mod_up=1; - - /* create the list of signatures */ - u->r.uid.siglist = make_sig_records( keyblock, node, - lid, keyid, min_expire, - mod_down, mod_up ); - } - - uidrecno = urecs? urecs->recnum : 0; - /* write the uidlist and release the memory */ - for( u = urecs; u ; u = u2 ) { - if( u->next ) - u->r.uid.next = u->next->recnum; - write_record( u ); - u2 = u->next; - gcry_free( u ); } - return uidrecno; + else + main_pk = pk; + + 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 ( validity < (vrec.r.valid.validity & TRUST_MASK) ) + validity = (vrec.r.valid.validity & TRUST_MASK); + if ( namehash && !memcmp (vrec.r.valid.namehash, namehash, 20) ) + break; + recno = vrec.r.valid.next; + } + + if (recno) /* okay, use the user ID associated one */ + validity = (vrec.r.valid.validity & TRUST_MASK); + + if ( (trec.r.trust.ownertrust & TRUST_FLAG_DISABLED) ) + validity |= TRUST_FLAG_DISABLED; + + 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; } - -/**************** - * Update all the info from the public keyblock. - * The key must already exist in the keydb. - */ int -update_trust_record( KBNODE keyblock, int recheck, int *modified ) +get_validity_info (PKT_public_key *pk, const byte *namehash) { - TRUSTREC drec; - int rc; + int trustlevel; + int c; - /* NOTE: We don't need recheck anymore, but this might chnage again in - * the future */ - if( opt.dry_run ) - return 0; - if( modified ) - *modified = 0; - init_trustdb(); - rc = get_dir_record( find_kbnode( keyblock, PKT_PUBLIC_KEY ) - ->pkt->pkt.public_key, &drec ); - if( rc ) - return rc; - - rc = do_update_trust_record( keyblock, &drec, 0, modified ); - return rc; + trustlevel = get_validity (pk, namehash); + if( trustlevel & TRUST_FLAG_DISABLED ) + return 'd'; + if( trustlevel & TRUST_FLAG_REVOKED ) + return 'r'; + c = trust_letter ( (trustlevel & TRUST_MASK) ); + if( !c ) + c = '?'; + return c; } -/**************** - * Same as update_trust_record, but this functions expects the dir record. - * On exit the dir record will reflect any changes made. - * With sigs_only set only foreign key signatures are checked. - */ -static int -do_update_trust_record( KBNODE keyblock, TRUSTREC *drec, - int sigs_only, int *modified ) -{ - PKT_public_key *primary_pk; - TRUSTREC krec, urec, prec, helprec; - int i, rc = 0; - u32 keyid[2]; /* keyid of primary key */ - int mod_up = 0; - int mod_down = 0; - ulong recno, r2; - u32 expiretime; - - primary_pk = find_kbnode( keyblock, PKT_PUBLIC_KEY )->pkt->pkt.public_key; - if( !primary_pk->local_id ) - primary_pk->local_id = drec->recnum; - - keyid_from_pk( primary_pk, keyid ); - if( DBG_TRUST ) - log_debug("do_update_trust_record: %08lX.%lu\n", - (ulong)keyid[1], drec->recnum ); - - rc = tdbio_begin_transaction(); - if( rc ) - return rc; - - /* delete the old stuff FIXME: implementend sigs_only */ - for( recno=drec->r.dir.keylist; recno; recno = krec.r.key.next ) { - read_record( recno, &krec, RECTYPE_KEY ); - delete_record( recno ); - } - drec->r.dir.keylist = 0; - for( recno=drec->r.dir.uidlist; recno; recno = urec.r.uid.next ) { - read_record( recno, &urec, RECTYPE_UID ); - for(r2=urec.r.uid.prefrec ; r2; r2 = prec.r.pref.next ) { - read_record( r2, &prec, RECTYPE_PREF ); - delete_record( r2 ); - } - for(r2=urec.r.uid.siglist ; r2; r2 = helprec.r.sig.next ) { - read_record( r2, &helprec, RECTYPE_SIG ); - delete_record( r2 ); - } - delete_record( recno ); - } - drec->r.dir.uidlist = 0; - - - /* insert new stuff */ - drec->r.dir.dirflags &= ~DIRF_REVOKED; - drec->r.dir.dirflags &= ~DIRF_NEWKEYS; - drec->r.dir.keylist = make_key_records( keyblock, drec->recnum, keyid, &i ); - if( i ) /* primary key has been revoked */ - drec->r.dir.dirflags |= DIRF_REVOKED; - expiretime = 0; - drec->r.dir.uidlist = make_uid_records( keyblock, drec->recnum, keyid, - &expiretime, &mod_down, &mod_up ); - if( rc ) - rc = tdbio_cancel_transaction(); - else { - if( modified && tdbio_is_dirty() ) - *modified = 1; - drec->r.dir.dirflags |= DIRF_CHECKED; - drec->r.dir.valcheck = 0; - drec->r.dir.checkat = expiretime; - write_record( drec ); - tdbio_write_modify_stamp( mod_up, mod_down ); - rc = tdbio_end_transaction(); - } - return rc; -} -/**************** - * Insert a trust record into the TrustDB - * This function assumes that the record does not yet exist. - */ -int -insert_trust_record( KBNODE keyblock ) +void +list_trust_path( const char *username ) { - TRUSTREC dirrec; - TRUSTREC shadow; - KBNODE node; - int rc = 0; - PKT_public_key *pk; - - - if( opt.dry_run ) - return 0; - - init_trustdb(); - - pk = find_kbnode( keyblock, PKT_PUBLIC_KEY )->pkt->pkt.public_key; - if( pk->local_id ) { - log_debug("insert_trust_record with pk->local_id=%lu (2)\n", - pk->local_id ); - rc = update_trust_record( keyblock, 1, NULL ); - return rc; - } - - /* We have to look for a shadow dir record which must be reused - * as the dir record. */ - rc = tdbio_search_sdir( pk->keyid, pk->pubkey_algo, &shadow ); - if( rc && rc != -1 ) { - log_error(_("tdbio_search_dir failed: %s\n"), gpg_errstr(rc)); - tdbio_invalid(); - } - memset( &dirrec, 0, sizeof dirrec ); - dirrec.rectype = RECTYPE_DIR; - if( !rc ) /* we have a shadow dir record - convert to dir record */ - dirrec.recnum = shadow.recnum; - else - dirrec.recnum = tdbio_new_recnum(); - dirrec.r.dir.lid = dirrec.recnum; - write_record( &dirrec ); - - /* put the LID into the keyblock */ - pk->local_id = dirrec.r.dir.lid; - for( node=keyblock; node; node = node->next ) { - if( node->pkt->pkttype == PKT_PUBLIC_KEY - || node->pkt->pkttype == PKT_PUBLIC_SUBKEY ) { - PKT_public_key *a_pk = node->pkt->pkt.public_key; - a_pk->local_id = dirrec.r.dir.lid; - } - else if( node->pkt->pkttype == PKT_SIGNATURE ) { - PKT_signature *a_sig = node->pkt->pkt.signature; - a_sig->local_id = dirrec.r.dir.lid; - } - } - - - /* mark tdb as modified upwards */ - tdbio_write_modify_stamp( 1, 0 ); - - /* and put all the other stuff into the keydb */ - rc = do_update_trust_record( keyblock, &dirrec, 0, NULL ); - - do_sync(); - - /* keep track of new keys */ - if( !fresh_imported_keys ) - fresh_imported_keys = new_lid_table(); - ins_lid_table_item( fresh_imported_keys, pk->local_id, 0 ); - if( ++fresh_imported_keys_count > FRESH_KEY_CHECK_THRESHOLD ) - mark_fresh_keys(); - - return rc; } /**************** - * Insert a trust record indentified by a PK into the TrustDB + * 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 -insert_trust_record_by_pk( PKT_public_key *pk ) -{ - KBNODE 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( "insert_trust_record_by_pk: keyblock not found: %s\n", - gpg_errstr(rc) ); - } - else { - rc = insert_trust_record( keyblock ); - if( !rc ) /* copy the LID into the PK */ - pk->local_id = find_kbnode( keyblock, PKT_PUBLIC_KEY ) - ->pkt->pkt.public_key->local_id; - } - - release_kbnode( keyblock ); - return rc; -} - - -/**************** - * Check one trust record. This function is called for every - * directory record which is to be checked. The supplied - * dir record is modified according to the performed actions. - * Currently we only do an update_trust_record. - */ -static int -check_trust_record( TRUSTREC *drec, int sigs_only ) +enum_cert_paths( void **context, ulong *lid, + unsigned *ownertrust, unsigned *validity ) { - KBNODE keyblock; - int modified, rc; - - rc = get_keyblock_bylid( &keyblock, drec->recnum ); - if( rc ) { - log_debug( "check_trust_record %lu: keyblock not found: %s\n", - drec->recnum, gpg_errstr(rc) ); - return rc; - } - - rc = do_update_trust_record( keyblock, drec, sigs_only, &modified ); - release_kbnode( keyblock ); - - return rc; + return -1; } /**************** - * Walk over the keyrings and create trustdb records for all keys - * which are not currently in the trustdb. - * It is intended to be used after a fast-import operation. + * Print the current path */ void -update_trustdb() +enum_cert_paths_print( void **context, FILE *fp, + int refresh, ulong selected_lid ) { - KBNODE keyblock = NULL; - KBPOS kbpos; - int rc; - - if( opt.dry_run ) - return; - - init_trustdb(); - rc = enum_keyblocks_begin( &kbpos, 0 ); - if( !rc ) { - ulong count=0, err_count=0, new_count=0; - - while( !(rc = enum_keyblocks_next( kbpos, 1, &keyblock )) ) { - /*int modified;*/ - TRUSTREC drec; - PKT_public_key *pk = find_kbnode( keyblock, PKT_PUBLIC_KEY ) - ->pkt->pkt.public_key; - - rc = get_dir_record( pk, &drec ); - if( rc == -1 ) { /* not in trustdb: insert */ - rc = insert_trust_record( keyblock ); - if( rc && !pk->local_id ) { - log_error(_("lid ?: insert failed: %s\n"), - gpg_errstr(rc) ); - err_count++; - } - else if( rc ) { - log_error(_("lid %lu: insert failed: %s\n"), - pk->local_id, gpg_errstr(rc) ); - err_count++; - } - else { - if( opt.verbose ) - log_info(_("lid %lu: inserted\n"), pk->local_id ); - new_count++; - } - } - else if( rc ) { - log_error(_("error reading dir record: %s\n"), gpg_errstr(rc)); - err_count++; - } - - release_kbnode( keyblock ); keyblock = NULL; - if( !(++count % 100) ) - log_info(_("%lu keys so far processed\n"), count); - } - log_info(_("%lu keys processed\n"), count); - if( err_count ) - log_info(_("\t%lu keys with errors\n"), err_count); - if( new_count ) - log_info(_("\t%lu keys inserted\n"), new_count); - } - if( rc && rc != -1 ) - log_error(_("enumerate keyblocks failed: %s\n"), gpg_errstr(rc)); - - enum_keyblocks_end( kbpos ); - release_kbnode( keyblock ); + return; } + +/**************************************** + *********** NEW NEW NEW **************** + ****************************************/ -/**************** - * Do all required checks in the trustdb. This function walks over all - * records in the trustdb and does scheduled processing. - */ -void -check_trustdb( const char *username ) +static int +ask_ownertrust (u32 *kid) { - TRUSTREC rec; - ulong recnum; - ulong count=0, upd_count=0, err_count=0, skip_count=0, sigonly_count=0; - ulong current_time = make_timestamp(); - - if( username ) - log_info("given user IDs ignored in check_trustdb\n"); - - init_trustdb(); - - for(recnum=0; !tdbio_read_record( recnum, &rec, 0); recnum++ ) { - int sigs_only; - - if( rec.rectype != RECTYPE_DIR ) - continue; /* we only want the dir records */ - - if( count && !(count % 100) && !opt.quiet ) - log_info(_("%lu keys so far processed\n"), count); - count++; - sigs_only = 0; - - if( !(rec.r.dir.dirflags & DIRF_CHECKED) ) - ; - else if( !rec.r.dir.checkat || rec.r.dir.checkat > current_time ) { - if( !(rec.r.dir.dirflags & DIRF_NEWKEYS) ) { - skip_count++; - continue; /* not scheduled for checking */ - } - sigs_only = 1; /* new public keys - check them */ - sigonly_count++; - } - - if( !rec.r.dir.keylist ) { - log_info(_("lid %lu: dir record w/o key - skipped\n"), recnum); - skip_count++; - continue; - } + PKT_public_key *pk; + int rc; + int ot; - check_trust_record( &rec, sigs_only ); + pk = m_alloc_clear (sizeof *pk); + rc = get_pubkey (pk, kid); + if (rc) + { + log_error (_("public key %08lX not found: %s\n"), + (ulong)kid[1], g10_errstr(rc) ); + return TRUST_UNKNOWN; } - - log_info(_("%lu keys processed\n"), count); - if( sigonly_count ) - log_info(_("\t%lu due to new pubkeys\n"), sigonly_count); - if( skip_count ) - log_info(_("\t%lu keys skipped\n"), skip_count); - if( err_count ) - log_info(_("\t%lu keys with errors\n"), err_count); - if( upd_count ) - log_info(_("\t%lu keys updated\n"), upd_count); + + ot=edit_ownertrust(pk,0); + if(ot>0) + ot = get_ownertrust (pk); + else if(ot==0) + ot = TRUST_UNDEFINED; + else + ot = -1; /* quit */ + free_public_key( pk ); + return ot; } - -/*********************************************** - ********* Trust calculation ***************** - ***********************************************/ - -/**************** - * Find all certification paths of a given LID. - * Limit the search to MAX_DEPTH. stack is a helper variable which - * should have been allocated with size max_depth, stack[0] should - * be setup to the key we are investigating, so the minimal depth - * we should ever see in this function is 1. - * Returns: a new tree - * certchain_set must be a valid set or point to NULL; this function - * may modifiy it. - * - * Hmmm: add a fastscan mode which stops at valid validity nodes. - */ -static TN -build_cert_tree( ulong lid, int depth, int max_depth, TN helproot ) +static void +mark_keyblock_seen (KeyHashTable tbl, KBNODE node) { - TRUSTREC dirrec; - TRUSTREC uidrec; - ulong uidrno; - TN keynode; - - if( depth >= max_depth ) - return NULL; - - keynode = new_tn(); - if( !helproot ) - helproot = keynode; - keynode->lid = lid; - if( !qry_lid_table_flag( ultikey_table, lid, NULL ) ) { - /* this is an ultimately trusted key; - * which means that we have found the end of the chain: - * We do this here prior to reading the dir record - * because we don't really need the info from that record */ - keynode->n.k.ownertrust = TRUST_ULTIMATE; - keynode->n.k.buckstop = 1; - return keynode; - } - read_record( lid, &dirrec, 0 ); - if( dirrec.rectype != RECTYPE_DIR ) { - if( dirrec.rectype != RECTYPE_SDIR ) - log_debug("lid %lu, has rectype %d" - " - skipped\n", lid, dirrec.rectype ); - gcry_free(keynode); - return NULL; - } - - if( dirrec.r.dir.checkat && dirrec.r.dir.checkat <= make_timestamp() ) { - check_trust_record( &dirrec, 0 ); - } - else if( (dirrec.r.dir.dirflags & DIRF_NEWKEYS) ) { - check_trust_record( &dirrec, 1 ); - } - - keynode->n.k.ownertrust = dirrec.r.dir.ownertrust & TRUST_MASK; - - /* loop over all user ids */ - for( uidrno = dirrec.r.dir.uidlist; uidrno; uidrno = uidrec.r.uid.next ) { - TRUSTREC sigrec; - ulong sigrno; - TN uidnode = NULL; - - read_record( uidrno, &uidrec, RECTYPE_UID ); - - if( !(uidrec.r.uid.uidflags & UIDF_CHECKED) ) - continue; /* user id has not been checked */ - if( !(uidrec.r.uid.uidflags & UIDF_VALID) ) - continue; /* user id is not valid */ - if( (uidrec.r.uid.uidflags & UIDF_REVOKED) ) - continue; /* user id has been revoked */ - - /* loop over all signature records */ - for(sigrno=uidrec.r.uid.siglist; sigrno; sigrno = sigrec.r.sig.next ) { - int i; - TN tn; - - read_record( sigrno, &sigrec, RECTYPE_SIG ); - - for(i=0; i < SIGS_PER_RECORD; i++ ) { - if( !sigrec.r.sig.sig[i].lid ) - continue; /* skip deleted sigs */ - if( !(sigrec.r.sig.sig[i].flag & SIGF_CHECKED) ) - continue; /* skip unchecked signatures */ - if( !(sigrec.r.sig.sig[i].flag & SIGF_VALID) ) - continue; /* skip invalid signatures */ - if( (sigrec.r.sig.sig[i].flag & SIGF_EXPIRED) ) - continue; /* skip expired signatures */ - if( (sigrec.r.sig.sig[i].flag & SIGF_REVOKED) ) - continue; /* skip revoked signatures */ - /* check for cycles */ - for( tn=keynode; tn && tn->lid != sigrec.r.sig.sig[i].lid; - tn = tn->back ) - ; - if( tn ) - continue; /* cycle found */ - - tn = build_cert_tree( sigrec.r.sig.sig[i].lid, - depth+1, max_depth, helproot ); - if( !tn ) - continue; /* cert chain too deep or error */ - - if( !uidnode ) { - uidnode = new_tn(); - uidnode->back = keynode; - uidnode->lid = uidrno; - uidnode->is_uid = 1; - uidnode->next = keynode->list; - keynode->list = uidnode; - } - - tn->back = uidnode; - tn->next = uidnode->list; - uidnode->list = tn; - if( tn->n.k.buckstop ) { - /* ultimately trusted key found: - * no need to check more signatures of this uid */ - sigrec.r.sig.next = 0; - break; - } - } - } /* end loop over sig recs */ - } /* end loop over user ids */ + for ( ;node; node = node->next ) + if (node->pkt->pkttype == PKT_PUBLIC_KEY + || node->pkt->pkttype == PKT_PUBLIC_SUBKEY) + { + u32 aki[2]; - if( !keynode->list ) { - release_tn_tree( keynode ); - keynode = NULL; - } - - return keynode; + keyid_from_pk (node->pkt->pkt.public_key, aki); + add_key_hash_table (tbl, aki); + } } + static void -upd_one_ownertrust( ulong lid, unsigned new_trust, unsigned *retflgs ) -{ - TRUSTREC rec; - - read_record( lid, &rec, RECTYPE_DIR ); - if( DBG_TRUST ) - log_debug("upd_one_ownertrust of %lu from %u to %u\n", - lid, (unsigned)rec.r.dir.ownertrust, new_trust ); - if( retflgs ) { - if( (new_trust & TRUST_MASK) > (rec.r.dir.ownertrust & TRUST_MASK) ) - *retflgs |= 16; /* modified up */ - else - *retflgs |= 32; /* modified down */ +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'); + } + } } +} - /* we preserve the disabled state here */ - if( (rec.r.dir.ownertrust & TRUST_FLAG_DISABLED) ) - rec.r.dir.ownertrust = new_trust | TRUST_FLAG_DISABLED; - else - rec.r.dir.ownertrust = new_trust & ~TRUST_FLAG_DISABLED; - write_record( &rec ); -} -/**************** - * Update the ownertrust in the complete tree. - */ static void -propagate_ownertrust( TN kr, ulong lid, unsigned trust ) -{ - TN ur; - - for( ; kr; kr = kr->next ) { - if( kr->lid == lid ) - kr->n.k.ownertrust = trust; - for( ur=kr->list; ur; ur = ur->next ) - propagate_ownertrust( ur->list, lid, trust ); +store_validation_status (int depth, KBNODE keyblock) +{ + KBNODE node; + byte namehash[20]; + 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) + { + if( uid->attrib_data ) + rmd160_hash_buffer (namehash,uid->attrib_data,uid->attrib_len); + else + rmd160_hash_buffer (namehash, uid->name, uid->len ); + + update_validity (keyblock->pkt->pkt.public_key, + namehash, depth, status); + any = 1; + } + } } -} -/**************** - * Calculate the validity of all keys in the tree and especially - * the one of the top key. If add_fnc is not NULL, it is used to - * ask for missing ownertrust values (but only if this will help - * us to increase the validity. - * add_fnc is expected to take the LID of the key under question - * and return a ownertrust value or an error: positive values - * are assumed to be the new ownertrust value; a 0 does mean no change, - * a -1 is a request to cancel this validation procedure, a -2 requests - * a listing of the sub-tree using the tty functions. - * - * - * Returns: 0 = okay + if (any) + do_sync (); +} + +/* + * check whether the signature sig is in the klist k */ -static int -propagate_validity( TN root, TN node, int (*add_fnc)(ulong), unsigned *retflgs ) +static struct key_item * +is_in_klist (struct key_item *k, PKT_signature *sig) { - TN kr, ur; - int max_validity = 0; - - assert( !node->is_uid ); - if( node->n.k.ownertrust == TRUST_ULTIMATE ) { - /* this is one of our keys */ - assert( !node->list ); /* it should be a leaf */ - node->n.k.validity = TRUST_ULTIMATE; - if( retflgs ) - *retflgs |= 1; /* found a path to an ultimately trusted key */ - return 0; + for (; k; k = k->next) + { + if (k->kid[0] == sig->keyid[0] && k->kid[1] == sig->keyid[1]) + return k; } - - /* loop over all user ids */ - for( ur=node->list; ur && max_validity <= TRUST_FULLY; ur = ur->next ) { - assert( ur->is_uid ); - /* loop over all signators */ - for(kr=ur->list; kr && max_validity <= TRUST_FULLY; kr = kr->next ) { - if( propagate_validity( root, kr, add_fnc, retflgs ) ) - return -1; /* quit */ - if( kr->n.k.validity == TRUST_ULTIMATE ) { - ur->n.u.fully_count = opt.completes_needed; - } - else if( kr->n.k.validity == TRUST_FULLY ) { - if( add_fnc && !kr->n.k.ownertrust ) { - int rc; - - if( retflgs ) - *retflgs |= 2; /* found key with undefined ownertrust*/ - do { - rc = add_fnc( kr->lid ); - switch( rc ) { - case TRUST_NEVER: - case TRUST_MARGINAL: - case TRUST_FULLY: - propagate_ownertrust( root, kr->lid, rc ); - upd_one_ownertrust( kr->lid, rc, retflgs ); - if( retflgs ) - *retflgs |= 4; /* changed */ - break; - case -1: - return -1; /* cancel */ - case -2: - dump_tn_tree( NULL, 0, kr ); - tty_printf("\n"); - break; - default: - break; - } - } while( rc == -2 ); - } - if( kr->n.k.ownertrust == TRUST_FULLY ) - ur->n.u.fully_count++; - else if( kr->n.k.ownertrust == TRUST_MARGINAL ) - ur->n.u.marginal_count++; - } - - if( ur->n.u.fully_count >= opt.completes_needed - || ur->n.u.marginal_count >= opt.marginals_needed ) - ur->n.u.validity = TRUST_FULLY; - else if( ur->n.u.fully_count || ur->n.u.marginal_count ) - ur->n.u.validity = TRUST_MARGINAL; - - if( ur->n.u.validity >= max_validity ) - max_validity = ur->n.u.validity; - } - } - - node->n.k.validity = max_validity; - return 0; + return NULL; } - - -/**************** - * Given the directory record of a key, check whether we can - * find a path to an ultimately trusted key. We do this by - * checking all key signatures up to a some depth. +/* + * 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. Note that flag bits 9 and 10 are used for internal + * purposes. */ -static int -verify_key( int max_depth, TRUSTREC *drec, const char *namehash, - int (*add_fnc)(ulong), unsigned *retflgs ) -{ - TN tree; - int keytrust; - int pv_result; - - tree = build_cert_tree( drec->r.dir.lid, 0, opt.max_cert_depth, NULL ); - if( !tree ) - return TRUST_UNDEFINED; - pv_result = propagate_validity( tree, tree, add_fnc, retflgs ); - if( namehash && tree->n.k.validity != TRUST_ULTIMATE ) { - /* find the matching user id. - * We don't do this here if the key is ultimately trusted; in - * this case there will be no lids for the user IDs and frankly - * it does not make sense to compare by the name if we do - * have the secret key. - * fixme: the way we handle this is too inefficient */ - TN ur; - TRUSTREC rec; - - keytrust = 0; - for( ur=tree->list; ur; ur = ur->next ) { - read_record( ur->lid, &rec, RECTYPE_UID ); - if( !memcmp( namehash, rec.r.uid.namehash, 20 ) ) { - keytrust = ur->n.u.validity; - break; - } - } - } - else - keytrust = tree->n.k.validity; - - /* update the cached validity values */ - if( !pv_result - && keytrust >= TRUST_UNDEFINED - && tdbio_db_matches_options() - && ( !drec->r.dir.valcheck || drec->r.dir.validity != keytrust ) ) { - TN ur; - TRUSTREC rec; - - for( ur=tree->list; ur; ur = ur->next ) { - read_record( ur->lid, &rec, RECTYPE_UID ); - if( rec.r.uid.validity != ur->n.u.validity ) { - rec.r.uid.validity = ur->n.u.validity; - write_record( &rec ); - } - } +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) + { + node->flag &= ~(1<<8 | 1<<9 | 1<<10); + 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 (sig->keyid[0] == main_kid[0] && sig->keyid[1] == main_kid[1]) + continue; /* ignore self-signatures */ + if (!IS_UID_SIG(sig) && !IS_UID_REV(sig)) + continue; /* we only look at these signature classes */ + if (!is_in_klist (klist, sig)) + continue; /* no need to check it then */ + if (check_key_signature (keyblock, node, NULL)) + continue; /* ignore invalid signatures */ + node->flag |= 1<<9; + } + /* reset the remaining flags */ + for (; node; node = node->next) + node->flag &= ~(1<<8 | 1<<9 | 1 << 10); + + /* 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 */ + + /* 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]; + 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; - drec->r.dir.validity = tree->n.k.validity; - drec->r.dir.valcheck = make_timestamp(); - write_record( drec ); - do_sync(); + /* 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 (expire && expire < *next_expire) + *next_expire = expire; + } + } } - - release_tn_tree( tree ); - return keytrust; } -/**************** - * we have the pubkey record and all needed informations are in the trustdb - * but nothing more is known. +/* + * 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 -do_check( TRUSTREC *dr, unsigned *validity, - const char *namehash, int (*add_fnc)(ulong), unsigned *retflgs ) -{ - if( !dr->r.dir.keylist ) { - log_error(_("Ooops, no keys\n")); - return GPGERR_TRUSTDB; - } - if( !dr->r.dir.uidlist ) { - log_error(_("Ooops, no user IDs\n")); - return GPGERR_TRUSTDB; +validate_one_keyblock (KBNODE kb, struct key_item *klist, + u32 curtime, u32 *next_expire) +{ + struct key_item *kr; + KBNODE node, uidnode=NULL; + PKT_public_key *pk = kb->pkt->pkt.public_key; + u32 main_kid[2]; + int issigned=0, any_signed = 0, fully_count =0, marginal_count = 0; + + keyid_from_pk(pk, main_kid); + for (node=kb; node; node = node->next) + { + if (node->pkt->pkttype == PKT_USER_ID) + { + if (uidnode && issigned) + { + if (fully_count >= opt.completes_needed + || marginal_count >= opt.marginals_needed ) + uidnode->flag |= 4; + else if (fully_count || marginal_count) + uidnode->flag |= 2; + uidnode->flag |= 1; + any_signed = 1; + } + uidnode = node; + issigned = 0; + fully_count = marginal_count = 0; + mark_usable_uid_certs (kb, uidnode, main_kid, klist, + curtime, next_expire); + } + else if (node->pkt->pkttype == PKT_SIGNATURE + && (node->flag & (1<<8)) ) + { + PKT_signature *sig = node->pkt->pkt.signature; + + kr = is_in_klist (klist, sig); + if (kr) + { + if (kr->ownertrust == TRUST_ULTIMATE) + fully_count = opt.completes_needed; + else if (kr->ownertrust == TRUST_FULLY) + fully_count++; + else if (kr->ownertrust == TRUST_MARGINAL) + marginal_count++; + issigned = 1; + } + } } - if( retflgs ) - *retflgs &= ~(16|32); /* reset the 2 special flags */ - - if( (dr->r.dir.ownertrust & TRUST_FLAG_DISABLED) ) - *validity = 0; /* no need to check further */ - else if( namehash ) { - /* Fixme: use a cache */ - *validity = verify_key( opt.max_cert_depth, dr, namehash, - add_fnc, retflgs ); - } - else if( !add_fnc - && tdbio_db_matches_options() - /* FIXME, TODO: This comparision is WRONG ! */ - && dr->r.dir.valcheck - > tdbio_read_modify_stamp( (dr->r.dir.validity < TRUST_FULLY) ) - && dr->r.dir.validity ) - *validity = dr->r.dir.validity; - else - *validity = verify_key( opt.max_cert_depth, dr, NULL, - add_fnc, retflgs ); - - if( !(*validity & TRUST_MASK) ) - *validity = TRUST_UNDEFINED; - - if( (dr->r.dir.ownertrust & TRUST_FLAG_DISABLED) ) - *validity |= TRUST_FLAG_DISABLED; - - if( dr->r.dir.dirflags & DIRF_REVOKED ) - *validity |= TRUST_FLAG_REVOKED; - - /* If we have changed some ownertrusts, set the trustdb timestamps - * and do a sync */ - if( retflgs && (*retflgs & (16|32)) ) { - tdbio_write_modify_stamp( (*retflgs & 16), (*retflgs & 32) ); - do_sync(); + if (uidnode && issigned) + { + if (fully_count >= opt.completes_needed + || marginal_count >= opt.marginals_needed ) + uidnode->flag |= 4; + else if (fully_count || marginal_count) + uidnode->flag |= 2; + uidnode->flag |= 1; + any_signed = 1; } - - return 0; + return any_signed; } - -/*********************************************** - ********* Change trustdb values ************** - ***********************************************/ - -int -update_ownertrust( ulong lid, unsigned new_trust ) -{ - TRUSTREC rec; - - init_trustdb(); - read_record( lid, &rec, RECTYPE_DIR ); - if( DBG_TRUST ) - log_debug("update_ownertrust of %lu from %u to %u\n", - lid, (unsigned)rec.r.dir.ownertrust, new_trust ); - rec.r.dir.ownertrust = new_trust; - write_record( &rec ); - do_sync(); - return 0; -} - - -int -clear_trust_checked_flag( PKT_public_key *pk ) -{ - TRUSTREC rec; - int rc; - - if( opt.dry_run ) - return 0; - - init_trustdb(); - rc = get_dir_record( pk, &rec ); - if( rc ) - return rc; - - /* check whether they are already reset */ - if( !(rec.r.dir.dirflags & DIRF_CHECKED) && !rec.r.dir.valcheck ) - return 0; - - /* reset the flag */ - rec.r.dir.dirflags &= ~DIRF_CHECKED; - rec.r.dir.valcheck = 0; - write_record( &rec ); - do_sync(); - return 0; -} - - - - - -/*********************************************** - ********* Query trustdb values ************** - ***********************************************/ - - -/**************** - * This function simply looks for the key in the trustdb - * and makes sure that pk->local_id is set to the correct value. - * Return: 0 = found - * -1 = not found - * other = error - */ -int -query_trust_record( PKT_public_key *pk ) +static int +search_skipfnc (void *opaque, u32 *kid) { - TRUSTREC rec; - init_trustdb(); - return get_dir_record( pk, &rec ); + return test_key_hash_table ((KeyHashTable)opaque, kid); } -/**************** - * Get the trustlevel for this PK. - * Note: This does not ask any questions - * Returns: 0 okay of an errorcode - * - * It operates this way: - * locate the pk in the trustdb - * found: - * Do we have a valid cache record for it? - * yes: return trustlevel from cache - * no: make a cache record and all the other stuff - * not found: - * try to insert the pubkey into the trustdb and check again - * - * Problems: How do we get the complete keyblock to check that the - * cache record is actually valid? Think we need a clever - * cache in getkey.c to keep track of this stuff. Maybe it - * is not necessary to check this if we use a local pubring. Hmmmm. +/* + * 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. */ -int -check_trust( PKT_public_key *pk, unsigned *r_trustlevel, - const byte *namehash, int (*add_fnc)(ulong), unsigned *retflgs ) -{ - TRUSTREC rec; - unsigned trustlevel = TRUST_UNKNOWN; - int rc=0; - u32 cur_time; - u32 keyid[2]; - - - init_trustdb(); - keyid_from_pk( pk, keyid ); - - /* get the pubkey record */ - if( pk->local_id ) { - read_record( pk->local_id, &rec, RECTYPE_DIR ); - } - else { /* no local_id: scan the trustdb */ - if( (rc=tdbio_search_dir_bypk( pk, &rec )) && rc != -1 ) { - log_error(_("check_trust: search dir record failed: %s\n"), - gpg_errstr(rc)); - return rc; - } - else if( rc == -1 && opt.dry_run ) - return GPGERR_GENERAL; - else if( rc == -1 ) { /* not found - insert */ - rc = insert_trust_record_by_pk( pk ); - if( rc ) { - log_error(_("key %08lX: insert trust record failed: %s\n"), - (ulong)keyid[1], gpg_errstr(rc)); - goto leave; - } - log_info(_("key %08lX.%lu: inserted into trustdb\n"), - (ulong)keyid[1], pk->local_id ); - /* and re-read the dir record */ - read_record( pk->local_id, &rec, RECTYPE_DIR ); - } - } - cur_time = make_timestamp(); - if( pk->timestamp > cur_time ) { - log_info(_("key %08lX.%lu: created in future " - "(time warp or clock problem)\n"), - (ulong)keyid[1], pk->local_id ); - if( !opt.ignore_time_conflict ) - return GPGERR_TIME_CONFLICT; - } +static struct key_array * +validate_key_list (KEYDB_HANDLE hd, KeyHashTable visited, + 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 = m_alloc ((maxkeys+1) * sizeof *keys); + nkeys = 0; + + rc = keydb_search_reset (hd); + if (rc) + { + log_error ("keydb_search_reset failed: %s\n", g10_errstr(rc)); + m_free (keys); + return NULL; + } + + memset (&desc, 0, sizeof desc); + desc.mode = KEYDB_SEARCH_MODE_FIRST; + desc.skipfnc = search_skipfnc; + desc.skipfncvalue = visited; + 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)); + m_free (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)); + m_free (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; + } - if( !(rec.r.dir.dirflags & DIRF_CHECKED) ) - check_trust_record( &rec, 0 ); - else if( rec.r.dir.checkat && rec.r.dir.checkat <= cur_time ) - check_trust_record( &rec, 0 ); - else if( (rec.r.dir.dirflags & DIRF_NEWKEYS) ) - check_trust_record( &rec, 1 ); - - if( pk->expiredate && pk->expiredate <= cur_time ) { - log_info(_("key %08lX.%lu: expired at %s\n"), - (ulong)keyid[1], pk->local_id, - asctimestamp( pk->expiredate) ); - trustlevel = TRUST_EXPIRED; - } - else { - rc = do_check( &rec, &trustlevel, namehash, add_fnc, retflgs ); - if( rc ) { - log_error(_("key %08lX.%lu: trust check failed: %s\n"), - (ulong)keyid[1], pk->local_id, gpg_errstr(rc)); - return rc; - } - } + /* 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 (visited, keyblock); + } + else if (validate_one_keyblock (keyblock, klist, curtime, next_expire)) + { + if (pk->expiredate && pk->expiredate >= curtime + && pk->expiredate < *next_expire) + *next_expire = pk->expiredate; + + if (nkeys == maxkeys) { + maxkeys += 1000; + keys = m_realloc (keys, (maxkeys+1) * sizeof *keys); + } + keys[nkeys++].keyblock = keyblock; + /* this key is signed - don't check it again */ + mark_keyblock_seen (visited, keyblock); + keyblock = NULL; + } - /* is a subkey has been requested, we have to check its keyflags */ - if( !rc ) { - TRUSTREC krec; - byte fpr[MAX_FINGERPRINT_LEN] = {0}; /* to avoid compiler warnings */ - size_t fprlen = 0; - ulong recno; - int kcount=0; - - for( recno = rec.r.dir.keylist; recno; recno = krec.r.key.next ) { - read_record( recno, &krec, RECTYPE_KEY ); - if( ++kcount == 1 ) - continue; /* skip the primary key */ - if( kcount == 2 ) /* now we need the fingerprint */ - fingerprint_from_pk( pk, fpr, &fprlen ); - - if( krec.r.key.fingerprint_len == fprlen - && !memcmp( krec.r.key.fingerprint, fpr, fprlen ) ) { - /* found the subkey */ - if( (krec.r.key.keyflags & KEYF_REVOKED) ) - trustlevel |= TRUST_FLAG_SUB_REVOKED; - /* should we check for keybinding here??? */ - /* Hmmm: Maybe this whole checking stuff should not go - * into the trustdb, but be done direct from the keyblock. - * Chnage this all when we add an abstarction layer around - * the way certificates are handled by different standards */ - break; - } - } + 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)); + m_free (keys); + return NULL; } - - leave: - if( DBG_TRUST ) - log_debug("check_trust() returns trustlevel %04x.\n", trustlevel); - *r_trustlevel = trustlevel; - return 0; -} + keys[nkeys].keyblock = NULL; + return keys; +} -/**************** - * scan the whole trustdb and mark all signature records whose keys - * are freshly imported. - */ static void -mark_fresh_keys() -{ - TRUSTREC dirrec, rec; - ulong recnum, lid; - int i; - - memset( &dirrec, 0, sizeof dirrec ); - - for(recnum=0; !tdbio_read_record( recnum, &rec, 0); recnum++ ) { - if( rec.rectype != RECTYPE_SIG ) - continue; - /* if we have already have the dir record, we can check it now */ - if( dirrec.recnum == rec.r.sig.lid - && (dirrec.r.dir.dirflags & DIRF_NEWKEYS) ) - continue; /* flag is already set */ - - for(i=0; i < SIGS_PER_RECORD; i++ ) { - if( !(lid=rec.r.sig.sig[i].lid) ) - continue; /* skip deleted sigs */ - if( !(rec.r.sig.sig[i].flag & SIGF_CHECKED) ) - continue; /* skip checked signatures */ - if( qry_lid_table_flag( fresh_imported_keys, lid, NULL ) ) - continue; /* not in the list of new keys */ - read_record( rec.r.sig.lid, &dirrec, RECTYPE_DIR ); - if( !(dirrec.r.dir.dirflags & DIRF_NEWKEYS) ) { - dirrec.r.dir.dirflags |= DIRF_NEWKEYS; - write_record( &dirrec ); - } - break; - } - } - - do_sync(); - - clear_lid_table( fresh_imported_keys ); - fresh_imported_keys_count = 0; -} - - - -int -query_trust_info( PKT_public_key *pk, const byte *namehash ) -{ - unsigned trustlevel; - int c; - - init_trustdb(); - if( check_trust( pk, &trustlevel, namehash, NULL, NULL ) ) - return '?'; - if( trustlevel & TRUST_FLAG_DISABLED ) - return 'd'; - if( trustlevel & TRUST_FLAG_REVOKED ) - return 'r'; - c = trust_letter( (trustlevel & TRUST_MASK) ); - if( !c ) - c = '?'; - return c; -} - - - -/**************** - * Return the assigned ownertrust value for the given LID - */ -unsigned -get_ownertrust( ulong lid ) -{ - TRUSTREC rec; - - init_trustdb(); - read_record( lid, &rec, RECTYPE_DIR ); - return rec.r.dir.ownertrust; -} - -int -get_ownertrust_info( ulong lid ) -{ - unsigned otrust; - int c; - - init_trustdb(); - otrust = get_ownertrust( lid ); - c = trust_letter( (otrust & TRUST_MASK) ); - if( !c ) - c = '?'; - return c; -} - - - -void -list_trust_path( const char *username ) -{ - int rc; - ulong lid; - TRUSTREC rec; - TN tree; - PKT_public_key *pk = gcry_xcalloc( 1, sizeof *pk ); - - init_trustdb(); - if( (rc = get_pubkey_byname(NULL, pk, username, NULL )) ) - log_error(_("user '%s' not found: %s\n"), username, gpg_errstr(rc) ); - else if( (rc=tdbio_search_dir_bypk( pk, &rec )) && rc != -1 ) - log_error(_("problem finding '%s' in trustdb: %s\n"), - username, gpg_errstr(rc)); - else if( rc == -1 ) { - log_info(_("user '%s' not in trustdb - inserting\n"), username); - rc = insert_trust_record_by_pk( pk ); - if( rc ) - log_error(_("failed to put '%s' into trustdb: %s\n"), - username, gpg_errstr(rc)); - else { - assert( pk->local_id ); - } +reset_unconnected_keys (KEYDB_HANDLE hd, KeyHashTable visited) +{ + int rc; + KBNODE keyblock = NULL; + KEYDB_SEARCH_DESC desc; + int count = 0, nreset = 0; + + rc = keydb_search_reset (hd); + if (rc) + { + log_error ("keydb_search_reset failed: %s\n", g10_errstr(rc)); + return; + } + + memset (&desc, 0, sizeof desc); + desc.mode = KEYDB_SEARCH_MODE_FIRST; + desc.skipfnc = search_skipfnc; + desc.skipfncvalue = visited; + rc = keydb_search (hd, &desc, 1); + if (rc && rc != -1 ) + log_error ("keydb_search_first failed: %s\n", g10_errstr(rc)); + else if (!rc) + { + desc.mode = KEYDB_SEARCH_MODE_NEXT; /* change mode */ + do + { + rc = keydb_get_keyblock (hd, &keyblock); + if (rc) + { + log_error ("keydb_get_keyblock failed: %s\n", g10_errstr(rc)); + break; + } + count++; + + if (keyblock->pkt->pkttype == PKT_PUBLIC_KEY) /* paranoid assertion*/ + { + nreset += clear_validity (keyblock->pkt->pkt.public_key); + release_kbnode (keyblock); + } + } + while ( !(rc = keydb_search (hd, &desc, 1)) ); + if (rc && rc != -1) + log_error ("keydb_search_next failed: %s\n", g10_errstr(rc)); } - lid = pk->local_id; - - tree = build_cert_tree( lid, 0, opt.max_cert_depth, NULL ); - if( tree ) - propagate_validity( tree, tree, NULL, NULL ); - if( opt.with_colons ) - dump_tn_tree_with_colons( 0, tree ); - else - dump_tn_tree( stdout, 0, tree ); - /*printf("(alloced tns=%d max=%d)\n", alloced_tns, max_alloced_tns );*/ - release_tn_tree( tree ); - /*printf("Ownertrust=%c Validity=%c\n", get_ownertrust_info( lid ), - query_trust_info( pk, NULL ) ); */ - - free_public_key( pk ); - -} + if (opt.verbose) + log_info ("%d unconnected keys (%d trust records cleared)\n", + count, nreset); + do_sync (); +} - - -/**************** - * 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). +/* + * Run the key validation procedure. * - * 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. + * 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 * - * 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; - #if 0 - struct enum_cert_paths_ctx *ctx; - fixme: ..... tsl; - - init_trustdb(); - if( !lid ) { /* release the context */ - if( *context ) { - FIXME: ........tsl2; - - ctx = *context; - for(tsl = ctx->tsl_head; tsl; tsl = tsl2 ) { - tsl2 = tsl->next; - gcry_free( tsl ); - } - *context = NULL; - } - return -1; - } - - if( !*context ) { - FIXME .... *tmppath; - TRUSTREC rec; - - if( !*lid ) - return -1; - - ctx = gcry_xcalloc( 1, sizeof *ctx ); - *context = ctx; - /* collect the paths */ - #if 0 - read_record( *lid, &rec, RECTYPE_DIR ); - tmppath = gcry_xcalloc( 1, (opt.max_cert_depth+1)* sizeof *tmppath ); - tsl = NULL; - collect_paths( 0, opt.max_cert_depth, 1, &rec, tmppath, &tsl ); - gcry_free( tmppath ); - sort_tsl_list( &tsl ); - #endif - /* setup the context */ - ctx->tsl_head = tsl; - ctx->tsl = ctx->tsl_head; - ctx->idx = 0; - } - else - ctx = *context; - - while( ctx->tsl && ctx->idx >= ctx->tsl->pathlen ) { - ctx->tsl = ctx->tsl->next; - ctx->idx = 0; - } - tsl = ctx->tsl; - if( !tsl ) - return -1; /* eof */ - - if( ownertrust ) - *ownertrust = tsl->path[ctx->idx].otrust; - if( validity ) - *validity = tsl->path[ctx->idx].trust; - *lid = tsl->path[ctx->idx].lid; - ctx->idx++; - return ctx->idx-1; - #endif -} - - -/**************** - * Print the current path */ -void -enum_cert_paths_print( void **context, FILE *fp, - int refresh, ulong selected_lid ) -{ - return; - #if 0 - struct enum_cert_paths_ctx *ctx; - FIXME......... tsl; - - if( !*context ) - return; - init_trustdb(); - ctx = *context; - if( !ctx->tsl ) - return; - tsl = ctx->tsl; - - if( !fp ) - fp = stderr; - - if( refresh ) { /* update the ownertrust and if possible the validity */ - int i; - int match = tdbio_db_matches_options(); - - for( i = 0; i < tsl->pathlen; i++ ) { - TRUSTREC rec; - - read_record( tsl->path[i].lid, &rec, RECTYPE_DIR ); - tsl->path[i].otrust = rec.r.dir.ownertrust; - /* update validity only if we have it in the cache - * calculation is too time consuming */ - if( match && rec.r.dir.valcheck && rec.r.dir.validity ) { - tsl->path[i].trust = rec.r.dir.validity; - if( rec.r.dir.dirflags & DIRF_REVOKED ) - tsl->path[i].trust = TRUST_FLAG_REVOKED; +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 key_count; + int ot_unknown, ot_undefined, ot_never, ot_marginal, ot_full, ot_ultimate; + KeyHashTable visited; + u32 start_time, next_expire; + + start_time = make_timestamp (); + next_expire = 0xffffffff; /* set next expire to the year 2106 */ + visited = new_key_hash_table (); + /* Fixme: Instead of always building a UTK list, we could just build it + * here when needed */ + if (!utk_list) + { + log_info ("no ultimately trusted keys found\n"); + goto leave; + } + + + /* mark all UTKs as visited 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 %08lX not found\n"), (ulong)k->kid[1]); + continue; + } + mark_keyblock_seen (visited, keyblock); + pk = keyblock->pkt->pkt.public_key; + for (node=keyblock; node; node = node->next) + { + if (node->pkt->pkttype == PKT_USER_ID) + { + byte namehash[20]; + PKT_user_id *uid = node->pkt->pkt.user_id; + + if( uid->attrib_data ) + rmd160_hash_buffer (namehash,uid->attrib_data,uid->attrib_len); + else + rmd160_hash_buffer (namehash, uid->name, uid->len ); + update_validity (pk, namehash, 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; + kdb = keydb_new (0); + + for (depth=0; depth < opt.max_cert_depth; depth++) + { + /* See whether we should assign ownertrust values to the keys in + utk_list. */ + ot_unknown = ot_undefined = ot_never = 0; + ot_marginal = ot_full = ot_ultimate = 0; + for (k=klist; k; k = k->next) + { + if (interactive && k->ownertrust == TRUST_UNKNOWN) + k->ownertrust = ask_ownertrust (k->kid); + if (k->ownertrust == -1) + { + quit=1; + goto leave; } - } - } + else 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++; + } - print_path( tsl->pathlen, tsl->path, fp, selected_lid ); - #endif -} + /* Find all keys which are signed by a key in kdlist */ + keys = validate_key_list (kdb, visited, klist, start_time, &next_expire); + if (!keys) + { + log_error ("validate_key_list failed\n"); + rc = G10ERR_GENERAL; + goto leave; + } -/* - * Return an allocated buffer with the preference values for - * the key with LID and the userid which is identified by the - * HAMEHASH or the first one if namehash is NULL. ret_n receives - * the length of the allocated buffer. Structure of the buffer is - * a repeated sequences of 2 bytes; where the first byte describes the - * type of the preference and the second one the value. The constants - * PREFTYPE_xxxx should be used to reference a type. - */ -byte * -get_pref_data( ulong lid, const byte *namehash, size_t *ret_n ) -{ - TRUSTREC rec; - ulong recno; - - init_trustdb(); - read_record( lid, &rec, RECTYPE_DIR ); - for( recno=rec.r.dir.uidlist; recno; recno = rec.r.uid.next ) { - read_record( recno, &rec, RECTYPE_UID ); - if( rec.r.uid.prefrec - && ( !namehash || !memcmp(namehash, rec.r.uid.namehash, 20) )) { - byte *buf; - /* found the correct one or the first one */ - read_record( rec.r.uid.prefrec, &rec, RECTYPE_PREF ); - if( rec.r.pref.next ) - log_info(_("WARNING: can't yet handle long pref records\n")); - buf = gcry_xmalloc( ITEMS_PER_PREF_RECORD ); - memcpy( buf, rec.r.pref.data, ITEMS_PER_PREF_RECORD ); - *ret_n = ITEMS_PER_PREF_RECORD; - return buf; - } + 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); + + log_info (_("checking at depth %d signed=%d" + " ot(-/q/n/m/f/u)=%d/%d/%d/%d/%d/%d\n"), + depth, key_count, ot_unknown, ot_undefined, + ot_never, ot_marginal, ot_full, ot_ultimate ); + + for (kar=keys; kar->keyblock; kar++) + store_validation_status (depth, kar->keyblock); + + /* 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)) + { + k = new_key_item (); + keyid_from_pk (kar->keyblock->pkt->pkt.public_key, k->kid); + k->ownertrust = get_ownertrust (kar->keyblock + ->pkt->pkt.public_key); + k->next = klist; + klist = k; + break; + } + } + } + release_key_array (keys); + keys = NULL; + if (!klist) + break; /* no need to dive in deeper */ + } + + reset_unconnected_keys (kdb, visited); + + leave: + keydb_release (kdb); + release_key_array (keys); + release_key_items (klist); + release_key_hash_table (visited); + 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)); + } + do_sync (); + pending_check_trustdb = 0; } - return NULL; + return rc; } - -/**************** - * Check whether the algorithm is in one of the pref records - */ -int -is_algo_in_prefs( ulong lid, int preftype, int algo ) -{ - TRUSTREC rec; - ulong recno; - int i; - byte *pref; - - init_trustdb(); - read_record( lid, &rec, RECTYPE_DIR ); - for( recno=rec.r.dir.uidlist; recno; recno = rec.r.uid.next ) { - read_record( recno, &rec, RECTYPE_UID ); - if( rec.r.uid.prefrec ) { - read_record( rec.r.uid.prefrec, &rec, RECTYPE_PREF ); - if( rec.r.pref.next ) - log_info(_("WARNING: can't yet handle long pref records\n")); - pref = rec.r.pref.data; - for(i=0; i+1 < ITEMS_PER_PREF_RECORD; i+=2 ) { - if( pref[i] == preftype && pref[i+1] == algo ) - return 1; - } - } - } - return 0; -} - diff --git a/g10/trustdb.h b/g10/trustdb.h index 1279edb0f..c94a2daa1 100644 --- a/g10/trustdb.h +++ b/g10/trustdb.h @@ -1,5 +1,5 @@ /* trustdb.h - Trust database - * Copyright (C) 1998, 1999, 2000 Free Software Foundation, Inc. + * Copyright (C) 1998, 1999, 2000, 2001, 2002 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -18,13 +18,13 @@ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA */ -#ifndef GPG_TRUSTDB_H -#define GPG_TRUSTDB_H +#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 */ +#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 */ @@ -33,43 +33,37 @@ #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 +#define TRUST_FLAG_SUB_REVOKED 64 /* r: revoked but for subkeys */ #define TRUST_FLAG_DISABLED 128 /* d: key/uid disabled */ - - -#define PREFTYPE_SYM 1 -#define PREFTYPE_HASH 2 -#define PREFTYPE_COMPR 3 - +#define TRUST_FLAG_PENDING_CHECK 256 /* a check-trustdb is pending */ /*-- trustdb.c --*/ -void list_trust_path( const char *username ); void register_trusted_key( const char *string ); -void check_trustdb( const char *username ); -void update_trustdb( void ); +void check_trustdb (void); +void update_trustdb (void); int setup_trustdb( int level, const char *dbname ); void init_trustdb( void ); void sync_trustdb( void ); -int check_trust( PKT_public_key *pk, unsigned *r_trustlevel, - const byte* nh, int (*add_fnc)(ulong), unsigned *retflgs ); -int query_trust_info( PKT_public_key *pk, const byte *nh ); + +int trust_letter( unsigned value ); + +void revalidation_mark (void); + +unsigned int get_validity (PKT_public_key *pk, const byte *namehash); +int get_validity_info (PKT_public_key *pk, const byte *namehash); + +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 ); -unsigned get_ownertrust( ulong lid ); -int get_ownertrust_info( ulong lid ); -byte *get_pref_data( ulong lid, const byte *namehash, size_t *ret_n ); -int is_algo_in_prefs( ulong lid, int preftype, int algo ); -int keyid_from_lid( ulong lid, u32 *keyid ); -ulong lid_from_keyblock( KBNODE keyblock ); -int query_trust_record( PKT_public_key *pk ); -int clear_trust_checked_flag( PKT_public_key *pk ); -int update_trust_record( KBNODE keyblock, int fast, int *modified ); -int insert_trust_record( KBNODE keyblock ); -int insert_trust_record_by_pk( PKT_public_key *pk ); -int update_ownertrust( ulong lid, unsigned new_trust ); -int trust_letter( unsigned value ); + +unsigned int get_ownertrust (PKT_public_key *pk); +int get_ownertrust_info (PKT_public_key *pk); +void update_ownertrust (PKT_public_key *pk, unsigned int new_trust ); +int clear_ownertrust (PKT_public_key *pk); + /*-- tdbdump.c --*/ void list_trustdb(const char *username); @@ -77,6 +71,6 @@ void export_ownertrust(void); void import_ownertrust(const char *fname); /*-- pkclist.c --*/ -int edit_ownertrust( ulong lid, int mode ); +int edit_ownertrust (PKT_public_key *pk, int mode ); -#endif /*GPG_TRUSTDB_H*/ +#endif /*G10_TRUSTDB_H*/ diff --git a/g10/verify.c b/g10/verify.c index f3f9a36eb..7ab20c140 100644 --- a/g10/verify.c +++ b/g10/verify.c @@ -1,5 +1,5 @@ /* verify.c - verify signed data - * Copyright (C) 1998, 1999, 2000 Free Software Foundation, Inc. + * Copyright (C) 1998, 1999, 2000, 2001 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -24,13 +24,14 @@ #include <string.h> #include <errno.h> #include <assert.h> +#include <unistd.h> /* for isatty() */ -#include <gcrypt.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" @@ -60,13 +61,38 @@ verify_signatures( int nfiles, char **files ) 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 ) { log_error(_("can't open `%s'\n"), print_fname_stdin(sigfile)); - return GPGERR_OPEN_FILE; + return G10ERR_OPEN_FILE; } if( !opt.no_armor && use_armor_filter( fp ) ) @@ -89,13 +115,13 @@ verify_signatures( int nfiles, char **files ) } -static void +void print_file_status( int status, const char *name, int what ) { - char *p = gcry_xmalloc(strlen(name)+10); + char *p = m_alloc(strlen(name)+10); sprintf(p, "%d %s", what, name ); write_status_text( status, p ); - gcry_free(p); + m_free(p); } @@ -111,7 +137,7 @@ verify_one_file( const char *name ) if( !fp ) { print_file_status( STATUS_FILE_ERROR, name, 1 ); log_error(_("can't open `%s'\n"), print_fname_stdin(name)); - return GPGERR_OPEN_FILE; + return G10ERR_OPEN_FILE; } if( !opt.no_armor ) { @@ -145,7 +171,7 @@ verify_files( int nfiles, char **files ) lno++; if( !*line || line[strlen(line)-1] != '\n' ) { log_error(_("input line %u too long or missing LF\n"), lno ); - return GPGERR_GENERAL; + 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 @@ -161,4 +187,3 @@ verify_files( int nfiles, char **files ) } return 0; } - |