summaryrefslogtreecommitdiff
path: root/sm
diff options
context:
space:
mode:
authorAnas Nashif <anas.nashif@intel.com>2013-03-27 09:15:23 -0700
committerAnas Nashif <anas.nashif@intel.com>2013-03-27 09:15:23 -0700
commit6576640b55777bd811a12a188b9b1f3c63653799 (patch)
tree7dab5ece3a5bf7ed238e8b0824194ce01b61121e /sm
downloadgpg2-6576640b55777bd811a12a188b9b1f3c63653799.tar.gz
gpg2-6576640b55777bd811a12a188b9b1f3c63653799.tar.bz2
gpg2-6576640b55777bd811a12a188b9b1f3c63653799.zip
Imported Upstream version 2.0.19upstream/2.0.19
Diffstat (limited to 'sm')
-rw-r--r--sm/ChangeLog-20112705
-rw-r--r--sm/Makefile.am71
-rw-r--r--sm/Makefile.in711
-rw-r--r--sm/base64.c733
-rw-r--r--sm/call-agent.c1069
-rw-r--r--sm/call-dirmngr.c1126
-rw-r--r--sm/certchain.c2044
-rw-r--r--sm/certcheck.c439
-rw-r--r--sm/certdump.c974
-rw-r--r--sm/certlist.c555
-rw-r--r--sm/certreqgen-ui.c415
-rw-r--r--sm/certreqgen.c884
-rw-r--r--sm/decrypt.c586
-rw-r--r--sm/delete.c182
-rw-r--r--sm/encrypt.c513
-rw-r--r--sm/export.c749
-rw-r--r--sm/fingerprint.c347
-rw-r--r--sm/gpgsm.c2181
-rw-r--r--sm/gpgsm.h423
-rw-r--r--sm/import.c807
-rw-r--r--sm/keydb.c1535
-rw-r--r--sm/keydb.h86
-rw-r--r--sm/keylist.c1562
-rw-r--r--sm/misc.c89
-rw-r--r--sm/qualified.c321
-rw-r--r--sm/server.c1464
-rw-r--r--sm/sign.c780
-rw-r--r--sm/verify.c660
28 files changed, 24011 insertions, 0 deletions
diff --git a/sm/ChangeLog-2011 b/sm/ChangeLog-2011
new file mode 100644
index 0000000..4efea96
--- /dev/null
+++ b/sm/ChangeLog-2011
@@ -0,0 +1,2705 @@
+2011-12-02 Werner Koch <wk@g10code.com>
+
+ NB: ChangeLog files are no longer manually maintained. Starting
+ on December 1st, 2011 we put change information only in the GIT
+ commit log, and generate a top-level ChangeLog file from logs at
+ "make dist". See doc/HACKING for details.
+
+2011-08-04 Werner Koch <wk@g10code.com>
+
+ * keydb.c (keydb_add_resource): Remove set but unused var
+ CREATED_FNAME.
+ * gpgsm.c (main): Remove set but used var FNAME.
+
+2011-07-21 Werner Koch <wk@g10code.com>
+
+ * call-dirmngr.c (get_cached_cert, get_cached_cert_data_cb): New.
+ (gpgsm_dirmngr_isvalid): Try to get the only-valid-if-cert-valid
+ certificate from the dirmngr first.
+
+2010-09-16 Werner Koch <wk@g10code.com>
+
+ * certchain.c (gpgsm_walk_cert_chain): Use GPG_ERR_MISSING_ISSUER_CERT.
+ (do_validate_chain): Ditto.
+ (gpgsm_basic_cert_check): Ditto.
+ * call-agent.c (learn_cb): Take care of new
+ GPG_ERR_MISSING_ISSUER_CERT.
+ * import.c (check_and_store): Ditto.
+ (check_and_store): Ditto.
+
+2010-05-12 Werner Koch <wk@g10code.com>
+
+ * Makefile.am (gpgsm_LDADD): Include NETLIBS which is required for
+ Solaris.
+
+2010-03-12 Werner Koch <wk@g10code.com>
+
+ * server.c (cmd_passwd): New. From trunk.
+ (register_commands): Register it.
+
+2010-02-11 Marcus Brinkmann <marcus@g10code.de>
+
+ From trunk 2009-09-23, 2009-11-02, 2009-11-04, 2009-11-05, 2009-11-25,
+ 2009-12-08:
+
+ * call-agent.c (membuf_data_cb, default_inq_cb)
+ (inq_ciphertext_cb, scd_serialno_status_cb)
+ (scd_keypairinfo_status_cb, istrusted_status_cb)
+ (learn_status_cb, learn_cb, keyinfo_status_cb): Return gpg_error_t.
+ * gpgsm.c (main): Update to new assuan API.
+ * server.c: Include "gpgsm.h" before <assuan.h> due to check for
+ GPG_ERR_SOURCE_DEFAULT and assuan.h now including gpg-error.h.
+ * server.c (reset_notify, input_notify, output_notify): Update to
+ new assuan interface.
+ (option_handler, cmd_recipient, cmd_signer, cmd_encrypt)
+ (cmd_decrypt, cmd_verify, cmd_sign, cmd_import, cmd_export)
+ (cmd_delkeys, cmd_message, cmd_listkeys, cmd_dumpkeys)
+ (cmd_listsecretkeys, cmd_dumpsecretkeys, cmd_genkey)
+ (cmd_getauditlog, cmd_getinfo): Return gpg_error_t instead of int.
+ (register_commands): Use assuan_handler_t. Same for member HANDLER
+ in table. Add NULL arg to assuan_register_command. Add help arg to
+ assuan_register_command. Provide help strings for all commands.
+ (gpgsm_server): Allocate assuan context before starting server.
+ Use assuan_fd_t and assuan_fdopen on fds.
+ * call-dirmngr.c (prepare_dirmngr): Check for CTX and error before
+ setting LDAPSERVER.
+ (start_dirmngr_ext): Allocate assuan context before starting
+ server. Update use ofassuan_pipe_connect and assuan_socket_connect.
+ Convert posix fd to assuan fd.
+ (inq_certificate, isvalid_status_cb, lookup_cb, lookup_status_cb)
+ (run_command_cb, run_command_inq_cb, run_command_status_cb):
+ Return gpg_error_t instead of int.
+
+2009-12-10 Werner Koch <wk@g10code.com>
+
+ * gpgsm.c: Add option --ignore-cert-extension.
+ * gpgsm.h (opt): Add field IGNORED_CERT_EXTENSIONS.
+ * certchain.c (unknown_criticals): Handle ignored extensions,
+
+2009-12-03 Werner Koch <wk@g10code.com>
+
+ From trunk:
+
+ * verify.c (gpgsm_verify): Add audit info on hash algorithms.
+ * sign.c (gpgsm_sign): Add audit log calls.
+ (hash_data): Return an error indicator.
+ * decrypt.c (gpgsm_decrypt): Add audit log calls.
+
+ * gpgsm.c: New option --html-audit-log.
+
+ * certreqgen.c (proc_parameters): Change fallback key length to
+ 2048.
+ * gpgsm.c (main) <aGpgConfList>: Add key "default_pubkey_algo".
+
+2009-12-03 Werner Koch <wk@g10code.com>
+
+ * gpgsm.c (set_debug): Allow for numerical debug levels. Print
+ active debug flags.
+
+2009-10-16 Werner Koch <wk@g10code.com>
+
+ * gpgsm.c (DEFAULT_INCLUDE_CERTS): New.
+ (default_include_certs): Init to -2.
+
+2009-08-06 Werner Koch <wk@g10code.com>
+
+ * sign.c (gpgsm_sign): Print INV_SNDR for a bad default key.
+
+ * server.c (cmd_signer): Remove unneeded case for -1. Send
+ INV_SGNR. Use new map function.
+ (cmd_recipient): Use new map function.
+ * gpgsm.c (do_add_recipient): Use new map function for INV_RECP.
+ (main): Ditto. Also send INV_SGNR.
+
+2009-07-30 Werner Koch <wk@g10code.com>
+
+ * call-agent.c (learn_cb): Do not store as ephemeral.
+
+2009-07-29 Marcus Brinkmann <marcus@g10code.com>
+
+ * keylist.c (print_capabilities): Print a trailing colon.
+
+2009-07-23 Werner Koch <wk@g10code.com>
+
+ * certchain.c (is_cert_still_valid): Emit AUDIT_CRL_CHECK.
+
+2009-07-07 Werner Koch <wk@g10code.com>
+
+ * server.c (command_has_option): New.
+ (cmd_getinfo): Add subcommand "cmd_has_option".
+ (cmd_import): Implement option --re-import.
+ * import.c (gpgsm_import): Add arg reimport_mode.
+ (reimport_one): New.
+
+ * gpgsm.h: Include session-env.h.
+ (opt): Add field SESSION_ENV. Remove obsolete fields.
+ * server.c (option_handler): Rewrite setting of option fields.
+ Replace strdup by xtrystrdup.
+ * gpgsm.c (set_opt_session_env): New.
+ (main): Use it for oDisplay, oTTYname, oTTYtype and oXauthority.
+ * call-agent.c (start_agent): Adjust start_new_gpg_agent for
+ changed args.
+ * misc.c (setup_pinentry_env): Use new session_env stuff.
+
+2009-07-02 Werner Koch <wk@g10code.com>
+
+ * certreqgen-ui.c (gpgsm_gencertreq_tty): Allow using a key from a
+ card.
+ * call-agent.c (gpgsm_agent_scd_serialno)
+ (scd_serialno_status_cb, store_serialno): New.
+ (scd_keypairinfo_status_cb, gpgsm_agent_scd_keypairinfo): New.
+
+2009-07-01 Werner Koch <wk@g10code.com>
+
+ * certreqgen-ui.c (check_keygrip): New.
+ (gpgsm_gencertreq_tty): Allow using an existing key.
+
+ * gpgsm.c (open_es_fread): New.
+ (main) <aKeygen>: Implement --batch mode.
+
+2009-06-24 Werner Koch <wk@g10code.com>
+
+ * call-dirmngr.c (pattern_from_strlist): Remove dead assignment of N.
+ * sign.c (gpgsm_sign): Remove dead assignment.
+ * certreqgen.c (create_request): Assign GPG_ERR_BUG to RC.
+ Reported by Fabian Keil.
+
+2009-05-27 Werner Koch <wk@g10code.com>
+
+ * encrypt.c (encrypt_dek): Make use of make_canon_sexp.
+
+2009-05-18 Werner Koch <wk@g10code.com>
+
+ * server.c (option_handler): New option "no-encrypt-to".
+ (cmd_encrypt): Make use of it.
+
+ * gpgsm.c: Remove not implemented --verify-files.
+
+2009-04-02 Werner Koch <wk@g10code.com>
+
+ * keylist.c (list_cert_std): Print card serial number.
+
+2009-04-01 Werner Koch <wk@g10code.com>
+
+ * export.c (popen_protect_tool): Add command line option
+ --agent-program and pass flag bit 6.
+ * import.c (popen_protect_tool): Ditto.
+
+2009-03-26 Werner Koch <wk@g10code.com>
+
+ * gpgsm.c (main): s/def_digest_string/forced_digest_algo/ and
+ activate the --digest-algo option.
+ * gpgsm.h (struct opt): s/def_digest_algo/forced_digest_algo/.
+ * sign.c (gpgsm_sign): Implement --digest-algo.
+
+ * sign.c (MAX_DIGEST_LEN): Change to 64.
+
+ * call-agent.c (gpgsm_agent_marktrusted): Format the issuer name.
+
+2009-03-25 Werner Koch <wk@g10code.com>
+
+ * decrypt.c (gpgsm_decrypt): Print ENC_TO and NO_SECKEY stati.
+ Fixes bug#1020.
+ * fingerprint.c (gpgsm_get_short_fingerprint): Add arg R_HIGH and
+ change all callers.
+
+2009-03-23 Werner Koch <wk@g10code.com>
+
+ * delete.c (delete_one): Also delete ephemeral certificates if
+ specified uniquely.
+
+2009-03-20 Werner Koch <wk@g10code.com>
+
+ * keylist.c (list_internal_keys): Set released cert to NULL.
+
+ * call-agent.c (learn_status_cb): New.
+ (gpgsm_agent_learn): Use it.
+ (learn_cb): Send a progress for every certificate.
+
+2009-03-18 Werner Koch <wk@g10code.com>
+
+ * gpgsm.h (struct opt): Move field WITH_EPHEMERAL_KEYS to struct
+ server_control_s.
+ * gpgsm.c (main): Change accordingly.
+ * keylist.c (list_internal_keys): Ditto.
+ * server.c (option_handler): Add "with-ephemeral-keys".
+
+2009-03-12 Werner Koch <wk@g10code.com>
+
+ * certdump.c (gpgsm_dump_time): Remove.
+ * certdump.c, verify.c, certchain.c
+ * gpgsm.c: s/gpgsm_dump_time/dump_isotime/.
+
+2009-03-06 Werner Koch <wk@g10code.com>
+
+ * call-agent.c (gpgsm_agent_keyinfo, keyinfo_status_cb): New.
+ * keylist.c (list_cert_colon): Print card S/N.
+
+ * keylist.c (list_internal_keys): Always list ephemeral keys if
+ specified by keygrip or fingerprint.
+ (list_cert_raw): Always show ephemeral flag.
+ * export.c (gpgsm_export): Export ephemeral keys if specified by
+ keygrip.
+
+2009-02-09 Werner Koch <wk@g10code.com>
+
+ * gpgsm.c (main): Change default cipher back to 3DES.
+
+2009-01-12 Werner Koch <wk@g10code.com>
+
+ * keylist.c (print_utf8_extn_raw): Cast printf precision argument.
+
+2009-01-08 Werner Koch <wk@g10code.com>
+
+ * fingerprint.c (gpgsm_get_keygrip_hexstring): Add error detection.
+
+2008-12-10 Werner Koch <wk@g10code.com>
+
+ * gpgsm.c (our_cipher_test_algo): Use the GCRY constants as we now
+ require 1.4.
+ (our_md_test_algo): Ditto. Add SHA224.
+ (main) <aGpgConfList>: Update default cipher algo.
+
+2008-12-09 Werner Koch <wk@g10code.com>
+
+ * gpgsm.c (main): Call i18n_init before init_common_subsystems.
+
+2008-12-05 Werner Koch <wk@g10code.com>
+
+ * certreqgen.c (create_request): Provide a custom prompt for the
+ signing.
+
+ * certdump.c (gpgsm_format_keydesc): Remove debug output.
+ (gpgsm_format_keydesc): Remove saving of errno as xfree is
+ supposed not to change it. Use the new percent_plus_escape
+ function which also fixes the issue that we did not escaped a
+ percent in the past.
+
+2008-11-18 Werner Koch <wk@g10code.com>
+
+ * gpgsm.c (make_libversion): New.
+ (my_strusage): Use new function.
+ (build_lib_list): Remove.
+
+2008-11-13 Werner Koch <wk@g10code.com>
+
+ * gpgsm.c: Remove all unused options. Use ARGPARSE macros.
+
+2008-10-28 Werner Koch <wk@g10code.com>
+
+ * certdump.c (gpgsm_format_keydesc): Use xtryasprintf and xfree.
+ (gpgsm_es_print_name): Factor code out to ...
+ (gpgsm_es_print_name2): New function.
+ (gpgsm_format_name2, format_name_writer): Use estream so that it
+ works on all platforms.
+ (format_name_writer): Fix reallocation bug.
+
+2008-10-23 Werner Koch <wk@g10code.com>
+
+ * import.c (popen_protect_tool): Add arg CTRL and assure that the
+ agent is running. Pass a value for CTRL from all caller.
+ * export.c (popen_protect_tool): Ditto.
+
+2008-10-21 Werner Koch <wk@g10code.com>
+
+ * call-dirmngr.c (inq_certificate_parm_s): Add field CTRL.
+ (gpgsm_dirmngr_isvalid): Supply a value for that field.
+ (inq_certificate): Add inquiry ISTRUSTED.
+
+ * call-agent.c (gpgsm_agent_istrusted): Add new optional arg
+ HEXFPR. Changed all callers.
+
+2008-10-20 Werner Koch <wk@g10code.com>
+
+ * keydb.c (keydb_locate_writable): Mark unused arg.
+ (keydb_search_kid): Ditto.
+ (keydb_clear_some_cert_flags): Ditto.
+ * server.c (cmd_encrypt): Ditto.
+ (cmd_decrypt, cmd_verify, cmd_import, cmd_genkey): Ditto.
+ * call-agent.c (gpgsm_scd_pksign): Ditto.
+ * call-dirmngr.c (release_dirmngr, release_dirmngr2)
+ (run_command_cb): Ditto.
+ * certlist.c (gpgsm_add_cert_to_certlist): Ditto.
+ * certchain.c (find_up_dirmngr): Ditto.
+ * keylist.c (print_key_data): Ditto.
+ (list_cert_raw, list_cert_std): Ditto.
+ * qualified.c (gpgsm_is_in_qualified_list): Ditto.
+
+ * gpgsm.c (set_binary) [!W32]: Mark unused arg.
+
+2008-10-17 Werner Koch <wk@g10code.com>
+
+ * call-dirmngr.c (start_dirmngr, start_dirmngr2): Reset the lock
+ flag on error.
+ (release_dirmngr, release_dirmngr2): Replace asserts by error messages.
+ (gpgsm_dirmngr_lookup): Replace assert by fatal error message.
+
+2008-10-13 Werner Koch <wk@g10code.com>
+
+ * gpgsm.c: Add alias --delete-keys.
+
+2008-09-30 Werner Koch <wk@g10code.com>
+
+ * server.c (cmd_getinfo): New subcommand agent-check.
+ * call-agent.c (gpgsm_agent_send_nop): New.
+
+2008-09-29 Werner Koch <wk@g10code.com>
+
+ * certcheck.c (MY_GCRY_PK_ECDSA): Remove. Change users to
+ GCRY_PK_ECDSA.
+ * gpgsm.c (MY_GCRY_PK_ECDSA): Ditto.
+ * sign.c (MY_GCRY_MD_SHA224): Remove change users to GCRY_MD_SHA224.
+
+2008-09-04 Werner Koch <wk@g10code.com>
+
+ * certdump.c (gpgsm_format_keydesc): Work around a mingw32 bug.
+
+2008-09-03 Werner Koch <wk@g10code.com>
+
+ * sign.c (MY_GCRY_MD_SHA224): New, so that we don't need libgcrypt
+ 1.2.
+
+2008-08-13 Werner Koch <wk@g10code.com>
+
+ * keylist.c (list_cert_colon): Print 'f' for validated certs.
+
+2008-08-08 Marcus Brinkmann <marcus@g10code.de>
+
+ * gpgsm.h (struct server_control_s): Remove member dirmngr_seen.
+ * call-dirmngr.c (dirmngr2_ctx, dirmngr_ctx_locked)
+ (dirmngr2_ctx_locked): New global variables.
+ (prepare_dirmngr): Don't check dirmngr_seen anymore.
+ (start_dirmngr): Move bunch of code to ...
+ (start_dirmngr_ext): ... this new function.
+ (release_dirmngr, start_dirmngr2, release_dirmngr2): New
+ functions.
+ (gpgsm_dirmngr_isvalid): Call release_dirmngr.
+ (gpgsm_dirmngr_lookup): Call release_dirmngr. If dirmngr_ctx is
+ locked, use dirmngr2_locked.
+ (gpgsm_dirmngr_run_command): Call release_dirmngr.
+
+2008-06-25 Werner Koch <wk@g10code.com>
+
+ * sign.c (gpgsm_sign): Revamp the hash algorithm selection.
+ * gpgsm.h (struct certlist_s): Add field HASH_ALGO and HASH_ALGO_OID.
+
+ * qualified.c (gpgsm_qualified_consent): Fix double free.
+
+ * gpgsm.c (main): Change default cipher algo to AES.
+
+ * keylist.c (print_utf8_extn_raw, print_utf8_extn): New.
+ (list_cert_raw, list_cert_std): Print the TeleSec restriction
+ extension.
+
+2008-06-23 Werner Koch <wk@g10code.com>
+
+ * encrypt.c (encode_session_key): Replace xmalloc by xtrymalloc.
+ Use bin2hex instead of open coding the conversion.
+ (encrypt_dek): Init S_DATA.
+
+2008-06-13 Marcus Brinkmann <marcus@ulysses.g10code.com>
+
+ * call-dirmngr.c (prepare_dirmngr): Fix error code to ignore.
+
+2008-06-12 Marcus Brinkmann <marcus@g10code.de>
+
+ * gpgsm.h (struct keyserver_spec): New struct.
+ (opt): Add member keyserver.
+ * gpgsm.c (keyserver_list_free, parse_keyserver_line): New functions.
+ (main): Implement --keyserver option.
+ * call-dirmngr.c (prepare_dirmngr): Send LDAPSERVER commands.
+
+2008-05-20 Werner Koch <wk@g10code.com>
+
+ * gpgsm.c (main) <aExportSecretKeyP12>: Pass FP and not stdout to
+ the export function. Reported by Marc Mutz.
+
+2008-05-06 Werner Koch <wk@g10code.com>
+
+ * keylist.c (list_external_keys): Ignore NOT FOUND error code.
+ This is bug#907.
+
+2008-04-23 Werner Koch <wk@g10code.com>
+
+ * certchain.c (find_up): Make correct C89 code. Declare variable
+ at the top of the block. Reported by Alain Guibert.
+
+2008-04-09 Werner Koch <wk@g10code.com>
+
+ * verify.c (gpgsm_verify): Print the message hash values on error.
+
+2008-03-31 Werner Koch <wk@g10code.com>
+
+ * call-dirmngr.c (start_dirmngr): Use log_info instead of
+ log_error when falling back to start dirmngr.
+
+2008-03-20 Werner Koch <wk@g10code.com>
+
+ * certlist.c (gpgsm_add_to_certlist): Always save the first
+ subject and issuer. Initialize issuer with issuer and not with
+ subject.
+ (same_subject_issuer): Set issuer2 to issuer and not to subject.
+
+2008-03-17 Werner Koch <wk@g10code.com>
+
+ * certdump.c (my_funopen_hook_size_t): New.
+ (format_name_writer): Use it.
+
+2008-03-13 Werner Koch <wk@g10code.com>
+
+ * certdump.c (gpgsm_fpr_and_name_for_status): Fix signed/unsigned
+ char issue.
+ (gpgsm_format_keydesc): Remove superfluous test. Add expire date
+ to the prompt.
+
+2008-02-18 Werner Koch <wk@g10code.com>
+
+ * certchain.c (gpgsm_is_root_cert): Factor code out to ...
+ (is_root_cert): New. Extend test for self-issued certificates
+ signed by other CAs.
+ (do_validate_chain, gpgsm_basic_cert_check)
+ (gpgsm_walk_cert_chain): Use it here.
+
+ * gpgsm.c: Add option --no-common-certs-import.
+
+ * certchain.c (find_up_dirmngr, find_up, do_validate_chain)
+ (check_cert_policy): Be more silent with --quiet.
+
+ * gpgsm.c: Add option --disable-dirmngr.
+ * gpgsm.h (opt): Add field DISABLE_DIRMNGR.
+ * call-dirmngr.c (start_dirmngr): Implement option.
+
+2008-02-14 Werner Koch <wk@g10code.com>
+
+ * server.c (option_handler): Add option allow-pinentry-notify.
+ (gpgsm_proxy_pinentry_notify): New.
+ * call-agent.c (default_inq_cb): New.
+ (gpgsm_agent_pksign, gpgsm_scd_pksign, gpgsm_agent_readkey)
+ (gpgsm_agent_istrusted, gpgsm_agent_marktrusted)
+ (gpgsm_agent_passwd, gpgsm_agent_get_confirmation): Call it.
+ (struct cipher_parm_s, struct genkey_parm_s): Add field CTRL.
+ (inq_ciphertext_cb): Test keyword and fallback to default_inq_cb.
+ (inq_genkey_parms): Ditto.
+ (start_agent): Tell agent to send us the pinentry notifications.
+
+2008-02-13 Werner Koch <wk@g10code.com>
+
+ * call-dirmngr.c (gpgsm_dirmngr_lookup): Add arg CACHE_ONLY.
+ * keylist.c (list_external_keys): Pass false for new arg.
+ * certchain.c (find_up_dirmngr): New.
+ (find_up): Also try to read from the dirmngr cache.
+ (find_up, find_up_external, gpgsm_walk_cert_chain)
+ (gpgsm_basic_cert_check, allowed_ca): Add arg CTRL and changed all
+ callers.
+ * call-agent.c (struct learn_parm_s): Add field CTRL.
+ (gpgsm_agent_learn): Set it.
+
+2008-02-11 Werner Koch <wk@g10code.com>
+
+ * server.c (cmd_getinfo): New.
+ (gpgsm_server): Register GETINFO.
+
+2008-01-29 Marcus Brinkmann <marcus@g10code.de>
+
+ * keylist.c (list_internal_keys): New variable lastcert. Use it
+ to suppress duplicates which immediately follow each other.
+
+2008-01-27 Werner Koch <wk@g10code.com>
+
+ * import.c (popen_protect_tool): Set bit 7 in the flags for
+ gnupg_spawn_process so that under W32 no window appears.
+ * export.c (popen_protect_tool): Ditto.
+
+2007-12-13 Werner Koch <wk@g10code.com>
+
+ * gpgsm.c (main): Add option --extra-digest-algo.
+ * gpgsm.h (struct): Add EXTRA_DIGEST_ALGO.
+ * verify.c (gpgsm_verify): Use it. Use the hash algorithm from
+ the signature value.
+
+2007-12-11 Werner Koch <wk@g10code.com>
+
+ * certchain.c (do_validate_chain): Log AUDIT_ROOT_TRUSTED.
+
+ * server.c (cmd_sign, cmd_decrypt, cmd_encrypt): Start audit log.
+ (cmd_recipient): Start audit session.
+
+ * gpgsm.c (main): Revamp creation of the audit log.
+
+ * gpgsm.h (struct server_control_s): Add AGENT_SEEN and DIRMNGR_SEEN.
+ * call-agent.c (start_agent): Record an audit event.
+ * call-dirmngr.c (start_dirmngr): Ditto. Add new arg CTRL and pass
+ it from all callers.
+ (prepare_dirmngr): New helper for start_dirmngr.
+
+ * encrypt.c (gpgsm_encrypt): Add calls to audit_log.
+
+2007-12-03 Werner Koch <wk@g10code.com>
+
+ * gpgsm.c (main): Call gnupg_reopen_std.
+
+h2007-11-22 Werner Koch <wk@g10code.com>
+
+ * server.c (cmd_getauditlog): New.
+ (register_commands): Register GETAUDITLOG.
+
+2007-11-19 Werner Koch <wk@g10code.com>
+
+ * server.c (cmd_recipient, cmd_signer): Add error reason 11.
+
+ * gpgsm.c (main): Print a warning if --audit-log is used.
+
+2007-11-15 Werner Koch <wk@g10code.com>
+
+ * gpgsm.h (struct): Add XAUTHORITY and PINENTRY_USER_DATA.
+ * misc.c (setup_pinentry_env): Add XAUTHORITY and PINENTRY_USER_DATA.
+ * gpgsm.c (main): New option --xauthority.
+ * call-agent.c (start_agent): Adjust for changed start_new_gpg_agent.
+ * server.c (option_handler): Ad the new options.
+
+2007-11-07 Werner Koch <wk@g10code.com>
+
+ * gpgsm.c (main): New option --audit-log.
+ * server.c (option_handler): New option enable-audit-log.
+ (start_audit_session): New.
+ (cmd_verify): Create audit context.
+ (gpgsm_server): Release the context.
+
+ * gpgsm.h (struct server_control_s): Add member AUDIT, include
+ audit.h.
+ * certdump.c (gpgsm_format_sn_issuer): New.
+ * verify.c (hash_data): Return an error code.
+ (gpgsm_verify): Add calls to audit_log.
+
+ * gpgsm.c (get_status_string): Remove.
+ * gpgsm.h: Include status.h instead of errors.h.
+
+2007-10-19 Werner Koch <wk@g10code.com>
+
+ * qualified.c (gpgsm_qualified_consent): Use i18N-swicth functions.
+ (gpgsm_not_qualified_warning): Ditto.
+ * certdump.c (gpgsm_format_keydesc): Ditto.
+
+2007-09-14 Werner Koch <wk@g10code.com>
+
+ * gpgsm.c (build_lib_list): New.
+ (my_strusage): Print lib info.
+
+2007-08-24 Werner Koch <wk@g10code.com>
+
+ * Makefile.am (common_libs): Swap libkeybox and jnlib.
+
+2007-08-23 Werner Koch <wk@g10code.com>
+
+ * certlist.c (gpgsm_certs_identical_p): New.
+ (gpgsm_add_to_certlist): Ignore duplicate certificates in
+ ambigious name detection.
+ (gpgsm_find_cert): Ditto.
+ * export.c (gpgsm_p12_export): Ditto.
+
+2007-08-22 Werner Koch <wk@g10code.com>
+
+ * certreqgen.c (create_request): Replace open coding by bin2hex.
+
+ * certreqgen-ui.c (gpgsm_gencertreq_tty): Use es_fopenmem.
+
+2007-08-21 Werner Koch <wk@g10code.com>
+
+ * import.c (parse_p12): Use gnupg_tmpfile.
+ * export.c (export_p12): Ditto.
+
+2007-08-20 Werner Koch <wk@g10code.com>
+
+ * certreqgen.c (read_parameters): Change FP to an estream_t.
+ (gpgsm_genkey): Replace in_fd and in_stream by a estream_t.
+ * server.c (cmd_genkey): Adjust for that.
+ * certreqgen-ui.c (gpgsm_gencertreq_tty): Use es_open_memstream
+ instead of a temporary file.
+
+2007-08-14 Werner Koch <wk@g10code.com>
+
+ * call-dirmngr.c (start_dirmngr): Use dirmngr_socket_name. change
+ the way infostr is xstrdupped.
+
+ * gpgsm.c (main) [W32]: Make --prefer-system-dirmngr a dummy under
+ Windows.
+
+2007-08-13 Werner Koch <wk@g10code.com>
+
+ * gpgsm.c (do_add_recipient): Add RECP_REQUIRED and make error
+ message depend on that.
+ (main): Add avriable RECP_REQUIRED, set ift for encryption
+ commands and pass it to do_add_recipient.
+ (our_pk_test_algo, our_cipher_test_algo, our_md_test_algo): Implement.
+
+2007-08-09 Werner Koch <wk@g10code.com>
+
+ * gpgsm.c (main) [W32]: Enable CRL check by default.
+ (main): Update the default control structure after reading the
+ options.
+ (gpgsm_parse_validation_model, parse_validation_model): New.
+ (main): New option --validation-model.
+ * certchain.c (gpgsm_validate_chain): Implement this option.
+ * server.c (option_handler): Ditto.
+
+ * certchain.c (is_cert_still_valid): Reformatted. Add arg
+ FORCE_OCSP. Changed callers to set this flag when using the chain
+ model.
+
+2007-08-08 Werner Koch <wk@g10code.com>
+
+ * certdump.c (gpgsm_print_serial): Fixed brown paper bag style bugs
+ which prefixed the output with a 3A and cut it off at a 00.
+
+ * keylist.c (list_cert_raw): Print the certificate ID first and
+ rename "Serial number" to "S/N".
+ (list_cert_std): Ditto.
+
+2007-08-07 Werner Koch <wk@g10code.com>
+
+ * gpgsm.c (main): Allow a string for --faked-system-time.
+
+2007-08-06 Werner Koch <wk@g10code.com>
+
+ Implementation of the chain model.
+
+ * gpgsm.h (struct rootca_flags_s): Define new members VALID and
+ CHAIN_MODEL.
+ * call-agent.c (gpgsm_agent_istrusted): Mark ROOTCA_FLAGS valid.
+ (istrusted_status_cb): Set CHAIN_MODEL.
+ * certchain.c (gpgsm_validate_chain): Replace LM alias by LISTMODE
+ and FP by LISTFP.
+ (gpgsm_validate_chain): Factor some code out to ...
+ (check_validity_period, ask_marktrusted): .. new.
+ (check_validity_cm_basic, check_validity_cm_main): New.
+ (do_validate_chain): New with all code from gpgsm_validate_chain.
+ New arg ROOTCA_FLAGS.
+ (gpgsm_validate_chain): Provide ROOTCA_FLAGS and fallback to chain
+ model. Add RETFLAGS arg and changed all callers to pass NULL. Add
+ CHECKTIME arg and changed all callers to pass a nil value.
+ (has_validity_model_chain): New.
+ * verify.c (gpgsm_verify): Check for chain model and return as
+ part of the trust status.
+
+ * gpgsm.h (VALIDATE_FLAG_NO_DIRMNGR): New.
+ (VALIDATE_FLAG_NO_DIRMNGR): New.
+ * call-dirmngr.c (gpgsm_dirmngr_isvalid): Use constant here.
+
+2007-08-03 Werner Koch <wk@g10code.com>
+
+ * keylist.c (list_cert_colon): Avoid duplicate listing of kludge
+ uids.
+
+ * verify.c (gpgsm_verify): Make STATUS_VERIFY return the hash and
+ pk algo.
+ * certcheck.c (gpgsm_check_cms_signature): Add arg R_PKALGO.
+
+2007-08-02 Werner Koch <wk@g10code.com>
+
+ * gpgsm.c (main): Factored GC_OPT_FLAGS out to gc-opt-flags.h.
+
+2007-07-17 Werner Koch <wk@g10code.com>
+
+ * gpgsm.c (main): Implement --default-key.
+ (main) <gpgconf-list>: Declare --default-key and --encrypt-to.
+
+2007-07-16 Werner Koch <wk@g10code.com>
+
+ * server.c (cmd_message): Use gnupg_fd_t to avoid dependecy on
+ newer assuan versions.
+
+2007-07-12 Werner Koch <wk@g10code.com>
+
+ * gpgsm.c (check_special_filename): Use translate_sys2libc_fd_int
+ when passing an int value.
+ * server.c (cmd_encrypt, cmd_decrypt, cmd_verify, cmd_import)
+ (cmd_export, cmd_message, cmd_genkey): Translate file descriptors.
+
+2007-07-05 Werner Koch <wk@g10code.com>
+
+ * Makefile.am (common_libs): Changed order of libs.
+
+2007-07-04 Werner Koch <wk@g10code.com>
+
+ * certchain.c (check_cert_policy): Remove extra checks for
+ GPG_ERR_NO_VALUE. They are not needed since libksba 1.0.1.
+ * keylist.c (print_capabilities, list_cert_raw, list_cert_std): Ditto.
+ * certlist.c (cert_usage_p, cert_usage_p): Ditto.
+
+2007-06-26 Werner Koch <wk@g10code.com>
+
+ * gpgsm.c (main): Call gnupg_rl_initialize.
+ * Makefile.am (gpgsm_LDADD): Add LIBREADLINE and libgpgrl.a.
+
+2007-06-25 Werner Koch <wk@g10code.com>
+
+ * gpgsm.c (check_special_filename): Use translate_sys2libc_fd and
+ add new arg FOR_WRITE. Change callers to pass new arg.
+
+2007-06-24 Werner Koch <wk@g10code.com>
+
+ * gpgsm.c (open_es_fwrite): Avoid the dup by using the new
+ es_fdopen_nc().
+
+2007-06-21 Werner Koch <wk@g10code.com>
+
+ * certreqgen-ui.c: New.
+ * gpgsm.c (main): Let --gen-key call it.
+ * certreqgen.c (gpgsm_genkey): Add optional IN_STREAM arg and
+ adjusted caller.
+
+ * gpgsm.h (ctrl_t): Remove. It is now declared in ../common/util.h.
+
+ * call-agent.c (start_agent): Factored almost all code out to
+ ../common/asshelp.c.
+
+2007-06-20 Werner Koch <wk@g10code.com>
+
+ * call-agent.c (start_agent) [W32]: Start the agent on the fly.
+
+2007-06-18 Marcus Brinkmann <marcus@g10code.de>
+
+ * gpgsm.c (main): Percent escape output of --gpgconf-list.
+
+2007-06-14 Werner Koch <wk@g10code.com>
+
+ * call-agent.c (start_agent): Use gnupg_module_name.
+ * call-dirmngr.c (start_dirmngr): Ditto.
+ * export.c (export_p12): Ditto.
+ * import.c (parse_p12): Ditto.
+ * gpgsm.c (run_protect_tool): Ditto.
+
+2007-06-12 Werner Koch <wk@g10code.com>
+
+ * gpgsm.c (main): Replace some calls by init_common_subsystems.
+ (main): Use gnupg_datadir.
+ * qualified.c (read_list): Use gnupg-datadir.
+
+2007-06-11 Werner Koch <wk@g10code.com>
+
+ * Makefile.am (common_libs): Use libcommaonstd macr.
+
+ * gpgsm.c (main) [W32]: Call pth_init.
+
+2007-06-06 Werner Koch <wk@g10code.com>
+
+ * qualified.c (gpgsm_not_qualified_warning) [!ENABLE_NLS]: Do not
+ define orig_codeset.
+ * certdump.c (gpgsm_format_keydesc) [!ENABLE_NLS]: Do not define
+ orig_codeset.
+ (format_name_writer): Define only if funopen et al is available.
+
+ * gpgsm.c (i18n_init): Remove.
+
+2007-05-29 Werner Koch <wk@g10code.com>
+
+ * export.c (gpgsm_p12_export): Print passphrase encoding info only
+ in PEM mode.
+
+2007-05-18 Marcus Brinkmann <marcus@g10code.de>
+
+ * qualified.c (gpgsm_qualified_consent,
+ gpgsm_not_qualified_warning): Free ORIG_CODESET on error.
+ * certdump.c (gpgsm_format_keydesc): Likewise.
+
+2007-05-07 Werner Koch <wk@g10code.com>
+
+ * certcheck.c (MY_GCRY_PK_ECDSA): New.
+
+2007-04-20 Werner Koch <wk@g10code.com>
+
+ * gpgsm.c (main): Parameterize failed versions check messages.
+
+2007-04-19 Werner Koch <wk@g10code.com>
+
+ * certcheck.c (do_encode_md): Add arg PKEY. Add support for DSA2
+ and all ECDSA sizes.
+ (get_dsa_qbits): New.
+ (pk_algo_from_sexp): A key will never contain ecdsa as algorithm,
+ so remove that.
+
+2007-04-18 Werner Koch <wk@g10code.com>
+
+ * certcheck.c (do_encode_md): Support 160 bit ECDSA.
+
+2007-04-13 Werner Koch <wk@g10code.com>
+
+ * call-agent.c (start_agent): Don't use log_error when using the
+ fallback hack to start the agent. This is bug 782.
+
+2007-03-20 Werner Koch <wk@g10code.com>
+
+ * fingerprint.c (gpgsm_get_fingerprint): Add caching.
+ (gpgsm_get_fingerprint_string): Use bin2hexcolon().
+ (gpgsm_get_fingerprint_hexstring): Use bin2hex and allocate only
+ as much memory as required.
+ (gpgsm_get_keygrip_hexstring): Use bin2hex.
+
+ * certchain.c (gpgsm_validate_chain): Keep track of the
+ certificate chain and reset the ephemeral flags.
+ * keydb.c (keydb_set_cert_flags): New args EPHEMERAL and MASK.
+ Changed caller to use a mask of ~0. Return a proper error code if
+ the certificate is not available.
+
+ * gpgsm.c: Add option --p12-charset.
+ * gpgsm.h (struct opt): Add p12_charset.
+ * export.c (popen_protect_tool): Use new option.
+
+2007-03-19 Werner Koch <wk@g10code.com>
+
+ Changes to let export and key listing use estream to help systems
+ without funopen.
+
+ * keylist.c: Use estream in place of stdio functions.
+ * gpgsm.c (open_es_fwrite): New.
+ (main): Use it for the list commands.
+ * server.c (data_line_cookie_functions): New.
+ (data_line_cookie_write, data_line_cookie_close): New.
+ (do_listkeys): Use estream.
+
+ * certdump.c (gpgsm_print_serial): Changed to use estream.
+ (gpgsm_print_time): Ditto.
+ (pretty_es_print_sexp): New.
+ (gpgsm_es_print_name): New.
+ (print_dn_part): New arg STREAM. Changed all callers.
+ (print_dn_parts): Ditto.
+ * certchain.c (gpgsm_validate_chain): Changed FP to type
+ estream_t.
+ (do_list, unknown_criticals, allowed_ca, check_cert_policy)
+ (is_cert_still_valid): Ditto.
+
+ * export.c (gpgsm_export): New arg STREAM.
+ (do_putc, do_fputs): New.
+ (print_short_info): Allow printing to optional STREAM.
+ * server.c (cmd_export): Use stream.
+ * base64.c (do_putc, do_fputs): New.
+ (base64_writer_cb, base64_finish_write): Let them cope with an
+ alternate output function.
+ (plain_writer_cb): New.
+ (gpgsm_create_writer): New arg STREAM and call plain_writer_cb for
+ binary output to an estream. Changed call callers.
+
+2007-01-31 Werner Koch <wk@g10code.com>
+
+ * gpgsm.c (main): Let --gen-key print a more informative error
+ message.
+
+2007-01-25 Werner Koch <wk@g10code.com>
+
+ * Makefile.am (gpgsm_LDADD): Add LIBICONV. Noted by Billy Halsey.
+
+2007-01-05 Werner Koch <wk@g10code.com>
+
+ * certchain.c (unknown_criticals): Add subjectAltName.
+
+2006-12-21 Werner Koch <wk@g10code.com>
+
+ * gpgsm.c: Comment mtrace feature.
+
+2006-12-21 Marcus Brinkmann <marcus@g10code.de>
+
+ * certchain.c (gpgsm_basic_cert_check): Release SUBJECT.
+
+ * encrypt.c (encrypt_dek): Release S_CIPH.
+
+2006-12-20 Marcus Brinkmann <marcus@g10code.de>
+
+ * server.c (gpgsm_server): Release CTRL->server_local.
+
+ * base64.c: Add new members READER and WRITER in union U2.
+ (gpgsm_create_reader): Initialise CTX->u2.reader.
+ (gpgsm_destroy_reader): Invoke ksba_reader_release. Return early
+ if CTX is NULL.
+ (gpgsm_create_writer): Initialise CTX->u2.writer.
+ (gpgsm_destroy_writer): Invoke ksba_writer_release. Return early
+ if CTX is NULL.
+
+2006-12-18 Marcus Brinkmann <marcus@g10code.de>
+
+ * fingerprint.c (gpgsm_get_fingerprint): Close MD.
+
+2006-11-24 Werner Koch <wk@g10code.com>
+
+ * certdump.c (parse_dn_part): Take '#' as a special character only
+ at the beginning of a string.
+
+2006-11-21 Werner Koch <wk@g10code.com>
+
+ * certdump.c (my_funopen_hook_ret_t): New.
+ (format_name_writer): Use it for the return value.
+
+2006-11-14 Werner Koch <wk@g10code.com>
+
+ * server.c (skip_options): Skip leading spaces.
+ (has_option): Honor "--".
+ (cmd_export): Add option --data to do an inline export. Skip all
+ options.
+
+ * certdump.c (gpgsm_fpr_and_name_for_status): New.
+ * verify.c (gpgsm_verify): Use it to print correct status messages.
+
+2006-11-11 Werner Koch <wk@g10code.com>
+
+ * server.c (skip_options): New.
+
+2006-10-24 Marcus Brinkmann <marcus@g10code.de>
+
+ * Makefile.am (AM_CFLAGS): Add $(LIBASSUAN_CFLAGS).
+
+2006-10-23 Werner Koch <wk@g10code.com>
+
+ * gpgsm.c (main): Remap common cipher algo names to their OIDs.
+ (main): New command --gpgconf-test.
+
+2006-10-20 Werner Koch <wk@g10code.com>
+
+ * keydb.c (classify_user_id): Parse keygrip for the '&' identifier.
+
+2006-10-18 Werner Koch <wk@g10code.com>
+
+ * keylist.c (list_cert_raw): Also test for GPG_ERR_NO_VALUE when
+ testing for GPG_ERR_NO_DATA.
+ * certlist.c (cert_usage_p, gpgsm_find_cert): Ditto.
+ * certchain.c (check_cert_policy): Ditto.
+
+ * keylist.c (list_cert_std, list_cert_raw): Print "none" for no
+ chain length available.
+
+2006-10-17 Werner Koch <wk@g10code.com>
+
+ * gpgsm.c: No need for pth.h.
+ (main): or to init it. It used to be hack for W32.
+
+ * sign.c (gpgsm_get_default_cert): Changed to return only
+ certificates usable for signing.
+
+2006-10-16 Werner Koch <wk@g10code.com>
+
+ * certchain.c (already_asked_marktrusted)
+ (set_already_asked_marktrusted): New.
+ (gpgsm_validate_chain) <not trusted>: Keep track of certificates
+ we already asked for.
+
+2006-10-11 Werner Koch <wk@g10code.com>
+
+ * certreqgen.c (proc_parameters, create_request): Allow for
+ creation directly from a card.
+ * call-agent.c (gpgsm_agent_readkey): New arg FROMCARD.
+ (gpgsm_scd_pksign): New.
+
+2006-10-06 Werner Koch <wk@g10code.com>
+
+ * Makefile.am (AM_CFLAGS): Use PTH version of libassuan.
+ (gpgsm_LDADD): Ditto.
+
+2006-10-05 Werner Koch <wk@g10code.com>
+
+ * certcheck.c (do_encode_md): Check that the has algo is valid.
+
+2006-10-02 Marcus Brinkmann <marcus@g10code.de>
+
+ * server.c (register_commands): New commands DUMPKEYS and
+ DUMPSECRETKEYS.
+ (cmd_dumpkeys, cmd_dumpsecretkeys): New functions.
+ (option_handler): Support with-key-data option.
+
+2006-09-26 Werner Koch <wk@g10code.com>
+
+ * certchain.c (gpgsm_validate_chain): More changes for the relax
+ feature. Use certificate reference counting instead of the old
+ explicit tests. Added a missing free.
+
+2006-09-25 Werner Koch <wk@g10code.com>
+
+ * gpgsm.h (struct rootca_flags_s): New.
+ * call-agent.c (istrusted_status_cb): New.
+ (gpgsm_agent_istrusted): New arg ROOTCA_FLAGS.
+ * keylist.c (list_cert_colon): Use dummy for new arg.
+ * certchain.c (gpgsm_validate_chain): Make use of the relax flag
+ for root certificates.
+ (unknown_criticals): Ignore a GPG_ERR_NO_VALUE.
+
+2006-09-20 Werner Koch <wk@g10code.com>
+
+ * gpgsm.c: Add alias command --dump-cert.
+
+ * Makefile.am: Changes to allow parallel make runs.
+
+2006-09-18 Werner Koch <wk@g10code.com>
+
+ * gpgsm.c (main): Use this to import standard certificates.
+ * keydb.c (keydb_add_resource): New arg AUTO_CREATED.
+
+2006-09-14 Werner Koch <wk@g10code.com>
+
+ Replaced all call gpg_error_from_errno(errno) by
+ gpg_error_from_syserror().
+
+2006-09-13 Werner Koch <wk@g10code.com>
+
+ * keylist.c (list_internal_keys): Print marker line to FP and not
+ to stdout.
+
+ * gpgsm.c (main): All list key list commands now make ose of
+ --output. Cleaned up calls to list modes. New command
+ --dump-chain. Renamed --list-sigs to --list-chain and added an
+ alias for the old one.
+
+ * server.c (cmd_message): Changed to use assuan_command_parse_fd.
+ (option_handler): New option list-to-output.
+ (do_listkeys): Use it.
+
+2006-09-06 Werner Koch <wk@g10code.com>
+
+ * gpgsm.h (OUT_OF_CORE): Removed and changed all callers to
+ out_of_core.
+ (CTRL): Removed and changed everywhere to ctrl_t.
+ (CERTLIST): Ditto.
+
+ Replaced all Assuan error codes by libgpg-error codes. Removed
+ all map_to_assuan_status and map_assuan_err.
+
+ * gpgsm.c (main): Call assuan_set_assuan_err_source to have Assuan
+ switch to gpg-error codes.
+ * server.c (set_error): Adjusted.
+
+2006-08-29 Werner Koch <wk@g10code.com>
+
+ * call-agent.c (gpgsm_agent_pkdecrypt): Allow decryption using
+ complete S-expressions as implemented by the current gpg-agent.
+
+ * gpgsm.c (main): Implement --output for encrypt, decrypt, sign
+ and export.
+
+2006-07-03 Werner Koch <wk@g10code.com>
+
+ * certreqgen.c (proc_parameters): Print the component label of a
+ faulty DN.
+
+2006-06-26 Werner Koch <wk@g10code.com>
+
+ * certdump.c (gpgsm_cert_log_name): New.
+ * certchain.c (is_cert_still_valid): Log the name of the certificate.
+
+2006-06-20 Werner Koch <wk@g10code.com>
+
+ * gpgsm.c (gpgsm_init_default_ctrl): Take care of the command line
+ option --include-certs.
+
+ * keylist.c (list_cert_raw): Print the certid.
+
+2006-05-23 Werner Koch <wk@g10code.com>
+
+ * keydb.c (hextobyte): Deleted as it is now defined in jnlib.
+
+ * Makefile.am (gpgsm_LDADD): Include ZLIBS.
+
+2006-05-19 Marcus Brinkmann <marcus@g10code.de>
+
+ * keydb.c (keydb_insert_cert): Do not lock here, but only check if
+ it is locked.
+ (keydb_store_cert): Lock here.
+
+ * keydb.h (keydb_delete): Accept new argument UNLOCK.
+ * keydb.c (keydb_delete): Likewise. Only unlock if this is set.
+ * delete.c (delete_one): Add new argument to invocation of
+ keydb_delete.
+
+2006-05-15 Werner Koch <wk@g10code.com>
+
+ * keylist.c (print_names_raw): Sanitize URI.
+
+2006-03-21 Werner Koch <wk@g10code.com>
+
+ * certchain.c (get_regtp_ca_info): New.
+ (allowed_ca): Use it.
+
+2006-03-20 Werner Koch <wk@g10code.com>
+
+ * qualified.c (gpgsm_is_in_qualified_list): New optional arg COUNTRY.
+
+2006-02-17 Werner Koch <wk@g10code.com>
+
+ * call-dirmngr.c (start_dirmngr): Print name of dirmngr to be started.
+
+2005-11-23 Werner Koch <wk@g10code.com>
+
+ * gpgsm.h: New member QUALSIG_APPROVAL.
+ * sign.c (gpgsm_sign): Print a warning if a certificate is not
+ qualified.
+ * qualified.c (gpgsm_qualified_consent): Include a note that this
+ is not approved software.
+ (gpgsm_not_qualified_warning): New.
+ * gpgsm.c (main): Prepared to print a note whether the software
+ has been approved.
+
+2005-11-13 Werner Koch <wk@g10code.com>
+
+ * call-agent.c (gpgsm_agent_get_confirmation): New.
+
+ * keylist.c (list_cert_std): Print qualified status.
+ * qualified.c: New.
+ * certchain.c (gpgsm_validate_chain): Check for qualified
+ certificates.
+
+ * certchain.c (gpgsm_basic_cert_check): Release keydb handle when
+ no-chain-validation is used.
+
+2005-11-11 Werner Koch <wk@g10code.com>
+
+ * keylist.c (print_capabilities): Print is_qualified status.
+
+2005-10-28 Werner Koch <wk@g10code.com>
+
+ * certdump.c (pretty_print_sexp): New.
+ (gpgsm_print_name2): Use it here. This allows proper printing of
+ DNS names as used with server certificates.
+
+2005-10-10 Werner Koch <wk@g10code.com>
+
+ * keylist.c: Add pkaAdress OID as reference.
+
+2005-10-08 Marcus Brinkmann <marcus@g10code.de>
+
+ * Makefile.am (gpgsm_LDADD): Add ../gl/libgnu.a after
+ ../common/libcommon.a.
+
+2005-09-13 Werner Koch <wk@g10code.com>
+
+ * verify.c (gpgsm_verify): Print a note if the unknown algorithm
+ is MD2.
+ * sign.c (gpgsm_sign): Ditto.
+ * certcheck.c (gpgsm_check_cert_sig): Ditto.
+
+2005-09-08 Werner Koch <wk@g10code.com>
+
+ * export.c (popen_protect_tool): Add option --have-cert. We
+ probably lost this option with 1.9.14 due to restructuring of
+ export.c.
+
+2005-07-21 Werner Koch <wk@g10code.com>
+
+ * gpgsm.c (main): New options --no-log-file and --debug-none.
+
+ * certreqgen.c (get_parameter, get_parameter_value): Add SEQ arg
+ to allow enumeration. Changed all callers.
+ (create_request): Process DNS and URI parameters.
+
+2005-07-20 Werner Koch <wk@g10code.com>
+
+ * keylist.c (email_kludge): Reworked.
+
+ * certdump.c (gpgsm_print_serial, gpgsm_dump_serial): Cast printf
+ arg to unsigned.
+ * call-dirmngr.c (gpgsm_dirmngr_run_command): Ditto
+
+2005-07-19 Werner Koch <wk@g10code.com>
+
+ * fingerprint.c (gpgsm_get_certid): Cast printf arg to unsigned.
+ Bug accidently introduced while solving the #$%^& gcc
+ signed/unsigned char* warnings.
+
+2005-06-15 Werner Koch <wk@g10code.com>
+
+ * delete.c (delete_one): Changed FPR to unsigned.
+ * encrypt.c (encrypt_dek): Made ENCVAL unsigned.
+ (gpgsm_encrypt): Ditto.
+ * sign.c (gpgsm_sign): Made SIGVAL unsigned.
+ * base64.c (base64_reader_cb): Need to use some casting to get
+ around signed/unsigned char* warnings.
+ * certcheck.c (gpgsm_check_cms_signature): Ditto.
+ (gpgsm_create_cms_signature): Changed arg R_SIGVAL to unsigned char*.
+ (do_encode_md): Made NFRAME a size_t.
+ * certdump.c (gpgsm_print_serial): Fixed signed/unsigned warning.
+ (gpgsm_dump_serial): Ditto.
+ (gpgsm_format_serial): Ditto.
+ (gpgsm_dump_string): Ditto.
+ (gpgsm_dump_cert): Ditto.
+ (parse_dn_part): Ditto.
+ (gpgsm_print_name2): Ditto.
+ * keylist.c (email_kludge): Ditto.
+ * certreqgen.c (proc_parameters, create_request): Ditto.
+ (create_request): Ditto.
+ * call-agent.c (gpgsm_agent_pksign): Made arg R_BUF unsigned.
+ (struct cipher_parm_s): Made CIPHERTEXT unsigned.
+ (struct genkey_parm_s): Ditto.
+ * server.c (strcpy_escaped_plus): Made arg S signed char*.
+ * fingerprint.c (gpgsm_get_fingerprint): Made ARRAY unsigned.
+ (gpgsm_get_keygrip): Ditto.
+ * keydb.c (keydb_insert_cert): Made DIGEST unsigned.
+ (keydb_update_cert): Ditto.
+ (classify_user_id): Apply cast to signed/unsigned assignment.
+ (hextobyte): Ditto.
+
+2005-06-01 Werner Koch <wk@g10code.com>
+
+ * misc.c: Include setenv.h.
+
+2005-04-21 Werner Koch <wk@g10code.com>
+
+ * gpgsm.c: New options --{enable,disable}-trusted-cert-crl-check.
+ * certchain.c (gpgsm_validate_chain): Make use of it.
+
+ * certchain.c (gpgsm_validate_chain): Check revocations even for
+ expired certificates. This is required because on signature
+ verification an expired key is fine whereas a revoked one is not.
+
+2005-04-20 Werner Koch <wk@g10code.com>
+
+ * Makefile.am (AM_CFLAGS): Add PTH_CFLAGS as noted by several folks.
+
+2005-04-19 Werner Koch <wk@g10code.com>
+
+ * certchain.c (check_cert_policy): Print the diagnostic for a open
+ failure of policies.txt only in verbose mode or when it is not
+ ENOENT.
+
+2005-04-17 Werner Koch <wk@g10code.com>
+
+ * call-dirmngr.c (inq_certificate): Add new inquire SENDCERT_SKI.
+ * certlist.c (gpgsm_find_cert): Add new arg KEYID and implement
+ this filter. Changed all callers.
+
+ * certchain.c (find_up_search_by_keyid): New helper.
+ (find_up): Also try using the AKI.keyIdentifier.
+ (find_up_external): Ditto.
+
+2005-04-15 Werner Koch <wk@g10code.com>
+
+ * keylist.c (list_cert_raw): Print the subjectKeyIdentifier as
+ well as the keyIdentifier part of the authorityKeyIdentifier.
+
+2005-03-31 Werner Koch <wk@g10code.com>
+
+ * call-dirmngr.c (start_dirmngr): Use PATHSEP_C instead of ':'.
+ * call-agent.c (start_agent): Ditto.
+
+2005-03-17 Werner Koch <wk@g10code.com>
+
+ * certcheck.c: Fixed use of DBG_CRYPTO and DBG_X509.
+
+ * certchain.c (gpgsm_basic_cert_check): Dump certificates after a
+ failed gcry_pk_verify.
+ (find_up): Do an external lookup also for an authorityKeyIdentifier
+ lookup. Factored external lookup code out to ..
+ (find_up_external): .. new.
+
+2005-03-03 Werner Koch <wk@g10code.com>
+
+ * Makefile.am (gpgsm_LDADD): Added PTH_LIBS. Noted by Kazu Yamamoto.
+
+2005-01-13 Werner Koch <wk@g10code.com>
+
+ * certreqgen.c (proc_parameters): Cast printf arg.
+
+2004-12-22 Werner Koch <wk@g10code.com>
+
+ * gpgsm.c (set_binary): New.
+ (main, open_read, open_fwrite): Use it.
+
+2004-12-21 Werner Koch <wk@g10code.com>
+
+ * gpgsm.c (main): Use default_homedir().
+ (main) [W32]: Default to disabled CRL checks.
+
+2004-12-20 Werner Koch <wk@g10code.com>
+
+ * call-agent.c (start_agent): Before starting a pipe server start
+ to connect to a server on the standard socket. Use PATHSEP
+ * call-dirmngr.c (start_dirmngr): Use PATHSEP.
+
+ * import.c: Include unistd.h for dup and close.
+
+2004-12-18 Werner Koch <wk@g10code.com>
+
+ * gpgsm.h (map_assuan_err): Define in terms of
+ map_assuan_err_with_source.
+ * call-agent.c (start_agent): Pass error source to
+ send_pinentry_environment.
+
+2004-12-17 Werner Koch <wk@g10code.com>
+
+ * call-dirmngr.c (isvalid_status_cb, lookup_status_cb)
+ (run_command_status_cb): Return cancel status if gpgsm_status
+ returned an error.
+
+ * server.c (gpgsm_status, gpgsm_status2)
+ (gpgsm_status_with_err_code): Return an error code.
+ (gpgsm_status2): Always call va_end().
+
+2004-12-15 Werner Koch <wk@g10code.com>
+
+ * call-dirmngr.c (lookup_status_cb): Send progress messages
+ upstream.
+ (isvalid_status_cb): Ditto.
+ (gpgsm_dirmngr_isvalid): Put CTRL into status CB parameters.
+ (gpgsm_dirmngr_run_command, run_command_status_cb): Pass CTRL to
+ status callback and handle PROGRESS.
+
+ * misc.c (setup_pinentry_env) [W32]: Don't use it.
+
+ * gpgsm.c (main) [W32]: Init Pth because we need it for the socket
+ operations and to resolve libassuan symbols.
+ (run_protect_tool) [W32]: Disable it.
+
+ * Makefile.am (gpgsm_LDADD): Move LIBASSUAN_LIBS more to the end.
+
+2004-12-07 Werner Koch <wk@g10code.com>
+
+ * Makefile.am (gpgsm_LDADD): Put libassuan before jnlib because
+ under W32 we need the w32 pth code from jnlib.
+
+ * misc.c (setup_pinentry_env) [W32]: Disabled.
+
+2004-12-06 Werner Koch <wk@g10code.com>
+
+ * gpgsm.c (run_protect_tool) [_WIN32]: Disabled.
+
+ * import.c (popen_protect_tool): Simplified by making use of
+ gnupg_spawn_process.
+ (parse_p12): Likewise, using gnupg_wait_process.
+ * export.c (popen_protect_tool): Ditto.
+ (export_p12): Ditto.
+
+ * keydb.c: Don't define DIRSEP_S here.
+
+2004-12-02 Werner Koch <wk@g10code.com>
+
+ * certchain.c (gpgsm_basic_cert_check): Dump certs with bad
+ signature for debugging.
+ (gpgsm_validate_chain): Ditto.
+
+2004-11-29 Werner Koch <wk@g10code.com>
+
+ * gpgsm.c (set_debug): Changed to use a globals DEBUG_LEVEL and
+ DEBUG_VALUE.
+ (main): Made DEBUG_LEVEL global and introduced DEBUG_VALUE. This
+ now allows to add debug flags on top of a debug-level setting.
+
+2004-11-23 Werner Koch <wk@g10code.com>
+
+ * gpgsm.c: New option --prefer-system-dirmngr.
+ * call-dirmngr.c (start_dirmngr): Implement this option.
+
+2004-10-22 Werner Koch <wk@g10code.com>
+
+ * certreqgen.c (gpgsm_genkey): Remove the NEW from the certificate
+ request PEM header. This is according to the Sphinx standard.
+
+2004-10-08 Moritz Schulte <moritz@g10code.com>
+
+ * certchain.c (gpgsm_validate_chain): Do not use keydb_new() in
+ case the no_chain_validation-return-short-cut is used (fixes
+ memory leak).
+
+2004-10-04 Werner Koch <wk@g10code.com>
+
+ * misc.c (setup_pinentry_env): Try hard to set a default for GPG_TTY.
+
+2004-09-30 Werner Koch <wk@g10code.com>
+
+ * gpgsm.c (i18n_init): Always use LC_ALL.
+
+ * certdump.c (gpgsm_format_name): Factored code out to ..
+ (gpgsm_format_name2): .. new.
+ (gpgsm_print_name): Factored code out to ..
+ (gpgsm_print_name2): .. new.
+ (print_dn_part): New arg TRANSLATE. Changed all callers.
+ (print_dn_parts): Ditto.
+ (gpgsm_format_keydesc): Do not translate the SUBJECT; we require
+ it to stay UTF-8 but we still want to filter out bad control
+ characters.
+
+ * Makefile.am: Adjusted for gettext 0.14.
+
+ * keylist.c (list_cert_colon): Make sure that the expired flag has
+ a higher precedence than the invalid flag.
+
+2004-09-29 Werner Koch <wk@g10code.com>
+
+ * import.c (parse_p12): Write an error status line for bad
+ passphrases. Add new arg CTRL and changed caller.
+ * export.c (export_p12): Likewise.
+
+2004-09-14 Werner Koch <wk@g10code.com>
+
+ * certchain.c (gpgsm_validate_chain): Give expired certificates a
+ higher error precedence and don't bother to check any CRL in that
+ case.
+
+2004-08-24 Werner Koch <wk@g10code.de>
+
+ * certlist.c: Fixed typo in ocsp OID.
+
+2004-08-18 Werner Koch <wk@g10code.de>
+
+ * certlist.c (gpgsm_cert_use_ocsp_p): New.
+ (cert_usage_p): Support it here.
+ * call-dirmngr.c (gpgsm_dirmngr_isvalid): Use it here.
+
+2004-08-17 Marcus Brinkmann <marcus@g10code.de>
+
+ * import.c: Fix typo in last change.
+
+2004-08-17 Werner Koch <wk@g10code.de>
+
+ * import.c (check_and_store): Do a full validation if
+ --with-validation is set.
+
+ * certchain.c (gpgsm_basic_cert_check): Print more detailed error
+ messages.
+
+ * certcheck.c (do_encode_md): Partly support DSA. Add new arg
+ PKALGO. Changed all callers to pass it.
+ (pk_algo_from_sexp): New.
+
+2004-08-16 Werner Koch <wk@g10code.de>
+
+ * gpgsm.c: New option --fixed-passphrase.
+ * import.c (popen_protect_tool): Pass it to the protect-tool.
+
+ * server.c (cmd_encrypt): Use DEFAULT_RECPLIST and not recplist
+ for encrypt-to keys.
+
+2004-08-06 Werner Koch <wk@g10code.com>
+
+ * gpgsm.c: New option --with-ephemeral-keys.
+ * keylist.c (list_internal_keys): Set it here.
+ (list_cert_raw): And indicate those keys. Changed all our callers
+ to pass the new arg HD through.
+
+2004-07-23 Werner Koch <wk@g10code.de>
+
+ * certreqgen.c (proc_parameters): Do not allow key length below
+ 1024.
+
+2004-07-22 Werner Koch <wk@g10code.de>
+
+ * keylist.c (list_cert_raw): Print the keygrip.
+
+2004-07-20 Werner Koch <wk@gnupg.org>
+
+ * certchain.c (gpgsm_validate_chain): The trust check didn't
+ worked anymore, probably due to the changes at 2003-03-04. Fixed.
+
+2004-06-06 Werner Koch <wk@gnupg.org>
+
+ * certreqgen.c (get_parameter_uint, create_request): Create
+ an extension for key usage when requested.
+
+2004-05-12 Werner Koch <wk@gnupg.org>
+
+ * gpgsm.c (main): Install emergency_cleanup also as an atexit
+ handler.
+
+ * verify.c (gpgsm_verify): Removed the separate error code
+ handling for KSBA. We use shared error codes anyway.
+
+ * export.c (export_p12): Removed debugging code.
+
+ * encrypt.c (gpgsm_encrypt): Put the session key in to secure memory.
+
+2004-05-11 Werner Koch <wk@gnupg.org>
+
+ * sign.c (gpgsm_sign): Include the error source in the final error
+ message.
+ * decrypt.c (gpgsm_decrypt): Ditto.
+
+ * fingerprint.c (gpgsm_get_key_algo_info): New.
+ * sign.c (gpgsm_sign): Don't assume RSA in the status line.
+ * keylist.c (list_cert_colon): Really print the algorithm and key
+ length.
+ (list_cert_raw, list_cert_std): Ditto.
+ (list_cert_colon): Reorganized to be able to tell whether a root
+ certificate is trusted.
+
+ * gpgsm.c: New option --debug-allow-core-dump.
+
+ * gpgsm.h (opt): Add member CONFIG_FILENAME.
+ * gpgsm.c (main): Use it here instead of the local var.
+
+ * server.c (gpgsm_server): Print some additional information with
+ the hello in verbose mode.
+
+2004-04-30 Werner Koch <wk@gnupg.org>
+
+ * import.c (check_and_store): Do not update the stats for hidden
+ imports of issuer certs.
+ (popen_protect_tool): Request statusmessages from the protect-tool.
+ (parse_p12): Detect status messages. Add new arg STATS and update them.
+ (print_imported_summary): Include secret key stats.
+
+2004-04-28 Werner Koch <wk@gnupg.org>
+
+ * gpgsm.c: New command --keydb-clear-some-cert-flags.
+ * keydb.c (keydb_clear_some_cert_flags): New.
+ (keydb_update_keyblock, keydb_set_flags): Change error code
+ CONFLICT to NOT_LOCKED.
+
+2004-04-26 Werner Koch <wk@gnupg.org>
+
+ * gpgsm.c (main) <gpgconf>: Do not use /dev/null as default config
+ filename.
+
+ * call-agent.c (gpgsm_agent_pksign, gpgsm_agent_pkdecrypt)
+ (gpgsm_agent_genkey, gpgsm_agent_istrusted)
+ (gpgsm_agent_marktrusted, gpgsm_agent_havekey)
+ (gpgsm_agent_passwd): Add new arg CTRL and changed all callers.
+ (start_agent): New arg CTRL. Send progress item when starting a
+ new agent.
+ * sign.c (gpgsm_get_default_cert, get_default_signer): New arg
+ CTRL to be passed down to the agent function.
+ * decrypt.c (prepare_decryption): Ditto.
+ * certreqgen.c (proc_parameters, read_parameters): Ditto.
+ * certcheck.c (gpgsm_create_cms_signature): Ditto.
+
+2004-04-23 Werner Koch <wk@gnupg.org>
+
+ * keydb.c (keydb_add_resource): Try to compress the file on init.
+
+ * keylist.c (oidtranstbl): New. OIDs collected from several sources.
+ (print_name_raw, print_names_raw, list_cert_raw): New.
+ (gpgsm_list_keys): Check the dump mode and pass it down as
+ necessary.
+
+2004-04-22 Werner Koch <wk@gnupg.org>
+
+ * gpgsm.c (main): New commands --dump-keys, --dump-external-keys,
+ --dump-secret-keys.
+
+2004-04-13 Werner Koch <wk@gnupg.org>
+
+ * misc.c (setup_pinentry_env): New.
+ * import.c (popen_protect_tool): Call it.
+ * export.c (popen_protect_tool): Call it.
+
+2004-04-08 Werner Koch <wk@gnupg.org>
+
+ * decrypt.c (gpgsm_decrypt): Return GPG_ERR_NO_DATA if it is not a
+ encrypted message.
+
+2004-04-07 Werner Koch <wk@gnupg.org>
+
+ * gpgsm.c: New option --force-crl-refresh.
+ * call-dirmngr.c (gpgsm_dirmngr_isvalid): Pass option to dirmngr.
+
+2004-04-05 Werner Koch <wk@gnupg.org>
+
+ * server.c (get_status_string): Add STATUS_NEWSIG.
+ * verify.c (gpgsm_verify): Print STATUS_NEWSIG for each signature.
+
+ * certchain.c (gpgsm_validate_chain) <gpgsm_cert_use_cer_p>: Do
+ not just warn if a cert is not suitable; bail out immediately.
+
+2004-04-01 Werner Koch <wk@gnupg.org>
+
+ * call-dirmngr.c (isvalid_status_cb): New.
+ (unhexify_fpr): New. Taken from ../g10/call-agent.c
+ (gpgsm_dirmngr_isvalid): Add new arg CTRL, changed caller to pass
+ it thru. Detect need to check the respondert cert and do that.
+ * certchain.c (gpgsm_validate_chain): Add new arg FLAGS. Changed
+ all callers.
+
+2004-03-24 Werner Koch <wk@gnupg.org>
+
+ * sign.c (gpgsm_sign): Include a short list of capabilities.
+
+2004-03-17 Werner Koch <wk@gnupg.org>
+
+ * gpgsm.c (main) <gpgconf>: Fixed default value quoting.
+
+2004-03-16 Werner Koch <wk@gnupg.org>
+
+ * gpgsm.c (main): Implemented --gpgconf-list.
+
+2004-03-15 Werner Koch <wk@gnupg.org>
+
+ * keylist.c (list_cert_colon): Hack to set the expired flag.
+
+2004-03-09 Werner Koch <wk@gnupg.org>
+
+ * gpgsm.c (main): Correctly intitialze USE_OCSP flag.
+
+ * keydb.c (keydb_delete): s/GPG_ERR_CONFLICT/GPG_ERR_NOT_LOCKED/
+
+2004-03-04 Werner Koch <wk@gnupg.org>
+
+ * call-dirmngr.c (gpgsm_dirmngr_isvalid): New arg ISSUER_CERT.
+
+ * certchain.c (is_cert_still_valid): New. Code moved from ...
+ (gpgsm_validate_chain): ... here because we now need to check at
+ two places and at a later stage, so that we can pass the issuer
+ cert down to the dirmngr.
+
+2004-03-03 Werner Koch <wk@gnupg.org>
+
+ * call-agent.c (start_agent): Replaced pinentry setup code by a
+ call to a new common function.
+
+ * certdump.c (gpgsm_format_keydesc): Make sure the string is
+ returned as utf-8.
+
+ * export.c (gpgsm_export): Make sure that we don't export more
+ than one certificate.
+
+2004-03-02 Werner Koch <wk@gnupg.org>
+
+ * export.c (create_duptable, destroy_duptable)
+ (insert_duptable): New.
+ (gpgsm_export): Avoid duplicates.
+
+2004-02-26 Werner Koch <wk@gnupg.org>
+
+ * certchain.c (compare_certs): New.
+ (gpgsm_validate_chain): Fixed infinite certificate checks after
+ bad signatures.
+
+2004-02-24 Werner Koch <wk@gnupg.org>
+
+ * keylist.c (list_cert_colon): Print the fingerprint as the
+ cert-id for root certificates.
+
+2004-02-21 Werner Koch <wk@gnupg.org>
+
+ * keylist.c (list_internal_keys): Return error codes.
+ (list_external_keys, gpgsm_list_keys): Ditto.
+ * server.c (do_listkeys): Ditto.
+
+ * gpgsm.c (main): Display a key description for --passwd.
+ * call-agent.c (gpgsm_agent_passwd): New arg DESC.
+
+2004-02-20 Werner Koch <wk@gnupg.org>
+
+ * gpgsm.c (main): New option --debug-ignore-expiration.
+ * certchain.c (gpgsm_validate_chain): Use it here.
+
+ * certlist.c (cert_usage_p): Apply extKeyUsage.
+
+2004-02-19 Werner Koch <wk@gnupg.org>
+
+ * export.c (export_p12, popen_protect_tool)
+ (gpgsm_p12_export): New.
+ * gpgsm.c (main): New command --export-secret-key-p12.
+
+2004-02-18 Werner Koch <wk@gnupg.org>
+
+ * gpgsm.c (set_debug): Set the new --debug-level flags.
+ (main): New option --gpgconf-list.
+ (main): Do not setup -u and -r keys when not required.
+ (main): Setup the used character set.
+
+ * keydb.c (keydb_add_resource): Print a hint to start the
+ gpg-agent.
+
+2004-02-17 Werner Koch <wk@gnupg.org>
+
+ * gpgsm.c: Fixed value parsing for --with-validation.
+ * call-agent.c (start_agent): Ignore an empty GPG_AGENT_INFO.
+ * call-dirmngr.c (start_dirmngr): Likewise for DIRMNGR_INFO.
+
+ * gpgsm.c: New option --with-md5-fingerprint.
+ * keylist.c (list_cert_std): Print MD5 fpr.
+
+ * gpgsm.c: New options --with-validation.
+ * server.c (option_handler): New option "with-validation".
+ * keylist.c (list_cert_std, list_internal_keys): New args CTRL and
+ WITH_VALIDATION. Changed callers to set it.
+ (list_external_cb, list_external_keys): Pass CTRL to the callback.
+ (list_cert_colon): Add arg CTRL. Check validation if requested.
+ * certchain.c (unknown_criticals, allowed_ca, check_cert_policy)
+ (gpgsm_validate_chain): New args LISTMODE and FP.
+ (do_list): New helper for info output.
+ (find_up): New arg FIND_NEXT.
+ (gpgsm_validate_chain): After a bad signature try again with other
+ CA certificates.
+
+ * import.c (print_imported_status): New arg NEW_CERT. Print
+ additional STATUS_IMPORT_OK becuase that is what gpgme expects.
+ (check_and_store): Always call above function after import.
+ * server.c (get_status_string): Added STATUS_IMPORT_OK.
+
+2004-02-13 Werner Koch <wk@gnupg.org>
+
+ * certcheck.c (gpgsm_create_cms_signature): Format a description
+ for use by the pinentry.
+ * decrypt.c (gpgsm_decrypt): Ditto. Free HEXKEYGRIP.
+ * certdump.c (format_name_cookie, format_name_writer)
+ (gpgsm_format_name): New.
+ (gpgsm_format_serial): New.
+ (gpgsm_format_keydesc): New.
+ * call-agent.c (gpgsm_agent_pksign): New arg DESC.
+ (gpgsm_agent_pkdecrypt): Ditto.
+
+ * encrypt.c (init_dek): Check for too weak algorithms.
+
+ * import.c (parse_p12, popen_protect_tool): New.
+
+ * base64.c (gpgsm_create_reader): New arg ALLOW_MULTI_PEM.
+ Changed all callers.
+ (base64_reader_cb): Handle it here.
+ (gpgsm_reader_eof_seen): New.
+ (base64_reader_cb): Set a flag for EOF.
+ (simple_reader_cb): Ditto.
+
+2004-02-12 Werner Koch <wk@gnupg.org>
+
+ * gpgsm.h, gpgsm.c: New option --protect-tool-program.
+ * gpgsm.c (run_protect_tool): Use it.
+
+2004-02-11 Werner Koch <wk@gnupg.org>
+
+ * Makefile.am (AM_CPPFLAGS): Pass directory constants via -D; this
+ will allow to override directory names at make time.
+
+2004-02-02 Werner Koch <wk@gnupg.org>
+
+ * import.c (check_and_store): Import certificates even with
+ missing issuer's cert. Fixed an "depending on the verbose
+ setting" bug.
+
+ * certchain.c (gpgsm_validate_chain): Mark revoked certs in the
+ keybox.
+
+ * keylist.c (list_cert_colon): New arg VALIDITY; use it to print a
+ revoked flag.
+ (list_internal_keys): Retrieve validity flag.
+ (list_external_cb): Pass 0 as validity flag.
+ * keydb.c (keydb_get_flags, keydb_set_flags): New.
+ (keydb_set_cert_flags): New.
+ (lock_all): Return a proper error code.
+ (keydb_lock): New.
+ (keydb_delete): Don't lock but check that it has been locked.
+ (keydb_update_keyblock): Ditto.
+ * delete.c (delete_one): Take a lock.
+
+2004-01-30 Werner Koch <wk@gnupg.org>
+
+ * certchain.c (check_cert_policy): Fixed read error checking.
+ (check_cert_policy): With no critical policies issue only a
+ warning if the policy file does not exists.
+
+ * sign.c (add_certificate_list): Decrement N for the first cert.
+
+2004-01-29 Werner Koch <wk@gnupg.org>
+
+ * certdump.c (parse_dn_part): Map common OIDs to human readable
+ labels. Make sure that a value won't get truncated if it includes
+ a Nul.
+
+2004-01-28 Werner Koch <wk@gnupg.org>
+
+ * certchain.c (gpgsm_validate_chain): Changed the message printed
+ for an untrusted root certificate.
+
+2004-01-27 Werner Koch <wk@gnupg.org>
+
+ * certdump.c (parse_dn_part): Pretty print the nameDistinguisher OID.
+ (print_dn_part): Do not delimit multiple RDN by " + ". Handle
+ multi-valued RDNs in a special way, i.e. in the order specified by
+ the certificate.
+ (print_dn_parts): Simplified.
+
+2004-01-16 Werner Koch <wk@gnupg.org>
+
+ * sign.c (gpgsm_sign): Print an error message on all failures.
+ * decrypt.c (gpgsm_decrypt): Ditto.
+
+2003-12-17 Werner Koch <wk@gnupg.org>
+
+ * server.c (gpgsm_server): Add arg DEFAULT_RECPLIST.
+ (cmd_encrypt): Add all enrypt-to marked certs to the list.
+ * encrypt.c (gpgsm_encrypt): Check that real recipients are
+ available.
+ * gpgsm.c (main): Make the --encrypt-to and --no-encrypt-to
+ options work. Pass the list of recients to gpgsm_server.
+ * gpgsm.h (certlist_s): Add field IS_ENCRYPT_TO.
+ (opt): Add NO_ENCRYPT_TO.
+ * certlist.c (gpgsm_add_to_certlist): New arg IS_ENCRYPT_TO.
+ Changed all callers and ignore duplicate entries.
+ (is_cert_in_certlist): New.
+ (gpgsm_add_cert_to_certlist): New.
+
+ * certdump.c (gpgsm_print_serial): Cleaned up cast use in strtoul.
+ (gpgsm_dump_serial): Ditto.
+
+ * decrypt.c (gpgsm_decrypt): Replaced ERR by RC.
+
+2003-12-16 Werner Koch <wk@gnupg.org>
+
+ * gpgsm.c (main): Set the prefixes for assuan logging.
+
+ * sign.c (gpgsm_sign): Add validation checks for the default
+ certificate.
+
+ * gpgsm.c: Add -k as alias for --list-keys and -K for
+ --list-secret-keys.
+
+2003-12-15 Werner Koch <wk@gnupg.org>
+
+ * encrypt.c (init_dek): Use gry_create_nonce for the IV; there is
+ not need for real strong random here and it even better protect
+ the random bits used for the key.
+
+2003-12-01 Werner Koch <wk@gnupg.org>
+
+ * gpgsm.c, gpgsm.h: New options --{enable,disable}-ocsp.
+ (gpgsm_init_default_ctrl): Set USE_OCSP to the default value.
+ * certchain.c (gpgsm_validate_chain): Handle USE_OCSP.
+ * call-dirmngr.c (gpgsm_dirmngr_isvalid): Add arg USE_OCSP and
+ proceed accordingly.
+
+2003-11-19 Werner Koch <wk@gnupg.org>
+
+ * verify.c (gpgsm_verify): Use "0" instead of an empty string for
+ the VALIDSIG status.
+
+2003-11-18 Werner Koch <wk@gnupg.org>
+
+ * verify.c (gpgsm_verify): Fixed for changes API of gcry_md_info.
+
+ * certchain.c (unknown_criticals): Fixed an error code test.
+
+2003-11-12 Werner Koch <wk@gnupg.org>
+
+ Adjusted for API changes in Libksba.
+
+2003-10-31 Werner Koch <wk@gnupg.org>
+
+ * certchain.c (gpgsm_validate_chain): Changed to use ksba_isotime_t.
+ * verify.c (strtimestamp_r, gpgsm_verify): Ditto.
+ * sign.c (gpgsm_sign): Ditto.
+ * keylist.c (print_time, list_cert_std, list_cert_colon): Ditto.
+ * certdump.c (gpgsm_print_time, gpgsm_dump_time, gpgsm_dump_cert):
+ Ditto.
+
+2003-10-25 Werner Koch <wk@gnupg.org>
+
+ * certreqgen.c (read_parameters): Fixed faulty of !spacep().
+
+2003-08-20 Marcus Brinkmann <marcus@g10code.de>
+
+ * encrypt.c (encode_session_key): Allocate enough space. Cast key
+ byte to unsigned char to prevent sign extension.
+ (encrypt_dek): Check return value before error.
+
+2003-08-14 Timo Schulz <twoaday@freakmail.de>
+
+ * encrypt.c (encode_session_key): Use new Libgcrypt interface.
+
+2003-07-31 Werner Koch <wk@gnupg.org>
+
+ * Makefile.am (gpgsm_LDADD): Added INTLLIBS.
+
+2003-07-29 Werner Koch <wk@gnupg.org>
+
+ * gpgsm.c (main): Add secmem features and set the random seed file.
+ (gpgsm_exit): Update the random seed file and enable debug output.
+
+2003-07-27 Werner Koch <wk@gnupg.org>
+
+ Adjusted for gcry_mpi_print and gcry_mpi_scan API change.
+
+2003-06-24 Werner Koch <wk@gnupg.org>
+
+ * server.c (gpgsm_status_with_err_code): New.
+ * verify.c (gpgsm_verify): Use it here instead of the old
+ tokenizing version.
+
+ * verify.c (strtimestamp): Renamed to strtimestamp_r
+
+ Adjusted for changes in the libgcrypt API. Some more fixes for the
+ libgpg-error stuff.
+
+2003-06-04 Werner Koch <wk@gnupg.org>
+
+ * call-agent.c (init_membuf,put_membuf,get_membuf): Removed.
+ Include new membuf header and changed used type.
+
+ Renamed error codes from INVALID to INV and removed _ERROR suffixes.
+
+2003-06-03 Werner Koch <wk@gnupg.org>
+
+ Changed all error codes in all files to the new libgpg-error scheme.
+
+ * gpgsm.h: Include gpg-error.h .
+ * Makefile.am: Link with libgpg-error.
+
+2003-04-29 Werner Koch <wk@gnupg.org>
+
+ * Makefile.am: Use libassuan. Don't override LDFLAGS anymore.
+ * server.c (register_commands): Adjust for new Assuan semantics.
+
+2002-12-03 Werner Koch <wk@gnupg.org>
+
+ * call-agent.c (gpgsm_agent_passwd): New.
+ * gpgsm.c (main): New command --passwd and --call-protect-tool
+ (run_protect_tool): New.
+
+2002-11-25 Werner Koch <wk@gnupg.org>
+
+ * verify.c (gpgsm_verify): Handle content-type attribute.
+
+2002-11-13 Werner Koch <wk@gnupg.org>
+
+ * call-agent.c (start_agent): Try to use $GPG_TTY instead of
+ ttyname. Changed ttyname to test stdin becuase it can be assumed
+ that output redirection is more common that input redirection.
+
+2002-11-12 Werner Koch <wk@gnupg.org>
+
+ * gpgsm.c: New command --call-dirmngr.
+ * call-dirmngr.c (gpgsm_dirmngr_run_command)
+ (run_command_inq_cb,run_command_cb)
+ (run_command_status_cb): New.
+
+2002-11-11 Werner Koch <wk@gnupg.org>
+
+ * certcheck.c (gpgsm_check_cms_signature): Don't double free
+ s_sig but free s_pkey at leave.
+
+2002-11-10 Werner Koch <wk@gnupg.org>
+
+ * gpgsm.c: Removed duplicate --list-secret-key entry.
+
+2002-09-19 Werner Koch <wk@gnupg.org>
+
+ * certcheck.c (gpgsm_check_cert_sig): Add cert hash debugging.
+
+ * certchain.c (find_up): Print info when the cert was not found
+ by the autorithyKeyIdentifier.
+
+2002-09-03 Werner Koch <wk@gnupg.org>
+
+ * gpgsm.c (main): Disable the internal libgcrypt locking.
+
+2002-08-21 Werner Koch <wk@gnupg.org>
+
+ * import.c (print_imported_summary): Cleaned up. Print new
+ not_imported value.
+ (check_and_store): Update non_imported counter.
+ (print_import_problem): New.
+ (check_and_store): Print error status message.
+ * server.c (get_status_string): Added STATUS_IMPORT_PROBLEM.
+
+2002-08-20 Werner Koch <wk@gnupg.org>
+
+ * gpgsm.c (main): Use the log file only in server mode.
+
+ * import.c (print_imported_summary): New.
+ (check_and_store): Update the counters, take new argument.
+ (import_one): Factored out core of gpgsm_import.
+ (gpgsm_import): Print counters.
+ (gpgsm_import_files): New.
+ * gpgsm.c (main): Use the new function for import.
+
+2002-08-19 Werner Koch <wk@gnupg.org>
+
+ * decrypt.c (gpgsm_decrypt): Return a better error status token.
+ * verify.c (gpgsm_verify): Don't error on messages with no signing
+ time or no message digest. This is only the case for messages
+ without any signed attributes.
+
+2002-08-16 Werner Koch <wk@gnupg.org>
+
+ * certpath.c: Renamed to ..
+ * certchain.c: this. Renamed all all other usages of "path" in the
+ context of certificates to "chain".
+
+ * call-agent.c (learn_cb): Special treatment when the issuer
+ certificate is missing.
+
+2002-08-10 Werner Koch <wk@gnupg.org>
+
+ * Makefile.am (INCLUDES): Add definition for localedir.
+
+ * keylist.c (list_cert_colon): Print the short fingerprint in the
+ key ID field.
+ * fingerprint.c (gpgsm_get_short_fingerprint): New.
+ * verify.c (gpgsm_verify): Print more verbose info for a good
+ signature.
+
+2002-08-09 Werner Koch <wk@gnupg.org>
+
+ * decrypt.c (prepare_decryption): Hack to detected already
+ unpkcsedone keys.
+
+ * gpgsm.c (emergency_cleanup): New.
+ (main): Initialize the signal handler.
+
+ * sign.c (gpgsm_sign): Reset the hash context for subsequent
+ signers and release it at the end.
+
+2002-08-05 Werner Koch <wk@gnupg.org>
+
+ * server.c (cmd_signer): New command "SIGNER"
+ (register_commands): Register it.
+ (cmd_sign): Pass the signer list to gpgsm_sign.
+ * certlist.c (gpgsm_add_to_certlist): Add SECRET argument, check
+ for secret key if set and changed all callers.
+ * sign.c (gpgsm_sign): New argument SIGNERLIST and implemt
+ multiple signers.
+ * gpgsm.c (main): Support more than one -u.
+
+ * server.c (cmd_recipient): Return reason code 1 for No_Public_Key
+ which is actually what gets returned from add_to_certlist.
+
+2002-07-26 Werner Koch <wk@gnupg.org>
+
+ * certcheck.c (gpgsm_check_cert_sig): Implement proper cleanup.
+ (gpgsm_check_cms_signature): Ditto.
+
+2002-07-22 Werner Koch <wk@gnupg.org>
+
+ * keydb.c (keydb_add_resource): Register a lock file.
+ (lock_all, unlock_all): Implemented.
+
+ * delete.c: New.
+ * gpgsm.c: Made --delete-key work.
+ * server.c (cmd_delkeys): New.
+ (register_commands): New command DELKEYS.
+
+ * decrypt.c (gpgsm_decrypt): Print a convenience note when RC2 is
+ used and a STATUS_ERROR with the algorithm oid.
+
+2002-07-03 Werner Koch <wk@gnupg.org>
+
+ * server.c (gpgsm_status2): Insert a blank between all optional
+ arguments when using assuan.
+ * server.c (cmd_recipient): No more need for extra blank in constants.
+ * import.c (print_imported_status): Ditto.
+ * gpgsm.c (main): Ditto.
+
+2002-07-02 Werner Koch <wk@gnupg.org>
+
+ * verify.c (gpgsm_verify): Extend the STATUS_BADSIG line with
+ the fingerprint.
+
+ * certpath.c (check_cert_policy): Don't use log_error to print a
+ warning.
+
+ * keydb.c (keydb_store_cert): Add optional ar EXISTED and changed
+ all callers.
+ * call-agent.c (learn_cb): Print info message only for real imports.
+
+ * import.c (gpgsm_import): Moved duplicated code to ...
+ (check_and_store): new function. Added magic to import the entire
+ chain. Print status only for real imports and moved printing code
+ to ..
+ (print_imported_status): New.
+
+ * call-dirmngr.c (gpgsm_dirmngr_isvalid): print status of dirmngr
+ call in very verbose mode.
+
+ * gpgsm.c (main): Use the same error codes for STATUS_INV_RECP as
+ with the server mode.
+
+2002-06-29 Werner Koch <wk@gnupg.org>
+
+ * gpgsm.c: New option --auto-issuer-key-retrieve.
+ * certpath.c (find_up): Try to retrieve an issuer key from an
+ external source and from the ephemeral key DB.
+ (find_up_store_certs_cb): New.
+
+ * keydb.c (keydb_set_ephemeral): Does now return the old
+ state. Call the backend only when required.
+
+ * call-dirmngr.c (start_dirmngr): Use GNUPG_DEFAULT_DIRMNGR.
+ (lookup_status_cb): Issue status only when CTRL is not NULL.
+ (gpgsm_dirmngr_lookup): Document that CTRL is optional.
+
+ * call-agent.c (start_agent): Use GNUPG_DEFAULT_AGENT.
+
+2002-06-28 Werner Koch <wk@gnupg.org>
+
+ * server.c (cmd_recipient): Add more reason codes.
+
+2002-06-27 Werner Koch <wk@gnupg.org>
+
+ * certpath.c (gpgsm_basic_cert_check): Use
+ --debug-no-path-validation to also bypass this basic check.
+
+ * gpgsm.c (main): Use GNUPG_DEFAULT_HOMEDIR constant.
+
+ * call-agent.c (start_agent): Create and pass the list of FD to
+ keep in the child to assuan.
+ * call-dirmngr.c (start_dirmngr): Ditto.
+
+2002-06-26 Werner Koch <wk@gnupg.org>
+
+ * import.c (gpgsm_import): Print an STATUS_IMPORTED.
+
+ * gpgsm.c: --debug-no-path-validation does not take an argument.
+
+2002-06-25 Werner Koch <wk@gnupg.org>
+
+ * certdump.c (print_dn_part): Always print a leading slash,
+ removed NEED_DELIM arg and changed caller.
+
+ * export.c (gpgsm_export): Print LFs to FP and not stdout.
+ (print_short_info): Ditto. Make use of gpgsm_print_name.
+
+ * server.c (cmd_export): Use output-fd instead of data lines; this
+ was actually the specified way.
+
+2002-06-24 Werner Koch <wk@gnupg.org>
+
+ * gpgsm.c: Removed duped help entry for --list-keys.
+
+ * gpgsm.c, gpgsm.h: New option --debug-no-path-validation.
+
+ * certpath.c (gpgsm_validate_path): Use it here instead of the
+ debug flag hack.
+
+ * certpath.c (check_cert_policy): Return No_Policy_Match if the
+ policy file could not be opened.
+
+2002-06-20 Werner Koch <wk@gnupg.org>
+
+ * certlist.c (gpgsm_add_to_certlist): Fixed locating of a
+ certificate with the required key usage.
+
+ * gpgsm.c (main): Fixed a segv when using --outfile without an
+ argument.
+
+ * keylist.c (print_capabilities): Also check for non-repudiation
+ and data encipherment.
+ * certlist.c (cert_usage_p): Test for signing and encryption was
+ swapped. Add a case for certification usage, handle
+ non-repudiation and data encipherment.
+ (gpgsm_cert_use_cert_p): New.
+ (gpgsm_add_to_certlist): Added a CTRL argument and changed all
+ callers to pass it.
+ * certpath.c (gpgsm_validate_path): Use it here to print a status
+ message. Added a CTRL argument and changed all callers to pass it.
+ * decrypt.c (gpgsm_decrypt): Print a status message for wrong key
+ usage.
+ * verify.c (gpgsm_verify): Ditto.
+ * keydb.c (classify_user_id): Allow a colon delimited fingerprint.
+
+2002-06-19 Werner Koch <wk@gnupg.org>
+
+ * call-agent.c (learn_cb): Use log_info instead of log_error on
+ successful import.
+
+ * keydb.c (keydb_set_ephemeral): New.
+ (keydb_store_cert): New are ephemeral, changed all callers.
+ * keylist.c (list_external_cb): Store cert as ephemeral.
+ * export.c (gpgsm_export): Kludge to export epehmeral certificates.
+
+ * gpgsm.c (main): New command --list-external-keys.
+
+2002-06-17 Werner Koch <wk@gnupg.org>
+
+ * certreqgen.c (read_parameters): Improved error handling.
+ (gpgsm_genkey): Print error message.
+
+2002-06-13 Werner Koch <wk@gnupg.org>
+
+ * gpgsm.c (main): New option --log-file.
+
+2002-06-12 Werner Koch <wk@gnupg.org>
+
+ * call-dirmngr.c (lookup_status_cb): New.
+ (gpgsm_dirmngr_lookup): Use the status CB. Add new arg CTRL and
+ changed caller to pass it.
+
+ * gpgsm.c (open_fwrite): New.
+ (main): Allow --output for --verify.
+
+ * sign.c (hash_and_copy_data): New.
+ (gpgsm_sign): Implemented normal (non-detached) signatures.
+ * gpgsm.c (main): Ditto.
+
+ * certpath.c (gpgsm_validate_path): Special error handling for
+ no policy match.
+
+2002-06-10 Werner Koch <wk@gnupg.org>
+
+ * server.c (get_status_string): Add STATUS_ERROR.
+
+ * certpath.c (gpgsm_validate_path): Tweaked the error checking to
+ return error codes in a more sensitive way.
+ * verify.c (gpgsm_verify): Send status TRUST_NEVER also for a bad
+ CA certificate and when the certificate has been revoked. Issue
+ TRUST_FULLY even when the cert has expired. Append an error token
+ to these status lines. Issue the new generic error status when a
+ cert was not found and when leaving the function.
+
+2002-06-04 Werner Koch <wk@gnupg.org>
+
+ * gpgsm.c (main): New command --list-sigs
+ * keylist.c (list_cert_std): New. Use it whenever colon mode is
+ not used.
+ (list_cert_chain): New.
+
+2002-05-31 Werner Koch <wk@gnupg.org>
+
+ * gpgsm.c (main): Don't print the "go ahead" message for an
+ invalid command.
+
+2002-05-23 Werner Koch <wk@gnupg.org>
+
+ * import.c (gpgsm_import): Add error messages.
+
+2002-05-21 Werner Koch <wk@gnupg.org>
+
+ * keylist.c (list_internal_keys): Renamed from gpgsm_list_keys.
+ (list_external_keys): New.
+ (gpgsm_list_keys): Dispatcher for above.
+ * call-dirmngr.c (lookup_cb,pattern_from_strlist)
+ (gpgsm_dirmngr_lookup): New.
+ * server.c (option_handler): Handle new option --list-mode.
+ (do_listkeys): Handle options and actually use the mode argument.
+ (get_status_string): New code TRUNCATED.
+
+ * import.c (gpgsm_import): Try to identify the type of input and
+ handle certs-only messages.
+
+2002-05-14 Werner Koch <wk@gnupg.org>
+
+ * gpgsm.c: New option --faked-system-time
+ * sign.c (gpgsm_sign): And use it here.
+ * certpath.c (gpgsm_validate_path): Ditto.
+
+2002-05-03 Werner Koch <wk@gnupg.org>
+
+ * certpath.c (gpgsm_validate_path): Added EXPTIME arg and changed
+ all callers.
+ * verify.c (gpgsm_verify): Tweaked usage of log_debug and
+ log_error. Return EXPSIG status and add expiretime to VALIDSIG.
+
+2002-04-26 Werner Koch <wk@gnupg.org>
+
+ * gpgsm.h (DBG_AGENT,DBG_AGENT_VALUE): Replaced by DBG_ASSUAN_*.
+ Changed all users.
+
+ * call-agent.c (start_agent): Be more silent without -v.
+ * call-dirmngr.c (start_dirmngr): Ditto.
+
+2002-04-25 Werner Koch <wk@gnupg.org>
+
+ * call-agent.c (start_agent): Make copies of old locales and check
+ for setlocale.
+
+2002-04-25 Marcus Brinkmann <marcus@g10code.de>
+
+ * call-agent.c (start_agent): Fix error handling logic so the
+ locale is always correctly reset.
+
+2002-04-25 Marcus Brinkmann <marcus@g10code.de>
+
+ * server.c (option_handler): Accept display, ttyname, ttytype,
+ lc_ctype and lc_messages options.
+ * gpgsm.c (main): Allocate memory for these options.
+ * gpgsm.h (struct opt): Make corresponding members non-const.
+
+2002-04-24 Marcus Brinkmann <marcus@g10code.de>
+
+ * gpgsm.h (struct opt): New members display, ttyname, ttytype,
+ lc_ctype, lc_messages.
+ * gpgsm.c (enum cmd_and_opt_values): New members oDisplay,
+ oTTYname, oTTYtype, oLCctype, oLCmessages.
+ (opts): New entries for these options.
+ (main): Handle these new options.
+ * call-agent.c (start_agent): Set the various display and tty
+ parameter after resetting.
+
+2002-04-18 Werner Koch <wk@gnupg.org>
+
+ * certreqgen.c (gpgsm_genkey): Write status output on success.
+
+2002-04-15 Werner Koch <wk@gnupg.org>
+
+ * gpgsm.c (main): Check ksba version.
+
+ * certpath.c (find_up): New to use the authorithKeyIdentifier.
+ Use it in all other functions to locate the signing cert..
+
+2002-04-11 Werner Koch <wk@gnupg.org>
+
+ * certlist.c (cert_usable_p): New.
+ (gpgsm_cert_use_sign_p,gpgsm_cert_use_encrypt_p): New.
+ (gpgsm_cert_use_verify_p,gpgsm_cert_use_decrypt_p): New.
+ (gpgsm_add_to_certlist): Check the key usage.
+ * sign.c (gpgsm_sign): Ditto.
+ * verify.c (gpgsm_verify): Print a message wehn an unsuitable
+ certificate was used.
+ * decrypt.c (gpgsm_decrypt): Ditto
+ * keylist.c (print_capabilities): Determine values from the cert.
+
+2002-03-28 Werner Koch <wk@gnupg.org>
+
+ * keylist.c (list_cert_colon): Fixed listing of crt record; the
+ issuer is not at the right place. Print a chainingID.
+ * certpath.c (gpgsm_walk_cert_chain): Be a bit more silent on
+ common errors.
+
+2002-03-21 Werner Koch <wk@gnupg.org>
+
+ * export.c: New.
+ * gpgsm.c: Add command --export.
+ * server.c (cmd_export): New.
+
+2002-03-13 Werner Koch <wk@gnupg.org>
+
+ * decrypt.c (gpgsm_decrypt): Allow multiple recipients.
+
+2002-03-12 Werner Koch <wk@gnupg.org>
+
+ * certpath.c (check_cert_policy): Print the policy list.
+
+ * verify.c (gpgsm_verify): Detect certs-only message.
+
+2002-03-11 Werner Koch <wk@gnupg.org>
+
+ * import.c (gpgsm_import): Print a notice about imported certificates
+ when in verbose mode.
+
+ * gpgsm.c (main): Print INV_RECP status.
+ * server.c (cmd_recipient): Ditto.
+
+ * server.c (gpgsm_status2): New. Allows for a list of strings.
+ (gpgsm_status): Divert to gpgsm_status2.
+
+ * encrypt.c (gpgsm_encrypt): Don't use a default key when no
+ recipients are given. Print a NO_RECP status.
+
+2002-03-06 Werner Koch <wk@gnupg.org>
+
+ * server.c (cmd_listkeys, cmd_listsecretkeys): Divert to
+ (do_listkeys): new. Add pattern parsing.
+
+ * keylist.c (gpgsm_list_keys): Handle selection pattern.
+
+ * gpgsm.c: New command --learn-card
+ * call-agent.c (learn_cb,gpgsm_agent_learn): New.
+
+ * gpgsm.c (main): Print error messages for non-implemented commands.
+
+ * base64.c (base64_reader_cb): Use case insensitive compare of the
+ Content-Type string to detect plain base-64.
+
+2002-03-05 Werner Koch <wk@gnupg.org>
+
+ * gpgsm.c, gpgsm.h: Add local_user.
+ * sign.c (gpgsm_get_default_cert): New.
+ (get_default_signer): Use the new function if local_user is not
+ set otherwise used that value.
+ * encrypt.c (get_default_recipient): Removed.
+ (gpgsm_encrypt): Use gpgsm_get_default_cert.
+
+ * verify.c (gpgsm_verify): Better error text for a bad signature
+ found by comparing the hashs.
+
+2002-02-27 Werner Koch <wk@gnupg.org>
+
+ * call-dirmngr.c, call-agent.c: Add 2 more arguments to all uses
+ of assuan_transact.
+
+2002-02-25 Werner Koch <wk@gnupg.org>
+
+ * server.c (option_handler): Allow to use -2 for "send all certs
+ except the root cert".
+ * sign.c (add_certificate_list): Implement it here.
+ * certpath.c (gpgsm_is_root_cert): New.
+
+2002-02-19 Werner Koch <wk@gnupg.org>
+
+ * certpath.c (check_cert_policy): New.
+ (gpgsm_validate_path): And call it from here.
+ * gpgsm.c (main): New options --policy-file,
+ --disable-policy-checks and --enable-policy-checks.
+ * gpgsm.h (opt): Added policy_file, no_policy_checks.
+
+2002-02-18 Werner Koch <wk@gnupg.org>
+
+ * certpath.c (gpgsm_validate_path): Ask the agent to add the
+ certificate into the trusted list.
+ * call-agent.c (gpgsm_agent_marktrusted): New.
+
+2002-02-07 Werner Koch <wk@gnupg.org>
+
+ * certlist.c (gpgsm_add_to_certlist): Check that the specified
+ name identifies a certificate unambiguously.
+ (gpgsm_find_cert): Ditto.
+
+ * server.c (cmd_listkeys): Check that the data stream is available.
+ (cmd_listsecretkeys): Ditto.
+ (has_option): New.
+ (cmd_sign): Fix ambiguousity in option recognition.
+
+ * gpgsm.c (main): Enable --logger-fd.
+
+ * encrypt.c (gpgsm_encrypt): Increased buffer size for better
+ performance.
+
+ * call-agent.c (gpgsm_agent_pksign): Check the S-Exp received from
+ the agent.
+
+ * keylist.c (list_cert_colon): Filter out control characters.
+
+2002-02-06 Werner Koch <wk@gnupg.org>
+
+ * decrypt.c (gpgsm_decrypt): Bail out after an decryption error.
+
+ * server.c (reset_notify): Close input and output FDs.
+ (cmd_encrypt,cmd_decrypt,cmd_verify,cmd_sign.cmd_import)
+ (cmd_genkey): Close the FDs and release the recipient list even in
+ the error case.
+
+2002-02-01 Marcus Brinkmann <marcus@g10code.de>
+
+ * sign.c (gpgsm_sign): Do not release certificate twice.
+
+2002-01-29 Werner Koch <wk@gnupg.org>
+
+ * call-agent.c (gpgsm_agent_havekey): New.
+ * keylist.c (list_cert_colon): New arg HAVE_SECRET, print "crs"
+ when we know that the secret key is available.
+ (gpgsm_list_keys): New arg MODE, check whether a secret key is
+ available. Changed all callers.
+ * gpgsm.c (main): New command --list-secret-keys.
+ * server.c (cmd_listsecretkeys): New.
+ (cmd_listkeys): Return secret keys with "crs" record.
+
+2002-01-28 Werner Koch <wk@gnupg.org>
+
+ * certreqgen.c (create_request): Store the email address in the req.
+
+2002-01-25 Werner Koch <wk@gnupg.org>
+
+ * gpgsm.c (main): Disable core dumps.
+
+ * sign.c (add_certificate_list): New.
+ (gpgsm_sign): Add the certificates to the CMS object.
+ * certpath.c (gpgsm_walk_cert_chain): New.
+ * gpgsm.h (server_control_s): Add included_certs.
+ * gpgsm.c: Add option --include-certs.
+ (gpgsm_init_default_ctrl): New.
+ (main): Call it.
+ * server.c (gpgsm_server): Ditto.
+ (option_handler): Support --include-certs.
+
+2002-01-23 Werner Koch <wk@gnupg.org>
+
+ * certpath.c (gpgsm_validate_path): Print the DN of a missing issuer.
+ * certdump.c (gpgsm_dump_string): New.
+ (print_dn): Replaced by above.
+
+2002-01-22 Werner Koch <wk@gnupg.org>
+
+ * certpath.c (unknown_criticals): New.
+ (allowed_ca): New.
+ (gpgsm_validate_path): Check validity, CA attribute, path length
+ and unknown critical extensions.
+
+2002-01-21 Werner Koch <wk@gnupg.org>
+
+ * gpgsm.c: Add option --enable-crl-checks.
+
+ * call-agent.c (start_agent): Implemented socket based access.
+ * call-dirmngr.c (start_dirmngr): Ditto.
+
+2002-01-20 Werner Koch <wk@gnupg.org>
+
+ * server.c (option_handler): New.
+ (gpgsm_server): Register it with assuan.
+
+2002-01-19 Werner Koch <wk@gnupg.org>
+
+ * server.c (gpgsm_server): Use assuan_deinit_server and setup
+ assuan logging if enabled.
+ * call-agent.c (inq_ciphertext_cb): Don't show the session key in
+ an Assuan log file.
+
+ * gpgsm.c (my_strusage): Take bugreport address from configure.ac
+
+2002-01-15 Werner Koch <wk@gnupg.org>
+
+ * import.c (gpgsm_import): Just do a basic cert check before
+ storing it.
+ * certpath.c (gpgsm_basic_cert_check): New.
+
+ * keydb.c (keydb_store_cert): New.
+ * import.c (store_cert): Removed and change all caller to use
+ the new function.
+ * verify.c (store_cert): Ditto.
+
+ * certlist.c (gpgsm_add_to_certlist): Validate the path
+
+ * certpath.c (gpgsm_validate_path): Check the trust list.
+ * call-agent.c (gpgsm_agent_istrusted): New.
+
+2002-01-14 Werner Koch <wk@gnupg.org>
+
+ * call-dirmngr.c (inq_certificate): Changed for new interface semantic.
+ * certlist.c (gpgsm_find_cert): New.
+
+2002-01-13 Werner Koch <wk@gnupg.org>
+
+ * fingerprint.c (gpgsm_get_certid): Print the serial and not the
+ hash after the dot.
+
+2002-01-11 Werner Koch <wk@gnupg.org>
+
+ * call-dirmngr.c: New.
+ * certpath.c (gpgsm_validate_path): Check the CRL here.
+ * fingerprint.c (gpgsm_get_certid): New.
+ * gpgsm.c: New options --dirmngr-program and --disable-crl-checks.
+
+2002-01-10 Werner Koch <wk@gnupg.org>
+
+ * base64.c (gpgsm_create_writer): Allow to set the object name
+
+2002-01-08 Werner Koch <wk@gnupg.org>
+
+ * keydb.c (spacep): Removed because it is now in util.c
+
+ * server.c (cmd_genkey): New.
+ * certreqgen.c: New. The parameter handling code has been taken
+ from gnupg/g10/keygen.c version 1.0.6.
+ * call-agent.c (gpgsm_agent_genkey): New.
+
+2002-01-02 Werner Koch <wk@gnupg.org>
+
+ * server.c (rc_to_assuan_status): Removed and changed all callers
+ to use map_to_assuan_status.
+
+2001-12-20 Werner Koch <wk@gnupg.org>
+
+ * verify.c (gpgsm_verify): Implemented non-detached signature
+ verification. Add OUT_FP arg, initialize a writer and changed all
+ callers.
+ * server.c (cmd_verify): Pass an out_fp if one has been set.
+
+ * base64.c (base64_reader_cb): Try to detect an S/MIME body part.
+
+ * certdump.c (print_sexp): Renamed to gpgsm_dump_serial, made
+ global.
+ (print_time): Renamed to gpgsm_dump_time, made global.
+ (gpgsm_dump_serial): Take a real S-Expression as argument and
+ print the first item.
+ * keylist.c (list_cert_colon): Ditto.
+ * keydb.c (keydb_search_issuer_sn): Ditto.
+ * decrypt.c (print_integer_sexp): Removed and made callers
+ use gpgsm_dump_serial.
+ * verify.c (print_time): Removed, made callers use gpgsm_dump_time.
+
+2001-12-19 Marcus Brinkmann <marcus@g10code.de>
+
+ * call-agent.c (start_agent): Add new argument to assuan_pipe_connect.
+
+2001-12-18 Werner Koch <wk@gnupg.org>
+
+ * verify.c (print_integer_sexp): Renamed from print_integer and
+ print the serial number according to the S-Exp rules.
+ * decrypt.c (print_integer_sexp): Ditto.
+
+2001-12-17 Werner Koch <wk@gnupg.org>
+
+ * keylist.c (list_cert_colon): Changed for new return value of
+ get_serial.
+ * keydb.c (keydb_search_issuer_sn): Ditto.
+ * certcheck.c (gpgsm_check_cert_sig): Likewise for other S-Exp
+ returingin functions.
+ * fingerprint.c (gpgsm_get_keygrip): Ditto.
+ * encrypt.c (encrypt_dek): Ditto
+ * certcheck.c (gpgsm_check_cms_signature): Ditto
+ * decrypt.c (prepare_decryption): Ditto.
+ * call-agent.c (gpgsm_agent_pkdecrypt): Removed arg ciphertextlen,
+ use KsbaSexp type and calculate the length.
+
+ * certdump.c (print_sexp): Remaned from print_integer, changed caller.
+
+ * Makefile.am: Use the LIBGCRYPT and LIBKSBA variables.
+
+ * fingerprint.c (gpgsm_get_keygrip): Use the new
+ gcry_pk_get_keygrip to calculate the grip - note the algorithm and
+ therefore the grip values changed.
+
+2001-12-15 Werner Koch <wk@gnupg.org>
+
+ * certcheck.c (gpgsm_check_cms_signature): Removed the faked-key
+ kludge.
+ (gpgsm_create_cms_signature): Removed the commented fake key
+ code. This makes the function pretty simple.
+
+ * gpgsm.c (main): Renamed the default key database to "keyring.kbx".
+
+ * decrypt.c (gpgsm_decrypt): Write STATUS_DECRYPTION_*.
+ * sign.c (gpgsm_sign): Write a STATUS_SIG_CREATED.
+
+2001-12-14 Werner Koch <wk@gnupg.org>
+
+ * keylist.c (list_cert_colon): Kludge to show an email address
+ encoded in the subject's DN.
+
+ * verify.c (gpgsm_verify): Add hash debug helpers
+ * sign.c (gpgsm_sign): Ditto.
+
+ * base64.c (base64_reader_cb): Reset the linelen when we need to
+ skip the line and adjusted test; I somehow forgot about DeMorgan.
+
+ * server.c (cmd_encrypt,cmd_decrypt,cmd_sign,cmd_verify)
+ (cmd_import): Close the FDs on success.
+ (close_message_fd): New.
+ (input_notify): Setting autodetect_encoding to 0 after initializing
+ it to 0 is pretty pointless. Easy to fix.
+
+ * gpgsm.c (main): New option --debug-wait n, so that it is
+ possible to attach gdb when used in server mode.
+
+ * sign.c (get_default_signer): Use keydb_classify_name here.
+
+2001-12-14 Marcus Brinkmann <marcus@g10code.de>
+
+ * call-agent.c (LINELENGTH): Removed.
+ (gpgsm_agent_pksign): Use ASSUAN_LINELENGTH, not LINELENGTH.
+ (gpgsm_agent_pkdecrypt): Likewise.
+
+2001-12-13 Werner Koch <wk@gnupg.org>
+
+ * keylist.c (list_cert_colon): Print alternative names of subject
+ and a few other values.
+
+2001-12-12 Werner Koch <wk@gnupg.org>
+
+ * gpgsm.c (main): New options --assume-{armor,base64,binary}.
+ * base64.c (base64_reader_cb): Fixed non-autodetection mode.
+
+2001-12-04 Werner Koch <wk@gnupg.org>
+
+ * call-agent.c (read_from_agent): Check for inquire responses.
+ (request_reply): Handle them using a new callback arg, changed all
+ callers.
+ (gpgsm_agent_pkdecrypt): New.
+
+2001-11-27 Werner Koch <wk@gnupg.org>
+
+ * base64.c: New. Changed all other functions to use this instead
+ of direct creation of ksba_reader/writer.
+ * gpgsm.c (main): Set ctrl.auto_encoding unless --no-armor is used.
+
+2001-11-26 Werner Koch <wk@gnupg.org>
+
+ * gpgsm.c: New option --agent-program
+ * call-agent.c (start_agent): Allow to override the default path
+ to the agent.
+
+ * keydb.c (keydb_add_resource): Create keybox
+
+ * keylist.c (gpgsm_list_keys): Fixed non-server keylisting.
+
+ * server.c (rc_to_assuan_status): New. Use it for all commands.
+
+
+ Copyright 2001, 2002, 2003, 2004, 2005, 2006,
+ 2007, 2008, 2009 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/sm/Makefile.am b/sm/Makefile.am
new file mode 100644
index 0000000..d945d71
--- /dev/null
+++ b/sm/Makefile.am
@@ -0,0 +1,71 @@
+# Copyright (C) 2001, 2002, 2003 Free Software Foundation, Inc.
+#
+# This file is part of GnuPG.
+#
+# GnuPG is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# GnuPG is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, see <http://www.gnu.org/licenses/>.
+
+## Process this file with automake to produce Makefile.in
+
+
+bin_PROGRAMS = gpgsm
+
+EXTRA_DIST = ChangeLog-2011
+
+AM_CFLAGS = $(LIBGCRYPT_CFLAGS) $(KSBA_CFLAGS) $(LIBASSUAN_CFLAGS)
+
+AM_CPPFLAGS = -I$(top_srcdir)/gl -I$(top_srcdir)/common -I$(top_srcdir)/intl
+include $(top_srcdir)/am/cmacros.am
+
+
+gpgsm_SOURCES = \
+ gpgsm.c gpgsm.h \
+ misc.c \
+ keydb.c keydb.h \
+ server.c \
+ call-agent.c \
+ call-dirmngr.c \
+ fingerprint.c \
+ base64.c \
+ certlist.c \
+ certdump.c \
+ certcheck.c \
+ certchain.c \
+ keylist.c \
+ verify.c \
+ sign.c \
+ encrypt.c \
+ decrypt.c \
+ import.c \
+ export.c \
+ delete.c \
+ certreqgen.c \
+ certreqgen-ui.c \
+ qualified.c
+
+
+common_libs = $(libcommon) ../kbx/libkeybox.a ../jnlib/libjnlib.a \
+ ../gl/libgnu.a
+
+gpgsm_LDADD = $(common_libs) ../common/libgpgrl.a $(NETLIBS) \
+ $(LIBGCRYPT_LIBS) $(KSBA_LIBS) $(LIBASSUAN_LIBS) \
+ $(GPG_ERROR_LIBS) $(LIBREADLINE) $(LIBINTL) $(ZLIBS) $(LIBICONV)
+
+# Make sure that all libs are build before we use them. This is
+# important for things like make -j2.
+$(PROGRAMS): $(common_libs)
+
+
+
+
+
diff --git a/sm/Makefile.in b/sm/Makefile.in
new file mode 100644
index 0000000..b1cd1da
--- /dev/null
+++ b/sm/Makefile.in
@@ -0,0 +1,711 @@
+# Makefile.in generated by automake 1.11.1 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
+# 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation,
+# Inc.
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+# Copyright (C) 2001, 2002, 2003 Free Software Foundation, Inc.
+#
+# This file is part of GnuPG.
+#
+# GnuPG is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# GnuPG is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, see <http://www.gnu.org/licenses/>.
+
+# cmacros.am - C macro definitions
+# Copyright (C) 2004 Free Software Foundation, Inc.
+#
+# This file is part of GnuPG.
+#
+# GnuPG is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# GnuPG is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, see <http://www.gnu.org/licenses/>.
+
+VPATH = @srcdir@
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+bin_PROGRAMS = gpgsm$(EXEEXT)
+DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in \
+ $(top_srcdir)/am/cmacros.am
+@HAVE_DOSISH_SYSTEM_FALSE@am__append_1 = -DGNUPG_BINDIR="\"$(bindir)\"" \
+@HAVE_DOSISH_SYSTEM_FALSE@ -DGNUPG_LIBEXECDIR="\"$(libexecdir)\"" \
+@HAVE_DOSISH_SYSTEM_FALSE@ -DGNUPG_LIBDIR="\"$(libdir)/@PACKAGE@\"" \
+@HAVE_DOSISH_SYSTEM_FALSE@ -DGNUPG_DATADIR="\"$(datadir)/@PACKAGE@\"" \
+@HAVE_DOSISH_SYSTEM_FALSE@ -DGNUPG_SYSCONFDIR="\"$(sysconfdir)/@PACKAGE@\""
+
+
+# If a specific protect tool program has been defined, pass its name
+# to cc. Note that these macros should not be used directly but via
+# the gnupg_module_name function.
+@GNUPG_AGENT_PGM_TRUE@am__append_2 = -DGNUPG_DEFAULT_AGENT="\"@GNUPG_AGENT_PGM@\""
+@GNUPG_PINENTRY_PGM_TRUE@am__append_3 = -DGNUPG_DEFAULT_PINENTRY="\"@GNUPG_PINENTRY_PGM@\""
+@GNUPG_SCDAEMON_PGM_TRUE@am__append_4 = -DGNUPG_DEFAULT_SCDAEMON="\"@GNUPG_SCDAEMON_PGM@\""
+@GNUPG_DIRMNGR_PGM_TRUE@am__append_5 = -DGNUPG_DEFAULT_DIRMNGR="\"@GNUPG_DIRMNGR_PGM@\""
+@GNUPG_PROTECT_TOOL_PGM_TRUE@am__append_6 = -DGNUPG_DEFAULT_PROTECT_TOOL="\"@GNUPG_PROTECT_TOOL_PGM@\""
+subdir = sm
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/gl/m4/absolute-header.m4 \
+ $(top_srcdir)/gl/m4/alloca.m4 $(top_srcdir)/gl/m4/allocsa.m4 \
+ $(top_srcdir)/gl/m4/eealloc.m4 \
+ $(top_srcdir)/gl/m4/gnulib-comp.m4 \
+ $(top_srcdir)/gl/m4/gnulib-tool.m4 \
+ $(top_srcdir)/gl/m4/mkdtemp.m4 $(top_srcdir)/gl/m4/setenv.m4 \
+ $(top_srcdir)/gl/m4/stdint.m4 $(top_srcdir)/gl/m4/strpbrk.m4 \
+ $(top_srcdir)/gl/m4/unistd_h.m4 $(top_srcdir)/m4/autobuild.m4 \
+ $(top_srcdir)/m4/codeset.m4 $(top_srcdir)/m4/estream.m4 \
+ $(top_srcdir)/m4/gettext.m4 $(top_srcdir)/m4/gnupg-pth.m4 \
+ $(top_srcdir)/m4/gpg-error.m4 $(top_srcdir)/m4/iconv.m4 \
+ $(top_srcdir)/m4/isc-posix.m4 $(top_srcdir)/m4/ksba.m4 \
+ $(top_srcdir)/m4/lcmessage.m4 $(top_srcdir)/m4/ldap.m4 \
+ $(top_srcdir)/m4/lib-ld.m4 $(top_srcdir)/m4/lib-link.m4 \
+ $(top_srcdir)/m4/lib-prefix.m4 $(top_srcdir)/m4/libassuan.m4 \
+ $(top_srcdir)/m4/libcurl.m4 $(top_srcdir)/m4/libgcrypt.m4 \
+ $(top_srcdir)/m4/longdouble.m4 $(top_srcdir)/m4/nls.m4 \
+ $(top_srcdir)/m4/po.m4 $(top_srcdir)/m4/progtest.m4 \
+ $(top_srcdir)/m4/readline.m4 $(top_srcdir)/m4/size_max.m4 \
+ $(top_srcdir)/m4/socklen.m4 $(top_srcdir)/m4/sys_socket_h.m4 \
+ $(top_srcdir)/m4/tar-ustar.m4 $(top_srcdir)/m4/xsize.m4 \
+ $(top_srcdir)/acinclude.m4 $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+mkinstalldirs = $(SHELL) $(top_srcdir)/scripts/mkinstalldirs
+CONFIG_HEADER = $(top_builddir)/config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+am__installdirs = "$(DESTDIR)$(bindir)"
+PROGRAMS = $(bin_PROGRAMS)
+am_gpgsm_OBJECTS = gpgsm.$(OBJEXT) misc.$(OBJEXT) keydb.$(OBJEXT) \
+ server.$(OBJEXT) call-agent.$(OBJEXT) call-dirmngr.$(OBJEXT) \
+ fingerprint.$(OBJEXT) base64.$(OBJEXT) certlist.$(OBJEXT) \
+ certdump.$(OBJEXT) certcheck.$(OBJEXT) certchain.$(OBJEXT) \
+ keylist.$(OBJEXT) verify.$(OBJEXT) sign.$(OBJEXT) \
+ encrypt.$(OBJEXT) decrypt.$(OBJEXT) import.$(OBJEXT) \
+ export.$(OBJEXT) delete.$(OBJEXT) certreqgen.$(OBJEXT) \
+ certreqgen-ui.$(OBJEXT) qualified.$(OBJEXT)
+gpgsm_OBJECTS = $(am_gpgsm_OBJECTS)
+am__DEPENDENCIES_1 =
+gpgsm_DEPENDENCIES = $(common_libs) ../common/libgpgrl.a \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1)
+DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)
+depcomp = $(SHELL) $(top_srcdir)/scripts/depcomp
+am__depfiles_maybe = depfiles
+am__mv = mv -f
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+CCLD = $(CC)
+LINK = $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@
+SOURCES = $(gpgsm_SOURCES)
+DIST_SOURCES = $(gpgsm_SOURCES)
+ETAGS = etags
+CTAGS = ctags
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ABSOLUTE_STDINT_H = @ABSOLUTE_STDINT_H@
+ACLOCAL = @ACLOCAL@
+ADNSLIBS = @ADNSLIBS@
+ALLOCA = @ALLOCA@
+ALLOCA_H = @ALLOCA_H@
+AMTAR = @AMTAR@
+AR = @AR@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+BITSIZEOF_PTRDIFF_T = @BITSIZEOF_PTRDIFF_T@
+BITSIZEOF_SIG_ATOMIC_T = @BITSIZEOF_SIG_ATOMIC_T@
+BITSIZEOF_SIZE_T = @BITSIZEOF_SIZE_T@
+BITSIZEOF_WCHAR_T = @BITSIZEOF_WCHAR_T@
+BITSIZEOF_WINT_T = @BITSIZEOF_WINT_T@
+BUILD_INCLUDED_LIBINTL = @BUILD_INCLUDED_LIBINTL@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CC_FOR_BUILD = @CC_FOR_BUILD@
+CFLAGS = @CFLAGS@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DL_LIBS = @DL_LIBS@
+DNSLIBS = @DNSLIBS@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+FAQPROG = @FAQPROG@
+GETTEXT_MACRO_VERSION = @GETTEXT_MACRO_VERSION@
+GMSGFMT = @GMSGFMT@
+GMSGFMT_015 = @GMSGFMT_015@
+GNUPG_AGENT_PGM = @GNUPG_AGENT_PGM@
+GNUPG_DIRMNGR_PGM = @GNUPG_DIRMNGR_PGM@
+GNUPG_PINENTRY_PGM = @GNUPG_PINENTRY_PGM@
+GNUPG_PROTECT_TOOL_PGM = @GNUPG_PROTECT_TOOL_PGM@
+GNUPG_SCDAEMON_PGM = @GNUPG_SCDAEMON_PGM@
+GPGKEYS_CURL = @GPGKEYS_CURL@
+GPGKEYS_FINGER = @GPGKEYS_FINGER@
+GPGKEYS_HKP = @GPGKEYS_HKP@
+GPGKEYS_KDNS = @GPGKEYS_KDNS@
+GPGKEYS_LDAP = @GPGKEYS_LDAP@
+GPGKEYS_MAILTO = @GPGKEYS_MAILTO@
+GPG_ERROR_CFLAGS = @GPG_ERROR_CFLAGS@
+GPG_ERROR_CONFIG = @GPG_ERROR_CONFIG@
+GPG_ERROR_LIBS = @GPG_ERROR_LIBS@
+GREP = @GREP@
+HAVE_INTTYPES_H = @HAVE_INTTYPES_H@
+HAVE_LONG_LONG_INT = @HAVE_LONG_LONG_INT@
+HAVE_SIGNED_SIG_ATOMIC_T = @HAVE_SIGNED_SIG_ATOMIC_T@
+HAVE_SIGNED_WCHAR_T = @HAVE_SIGNED_WCHAR_T@
+HAVE_SIGNED_WINT_T = @HAVE_SIGNED_WINT_T@
+HAVE_STDINT_H = @HAVE_STDINT_H@
+HAVE_SYS_BITYPES_H = @HAVE_SYS_BITYPES_H@
+HAVE_SYS_INTTYPES_H = @HAVE_SYS_INTTYPES_H@
+HAVE_SYS_TYPES_H = @HAVE_SYS_TYPES_H@
+HAVE_UNSIGNED_LONG_LONG_INT = @HAVE_UNSIGNED_LONG_LONG_INT@
+HAVE_WCHAR_H = @HAVE_WCHAR_H@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+INTLLIBS = @INTLLIBS@
+INTL_MACOSX_LIBS = @INTL_MACOSX_LIBS@
+KSBA_CFLAGS = @KSBA_CFLAGS@
+KSBA_CONFIG = @KSBA_CONFIG@
+KSBA_LIBS = @KSBA_LIBS@
+LDAPLIBS = @LDAPLIBS@
+LDAP_CPPFLAGS = @LDAP_CPPFLAGS@
+LDFLAGS = @LDFLAGS@
+LIBASSUAN_CFLAGS = @LIBASSUAN_CFLAGS@
+LIBASSUAN_CONFIG = @LIBASSUAN_CONFIG@
+LIBASSUAN_LIBS = @LIBASSUAN_LIBS@
+LIBCURL = @LIBCURL@
+LIBCURL_CPPFLAGS = @LIBCURL_CPPFLAGS@
+LIBGCRYPT_CFLAGS = @LIBGCRYPT_CFLAGS@
+LIBGCRYPT_CONFIG = @LIBGCRYPT_CONFIG@
+LIBGCRYPT_LIBS = @LIBGCRYPT_LIBS@
+LIBGNU_LIBDEPS = @LIBGNU_LIBDEPS@
+LIBGNU_LTLIBDEPS = @LIBGNU_LTLIBDEPS@
+LIBICONV = @LIBICONV@
+LIBINTL = @LIBINTL@
+LIBOBJS = @LIBOBJS@
+LIBREADLINE = @LIBREADLINE@
+LIBS = @LIBS@
+LIBUSB_LIBS = @LIBUSB_LIBS@
+LIBUTIL_LIBS = @LIBUTIL_LIBS@
+LN_S = @LN_S@
+LTLIBICONV = @LTLIBICONV@
+LTLIBINTL = @LTLIBINTL@
+LTLIBOBJS = @LTLIBOBJS@
+MAINT = @MAINT@
+MAKEINFO = @MAKEINFO@
+MKDIR_P = @MKDIR_P@
+MSGFMT = @MSGFMT@
+MSGFMT_015 = @MSGFMT_015@
+MSGMERGE = @MSGMERGE@
+NETLIBS = @NETLIBS@
+OBJEXT = @OBJEXT@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_GT = @PACKAGE_GT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PERL = @PERL@
+POSUB = @POSUB@
+PTH_CFLAGS = @PTH_CFLAGS@
+PTH_CONFIG = @PTH_CONFIG@
+PTH_LIBS = @PTH_LIBS@
+PTRDIFF_T_SUFFIX = @PTRDIFF_T_SUFFIX@
+RANLIB = @RANLIB@
+SENDMAIL = @SENDMAIL@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+SHRED = @SHRED@
+SIG_ATOMIC_T_SUFFIX = @SIG_ATOMIC_T_SUFFIX@
+SIZE_T_SUFFIX = @SIZE_T_SUFFIX@
+STDINT_H = @STDINT_H@
+STRIP = @STRIP@
+SYS_SOCKET_H = @SYS_SOCKET_H@
+TAR = @TAR@
+UNISTD_H = @UNISTD_H@
+USE_INCLUDED_LIBINTL = @USE_INCLUDED_LIBINTL@
+USE_NLS = @USE_NLS@
+VERSION = @VERSION@
+W32SOCKLIBS = @W32SOCKLIBS@
+WCHAR_T_SUFFIX = @WCHAR_T_SUFFIX@
+WINDRES = @WINDRES@
+WINT_T_SUFFIX = @WINT_T_SUFFIX@
+XGETTEXT = @XGETTEXT@
+XGETTEXT_015 = @XGETTEXT_015@
+XGETTEXT_EXTRA_OPTIONS = @XGETTEXT_EXTRA_OPTIONS@
+ZLIBS = @ZLIBS@
+_libcurl_config = @_libcurl_config@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_CC = @ac_ct_CC@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = $(datadir)/locale
+localstatedir = @localstatedir@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+EXTRA_DIST = ChangeLog-2011
+AM_CFLAGS = $(LIBGCRYPT_CFLAGS) $(KSBA_CFLAGS) $(LIBASSUAN_CFLAGS)
+AM_CPPFLAGS = -I$(top_srcdir)/gl -I$(top_srcdir)/common \
+ -I$(top_srcdir)/intl -DLOCALEDIR=\"$(localedir)\" \
+ $(am__append_1) $(am__append_2) $(am__append_3) \
+ $(am__append_4) $(am__append_5) $(am__append_6)
+
+# Convenience macros
+libcommon = ../common/libcommon.a
+libcommonpth = ../common/libcommonpth.a
+gpgsm_SOURCES = \
+ gpgsm.c gpgsm.h \
+ misc.c \
+ keydb.c keydb.h \
+ server.c \
+ call-agent.c \
+ call-dirmngr.c \
+ fingerprint.c \
+ base64.c \
+ certlist.c \
+ certdump.c \
+ certcheck.c \
+ certchain.c \
+ keylist.c \
+ verify.c \
+ sign.c \
+ encrypt.c \
+ decrypt.c \
+ import.c \
+ export.c \
+ delete.c \
+ certreqgen.c \
+ certreqgen-ui.c \
+ qualified.c
+
+common_libs = $(libcommon) ../kbx/libkeybox.a ../jnlib/libjnlib.a \
+ ../gl/libgnu.a
+
+gpgsm_LDADD = $(common_libs) ../common/libgpgrl.a $(NETLIBS) \
+ $(LIBGCRYPT_LIBS) $(KSBA_LIBS) $(LIBASSUAN_LIBS) \
+ $(GPG_ERROR_LIBS) $(LIBREADLINE) $(LIBINTL) $(ZLIBS) $(LIBICONV)
+
+all: all-am
+
+.SUFFIXES:
+.SUFFIXES: .c .o .obj
+$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(top_srcdir)/am/cmacros.am $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+ && { if test -f $@; then exit 0; else break; fi; }; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu sm/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --gnu sm/Makefile
+.PRECIOUS: Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
+ esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+install-binPROGRAMS: $(bin_PROGRAMS)
+ @$(NORMAL_INSTALL)
+ test -z "$(bindir)" || $(MKDIR_P) "$(DESTDIR)$(bindir)"
+ @list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed 's/$(EXEEXT)$$//' | \
+ while read p p1; do if test -f $$p; \
+ then echo "$$p"; echo "$$p"; else :; fi; \
+ done | \
+ sed -e 'p;s,.*/,,;n;h' -e 's|.*|.|' \
+ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \
+ sed 'N;N;N;s,\n, ,g' | \
+ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \
+ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
+ if ($$2 == $$4) files[d] = files[d] " " $$1; \
+ else { print "f", $$3 "/" $$4, $$1; } } \
+ END { for (d in files) print "f", d, files[d] }' | \
+ while read type dir files; do \
+ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
+ test -z "$$files" || { \
+ echo " $(INSTALL_PROGRAM_ENV) $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(bindir)$$dir'"; \
+ $(INSTALL_PROGRAM_ENV) $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(bindir)$$dir" || exit $$?; \
+ } \
+ ; done
+
+uninstall-binPROGRAMS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \
+ files=`for p in $$list; do echo "$$p"; done | \
+ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \
+ -e 's/$$/$(EXEEXT)/' `; \
+ test -n "$$list" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(bindir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(bindir)" && rm -f $$files
+
+clean-binPROGRAMS:
+ -test -z "$(bin_PROGRAMS)" || rm -f $(bin_PROGRAMS)
+gpgsm$(EXEEXT): $(gpgsm_OBJECTS) $(gpgsm_DEPENDENCIES)
+ @rm -f gpgsm$(EXEEXT)
+ $(LINK) $(gpgsm_OBJECTS) $(gpgsm_LDADD) $(LIBS)
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/base64.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/call-agent.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/call-dirmngr.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/certchain.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/certcheck.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/certdump.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/certlist.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/certreqgen-ui.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/certreqgen.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/decrypt.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/delete.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/encrypt.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/export.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fingerprint.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gpgsm.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/import.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/keydb.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/keylist.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/misc.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/qualified.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/server.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sign.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/verify.Po@am__quote@
+
+.c.o:
+@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(COMPILE) -c $<
+
+.c.obj:
+@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(COMPILE) -c `$(CYGPATH_W) '$<'`
+
+ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES)
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in files) print i; }; }'`; \
+ mkid -fID $$unique
+tags: TAGS
+
+TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
+ $(TAGS_FILES) $(LISP)
+ set x; \
+ here=`pwd`; \
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in files) print i; }; }'`; \
+ shift; \
+ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ if test $$# -gt 0; then \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ "$$@" $$unique; \
+ else \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$unique; \
+ fi; \
+ fi
+ctags: CTAGS
+CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
+ $(TAGS_FILES) $(LISP)
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in files) print i; }; }'`; \
+ test -z "$(CTAGS_ARGS)$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && $(am__cd) $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) "$$here"
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d "$(distdir)/$$file"; then \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+ else \
+ test -f "$(distdir)/$$file" \
+ || cp -p $$d/$$file "$(distdir)/$$file" \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: check-am
+all-am: Makefile $(PROGRAMS)
+installdirs:
+ for dir in "$(DESTDIR)$(bindir)"; do \
+ test -z "$$dir" || $(MKDIR_P) "$$dir"; \
+ done
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ `test -z '$(STRIP)' || \
+ echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+ -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-binPROGRAMS clean-generic mostlyclean-am
+
+distclean: distclean-am
+ -rm -rf ./$(DEPDIR)
+ -rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+ distclean-tags
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+html-am:
+
+info: info-am
+
+info-am:
+
+install-data-am:
+
+install-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am: install-binPROGRAMS
+
+install-html: install-html-am
+
+install-html-am:
+
+install-info: install-info-am
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-am
+
+install-pdf-am:
+
+install-ps: install-ps-am
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+ -rm -rf ./$(DEPDIR)
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am: uninstall-binPROGRAMS
+
+.MAKE: install-am install-strip
+
+.PHONY: CTAGS GTAGS all all-am check check-am clean clean-binPROGRAMS \
+ clean-generic ctags distclean distclean-compile \
+ distclean-generic distclean-tags distdir dvi dvi-am html \
+ html-am info info-am install install-am install-binPROGRAMS \
+ install-data install-data-am install-dvi install-dvi-am \
+ install-exec install-exec-am install-html install-html-am \
+ install-info install-info-am install-man install-pdf \
+ install-pdf-am install-ps install-ps-am install-strip \
+ installcheck installcheck-am installdirs maintainer-clean \
+ maintainer-clean-generic mostlyclean mostlyclean-compile \
+ mostlyclean-generic pdf pdf-am ps ps-am tags uninstall \
+ uninstall-am uninstall-binPROGRAMS
+
+
+# Make sure that all libs are build before we use them. This is
+# important for things like make -j2.
+$(PROGRAMS): $(common_libs)
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/sm/base64.c b/sm/base64.c
new file mode 100644
index 0000000..b0c8dc8
--- /dev/null
+++ b/sm/base64.c
@@ -0,0 +1,733 @@
+/* base64.c
+ * Copyright (C) 2001, 2003 Free Software Foundation, Inc.
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuPG is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <time.h>
+#include <assert.h>
+
+#include "gpgsm.h"
+
+
+#include <ksba.h>
+
+#include "i18n.h"
+
+#ifdef HAVE_DOSISH_SYSTEM
+ #define LF "\r\n"
+#else
+ #define LF "\n"
+#endif
+
+/* data used by the reader callbacks */
+struct reader_cb_parm_s {
+ FILE *fp;
+
+ unsigned char line[1024];
+ int linelen;
+ int readpos;
+ int have_lf;
+ unsigned long line_counter;
+
+ int allow_multi_pem; /* Allow processing of multiple PEM objects. */
+ int autodetect; /* Try to detect the input encoding. */
+ int assume_pem; /* Assume input encoding is PEM. */
+ int assume_base64; /* Assume input is base64 encoded. */
+
+ int identified;
+ int is_pem;
+ int is_base64;
+ int stop_seen;
+ int might_be_smime;
+
+ int eof_seen;
+
+ struct {
+ int idx;
+ unsigned char val;
+ int stop_seen;
+ } base64;
+};
+
+/* data used by the writer callbacks */
+struct writer_cb_parm_s {
+ FILE *fp; /* FP is only used if STREAM is NULL. */
+ estream_t stream; /* Alternative output if not NULL. */
+
+ const char *pem_name;
+
+ int wrote_begin;
+ int did_finish;
+
+ struct {
+ int idx;
+ int quad_count;
+ unsigned char radbuf[4];
+ } base64;
+
+};
+
+
+/* context for this module's functions */
+struct base64_context_s {
+ union {
+ struct reader_cb_parm_s rparm;
+ struct writer_cb_parm_s wparm;
+ } u;
+
+ union {
+ ksba_reader_t reader;
+ ksba_writer_t writer;
+ } u2;
+};
+
+
+/* The base-64 character list */
+static char bintoasc[64] =
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+ "abcdefghijklmnopqrstuvwxyz"
+ "0123456789+/";
+/* The reverse base-64 list */
+static unsigned char asctobin[256] = {
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3e, 0xff, 0xff, 0xff, 0x3f,
+ 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
+ 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12,
+ 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24,
+ 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30,
+ 0x31, 0x32, 0x33, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff
+};
+
+
+static int
+has_only_base64 (const unsigned char *line, int linelen)
+{
+ if (linelen < 20)
+ return 0;
+ for (; linelen; line++, linelen--)
+ {
+ if (*line == '\n' || (linelen > 1 && *line == '\r' && line[1] == '\n'))
+ break;
+ if ( !strchr (bintoasc, *line) )
+ return 0;
+ }
+ return 1; /* yes */
+}
+
+static int
+is_empty_line (const unsigned char *line, int linelen)
+{
+ if (linelen >= 2 && *line == '\r' && line[1] == '\n')
+ return 1;
+ if (linelen >= 1 && *line == '\n')
+ return 1;
+ return 0;
+}
+
+
+static int
+base64_reader_cb (void *cb_value, char *buffer, size_t count, size_t *nread)
+{
+ struct reader_cb_parm_s *parm = cb_value;
+ size_t n;
+ int c, c2;
+
+ *nread = 0;
+ if (!buffer)
+ return -1; /* not supported */
+
+ next:
+ if (!parm->linelen)
+ {
+ /* read an entire line or up to the size of the buffer */
+ parm->line_counter++;
+ parm->have_lf = 0;
+ for (n=0; n < DIM(parm->line);)
+ {
+ c = getc (parm->fp);
+ if (c == EOF)
+ {
+ parm->eof_seen = 1;
+ if (ferror (parm->fp))
+ return -1;
+ break;
+ }
+ parm->line[n++] = c;
+ if (c == '\n')
+ {
+ parm->have_lf = 1;
+ /* Fixme: we need to skip overlong lines while detecting
+ the dashed lines */
+ break;
+ }
+ }
+ parm->linelen = n;
+ if (!n)
+ return -1; /* eof */
+ parm->readpos = 0;
+ }
+
+ if (!parm->identified)
+ {
+ if (!parm->autodetect)
+ {
+ if (parm->assume_pem)
+ {
+ /* wait for the header line */
+ parm->linelen = parm->readpos = 0;
+ if (!parm->have_lf
+ || strncmp ((char*)parm->line, "-----BEGIN ", 11)
+ || !strncmp ((char*)parm->line+11, "PGP ", 4))
+ goto next;
+ parm->is_pem = 1;
+ }
+ else if (parm->assume_base64)
+ parm->is_base64 = 1;
+ }
+ else if (parm->line_counter == 1 && !parm->have_lf)
+ {
+ /* first line too long - assume DER encoding */
+ parm->is_pem = 0;
+ }
+ else if (parm->line_counter == 1 && parm->linelen && *parm->line == 0x30)
+ {
+ /* the very first byte does pretty much look like a SEQUENCE tag*/
+ parm->is_pem = 0;
+ }
+ else if ( parm->have_lf
+ && !strncmp ((char*)parm->line, "-----BEGIN ", 11)
+ && strncmp ((char *)parm->line+11, "PGP ", 4) )
+ {
+ /* Fixme: we must only compare if the line really starts at
+ the beginning */
+ parm->is_pem = 1;
+ parm->linelen = parm->readpos = 0;
+ }
+ else if ( parm->have_lf && parm->line_counter == 1
+ && parm->linelen >= 13
+ && !ascii_memcasecmp (parm->line, "Content-Type:", 13))
+ { /* might be a S/MIME body */
+ parm->might_be_smime = 1;
+ parm->linelen = parm->readpos = 0;
+ goto next;
+ }
+ else if (parm->might_be_smime == 1
+ && is_empty_line (parm->line, parm->linelen))
+ {
+ parm->might_be_smime = 2;
+ parm->linelen = parm->readpos = 0;
+ goto next;
+ }
+ else if (parm->might_be_smime == 2)
+ {
+ parm->might_be_smime = 0;
+ if ( !has_only_base64 (parm->line, parm->linelen))
+ {
+ parm->linelen = parm->readpos = 0;
+ goto next;
+ }
+ parm->is_pem = 1;
+ }
+ else
+ {
+ parm->linelen = parm->readpos = 0;
+ goto next;
+ }
+ parm->identified = 1;
+ parm->base64.stop_seen = 0;
+ parm->base64.idx = 0;
+ }
+
+
+ n = 0;
+ if (parm->is_pem || parm->is_base64)
+ {
+ if (parm->is_pem && parm->have_lf
+ && !strncmp ((char*)parm->line, "-----END ", 9))
+ {
+ parm->identified = 0;
+ parm->linelen = parm->readpos = 0;
+
+ /* If the caller want to read multiple PEM objects from one
+ file, we have to reset our internal state and return a
+ EOF immediately. The caller is the expected to use
+ ksba_reader_clear to clear the EOF condition and continue
+ to read. If we don't want to do that we just return 0
+ bytes which will force the ksba_reader to skip until
+ EOF. */
+ if (parm->allow_multi_pem)
+ {
+ parm->identified = 0;
+ parm->autodetect = 0;
+ parm->assume_pem = 1;
+ parm->stop_seen = 0;
+ return -1; /* Send EOF now. */
+ }
+ }
+ else if (parm->stop_seen)
+ { /* skip the rest of the line */
+ parm->linelen = parm->readpos = 0;
+ }
+ else
+ {
+ int idx = parm->base64.idx;
+ unsigned char val = parm->base64.val;
+
+ while (n < count && parm->readpos < parm->linelen )
+ {
+ c = parm->line[parm->readpos++];
+ if (c == '\n' || c == ' ' || c == '\r' || c == '\t')
+ continue;
+ if (c == '=')
+ { /* pad character: stop */
+ if (idx == 1)
+ buffer[n++] = val;
+ parm->stop_seen = 1;
+ break;
+ }
+ if( (c = asctobin[(c2=c)]) == 255 )
+ {
+ log_error (_("invalid radix64 character %02x skipped\n"),
+ c2);
+ continue;
+ }
+ switch (idx)
+ {
+ case 0:
+ val = c << 2;
+ break;
+ case 1:
+ val |= (c>>4)&3;
+ buffer[n++] = val;
+ val = (c<<4)&0xf0;
+ break;
+ case 2:
+ val |= (c>>2)&15;
+ buffer[n++] = val;
+ val = (c<<6)&0xc0;
+ break;
+ case 3:
+ val |= c&0x3f;
+ buffer[n++] = val;
+ break;
+ }
+ idx = (idx+1) % 4;
+ }
+ if (parm->readpos == parm->linelen)
+ parm->linelen = parm->readpos = 0;
+
+ parm->base64.idx = idx;
+ parm->base64.val = val;
+ }
+ }
+ else
+ { /* DER encoded */
+ while (n < count && parm->readpos < parm->linelen)
+ buffer[n++] = parm->line[parm->readpos++];
+ if (parm->readpos == parm->linelen)
+ parm->linelen = parm->readpos = 0;
+ }
+
+ *nread = n;
+ return 0;
+}
+
+
+
+static int
+simple_reader_cb (void *cb_value, char *buffer, size_t count, size_t *nread)
+{
+ struct reader_cb_parm_s *parm = cb_value;
+ size_t n;
+ int c = 0;
+
+ *nread = 0;
+ if (!buffer)
+ return -1; /* not supported */
+
+ for (n=0; n < count; n++)
+ {
+ c = getc (parm->fp);
+ if (c == EOF)
+ {
+ parm->eof_seen = 1;
+ if ( ferror (parm->fp) )
+ return -1;
+ if (n)
+ break; /* return what we have before an EOF */
+ return -1;
+ }
+ *(byte *)buffer++ = c;
+ }
+
+ *nread = n;
+ return 0;
+}
+
+
+
+
+/* Call either es_putc or the plain putc. */
+static void
+do_putc (int value, FILE *fp, estream_t stream)
+{
+ if (stream)
+ es_putc (value, stream);
+ else
+ putc (value, fp);
+}
+
+/* Call either es_fputs or the plain fputs. */
+static void
+do_fputs (const char *string, FILE *fp, estream_t stream)
+{
+ if (stream)
+ es_fputs (string, stream);
+ else
+ fputs (string, fp);
+}
+
+
+static int
+base64_writer_cb (void *cb_value, const void *buffer, size_t count)
+{
+ struct writer_cb_parm_s *parm = cb_value;
+ unsigned char radbuf[4];
+ int i, c, idx, quad_count;
+ const unsigned char *p;
+ FILE *fp = parm->fp;
+ estream_t stream = parm->stream;
+
+ if (!count)
+ return 0;
+
+ if (!parm->wrote_begin)
+ {
+ if (parm->pem_name)
+ {
+ do_fputs ("-----BEGIN ", fp, stream);
+ do_fputs (parm->pem_name, fp, stream);
+ do_fputs ("-----\n", fp, stream);
+ }
+ parm->wrote_begin = 1;
+ parm->base64.idx = 0;
+ parm->base64.quad_count = 0;
+ }
+
+ idx = parm->base64.idx;
+ quad_count = parm->base64.quad_count;
+ for (i=0; i < idx; i++)
+ radbuf[i] = parm->base64.radbuf[i];
+
+ for (p=buffer; count; p++, count--)
+ {
+ radbuf[idx++] = *p;
+ if (idx > 2)
+ {
+ idx = 0;
+ c = bintoasc[(*radbuf >> 2) & 077];
+ do_putc (c, fp, stream);
+ c = bintoasc[(((*radbuf<<4)&060)|((radbuf[1] >> 4)&017))&077];
+ do_putc (c, fp, stream);
+ c = bintoasc[(((radbuf[1]<<2)&074)|((radbuf[2]>>6)&03))&077];
+ do_putc (c, fp, stream);
+ c = bintoasc[radbuf[2]&077];
+ do_putc (c, fp, stream);
+ if (++quad_count >= (64/4))
+ {
+ do_fputs (LF, fp, stream);
+ quad_count = 0;
+ }
+ }
+ }
+ for (i=0; i < idx; i++)
+ parm->base64.radbuf[i] = radbuf[i];
+ parm->base64.idx = idx;
+ parm->base64.quad_count = quad_count;
+
+ return ((stream? es_ferror (stream) : ferror (fp))
+ ? gpg_error_from_syserror ()
+ : 0);
+}
+
+/* This callback is only used in stream mode. Hiowever, we don't
+ restrict it to this. */
+static int
+plain_writer_cb (void *cb_value, const void *buffer, size_t count)
+{
+ struct writer_cb_parm_s *parm = cb_value;
+ FILE *fp = parm->fp;
+ estream_t stream = parm->stream;
+
+ if (!count)
+ return 0;
+
+ if (stream)
+ es_write (stream, buffer, count, NULL);
+ else
+ fwrite (buffer, count, 1, fp);
+
+ return ((stream? es_ferror (stream) : ferror (fp))
+ ? gpg_error_from_syserror ()
+ : 0);
+}
+
+static int
+base64_finish_write (struct writer_cb_parm_s *parm)
+{
+ unsigned char radbuf[4];
+ int i, c, idx, quad_count;
+ FILE *fp = parm->fp;
+ estream_t stream = parm->stream;
+
+ if (!parm->wrote_begin)
+ return 0; /* Nothing written or we are not called in base-64 mode. */
+
+ /* flush the base64 encoding */
+ idx = parm->base64.idx;
+ quad_count = parm->base64.quad_count;
+ for (i=0; i < idx; i++)
+ radbuf[i] = parm->base64.radbuf[i];
+
+ if (idx)
+ {
+ c = bintoasc[(*radbuf>>2)&077];
+ do_putc (c, fp, stream);
+ if (idx == 1)
+ {
+ c = bintoasc[((*radbuf << 4) & 060) & 077];
+ do_putc (c, fp, stream);
+ do_putc ('=', fp, stream);
+ do_putc ('=', fp, stream);
+ }
+ else
+ {
+ c = bintoasc[(((*radbuf<<4)&060)|((radbuf[1]>>4)&017))&077];
+ do_putc (c, fp, stream);
+ c = bintoasc[((radbuf[1] << 2) & 074) & 077];
+ do_putc (c, fp, stream);
+ do_putc ('=', fp, stream);
+
+ }
+ if (++quad_count >= (64/4))
+ {
+ do_fputs (LF, fp, stream);
+ quad_count = 0;
+ }
+ }
+
+ if (quad_count)
+ do_fputs (LF, fp, stream);
+
+ if (parm->pem_name)
+ {
+ do_fputs ("-----END ", fp, stream);
+ do_fputs (parm->pem_name, fp, stream);
+ do_fputs ("-----\n", fp, stream);
+ }
+
+ return ((stream? es_ferror (stream) : ferror (fp))
+ ? gpg_error_from_syserror ()
+ : 0);
+}
+
+
+
+
+/* Create a reader for the given file descriptor. Depending on the
+ control information an input decoding is automagically choosen.
+ The function returns a Base64Context object which must be passed to
+ the gpgme_destroy_reader function. The created KsbaReader object
+ is also returned, but the caller must not call the
+ ksba_reader_release function on. If ALLOW_MULTI_PEM is true, the
+ reader expects that the caller uses ksba_reader_clear after EOF
+ until no more objects were found. */
+int
+gpgsm_create_reader (Base64Context *ctx,
+ ctrl_t ctrl, FILE *fp, int allow_multi_pem,
+ ksba_reader_t *r_reader)
+{
+ int rc;
+ ksba_reader_t r;
+
+ *r_reader = NULL;
+ *ctx = xtrycalloc (1, sizeof **ctx);
+ if (!*ctx)
+ return out_of_core ();
+ (*ctx)->u.rparm.allow_multi_pem = allow_multi_pem;
+
+ rc = ksba_reader_new (&r);
+ if (rc)
+ {
+ xfree (*ctx); *ctx = NULL;
+ return rc;
+ }
+
+ (*ctx)->u.rparm.fp = fp;
+ if (ctrl->is_pem)
+ {
+ (*ctx)->u.rparm.assume_pem = 1;
+ (*ctx)->u.rparm.assume_base64 = 1;
+ rc = ksba_reader_set_cb (r, base64_reader_cb, &(*ctx)->u.rparm);
+ }
+ else if (ctrl->is_base64)
+ {
+ (*ctx)->u.rparm.assume_base64 = 1;
+ rc = ksba_reader_set_cb (r, base64_reader_cb, &(*ctx)->u.rparm);
+ }
+ else if (ctrl->autodetect_encoding)
+ {
+ (*ctx)->u.rparm.autodetect = 1;
+ rc = ksba_reader_set_cb (r, base64_reader_cb, &(*ctx)->u.rparm);
+ }
+ else
+ rc = ksba_reader_set_cb (r, simple_reader_cb, &(*ctx)->u.rparm);
+
+ if (rc)
+ {
+ ksba_reader_release (r);
+ xfree (*ctx); *ctx = NULL;
+ return rc;
+ }
+
+ (*ctx)->u2.reader = r;
+ *r_reader = r;
+ return 0;
+}
+
+
+int
+gpgsm_reader_eof_seen (Base64Context ctx)
+{
+ return ctx && ctx->u.rparm.eof_seen;
+}
+
+void
+gpgsm_destroy_reader (Base64Context ctx)
+{
+ if (!ctx)
+ return;
+
+ ksba_reader_release (ctx->u2.reader);
+ xfree (ctx);
+}
+
+
+
+/* Create a writer for the given stream FP or STREAM. Depending on
+ the control information an output encoding is automagically
+ choosen. The function returns a Base64Context object which must be
+ passed to the gpgme_destroy_writer function. The created
+ KsbaWriter object is also returned, but the caller must not call
+ the ksba_reader_release function on. */
+int
+gpgsm_create_writer (Base64Context *ctx,
+ ctrl_t ctrl, FILE *fp, estream_t stream,
+ ksba_writer_t *r_writer)
+{
+ int rc;
+ ksba_writer_t w;
+
+ *r_writer = NULL;
+ *ctx = xtrycalloc (1, sizeof **ctx);
+ if (!*ctx)
+ return out_of_core ();
+
+ rc = ksba_writer_new (&w);
+ if (rc)
+ {
+ xfree (*ctx); *ctx = NULL;
+ return rc;
+ }
+
+ if (ctrl->create_pem || ctrl->create_base64)
+ {
+ (*ctx)->u.wparm.fp = fp;
+ (*ctx)->u.wparm.stream = stream;
+ if (ctrl->create_pem)
+ (*ctx)->u.wparm.pem_name = ctrl->pem_name? ctrl->pem_name
+ : "CMS OBJECT";
+ rc = ksba_writer_set_cb (w, base64_writer_cb, &(*ctx)->u.wparm);
+ }
+ else if (stream)
+ {
+ (*ctx)->u.wparm.fp = fp;
+ (*ctx)->u.wparm.stream = stream;
+ rc = ksba_writer_set_cb (w, plain_writer_cb, &(*ctx)->u.wparm);
+ }
+ else
+ rc = ksba_writer_set_file (w, fp);
+
+ if (rc)
+ {
+ ksba_writer_release (w);
+ xfree (*ctx); *ctx = NULL;
+ return rc;
+ }
+
+ (*ctx)->u2.writer = w;
+ *r_writer = w;
+ return 0;
+}
+
+
+int
+gpgsm_finish_writer (Base64Context ctx)
+{
+ struct writer_cb_parm_s *parm;
+
+ if (!ctx)
+ return gpg_error (GPG_ERR_INV_VALUE);
+ parm = &ctx->u.wparm;
+ if (parm->did_finish)
+ return 0; /* Already done. */
+ parm->did_finish = 1;
+ if (!parm->fp && !parm->stream)
+ return 0; /* Callback was not used. */
+ return base64_finish_write (parm);
+}
+
+void
+gpgsm_destroy_writer (Base64Context ctx)
+{
+ if (!ctx)
+ return;
+
+ ksba_writer_release (ctx->u2.writer);
+ xfree (ctx);
+}
diff --git a/sm/call-agent.c b/sm/call-agent.c
new file mode 100644
index 0000000..7eb16ed
--- /dev/null
+++ b/sm/call-agent.c
@@ -0,0 +1,1069 @@
+/* call-agent.c - Divert GPGSM operations to the agent
+ * Copyright (C) 2001, 2002, 2003, 2005, 2007,
+ * 2008, 2009 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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuPG is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <time.h>
+#include <assert.h>
+#ifdef HAVE_LOCALE_H
+#include <locale.h>
+#endif
+
+#include "gpgsm.h"
+#include <gcrypt.h>
+#include <assuan.h>
+#include "i18n.h"
+#include "asshelp.h"
+#include "keydb.h" /* fixme: Move this to import.c */
+#include "membuf.h"
+
+
+static assuan_context_t agent_ctx = NULL;
+
+
+struct cipher_parm_s
+{
+ ctrl_t ctrl;
+ assuan_context_t ctx;
+ const unsigned char *ciphertext;
+ size_t ciphertextlen;
+};
+
+struct genkey_parm_s
+{
+ ctrl_t ctrl;
+ assuan_context_t ctx;
+ const unsigned char *sexp;
+ size_t sexplen;
+};
+
+struct learn_parm_s
+{
+ int error;
+ ctrl_t ctrl;
+ assuan_context_t ctx;
+ membuf_t *data;
+};
+
+
+
+/* Try to connect to the agent via socket or fork it off and work by
+ pipes. Handle the server's initial greeting */
+static int
+start_agent (ctrl_t ctrl)
+{
+ int rc;
+
+ if (agent_ctx)
+ rc = 0; /* fixme: We need a context for each thread or
+ serialize the access to the agent (which is
+ suitable given that the agent is not MT. */
+ else
+ {
+ rc = start_new_gpg_agent (&agent_ctx,
+ GPG_ERR_SOURCE_DEFAULT,
+ opt.homedir,
+ opt.agent_program,
+ opt.lc_ctype, opt.lc_messages,
+ opt.session_env,
+ opt.verbose, DBG_ASSUAN,
+ gpgsm_status2, ctrl);
+
+ if (!rc)
+ {
+ /* Tell the agent that we support Pinentry notifications. No
+ error checking so that it will work also with older
+ agents. */
+ assuan_transact (agent_ctx, "OPTION allow-pinentry-notify",
+ NULL, NULL, NULL, NULL, NULL, NULL);
+ }
+ }
+
+ if (!ctrl->agent_seen)
+ {
+ ctrl->agent_seen = 1;
+ audit_log_ok (ctrl->audit, AUDIT_AGENT_READY, rc);
+ }
+
+ return rc;
+}
+
+
+
+static gpg_error_t
+membuf_data_cb (void *opaque, const void *buffer, size_t length)
+{
+ membuf_t *data = opaque;
+
+ if (buffer)
+ put_membuf (data, buffer, length);
+ return 0;
+}
+
+
+/* This is the default inquiry callback. It mainly handles the
+ Pinentry notifications. */
+static gpg_error_t
+default_inq_cb (void *opaque, const char *line)
+{
+ gpg_error_t err;
+ ctrl_t ctrl = opaque;
+
+ if (!strncmp (line, "PINENTRY_LAUNCHED", 17) && (line[17]==' '||!line[17]))
+ {
+ err = gpgsm_proxy_pinentry_notify (ctrl, line);
+ if (err)
+ log_error (_("failed to proxy %s inquiry to client\n"),
+ "PINENTRY_LAUNCHED");
+ /* We do not pass errors to avoid breaking other code. */
+ }
+ else
+ log_error ("ignoring gpg-agent inquiry `%s'\n", line);
+
+ return 0;
+}
+
+
+
+
+/* Call the agent to do a sign operation using the key identified by
+ the hex string KEYGRIP. */
+int
+gpgsm_agent_pksign (ctrl_t ctrl, const char *keygrip, const char *desc,
+ unsigned char *digest, size_t digestlen, int digestalgo,
+ unsigned char **r_buf, size_t *r_buflen )
+{
+ int rc, i;
+ char *p, line[ASSUAN_LINELENGTH];
+ membuf_t data;
+ size_t len;
+
+ *r_buf = NULL;
+ rc = start_agent (ctrl);
+ if (rc)
+ return rc;
+
+ if (digestlen*2 + 50 > DIM(line))
+ return gpg_error (GPG_ERR_GENERAL);
+
+ rc = assuan_transact (agent_ctx, "RESET", NULL, NULL, NULL, NULL, NULL, NULL);
+ if (rc)
+ return rc;
+
+ snprintf (line, DIM(line)-1, "SIGKEY %s", keygrip);
+ line[DIM(line)-1] = 0;
+ rc = assuan_transact (agent_ctx, line, NULL, NULL, NULL, NULL, NULL, NULL);
+ if (rc)
+ return rc;
+
+ if (desc)
+ {
+ snprintf (line, DIM(line)-1, "SETKEYDESC %s", desc);
+ line[DIM(line)-1] = 0;
+ rc = assuan_transact (agent_ctx, line,
+ NULL, NULL, NULL, NULL, NULL, NULL);
+ if (rc)
+ return rc;
+ }
+
+ sprintf (line, "SETHASH %d ", digestalgo);
+ p = line + strlen (line);
+ for (i=0; i < digestlen ; i++, p += 2 )
+ sprintf (p, "%02X", digest[i]);
+ rc = assuan_transact (agent_ctx, line, NULL, NULL, NULL, NULL, NULL, NULL);
+ if (rc)
+ return rc;
+
+ init_membuf (&data, 1024);
+ rc = assuan_transact (agent_ctx, "PKSIGN",
+ membuf_data_cb, &data, default_inq_cb, ctrl,
+ NULL, NULL);
+ if (rc)
+ {
+ xfree (get_membuf (&data, &len));
+ return rc;
+ }
+ *r_buf = get_membuf (&data, r_buflen);
+
+ if (!gcry_sexp_canon_len (*r_buf, *r_buflen, NULL, NULL))
+ {
+ xfree (*r_buf); *r_buf = NULL;
+ return gpg_error (GPG_ERR_INV_VALUE);
+ }
+
+ return *r_buf? 0 : out_of_core ();
+}
+
+
+/* Call the scdaemon to do a sign operation using the key identified by
+ the hex string KEYID. */
+int
+gpgsm_scd_pksign (ctrl_t ctrl, const char *keyid, const char *desc,
+ unsigned char *digest, size_t digestlen, int digestalgo,
+ unsigned char **r_buf, size_t *r_buflen )
+{
+ int rc, i;
+ char *p, line[ASSUAN_LINELENGTH];
+ membuf_t data;
+ size_t len;
+ const char *hashopt;
+ unsigned char *sigbuf;
+ size_t sigbuflen;
+
+ (void)desc;
+
+ *r_buf = NULL;
+
+ switch(digestalgo)
+ {
+ case GCRY_MD_SHA1: hashopt = "--hash=sha1"; break;
+ case GCRY_MD_RMD160:hashopt = "--hash=rmd160"; break;
+ case GCRY_MD_MD5: hashopt = "--hash=md5"; break;
+ case GCRY_MD_SHA256:hashopt = "--hash=sha256"; break;
+ default:
+ return gpg_error (GPG_ERR_DIGEST_ALGO);
+ }
+
+ rc = start_agent (ctrl);
+ if (rc)
+ return rc;
+
+ if (digestlen*2 + 50 > DIM(line))
+ return gpg_error (GPG_ERR_GENERAL);
+
+ p = stpcpy (line, "SCD SETDATA " );
+ for (i=0; i < digestlen ; i++, p += 2 )
+ sprintf (p, "%02X", digest[i]);
+ rc = assuan_transact (agent_ctx, line, NULL, NULL, NULL, NULL, NULL, NULL);
+ if (rc)
+ return rc;
+
+ init_membuf (&data, 1024);
+
+ snprintf (line, DIM(line)-1, "SCD PKSIGN %s %s", hashopt, keyid);
+ line[DIM(line)-1] = 0;
+ rc = assuan_transact (agent_ctx, line,
+ membuf_data_cb, &data, default_inq_cb, ctrl,
+ NULL, NULL);
+ if (rc)
+ {
+ xfree (get_membuf (&data, &len));
+ return rc;
+ }
+ sigbuf = get_membuf (&data, &sigbuflen);
+
+ /* Create an S-expression from it which is formatted like this:
+ "(7:sig-val(3:rsa(1:sSIGBUFLEN:SIGBUF)))" Fixme: If a card ever
+ creates non-RSA keys we need to change things. */
+ *r_buflen = 21 + 11 + sigbuflen + 4;
+ p = xtrymalloc (*r_buflen);
+ *r_buf = (unsigned char*)p;
+ if (!p)
+ {
+ xfree (sigbuf);
+ return 0;
+ }
+ p = stpcpy (p, "(7:sig-val(3:rsa(1:s" );
+ sprintf (p, "%u:", (unsigned int)sigbuflen);
+ p += strlen (p);
+ memcpy (p, sigbuf, sigbuflen);
+ p += sigbuflen;
+ strcpy (p, ")))");
+ xfree (sigbuf);
+
+ assert (gcry_sexp_canon_len (*r_buf, *r_buflen, NULL, NULL));
+ return 0;
+}
+
+
+
+
+/* Handle a CIPHERTEXT inquiry. Note, we only send the data,
+ assuan_transact talkes care of flushing and writing the end */
+static gpg_error_t
+inq_ciphertext_cb (void *opaque, const char *line)
+{
+ struct cipher_parm_s *parm = opaque;
+ int rc;
+
+ if (!strncmp (line, "CIPHERTEXT", 10) && (line[10]==' '||!line[10]))
+ {
+ assuan_begin_confidential (parm->ctx);
+ rc = assuan_send_data (parm->ctx, parm->ciphertext, parm->ciphertextlen);
+ assuan_end_confidential (parm->ctx);
+ }
+ else
+ rc = default_inq_cb (parm->ctrl, line);
+
+ return rc;
+}
+
+
+/* Call the agent to do a decrypt operation using the key identified by
+ the hex string KEYGRIP. */
+int
+gpgsm_agent_pkdecrypt (ctrl_t ctrl, const char *keygrip, const char *desc,
+ ksba_const_sexp_t ciphertext,
+ char **r_buf, size_t *r_buflen )
+{
+ int rc;
+ char line[ASSUAN_LINELENGTH];
+ membuf_t data;
+ struct cipher_parm_s cipher_parm;
+ size_t n, len;
+ char *p, *buf, *endp;
+ size_t ciphertextlen;
+
+ if (!keygrip || strlen(keygrip) != 40 || !ciphertext || !r_buf || !r_buflen)
+ return gpg_error (GPG_ERR_INV_VALUE);
+ *r_buf = NULL;
+
+ ciphertextlen = gcry_sexp_canon_len (ciphertext, 0, NULL, NULL);
+ if (!ciphertextlen)
+ return gpg_error (GPG_ERR_INV_VALUE);
+
+ rc = start_agent (ctrl);
+ if (rc)
+ return rc;
+
+ rc = assuan_transact (agent_ctx, "RESET", NULL, NULL, NULL, NULL, NULL, NULL);
+ if (rc)
+ return rc;
+
+ assert ( DIM(line) >= 50 );
+ snprintf (line, DIM(line)-1, "SETKEY %s", keygrip);
+ line[DIM(line)-1] = 0;
+ rc = assuan_transact (agent_ctx, line, NULL, NULL, NULL, NULL, NULL, NULL);
+ if (rc)
+ return rc;
+
+ if (desc)
+ {
+ snprintf (line, DIM(line)-1, "SETKEYDESC %s", desc);
+ line[DIM(line)-1] = 0;
+ rc = assuan_transact (agent_ctx, line,
+ NULL, NULL, NULL, NULL, NULL, NULL);
+ if (rc)
+ return rc;
+ }
+
+ init_membuf (&data, 1024);
+ cipher_parm.ctrl = ctrl;
+ cipher_parm.ctx = agent_ctx;
+ cipher_parm.ciphertext = ciphertext;
+ cipher_parm.ciphertextlen = ciphertextlen;
+ rc = assuan_transact (agent_ctx, "PKDECRYPT",
+ membuf_data_cb, &data,
+ inq_ciphertext_cb, &cipher_parm, NULL, NULL);
+ if (rc)
+ {
+ xfree (get_membuf (&data, &len));
+ return rc;
+ }
+
+ put_membuf (&data, "", 1); /* Make sure it is 0 terminated. */
+ buf = get_membuf (&data, &len);
+ if (!buf)
+ return gpg_error (GPG_ERR_ENOMEM);
+ assert (len); /* (we forced Nul termination.) */
+
+ if (*buf == '(')
+ {
+ if (len < 13 || memcmp (buf, "(5:value", 8) ) /* "(5:valueN:D)\0" */
+ return gpg_error (GPG_ERR_INV_SEXP);
+ len -= 11; /* Count only the data of the second part. */
+ p = buf + 8; /* Skip leading parenthesis and the value tag. */
+ }
+ else
+ {
+ /* For compatibility with older gpg-agents handle the old style
+ incomplete S-exps. */
+ len--; /* Do not count the Nul. */
+ p = buf;
+ }
+
+ n = strtoul (p, &endp, 10);
+ if (!n || *endp != ':')
+ return gpg_error (GPG_ERR_INV_SEXP);
+ endp++;
+ if (endp-p+n > len)
+ return gpg_error (GPG_ERR_INV_SEXP); /* Oops: Inconsistent S-Exp. */
+
+ memmove (buf, endp, n);
+
+ *r_buflen = n;
+ *r_buf = buf;
+ return 0;
+}
+
+
+
+
+
+/* Handle a KEYPARMS inquiry. Note, we only send the data,
+ assuan_transact takes care of flushing and writing the end */
+static gpg_error_t
+inq_genkey_parms (void *opaque, const char *line)
+{
+ struct genkey_parm_s *parm = opaque;
+ int rc;
+
+ if (!strncmp (line, "KEYPARAM", 8) && (line[8]==' '||!line[8]))
+ {
+ rc = assuan_send_data (parm->ctx, parm->sexp, parm->sexplen);
+ }
+ else
+ rc = default_inq_cb (parm->ctrl, line);
+
+ return rc;
+}
+
+
+
+/* Call the agent to generate a newkey */
+int
+gpgsm_agent_genkey (ctrl_t ctrl,
+ ksba_const_sexp_t keyparms, ksba_sexp_t *r_pubkey)
+{
+ int rc;
+ struct genkey_parm_s gk_parm;
+ membuf_t data;
+ size_t len;
+ unsigned char *buf;
+
+ *r_pubkey = NULL;
+ rc = start_agent (ctrl);
+ if (rc)
+ return rc;
+
+ rc = assuan_transact (agent_ctx, "RESET", NULL, NULL, NULL, NULL, NULL, NULL);
+ if (rc)
+ return rc;
+
+ init_membuf (&data, 1024);
+ gk_parm.ctrl = ctrl;
+ gk_parm.ctx = agent_ctx;
+ gk_parm.sexp = keyparms;
+ gk_parm.sexplen = gcry_sexp_canon_len (keyparms, 0, NULL, NULL);
+ if (!gk_parm.sexplen)
+ return gpg_error (GPG_ERR_INV_VALUE);
+ rc = assuan_transact (agent_ctx, "GENKEY",
+ membuf_data_cb, &data,
+ inq_genkey_parms, &gk_parm, NULL, NULL);
+ if (rc)
+ {
+ xfree (get_membuf (&data, &len));
+ return rc;
+ }
+ buf = get_membuf (&data, &len);
+ if (!buf)
+ return gpg_error (GPG_ERR_ENOMEM);
+ if (!gcry_sexp_canon_len (buf, len, NULL, NULL))
+ {
+ xfree (buf);
+ return gpg_error (GPG_ERR_INV_SEXP);
+ }
+ *r_pubkey = buf;
+ return 0;
+}
+
+
+/* Call the agent to read the public key part for a given keygrip. If
+ FROMCARD is true, the key is directly read from the current
+ smartcard. In this case HEXKEYGRIP should be the keyID
+ (e.g. OPENPGP.3). */
+int
+gpgsm_agent_readkey (ctrl_t ctrl, int fromcard, const char *hexkeygrip,
+ ksba_sexp_t *r_pubkey)
+{
+ int rc;
+ membuf_t data;
+ size_t len;
+ unsigned char *buf;
+ char line[ASSUAN_LINELENGTH];
+
+ *r_pubkey = NULL;
+ rc = start_agent (ctrl);
+ if (rc)
+ return rc;
+
+ rc = assuan_transact (agent_ctx, "RESET",NULL, NULL, NULL, NULL, NULL, NULL);
+ if (rc)
+ return rc;
+
+ snprintf (line, DIM(line)-1, "%sREADKEY %s",
+ fromcard? "SCD ":"", hexkeygrip);
+ line[DIM(line)-1] = 0;
+
+ init_membuf (&data, 1024);
+ rc = assuan_transact (agent_ctx, line,
+ membuf_data_cb, &data,
+ default_inq_cb, ctrl, NULL, NULL);
+ if (rc)
+ {
+ xfree (get_membuf (&data, &len));
+ return rc;
+ }
+ buf = get_membuf (&data, &len);
+ if (!buf)
+ return gpg_error (GPG_ERR_ENOMEM);
+ if (!gcry_sexp_canon_len (buf, len, NULL, NULL))
+ {
+ xfree (buf);
+ return gpg_error (GPG_ERR_INV_SEXP);
+ }
+ *r_pubkey = buf;
+ return 0;
+}
+
+
+
+/* Take the serial number from LINE and return it verbatim in a newly
+ allocated string. We make sure that only hex characters are
+ returned. */
+static char *
+store_serialno (const char *line)
+{
+ const char *s;
+ char *p;
+
+ for (s=line; hexdigitp (s); s++)
+ ;
+ p = xtrymalloc (s + 1 - line);
+ if (p)
+ {
+ memcpy (p, line, s-line);
+ p[s-line] = 0;
+ }
+ return p;
+}
+
+
+/* Callback for the gpgsm_agent_serialno fucntion. */
+static gpg_error_t
+scd_serialno_status_cb (void *opaque, const char *line)
+{
+ char **r_serialno = opaque;
+ const char *keyword = line;
+ int keywordlen;
+
+ for (keywordlen=0; *line && !spacep (line); line++, keywordlen++)
+ ;
+ while (spacep (line))
+ line++;
+
+ if (keywordlen == 8 && !memcmp (keyword, "SERIALNO", keywordlen))
+ {
+ xfree (*r_serialno);
+ *r_serialno = store_serialno (line);
+ }
+
+ return 0;
+}
+
+
+/* Call the agent to read the serial number of the current card. */
+int
+gpgsm_agent_scd_serialno (ctrl_t ctrl, char **r_serialno)
+{
+ int rc;
+ char *serialno = NULL;
+
+ *r_serialno = NULL;
+ rc = start_agent (ctrl);
+ if (rc)
+ return rc;
+
+ rc = assuan_transact (agent_ctx, "SCD SERIALNO",
+ NULL, NULL,
+ default_inq_cb, ctrl,
+ scd_serialno_status_cb, &serialno);
+ if (!rc && !serialno)
+ rc = gpg_error (GPG_ERR_INTERNAL);
+ if (rc)
+ {
+ xfree (serialno);
+ return rc;
+ }
+ *r_serialno = serialno;
+ return 0;
+}
+
+
+
+/* Callback for the gpgsm_agent_serialno fucntion. */
+static gpg_error_t
+scd_keypairinfo_status_cb (void *opaque, const char *line)
+{
+ strlist_t *listaddr = opaque;
+ const char *keyword = line;
+ int keywordlen;
+ strlist_t sl;
+ char *p;
+
+ for (keywordlen=0; *line && !spacep (line); line++, keywordlen++)
+ ;
+ while (spacep (line))
+ line++;
+
+ if (keywordlen == 11 && !memcmp (keyword, "KEYPAIRINFO", keywordlen))
+ {
+ sl = append_to_strlist (listaddr, line);
+ p = sl->d;
+ /* Make sure that we only have two tokes so that future
+ extensions of the format won't change the format expected by
+ the caller. */
+ while (*p && !spacep (p))
+ p++;
+ if (*p)
+ {
+ while (spacep (p))
+ p++;
+ while (*p && !spacep (p))
+ p++;
+ *p = 0;
+ }
+ }
+
+ return 0;
+}
+
+
+/* Call the agent to read the keypairinfo lines of the current card.
+ The list is returned as a string made up of the keygrip, a space
+ and the keyid. */
+int
+gpgsm_agent_scd_keypairinfo (ctrl_t ctrl, strlist_t *r_list)
+{
+ int rc;
+ strlist_t list = NULL;
+
+ *r_list = NULL;
+ rc = start_agent (ctrl);
+ if (rc)
+ return rc;
+
+ rc = assuan_transact (agent_ctx, "SCD LEARN --force",
+ NULL, NULL,
+ default_inq_cb, ctrl,
+ scd_keypairinfo_status_cb, &list);
+ if (!rc && !list)
+ rc = gpg_error (GPG_ERR_NO_DATA);
+ if (rc)
+ {
+ free_strlist (list);
+ return rc;
+ }
+ *r_list = list;
+ return 0;
+}
+
+
+
+static gpg_error_t
+istrusted_status_cb (void *opaque, const char *line)
+{
+ struct rootca_flags_s *flags = opaque;
+
+ if (!strncmp (line, "TRUSTLISTFLAG", 13) && (line[13]==' ' || !line[13]))
+ {
+ for (line += 13; *line == ' '; line++)
+ ;
+ if (!strncmp (line, "relax", 5) && (line[5] == ' ' || !line[5]))
+ flags->relax = 1;
+ else if (!strncmp (line, "cm", 2) && (line[2] == ' ' || !line[2]))
+ flags->chain_model = 1;
+ }
+ return 0;
+}
+
+
+
+/* Ask the agent whether the certificate is in the list of trusted
+ keys. The certificate is either specified by the CERT object or by
+ the fingerprint HEXFPR. ROOTCA_FLAGS is guaranteed to be cleared
+ on error. */
+int
+gpgsm_agent_istrusted (ctrl_t ctrl, ksba_cert_t cert, const char *hexfpr,
+ struct rootca_flags_s *rootca_flags)
+{
+ int rc;
+ char line[ASSUAN_LINELENGTH];
+
+ memset (rootca_flags, 0, sizeof *rootca_flags);
+
+ if (cert && hexfpr)
+ return gpg_error (GPG_ERR_INV_ARG);
+
+ rc = start_agent (ctrl);
+ if (rc)
+ return rc;
+
+ if (hexfpr)
+ {
+ snprintf (line, DIM(line)-1, "ISTRUSTED %s", hexfpr);
+ line[DIM(line)-1] = 0;
+ }
+ else
+ {
+ char *fpr;
+
+ fpr = gpgsm_get_fingerprint_hexstring (cert, GCRY_MD_SHA1);
+ if (!fpr)
+ {
+ log_error ("error getting the fingerprint\n");
+ return gpg_error (GPG_ERR_GENERAL);
+ }
+
+ snprintf (line, DIM(line)-1, "ISTRUSTED %s", fpr);
+ line[DIM(line)-1] = 0;
+ xfree (fpr);
+ }
+
+ rc = assuan_transact (agent_ctx, line, NULL, NULL, NULL, NULL,
+ istrusted_status_cb, rootca_flags);
+ if (!rc)
+ rootca_flags->valid = 1;
+ return rc;
+}
+
+/* Ask the agent to mark CERT as a trusted Root-CA one */
+int
+gpgsm_agent_marktrusted (ctrl_t ctrl, ksba_cert_t cert)
+{
+ int rc;
+ char *fpr, *dn, *dnfmt;
+ char line[ASSUAN_LINELENGTH];
+
+ rc = start_agent (ctrl);
+ if (rc)
+ return rc;
+
+ fpr = gpgsm_get_fingerprint_hexstring (cert, GCRY_MD_SHA1);
+ if (!fpr)
+ {
+ log_error ("error getting the fingerprint\n");
+ return gpg_error (GPG_ERR_GENERAL);
+ }
+
+ dn = ksba_cert_get_issuer (cert, 0);
+ if (!dn)
+ {
+ xfree (fpr);
+ return gpg_error (GPG_ERR_GENERAL);
+ }
+ dnfmt = gpgsm_format_name2 (dn, 0);
+ xfree (dn);
+ if (!dnfmt)
+ return gpg_error_from_syserror ();
+ snprintf (line, DIM(line)-1, "MARKTRUSTED %s S %s", fpr, dnfmt);
+ line[DIM(line)-1] = 0;
+ ksba_free (dnfmt);
+ xfree (fpr);
+
+ rc = assuan_transact (agent_ctx, line, NULL, NULL,
+ default_inq_cb, ctrl, NULL, NULL);
+ return rc;
+}
+
+
+
+/* Ask the agent whether the a corresponding secret key is available
+ for the given keygrip */
+int
+gpgsm_agent_havekey (ctrl_t ctrl, const char *hexkeygrip)
+{
+ int rc;
+ char line[ASSUAN_LINELENGTH];
+
+ rc = start_agent (ctrl);
+ if (rc)
+ return rc;
+
+ if (!hexkeygrip || strlen (hexkeygrip) != 40)
+ return gpg_error (GPG_ERR_INV_VALUE);
+
+ snprintf (line, DIM(line)-1, "HAVEKEY %s", hexkeygrip);
+ line[DIM(line)-1] = 0;
+
+ rc = assuan_transact (agent_ctx, line, NULL, NULL, NULL, NULL, NULL, NULL);
+ return rc;
+}
+
+
+static gpg_error_t
+learn_status_cb (void *opaque, const char *line)
+{
+ struct learn_parm_s *parm = opaque;
+
+ /* Pass progress data to the caller. */
+ if (!strncmp (line, "PROGRESS", 8) && (line[8]==' ' || !line[8]))
+ {
+ if (parm->ctrl)
+ {
+ for (line += 8; *line == ' '; line++)
+ ;
+ if (gpgsm_status (parm->ctrl, STATUS_PROGRESS, line))
+ return gpg_error (GPG_ERR_ASS_CANCELED);
+ }
+ }
+ return 0;
+}
+
+static gpg_error_t
+learn_cb (void *opaque, const void *buffer, size_t length)
+{
+ struct learn_parm_s *parm = opaque;
+ size_t len;
+ char *buf;
+ ksba_cert_t cert;
+ int rc;
+
+ if (parm->error)
+ return 0;
+
+ if (buffer)
+ {
+ put_membuf (parm->data, buffer, length);
+ return 0;
+ }
+ /* END encountered - process what we have */
+ buf = get_membuf (parm->data, &len);
+ if (!buf)
+ {
+ parm->error = gpg_error (GPG_ERR_ENOMEM);
+ return 0;
+ }
+
+ if (gpgsm_status (parm->ctrl, STATUS_PROGRESS, "learncard C 0 0"))
+ return gpg_error (GPG_ERR_ASS_CANCELED);
+
+ /* FIXME: this should go into import.c */
+ rc = ksba_cert_new (&cert);
+ if (rc)
+ {
+ parm->error = rc;
+ return 0;
+ }
+ rc = ksba_cert_init_from_mem (cert, buf, len);
+ if (rc)
+ {
+ log_error ("failed to parse a certificate: %s\n", gpg_strerror (rc));
+ ksba_cert_release (cert);
+ parm->error = rc;
+ return 0;
+ }
+
+ /* We do not store a certifciate with missing issuers as ephemeral
+ because we can assume that the --learn-card command has been used
+ on purpose. */
+ rc = gpgsm_basic_cert_check (parm->ctrl, cert);
+ if (rc && gpg_err_code (rc) != GPG_ERR_MISSING_CERT
+ && gpg_err_code (rc) != GPG_ERR_MISSING_ISSUER_CERT)
+ log_error ("invalid certificate: %s\n", gpg_strerror (rc));
+ else
+ {
+ int existed;
+
+ if (!keydb_store_cert (cert, 0, &existed))
+ {
+ if (opt.verbose > 1 && existed)
+ log_info ("certificate already in DB\n");
+ else if (opt.verbose && !existed)
+ log_info ("certificate imported\n");
+ }
+ }
+
+ ksba_cert_release (cert);
+ init_membuf (parm->data, 4096);
+ return 0;
+}
+
+/* Call the agent to learn about a smartcard */
+int
+gpgsm_agent_learn (ctrl_t ctrl)
+{
+ int rc;
+ struct learn_parm_s learn_parm;
+ membuf_t data;
+ size_t len;
+
+ rc = start_agent (ctrl);
+ if (rc)
+ return rc;
+
+ init_membuf (&data, 4096);
+ learn_parm.error = 0;
+ learn_parm.ctrl = ctrl;
+ learn_parm.ctx = agent_ctx;
+ learn_parm.data = &data;
+ rc = assuan_transact (agent_ctx, "LEARN --send",
+ learn_cb, &learn_parm,
+ NULL, NULL,
+ learn_status_cb, &learn_parm);
+ xfree (get_membuf (&data, &len));
+ if (rc)
+ return rc;
+ return learn_parm.error;
+}
+
+
+/* Ask the agent to change the passphrase of the key identified by
+ HEXKEYGRIP. If DESC is not NULL, display instead of the default
+ description message. */
+int
+gpgsm_agent_passwd (ctrl_t ctrl, const char *hexkeygrip, const char *desc)
+{
+ int rc;
+ char line[ASSUAN_LINELENGTH];
+
+ rc = start_agent (ctrl);
+ if (rc)
+ return rc;
+
+ if (!hexkeygrip || strlen (hexkeygrip) != 40)
+ return gpg_error (GPG_ERR_INV_VALUE);
+
+ if (desc)
+ {
+ snprintf (line, DIM(line)-1, "SETKEYDESC %s", desc);
+ line[DIM(line)-1] = 0;
+ rc = assuan_transact (agent_ctx, line,
+ NULL, NULL, NULL, NULL, NULL, NULL);
+ if (rc)
+ return rc;
+ }
+
+ snprintf (line, DIM(line)-1, "PASSWD %s", hexkeygrip);
+ line[DIM(line)-1] = 0;
+
+ rc = assuan_transact (agent_ctx, line, NULL, NULL,
+ default_inq_cb, ctrl, NULL, NULL);
+ return rc;
+}
+
+
+
+/* Ask the agent to pop up a confirmation dialog with the text DESC
+ and an okay and cancel button. */
+gpg_error_t
+gpgsm_agent_get_confirmation (ctrl_t ctrl, const char *desc)
+{
+ int rc;
+ char line[ASSUAN_LINELENGTH];
+
+ rc = start_agent (ctrl);
+ if (rc)
+ return rc;
+
+ snprintf (line, DIM(line)-1, "GET_CONFIRMATION %s", desc);
+ line[DIM(line)-1] = 0;
+
+ rc = assuan_transact (agent_ctx, line, NULL, NULL,
+ default_inq_cb, ctrl, NULL, NULL);
+ return rc;
+}
+
+
+
+/* Return 0 if the agent is alive. This is useful to make sure that
+ an agent has been started. */
+gpg_error_t
+gpgsm_agent_send_nop (ctrl_t ctrl)
+{
+ int rc;
+
+ rc = start_agent (ctrl);
+ if (!rc)
+ rc = assuan_transact (agent_ctx, "NOP",
+ NULL, NULL, NULL, NULL, NULL, NULL);
+ return rc;
+}
+
+
+
+static gpg_error_t
+keyinfo_status_cb (void *opaque, const char *line)
+{
+ char **serialno = opaque;
+ const char *s, *s2;
+
+ if (!strncmp (line, "KEYINFO ", 8) && !*serialno)
+ {
+ s = strchr (line+8, ' ');
+ if (s && s[1] == 'T' && s[2] == ' ' && s[3])
+ {
+ s += 3;
+ s2 = strchr (s, ' ');
+ if ( s2 > s )
+ {
+ *serialno = xtrymalloc ((s2 - s)+1);
+ if (*serialno)
+ {
+ memcpy (*serialno, s, s2 - s);
+ (*serialno)[s2 - s] = 0;
+ }
+ }
+ }
+ }
+ return 0;
+}
+
+/* Return the serial number for a secret key. If the returned serial
+ number is NULL, the key is not stored on a smartcard. Caller needs
+ to free R_SERIALNO. */
+gpg_error_t
+gpgsm_agent_keyinfo (ctrl_t ctrl, const char *hexkeygrip, char **r_serialno)
+{
+ gpg_error_t err;
+ char line[ASSUAN_LINELENGTH];
+ char *serialno = NULL;
+
+ *r_serialno = NULL;
+
+ err = start_agent (ctrl);
+ if (err)
+ return err;
+
+ if (!hexkeygrip || strlen (hexkeygrip) != 40)
+ return gpg_error (GPG_ERR_INV_VALUE);
+
+ snprintf (line, DIM(line)-1, "KEYINFO %s", hexkeygrip);
+ line[DIM(line)-1] = 0;
+
+ err = assuan_transact (agent_ctx, line, NULL, NULL, NULL, NULL,
+ keyinfo_status_cb, &serialno);
+ if (!err && serialno)
+ {
+ /* Sanity check for bad characters. */
+ if (strpbrk (serialno, ":\n\r"))
+ err = GPG_ERR_INV_VALUE;
+ }
+ if (err)
+ xfree (serialno);
+ else
+ *r_serialno = serialno;
+ return err;
+}
+
diff --git a/sm/call-dirmngr.c b/sm/call-dirmngr.c
new file mode 100644
index 0000000..6540a8f
--- /dev/null
+++ b/sm/call-dirmngr.c
@@ -0,0 +1,1126 @@
+/* call-dirmngr.c - communication with the dromngr
+ * Copyright (C) 2002, 2003, 2005, 2007, 2008 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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuPG is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <time.h>
+#include <assert.h>
+#include <ctype.h>
+
+#include "gpgsm.h"
+#include <gcrypt.h>
+#include <assuan.h>
+
+#include "i18n.h"
+#include "keydb.h"
+
+
+struct membuf {
+ size_t len;
+ size_t size;
+ char *buf;
+ int out_of_core;
+};
+
+
+
+/* fixme: We need a context for each thread or serialize the access to
+ the dirmngr. */
+static assuan_context_t dirmngr_ctx = NULL;
+static assuan_context_t dirmngr2_ctx = NULL;
+
+static int dirmngr_ctx_locked;
+static int dirmngr2_ctx_locked;
+
+static int force_pipe_server = 0;
+
+struct inq_certificate_parm_s {
+ ctrl_t ctrl;
+ assuan_context_t ctx;
+ ksba_cert_t cert;
+ ksba_cert_t issuer_cert;
+};
+
+struct isvalid_status_parm_s {
+ ctrl_t ctrl;
+ int seen;
+ unsigned char fpr[20];
+};
+
+
+struct lookup_parm_s {
+ ctrl_t ctrl;
+ assuan_context_t ctx;
+ void (*cb)(void *, ksba_cert_t);
+ void *cb_value;
+ struct membuf data;
+ int error;
+};
+
+struct run_command_parm_s {
+ assuan_context_t ctx;
+};
+
+
+
+static gpg_error_t get_cached_cert (assuan_context_t ctx,
+ const unsigned char *fpr,
+ ksba_cert_t *r_cert);
+
+
+
+/* A simple implementation of a dynamic buffer. Use init_membuf() to
+ create a buffer, put_membuf to append bytes and get_membuf to
+ release and return the buffer. Allocation errors are detected but
+ only returned at the final get_membuf(), this helps not to clutter
+ the code with out of core checks. */
+
+static void
+init_membuf (struct membuf *mb, int initiallen)
+{
+ mb->len = 0;
+ mb->size = initiallen;
+ mb->out_of_core = 0;
+ mb->buf = xtrymalloc (initiallen);
+ if (!mb->buf)
+ mb->out_of_core = 1;
+}
+
+static void
+put_membuf (struct membuf *mb, const void *buf, size_t len)
+{
+ if (mb->out_of_core)
+ return;
+
+ if (mb->len + len >= mb->size)
+ {
+ char *p;
+
+ mb->size += len + 1024;
+ p = xtryrealloc (mb->buf, mb->size);
+ if (!p)
+ {
+ mb->out_of_core = 1;
+ return;
+ }
+ mb->buf = p;
+ }
+ memcpy (mb->buf + mb->len, buf, len);
+ mb->len += len;
+}
+
+static void *
+get_membuf (struct membuf *mb, size_t *len)
+{
+ char *p;
+
+ if (mb->out_of_core)
+ {
+ xfree (mb->buf);
+ mb->buf = NULL;
+ return NULL;
+ }
+
+ p = mb->buf;
+ *len = mb->len;
+ mb->buf = NULL;
+ mb->out_of_core = 1; /* don't allow a reuse */
+ return p;
+}
+
+
+/* This function prepares the dirmngr for a new session. The
+ audit-events option is used so that other dirmngr clients won't get
+ disturbed by such events. */
+static void
+prepare_dirmngr (ctrl_t ctrl, assuan_context_t ctx, gpg_error_t err)
+{
+ struct keyserver_spec *server;
+
+ if (!err)
+ {
+ err = assuan_transact (ctx, "OPTION audit-events=1",
+ NULL, NULL, NULL, NULL, NULL, NULL);
+ if (gpg_err_code (err) == GPG_ERR_UNKNOWN_OPTION)
+ err = 0; /* Allow the use of old dirmngr versions. */
+ }
+ audit_log_ok (ctrl->audit, AUDIT_DIRMNGR_READY, err);
+
+ if (!ctx || err)
+ return;
+
+ server = opt.keyserver;
+ while (server)
+ {
+ char line[ASSUAN_LINELENGTH];
+ char *user = server->user ? server->user : "";
+ char *pass = server->pass ? server->pass : "";
+ char *base = server->base ? server->base : "";
+
+ snprintf (line, DIM (line) - 1, "LDAPSERVER %s:%i:%s:%s:%s",
+ server->host, server->port, user, pass, base);
+ line[DIM (line) - 1] = 0;
+
+ err = assuan_transact (ctx, line, NULL, NULL, NULL, NULL, NULL, NULL);
+ if (gpg_err_code (err) == GPG_ERR_ASS_UNKNOWN_CMD)
+ err = 0; /* Allow the use of old dirmngr versions. */
+
+ server = server->next;
+ }
+}
+
+
+
+/* Try to connect to the agent via socket or fork it off and work by
+ pipes. Handle the server's initial greeting */
+static int
+start_dirmngr_ext (ctrl_t ctrl, assuan_context_t *ctx_r)
+{
+ int rc;
+ char *infostr, *p;
+ assuan_context_t ctx = NULL;
+ int try_default = 0;
+
+ if (opt.disable_dirmngr)
+ return gpg_error (GPG_ERR_NO_DIRMNGR);
+
+ if (*ctx_r)
+ return 0;
+
+ /* Note: if you change this to multiple connections, you also need
+ to take care of the implicit option sending caching. */
+
+#ifdef HAVE_W32_SYSTEM
+ infostr = NULL;
+ opt.prefer_system_dirmngr = 1;
+#else
+ infostr = force_pipe_server? NULL : getenv ("DIRMNGR_INFO");
+#endif /*HAVE_W32_SYSTEM*/
+ if (infostr && !*infostr)
+ infostr = NULL;
+ else if (infostr)
+ infostr = xstrdup (infostr);
+
+ if (opt.prefer_system_dirmngr && !force_pipe_server && !infostr)
+ {
+ infostr = xstrdup (dirmngr_socket_name ());
+ try_default = 1;
+ }
+
+ rc = assuan_new (&ctx);
+ if (rc)
+ {
+ log_error ("can't allocate assuan context: %s\n", gpg_strerror (rc));
+ return rc;
+ }
+
+ if (!infostr)
+ {
+ const char *pgmname;
+ const char *argv[3];
+ int no_close_list[3];
+ int i;
+
+ if (!opt.dirmngr_program || !*opt.dirmngr_program)
+ opt.dirmngr_program = gnupg_module_name (GNUPG_MODULE_NAME_DIRMNGR);
+ if ( !(pgmname = strrchr (opt.dirmngr_program, '/')))
+ pgmname = opt.dirmngr_program;
+ else
+ pgmname++;
+
+ if (opt.verbose)
+ log_info (_("no running dirmngr - starting `%s'\n"),
+ opt.dirmngr_program);
+
+ if (fflush (NULL))
+ {
+ gpg_error_t tmperr = gpg_error (gpg_err_code_from_errno (errno));
+ log_error ("error flushing pending output: %s\n", strerror (errno));
+ return tmperr;
+ }
+
+ argv[0] = pgmname;
+ argv[1] = "--server";
+ argv[2] = NULL;
+
+ i=0;
+ if (log_get_fd () != -1)
+ no_close_list[i++] = assuan_fd_from_posix_fd (log_get_fd ());
+ no_close_list[i++] = assuan_fd_from_posix_fd (fileno (stderr));
+ no_close_list[i] = -1;
+
+ /* connect to the agent and perform initial handshaking */
+ rc = assuan_pipe_connect (ctx, opt.dirmngr_program, argv,
+ no_close_list, NULL, NULL, 0);
+ }
+ else
+ {
+ int prot;
+ int pid;
+
+ if (!try_default)
+ {
+ if ( !(p = strchr (infostr, PATHSEP_C)) || p == infostr)
+ {
+ log_error (_("malformed DIRMNGR_INFO environment variable\n"));
+ xfree (infostr);
+ force_pipe_server = 1;
+ return start_dirmngr_ext (ctrl, ctx_r);
+ }
+ *p++ = 0;
+ pid = atoi (p);
+ while (*p && *p != PATHSEP_C)
+ p++;
+ prot = *p? atoi (p+1) : 0;
+ if (prot != 1)
+ {
+ log_error (_("dirmngr protocol version %d is not supported\n"),
+ prot);
+ xfree (infostr);
+ force_pipe_server = 1;
+ return start_dirmngr_ext (ctrl, ctx_r);
+ }
+ }
+ else
+ pid = -1;
+
+ rc = assuan_socket_connect (ctx, infostr, pid, 0);
+#ifdef HAVE_W32_SYSTEM
+ if (rc)
+ log_debug ("connecting dirmngr at `%s' failed\n", infostr);
+#endif
+
+ xfree (infostr);
+#ifndef HAVE_W32_SYSTEM
+ if (gpg_err_code (rc) == GPG_ERR_ASS_CONNECT_FAILED)
+ {
+ log_info (_("can't connect to the dirmngr - trying fall back\n"));
+ force_pipe_server = 1;
+ return start_dirmngr_ext (ctrl, ctx_r);
+ }
+#endif /*!HAVE_W32_SYSTEM*/
+ }
+
+ prepare_dirmngr (ctrl, ctx, rc);
+
+ if (rc)
+ {
+ assuan_release (ctx);
+ log_error ("can't connect to the dirmngr: %s\n", gpg_strerror (rc));
+ return gpg_error (GPG_ERR_NO_DIRMNGR);
+ }
+ *ctx_r = ctx;
+
+ if (DBG_ASSUAN)
+ log_debug ("connection to dirmngr established\n");
+ return 0;
+}
+
+
+static int
+start_dirmngr (ctrl_t ctrl)
+{
+ gpg_error_t err;
+
+ assert (! dirmngr_ctx_locked);
+ dirmngr_ctx_locked = 1;
+
+ err = start_dirmngr_ext (ctrl, &dirmngr_ctx);
+ /* We do not check ERR but the existance of a context because the
+ error might come from a failed command send to the dirmngr.
+ Fixme: Why don't we close the drimngr context if we encountered
+ an error in prepare_dirmngr? */
+ if (!dirmngr_ctx)
+ dirmngr_ctx_locked = 0;
+ return err;
+}
+
+
+static void
+release_dirmngr (ctrl_t ctrl)
+{
+ (void)ctrl;
+
+ if (!dirmngr_ctx_locked)
+ log_error ("WARNING: trying to release a non-locked dirmngr ctx\n");
+ dirmngr_ctx_locked = 0;
+}
+
+
+static int
+start_dirmngr2 (ctrl_t ctrl)
+{
+ gpg_error_t err;
+
+ assert (! dirmngr2_ctx_locked);
+ dirmngr2_ctx_locked = 1;
+
+ err = start_dirmngr_ext (ctrl, &dirmngr2_ctx);
+ if (!dirmngr2_ctx)
+ dirmngr2_ctx_locked = 0;
+ return err;
+}
+
+
+static void
+release_dirmngr2 (ctrl_t ctrl)
+{
+ (void)ctrl;
+
+ if (!dirmngr2_ctx_locked)
+ log_error ("WARNING: trying to release a non-locked dirmngr2 ctx\n");
+ dirmngr2_ctx_locked = 0;
+}
+
+
+
+/* Handle a SENDCERT inquiry. */
+static gpg_error_t
+inq_certificate (void *opaque, const char *line)
+{
+ struct inq_certificate_parm_s *parm = opaque;
+ int rc;
+ const unsigned char *der;
+ size_t derlen;
+ int issuer_mode = 0;
+ ksba_sexp_t ski = NULL;
+
+ if (!strncmp (line, "SENDCERT", 8) && (line[8] == ' ' || !line[8]))
+ {
+ line += 8;
+ }
+ else if (!strncmp (line, "SENDCERT_SKI", 12) && (line[12]==' ' || !line[12]))
+ {
+ size_t n;
+
+ /* Send a certificate where a sourceKeyIdentifier is included. */
+ line += 12;
+ while (*line == ' ')
+ line++;
+ ski = make_simple_sexp_from_hexstr (line, &n);
+ line += n;
+ while (*line == ' ')
+ line++;
+ }
+ else if (!strncmp (line, "SENDISSUERCERT", 14)
+ && (line[14] == ' ' || !line[14]))
+ {
+ line += 14;
+ issuer_mode = 1;
+ }
+ else if (!strncmp (line, "ISTRUSTED", 9) && (line[9]==' ' || !line[9]))
+ {
+ /* The server is asking us whether the certificate is a trusted
+ root certificate. */
+ const char *s;
+ size_t n;
+ char fpr[41];
+ struct rootca_flags_s rootca_flags;
+
+ line += 9;
+ while (*line == ' ')
+ line++;
+
+ for (s=line,n=0; hexdigitp (s); s++, n++)
+ ;
+ if (*s || n != 40)
+ return gpg_error (GPG_ERR_ASS_PARAMETER);
+ for (s=line, n=0; n < 40; s++, n++)
+ fpr[n] = (*s >= 'a')? (*s & 0xdf): *s;
+ fpr[n] = 0;
+
+ if (!gpgsm_agent_istrusted (parm->ctrl, NULL, fpr, &rootca_flags))
+ rc = assuan_send_data (parm->ctx, "1", 1);
+ else
+ rc = 0;
+ return rc;
+ }
+ else
+ {
+ log_error ("unsupported inquiry `%s'\n", line);
+ return gpg_error (GPG_ERR_ASS_UNKNOWN_INQUIRE);
+ }
+
+ if (!*line)
+ { /* Send the current certificate. */
+ der = ksba_cert_get_image (issuer_mode? parm->issuer_cert : parm->cert,
+ &derlen);
+ if (!der)
+ rc = gpg_error (GPG_ERR_INV_CERT_OBJ);
+ else
+ rc = assuan_send_data (parm->ctx, der, derlen);
+ }
+ else if (issuer_mode)
+ {
+ log_error ("sending specific issuer certificate back "
+ "is not yet implemented\n");
+ rc = gpg_error (GPG_ERR_ASS_UNKNOWN_INQUIRE);
+ }
+ else
+ { /* Send the given certificate. */
+ int err;
+ ksba_cert_t cert;
+
+
+ err = gpgsm_find_cert (line, ski, &cert);
+ if (err)
+ {
+ log_error ("certificate not found: %s\n", gpg_strerror (err));
+ rc = gpg_error (GPG_ERR_NOT_FOUND);
+ }
+ else
+ {
+ der = ksba_cert_get_image (cert, &derlen);
+ if (!der)
+ rc = gpg_error (GPG_ERR_INV_CERT_OBJ);
+ else
+ rc = assuan_send_data (parm->ctx, der, derlen);
+ ksba_cert_release (cert);
+ }
+ }
+
+ xfree (ski);
+ return rc;
+}
+
+
+/* Take a 20 byte hexencoded string and put it into the the provided
+ 20 byte buffer FPR in binary format. */
+static int
+unhexify_fpr (const char *hexstr, unsigned char *fpr)
+{
+ const char *s;
+ int n;
+
+ for (s=hexstr, n=0; hexdigitp (s); s++, n++)
+ ;
+ if (*s || (n != 40))
+ return 0; /* no fingerprint (invalid or wrong length). */
+ n /= 2;
+ for (s=hexstr, n=0; *s; s += 2, n++)
+ fpr[n] = xtoi_2 (s);
+ return 1; /* okay */
+}
+
+
+static gpg_error_t
+isvalid_status_cb (void *opaque, const char *line)
+{
+ struct isvalid_status_parm_s *parm = opaque;
+
+ if (!strncmp (line, "PROGRESS", 8) && (line[8]==' ' || !line[8]))
+ {
+ if (parm->ctrl)
+ {
+ for (line += 8; *line == ' '; line++)
+ ;
+ if (gpgsm_status (parm->ctrl, STATUS_PROGRESS, line))
+ return gpg_error (GPG_ERR_ASS_CANCELED);
+ }
+ }
+ else if (!strncmp (line, "ONLY_VALID_IF_CERT_VALID", 24)
+ && (line[24]==' ' || !line[24]))
+ {
+ parm->seen++;
+ if (!line[24] || !unhexify_fpr (line+25, parm->fpr))
+ parm->seen++; /* Bumb it to indicate an error. */
+ }
+ return 0;
+}
+
+
+
+
+/* Call the directory manager to check whether the certificate is valid
+ Returns 0 for valid or usually one of the errors:
+
+ GPG_ERR_CERTIFICATE_REVOKED
+ GPG_ERR_NO_CRL_KNOWN
+ GPG_ERR_CRL_TOO_OLD
+
+ Values for USE_OCSP:
+ 0 = Do CRL check.
+ 1 = Do an OCSP check.
+ 2 = Do an OCSP check using only the default responder.
+ */
+int
+gpgsm_dirmngr_isvalid (ctrl_t ctrl,
+ ksba_cert_t cert, ksba_cert_t issuer_cert, int use_ocsp)
+{
+ static int did_options;
+ int rc;
+ char *certid;
+ char line[ASSUAN_LINELENGTH];
+ struct inq_certificate_parm_s parm;
+ struct isvalid_status_parm_s stparm;
+
+ rc = start_dirmngr (ctrl);
+ if (rc)
+ return rc;
+
+ if (use_ocsp)
+ {
+ certid = gpgsm_get_fingerprint_hexstring (cert, GCRY_MD_SHA1);
+ }
+ else
+ {
+ certid = gpgsm_get_certid (cert);
+ if (!certid)
+ {
+ log_error ("error getting the certificate ID\n");
+ release_dirmngr (ctrl);
+ return gpg_error (GPG_ERR_GENERAL);
+ }
+ }
+
+ if (opt.verbose > 1)
+ {
+ char *fpr = gpgsm_get_fingerprint_string (cert, GCRY_MD_SHA1);
+ log_info ("asking dirmngr about %s%s\n", fpr,
+ use_ocsp? " (using OCSP)":"");
+ xfree (fpr);
+ }
+
+ parm.ctx = dirmngr_ctx;
+ parm.ctrl = ctrl;
+ parm.cert = cert;
+ parm.issuer_cert = issuer_cert;
+
+ stparm.ctrl = ctrl;
+ stparm.seen = 0;
+ memset (stparm.fpr, 0, 20);
+
+ /* FIXME: If --disable-crl-checks has been set, we should pass an
+ option to dirmngr, so that no fallback CRL check is done after an
+ ocsp check. It is not a problem right now as dirmngr does not
+ fallback to CRL checking. */
+
+ /* It is sufficient to send the options only once because we have
+ one connection per process only. */
+ if (!did_options)
+ {
+ if (opt.force_crl_refresh)
+ assuan_transact (dirmngr_ctx, "OPTION force-crl-refresh=1",
+ NULL, NULL, NULL, NULL, NULL, NULL);
+ did_options = 1;
+ }
+ snprintf (line, DIM(line)-1, "ISVALID%s %s",
+ use_ocsp == 2? " --only-ocsp --force-default-responder":"",
+ certid);
+ line[DIM(line)-1] = 0;
+ xfree (certid);
+
+ rc = assuan_transact (dirmngr_ctx, line, NULL, NULL,
+ inq_certificate, &parm,
+ isvalid_status_cb, &stparm);
+ if (opt.verbose > 1)
+ log_info ("response of dirmngr: %s\n", rc? gpg_strerror (rc): "okay");
+ rc = rc;
+
+ if (!rc && stparm.seen)
+ {
+ /* Need to also check the certificate validity. */
+ if (stparm.seen != 1)
+ {
+ log_error ("communication problem with dirmngr detected\n");
+ rc = gpg_error (GPG_ERR_INV_CRL);
+ }
+ else
+ {
+ ksba_cert_t rspcert = NULL;
+
+ if (get_cached_cert (dirmngr_ctx, stparm.fpr, &rspcert))
+ {
+ /* Ooops: Something went wrong getting the certificate
+ from the dirmngr. Try our own cert store now. */
+ KEYDB_HANDLE kh;
+
+ kh = keydb_new (0);
+ if (!kh)
+ rc = gpg_error (GPG_ERR_ENOMEM);
+ if (!rc)
+ rc = keydb_search_fpr (kh, stparm.fpr);
+ if (!rc)
+ rc = keydb_get_cert (kh, &rspcert);
+ if (rc)
+ {
+ log_error ("unable to find the certificate used "
+ "by the dirmngr: %s\n", gpg_strerror (rc));
+ rc = gpg_error (GPG_ERR_INV_CRL);
+ }
+ keydb_release (kh);
+ }
+
+ if (!rc)
+ {
+ rc = gpgsm_cert_use_ocsp_p (rspcert);
+ if (rc)
+ rc = gpg_error (GPG_ERR_INV_CRL);
+ else
+ {
+ /* Note the no_dirmngr flag: This avoids checking
+ this certificate over and over again. */
+ rc = gpgsm_validate_chain (ctrl, rspcert, "", NULL, 0, NULL,
+ VALIDATE_FLAG_NO_DIRMNGR, NULL);
+ if (rc)
+ {
+ log_error ("invalid certificate used for CRL/OCSP: %s\n",
+ gpg_strerror (rc));
+ rc = gpg_error (GPG_ERR_INV_CRL);
+ }
+ }
+ }
+ ksba_cert_release (rspcert);
+ }
+ }
+ release_dirmngr (ctrl);
+ return rc;
+}
+
+
+
+/* Lookup helpers*/
+static gpg_error_t
+lookup_cb (void *opaque, const void *buffer, size_t length)
+{
+ struct lookup_parm_s *parm = opaque;
+ size_t len;
+ char *buf;
+ ksba_cert_t cert;
+ int rc;
+
+ if (parm->error)
+ return 0;
+
+ if (buffer)
+ {
+ put_membuf (&parm->data, buffer, length);
+ return 0;
+ }
+ /* END encountered - process what we have */
+ buf = get_membuf (&parm->data, &len);
+ if (!buf)
+ {
+ parm->error = gpg_error (GPG_ERR_ENOMEM);
+ return 0;
+ }
+
+ rc = ksba_cert_new (&cert);
+ if (rc)
+ {
+ parm->error = rc;
+ return 0;
+ }
+ rc = ksba_cert_init_from_mem (cert, buf, len);
+ if (rc)
+ {
+ log_error ("failed to parse a certificate: %s\n", gpg_strerror (rc));
+ }
+ else
+ {
+ parm->cb (parm->cb_value, cert);
+ }
+
+ ksba_cert_release (cert);
+ init_membuf (&parm->data, 4096);
+ return 0;
+}
+
+/* Return a properly escaped pattern from NAMES. The only error
+ return is NULL to indicate a malloc failure. */
+static char *
+pattern_from_strlist (strlist_t names)
+{
+ strlist_t sl;
+ int n;
+ const char *s;
+ char *pattern, *p;
+
+ for (n=0, sl=names; sl; sl = sl->next)
+ {
+ for (s=sl->d; *s; s++, n++)
+ {
+ if (*s == '%' || *s == ' ' || *s == '+')
+ n += 2;
+ }
+ n++;
+ }
+
+ p = pattern = xtrymalloc (n+1);
+ if (!pattern)
+ return NULL;
+
+ for (sl=names; sl; sl = sl->next)
+ {
+ for (s=sl->d; *s; s++)
+ {
+ switch (*s)
+ {
+ case '%':
+ *p++ = '%';
+ *p++ = '2';
+ *p++ = '5';
+ break;
+ case ' ':
+ *p++ = '%';
+ *p++ = '2';
+ *p++ = '0';
+ break;
+ case '+':
+ *p++ = '%';
+ *p++ = '2';
+ *p++ = 'B';
+ break;
+ default:
+ *p++ = *s;
+ break;
+ }
+ }
+ *p++ = ' ';
+ }
+ if (p == pattern)
+ *pattern = 0; /* is empty */
+ else
+ p[-1] = '\0'; /* remove trailing blank */
+
+ return pattern;
+}
+
+static gpg_error_t
+lookup_status_cb (void *opaque, const char *line)
+{
+ struct lookup_parm_s *parm = opaque;
+
+ if (!strncmp (line, "PROGRESS", 8) && (line[8]==' ' || !line[8]))
+ {
+ if (parm->ctrl)
+ {
+ for (line += 8; *line == ' '; line++)
+ ;
+ if (gpgsm_status (parm->ctrl, STATUS_PROGRESS, line))
+ return gpg_error (GPG_ERR_ASS_CANCELED);
+ }
+ }
+ else if (!strncmp (line, "TRUNCATED", 9) && (line[9]==' ' || !line[9]))
+ {
+ if (parm->ctrl)
+ {
+ for (line +=9; *line == ' '; line++)
+ ;
+ gpgsm_status (parm->ctrl, STATUS_TRUNCATED, line);
+ }
+ }
+ return 0;
+}
+
+
+/* Run the Directory Manager's lookup command using the pattern
+ compiled from the strings given in NAMES. The caller must provide
+ the callback CB which will be passed cert by cert. Note that CTRL
+ is optional. With CACHE_ONLY the dirmngr will search only its own
+ key cache. */
+int
+gpgsm_dirmngr_lookup (ctrl_t ctrl, strlist_t names, int cache_only,
+ void (*cb)(void*, ksba_cert_t), void *cb_value)
+{
+ int rc;
+ char *pattern;
+ char line[ASSUAN_LINELENGTH];
+ struct lookup_parm_s parm;
+ size_t len;
+ assuan_context_t ctx;
+
+ /* The lookup function can be invoked from the callback of a lookup
+ function, for example to walk the chain. */
+ if (!dirmngr_ctx_locked)
+ {
+ rc = start_dirmngr (ctrl);
+ if (rc)
+ return rc;
+ ctx = dirmngr_ctx;
+ }
+ else if (!dirmngr2_ctx_locked)
+ {
+ rc = start_dirmngr2 (ctrl);
+ if (rc)
+ return rc;
+ ctx = dirmngr2_ctx;
+ }
+ else
+ {
+ log_fatal ("both dirmngr contexts are in use\n");
+ }
+
+ pattern = pattern_from_strlist (names);
+ if (!pattern)
+ {
+ if (ctx == dirmngr_ctx)
+ release_dirmngr (ctrl);
+ else
+ release_dirmngr2 (ctrl);
+
+ return out_of_core ();
+ }
+ snprintf (line, DIM(line)-1, "LOOKUP%s %s",
+ cache_only? " --cache-only":"", pattern);
+ line[DIM(line)-1] = 0;
+ xfree (pattern);
+
+ parm.ctrl = ctrl;
+ parm.ctx = ctx;
+ parm.cb = cb;
+ parm.cb_value = cb_value;
+ parm.error = 0;
+ init_membuf (&parm.data, 4096);
+
+ rc = assuan_transact (ctx, line, lookup_cb, &parm,
+ NULL, NULL, lookup_status_cb, &parm);
+ xfree (get_membuf (&parm.data, &len));
+
+ if (ctx == dirmngr_ctx)
+ release_dirmngr (ctrl);
+ else
+ release_dirmngr2 (ctrl);
+
+ if (rc)
+ return rc;
+ return parm.error;
+}
+
+
+
+static gpg_error_t
+get_cached_cert_data_cb (void *opaque, const void *buffer, size_t length)
+{
+ struct membuf *mb = opaque;
+
+ if (buffer)
+ put_membuf (mb, buffer, length);
+ return 0;
+}
+
+/* Return a certificate from the Directory Manager's cache. This
+ function only returns one certificate which must be specified using
+ the fingerprint FPR and will be stored at R_CERT. On error NULL is
+ stored at R_CERT and an error code returned. Note that the caller
+ must provide the locked dirmngr context CTX. */
+static gpg_error_t
+get_cached_cert (assuan_context_t ctx,
+ const unsigned char *fpr, ksba_cert_t *r_cert)
+{
+ gpg_error_t err;
+ char line[ASSUAN_LINELENGTH];
+ char hexfpr[2*20+1];
+ struct membuf mb;
+ char *buf;
+ size_t buflen;
+ ksba_cert_t cert;
+
+ *r_cert = NULL;
+
+ bin2hex (fpr, 20, hexfpr);
+ snprintf (line, DIM(line)-1, "LOOKUP --single --cache-only 0x%s", hexfpr);
+
+ init_membuf (&mb, 4096);
+ err = assuan_transact (ctx, line, get_cached_cert_data_cb, &mb,
+ NULL, NULL, NULL, NULL);
+ buf = get_membuf (&mb, &buflen);
+ if (err)
+ {
+ xfree (buf);
+ return err;
+ }
+ if (!buf)
+ return gpg_error (GPG_ERR_ENOMEM);
+
+ err = ksba_cert_new (&cert);
+ if (err)
+ {
+ xfree (buf);
+ return err;
+ }
+ err = ksba_cert_init_from_mem (cert, buf, buflen);
+ xfree (buf);
+ if (err)
+ {
+ log_error ("failed to parse a certificate: %s\n", gpg_strerror (err));
+ ksba_cert_release (cert);
+ return err;
+ }
+
+ *r_cert = cert;
+ return 0;
+}
+
+
+
+/* Run Command helpers*/
+
+/* Fairly simple callback to write all output of dirmngr to stdout. */
+static gpg_error_t
+run_command_cb (void *opaque, const void *buffer, size_t length)
+{
+ (void)opaque;
+
+ if (buffer)
+ {
+ if ( fwrite (buffer, length, 1, stdout) != 1 )
+ log_error ("error writing to stdout: %s\n", strerror (errno));
+ }
+ return 0;
+}
+
+/* Handle inquiries from the dirmngr COMMAND. */
+static gpg_error_t
+run_command_inq_cb (void *opaque, const char *line)
+{
+ struct run_command_parm_s *parm = opaque;
+ int rc = 0;
+
+ if ( !strncmp (line, "SENDCERT", 8) && (line[8] == ' ' || !line[8]) )
+ { /* send the given certificate */
+ int err;
+ ksba_cert_t cert;
+ const unsigned char *der;
+ size_t derlen;
+
+ line += 8;
+ if (!*line)
+ return gpg_error (GPG_ERR_ASS_PARAMETER);
+
+ err = gpgsm_find_cert (line, NULL, &cert);
+ if (err)
+ {
+ log_error ("certificate not found: %s\n", gpg_strerror (err));
+ rc = gpg_error (GPG_ERR_NOT_FOUND);
+ }
+ else
+ {
+ der = ksba_cert_get_image (cert, &derlen);
+ if (!der)
+ rc = gpg_error (GPG_ERR_INV_CERT_OBJ);
+ else
+ rc = assuan_send_data (parm->ctx, der, derlen);
+ ksba_cert_release (cert);
+ }
+ }
+ else if ( !strncmp (line, "PRINTINFO", 9) && (line[9] == ' ' || !line[9]) )
+ { /* Simply show the message given in the argument. */
+ line += 9;
+ log_info ("dirmngr: %s\n", line);
+ }
+ else
+ {
+ log_error ("unsupported inquiry `%s'\n", line);
+ rc = gpg_error (GPG_ERR_ASS_UNKNOWN_INQUIRE);
+ }
+
+ return rc;
+}
+
+static gpg_error_t
+run_command_status_cb (void *opaque, const char *line)
+{
+ ctrl_t ctrl = opaque;
+
+ if (opt.verbose)
+ {
+ log_info ("dirmngr status: %s\n", line);
+ }
+ if (!strncmp (line, "PROGRESS", 8) && (line[8]==' ' || !line[8]))
+ {
+ if (ctrl)
+ {
+ for (line += 8; *line == ' '; line++)
+ ;
+ if (gpgsm_status (ctrl, STATUS_PROGRESS, line))
+ return gpg_error (GPG_ERR_ASS_CANCELED);
+ }
+ }
+ return 0;
+}
+
+
+
+/* Pass COMMAND to dirmngr and print all output generated by Dirmngr
+ to stdout. A couple of inquiries are defined (see above). ARGC
+ arguments in ARGV are given to the Dirmngr. Spaces, plus and
+ percent characters within the argument strings are percent escaped
+ so that blanks can act as delimiters. */
+int
+gpgsm_dirmngr_run_command (ctrl_t ctrl, const char *command,
+ int argc, char **argv)
+{
+ int rc;
+ int i;
+ const char *s;
+ char *line, *p;
+ size_t len;
+ struct run_command_parm_s parm;
+
+ rc = start_dirmngr (ctrl);
+ if (rc)
+ return rc;
+
+ parm.ctx = dirmngr_ctx;
+
+ len = strlen (command) + 1;
+ for (i=0; i < argc; i++)
+ len += 1 + 3*strlen (argv[i]); /* enough space for percent escaping */
+ line = xtrymalloc (len);
+ if (!line)
+ {
+ release_dirmngr (ctrl);
+ return out_of_core ();
+ }
+
+ p = stpcpy (line, command);
+ for (i=0; i < argc; i++)
+ {
+ *p++ = ' ';
+ for (s=argv[i]; *s; s++)
+ {
+ if (!isascii (*s))
+ *p++ = *s;
+ else if (*s == ' ')
+ *p++ = '+';
+ else if (!isprint (*s) || *s == '+')
+ {
+ sprintf (p, "%%%02X", *(const unsigned char *)s);
+ p += 3;
+ }
+ else
+ *p++ = *s;
+ }
+ }
+ *p = 0;
+
+ rc = assuan_transact (dirmngr_ctx, line,
+ run_command_cb, NULL,
+ run_command_inq_cb, &parm,
+ run_command_status_cb, ctrl);
+ xfree (line);
+ log_info ("response of dirmngr: %s\n", rc? gpg_strerror (rc): "okay");
+ release_dirmngr (ctrl);
+ return rc;
+}
diff --git a/sm/certchain.c b/sm/certchain.c
new file mode 100644
index 0000000..f4ad214
--- /dev/null
+++ b/sm/certchain.c
@@ -0,0 +1,2044 @@
+/* certchain.c - certificate chain validation
+ * Copyright (C) 2001, 2002, 2003, 2004, 2005,
+ * 2006, 2007, 2008 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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuPG is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <time.h>
+#include <stdarg.h>
+#include <assert.h>
+
+#define JNLIB_NEED_LOG_LOGV /* We need log_logv. */
+
+#include "gpgsm.h"
+#include <gcrypt.h>
+#include <ksba.h>
+
+#include "keydb.h"
+#include "../kbx/keybox.h" /* for KEYBOX_FLAG_* */
+#include "i18n.h"
+#include "tlv.h"
+
+
+/* Object to keep track of certain root certificates. */
+struct marktrusted_info_s
+{
+ struct marktrusted_info_s *next;
+ unsigned char fpr[20];
+};
+static struct marktrusted_info_s *marktrusted_info;
+
+
+/* While running the validation function we want to keep track of the
+ certificates in the chain. This type is used for that. */
+struct chain_item_s
+{
+ struct chain_item_s *next;
+ ksba_cert_t cert; /* The certificate. */
+ int is_root; /* The certificate is the root certificate. */
+};
+typedef struct chain_item_s *chain_item_t;
+
+
+static int is_root_cert (ksba_cert_t cert,
+ const char *issuerdn, const char *subjectdn);
+static int get_regtp_ca_info (ctrl_t ctrl, ksba_cert_t cert, int *chainlen);
+
+
+/* This function returns true if we already asked during this session
+ whether the root certificate CERT shall be marked as trusted. */
+static int
+already_asked_marktrusted (ksba_cert_t cert)
+{
+ unsigned char fpr[20];
+ struct marktrusted_info_s *r;
+
+ gpgsm_get_fingerprint (cert, GCRY_MD_SHA1, fpr, NULL);
+ /* No context switches in the loop! */
+ for (r=marktrusted_info; r; r= r->next)
+ if (!memcmp (r->fpr, fpr, 20))
+ return 1;
+ return 0;
+}
+
+/* Flag certificate CERT as already asked whether it shall be marked
+ as trusted. */
+static void
+set_already_asked_marktrusted (ksba_cert_t cert)
+{
+ unsigned char fpr[20];
+ struct marktrusted_info_s *r;
+
+ gpgsm_get_fingerprint (cert, GCRY_MD_SHA1, fpr, NULL);
+ for (r=marktrusted_info; r; r= r->next)
+ if (!memcmp (r->fpr, fpr, 20))
+ return; /* Already marked. */
+ r = xtrycalloc (1, sizeof *r);
+ if (!r)
+ return;
+ memcpy (r->fpr, fpr, 20);
+ r->next = marktrusted_info;
+ marktrusted_info = r;
+}
+
+/* If LISTMODE is true, print FORMAT using LISTMODE to FP. If
+ LISTMODE is false, use the string to print an log_info or, if
+ IS_ERROR is true, and log_error. */
+static void
+do_list (int is_error, int listmode, estream_t fp, const char *format, ...)
+{
+ va_list arg_ptr;
+
+ va_start (arg_ptr, format) ;
+ if (listmode)
+ {
+ if (fp)
+ {
+ es_fputs (" [", fp);
+ es_vfprintf (fp, format, arg_ptr);
+ es_fputs ("]\n", fp);
+ }
+ }
+ else
+ {
+ log_logv (is_error? JNLIB_LOG_ERROR: JNLIB_LOG_INFO, format, arg_ptr);
+ log_printf ("\n");
+ }
+ va_end (arg_ptr);
+}
+
+/* Return 0 if A and B are equal. */
+static int
+compare_certs (ksba_cert_t a, ksba_cert_t b)
+{
+ const unsigned char *img_a, *img_b;
+ size_t len_a, len_b;
+
+ img_a = ksba_cert_get_image (a, &len_a);
+ if (!img_a)
+ return 1;
+ img_b = ksba_cert_get_image (b, &len_b);
+ if (!img_b)
+ return 1;
+ return !(len_a == len_b && !memcmp (img_a, img_b, len_a));
+}
+
+
+/* Return true if CERT has the validityModel extensions and defines
+ the use of the chain model. */
+static int
+has_validation_model_chain (ksba_cert_t cert, int listmode, estream_t listfp)
+{
+ gpg_error_t err;
+ int idx, yes;
+ const char *oid;
+ size_t off, derlen, objlen, hdrlen;
+ const unsigned char *der;
+ int class, tag, constructed, ndef;
+ char *oidbuf;
+
+ for (idx=0; !(err=ksba_cert_get_extension (cert, idx,
+ &oid, NULL, &off, &derlen));idx++)
+ if (!strcmp (oid, "1.3.6.1.4.1.8301.3.5") )
+ break;
+ if (err)
+ return 0; /* Not found. */
+ der = ksba_cert_get_image (cert, NULL);
+ if (!der)
+ {
+ err = gpg_error (GPG_ERR_INV_OBJ); /* Oops */
+ goto leave;
+ }
+ der += off;
+
+ err = parse_ber_header (&der, &derlen, &class, &tag, &constructed,
+ &ndef, &objlen, &hdrlen);
+ if (!err && (objlen > derlen || tag != TAG_SEQUENCE))
+ err = gpg_error (GPG_ERR_INV_OBJ);
+ if (err)
+ goto leave;
+ derlen = objlen;
+ err = parse_ber_header (&der, &derlen, &class, &tag, &constructed,
+ &ndef, &objlen, &hdrlen);
+ if (!err && (objlen > derlen || tag != TAG_OBJECT_ID))
+ err = gpg_error (GPG_ERR_INV_OBJ);
+ if (err)
+ goto leave;
+ oidbuf = ksba_oid_to_str (der, objlen);
+ if (!oidbuf)
+ {
+ err = gpg_error_from_syserror ();
+ goto leave;
+ }
+
+ if (opt.verbose)
+ do_list (0, listmode, listfp,
+ _("validation model requested by certificate: %s"),
+ !strcmp (oidbuf, "1.3.6.1.4.1.8301.3.5.1")? _("chain") :
+ !strcmp (oidbuf, "1.3.6.1.4.1.8301.3.5.2")? _("shell") :
+ /* */ oidbuf);
+ yes = !strcmp (oidbuf, "1.3.6.1.4.1.8301.3.5.1");
+ ksba_free (oidbuf);
+ return yes;
+
+
+ leave:
+ log_error ("error parsing validityModel: %s\n", gpg_strerror (err));
+ return 0;
+}
+
+
+
+static int
+unknown_criticals (ksba_cert_t cert, int listmode, estream_t fp)
+{
+ static const char *known[] = {
+ "2.5.29.15", /* keyUsage */
+ "2.5.29.17", /* subjectAltName
+ Japanese DoCoMo certs mark them as critical. PKIX
+ only requires them as critical if subjectName is
+ empty. I don't know whether our code gracefully
+ handles such empry subjectNames but that is
+ another story. */
+ "2.5.29.19", /* basic Constraints */
+ "2.5.29.32", /* certificatePolicies */
+ "2.5.29.37", /* extendedKeyUsage - handled by certlist.c */
+ "1.3.6.1.4.1.8301.3.5", /* validityModel - handled here. */
+ NULL
+ };
+ int rc = 0, i, idx, crit;
+ const char *oid;
+ gpg_error_t err;
+ int unsupported;
+ strlist_t sl;
+
+ for (idx=0; !(err=ksba_cert_get_extension (cert, idx,
+ &oid, &crit, NULL, NULL));idx++)
+ {
+ if (!crit)
+ continue;
+ for (i=0; known[i] && strcmp (known[i],oid); i++)
+ ;
+ unsupported = !known[i];
+
+ /* If this critical extension is not supoported, check the list
+ of to be ignored extensions to se whether we claim that it is
+ supported. */
+ if (unsupported && opt.ignored_cert_extensions)
+ {
+ for (sl=opt.ignored_cert_extensions;
+ sl && strcmp (sl->d, oid); sl = sl->next)
+ ;
+ if (sl)
+ unsupported = 0;
+ }
+ if (unsupported)
+ {
+ do_list (1, listmode, fp,
+ _("critical certificate extension %s is not supported"),
+ oid);
+ rc = gpg_error (GPG_ERR_UNSUPPORTED_CERT);
+ }
+ }
+ /* We ignore the error codes EOF as well as no-value. The later will
+ occur for certificates with no extensions at all. */
+ if (err
+ && gpg_err_code (err) != GPG_ERR_EOF
+ && gpg_err_code (err) != GPG_ERR_NO_VALUE)
+ rc = err;
+
+ return rc;
+}
+
+
+/* Check whether CERT is an allowed certificate. This requires that
+ CERT matches all requirements for such a CA, i.e. the
+ BasicConstraints extension. The function returns 0 on success and
+ the awlloed length of the chain at CHAINLEN. */
+static int
+allowed_ca (ctrl_t ctrl,
+ ksba_cert_t cert, int *chainlen, int listmode, estream_t fp)
+{
+ gpg_error_t err;
+ int flag;
+
+ err = ksba_cert_is_ca (cert, &flag, chainlen);
+ if (err)
+ return err;
+ if (!flag)
+ {
+ if (get_regtp_ca_info (ctrl, cert, chainlen))
+ {
+ /* Note that dirmngr takes a different way to cope with such
+ certs. */
+ return 0; /* RegTP issued certificate. */
+ }
+
+ do_list (1, listmode, fp,_("issuer certificate is not marked as a CA"));
+ return gpg_error (GPG_ERR_BAD_CA_CERT);
+ }
+ return 0;
+}
+
+
+static int
+check_cert_policy (ksba_cert_t cert, int listmode, estream_t fplist)
+{
+ gpg_error_t err;
+ char *policies;
+ FILE *fp;
+ int any_critical;
+
+ err = ksba_cert_get_cert_policies (cert, &policies);
+ if (gpg_err_code (err) == GPG_ERR_NO_DATA)
+ return 0; /* No policy given. */
+ if (err)
+ return err;
+
+ /* STRING is a line delimited list of certificate policies as stored
+ in the certificate. The line itself is colon delimited where the
+ first field is the OID of the policy and the second field either
+ N or C for normal or critical extension */
+
+ if (opt.verbose > 1 && !listmode)
+ log_info ("certificate's policy list: %s\n", policies);
+
+ /* The check is very minimal but won't give false positives */
+ any_critical = !!strstr (policies, ":C");
+
+ if (!opt.policy_file)
+ {
+ xfree (policies);
+ if (any_critical)
+ {
+ do_list (1, listmode, fplist,
+ _("critical marked policy without configured policies"));
+ return gpg_error (GPG_ERR_NO_POLICY_MATCH);
+ }
+ return 0;
+ }
+
+ fp = fopen (opt.policy_file, "r");
+ if (!fp)
+ {
+ if (opt.verbose || errno != ENOENT)
+ log_info (_("failed to open `%s': %s\n"),
+ opt.policy_file, strerror (errno));
+ xfree (policies);
+ /* With no critical policies this is only a warning */
+ if (!any_critical)
+ {
+ if (!opt.quiet)
+ do_list (0, listmode, fplist,
+ _("note: non-critical certificate policy not allowed"));
+ return 0;
+ }
+ do_list (1, listmode, fplist,
+ _("certificate policy not allowed"));
+ return gpg_error (GPG_ERR_NO_POLICY_MATCH);
+ }
+
+ for (;;)
+ {
+ int c;
+ char *p, line[256];
+ char *haystack, *allowed;
+
+ /* read line */
+ do
+ {
+ if (!fgets (line, DIM(line)-1, fp) )
+ {
+ gpg_error_t tmperr = gpg_error (gpg_err_code_from_errno (errno));
+
+ xfree (policies);
+ if (feof (fp))
+ {
+ fclose (fp);
+ /* With no critical policies this is only a warning */
+ if (!any_critical)
+ {
+ do_list (0, listmode, fplist,
+ _("note: non-critical certificate policy not allowed"));
+ return 0;
+ }
+ do_list (1, listmode, fplist,
+ _("certificate policy not allowed"));
+ return gpg_error (GPG_ERR_NO_POLICY_MATCH);
+ }
+ fclose (fp);
+ return tmperr;
+ }
+
+ if (!*line || line[strlen(line)-1] != '\n')
+ {
+ /* eat until end of line */
+ while ( (c=getc (fp)) != EOF && c != '\n')
+ ;
+ fclose (fp);
+ xfree (policies);
+ return gpg_error (*line? GPG_ERR_LINE_TOO_LONG
+ : GPG_ERR_INCOMPLETE_LINE);
+ }
+
+ /* Allow for empty lines and spaces */
+ for (p=line; spacep (p); p++)
+ ;
+ }
+ while (!*p || *p == '\n' || *p == '#');
+
+ /* parse line */
+ for (allowed=line; spacep (allowed); allowed++)
+ ;
+ p = strpbrk (allowed, " :\n");
+ if (!*p || p == allowed)
+ {
+ fclose (fp);
+ xfree (policies);
+ return gpg_error (GPG_ERR_CONFIGURATION);
+ }
+ *p = 0; /* strip the rest of the line */
+ /* See whether we find ALLOWED (which is an OID) in POLICIES */
+ for (haystack=policies; (p=strstr (haystack, allowed)); haystack = p+1)
+ {
+ if ( !(p == policies || p[-1] == '\n') )
+ continue; /* Does not match the begin of a line. */
+ if (p[strlen (allowed)] != ':')
+ continue; /* The length does not match. */
+ /* Yep - it does match so return okay. */
+ fclose (fp);
+ xfree (policies);
+ return 0;
+ }
+ }
+}
+
+
+/* Helper function for find_up. This resets the key handle and search
+ for an issuer ISSUER with a subjectKeyIdentifier of KEYID. Returns
+ 0 on success or -1 when not found. */
+static int
+find_up_search_by_keyid (KEYDB_HANDLE kh,
+ const char *issuer, ksba_sexp_t keyid)
+{
+ int rc;
+ ksba_cert_t cert = NULL;
+ ksba_sexp_t subj = NULL;
+
+ keydb_search_reset (kh);
+ while (!(rc = keydb_search_subject (kh, issuer)))
+ {
+ ksba_cert_release (cert); cert = NULL;
+ rc = keydb_get_cert (kh, &cert);
+ if (rc)
+ {
+ log_error ("keydb_get_cert() failed: rc=%d\n", rc);
+ rc = -1;
+ break;
+ }
+ xfree (subj);
+ if (!ksba_cert_get_subj_key_id (cert, NULL, &subj))
+ {
+ if (!cmp_simple_canon_sexp (keyid, subj))
+ break; /* Found matching cert. */
+ }
+ }
+
+ ksba_cert_release (cert);
+ xfree (subj);
+ return rc? -1:0;
+}
+
+
+static void
+find_up_store_certs_cb (void *cb_value, ksba_cert_t cert)
+{
+ if (keydb_store_cert (cert, 1, NULL))
+ log_error ("error storing issuer certificate as ephemeral\n");
+ ++*(int*)cb_value;
+}
+
+
+/* Helper for find_up(). Locate the certificate for ISSUER using an
+ external lookup. KH is the keydb context we are currently using.
+ On success 0 is returned and the certificate may be retrieved from
+ the keydb using keydb_get_cert(). KEYID is the keyIdentifier from
+ the AKI or NULL. */
+static int
+find_up_external (ctrl_t ctrl, KEYDB_HANDLE kh,
+ const char *issuer, ksba_sexp_t keyid)
+{
+ int rc;
+ strlist_t names = NULL;
+ int count = 0;
+ char *pattern;
+ const char *s;
+
+ if (opt.verbose)
+ log_info (_("looking up issuer at external location\n"));
+ /* The Dirmngr process is confused about unknown attributes. As a
+ quick and ugly hack we locate the CN and use the issuer string
+ starting at this attribite. Fixme: we should have far better
+ parsing for external lookups in the Dirmngr. */
+ s = strstr (issuer, "CN=");
+ if (!s || s == issuer || s[-1] != ',')
+ s = issuer;
+ pattern = xtrymalloc (strlen (s)+2);
+ if (!pattern)
+ return gpg_error_from_syserror ();
+ strcpy (stpcpy (pattern, "/"), s);
+ add_to_strlist (&names, pattern);
+ xfree (pattern);
+
+ rc = gpgsm_dirmngr_lookup (ctrl, names, 0, find_up_store_certs_cb, &count);
+ free_strlist (names);
+
+ if (opt.verbose)
+ log_info (_("number of issuers matching: %d\n"), count);
+ if (rc)
+ {
+ log_error ("external key lookup failed: %s\n", gpg_strerror (rc));
+ rc = -1;
+ }
+ else if (!count)
+ rc = -1;
+ else
+ {
+ int old;
+ /* The issuers are currently stored in the ephemeral key DB, so
+ we temporary switch to ephemeral mode. */
+ old = keydb_set_ephemeral (kh, 1);
+ if (keyid)
+ rc = find_up_search_by_keyid (kh, issuer, keyid);
+ else
+ {
+ keydb_search_reset (kh);
+ rc = keydb_search_subject (kh, issuer);
+ }
+ keydb_set_ephemeral (kh, old);
+ }
+ return rc;
+}
+
+
+/* Helper for find_up(). Ask the dirmngr for the certificate for
+ ISSUER with optional SERIALNO. KH is the keydb context we are
+ currently using. With SUBJECT_MODE set, ISSUER is searched as the
+ subject. On success 0 is returned and the certificate is available
+ in the ephemeral DB. */
+static int
+find_up_dirmngr (ctrl_t ctrl, KEYDB_HANDLE kh,
+ ksba_sexp_t serialno, const char *issuer, int subject_mode)
+{
+ int rc;
+ strlist_t names = NULL;
+ int count = 0;
+ char *pattern;
+
+ (void)kh;
+
+ if (opt.verbose)
+ log_info (_("looking up issuer from the Dirmngr cache\n"));
+ if (subject_mode)
+ {
+ pattern = xtrymalloc (strlen (issuer)+2);
+ if (pattern)
+ strcpy (stpcpy (pattern, "/"), issuer);
+ }
+ else if (serialno)
+ pattern = gpgsm_format_sn_issuer (serialno, issuer);
+ else
+ {
+ pattern = xtrymalloc (strlen (issuer)+3);
+ if (pattern)
+ strcpy (stpcpy (pattern, "#/"), issuer);
+ }
+ if (!pattern)
+ return gpg_error_from_syserror ();
+ add_to_strlist (&names, pattern);
+ xfree (pattern);
+
+ rc = gpgsm_dirmngr_lookup (ctrl, names, 1, find_up_store_certs_cb, &count);
+ free_strlist (names);
+
+ if (opt.verbose)
+ log_info (_("number of matching certificates: %d\n"), count);
+ if (rc && !opt.quiet)
+ log_info (_("dirmngr cache-only key lookup failed: %s\n"),
+ gpg_strerror (rc));
+ return (!rc && count)? 0 : -1;
+}
+
+
+
+/* Locate issuing certificate for CERT. ISSUER is the name of the
+ issuer used as a fallback if the other methods don't work. If
+ FIND_NEXT is true, the function shall return the next possible
+ issuer. The certificate itself is not directly returned but a
+ keydb_get_cert on the keyDb context KH will return it. Returns 0
+ on success, -1 if not found or an error code. */
+static int
+find_up (ctrl_t ctrl, KEYDB_HANDLE kh,
+ ksba_cert_t cert, const char *issuer, int find_next)
+{
+ ksba_name_t authid;
+ ksba_sexp_t authidno;
+ ksba_sexp_t keyid;
+ int rc = -1;
+
+ if (!ksba_cert_get_auth_key_id (cert, &keyid, &authid, &authidno))
+ {
+ const char *s = ksba_name_enum (authid, 0);
+ if (s && *authidno)
+ {
+ rc = keydb_search_issuer_sn (kh, s, authidno);
+ if (rc)
+ keydb_search_reset (kh);
+
+ /* In case of an error, try to get the certificate from the
+ dirmngr. That is done by trying to put that certifcate
+ into the ephemeral DB and let the code below do the
+ actual retrieve. Thus there is no error checking.
+ Skipped in find_next mode as usual. */
+ if (rc == -1 && !find_next)
+ find_up_dirmngr (ctrl, kh, authidno, s, 0);
+
+ /* In case of an error try the ephemeral DB. We can't do
+ that in find_next mode because we can't keep the search
+ state then. */
+ if (rc == -1 && !find_next)
+ {
+ int old = keydb_set_ephemeral (kh, 1);
+ if (!old)
+ {
+ rc = keydb_search_issuer_sn (kh, s, authidno);
+ if (rc)
+ keydb_search_reset (kh);
+ }
+ keydb_set_ephemeral (kh, old);
+ }
+ if (rc)
+ rc = -1; /* Need to make sure to have this error code. */
+ }
+
+ if (rc == -1 && keyid && !find_next)
+ {
+ /* Not found by AIK.issuer_sn. Lets try the AIK.ki
+ instead. Loop over all certificates with that issuer as
+ subject and stop for the one with a matching
+ subjectKeyIdentifier. */
+ /* Fixme: Should we also search in the dirmngr? */
+ rc = find_up_search_by_keyid (kh, issuer, keyid);
+ if (rc)
+ {
+ int old = keydb_set_ephemeral (kh, 1);
+ if (!old)
+ rc = find_up_search_by_keyid (kh, issuer, keyid);
+ keydb_set_ephemeral (kh, old);
+ }
+ if (rc)
+ rc = -1; /* Need to make sure to have this error code. */
+ }
+
+ /* If we still didn't found it, try to find it via the subject
+ from the dirmngr-cache. */
+ if (rc == -1 && !find_next)
+ {
+ if (!find_up_dirmngr (ctrl, kh, NULL, issuer, 1))
+ {
+ int old = keydb_set_ephemeral (kh, 1);
+ if (keyid)
+ rc = find_up_search_by_keyid (kh, issuer, keyid);
+ else
+ {
+ keydb_search_reset (kh);
+ rc = keydb_search_subject (kh, issuer);
+ }
+ keydb_set_ephemeral (kh, old);
+ }
+ if (rc)
+ rc = -1; /* Need to make sure to have this error code. */
+ }
+
+ /* If we still didn't found it, try an external lookup. */
+ if (rc == -1 && opt.auto_issuer_key_retrieve && !find_next)
+ rc = find_up_external (ctrl, kh, issuer, keyid);
+
+ /* Print a note so that the user does not feel too helpless when
+ an issuer certificate was found and gpgsm prints BAD
+ signature because it is not the correct one. */
+ if (rc == -1 && opt.quiet)
+ ;
+ else if (rc == -1)
+ {
+ log_info ("%sissuer certificate ", find_next?"next ":"");
+ if (keyid)
+ {
+ log_printf ("{");
+ gpgsm_dump_serial (keyid);
+ log_printf ("} ");
+ }
+ if (authidno)
+ {
+ log_printf ("(#");
+ gpgsm_dump_serial (authidno);
+ log_printf ("/");
+ gpgsm_dump_string (s);
+ log_printf (") ");
+ }
+ log_printf ("not found using authorityKeyIdentifier\n");
+ }
+ else if (rc)
+ log_error ("failed to find authorityKeyIdentifier: rc=%d\n", rc);
+ xfree (keyid);
+ ksba_name_release (authid);
+ xfree (authidno);
+ }
+
+ if (rc) /* Not found via authorithyKeyIdentifier, try regular issuer name. */
+ rc = keydb_search_subject (kh, issuer);
+ if (rc == -1 && !find_next)
+ {
+ int old;
+
+ /* Also try to get it from the Dirmngr cache. The function
+ merely puts it into the ephemeral database. */
+ find_up_dirmngr (ctrl, kh, NULL, issuer, 0);
+
+ /* Not found, let us see whether we have one in the ephemeral key DB. */
+ old = keydb_set_ephemeral (kh, 1);
+ if (!old)
+ {
+ keydb_search_reset (kh);
+ rc = keydb_search_subject (kh, issuer);
+ }
+ keydb_set_ephemeral (kh, old);
+ }
+
+ /* Still not found. If enabled, try an external lookup. */
+ if (rc == -1 && opt.auto_issuer_key_retrieve && !find_next)
+ rc = find_up_external (ctrl, kh, issuer, NULL);
+
+ return rc;
+}
+
+
+/* Return the next certificate up in the chain starting at START.
+ Returns -1 when there are no more certificates. */
+int
+gpgsm_walk_cert_chain (ctrl_t ctrl, ksba_cert_t start, ksba_cert_t *r_next)
+{
+ int rc = 0;
+ char *issuer = NULL;
+ char *subject = NULL;
+ KEYDB_HANDLE kh = keydb_new (0);
+
+ *r_next = NULL;
+ if (!kh)
+ {
+ log_error (_("failed to allocated keyDB handle\n"));
+ rc = gpg_error (GPG_ERR_GENERAL);
+ goto leave;
+ }
+
+ issuer = ksba_cert_get_issuer (start, 0);
+ subject = ksba_cert_get_subject (start, 0);
+ if (!issuer)
+ {
+ log_error ("no issuer found in certificate\n");
+ rc = gpg_error (GPG_ERR_BAD_CERT);
+ goto leave;
+ }
+ if (!subject)
+ {
+ log_error ("no subject found in certificate\n");
+ rc = gpg_error (GPG_ERR_BAD_CERT);
+ goto leave;
+ }
+
+ if (is_root_cert (start, issuer, subject))
+ {
+ rc = -1; /* we are at the root */
+ goto leave;
+ }
+
+ rc = find_up (ctrl, kh, start, issuer, 0);
+ if (rc)
+ {
+ /* It is quite common not to have a certificate, so better don't
+ print an error here. */
+ if (rc != -1 && opt.verbose > 1)
+ log_error ("failed to find issuer's certificate: rc=%d\n", rc);
+ rc = gpg_error (GPG_ERR_MISSING_ISSUER_CERT);
+ goto leave;
+ }
+
+ rc = keydb_get_cert (kh, r_next);
+ if (rc)
+ {
+ log_error ("keydb_get_cert() failed: rc=%d\n", rc);
+ rc = gpg_error (GPG_ERR_GENERAL);
+ }
+
+ leave:
+ xfree (issuer);
+ xfree (subject);
+ keydb_release (kh);
+ return rc;
+}
+
+
+/* Helper for gpgsm_is_root_cert. This one is used if the subject and
+ issuer DNs are already known. */
+static int
+is_root_cert (ksba_cert_t cert, const char *issuerdn, const char *subjectdn)
+{
+ gpg_error_t err;
+ int result = 0;
+ ksba_sexp_t serialno;
+ ksba_sexp_t ak_keyid;
+ ksba_name_t ak_name;
+ ksba_sexp_t ak_sn;
+ const char *ak_name_str;
+ ksba_sexp_t subj_keyid = NULL;
+
+ if (!issuerdn || !subjectdn)
+ return 0; /* No. */
+
+ if (strcmp (issuerdn, subjectdn))
+ return 0; /* No. */
+
+ err = ksba_cert_get_auth_key_id (cert, &ak_keyid, &ak_name, &ak_sn);
+ if (err)
+ {
+ if (gpg_err_code (err) == GPG_ERR_NO_DATA)
+ return 1; /* Yes. Without a authorityKeyIdentifier this needs
+ to be the Root certifcate (our trust anchor). */
+ log_error ("error getting authorityKeyIdentifier: %s\n",
+ gpg_strerror (err));
+ return 0; /* Well, it is broken anyway. Return No. */
+ }
+
+ serialno = ksba_cert_get_serial (cert);
+ if (!serialno)
+ {
+ log_error ("error getting serialno: %s\n", gpg_strerror (err));
+ goto leave;
+ }
+
+ /* Check whether the auth name's matches the issuer name+sn. If
+ that is the case this is a root certificate. */
+ ak_name_str = ksba_name_enum (ak_name, 0);
+ if (ak_name_str
+ && !strcmp (ak_name_str, issuerdn)
+ && !cmp_simple_canon_sexp (ak_sn, serialno))
+ {
+ result = 1; /* Right, CERT is self-signed. */
+ goto leave;
+ }
+
+ /* Similar for the ak_keyid. */
+ if (ak_keyid && !ksba_cert_get_subj_key_id (cert, NULL, &subj_keyid)
+ && !cmp_simple_canon_sexp (ak_keyid, subj_keyid))
+ {
+ result = 1; /* Right, CERT is self-signed. */
+ goto leave;
+ }
+
+
+ leave:
+ ksba_free (subj_keyid);
+ ksba_free (ak_keyid);
+ ksba_name_release (ak_name);
+ ksba_free (ak_sn);
+ ksba_free (serialno);
+ return result;
+}
+
+
+
+/* Check whether the CERT is a root certificate. Returns True if this
+ is the case. */
+int
+gpgsm_is_root_cert (ksba_cert_t cert)
+{
+ char *issuer;
+ char *subject;
+ int yes;
+
+ issuer = ksba_cert_get_issuer (cert, 0);
+ subject = ksba_cert_get_subject (cert, 0);
+ yes = is_root_cert (cert, issuer, subject);
+ xfree (issuer);
+ xfree (subject);
+ return yes;
+}
+
+
+/* This is a helper for gpgsm_validate_chain. */
+static gpg_error_t
+is_cert_still_valid (ctrl_t ctrl, int force_ocsp, int lm, estream_t fp,
+ ksba_cert_t subject_cert, ksba_cert_t issuer_cert,
+ int *any_revoked, int *any_no_crl, int *any_crl_too_old)
+{
+ gpg_error_t err;
+
+ if (opt.no_crl_check && !ctrl->use_ocsp)
+ {
+ audit_log_ok (ctrl->audit, AUDIT_CRL_CHECK,
+ gpg_error (GPG_ERR_NOT_ENABLED));
+ return 0;
+ }
+
+ err = gpgsm_dirmngr_isvalid (ctrl,
+ subject_cert, issuer_cert,
+ force_ocsp? 2 : !!ctrl->use_ocsp);
+ audit_log_ok (ctrl->audit, AUDIT_CRL_CHECK, err);
+
+ if (err)
+ {
+ if (!lm)
+ gpgsm_cert_log_name (NULL, subject_cert);
+ switch (gpg_err_code (err))
+ {
+ case GPG_ERR_CERT_REVOKED:
+ do_list (1, lm, fp, _("certificate has been revoked"));
+ *any_revoked = 1;
+ /* Store that in the keybox so that key listings are able to
+ return the revoked flag. We don't care about error,
+ though. */
+ keydb_set_cert_flags (subject_cert, 1, KEYBOX_FLAG_VALIDITY, 0,
+ ~0, VALIDITY_REVOKED);
+ break;
+
+ case GPG_ERR_NO_CRL_KNOWN:
+ do_list (1, lm, fp, _("no CRL found for certificate"));
+ *any_no_crl = 1;
+ break;
+
+ case GPG_ERR_NO_DATA:
+ do_list (1, lm, fp, _("the status of the certificate is unknown"));
+ *any_no_crl = 1;
+ break;
+
+ case GPG_ERR_CRL_TOO_OLD:
+ do_list (1, lm, fp, _("the available CRL is too old"));
+ if (!lm)
+ log_info (_("please make sure that the "
+ "\"dirmngr\" is properly installed\n"));
+ *any_crl_too_old = 1;
+ break;
+
+ default:
+ do_list (1, lm, fp, _("checking the CRL failed: %s"),
+ gpg_strerror (err));
+ return err;
+ }
+ }
+ return 0;
+}
+
+
+/* Helper for gpgsm_validate_chain to check the validity period of
+ SUBJECT_CERT. The caller needs to pass EXPTIME which will be
+ updated to the nearest expiration time seen. A DEPTH of 0 indicates
+ the target certifciate, -1 the final root certificate and other
+ values intermediate certificates. */
+static gpg_error_t
+check_validity_period (ksba_isotime_t current_time,
+ ksba_cert_t subject_cert,
+ ksba_isotime_t exptime,
+ int listmode, estream_t listfp, int depth)
+{
+ gpg_error_t err;
+ ksba_isotime_t not_before, not_after;
+
+ err = ksba_cert_get_validity (subject_cert, 0, not_before);
+ if (!err)
+ err = ksba_cert_get_validity (subject_cert, 1, not_after);
+ if (err)
+ {
+ do_list (1, listmode, listfp,
+ _("certificate with invalid validity: %s"), gpg_strerror (err));
+ return gpg_error (GPG_ERR_BAD_CERT);
+ }
+
+ if (*not_after)
+ {
+ if (!*exptime)
+ gnupg_copy_time (exptime, not_after);
+ else if (strcmp (not_after, exptime) < 0 )
+ gnupg_copy_time (exptime, not_after);
+ }
+
+ if (*not_before && strcmp (current_time, not_before) < 0 )
+ {
+ do_list (1, listmode, listfp,
+ depth == 0 ? _("certificate not yet valid") :
+ depth == -1 ? _("root certificate not yet valid") :
+ /* other */ _("intermediate certificate not yet valid"));
+ if (!listmode)
+ {
+ log_info (" (valid from ");
+ dump_isotime (not_before);
+ log_printf (")\n");
+ }
+ return gpg_error (GPG_ERR_CERT_TOO_YOUNG);
+ }
+
+ if (*not_after && strcmp (current_time, not_after) > 0 )
+ {
+ do_list (opt.ignore_expiration?0:1, listmode, listfp,
+ depth == 0 ? _("certificate has expired") :
+ depth == -1 ? _("root certificate has expired") :
+ /* other */ _("intermediate certificate has expired"));
+ if (!listmode)
+ {
+ log_info (" (expired at ");
+ dump_isotime (not_after);
+ log_printf (")\n");
+ }
+ if (opt.ignore_expiration)
+ log_info ("WARNING: ignoring expiration\n");
+ else
+ return gpg_error (GPG_ERR_CERT_EXPIRED);
+ }
+
+ return 0;
+}
+
+/* This is a variant of check_validity_period used with the chain
+ model. The dextra contraint here is that notBefore and notAfter
+ must exists and if the additional argument CHECK_TIME is given this
+ time is used to check the validity period of SUBJECT_CERT. */
+static gpg_error_t
+check_validity_period_cm (ksba_isotime_t current_time,
+ ksba_isotime_t check_time,
+ ksba_cert_t subject_cert,
+ ksba_isotime_t exptime,
+ int listmode, estream_t listfp, int depth)
+{
+ gpg_error_t err;
+ ksba_isotime_t not_before, not_after;
+
+ err = ksba_cert_get_validity (subject_cert, 0, not_before);
+ if (!err)
+ err = ksba_cert_get_validity (subject_cert, 1, not_after);
+ if (err)
+ {
+ do_list (1, listmode, listfp,
+ _("certificate with invalid validity: %s"), gpg_strerror (err));
+ return gpg_error (GPG_ERR_BAD_CERT);
+ }
+ if (!*not_before || !*not_after)
+ {
+ do_list (1, listmode, listfp,
+ _("required certificate attributes missing: %s%s%s"),
+ !*not_before? "notBefore":"",
+ (!*not_before && !*not_after)? ", ":"",
+ !*not_before? "notAfter":"");
+ return gpg_error (GPG_ERR_BAD_CERT);
+ }
+ if (strcmp (not_before, not_after) > 0 )
+ {
+ do_list (1, listmode, listfp,
+ _("certificate with invalid validity"));
+ log_info (" (valid from ");
+ dump_isotime (not_before);
+ log_printf (" expired at ");
+ dump_isotime (not_after);
+ log_printf (")\n");
+ return gpg_error (GPG_ERR_BAD_CERT);
+ }
+
+ if (!*exptime)
+ gnupg_copy_time (exptime, not_after);
+ else if (strcmp (not_after, exptime) < 0 )
+ gnupg_copy_time (exptime, not_after);
+
+ if (strcmp (current_time, not_before) < 0 )
+ {
+ do_list (1, listmode, listfp,
+ depth == 0 ? _("certificate not yet valid") :
+ depth == -1 ? _("root certificate not yet valid") :
+ /* other */ _("intermediate certificate not yet valid"));
+ if (!listmode)
+ {
+ log_info (" (valid from ");
+ dump_isotime (not_before);
+ log_printf (")\n");
+ }
+ return gpg_error (GPG_ERR_CERT_TOO_YOUNG);
+ }
+
+ if (*check_time
+ && (strcmp (check_time, not_before) < 0
+ || strcmp (check_time, not_after) > 0))
+ {
+ /* Note that we don't need a case for the root certificate
+ because its own consitency has already been checked. */
+ do_list(opt.ignore_expiration?0:1, listmode, listfp,
+ depth == 0 ?
+ _("signature not created during lifetime of certificate") :
+ depth == 1 ?
+ _("certificate not created during lifetime of issuer") :
+ _("intermediate certificate not created during lifetime "
+ "of issuer"));
+ if (!listmode)
+ {
+ log_info (depth== 0? _(" ( signature created at ") :
+ /* */ _(" (certificate created at ") );
+ dump_isotime (check_time);
+ log_printf (")\n");
+ log_info (depth==0? _(" (certificate valid from ") :
+ /* */ _(" ( issuer valid from ") );
+ dump_isotime (not_before);
+ log_info (" to ");
+ dump_isotime (not_after);
+ log_printf (")\n");
+ }
+ if (opt.ignore_expiration)
+ log_info ("WARNING: ignoring expiration\n");
+ else
+ return gpg_error (GPG_ERR_CERT_EXPIRED);
+ }
+
+ return 0;
+}
+
+
+
+/* Ask the user whether he wants to mark the certificate CERT trusted.
+ Returns true if the CERT is the trusted. We also check whether the
+ agent is at all enabled to allow marktrusted and don't call it in
+ this session again if it is not. */
+static int
+ask_marktrusted (ctrl_t ctrl, ksba_cert_t cert, int listmode)
+{
+ static int no_more_questions;
+ int rc;
+ char *fpr;
+ int success = 0;
+
+ fpr = gpgsm_get_fingerprint_string (cert, GCRY_MD_SHA1);
+ log_info (_("fingerprint=%s\n"), fpr? fpr : "?");
+ xfree (fpr);
+
+ if (no_more_questions)
+ rc = gpg_error (GPG_ERR_NOT_SUPPORTED);
+ else
+ rc = gpgsm_agent_marktrusted (ctrl, cert);
+ if (!rc)
+ {
+ log_info (_("root certificate has now been marked as trusted\n"));
+ success = 1;
+ }
+ else if (!listmode)
+ {
+ gpgsm_dump_cert ("issuer", cert);
+ log_info ("after checking the fingerprint, you may want "
+ "to add it manually to the list of trusted certificates.\n");
+ }
+
+ if (gpg_err_code (rc) == GPG_ERR_NOT_SUPPORTED)
+ {
+ if (!no_more_questions)
+ log_info (_("interactive marking as trusted "
+ "not enabled in gpg-agent\n"));
+ no_more_questions = 1;
+ }
+ else if (gpg_err_code (rc) == GPG_ERR_CANCELED)
+ {
+ log_info (_("interactive marking as trusted "
+ "disabled for this session\n"));
+ no_more_questions = 1;
+ }
+ else
+ set_already_asked_marktrusted (cert);
+
+ return success;
+}
+
+
+
+
+/* Validate a chain and optionally return the nearest expiration time
+ in R_EXPTIME. With LISTMODE set to 1 a special listmode is
+ activated where only information about the certificate is printed
+ to LISTFP and no output is send to the usual log stream. If
+ CHECKTIME_ARG is set, it is used only in the chain model instead of the
+ current time.
+
+ Defined flag bits
+
+ VALIDATE_FLAG_NO_DIRMNGR - Do not do any dirmngr isvalid checks.
+ VALIDATE_FLAG_CHAIN_MODEL - Check according to chain model.
+*/
+static int
+do_validate_chain (ctrl_t ctrl, ksba_cert_t cert, ksba_isotime_t checktime_arg,
+ ksba_isotime_t r_exptime,
+ int listmode, estream_t listfp, unsigned int flags,
+ struct rootca_flags_s *rootca_flags)
+{
+ int rc = 0, depth, maxdepth;
+ char *issuer = NULL;
+ char *subject = NULL;
+ KEYDB_HANDLE kh = NULL;
+ ksba_cert_t subject_cert = NULL, issuer_cert = NULL;
+ ksba_isotime_t current_time;
+ ksba_isotime_t check_time;
+ ksba_isotime_t exptime;
+ int any_expired = 0;
+ int any_revoked = 0;
+ int any_no_crl = 0;
+ int any_crl_too_old = 0;
+ int any_no_policy_match = 0;
+ int is_qualified = -1; /* Indicates whether the certificate stems
+ from a qualified root certificate.
+ -1 = unknown, 0 = no, 1 = yes. */
+ chain_item_t chain = NULL; /* A list of all certificates in the chain. */
+
+
+ gnupg_get_isotime (current_time);
+
+ if ( (flags & VALIDATE_FLAG_CHAIN_MODEL) )
+ {
+ if (!strcmp (checktime_arg, "19700101T000000"))
+ {
+ do_list (1, listmode, listfp,
+ _("WARNING: creation time of signature not known - "
+ "assuming current time"));
+ gnupg_copy_time (check_time, current_time);
+ }
+ else
+ gnupg_copy_time (check_time, checktime_arg);
+ }
+ else
+ *check_time = 0;
+
+ if (r_exptime)
+ *r_exptime = 0;
+ *exptime = 0;
+
+ if (opt.no_chain_validation && !listmode)
+ {
+ log_info ("WARNING: bypassing certificate chain validation\n");
+ return 0;
+ }
+
+ kh = keydb_new (0);
+ if (!kh)
+ {
+ log_error (_("failed to allocated keyDB handle\n"));
+ rc = gpg_error (GPG_ERR_GENERAL);
+ goto leave;
+ }
+
+ if (DBG_X509 && !listmode)
+ gpgsm_dump_cert ("target", cert);
+
+ subject_cert = cert;
+ ksba_cert_ref (subject_cert);
+ maxdepth = 50;
+ depth = 0;
+
+ for (;;)
+ {
+ int is_root;
+ gpg_error_t istrusted_rc = -1;
+
+ /* Put the certificate on our list. */
+ {
+ chain_item_t ci;
+
+ ci = xtrycalloc (1, sizeof *ci);
+ if (!ci)
+ {
+ rc = gpg_error_from_syserror ();
+ goto leave;
+ }
+ ksba_cert_ref (subject_cert);
+ ci->cert = subject_cert;
+ ci->next = chain;
+ chain = ci;
+ }
+
+ xfree (issuer);
+ xfree (subject);
+ issuer = ksba_cert_get_issuer (subject_cert, 0);
+ subject = ksba_cert_get_subject (subject_cert, 0);
+
+ if (!issuer)
+ {
+ do_list (1, listmode, listfp, _("no issuer found in certificate"));
+ rc = gpg_error (GPG_ERR_BAD_CERT);
+ goto leave;
+ }
+
+
+ /* Is this a self-issued certificate (i.e. the root certificate)? */
+ is_root = is_root_cert (subject_cert, issuer, subject);
+ if (is_root)
+ {
+ chain->is_root = 1;
+ /* Check early whether the certificate is listed as trusted.
+ We used to do this only later but changed it to call the
+ check right here so that we can access special flags
+ associated with that specific root certificate. */
+ istrusted_rc = gpgsm_agent_istrusted (ctrl, subject_cert, NULL,
+ rootca_flags);
+ audit_log_cert (ctrl->audit, AUDIT_ROOT_TRUSTED,
+ subject_cert, istrusted_rc);
+ /* If the chain model extended attribute is used, make sure
+ that our chain model flag is set. */
+ if (has_validation_model_chain (subject_cert, listmode, listfp))
+ rootca_flags->chain_model = 1;
+ }
+
+
+ /* Check the validity period. */
+ if ( (flags & VALIDATE_FLAG_CHAIN_MODEL) )
+ rc = check_validity_period_cm (current_time, check_time, subject_cert,
+ exptime, listmode, listfp,
+ (depth && is_root)? -1: depth);
+ else
+ rc = check_validity_period (current_time, subject_cert,
+ exptime, listmode, listfp,
+ (depth && is_root)? -1: depth);
+ if (gpg_err_code (rc) == GPG_ERR_CERT_EXPIRED)
+ {
+ any_expired = 1;
+ rc = 0;
+ }
+ else if (rc)
+ goto leave;
+
+
+ /* Assert that we understand all critical extensions. */
+ rc = unknown_criticals (subject_cert, listmode, listfp);
+ if (rc)
+ goto leave;
+
+ /* Do a policy check. */
+ if (!opt.no_policy_check)
+ {
+ rc = check_cert_policy (subject_cert, listmode, listfp);
+ if (gpg_err_code (rc) == GPG_ERR_NO_POLICY_MATCH)
+ {
+ any_no_policy_match = 1;
+ rc = 1;
+ }
+ else if (rc)
+ goto leave;
+ }
+
+
+ /* If this is the root certificate we are at the end of the chain. */
+ if (is_root)
+ {
+ if (!istrusted_rc)
+ ; /* No need to check the certificate for a trusted one. */
+ else if (gpgsm_check_cert_sig (subject_cert, subject_cert) )
+ {
+ /* We only check the signature if the certificate is not
+ trusted for better diagnostics. */
+ do_list (1, listmode, listfp,
+ _("self-signed certificate has a BAD signature"));
+ if (DBG_X509)
+ {
+ gpgsm_dump_cert ("self-signing cert", subject_cert);
+ }
+ rc = gpg_error (depth? GPG_ERR_BAD_CERT_CHAIN
+ : GPG_ERR_BAD_CERT);
+ goto leave;
+ }
+ if (!rootca_flags->relax)
+ {
+ rc = allowed_ca (ctrl, subject_cert, NULL, listmode, listfp);
+ if (rc)
+ goto leave;
+ }
+
+
+ /* Set the flag for qualified signatures. This flag is
+ deduced from a list of root certificates allowed for
+ qualified signatures. */
+ if (is_qualified == -1)
+ {
+ gpg_error_t err;
+ size_t buflen;
+ char buf[1];
+
+ if (!ksba_cert_get_user_data (cert, "is_qualified",
+ &buf, sizeof (buf),
+ &buflen) && buflen)
+ {
+ /* We already checked this for this certificate,
+ thus we simply take it from the user data. */
+ is_qualified = !!*buf;
+ }
+ else
+ {
+ /* Need to consult the list of root certificates for
+ qualified signatures. */
+ err = gpgsm_is_in_qualified_list (ctrl, subject_cert, NULL);
+ if (!err)
+ is_qualified = 1;
+ else if ( gpg_err_code (err) == GPG_ERR_NOT_FOUND)
+ is_qualified = 0;
+ else
+ log_error ("checking the list of qualified "
+ "root certificates failed: %s\n",
+ gpg_strerror (err));
+ if ( is_qualified != -1 )
+ {
+ /* Cache the result but don't care too much
+ about an error. */
+ buf[0] = !!is_qualified;
+ err = ksba_cert_set_user_data (subject_cert,
+ "is_qualified", buf, 1);
+ if (err)
+ log_error ("set_user_data(is_qualified) failed: %s\n",
+ gpg_strerror (err));
+ }
+ }
+ }
+
+
+ /* Act on the check for a trusted root certificates. */
+ rc = istrusted_rc;
+ if (!rc)
+ ;
+ else if (gpg_err_code (rc) == GPG_ERR_NOT_TRUSTED)
+ {
+ do_list (0, listmode, listfp,
+ _("root certificate is not marked trusted"));
+ /* If we already figured out that the certificate is
+ expired it does not make much sense to ask the user
+ whether we wants to trust the root certificate. We
+ should do this only if the certificate under question
+ will then be usable. */
+ if ( !any_expired
+ && (!listmode || !already_asked_marktrusted (subject_cert))
+ && ask_marktrusted (ctrl, subject_cert, listmode) )
+ rc = 0;
+ }
+ else
+ {
+ log_error (_("checking the trust list failed: %s\n"),
+ gpg_strerror (rc));
+ }
+
+ if (rc)
+ goto leave;
+
+ /* Check for revocations etc. */
+ if ((flags & VALIDATE_FLAG_NO_DIRMNGR))
+ ;
+ else if (opt.no_trusted_cert_crl_check || rootca_flags->relax)
+ ;
+ else
+ rc = is_cert_still_valid (ctrl,
+ (flags & VALIDATE_FLAG_CHAIN_MODEL),
+ listmode, listfp,
+ subject_cert, subject_cert,
+ &any_revoked, &any_no_crl,
+ &any_crl_too_old);
+ if (rc)
+ goto leave;
+
+ break; /* Okay: a self-signed certicate is an end-point. */
+ } /* End is_root. */
+
+
+ /* Take care that the chain does not get too long. */
+ if ((depth+1) > maxdepth)
+ {
+ do_list (1, listmode, listfp, _("certificate chain too long\n"));
+ rc = gpg_error (GPG_ERR_BAD_CERT_CHAIN);
+ goto leave;
+ }
+
+ /* Find the next cert up the tree. */
+ keydb_search_reset (kh);
+ rc = find_up (ctrl, kh, subject_cert, issuer, 0);
+ if (rc)
+ {
+ if (rc == -1)
+ {
+ do_list (0, listmode, listfp, _("issuer certificate not found"));
+ if (!listmode)
+ {
+ log_info ("issuer certificate: #/");
+ gpgsm_dump_string (issuer);
+ log_printf ("\n");
+ }
+ }
+ else
+ log_error ("failed to find issuer's certificate: rc=%d\n", rc);
+ rc = gpg_error (GPG_ERR_MISSING_ISSUER_CERT);
+ goto leave;
+ }
+
+ ksba_cert_release (issuer_cert); issuer_cert = NULL;
+ rc = keydb_get_cert (kh, &issuer_cert);
+ if (rc)
+ {
+ log_error ("keydb_get_cert() failed: rc=%d\n", rc);
+ rc = gpg_error (GPG_ERR_GENERAL);
+ goto leave;
+ }
+
+ try_another_cert:
+ if (DBG_X509)
+ {
+ log_debug ("got issuer's certificate:\n");
+ gpgsm_dump_cert ("issuer", issuer_cert);
+ }
+
+ rc = gpgsm_check_cert_sig (issuer_cert, subject_cert);
+ if (rc)
+ {
+ do_list (0, listmode, listfp, _("certificate has a BAD signature"));
+ if (DBG_X509)
+ {
+ gpgsm_dump_cert ("signing issuer", issuer_cert);
+ gpgsm_dump_cert ("signed subject", subject_cert);
+ }
+ if (gpg_err_code (rc) == GPG_ERR_BAD_SIGNATURE)
+ {
+ /* We now try to find other issuer certificates which
+ might have been used. This is required because some
+ CAs are reusing the issuer and subject DN for new
+ root certificates. */
+ /* FIXME: Do this only if we don't have an
+ AKI.keyIdentifier */
+ rc = find_up (ctrl, kh, subject_cert, issuer, 1);
+ if (!rc)
+ {
+ ksba_cert_t tmp_cert;
+
+ rc = keydb_get_cert (kh, &tmp_cert);
+ if (rc || !compare_certs (issuer_cert, tmp_cert))
+ {
+ /* The find next did not work or returned an
+ identical certificate. We better stop here
+ to avoid infinite checks. */
+ rc = gpg_error (GPG_ERR_BAD_SIGNATURE);
+ ksba_cert_release (tmp_cert);
+ }
+ else
+ {
+ do_list (0, listmode, listfp,
+ _("found another possible matching "
+ "CA certificate - trying again"));
+ ksba_cert_release (issuer_cert);
+ issuer_cert = tmp_cert;
+ goto try_another_cert;
+ }
+ }
+ }
+
+ /* We give a more descriptive error code than the one
+ returned from the signature checking. */
+ rc = gpg_error (GPG_ERR_BAD_CERT_CHAIN);
+ goto leave;
+ }
+
+ is_root = gpgsm_is_root_cert (issuer_cert);
+ istrusted_rc = -1;
+
+
+ /* Check that a CA is allowed to issue certificates. */
+ {
+ int chainlen;
+
+ rc = allowed_ca (ctrl, issuer_cert, &chainlen, listmode, listfp);
+ if (rc)
+ {
+ /* Not allowed. Check whether this is a trusted root
+ certificate and whether we allow special exceptions.
+ We could carry the result of the test over to the
+ regular root check at the top of the loop but for
+ clarity we won't do that. Given that the majority of
+ certificates carry proper BasicContraints our way of
+ overriding an error in the way is justified for
+ performance reasons. */
+ if (is_root)
+ {
+ istrusted_rc = gpgsm_agent_istrusted (ctrl, issuer_cert, NULL,
+ rootca_flags);
+ if (!istrusted_rc && rootca_flags->relax)
+ {
+ /* Ignore the error due to the relax flag. */
+ rc = 0;
+ chainlen = -1;
+ }
+ }
+ }
+ if (rc)
+ goto leave;
+ if (chainlen >= 0 && depth > chainlen)
+ {
+ do_list (1, listmode, listfp,
+ _("certificate chain longer than allowed by CA (%d)"),
+ chainlen);
+ rc = gpg_error (GPG_ERR_BAD_CERT_CHAIN);
+ goto leave;
+ }
+ }
+
+ /* Is the certificate allowed to sign other certificates. */
+ if (!listmode)
+ {
+ rc = gpgsm_cert_use_cert_p (issuer_cert);
+ if (rc)
+ {
+ char numbuf[50];
+ sprintf (numbuf, "%d", rc);
+ gpgsm_status2 (ctrl, STATUS_ERROR, "certcert.issuer.keyusage",
+ numbuf, NULL);
+ goto leave;
+ }
+ }
+
+ /* Check for revocations etc. Note that for a root certificate
+ this test is done a second time later. This should eventually
+ be fixed. */
+ if ((flags & VALIDATE_FLAG_NO_DIRMNGR))
+ rc = 0;
+ else if (is_root && (opt.no_trusted_cert_crl_check
+ || (!istrusted_rc && rootca_flags->relax)))
+ rc = 0;
+ else
+ rc = is_cert_still_valid (ctrl,
+ (flags & VALIDATE_FLAG_CHAIN_MODEL),
+ listmode, listfp,
+ subject_cert, issuer_cert,
+ &any_revoked, &any_no_crl, &any_crl_too_old);
+ if (rc)
+ goto leave;
+
+
+ if (opt.verbose && !listmode)
+ log_info (depth == 0 ? _("certificate is good\n") :
+ !is_root ? _("intermediate certificate is good\n") :
+ /* other */ _("root certificate is good\n"));
+
+ /* Under the chain model the next check time is the creation
+ time of the subject certificate. */
+ if ( (flags & VALIDATE_FLAG_CHAIN_MODEL) )
+ {
+ rc = ksba_cert_get_validity (subject_cert, 0, check_time);
+ if (rc)
+ {
+ /* That will never happen as we have already checked
+ this above. */
+ BUG ();
+ }
+ }
+
+ /* For the next round the current issuer becomes the new subject. */
+ keydb_search_reset (kh);
+ ksba_cert_release (subject_cert);
+ subject_cert = issuer_cert;
+ issuer_cert = NULL;
+ depth++;
+ } /* End chain traversal. */
+
+ if (!listmode && !opt.quiet)
+ {
+ if (opt.no_policy_check)
+ log_info ("policies not checked due to %s option\n",
+ "--disable-policy-checks");
+ if (opt.no_crl_check && !ctrl->use_ocsp)
+ log_info ("CRLs not checked due to %s option\n",
+ "--disable-crl-checks");
+ }
+
+ if (!rc)
+ { /* If we encountered an error somewhere during the checks, set
+ the error code to the most critical one */
+ if (any_revoked)
+ rc = gpg_error (GPG_ERR_CERT_REVOKED);
+ else if (any_expired)
+ rc = gpg_error (GPG_ERR_CERT_EXPIRED);
+ else if (any_no_crl)
+ rc = gpg_error (GPG_ERR_NO_CRL_KNOWN);
+ else if (any_crl_too_old)
+ rc = gpg_error (GPG_ERR_CRL_TOO_OLD);
+ else if (any_no_policy_match)
+ rc = gpg_error (GPG_ERR_NO_POLICY_MATCH);
+ }
+
+ leave:
+ /* If we have traversed a complete chain up to the root we will
+ reset the ephemeral flag for all these certificates. This is done
+ regardless of any error because those errors may only be
+ transient. */
+ if (chain && chain->is_root)
+ {
+ gpg_error_t err;
+ chain_item_t ci;
+
+ for (ci = chain; ci; ci = ci->next)
+ {
+ /* Note that it is possible for the last certificate in the
+ chain (i.e. our target certificate) that it has not yet
+ been stored in the keybox and thus the flag can't be set.
+ We ignore this error becuase it will later be stored
+ anyway. */
+ err = keydb_set_cert_flags (ci->cert, 1, KEYBOX_FLAG_BLOB, 0,
+ KEYBOX_FLAG_BLOB_EPHEMERAL, 0);
+ if (!ci->next && gpg_err_code (err) == GPG_ERR_NOT_FOUND)
+ ;
+ else if (err)
+ log_error ("clearing ephemeral flag failed: %s\n",
+ gpg_strerror (err));
+ }
+ }
+
+ /* If we have figured something about the qualified signature
+ capability of the certificate under question, store the result as
+ user data in all certificates of the chain. We do this even if the
+ validation itself failed. */
+ if (is_qualified != -1)
+ {
+ gpg_error_t err;
+ chain_item_t ci;
+ char buf[1];
+
+ buf[0] = !!is_qualified;
+
+ for (ci = chain; ci; ci = ci->next)
+ {
+ err = ksba_cert_set_user_data (ci->cert, "is_qualified", buf, 1);
+ if (err)
+ {
+ log_error ("set_user_data(is_qualified) failed: %s\n",
+ gpg_strerror (err));
+ if (!rc)
+ rc = err;
+ }
+ }
+ }
+
+ /* If auditing has been enabled, record what is in the chain. */
+ if (ctrl->audit)
+ {
+ chain_item_t ci;
+
+ audit_log (ctrl->audit, AUDIT_CHAIN_BEGIN);
+ for (ci = chain; ci; ci = ci->next)
+ {
+ audit_log_cert (ctrl->audit,
+ ci->is_root? AUDIT_CHAIN_ROOTCERT : AUDIT_CHAIN_CERT,
+ ci->cert, 0);
+ }
+ audit_log (ctrl->audit, AUDIT_CHAIN_END);
+ }
+
+ if (r_exptime)
+ gnupg_copy_time (r_exptime, exptime);
+ xfree (issuer);
+ xfree (subject);
+ keydb_release (kh);
+ while (chain)
+ {
+ chain_item_t ci_next = chain->next;
+ ksba_cert_release (chain->cert);
+ xfree (chain);
+ chain = ci_next;
+ }
+ ksba_cert_release (issuer_cert);
+ ksba_cert_release (subject_cert);
+ return rc;
+}
+
+
+/* Validate a certificate chain. For a description see
+ do_validate_chain. This function is a wrapper to handle a root
+ certificate with the chain_model flag set. If RETFLAGS is not
+ NULL, flags indicating now the verification was done are stored
+ there. The only defined flag for RETFLAGS is
+ VALIDATE_FLAG_CHAIN_MODEL.
+
+ If you are verifying a signature you should set CHECKTIME to the
+ creation time of the signature. If your are verifying a
+ certificate, set it nil (i.e. the empty string). If the creation
+ date of the signature is not known use the special date
+ "19700101T000000" which is treated in a special way here. */
+int
+gpgsm_validate_chain (ctrl_t ctrl, ksba_cert_t cert, ksba_isotime_t checktime,
+ ksba_isotime_t r_exptime,
+ int listmode, estream_t listfp, unsigned int flags,
+ unsigned int *retflags)
+{
+ int rc;
+ struct rootca_flags_s rootca_flags;
+ unsigned int dummy_retflags;
+
+ if (!retflags)
+ retflags = &dummy_retflags;
+
+ if (ctrl->validation_model == 1)
+ flags |= VALIDATE_FLAG_CHAIN_MODEL;
+
+ *retflags = (flags & VALIDATE_FLAG_CHAIN_MODEL);
+ memset (&rootca_flags, 0, sizeof rootca_flags);
+
+ rc = do_validate_chain (ctrl, cert, checktime,
+ r_exptime, listmode, listfp, flags,
+ &rootca_flags);
+ if (gpg_err_code (rc) == GPG_ERR_CERT_EXPIRED
+ && !(flags & VALIDATE_FLAG_CHAIN_MODEL)
+ && (rootca_flags.valid && rootca_flags.chain_model))
+ {
+ do_list (0, listmode, listfp, _("switching to chain model"));
+ rc = do_validate_chain (ctrl, cert, checktime,
+ r_exptime, listmode, listfp,
+ (flags |= VALIDATE_FLAG_CHAIN_MODEL),
+ &rootca_flags);
+ *retflags |= VALIDATE_FLAG_CHAIN_MODEL;
+ }
+
+ if (opt.verbose)
+ do_list (0, listmode, listfp, _("validation model used: %s"),
+ (*retflags & VALIDATE_FLAG_CHAIN_MODEL)?
+ _("chain"):_("shell"));
+
+ return rc;
+}
+
+
+/* Check that the given certificate is valid but DO NOT check any
+ constraints. We assume that the issuers certificate is already in
+ the DB and that this one is valid; which it should be because it
+ has been checked using this function. */
+int
+gpgsm_basic_cert_check (ctrl_t ctrl, ksba_cert_t cert)
+{
+ int rc = 0;
+ char *issuer = NULL;
+ char *subject = NULL;
+ KEYDB_HANDLE kh;
+ ksba_cert_t issuer_cert = NULL;
+
+ if (opt.no_chain_validation)
+ {
+ log_info ("WARNING: bypassing basic certificate checks\n");
+ return 0;
+ }
+
+ kh = keydb_new (0);
+ if (!kh)
+ {
+ log_error (_("failed to allocated keyDB handle\n"));
+ rc = gpg_error (GPG_ERR_GENERAL);
+ goto leave;
+ }
+
+ issuer = ksba_cert_get_issuer (cert, 0);
+ subject = ksba_cert_get_subject (cert, 0);
+ if (!issuer)
+ {
+ log_error ("no issuer found in certificate\n");
+ rc = gpg_error (GPG_ERR_BAD_CERT);
+ goto leave;
+ }
+
+ if (is_root_cert (cert, issuer, subject))
+ {
+ rc = gpgsm_check_cert_sig (cert, cert);
+ if (rc)
+ {
+ log_error ("self-signed certificate has a BAD signature: %s\n",
+ gpg_strerror (rc));
+ if (DBG_X509)
+ {
+ gpgsm_dump_cert ("self-signing cert", cert);
+ }
+ rc = gpg_error (GPG_ERR_BAD_CERT);
+ goto leave;
+ }
+ }
+ else
+ {
+ /* Find the next cert up the tree. */
+ keydb_search_reset (kh);
+ rc = find_up (ctrl, kh, cert, issuer, 0);
+ if (rc)
+ {
+ if (rc == -1)
+ {
+ log_info ("issuer certificate (#/");
+ gpgsm_dump_string (issuer);
+ log_printf (") not found\n");
+ }
+ else
+ log_error ("failed to find issuer's certificate: rc=%d\n", rc);
+ rc = gpg_error (GPG_ERR_MISSING_ISSUER_CERT);
+ goto leave;
+ }
+
+ ksba_cert_release (issuer_cert); issuer_cert = NULL;
+ rc = keydb_get_cert (kh, &issuer_cert);
+ if (rc)
+ {
+ log_error ("keydb_get_cert() failed: rc=%d\n", rc);
+ rc = gpg_error (GPG_ERR_GENERAL);
+ goto leave;
+ }
+
+ rc = gpgsm_check_cert_sig (issuer_cert, cert);
+ if (rc)
+ {
+ log_error ("certificate has a BAD signature: %s\n",
+ gpg_strerror (rc));
+ if (DBG_X509)
+ {
+ gpgsm_dump_cert ("signing issuer", issuer_cert);
+ gpgsm_dump_cert ("signed subject", cert);
+ }
+ rc = gpg_error (GPG_ERR_BAD_CERT);
+ goto leave;
+ }
+ if (opt.verbose)
+ log_info (_("certificate is good\n"));
+ }
+
+ leave:
+ xfree (issuer);
+ xfree (subject);
+ keydb_release (kh);
+ ksba_cert_release (issuer_cert);
+ return rc;
+}
+
+
+
+/* Check whether the certificate CERT has been issued by the German
+ authority for qualified signature. They do not set the
+ basicConstraints and thus we need this workaround. It works by
+ looking up the root certificate and checking whether that one is
+ listed as a qualified certificate for Germany.
+
+ We also try to cache this data but as long as don't keep a
+ reference to the certificate this won't be used.
+
+ Returns: True if CERT is a RegTP issued CA cert (i.e. the root
+ certificate itself or one of the CAs). In that case CHAINLEN will
+ receive the length of the chain which is either 0 or 1.
+*/
+static int
+get_regtp_ca_info (ctrl_t ctrl, ksba_cert_t cert, int *chainlen)
+{
+ gpg_error_t err;
+ ksba_cert_t next;
+ int rc = 0;
+ int i, depth;
+ char country[3];
+ ksba_cert_t array[4];
+ char buf[2];
+ size_t buflen;
+ int dummy_chainlen;
+
+ if (!chainlen)
+ chainlen = &dummy_chainlen;
+
+ *chainlen = 0;
+ err = ksba_cert_get_user_data (cert, "regtp_ca_chainlen",
+ &buf, sizeof (buf), &buflen);
+ if (!err)
+ {
+ /* Got info. */
+ if (buflen < 2 || !*buf)
+ return 0; /* Nothing found. */
+ *chainlen = buf[1];
+ return 1; /* This is a regtp CA. */
+ }
+ else if (gpg_err_code (err) != GPG_ERR_NOT_FOUND)
+ {
+ log_error ("ksba_cert_get_user_data(%s) failed: %s\n",
+ "regtp_ca_chainlen", gpg_strerror (err));
+ return 0; /* Nothing found. */
+ }
+
+ /* Need to gather the info. This requires to walk up the chain
+ until we have found the root. Because we are only interested in
+ German Bundesnetzagentur (former RegTP) derived certificates 3
+ levels are enough. (The German signature law demands a 3 tier
+ hierachy; thus there is only one CA between the EE and the Root
+ CA.) */
+ memset (&array, 0, sizeof array);
+
+ depth = 0;
+ ksba_cert_ref (cert);
+ array[depth++] = cert;
+ ksba_cert_ref (cert);
+ while (depth < DIM(array) && !(rc=gpgsm_walk_cert_chain (ctrl, cert, &next)))
+ {
+ ksba_cert_release (cert);
+ ksba_cert_ref (next);
+ array[depth++] = next;
+ cert = next;
+ }
+ ksba_cert_release (cert);
+ if (rc != -1 || !depth || depth == DIM(array) )
+ {
+ /* We did not reached the root. */
+ goto leave;
+ }
+
+ /* If this is a German signature law issued certificate, we store
+ additional additional information. */
+ if (!gpgsm_is_in_qualified_list (NULL, array[depth-1], country)
+ && !strcmp (country, "de"))
+ {
+ /* Setting the pathlen for the root CA and the CA flag for the
+ next one is all what we need to do. */
+ err = ksba_cert_set_user_data (array[depth-1], "regtp_ca_chainlen",
+ "\x01\x01", 2);
+ if (!err && depth > 1)
+ err = ksba_cert_set_user_data (array[depth-2], "regtp_ca_chainlen",
+ "\x01\x00", 2);
+ if (err)
+ log_error ("ksba_set_user_data(%s) failed: %s\n",
+ "regtp_ca_chainlen", gpg_strerror (err));
+ for (i=0; i < depth; i++)
+ ksba_cert_release (array[i]);
+ *chainlen = (depth>1? 0:1);
+ return 1;
+ }
+
+ leave:
+ /* Nothing special with this certificate. Mark the target
+ certificate anyway to avoid duplicate lookups. */
+ err = ksba_cert_set_user_data (cert, "regtp_ca_chainlen", "", 1);
+ if (err)
+ log_error ("ksba_set_user_data(%s) failed: %s\n",
+ "regtp_ca_chainlen", gpg_strerror (err));
+ for (i=0; i < depth; i++)
+ ksba_cert_release (array[i]);
+ return 0;
+}
diff --git a/sm/certcheck.c b/sm/certcheck.c
new file mode 100644
index 0000000..51a809b
--- /dev/null
+++ b/sm/certcheck.c
@@ -0,0 +1,439 @@
+/* certcheck.c - check one certificate
+ * Copyright (C) 2001, 2003, 2004 Free Software Foundation, Inc.
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuPG is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <time.h>
+#include <assert.h>
+
+#include "gpgsm.h"
+#include <gcrypt.h>
+#include <ksba.h>
+
+#include "keydb.h"
+#include "i18n.h"
+
+
+/* Return the number of bits of the Q parameter from the DSA key
+ KEY. */
+static unsigned int
+get_dsa_qbits (gcry_sexp_t key)
+{
+ gcry_sexp_t l1, l2;
+ gcry_mpi_t q;
+ unsigned int nbits;
+
+ l1 = gcry_sexp_find_token (key, "public-key", 0);
+ if (!l1)
+ return 0; /* Does not contain a key object. */
+ l2 = gcry_sexp_cadr (l1);
+ gcry_sexp_release (l1);
+ l1 = gcry_sexp_find_token (l2, "q", 1);
+ gcry_sexp_release (l2);
+ if (!l1)
+ return 0; /* Invalid object. */
+ q = gcry_sexp_nth_mpi (l1, 1, GCRYMPI_FMT_USG);
+ gcry_sexp_release (l1);
+ if (!q)
+ return 0; /* Missing value. */
+ nbits = gcry_mpi_get_nbits (q);
+ gcry_mpi_release (q);
+
+ return nbits;
+}
+
+
+static int
+do_encode_md (gcry_md_hd_t md, int algo, int pkalgo, unsigned int nbits,
+ gcry_sexp_t pkey, gcry_mpi_t *r_val)
+{
+ int n;
+ size_t nframe;
+ unsigned char *frame;
+
+ if (pkalgo == GCRY_PK_DSA || pkalgo == GCRY_PK_ECDSA)
+ {
+ unsigned int qbits;
+
+ if ( pkalgo == GCRY_PK_ECDSA )
+ qbits = gcry_pk_get_nbits (pkey);
+ else
+ qbits = get_dsa_qbits (pkey);
+
+ if ( (qbits%8) )
+ {
+ log_error(_("DSA requires the hash length to be a"
+ " multiple of 8 bits\n"));
+ return gpg_error (GPG_ERR_INTERNAL);
+ }
+
+ /* Don't allow any Q smaller than 160 bits. We don't want
+ someone to issue signatures from a key with a 16-bit Q or
+ something like that, which would look correct but allow
+ trivial forgeries. Yes, I know this rules out using MD5 with
+ DSA. ;) */
+ if (qbits < 160)
+ {
+ log_error (_("%s key uses an unsafe (%u bit) hash\n"),
+ gcry_pk_algo_name (pkalgo), qbits);
+ return gpg_error (GPG_ERR_INTERNAL);
+ }
+
+ /* Check if we're too short. Too long is safe as we'll
+ automatically left-truncate. */
+ nframe = gcry_md_get_algo_dlen (algo);
+ if (nframe < qbits/8)
+ {
+ log_error (_("a %u bit hash is not valid for a %u bit %s key\n"),
+ (unsigned int)nframe*8,
+ gcry_pk_get_nbits (pkey),
+ gcry_pk_algo_name (pkalgo));
+ /* FIXME: we need to check the requirements for ECDSA. */
+ if (nframe < 20 || pkalgo == GCRY_PK_DSA )
+ return gpg_error (GPG_ERR_INTERNAL);
+ }
+
+ frame = xtrymalloc (nframe);
+ if (!frame)
+ return out_of_core ();
+ memcpy (frame, gcry_md_read (md, algo), nframe);
+ n = nframe;
+ /* Truncate. */
+ if (n > qbits/8)
+ n = qbits/8;
+ }
+ else
+ {
+ int i;
+ unsigned char asn[100];
+ size_t asnlen;
+ size_t len;
+
+ nframe = (nbits+7) / 8;
+
+ asnlen = DIM(asn);
+ if (!algo || gcry_md_test_algo (algo))
+ return gpg_error (GPG_ERR_DIGEST_ALGO);
+ if (gcry_md_algo_info (algo, GCRYCTL_GET_ASNOID, asn, &asnlen))
+ {
+ log_error ("no object identifier for algo %d\n", algo);
+ return gpg_error (GPG_ERR_INTERNAL);
+ }
+
+ len = gcry_md_get_algo_dlen (algo);
+
+ if ( len + asnlen + 4 > nframe )
+ {
+ log_error ("can't encode a %d bit MD into a %d bits frame\n",
+ (int)(len*8), (int)nbits);
+ return gpg_error (GPG_ERR_INTERNAL);
+ }
+
+ /* We encode the MD in this way:
+ *
+ * 0 A PAD(n bytes) 0 ASN(asnlen bytes) MD(len bytes)
+ *
+ * PAD consists of FF bytes.
+ */
+ frame = xtrymalloc (nframe);
+ if (!frame)
+ return out_of_core ();
+ n = 0;
+ frame[n++] = 0;
+ frame[n++] = 1; /* block type */
+ i = nframe - len - asnlen -3 ;
+ assert ( i > 1 );
+ memset ( frame+n, 0xff, i ); n += i;
+ frame[n++] = 0;
+ memcpy ( frame+n, asn, asnlen ); n += asnlen;
+ memcpy ( frame+n, gcry_md_read(md, algo), len ); n += len;
+ assert ( n == nframe );
+ }
+ if (DBG_CRYPTO)
+ {
+ int j;
+ log_debug ("encoded hash:");
+ for (j=0; j < nframe; j++)
+ log_printf (" %02X", frame[j]);
+ log_printf ("\n");
+ }
+
+ gcry_mpi_scan (r_val, GCRYMPI_FMT_USG, frame, n, &nframe);
+ xfree (frame);
+ return 0;
+}
+
+/* Return the public key algorithm id from the S-expression PKEY.
+ FIXME: libgcrypt should provide such a function. Note that this
+ implementation uses the names as used by libksba. */
+static int
+pk_algo_from_sexp (gcry_sexp_t pkey)
+{
+ gcry_sexp_t l1, l2;
+ const char *name;
+ size_t n;
+ int algo;
+
+ l1 = gcry_sexp_find_token (pkey, "public-key", 0);
+ if (!l1)
+ return 0; /* Not found. */
+ l2 = gcry_sexp_cadr (l1);
+ gcry_sexp_release (l1);
+
+ name = gcry_sexp_nth_data (l2, 0, &n);
+ if (!name)
+ algo = 0; /* Not found. */
+ else if (n==3 && !memcmp (name, "rsa", 3))
+ algo = GCRY_PK_RSA;
+ else if (n==3 && !memcmp (name, "dsa", 3))
+ algo = GCRY_PK_DSA;
+ /* Because this function is called only for verification we can
+ assume that ECC actually means ECDSA. */
+ else if (n==3 && !memcmp (name, "ecc", 3))
+ algo = GCRY_PK_ECDSA;
+ else if (n==13 && !memcmp (name, "ambiguous-rsa", 13))
+ algo = GCRY_PK_RSA;
+ else
+ algo = 0;
+ gcry_sexp_release (l2);
+ return algo;
+}
+
+
+/* Check the signature on CERT using the ISSUER-CERT. This function
+ does only test the cryptographic signature and nothing else. It is
+ assumed that the ISSUER_CERT is valid. */
+int
+gpgsm_check_cert_sig (ksba_cert_t issuer_cert, ksba_cert_t cert)
+{
+ const char *algoid;
+ gcry_md_hd_t md;
+ int rc, algo;
+ gcry_mpi_t frame;
+ ksba_sexp_t p;
+ size_t n;
+ gcry_sexp_t s_sig, s_hash, s_pkey;
+
+ algo = gcry_md_map_name ( (algoid=ksba_cert_get_digest_algo (cert)));
+ if (!algo)
+ {
+ log_error ("unknown hash algorithm `%s'\n", algoid? algoid:"?");
+ if (algoid
+ && ( !strcmp (algoid, "1.2.840.113549.1.1.2")
+ ||!strcmp (algoid, "1.2.840.113549.2.2")))
+ log_info (_("(this is the MD2 algorithm)\n"));
+ return gpg_error (GPG_ERR_GENERAL);
+ }
+ rc = gcry_md_open (&md, algo, 0);
+ if (rc)
+ {
+ log_error ("md_open failed: %s\n", gpg_strerror (rc));
+ return rc;
+ }
+ if (DBG_HASHING)
+ gcry_md_start_debug (md, "hash.cert");
+
+ rc = ksba_cert_hash (cert, 1, HASH_FNC, md);
+ if (rc)
+ {
+ log_error ("ksba_cert_hash failed: %s\n", gpg_strerror (rc));
+ gcry_md_close (md);
+ return rc;
+ }
+ gcry_md_final (md);
+
+ p = ksba_cert_get_sig_val (cert);
+ n = gcry_sexp_canon_len (p, 0, NULL, NULL);
+ if (!n)
+ {
+ log_error ("libksba did not return a proper S-Exp\n");
+ gcry_md_close (md);
+ ksba_free (p);
+ return gpg_error (GPG_ERR_BUG);
+ }
+ if (DBG_CRYPTO)
+ {
+ int j;
+ log_debug ("signature value:");
+ for (j=0; j < n; j++)
+ log_printf (" %02X", p[j]);
+ log_printf ("\n");
+ }
+
+ rc = gcry_sexp_sscan ( &s_sig, NULL, (char*)p, n);
+ ksba_free (p);
+ if (rc)
+ {
+ log_error ("gcry_sexp_scan failed: %s\n", gpg_strerror (rc));
+ gcry_md_close (md);
+ return rc;
+ }
+
+ p = ksba_cert_get_public_key (issuer_cert);
+ n = gcry_sexp_canon_len (p, 0, NULL, NULL);
+ if (!n)
+ {
+ log_error ("libksba did not return a proper S-Exp\n");
+ gcry_md_close (md);
+ ksba_free (p);
+ gcry_sexp_release (s_sig);
+ return gpg_error (GPG_ERR_BUG);
+ }
+ rc = gcry_sexp_sscan ( &s_pkey, NULL, (char*)p, n);
+ ksba_free (p);
+ if (rc)
+ {
+ log_error ("gcry_sexp_scan failed: %s\n", gpg_strerror (rc));
+ gcry_md_close (md);
+ gcry_sexp_release (s_sig);
+ return rc;
+ }
+
+ rc = do_encode_md (md, algo, pk_algo_from_sexp (s_pkey),
+ gcry_pk_get_nbits (s_pkey), s_pkey, &frame);
+ if (rc)
+ {
+ gcry_md_close (md);
+ gcry_sexp_release (s_sig);
+ gcry_sexp_release (s_pkey);
+ return rc;
+ }
+
+ /* put hash into the S-Exp s_hash */
+ if ( gcry_sexp_build (&s_hash, NULL, "%m", frame) )
+ BUG ();
+ gcry_mpi_release (frame);
+
+
+ rc = gcry_pk_verify (s_sig, s_hash, s_pkey);
+ if (DBG_X509)
+ log_debug ("gcry_pk_verify: %s\n", gpg_strerror (rc));
+ gcry_md_close (md);
+ gcry_sexp_release (s_sig);
+ gcry_sexp_release (s_hash);
+ gcry_sexp_release (s_pkey);
+ return rc;
+}
+
+
+
+int
+gpgsm_check_cms_signature (ksba_cert_t cert, ksba_const_sexp_t sigval,
+ gcry_md_hd_t md, int mdalgo, int *r_pkalgo)
+{
+ int rc;
+ ksba_sexp_t p;
+ gcry_mpi_t frame;
+ gcry_sexp_t s_sig, s_hash, s_pkey;
+ size_t n;
+ int pkalgo;
+
+ if (r_pkalgo)
+ *r_pkalgo = 0;
+
+ n = gcry_sexp_canon_len (sigval, 0, NULL, NULL);
+ if (!n)
+ {
+ log_error ("libksba did not return a proper S-Exp\n");
+ return gpg_error (GPG_ERR_BUG);
+ }
+ rc = gcry_sexp_sscan (&s_sig, NULL, (char*)sigval, n);
+ if (rc)
+ {
+ log_error ("gcry_sexp_scan failed: %s\n", gpg_strerror (rc));
+ return rc;
+ }
+
+ p = ksba_cert_get_public_key (cert);
+ n = gcry_sexp_canon_len (p, 0, NULL, NULL);
+ if (!n)
+ {
+ log_error ("libksba did not return a proper S-Exp\n");
+ ksba_free (p);
+ gcry_sexp_release (s_sig);
+ return gpg_error (GPG_ERR_BUG);
+ }
+ if (DBG_CRYPTO)
+ log_printhex ("public key: ", p, n);
+
+ rc = gcry_sexp_sscan ( &s_pkey, NULL, (char*)p, n);
+ ksba_free (p);
+ if (rc)
+ {
+ log_error ("gcry_sexp_scan failed: %s\n", gpg_strerror (rc));
+ gcry_sexp_release (s_sig);
+ return rc;
+ }
+
+ pkalgo = pk_algo_from_sexp (s_pkey);
+ if (r_pkalgo)
+ *r_pkalgo = pkalgo;
+ rc = do_encode_md (md, mdalgo, pkalgo,
+ gcry_pk_get_nbits (s_pkey), s_pkey, &frame);
+ if (rc)
+ {
+ gcry_sexp_release (s_sig);
+ gcry_sexp_release (s_pkey);
+ return rc;
+ }
+ /* put hash into the S-Exp s_hash */
+ if ( gcry_sexp_build (&s_hash, NULL, "%m", frame) )
+ BUG ();
+ gcry_mpi_release (frame);
+
+ rc = gcry_pk_verify (s_sig, s_hash, s_pkey);
+ if (DBG_X509)
+ log_debug ("gcry_pk_verify: %s\n", gpg_strerror (rc));
+ gcry_sexp_release (s_sig);
+ gcry_sexp_release (s_hash);
+ gcry_sexp_release (s_pkey);
+ return rc;
+}
+
+
+
+int
+gpgsm_create_cms_signature (ctrl_t ctrl, ksba_cert_t cert,
+ gcry_md_hd_t md, int mdalgo,
+ unsigned char **r_sigval)
+{
+ int rc;
+ char *grip, *desc;
+ size_t siglen;
+
+ grip = gpgsm_get_keygrip_hexstring (cert);
+ if (!grip)
+ return gpg_error (GPG_ERR_BAD_CERT);
+
+ desc = gpgsm_format_keydesc (cert);
+
+ rc = gpgsm_agent_pksign (ctrl, grip, desc, gcry_md_read(md, mdalgo),
+ gcry_md_get_algo_dlen (mdalgo), mdalgo,
+ r_sigval, &siglen);
+ xfree (desc);
+ xfree (grip);
+ return rc;
+}
+
+
+
diff --git a/sm/certdump.c b/sm/certdump.c
new file mode 100644
index 0000000..d339070
--- /dev/null
+++ b/sm/certdump.c
@@ -0,0 +1,974 @@
+/* certdump.c - Dump a certificate for debugging
+ * Copyright (C) 2001, 2004, 2007 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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuPG is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <time.h>
+#include <assert.h>
+#ifdef HAVE_LOCALE_H
+#include <locale.h>
+#endif
+#ifdef HAVE_LANGINFO_CODESET
+#include <langinfo.h>
+#endif
+
+#include "gpgsm.h"
+#include <gcrypt.h>
+#include <ksba.h>
+
+#include "keydb.h"
+#include "i18n.h"
+
+
+struct dn_array_s {
+ char *key;
+ char *value;
+ int multivalued;
+ int done;
+};
+
+
+/* Print the first element of an S-Expression. */
+void
+gpgsm_print_serial (estream_t fp, ksba_const_sexp_t sn)
+{
+ const char *p = (const char *)sn;
+ unsigned long n;
+ char *endp;
+
+ if (!p)
+ es_fputs (_("none"), fp);
+ else if (*p != '(')
+ es_fputs ("[Internal error - not an S-expression]", fp);
+ else
+ {
+ p++;
+ n = strtoul (p, &endp, 10);
+ p = endp;
+ if (*p++ != ':')
+ es_fputs ("[Internal Error - invalid S-expression]", fp);
+ else
+ es_write_hexstring (fp, p, n, 0, NULL);
+ }
+}
+
+
+/* Dump the serial number or any other simple S-expression. */
+void
+gpgsm_dump_serial (ksba_const_sexp_t sn)
+{
+ const char *p = (const char *)sn;
+ unsigned long n;
+ char *endp;
+
+ if (!p)
+ log_printf ("none");
+ else if (*p != '(')
+ log_printf ("ERROR - not an S-expression");
+ else
+ {
+ p++;
+ n = strtoul (p, &endp, 10);
+ p = endp;
+ if (*p!=':')
+ log_printf ("ERROR - invalid S-expression");
+ else
+ {
+ for (p++; n; n--, p++)
+ log_printf ("%02X", *(const unsigned char *)p);
+ }
+ }
+}
+
+
+char *
+gpgsm_format_serial (ksba_const_sexp_t sn)
+{
+ const char *p = (const char *)sn;
+ unsigned long n;
+ char *endp;
+ char *buffer;
+ int i;
+
+ if (!p)
+ return NULL;
+
+ if (*p != '(')
+ BUG (); /* Not a valid S-expression. */
+
+ p++;
+ n = strtoul (p, &endp, 10);
+ p = endp;
+ if (*p!=':')
+ BUG (); /* Not a valid S-expression. */
+ p++;
+
+ buffer = xtrymalloc (n*2+1);
+ if (buffer)
+ {
+ for (i=0; n; n--, p++, i+=2)
+ sprintf (buffer+i, "%02X", *(unsigned char *)p);
+ buffer[i] = 0;
+ }
+ return buffer;
+}
+
+
+
+
+void
+gpgsm_print_time (estream_t fp, ksba_isotime_t t)
+{
+ if (!t || !*t)
+ es_fputs (_("none"), fp);
+ else
+ es_fprintf (fp, "%.4s-%.2s-%.2s %.2s:%.2s:%s",
+ t, t+4, t+6, t+9, t+11, t+13);
+}
+
+
+void
+gpgsm_dump_string (const char *string)
+{
+
+ if (!string)
+ log_printf ("[error]");
+ else
+ {
+ const unsigned char *s;
+
+ for (s=(const unsigned char*)string; *s; s++)
+ {
+ if (*s < ' ' || (*s >= 0x7f && *s <= 0xa0))
+ break;
+ }
+ if (!*s && *string != '[')
+ log_printf ("%s", string);
+ else
+ {
+ log_printf ( "[ ");
+ log_printhex (NULL, string, strlen (string));
+ log_printf ( " ]");
+ }
+ }
+}
+
+
+/* This simple dump function is mainly used for debugging purposes. */
+void
+gpgsm_dump_cert (const char *text, ksba_cert_t cert)
+{
+ ksba_sexp_t sexp;
+ char *p;
+ char *dn;
+ ksba_isotime_t t;
+
+ log_debug ("BEGIN Certificate `%s':\n", text? text:"");
+ if (cert)
+ {
+ sexp = ksba_cert_get_serial (cert);
+ log_debug (" serial: ");
+ gpgsm_dump_serial (sexp);
+ ksba_free (sexp);
+ log_printf ("\n");
+
+ ksba_cert_get_validity (cert, 0, t);
+ log_debug (" notBefore: ");
+ dump_isotime (t);
+ log_printf ("\n");
+ ksba_cert_get_validity (cert, 1, t);
+ log_debug (" notAfter: ");
+ dump_isotime (t);
+ log_printf ("\n");
+
+ dn = ksba_cert_get_issuer (cert, 0);
+ log_debug (" issuer: ");
+ gpgsm_dump_string (dn);
+ ksba_free (dn);
+ log_printf ("\n");
+
+ dn = ksba_cert_get_subject (cert, 0);
+ log_debug (" subject: ");
+ gpgsm_dump_string (dn);
+ ksba_free (dn);
+ log_printf ("\n");
+
+ log_debug (" hash algo: %s\n", ksba_cert_get_digest_algo (cert));
+
+ p = gpgsm_get_fingerprint_string (cert, 0);
+ log_debug (" SHA1 Fingerprint: %s\n", p);
+ xfree (p);
+ }
+ log_debug ("END Certificate\n");
+}
+
+
+/* Return a new string holding the format serial number and issuer
+ ("#SN/issuer"). No filtering on invalid characters is done.
+ Caller must release the string. On memory failure NULL is
+ returned. */
+char *
+gpgsm_format_sn_issuer (ksba_sexp_t sn, const char *issuer)
+{
+ char *p, *p1;
+
+ if (sn && issuer)
+ {
+ p1 = gpgsm_format_serial (sn);
+ if (!p1)
+ p = xtrystrdup ("[invalid SN]");
+ else
+ {
+ p = xtrymalloc (strlen (p1) + strlen (issuer) + 2 + 1);
+ if (p)
+ {
+ *p = '#';
+ strcpy (stpcpy (stpcpy (p+1, p1),"/"), issuer);
+ }
+ xfree (p1);
+ }
+ }
+ else
+ p = xtrystrdup ("[invalid SN/issuer]");
+ return p;
+}
+
+
+/* Log the certificate's name in "#SN/ISSUERDN" format along with
+ TEXT. */
+void
+gpgsm_cert_log_name (const char *text, ksba_cert_t cert)
+{
+ log_info ("%s", text? text:"certificate" );
+ if (cert)
+ {
+ ksba_sexp_t sn;
+ char *p;
+
+ p = ksba_cert_get_issuer (cert, 0);
+ sn = ksba_cert_get_serial (cert);
+ if (p && sn)
+ {
+ log_printf (" #");
+ gpgsm_dump_serial (sn);
+ log_printf ("/");
+ gpgsm_dump_string (p);
+ }
+ else
+ log_printf (" [invalid]");
+ ksba_free (sn);
+ xfree (p);
+ }
+ log_printf ("\n");
+}
+
+
+
+
+
+
+/* helper for the rfc2253 string parser */
+static const unsigned char *
+parse_dn_part (struct dn_array_s *array, const unsigned char *string)
+{
+ static struct {
+ const char *label;
+ const char *oid;
+ } label_map[] = {
+ /* Warning: When adding new labels, make sure that the buffer
+ below we be allocated large enough. */
+ {"EMail", "1.2.840.113549.1.9.1" },
+ {"T", "2.5.4.12" },
+ {"GN", "2.5.4.42" },
+ {"SN", "2.5.4.4" },
+ {"NameDistinguisher", "0.2.262.1.10.7.20"},
+ {"ADDR", "2.5.4.16" },
+ {"BC", "2.5.4.15" },
+ {"D", "2.5.4.13" },
+ {"PostalCode", "2.5.4.17" },
+ {"Pseudo", "2.5.4.65" },
+ {"SerialNumber", "2.5.4.5" },
+ {NULL, NULL}
+ };
+ const unsigned char *s, *s1;
+ size_t n;
+ char *p;
+ int i;
+
+ /* Parse attributeType */
+ for (s = string+1; *s && *s != '='; s++)
+ ;
+ if (!*s)
+ return NULL; /* error */
+ n = s - string;
+ if (!n)
+ return NULL; /* empty key */
+
+ /* We need to allocate a few bytes more due to the possible mapping
+ from the shorter OID to the longer label. */
+ array->key = p = xtrymalloc (n+10);
+ if (!array->key)
+ return NULL;
+ memcpy (p, string, n);
+ p[n] = 0;
+ trim_trailing_spaces (p);
+
+ if (digitp (p))
+ {
+ for (i=0; label_map[i].label; i++ )
+ if ( !strcmp (p, label_map[i].oid) )
+ {
+ strcpy (p, label_map[i].label);
+ break;
+ }
+ }
+ string = s + 1;
+
+ if (*string == '#')
+ { /* hexstring */
+ string++;
+ for (s=string; hexdigitp (s); s++)
+ s++;
+ n = s - string;
+ if (!n || (n & 1))
+ return NULL; /* Empty or odd number of digits. */
+ n /= 2;
+ array->value = p = xtrymalloc (n+1);
+ if (!p)
+ return NULL;
+ for (s1=string; n; s1 += 2, n--, p++)
+ {
+ *(unsigned char *)p = xtoi_2 (s1);
+ if (!*p)
+ *p = 0x01; /* Better print a wrong value than truncating
+ the string. */
+ }
+ *p = 0;
+ }
+ else
+ { /* regular v3 quoted string */
+ for (n=0, s=string; *s; s++)
+ {
+ if (*s == '\\')
+ { /* pair */
+ s++;
+ if (*s == ',' || *s == '=' || *s == '+'
+ || *s == '<' || *s == '>' || *s == '#' || *s == ';'
+ || *s == '\\' || *s == '\"' || *s == ' ')
+ n++;
+ else if (hexdigitp (s) && hexdigitp (s+1))
+ {
+ s++;
+ n++;
+ }
+ else
+ return NULL; /* invalid escape sequence */
+ }
+ else if (*s == '\"')
+ return NULL; /* invalid encoding */
+ else if (*s == ',' || *s == '=' || *s == '+'
+ || *s == '<' || *s == '>' || *s == ';' )
+ break;
+ else
+ n++;
+ }
+
+ array->value = p = xtrymalloc (n+1);
+ if (!p)
+ return NULL;
+ for (s=string; n; s++, n--)
+ {
+ if (*s == '\\')
+ {
+ s++;
+ if (hexdigitp (s))
+ {
+ *(unsigned char *)p++ = xtoi_2 (s);
+ s++;
+ }
+ else
+ *p++ = *s;
+ }
+ else
+ *p++ = *s;
+ }
+ *p = 0;
+ }
+ return s;
+}
+
+
+/* Parse a DN and return an array-ized one. This is not a validating
+ parser and it does not support any old-stylish syntax; KSBA is
+ expected to return only rfc2253 compatible strings. */
+static struct dn_array_s *
+parse_dn (const unsigned char *string)
+{
+ struct dn_array_s *array;
+ size_t arrayidx, arraysize;
+ int i;
+
+ arraysize = 7; /* C,ST,L,O,OU,CN,email */
+ arrayidx = 0;
+ array = xtrymalloc ((arraysize+1) * sizeof *array);
+ if (!array)
+ return NULL;
+ while (*string)
+ {
+ while (*string == ' ')
+ string++;
+ if (!*string)
+ break; /* ready */
+ if (arrayidx >= arraysize)
+ {
+ struct dn_array_s *a2;
+
+ arraysize += 5;
+ a2 = xtryrealloc (array, (arraysize+1) * sizeof *array);
+ if (!a2)
+ goto failure;
+ array = a2;
+ }
+ array[arrayidx].key = NULL;
+ array[arrayidx].value = NULL;
+ string = parse_dn_part (array+arrayidx, string);
+ if (!string)
+ goto failure;
+ while (*string == ' ')
+ string++;
+ array[arrayidx].multivalued = (*string == '+');
+ array[arrayidx].done = 0;
+ arrayidx++;
+ if (*string && *string != ',' && *string != ';' && *string != '+')
+ goto failure; /* invalid delimiter */
+ if (*string)
+ string++;
+ }
+ array[arrayidx].key = NULL;
+ array[arrayidx].value = NULL;
+ return array;
+
+ failure:
+ for (i=0; i < arrayidx; i++)
+ {
+ xfree (array[i].key);
+ xfree (array[i].value);
+ }
+ xfree (array);
+ return NULL;
+}
+
+
+/* Print a DN part to STREAM or if STREAM is NULL to FP. */
+static void
+print_dn_part (FILE *fp, estream_t stream,
+ struct dn_array_s *dn, const char *key, int translate)
+{
+ struct dn_array_s *first_dn = dn;
+
+ for (; dn->key; dn++)
+ {
+ if (!dn->done && !strcmp (dn->key, key))
+ {
+ /* Forward to the last multi-valued RDN, so that we can
+ print them all in reverse in the correct order. Note
+ that this overrides the the standard sequence but that
+ seems to a reasonable thing to do with multi-valued
+ RDNs. */
+ while (dn->multivalued && dn[1].key)
+ dn++;
+ next:
+ if (!dn->done && dn->value && *dn->value)
+ {
+ if (stream)
+ {
+ es_fprintf (stream, "/%s=", dn->key);
+ if (translate)
+ es_write_sanitized_utf8_buffer (stream, dn->value,
+ strlen (dn->value),
+ "/", NULL);
+ else
+ es_write_sanitized (stream, dn->value, strlen (dn->value),
+ "/", NULL);
+ }
+ else
+ {
+ fprintf (fp, "/%s=", dn->key);
+ if (translate)
+ print_sanitized_utf8_string (fp, dn->value, '/');
+ else
+ print_sanitized_string (fp, dn->value, '/');
+ }
+ }
+ dn->done = 1;
+ if (dn > first_dn && dn[-1].multivalued)
+ {
+ dn--;
+ goto next;
+ }
+ }
+ }
+}
+
+/* Print all parts of a DN in a "standard" sequence. We first print
+ all the known parts, followed by the uncommon ones */
+static void
+print_dn_parts (FILE *fp, estream_t stream,
+ struct dn_array_s *dn, int translate)
+{
+ const char *stdpart[] = {
+ "CN", "OU", "O", "STREET", "L", "ST", "C", "EMail", NULL
+ };
+ int i;
+
+ for (i=0; stdpart[i]; i++)
+ print_dn_part (fp, stream, dn, stdpart[i], translate);
+
+ /* Now print the rest without any specific ordering */
+ for (; dn->key; dn++)
+ print_dn_part (fp, stream, dn, dn->key, translate);
+}
+
+
+/* Print the S-Expression in BUF, which has a valid length of BUFLEN,
+ as a human readable string in one line to FP. */
+static void
+pretty_print_sexp (FILE *fp, const unsigned char *buf, size_t buflen)
+{
+ size_t len;
+ gcry_sexp_t sexp;
+ char *result, *p;
+
+ if ( gcry_sexp_sscan (&sexp, NULL, (const char*)buf, buflen) )
+ {
+ fputs (_("[Error - invalid encoding]"), fp);
+ return;
+ }
+ len = gcry_sexp_sprint (sexp, GCRYSEXP_FMT_ADVANCED, NULL, 0);
+ assert (len);
+ result = xtrymalloc (len);
+ if (!result)
+ {
+ fputs (_("[Error - out of core]"), fp);
+ gcry_sexp_release (sexp);
+ return;
+ }
+ len = gcry_sexp_sprint (sexp, GCRYSEXP_FMT_ADVANCED, result, len);
+ assert (len);
+ for (p = result; len; len--, p++)
+ {
+ if (*p == '\n')
+ {
+ if (len > 1) /* Avoid printing the trailing LF. */
+ fputs ("\\n", fp);
+ }
+ else if (*p == '\r')
+ fputs ("\\r", fp);
+ else if (*p == '\v')
+ fputs ("\\v", fp);
+ else if (*p == '\t')
+ fputs ("\\t", fp);
+ else
+ putc (*p, fp);
+ }
+ xfree (result);
+ gcry_sexp_release (sexp);
+}
+
+/* Print the S-Expression in BUF to extended STREAM, which has a valid
+ length of BUFLEN, as a human readable string in one line to FP. */
+static void
+pretty_es_print_sexp (estream_t fp, const unsigned char *buf, size_t buflen)
+{
+ size_t len;
+ gcry_sexp_t sexp;
+ char *result, *p;
+
+ if ( gcry_sexp_sscan (&sexp, NULL, (const char*)buf, buflen) )
+ {
+ es_fputs (_("[Error - invalid encoding]"), fp);
+ return;
+ }
+ len = gcry_sexp_sprint (sexp, GCRYSEXP_FMT_ADVANCED, NULL, 0);
+ assert (len);
+ result = xtrymalloc (len);
+ if (!result)
+ {
+ es_fputs (_("[Error - out of core]"), fp);
+ gcry_sexp_release (sexp);
+ return;
+ }
+ len = gcry_sexp_sprint (sexp, GCRYSEXP_FMT_ADVANCED, result, len);
+ assert (len);
+ for (p = result; len; len--, p++)
+ {
+ if (*p == '\n')
+ {
+ if (len > 1) /* Avoid printing the trailing LF. */
+ es_fputs ("\\n", fp);
+ }
+ else if (*p == '\r')
+ es_fputs ("\\r", fp);
+ else if (*p == '\v')
+ es_fputs ("\\v", fp);
+ else if (*p == '\t')
+ es_fputs ("\\t", fp);
+ else
+ es_putc (*p, fp);
+ }
+ xfree (result);
+ gcry_sexp_release (sexp);
+}
+
+
+
+
+void
+gpgsm_print_name2 (FILE *fp, const char *name, int translate)
+{
+ const unsigned char *s = (const unsigned char *)name;
+ int i;
+
+ if (!s)
+ {
+ fputs (_("[Error - No name]"), fp);
+ }
+ else if (*s == '<')
+ {
+ const char *s2 = strchr ( (char*)s+1, '>');
+ if (s2)
+ {
+ if (translate)
+ print_sanitized_utf8_buffer (fp, s + 1, s2 - (char*)s - 1, 0);
+ else
+ print_sanitized_buffer (fp, s + 1, s2 - (char*)s - 1, 0);
+ }
+ }
+ else if (*s == '(')
+ {
+ pretty_print_sexp (fp, s, gcry_sexp_canon_len (s, 0, NULL, NULL));
+ }
+ else if (!((*s >= '0' && *s < '9')
+ || (*s >= 'A' && *s <= 'Z')
+ || (*s >= 'a' && *s <= 'z')))
+ fputs (_("[Error - invalid encoding]"), fp);
+ else
+ {
+ struct dn_array_s *dn = parse_dn (s);
+ if (!dn)
+ fputs (_("[Error - invalid DN]"), fp);
+ else
+ {
+ print_dn_parts (fp, NULL, dn, translate);
+ for (i=0; dn[i].key; i++)
+ {
+ xfree (dn[i].key);
+ xfree (dn[i].value);
+ }
+ xfree (dn);
+ }
+ }
+}
+
+
+void
+gpgsm_print_name (FILE *fp, const char *name)
+{
+ gpgsm_print_name2 (fp, name, 1);
+}
+
+
+/* This is a variant of gpgsm_print_name sending it output to an estream. */
+void
+gpgsm_es_print_name2 (estream_t fp, const char *name, int translate)
+{
+ const unsigned char *s = (const unsigned char *)name;
+ int i;
+
+ if (!s)
+ {
+ es_fputs (_("[Error - No name]"), fp);
+ }
+ else if (*s == '<')
+ {
+ const char *s2 = strchr ( (char*)s+1, '>');
+
+ if (s2)
+ {
+ if (translate)
+ es_write_sanitized_utf8_buffer (fp, s + 1, s2 - (char*)s - 1,
+ NULL, NULL);
+ else
+ es_write_sanitized (fp, s + 1, s2 - (char*)s - 1, NULL, NULL);
+ }
+ }
+ else if (*s == '(')
+ {
+ pretty_es_print_sexp (fp, s, gcry_sexp_canon_len (s, 0, NULL, NULL));
+ }
+ else if (!((*s >= '0' && *s < '9')
+ || (*s >= 'A' && *s <= 'Z')
+ || (*s >= 'a' && *s <= 'z')))
+ es_fputs (_("[Error - invalid encoding]"), fp);
+ else
+ {
+ struct dn_array_s *dn = parse_dn (s);
+
+ if (!dn)
+ es_fputs (_("[Error - invalid DN]"), fp);
+ else
+ {
+ print_dn_parts (NULL, fp, dn, translate);
+ for (i=0; dn[i].key; i++)
+ {
+ xfree (dn[i].key);
+ xfree (dn[i].value);
+ }
+ xfree (dn);
+ }
+ }
+}
+
+
+void
+gpgsm_es_print_name (estream_t fp, const char *name)
+{
+ gpgsm_es_print_name2 (fp, name, 1);
+}
+
+
+/* A cookie structure used for the memory stream. */
+struct format_name_cookie
+{
+ char *buffer; /* Malloced buffer with the data to deliver. */
+ size_t size; /* Allocated size of this buffer. */
+ size_t len; /* strlen (buffer). */
+ int error; /* system error code if any. */
+};
+
+/* The writer function for the memory stream. */
+static ssize_t
+format_name_writer (void *cookie, const void *buffer, size_t size)
+{
+ struct format_name_cookie *c = cookie;
+ char *p;
+
+ if (!c->buffer)
+ {
+ p = xtrymalloc (size + 1 + 1);
+ if (p)
+ {
+ c->size = size + 1;
+ c->buffer = p;
+ c->len = 0;
+ }
+ }
+ else if (c->len + size < c->len)
+ {
+ p = NULL;
+ errno = ENOMEM;
+ }
+ else if (c->size < c->len + size)
+ {
+ p = xtryrealloc (c->buffer, c->len + size + 1);
+ if (p)
+ {
+ c->size = c->len + size;
+ c->buffer = p;
+ }
+ }
+ else
+ p = c->buffer;
+ if (!p)
+ {
+ c->error = errno;
+ xfree (c->buffer);
+ c->buffer = NULL;
+ errno = c->error;
+ return -1;
+ }
+ memcpy (p + c->len, buffer, size);
+ c->len += size;
+ p[c->len] = 0; /* Terminate string. */
+
+ return (ssize_t)size;
+}
+
+
+/* Format NAME which is expected to be in rfc2253 format into a better
+ human readable format. Caller must free the returned string. NULL
+ is returned in case of an error. With TRANSLATE set to true the
+ name will be translated to the native encoding. Note that NAME is
+ internally always UTF-8 encoded. */
+char *
+gpgsm_format_name2 (const char *name, int translate)
+{
+ estream_t fp;
+ struct format_name_cookie cookie;
+ es_cookie_io_functions_t io = { NULL };
+
+ memset (&cookie, 0, sizeof cookie);
+
+ io.func_write = format_name_writer;
+ fp = es_fopencookie (&cookie, "w", io);
+ if (!fp)
+ {
+ int save_errno = errno;
+ log_error ("error creating memory stream: %s\n", strerror (errno));
+ errno = save_errno;
+ return NULL;
+ }
+ gpgsm_es_print_name2 (fp, name, translate);
+ es_fclose (fp);
+ if (cookie.error || !cookie.buffer)
+ {
+ xfree (cookie.buffer);
+ errno = cookie.error;
+ return NULL;
+ }
+ return cookie.buffer;
+}
+
+
+char *
+gpgsm_format_name (const char *name)
+{
+ return gpgsm_format_name2 (name, 1);
+}
+
+
+/* Return fingerprint and a percent escaped name in a human readable
+ format suitable for status messages like GOODSIG. May return NULL
+ on error (out of core). */
+char *
+gpgsm_fpr_and_name_for_status (ksba_cert_t cert)
+{
+ char *fpr, *name, *p;
+ char *buffer;
+
+ fpr = gpgsm_get_fingerprint_hexstring (cert, GCRY_MD_SHA1);
+ if (!fpr)
+ return NULL;
+
+ name = ksba_cert_get_subject (cert, 0);
+ if (!name)
+ {
+ xfree (fpr);
+ return NULL;
+ }
+
+ p = gpgsm_format_name2 (name, 0);
+ ksba_free (name);
+ name = p;
+ if (!name)
+ {
+ xfree (fpr);
+ return NULL;
+ }
+
+ buffer = xtrymalloc (strlen (fpr) + 1 + 3*strlen (name) + 1);
+ if (buffer)
+ {
+ const char *s;
+
+ p = stpcpy (stpcpy (buffer, fpr), " ");
+ for (s = name; *s; s++)
+ {
+ if (*s < ' ')
+ {
+ sprintf (p, "%%%02X", *(const unsigned char*)s);
+ p += 3;
+ }
+ else
+ *p++ = *s;
+ }
+ *p = 0;
+ }
+ xfree (fpr);
+ xfree (name);
+ return buffer;
+}
+
+
+/* Create a key description for the CERT, this may be passed to the
+ pinentry. The caller must free the returned string. NULL may be
+ returned on error. */
+char *
+gpgsm_format_keydesc (ksba_cert_t cert)
+{
+ char *name, *subject, *buffer;
+ ksba_isotime_t t;
+ char created[20];
+ char expires[20];
+ char *sn;
+ ksba_sexp_t sexp;
+ char *orig_codeset;
+
+ name = ksba_cert_get_subject (cert, 0);
+ subject = name? gpgsm_format_name2 (name, 0) : NULL;
+ ksba_free (name); name = NULL;
+
+ sexp = ksba_cert_get_serial (cert);
+ sn = sexp? gpgsm_format_serial (sexp) : NULL;
+ ksba_free (sexp);
+
+ ksba_cert_get_validity (cert, 0, t);
+ if (*t)
+ sprintf (created, "%.4s-%.2s-%.2s", t, t+4, t+6);
+ else
+ *created = 0;
+ ksba_cert_get_validity (cert, 1, t);
+ if (*t)
+ sprintf (expires, "%.4s-%.2s-%.2s", t, t+4, t+6);
+ else
+ *expires = 0;
+
+ orig_codeset = i18n_switchto_utf8 ();
+
+ name = xtryasprintf (_("Please enter the passphrase to unlock the"
+ " secret key for the X.509 certificate:\n"
+ "\"%s\"\n"
+ "S/N %s, ID 0x%08lX,\n"
+ "created %s, expires %s.\n" ),
+ subject? subject:"?",
+ sn? sn: "?",
+ gpgsm_get_short_fingerprint (cert, NULL),
+ created, expires);
+
+ i18n_switchback (orig_codeset);
+
+ if (!name)
+ {
+ xfree (subject);
+ xfree (sn);
+ return NULL;
+ }
+
+ xfree (subject);
+ xfree (sn);
+
+ buffer = percent_plus_escape (name);
+ xfree (name);
+ return buffer;
+}
+
diff --git a/sm/certlist.c b/sm/certlist.c
new file mode 100644
index 0000000..4137437
--- /dev/null
+++ b/sm/certlist.c
@@ -0,0 +1,555 @@
+/* certlist.c - build list of certificates
+ * Copyright (C) 2001, 2003, 2004, 2005, 2007,
+ * 2008 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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuPG is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <time.h>
+#include <assert.h>
+
+#include "gpgsm.h"
+#include <gcrypt.h>
+#include <ksba.h>
+
+#include "keydb.h"
+#include "i18n.h"
+
+
+static const char oid_kp_serverAuth[] = "1.3.6.1.5.5.7.3.1";
+static const char oid_kp_clientAuth[] = "1.3.6.1.5.5.7.3.2";
+static const char oid_kp_codeSigning[] = "1.3.6.1.5.5.7.3.3";
+static const char oid_kp_emailProtection[]= "1.3.6.1.5.5.7.3.4";
+static const char oid_kp_timeStamping[] = "1.3.6.1.5.5.7.3.8";
+static const char oid_kp_ocspSigning[] = "1.3.6.1.5.5.7.3.9";
+
+/* Return 0 if the cert is usable for encryption. A MODE of 0 checks
+ for signing a MODE of 1 checks for encryption, a MODE of 2 checks
+ for verification and a MODE of 3 for decryption (just for
+ debugging). MODE 4 is for certificate signing, MODE for COSP
+ response signing. */
+static int
+cert_usage_p (ksba_cert_t cert, int mode)
+{
+ gpg_error_t err;
+ unsigned int use;
+ char *extkeyusages;
+ int have_ocsp_signing = 0;
+
+ err = ksba_cert_get_ext_key_usages (cert, &extkeyusages);
+ if (gpg_err_code (err) == GPG_ERR_NO_DATA)
+ err = 0; /* no policy given */
+ if (!err)
+ {
+ unsigned int extusemask = ~0; /* Allow all. */
+
+ if (extkeyusages)
+ {
+ char *p, *pend;
+ int any_critical = 0;
+
+ extusemask = 0;
+
+ p = extkeyusages;
+ while (p && (pend=strchr (p, ':')))
+ {
+ *pend++ = 0;
+ /* Only care about critical flagged usages. */
+ if ( *pend == 'C' )
+ {
+ any_critical = 1;
+ if ( !strcmp (p, oid_kp_serverAuth))
+ extusemask |= (KSBA_KEYUSAGE_DIGITAL_SIGNATURE
+ | KSBA_KEYUSAGE_KEY_ENCIPHERMENT
+ | KSBA_KEYUSAGE_KEY_AGREEMENT);
+ else if ( !strcmp (p, oid_kp_clientAuth))
+ extusemask |= (KSBA_KEYUSAGE_DIGITAL_SIGNATURE
+ | KSBA_KEYUSAGE_KEY_AGREEMENT);
+ else if ( !strcmp (p, oid_kp_codeSigning))
+ extusemask |= (KSBA_KEYUSAGE_DIGITAL_SIGNATURE);
+ else if ( !strcmp (p, oid_kp_emailProtection))
+ extusemask |= (KSBA_KEYUSAGE_DIGITAL_SIGNATURE
+ | KSBA_KEYUSAGE_NON_REPUDIATION
+ | KSBA_KEYUSAGE_KEY_ENCIPHERMENT
+ | KSBA_KEYUSAGE_KEY_AGREEMENT);
+ else if ( !strcmp (p, oid_kp_timeStamping))
+ extusemask |= (KSBA_KEYUSAGE_DIGITAL_SIGNATURE
+ | KSBA_KEYUSAGE_NON_REPUDIATION);
+ }
+
+ /* This is a hack to cope with OCSP. Note that we do
+ not yet fully comply with the requirements and that
+ the entire CRL/OCSP checking thing should undergo a
+ thorough review and probably redesign. */
+ if ( !strcmp (p, oid_kp_ocspSigning))
+ have_ocsp_signing = 1;
+
+ if ((p = strchr (pend, '\n')))
+ p++;
+ }
+ xfree (extkeyusages);
+ extkeyusages = NULL;
+
+ if (!any_critical)
+ extusemask = ~0; /* Reset to the don't care mask. */
+ }
+
+
+ err = ksba_cert_get_key_usage (cert, &use);
+ if (gpg_err_code (err) == GPG_ERR_NO_DATA)
+ {
+ err = 0;
+ if (opt.verbose && mode < 2)
+ log_info (_("no key usage specified - assuming all usages\n"));
+ use = ~0;
+ }
+
+ /* Apply extKeyUsage. */
+ use &= extusemask;
+
+ }
+ if (err)
+ {
+ log_error (_("error getting key usage information: %s\n"),
+ gpg_strerror (err));
+ xfree (extkeyusages);
+ return err;
+ }
+
+ if (mode == 4)
+ {
+ if ((use & (KSBA_KEYUSAGE_KEY_CERT_SIGN)))
+ return 0;
+ log_info (_("certificate should have not "
+ "been used for certification\n"));
+ return gpg_error (GPG_ERR_WRONG_KEY_USAGE);
+ }
+
+ if (mode == 5)
+ {
+ if (use != ~0
+ && (have_ocsp_signing
+ || (use & (KSBA_KEYUSAGE_KEY_CERT_SIGN
+ |KSBA_KEYUSAGE_CRL_SIGN))))
+ return 0;
+ log_info (_("certificate should have not "
+ "been used for OCSP response signing\n"));
+ return gpg_error (GPG_ERR_WRONG_KEY_USAGE);
+ }
+
+ if ((use & ((mode&1)?
+ (KSBA_KEYUSAGE_KEY_ENCIPHERMENT|KSBA_KEYUSAGE_DATA_ENCIPHERMENT):
+ (KSBA_KEYUSAGE_DIGITAL_SIGNATURE|KSBA_KEYUSAGE_NON_REPUDIATION)))
+ )
+ return 0;
+
+ log_info (mode==3? _("certificate should have not been used for encryption\n"):
+ mode==2? _("certificate should have not been used for signing\n"):
+ mode==1? _("certificate is not usable for encryption\n"):
+ _("certificate is not usable for signing\n"));
+ return gpg_error (GPG_ERR_WRONG_KEY_USAGE);
+}
+
+
+/* Return 0 if the cert is usable for signing */
+int
+gpgsm_cert_use_sign_p (ksba_cert_t cert)
+{
+ return cert_usage_p (cert, 0);
+}
+
+
+/* Return 0 if the cert is usable for encryption */
+int
+gpgsm_cert_use_encrypt_p (ksba_cert_t cert)
+{
+ return cert_usage_p (cert, 1);
+}
+
+int
+gpgsm_cert_use_verify_p (ksba_cert_t cert)
+{
+ return cert_usage_p (cert, 2);
+}
+
+int
+gpgsm_cert_use_decrypt_p (ksba_cert_t cert)
+{
+ return cert_usage_p (cert, 3);
+}
+
+int
+gpgsm_cert_use_cert_p (ksba_cert_t cert)
+{
+ return cert_usage_p (cert, 4);
+}
+
+int
+gpgsm_cert_use_ocsp_p (ksba_cert_t cert)
+{
+ return cert_usage_p (cert, 5);
+}
+
+
+static int
+same_subject_issuer (const char *subject, const char *issuer, ksba_cert_t cert)
+{
+ char *subject2 = ksba_cert_get_subject (cert, 0);
+ char *issuer2 = ksba_cert_get_issuer (cert, 0);
+ int tmp;
+
+ tmp = (subject && subject2
+ && !strcmp (subject, subject2)
+ && issuer && issuer2
+ && !strcmp (issuer, issuer2));
+ xfree (subject2);
+ xfree (issuer2);
+ return tmp;
+}
+
+
+/* Return true if CERT_A is the same as CERT_B. */
+int
+gpgsm_certs_identical_p (ksba_cert_t cert_a, ksba_cert_t cert_b)
+{
+ const unsigned char *img_a, *img_b;
+ size_t len_a, len_b;
+
+ img_a = ksba_cert_get_image (cert_a, &len_a);
+ if (img_a)
+ {
+ img_b = ksba_cert_get_image (cert_b, &len_b);
+ if (img_b && len_a == len_b && !memcmp (img_a, img_b, len_a))
+ return 1; /* Identical. */
+ }
+ return 0;
+}
+
+
+/* Return true if CERT is already contained in CERTLIST. */
+static int
+is_cert_in_certlist (ksba_cert_t cert, certlist_t certlist)
+{
+ const unsigned char *img_a, *img_b;
+ size_t len_a, len_b;
+
+ img_a = ksba_cert_get_image (cert, &len_a);
+ if (img_a)
+ {
+ for ( ; certlist; certlist = certlist->next)
+ {
+ img_b = ksba_cert_get_image (certlist->cert, &len_b);
+ if (img_b && len_a == len_b && !memcmp (img_a, img_b, len_a))
+ return 1; /* Already contained. */
+ }
+ }
+ return 0;
+}
+
+
+/* Add CERT to the list of certificates at CERTADDR but avoid
+ duplicates. */
+int
+gpgsm_add_cert_to_certlist (ctrl_t ctrl, ksba_cert_t cert,
+ certlist_t *listaddr, int is_encrypt_to)
+{
+ (void)ctrl;
+
+ if (!is_cert_in_certlist (cert, *listaddr))
+ {
+ certlist_t cl = xtrycalloc (1, sizeof *cl);
+ if (!cl)
+ return out_of_core ();
+ cl->cert = cert;
+ ksba_cert_ref (cert);
+ cl->next = *listaddr;
+ cl->is_encrypt_to = is_encrypt_to;
+ *listaddr = cl;
+ }
+ return 0;
+}
+
+/* Add a certificate to a list of certificate and make sure that it is
+ a valid certificate. With SECRET set to true a secret key must be
+ available for the certificate. IS_ENCRYPT_TO sets the corresponding
+ flag in the new create LISTADDR item. */
+int
+gpgsm_add_to_certlist (ctrl_t ctrl, const char *name, int secret,
+ certlist_t *listaddr, int is_encrypt_to)
+{
+ int rc;
+ KEYDB_SEARCH_DESC desc;
+ KEYDB_HANDLE kh = NULL;
+ ksba_cert_t cert = NULL;
+
+ rc = keydb_classify_name (name, &desc);
+ if (!rc)
+ {
+ kh = keydb_new (0);
+ if (!kh)
+ rc = gpg_error (GPG_ERR_ENOMEM);
+ else
+ {
+ int wrong_usage = 0;
+ char *first_subject = NULL;
+ char *first_issuer = NULL;
+
+ get_next:
+ rc = keydb_search (kh, &desc, 1);
+ if (!rc)
+ rc = keydb_get_cert (kh, &cert);
+ if (!rc)
+ {
+ if (!first_subject)
+ {
+ /* Save the the subject and the issuer for key usage
+ and ambiguous name tests. */
+ first_subject = ksba_cert_get_subject (cert, 0);
+ first_issuer = ksba_cert_get_issuer (cert, 0);
+ }
+ rc = secret? gpgsm_cert_use_sign_p (cert)
+ : gpgsm_cert_use_encrypt_p (cert);
+ if (gpg_err_code (rc) == GPG_ERR_WRONG_KEY_USAGE)
+ {
+ /* There might be another certificate with the
+ correct usage, so we try again */
+ if (!wrong_usage)
+ { /* save the first match */
+ wrong_usage = rc;
+ ksba_cert_release (cert);
+ cert = NULL;
+ goto get_next;
+ }
+ else if (same_subject_issuer (first_subject, first_issuer,
+ cert))
+ {
+ wrong_usage = rc;
+ ksba_cert_release (cert);
+ cert = NULL;
+ goto get_next;
+ }
+ else
+ wrong_usage = rc;
+
+ }
+ }
+ /* We want the error code from the first match in this case. */
+ if (rc && wrong_usage)
+ rc = wrong_usage;
+
+ if (!rc)
+ {
+ certlist_t dup_certs = NULL;
+
+ next_ambigious:
+ rc = keydb_search (kh, &desc, 1);
+ if (rc == -1)
+ rc = 0;
+ else if (!rc)
+ {
+ ksba_cert_t cert2 = NULL;
+
+ /* If this is the first possible duplicate, add the original
+ certificate to our list of duplicates. */
+ if (!dup_certs)
+ gpgsm_add_cert_to_certlist (ctrl, cert, &dup_certs, 0);
+
+ /* We have to ignore ambigious names as long as
+ there only fault is a bad key usage. This is
+ required to support encryption and signing
+ certificates of the same subject.
+
+ Further we ignore them if they are due to an
+ identical certificate (which may happen if a
+ certificate is accidential duplicated in the
+ keybox). */
+ if (!keydb_get_cert (kh, &cert2))
+ {
+ int tmp = (same_subject_issuer (first_subject,
+ first_issuer,
+ cert2)
+ && ((gpg_err_code (
+ secret? gpgsm_cert_use_sign_p (cert2)
+ : gpgsm_cert_use_encrypt_p (cert2)
+ )
+ ) == GPG_ERR_WRONG_KEY_USAGE));
+ if (tmp)
+ gpgsm_add_cert_to_certlist (ctrl, cert2,
+ &dup_certs, 0);
+ else
+ {
+ if (is_cert_in_certlist (cert2, dup_certs))
+ tmp = 1;
+ }
+
+ ksba_cert_release (cert2);
+ if (tmp)
+ goto next_ambigious;
+ }
+ rc = gpg_error (GPG_ERR_AMBIGUOUS_NAME);
+ }
+ gpgsm_release_certlist (dup_certs);
+ }
+ xfree (first_subject);
+ xfree (first_issuer);
+ first_subject = NULL;
+ first_issuer = NULL;
+
+ if (!rc && !is_cert_in_certlist (cert, *listaddr))
+ {
+ if (!rc && secret)
+ {
+ char *p;
+
+ rc = gpg_error (GPG_ERR_NO_SECKEY);
+ p = gpgsm_get_keygrip_hexstring (cert);
+ if (p)
+ {
+ if (!gpgsm_agent_havekey (ctrl, p))
+ rc = 0;
+ xfree (p);
+ }
+ }
+ if (!rc)
+ rc = gpgsm_validate_chain (ctrl, cert, "", NULL,
+ 0, NULL, 0, NULL);
+ if (!rc)
+ {
+ certlist_t cl = xtrycalloc (1, sizeof *cl);
+ if (!cl)
+ rc = out_of_core ();
+ else
+ {
+ cl->cert = cert; cert = NULL;
+ cl->next = *listaddr;
+ cl->is_encrypt_to = is_encrypt_to;
+ *listaddr = cl;
+ }
+ }
+ }
+ }
+ }
+
+ keydb_release (kh);
+ ksba_cert_release (cert);
+ return rc == -1? gpg_error (GPG_ERR_NO_PUBKEY): rc;
+}
+
+
+void
+gpgsm_release_certlist (certlist_t list)
+{
+ while (list)
+ {
+ certlist_t cl = list->next;
+ ksba_cert_release (list->cert);
+ xfree (list);
+ list = cl;
+ }
+}
+
+
+/* Like gpgsm_add_to_certlist, but look only for one certificate. No
+ chain validation is done. If KEYID is not NULL it is taken as an
+ additional filter value which must match the
+ subjectKeyIdentifier. */
+int
+gpgsm_find_cert (const char *name, ksba_sexp_t keyid, ksba_cert_t *r_cert)
+{
+ int rc;
+ KEYDB_SEARCH_DESC desc;
+ KEYDB_HANDLE kh = NULL;
+
+ *r_cert = NULL;
+ rc = keydb_classify_name (name, &desc);
+ if (!rc)
+ {
+ kh = keydb_new (0);
+ if (!kh)
+ rc = gpg_error (GPG_ERR_ENOMEM);
+ else
+ {
+ nextone:
+ rc = keydb_search (kh, &desc, 1);
+ if (!rc)
+ {
+ rc = keydb_get_cert (kh, r_cert);
+ if (!rc && keyid)
+ {
+ ksba_sexp_t subj;
+
+ rc = ksba_cert_get_subj_key_id (*r_cert, NULL, &subj);
+ if (!rc)
+ {
+ if (cmp_simple_canon_sexp (keyid, subj))
+ {
+ xfree (subj);
+ goto nextone;
+ }
+ xfree (subj);
+ /* Okay: Here we know that the certificate's
+ subjectKeyIdentifier matches the requested
+ one. */
+ }
+ else if (gpg_err_code (rc) == GPG_ERR_NO_DATA)
+ goto nextone;
+ }
+ }
+
+ /* If we don't have the KEYID filter we need to check for
+ ambigious search results. Note, that it is somehwat
+ reasonable to assume that a specification of a KEYID
+ won't lead to ambiguous names. */
+ if (!rc && !keyid)
+ {
+ next_ambiguous:
+ rc = keydb_search (kh, &desc, 1);
+ if (rc == -1)
+ rc = 0;
+ else
+ {
+ if (!rc)
+ {
+ ksba_cert_t cert2 = NULL;
+
+ if (!keydb_get_cert (kh, &cert2))
+ {
+ if (gpgsm_certs_identical_p (*r_cert, cert2))
+ {
+ ksba_cert_release (cert2);
+ goto next_ambiguous;
+ }
+ ksba_cert_release (cert2);
+ }
+ rc = gpg_error (GPG_ERR_AMBIGUOUS_NAME);
+ }
+ ksba_cert_release (*r_cert);
+ *r_cert = NULL;
+ }
+ }
+ }
+ }
+
+ keydb_release (kh);
+ return rc == -1? gpg_error (GPG_ERR_NO_PUBKEY): rc;
+}
+
diff --git a/sm/certreqgen-ui.c b/sm/certreqgen-ui.c
new file mode 100644
index 0000000..3e98b66
--- /dev/null
+++ b/sm/certreqgen-ui.c
@@ -0,0 +1,415 @@
+/* certreqgen-ui.c - Simple user interface for certreqgen.c
+ * Copyright (C) 2007 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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuPG is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <time.h>
+#include <assert.h>
+
+#include "gpgsm.h"
+#include <gcrypt.h>
+
+#include "i18n.h"
+#include "ttyio.h"
+#include "membuf.h"
+
+
+/* Prompt for lines and append them to MB. */
+static void
+ask_mb_lines (membuf_t *mb, const char *prefix)
+{
+ char *answer = NULL;
+
+ do
+ {
+ xfree (answer);
+ answer = tty_get ("> ");
+ tty_kill_prompt ();
+ trim_spaces (answer);
+ if (*answer)
+ {
+ put_membuf_str (mb, prefix);
+ put_membuf_str (mb, answer);
+ put_membuf (mb, "\n", 1);
+ }
+ }
+ while (*answer);
+ xfree (answer);
+}
+
+/* Helper to store stuff in a membuf. */
+void
+store_key_value_lf (membuf_t *mb, const char *key, const char *value)
+{
+ put_membuf_str (mb, key);
+ put_membuf_str (mb, value);
+ put_membuf (mb, "\n", 1);
+}
+
+/* Helper tp store a membuf create by mb_ask_lines into MB. Returns
+ -1 on error. */
+int
+store_mb_lines (membuf_t *mb, membuf_t *lines)
+{
+ char *p;
+
+ if (get_membuf_len (lines))
+ {
+ put_membuf (lines, "", 1);
+ p = get_membuf (lines, NULL);
+ if (!p)
+ return -1;
+ put_membuf_str (mb, p);
+ xfree (p);
+ }
+ return 0;
+}
+
+
+/* Chech whether we have a key for the key with HEXGRIP. Returns NULL
+ if not or a string describing the type of the key (RSA, ELG, DSA,
+ etc..). */
+static const char *
+check_keygrip (ctrl_t ctrl, const char *hexgrip)
+{
+ gpg_error_t err;
+ ksba_sexp_t public;
+ size_t publiclen;
+ int algo;
+
+ if (hexgrip[0] == '&')
+ hexgrip++;
+
+ err = gpgsm_agent_readkey (ctrl, 0, hexgrip, &public);
+ if (err)
+ return NULL;
+ publiclen = gcry_sexp_canon_len (public, 0, NULL, NULL);
+
+ get_pk_algo_from_canon_sexp (public, publiclen, &algo);
+ xfree (public);
+
+ switch (algo)
+ {
+ case GCRY_PK_RSA: return "RSA";
+ case GCRY_PK_DSA: return "DSA";
+ case GCRY_PK_ELG: return "ELG";
+ case GCRY_PK_ECDSA: return "ECDSA";
+ default: return NULL;
+ }
+}
+
+
+/* This function is used to create a certificate request from the
+ command line. In the past the similar gpgsm-gencert.sh script has
+ been used for it; however that scripts requires a full Unix shell
+ and thus is not suitable for the Windows port. So here is the
+ re-implementation. */
+void
+gpgsm_gencertreq_tty (ctrl_t ctrl, FILE *output_fp)
+{
+ gpg_error_t err;
+ char *answer;
+ int selection;
+ estream_t fp = NULL;
+ int method;
+ char *keytype_buffer = NULL;
+ const char *keytype;
+ char *keygrip = NULL;
+ unsigned int nbits;
+ int minbits = 1024;
+ int maxbits = 4096;
+ int defbits = 2048;
+ const char *keyusage;
+ char *subject_name;
+ membuf_t mb_email, mb_dns, mb_uri, mb_result;
+ char *result = NULL;
+ int i;
+ const char *s, *s2;
+
+ answer = NULL;
+ init_membuf (&mb_email, 100);
+ init_membuf (&mb_dns, 100);
+ init_membuf (&mb_uri, 100);
+ init_membuf (&mb_result, 512);
+
+ again:
+ /* Get the type of the key. */
+ tty_printf (_("Please select what kind of key you want:\n"));
+ tty_printf (_(" (%d) RSA\n"), 1 );
+ tty_printf (_(" (%d) Existing key\n"), 2 );
+ tty_printf (_(" (%d) Existing key from card\n"), 3 );
+
+ do
+ {
+ xfree (answer);
+ answer = tty_get (_("Your selection? "));
+ tty_kill_prompt ();
+ selection = *answer? atoi (answer): 1;
+ }
+ while (!(selection >= 1 && selection <= 3));
+ method = selection;
+
+ /* Get size of the key. */
+ if (method == 1)
+ {
+ keytype = "RSA";
+ for (;;)
+ {
+ xfree (answer);
+ answer = tty_getf (_("What keysize do you want? (%u) "), defbits);
+ tty_kill_prompt ();
+ trim_spaces (answer);
+ nbits = *answer? atoi (answer): defbits;
+ if (nbits < minbits || nbits > maxbits)
+ tty_printf(_("%s keysizes must be in the range %u-%u\n"),
+ "RSA", minbits, maxbits);
+ else
+ break; /* Okay. */
+ }
+ tty_printf (_("Requested keysize is %u bits\n"), nbits);
+ /* We round it up so that it better matches the word size. */
+ if (( nbits % 64))
+ {
+ nbits = ((nbits + 63) / 64) * 64;
+ tty_printf (_("rounded up to %u bits\n"), nbits);
+ }
+ }
+ else if (method == 2)
+ {
+ for (;;)
+ {
+ xfree (answer);
+ answer = tty_get (_("Enter the keygrip: "));
+ tty_kill_prompt ();
+ trim_spaces (answer);
+
+ if (!*answer)
+ goto again;
+ else if (strlen (answer) != 40 &&
+ !(answer[0] == '&' && strlen (answer+1) == 40))
+ tty_printf (_("Not a valid keygrip (expecting 40 hex digits)\n"));
+ else if (!(keytype = check_keygrip (ctrl, answer)) )
+ tty_printf (_("No key with this keygrip\n"));
+ else
+ break; /* Okay. */
+ }
+ xfree (keygrip);
+ keygrip = answer;
+ answer = NULL;
+ nbits = 1024; /* A dummy value is sufficient. */
+ }
+ else /* method == 3 */
+ {
+ char *serialno;
+ strlist_t keypairlist, sl;
+ int count;
+
+ err = gpgsm_agent_scd_serialno (ctrl, &serialno);
+ if (err)
+ {
+ tty_printf (_("error reading the card: %s\n"), gpg_strerror (err));
+ goto again;
+ }
+ tty_printf (_("Serial number of the card: %s\n"), serialno);
+ xfree (serialno);
+
+ err = gpgsm_agent_scd_keypairinfo (ctrl, &keypairlist);
+ if (err)
+ {
+ tty_printf (_("error reading the card: %s\n"), gpg_strerror (err));
+ goto again;
+ }
+
+ do
+ {
+ tty_printf (_("Available keys:\n"));
+ for (count=1,sl=keypairlist; sl; sl = sl->next, count++)
+ tty_printf (" (%d) %s\n", count, sl->d);
+ xfree (answer);
+ answer = tty_get (_("Your selection? "));
+ tty_kill_prompt ();
+ trim_spaces (answer);
+ selection = atoi (answer);
+ }
+ while (!(selection > 0 && selection < count));
+
+ for (count=1,sl=keypairlist; sl; sl = sl->next, count++)
+ if (count == selection)
+ break;
+
+ s = sl->d;
+ while (*s && !spacep (s))
+ s++;
+ while (spacep (s))
+ s++;
+
+ xfree (keygrip);
+ keygrip = NULL;
+ xfree (keytype_buffer);
+ keytype_buffer = xasprintf ("card:%s", s);
+ free_strlist (keypairlist);
+ keytype = keytype_buffer;
+ nbits = 1024; /* A dummy value is sufficient. */
+ }
+
+ /* Ask for the key usage. */
+ tty_printf (_("Possible actions for a %s key:\n"), "RSA");
+ tty_printf (_(" (%d) sign, encrypt\n"), 1 );
+ tty_printf (_(" (%d) sign\n"), 2 );
+ tty_printf (_(" (%d) encrypt\n"), 3 );
+ do
+ {
+ xfree (answer);
+ answer = tty_get (_("Your selection? "));
+ tty_kill_prompt ();
+ trim_spaces (answer);
+ selection = *answer? atoi (answer): 1;
+ switch (selection)
+ {
+ case 1: keyusage = "sign, encrypt"; break;
+ case 2: keyusage = "sign"; break;
+ case 3: keyusage = "encrypt"; break;
+ default: keyusage = NULL; break;
+ }
+ }
+ while (!keyusage);
+
+ /* Get the subject name. */
+ do
+ {
+ size_t erroff, errlen;
+
+ xfree (answer);
+ answer = tty_get (_("Enter the X.509 subject name: "));
+ tty_kill_prompt ();
+ trim_spaces (answer);
+ if (!*answer)
+ tty_printf (_("No subject name given\n"));
+ else if ( (err = ksba_dn_teststr (answer, 0, &erroff, &errlen)) )
+ {
+ if (gpg_err_code (err) == GPG_ERR_UNKNOWN_NAME)
+ tty_printf (_("Invalid subject name label `%.*s'\n"),
+ (int)errlen, answer+erroff);
+ else
+ {
+ /* TRANSLATORS: The 22 in the second string is the
+ length of the first string up to the "%s". Please
+ adjust it do the length of your translation. The
+ second string is merely passed to atoi so you can
+ drop everything after the number. */
+ tty_printf (_("Invalid subject name `%s'\n"), answer);
+ tty_printf ("%*s^\n",
+ atoi (_("22 translator: see "
+ "certreg-ui.c:gpgsm_gencertreq_tty"))
+ + (int)erroff, "");
+ }
+ *answer = 0;
+ }
+ }
+ while (!*answer);
+ subject_name = answer;
+ answer = NULL;
+
+ /* Get the email addresses. */
+ tty_printf (_("Enter email addresses"));
+ tty_printf (_(" (end with an empty line):\n"));
+ ask_mb_lines (&mb_email, "Name-Email: ");
+
+ /* DNS names. */
+ tty_printf (_("Enter DNS names"));
+ tty_printf (_(" (optional; end with an empty line):\n"));
+ ask_mb_lines (&mb_email, "Name-DNS: ");
+
+ /* URIs. */
+ tty_printf (_("Enter URIs"));
+ tty_printf (_(" (optional; end with an empty line):\n"));
+ ask_mb_lines (&mb_email, "Name-URI: ");
+
+
+ /* Put it all together. */
+ store_key_value_lf (&mb_result, "Key-Type: ", keytype);
+ {
+ char numbuf[30];
+ snprintf (numbuf, sizeof numbuf, "%u", nbits);
+ store_key_value_lf (&mb_result, "Key-Length: ", numbuf);
+ }
+ store_key_value_lf (&mb_result, "Key-Usage: ", keyusage);
+ store_key_value_lf (&mb_result, "Name-DN: ", subject_name);
+ if (keygrip)
+ store_key_value_lf (&mb_result, "Key-Grip: ", keygrip);
+ if (store_mb_lines (&mb_result, &mb_email))
+ goto mem_error;
+ if (store_mb_lines (&mb_result, &mb_dns))
+ goto mem_error;
+ if (store_mb_lines (&mb_result, &mb_uri))
+ goto mem_error;
+ put_membuf (&mb_result, "", 1);
+ result = get_membuf (&mb_result, NULL);
+ if (!result)
+ goto mem_error;
+
+ tty_printf (_("Parameters to be used for the certificate request:\n"));
+ for (s=result; (s2 = strchr (s, '\n')); s = s2+1, i++)
+ tty_printf (" %.*s\n", (int)(s2-s), s);
+ tty_printf ("\n");
+
+
+ if (!tty_get_answer_is_yes ("Really create request? (y/N) "))
+ goto leave;
+
+ /* Now create a parameter file and generate the key. */
+ fp = es_fopenmem (0, "w+");
+ if (!fp)
+ {
+ log_error (_("error creating temporary file: %s\n"), strerror (errno));
+ goto leave;
+ }
+ es_fputs (result, fp);
+ es_rewind (fp);
+ tty_printf (_("Now creating certificate request. "
+ "This may take a while ...\n"));
+ {
+ int save_pem = ctrl->create_pem;
+ ctrl->create_pem = 1; /* Force creation of PEM. */
+ err = gpgsm_genkey (ctrl, fp, output_fp);
+ ctrl->create_pem = save_pem;
+ }
+ if (!err)
+ tty_printf (_("Ready. You should now send this request to your CA.\n"));
+
+
+ goto leave;
+ mem_error:
+ log_error (_("resource problem: out of core\n"));
+ leave:
+ es_fclose (fp);
+ xfree (answer);
+ xfree (subject_name);
+ xfree (keytype_buffer);
+ xfree (keygrip);
+ xfree (get_membuf (&mb_email, NULL));
+ xfree (get_membuf (&mb_dns, NULL));
+ xfree (get_membuf (&mb_uri, NULL));
+ xfree (get_membuf (&mb_result, NULL));
+ xfree (result);
+}
diff --git a/sm/certreqgen.c b/sm/certreqgen.c
new file mode 100644
index 0000000..49b2b92
--- /dev/null
+++ b/sm/certreqgen.c
@@ -0,0 +1,884 @@
+/* certreqgen.c - Generate a key and a certification request
+ * Copyright (C) 2002, 2003, 2005, 2007 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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuPG is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+/*
+The format of the native parameter file is follows:
+ o Text only, line length is limited to about 1000 chars.
+ o You must use UTF-8 encoding to specify non-ascii characters.
+ o Empty lines are ignored.
+ o Leading and trailing spaces are ignored.
+ o A hash sign as the first non white space character is a comment line.
+ o Control statements are indicated by a leading percent sign, the
+ arguments are separated by white space from the keyword.
+ o Parameters are specified by a keyword, followed by a colon. Arguments
+ are separated by white space.
+ o The first parameter must be "Key-Type", control statements
+ may be placed anywhere.
+ o Key generation takes place when either the end of the parameter file
+ is reached, the next "Key-Type" parameter is encountered or at the
+ controlstatement "%commit"
+ o Control statements:
+ %echo <text>
+ Print <text>.
+ %dry-run
+ Suppress actual key generation (useful for syntax checking).
+ %commit
+ Perform the key generation. Note that an implicit commit is done
+ at the next "Key-Type" parameter.
+ %certfile <filename>
+ Do not write the certificate to the keyDB but to <filename>.
+ This must be given before the first
+ commit to take place, duplicate specification of the same filename
+ is ignored, the last filename before a commit is used.
+ The filename is used until a new filename is used (at commit points)
+ and all keys are written to that file. If a new filename is given,
+ this file is created (and overwrites an existing one).
+ Both control statements must be given.
+ o The order of the parameters does not matter except for "Key-Type"
+ which must be the first parameter. The parameters are only for the
+ generated keyblock and parameters from previous key generations are not
+ used. Some syntactically checks may be performed.
+ The currently defined parameters are:
+ Key-Type: <algo>
+ Starts a new parameter block by giving the type of the
+ primary key. The algorithm must be capable of signing.
+ This is a required parameter. For now the only supported
+ algorithm is "rsa".
+ Key-Length: <length-in-bits>
+ Length of the key in bits. Default is 2048.
+ Key-Grip: hexstring
+ This is optional and used to generate a request for an already
+ existing key. Key-Length will be ignored when given,
+ Key-Usage: <usage-list>
+ Space or comma delimited list of key usage, allowed values are
+ "encrypt" and "sign". This is used to generate the KeyUsage extension.
+ Please make sure that the algorithm is capable of this usage. Default
+ is to allow encrypt and sign.
+ Name-DN: subject name
+ This is the DN name of the subject in rfc2253 format.
+ Name-Email: <string>
+ The is an email address for the altSubjectName
+ Name-DNS: <string>
+ The is an DNS name for the altSubjectName
+ Name-URI: <string>
+ The is an URI for the altSubjectName
+
+Here is an example:
+$ cat >foo <<EOF
+%echo Generating a standard key
+Key-Type: RSA
+Key-Length: 2048
+Name-DN: CN=test cert 1,OU=Aegypten Project,O=g10 Code GmbH,L=Düsseldorf,C=DE
+Name-Email: joe@foo.bar
+# Do a commit here, so that we can later print "done" :-)
+%commit
+%echo done
+EOF
+*/
+
+
+#include <config.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <time.h>
+#include <assert.h>
+
+#include "gpgsm.h"
+#include <gcrypt.h>
+#include <ksba.h>
+
+#include "keydb.h"
+#include "i18n.h"
+
+
+enum para_name {
+ pKEYTYPE,
+ pKEYLENGTH,
+ pKEYGRIP,
+ pKEYUSAGE,
+ pNAMEDN,
+ pNAMEEMAIL,
+ pNAMEDNS,
+ pNAMEURI
+};
+
+struct para_data_s {
+ struct para_data_s *next;
+ int lnr;
+ enum para_name key;
+ union {
+ unsigned int usage;
+ char value[1];
+ } u;
+};
+
+struct reqgen_ctrl_s {
+ int lnr;
+ int dryrun;
+ ksba_writer_t writer;
+};
+
+
+static const char oidstr_keyUsage[] = "2.5.29.15";
+
+
+static int proc_parameters (ctrl_t ctrl,
+ struct para_data_s *para,
+ struct reqgen_ctrl_s *outctrl);
+static int create_request (ctrl_t ctrl,
+ struct para_data_s *para,
+ const char *carddirect,
+ ksba_const_sexp_t public,
+ struct reqgen_ctrl_s *outctrl);
+
+
+
+static void
+release_parameter_list (struct para_data_s *r)
+{
+ struct para_data_s *r2;
+
+ for (; r ; r = r2)
+ {
+ r2 = r->next;
+ xfree(r);
+ }
+}
+
+static struct para_data_s *
+get_parameter (struct para_data_s *para, enum para_name key, int seq)
+{
+ struct para_data_s *r;
+
+ for (r = para; r ; r = r->next)
+ if ( r->key == key && !seq--)
+ return r;
+ return NULL;
+}
+
+static const char *
+get_parameter_value (struct para_data_s *para, enum para_name key, int seq)
+{
+ struct para_data_s *r = get_parameter (para, key, seq);
+ return (r && *r->u.value)? r->u.value : NULL;
+}
+
+static int
+get_parameter_algo (struct para_data_s *para, enum para_name key)
+{
+ struct para_data_s *r = get_parameter (para, key, 0);
+ if (!r)
+ return -1;
+ if (digitp (r->u.value))
+ return atoi( r->u.value );
+ return gcry_pk_map_name (r->u.value);
+}
+
+/* Parse the usage parameter. Returns 0 on success. Note that we
+ only care about sign and encrypt and don't (yet) allow all the
+ other X.509 usage to be specified; instead we will use a fixed
+ mapping to the X.509 usage flags. */
+static int
+parse_parameter_usage (struct para_data_s *para, enum para_name key)
+{
+ struct para_data_s *r = get_parameter (para, key, 0);
+ 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 |= GCRY_PK_USAGE_SIGN;
+ else if ( !ascii_strcasecmp (p, "encrypt") )
+ use |= GCRY_PK_USAGE_ENCR;
+ else
+ {
+ log_error ("line %d: invalid usage list\n", r->lnr);
+ return -1; /* error */
+ }
+ }
+ r->u.usage = use;
+ return 0;
+}
+
+
+static unsigned int
+get_parameter_uint (struct para_data_s *para, enum para_name key)
+{
+ struct para_data_s *r = get_parameter (para, key, 0);
+
+ if (!r)
+ return 0;
+
+ if (r->key == pKEYUSAGE)
+ return r->u.usage;
+
+ return (unsigned int)strtoul (r->u.value, NULL, 10);
+}
+
+
+
+/* Read the certificate generation parameters from FP and generate
+ (all) certificate requests. */
+static int
+read_parameters (ctrl_t ctrl, estream_t fp, ksba_writer_t writer)
+{
+ static struct {
+ const char *name;
+ enum para_name key;
+ int allow_dups;
+ } keywords[] = {
+ { "Key-Type", pKEYTYPE},
+ { "Key-Length", pKEYLENGTH },
+ { "Key-Grip", pKEYGRIP },
+ { "Key-Usage", pKEYUSAGE },
+ { "Name-DN", pNAMEDN },
+ { "Name-Email", pNAMEEMAIL, 1 },
+ { "Name-DNS", pNAMEDNS, 1 },
+ { "Name-URI", pNAMEURI, 1 },
+ { NULL, 0 }
+ };
+ char line[1024], *p;
+ const char *err = NULL;
+ struct para_data_s *para, *r;
+ int i, rc = 0, any = 0;
+ struct reqgen_ctrl_s outctrl;
+
+ memset (&outctrl, 0, sizeof (outctrl));
+ outctrl.writer = writer;
+
+ err = NULL;
+ para = NULL;
+ while (es_fgets (line, DIM(line)-1, fp) )
+ {
+ char *keyword, *value;
+
+ outctrl.lnr++;
+ if (*line && line[strlen(line)-1] != '\n')
+ {
+ err = "line too long";
+ break;
+ }
+ for (p=line; spacep (p); p++)
+ ;
+ if (!*p || *p == '#')
+ continue;
+
+ keyword = p;
+ if (*keyword == '%')
+ {
+ for (; *p && !spacep (p); p++)
+ ;
+ if (*p)
+ *p++ = 0;
+ for (; spacep (p); p++)
+ ;
+ value = p;
+ trim_trailing_spaces (value);
+
+ if (!ascii_strcasecmp (keyword, "%echo"))
+ log_info ("%s\n", value);
+ else if (!ascii_strcasecmp (keyword, "%dry-run"))
+ outctrl.dryrun = 1;
+ else if (!ascii_strcasecmp( keyword, "%commit"))
+ {
+ rc = proc_parameters (ctrl, para, &outctrl);
+ if (rc)
+ goto leave;
+ any = 1;
+ release_parameter_list (para);
+ para = NULL;
+ }
+ else
+ log_info ("skipping control `%s' (%s)\n", keyword, value);
+
+ continue;
+ }
+
+
+ if (!(p = strchr (p, ':')) || p == keyword)
+ {
+ err = "missing colon";
+ break;
+ }
+ if (*p)
+ *p++ = 0;
+ for (; spacep (p); p++)
+ ;
+ if (!*p)
+ {
+ err = "missing argument";
+ break;
+ }
+ value = p;
+ trim_trailing_spaces (value);
+
+ for (i=0; (keywords[i].name
+ && ascii_strcasecmp (keywords[i].name, keyword)); i++)
+ ;
+ if (!keywords[i].name)
+ {
+ err = "unknown keyword";
+ break;
+ }
+ if (keywords[i].key != pKEYTYPE && !para)
+ {
+ err = "parameter block does not start with \"Key-Type\"";
+ break;
+ }
+
+ if (keywords[i].key == pKEYTYPE && para)
+ {
+ rc = proc_parameters (ctrl, para, &outctrl);
+ if (rc)
+ goto leave;
+ any = 1;
+ release_parameter_list (para);
+ para = NULL;
+ }
+ else if (!keywords[i].allow_dups)
+ {
+ for (r = para; r && r->key != keywords[i].key; r = r->next)
+ ;
+ if (r)
+ {
+ err = "duplicate keyword";
+ break;
+ }
+ }
+
+ r = xtrycalloc (1, sizeof *r + strlen( value ));
+ if (!r)
+ {
+ err = "out of core";
+ break;
+ }
+ r->lnr = outctrl.lnr;
+ r->key = keywords[i].key;
+ strcpy (r->u.value, value);
+ r->next = para;
+ para = r;
+ }
+
+ if (err)
+ {
+ log_error ("line %d: %s\n", outctrl.lnr, err);
+ rc = gpg_error (GPG_ERR_GENERAL);
+ }
+ else if (es_ferror(fp))
+ {
+ log_error ("line %d: read error: %s\n", outctrl.lnr, strerror(errno) );
+ rc = gpg_error (GPG_ERR_GENERAL);
+ }
+ else if (para)
+ {
+ rc = proc_parameters (ctrl, para, &outctrl);
+ if (rc)
+ goto leave;
+ any = 1;
+ }
+
+ if (!rc && !any)
+ rc = gpg_error (GPG_ERR_NO_DATA);
+
+ leave:
+ release_parameter_list (para);
+ return rc;
+}
+
+/* check whether there are invalid characters in the email address S */
+static int
+has_invalid_email_chars (const char *s)
+{
+ int at_seen=0;
+ static char valid_chars[] = "01234567890_-."
+ "abcdefghijklmnopqrstuvwxyz"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
+ for (; *s; s++)
+ {
+ if (*s & 0x80)
+ return 1;
+ if (*s == '@')
+ at_seen++;
+ else if (!at_seen && !( !!strchr (valid_chars, *s) || *s == '+'))
+ return 1;
+ else if (at_seen && !strchr (valid_chars, *s))
+ return 1;
+ }
+ return at_seen != 1;
+}
+
+
+/* Check that all required parameters are given and perform the action */
+static int
+proc_parameters (ctrl_t ctrl,
+ struct para_data_s *para, struct reqgen_ctrl_s *outctrl)
+{
+ gpg_error_t err;
+ struct para_data_s *r;
+ const char *s;
+ int i;
+ unsigned int nbits;
+ char numbuf[20];
+ unsigned char keyparms[100];
+ int rc;
+ ksba_sexp_t public;
+ int seq;
+ size_t erroff, errlen;
+ char *cardkeyid = NULL;
+
+ /* Check that we have all required parameters; */
+ assert (get_parameter (para, pKEYTYPE, 0));
+
+ /* We can only use RSA for now. There is a problem with pkcs-10 on
+ how to use ElGamal because it is expected that a PK algorithm can
+ always be used for signing. Another problem is that on-card
+ generated encryption keys may not be used for signing. */
+ i = get_parameter_algo (para, pKEYTYPE);
+ if (!i && (s = get_parameter_value (para, pKEYTYPE, 0)) && *s)
+ {
+ /* Hack to allow creation of certificates directly from a smart
+ card. For example: "Key-Type: card:OPENPGP.3". */
+ if (!strncmp (s, "card:", 5) && s[5])
+ cardkeyid = xtrystrdup (s+5);
+ }
+ if ( (i < 1 || i != GCRY_PK_RSA) && !cardkeyid )
+ {
+ r = get_parameter (para, pKEYTYPE, 0);
+ log_error (_("line %d: invalid algorithm\n"), r->lnr);
+ return gpg_error (GPG_ERR_INV_PARAMETER);
+ }
+
+ /* Check the keylength. */
+ if (!get_parameter (para, pKEYLENGTH, 0))
+ nbits = 2048;
+ else
+ nbits = get_parameter_uint (para, pKEYLENGTH);
+ if ((nbits < 1024 || nbits > 4096) && !cardkeyid)
+ {
+ /* The BSI specs dated 2002-11-25 don't allow lengths below 1024. */
+ r = get_parameter (para, pKEYLENGTH, 0);
+ log_error (_("line %d: invalid key length %u (valid are %d to %d)\n"),
+ r->lnr, nbits, 1024, 4096);
+ xfree (cardkeyid);
+ return gpg_error (GPG_ERR_INV_PARAMETER);
+ }
+
+ /* Check the usage. */
+ if (parse_parameter_usage (para, pKEYUSAGE))
+ {
+ xfree (cardkeyid);
+ return gpg_error (GPG_ERR_INV_PARAMETER);
+ }
+
+ /* Check that there is a subject name and that this DN fits our
+ requirements. */
+ if (!(s=get_parameter_value (para, pNAMEDN, 0)))
+ {
+ r = get_parameter (para, pNAMEDN, 0);
+ log_error (_("line %d: no subject name given\n"), r->lnr);
+ xfree (cardkeyid);
+ return gpg_error (GPG_ERR_INV_PARAMETER);
+ }
+ err = ksba_dn_teststr (s, 0, &erroff, &errlen);
+ if (err)
+ {
+ r = get_parameter (para, pNAMEDN, 0);
+ if (gpg_err_code (err) == GPG_ERR_UNKNOWN_NAME)
+ log_error (_("line %d: invalid subject name label `%.*s'\n"),
+ r->lnr, (int)errlen, s+erroff);
+ else
+ log_error (_("line %d: invalid subject name `%s' at pos %d\n"),
+ r->lnr, s, (int)erroff);
+
+ xfree (cardkeyid);
+ return gpg_error (GPG_ERR_INV_PARAMETER);
+ }
+
+ /* Check that the optional email address is okay. */
+ for (seq=0; (s=get_parameter_value (para, pNAMEEMAIL, seq)); seq++)
+ {
+ if (has_invalid_email_chars (s)
+ || *s == '@'
+ || s[strlen(s)-1] == '@'
+ || s[strlen(s)-1] == '.'
+ || strstr(s, ".."))
+ {
+ r = get_parameter (para, pNAMEEMAIL, seq);
+ log_error (_("line %d: not a valid email address\n"), r->lnr);
+ xfree (cardkeyid);
+ return gpg_error (GPG_ERR_INV_PARAMETER);
+ }
+ }
+
+ if (cardkeyid) /* Take the key from the current smart card. */
+ {
+ rc = gpgsm_agent_readkey (ctrl, 1, cardkeyid, &public);
+ if (rc)
+ {
+ r = get_parameter (para, pKEYTYPE, 0);
+ log_error (_("line %d: error reading key `%s' from card: %s\n"),
+ r->lnr, cardkeyid, gpg_strerror (rc));
+ xfree (cardkeyid);
+ return rc;
+ }
+ }
+ else if ((s=get_parameter_value (para, pKEYGRIP, 0))) /* Use existing key.*/
+ {
+ rc = gpgsm_agent_readkey (ctrl, 0, s, &public);
+ if (rc)
+ {
+ r = get_parameter (para, pKEYTYPE, 0);
+ log_error (_("line %d: error getting key by keygrip `%s': %s\n"),
+ r->lnr, s, gpg_strerror (rc));
+ xfree (cardkeyid);
+ return rc;
+ }
+ }
+ else /* Generate new key. */
+ {
+ sprintf (numbuf, "%u", nbits);
+ snprintf ((char*)keyparms, DIM (keyparms)-1,
+ "(6:genkey(3:rsa(5:nbits%d:%s)))",
+ (int)strlen (numbuf), numbuf);
+ rc = gpgsm_agent_genkey (ctrl, keyparms, &public);
+ if (rc)
+ {
+ r = get_parameter (para, pKEYTYPE, 0);
+ log_error (_("line %d: key generation failed: %s <%s>\n"),
+ r->lnr, gpg_strerror (rc), gpg_strsource (rc));
+ xfree (cardkeyid);
+ return rc;
+ }
+ }
+
+ rc = create_request (ctrl, para, cardkeyid, public, outctrl);
+ xfree (public);
+ xfree (cardkeyid);
+
+ return rc;
+}
+
+
+/* Parameters are checked, the key pair has been created. Now
+ generate the request and write it out */
+static int
+create_request (ctrl_t ctrl,
+ struct para_data_s *para,
+ const char *carddirect,
+ ksba_const_sexp_t public,
+ struct reqgen_ctrl_s *outctrl)
+{
+ ksba_certreq_t cr;
+ gpg_error_t err;
+ gcry_md_hd_t md;
+ ksba_stop_reason_t stopreason;
+ int rc = 0;
+ const char *s;
+ unsigned int use;
+ int seq;
+ char *buf, *p;
+ size_t len;
+ char numbuf[30];
+
+ err = ksba_certreq_new (&cr);
+ if (err)
+ return err;
+
+ rc = gcry_md_open (&md, GCRY_MD_SHA1, 0);
+ if (rc)
+ {
+ log_error ("md_open failed: %s\n", gpg_strerror (rc));
+ goto leave;
+ }
+ if (DBG_HASHING)
+ gcry_md_start_debug (md, "cr.cri");
+
+ ksba_certreq_set_hash_function (cr, HASH_FNC, md);
+ ksba_certreq_set_writer (cr, outctrl->writer);
+
+ err = ksba_certreq_add_subject (cr, get_parameter_value (para, pNAMEDN, 0));
+ if (err)
+ {
+ log_error ("error setting the subject's name: %s\n",
+ gpg_strerror (err));
+ rc = err;
+ goto leave;
+ }
+
+ for (seq=0; (s = get_parameter_value (para, pNAMEEMAIL, seq)); seq++)
+ {
+ buf = xtrymalloc (strlen (s) + 3);
+ if (!buf)
+ {
+ rc = out_of_core ();
+ goto leave;
+ }
+ *buf = '<';
+ strcpy (buf+1, s);
+ strcat (buf+1, ">");
+ err = ksba_certreq_add_subject (cr, buf);
+ xfree (buf);
+ if (err)
+ {
+ log_error ("error setting the subject's alternate name: %s\n",
+ gpg_strerror (err));
+ rc = err;
+ goto leave;
+ }
+ }
+
+ for (seq=0; (s = get_parameter_value (para, pNAMEDNS, seq)); seq++)
+ {
+ len = strlen (s);
+ assert (len);
+ snprintf (numbuf, DIM(numbuf), "%u:", (unsigned int)len);
+ buf = p = xtrymalloc (11 + strlen (numbuf) + len + 3);
+ if (!buf)
+ {
+ rc = out_of_core ();
+ goto leave;
+ }
+ p = stpcpy (p, "(8:dns-name");
+ p = stpcpy (p, numbuf);
+ p = stpcpy (p, s);
+ strcpy (p, ")");
+
+ err = ksba_certreq_add_subject (cr, buf);
+ xfree (buf);
+ if (err)
+ {
+ log_error ("error setting the subject's alternate name: %s\n",
+ gpg_strerror (err));
+ rc = err;
+ goto leave;
+ }
+ }
+
+ for (seq=0; (s = get_parameter_value (para, pNAMEURI, seq)); seq++)
+ {
+ len = strlen (s);
+ assert (len);
+ snprintf (numbuf, DIM(numbuf), "%u:", (unsigned int)len);
+ buf = p = xtrymalloc (6 + strlen (numbuf) + len + 3);
+ if (!buf)
+ {
+ rc = out_of_core ();
+ goto leave;
+ }
+ p = stpcpy (p, "(3:uri");
+ p = stpcpy (p, numbuf);
+ p = stpcpy (p, s);
+ strcpy (p, ")");
+
+ err = ksba_certreq_add_subject (cr, buf);
+ xfree (buf);
+ if (err)
+ {
+ log_error ("error setting the subject's alternate name: %s\n",
+ gpg_strerror (err));
+ rc = err;
+ goto leave;
+ }
+ }
+
+
+ err = ksba_certreq_set_public_key (cr, public);
+ if (err)
+ {
+ log_error ("error setting the public key: %s\n",
+ gpg_strerror (err));
+ rc = err;
+ goto leave;
+ }
+
+
+ use = get_parameter_uint (para, pKEYUSAGE);
+ if (use == GCRY_PK_USAGE_SIGN)
+ {
+ /* For signing only we encode the bits:
+ KSBA_KEYUSAGE_DIGITAL_SIGNATURE
+ KSBA_KEYUSAGE_NON_REPUDIATION */
+ err = ksba_certreq_add_extension (cr, oidstr_keyUsage, 1,
+ "\x03\x02\x06\xC0", 4);
+ }
+ else if (use == GCRY_PK_USAGE_ENCR)
+ {
+ /* For encrypt only we encode the bits:
+ KSBA_KEYUSAGE_KEY_ENCIPHERMENT
+ KSBA_KEYUSAGE_DATA_ENCIPHERMENT */
+ err = ksba_certreq_add_extension (cr, oidstr_keyUsage, 1,
+ "\x03\x02\x04\x30", 4);
+ }
+ else
+ err = 0; /* Both or none given: don't request one. */
+ if (err)
+ {
+ log_error ("error setting the key usage: %s\n",
+ gpg_strerror (err));
+ rc = err;
+ goto leave;
+ }
+
+
+ do
+ {
+ err = ksba_certreq_build (cr, &stopreason);
+ if (err)
+ {
+ log_error ("ksba_certreq_build failed: %s\n", gpg_strerror (err));
+ rc = err;
+ goto leave;
+ }
+ if (stopreason == KSBA_SR_NEED_SIG)
+ {
+ gcry_sexp_t s_pkey;
+ size_t n;
+ unsigned char grip[20];
+ char hexgrip[41];
+ unsigned char *sigval;
+ size_t siglen;
+
+ n = gcry_sexp_canon_len (public, 0, NULL, NULL);
+ if (!n)
+ {
+ log_error ("libksba did not return a proper S-Exp\n");
+ rc = gpg_error (GPG_ERR_BUG);
+ goto leave;
+ }
+ rc = gcry_sexp_sscan (&s_pkey, NULL, (const char*)public, n);
+ if (rc)
+ {
+ log_error ("gcry_sexp_scan failed: %s\n", gpg_strerror (rc));
+ goto leave;
+ }
+ if ( !gcry_pk_get_keygrip (s_pkey, grip) )
+ {
+ rc = gpg_error (GPG_ERR_GENERAL);
+ log_error ("can't figure out the keygrip\n");
+ gcry_sexp_release (s_pkey);
+ goto leave;
+ }
+ gcry_sexp_release (s_pkey);
+ bin2hex (grip, 20, hexgrip);
+
+ log_info ("about to sign CSR for key: &%s\n", hexgrip);
+
+ if (carddirect)
+ rc = gpgsm_scd_pksign (ctrl, carddirect, NULL,
+ gcry_md_read(md, GCRY_MD_SHA1),
+ gcry_md_get_algo_dlen (GCRY_MD_SHA1),
+ GCRY_MD_SHA1,
+ &sigval, &siglen);
+ else
+ {
+ char *orig_codeset;
+ char *desc;
+
+ orig_codeset = i18n_switchto_utf8 ();
+ desc = percent_plus_escape
+ (_("To complete this certificate request please enter"
+ " the passphrase for the key you just created once"
+ " more.\n"));
+ i18n_switchback (orig_codeset);
+ rc = gpgsm_agent_pksign (ctrl, hexgrip, desc,
+ gcry_md_read(md, GCRY_MD_SHA1),
+ gcry_md_get_algo_dlen (GCRY_MD_SHA1),
+ GCRY_MD_SHA1,
+ &sigval, &siglen);
+ xfree (desc);
+ }
+ if (rc)
+ {
+ log_error ("signing failed: %s\n", gpg_strerror (rc));
+ goto leave;
+ }
+
+ err = ksba_certreq_set_sig_val (cr, sigval);
+ xfree (sigval);
+ if (err)
+ {
+ log_error ("failed to store the sig_val: %s\n",
+ gpg_strerror (err));
+ rc = err;
+ goto leave;
+ }
+ }
+ }
+ while (stopreason != KSBA_SR_READY);
+
+
+ leave:
+ gcry_md_close (md);
+ ksba_certreq_release (cr);
+ return rc;
+}
+
+
+
+/* Create a new key by reading the parameters from IN_FP. Multiple
+ keys may be created */
+int
+gpgsm_genkey (ctrl_t ctrl, estream_t in_stream, FILE *out_fp)
+{
+ int rc;
+ Base64Context b64writer = NULL;
+ ksba_writer_t writer;
+
+ ctrl->pem_name = "CERTIFICATE REQUEST";
+ rc = gpgsm_create_writer (&b64writer, ctrl, out_fp, NULL, &writer);
+ if (rc)
+ {
+ log_error ("can't create writer: %s\n", gpg_strerror (rc));
+ goto leave;
+ }
+
+ rc = read_parameters (ctrl, in_stream, writer);
+ if (rc)
+ {
+ log_error ("error creating certificate request: %s <%s>\n",
+ gpg_strerror (rc), gpg_strsource (rc));
+ goto leave;
+ }
+
+ rc = gpgsm_finish_writer (b64writer);
+ if (rc)
+ {
+ log_error ("write failed: %s\n", gpg_strerror (rc));
+ goto leave;
+ }
+
+ gpgsm_status (ctrl, STATUS_KEY_CREATED, "P");
+ log_info ("certificate request created\n");
+
+ leave:
+ gpgsm_destroy_writer (b64writer);
+ return rc;
+}
+
diff --git a/sm/decrypt.c b/sm/decrypt.c
new file mode 100644
index 0000000..de02551
--- /dev/null
+++ b/sm/decrypt.c
@@ -0,0 +1,586 @@
+/* decrypt.c - Decrypt a message
+ * Copyright (C) 2001, 2003 Free Software Foundation, Inc.
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuPG is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <time.h>
+#include <assert.h>
+
+#include "gpgsm.h"
+#include <gcrypt.h>
+#include <ksba.h>
+
+#include "keydb.h"
+#include "i18n.h"
+
+struct decrypt_filter_parm_s {
+ int algo;
+ int mode;
+ int blklen;
+ gcry_cipher_hd_t hd;
+ char iv[16];
+ size_t ivlen;
+ int any_data; /* dod we push anything through the filter at all? */
+ unsigned char lastblock[16]; /* to strip the padding we have to
+ keep this one */
+ char helpblock[16]; /* needed because there is no block buffering in
+ libgcrypt (yet) */
+ int helpblocklen;
+};
+
+
+
+/* Decrypt the session key and fill in the parm structure. The
+ algo and the IV is expected to be already in PARM. */
+static int
+prepare_decryption (ctrl_t ctrl, const char *hexkeygrip, const char *desc,
+ ksba_const_sexp_t enc_val,
+ struct decrypt_filter_parm_s *parm)
+{
+ char *seskey = NULL;
+ size_t n, seskeylen;
+ int rc;
+
+ rc = gpgsm_agent_pkdecrypt (ctrl, hexkeygrip, desc, enc_val,
+ &seskey, &seskeylen);
+ if (rc)
+ {
+ log_error ("error decrypting session key: %s\n", gpg_strerror (rc));
+ goto leave;
+ }
+
+ if (DBG_CRYPTO)
+ log_printhex ("pkcs1 encoded session key:", seskey, seskeylen);
+
+ n=0;
+ if (seskeylen == 24)
+ {
+ /* Smells like a 3-des key. This might happen because a SC has
+ already done the unpacking. */
+ }
+ else
+ {
+ if (n + 7 > seskeylen )
+ {
+ rc = gpg_error (GPG_ERR_INV_SESSION_KEY);
+ goto leave;
+ }
+
+ /* FIXME: Actually the leading zero is required but due to the way
+ we encode the output in libgcrypt as an MPI we are not able to
+ encode that leading zero. However, when using a Smartcard we are
+ doing it the right way and therefore we have to skip the zero. This
+ should be fixed in gpg-agent of course. */
+ if (!seskey[n])
+ n++;
+
+ if (seskey[n] != 2 ) /* Wrong block type version. */
+ {
+ rc = gpg_error (GPG_ERR_INV_SESSION_KEY);
+ goto leave;
+ }
+
+ for (n++; n < seskeylen && seskey[n]; n++) /* Skip the random bytes. */
+ ;
+ n++; /* and the zero byte */
+ if (n >= seskeylen )
+ {
+ rc = gpg_error (GPG_ERR_INV_SESSION_KEY);
+ goto leave;
+ }
+ }
+
+ if (DBG_CRYPTO)
+ log_printhex ("session key:", seskey+n, seskeylen-n);
+
+ rc = gcry_cipher_open (&parm->hd, parm->algo, parm->mode, 0);
+ if (rc)
+ {
+ log_error ("error creating decryptor: %s\n", gpg_strerror (rc));
+ goto leave;
+ }
+
+ rc = gcry_cipher_setkey (parm->hd, seskey+n, seskeylen-n);
+ if (gpg_err_code (rc) == GPG_ERR_WEAK_KEY)
+ {
+ log_info (_("WARNING: message was encrypted with "
+ "a weak key in the symmetric cipher.\n"));
+ rc = 0;
+ }
+ if (rc)
+ {
+ log_error("key setup failed: %s\n", gpg_strerror(rc) );
+ goto leave;
+ }
+
+ gcry_cipher_setiv (parm->hd, parm->iv, parm->ivlen);
+
+ leave:
+ xfree (seskey);
+ return rc;
+}
+
+
+/* This function is called by the KSBA writer just before the actual
+ write is done. The function must take INLEN bytes from INBUF,
+ decrypt it and store it inoutbuf which has a maximum size of
+ maxoutlen. The valid bytes in outbuf should be return in outlen.
+ Due to different buffer sizes or different length of input and
+ output, it may happen that fewer bytes are processed or fewer bytes
+ are written. */
+static gpg_error_t
+decrypt_filter (void *arg,
+ const void *inbuf, size_t inlen, size_t *inused,
+ void *outbuf, size_t maxoutlen, size_t *outlen)
+{
+ struct decrypt_filter_parm_s *parm = arg;
+ int blklen = parm->blklen;
+ size_t orig_inlen = inlen;
+
+ /* fixme: Should we issue an error when we have not seen one full block? */
+ if (!inlen)
+ return gpg_error (GPG_ERR_BUG);
+
+ if (maxoutlen < 2*parm->blklen)
+ return gpg_error (GPG_ERR_BUG);
+ /* Make some space because we will later need an extra block at the end. */
+ maxoutlen -= blklen;
+
+ if (parm->helpblocklen)
+ {
+ int i, j;
+
+ for (i=parm->helpblocklen,j=0; i < blklen && j < inlen; i++, j++)
+ parm->helpblock[i] = ((const char*)inbuf)[j];
+ inlen -= j;
+ if (blklen > maxoutlen)
+ return gpg_error (GPG_ERR_BUG);
+ if (i < blklen)
+ {
+ parm->helpblocklen = i;
+ *outlen = 0;
+ }
+ else
+ {
+ parm->helpblocklen = 0;
+ if (parm->any_data)
+ {
+ memcpy (outbuf, parm->lastblock, blklen);
+ *outlen =blklen;
+ }
+ else
+ *outlen = 0;
+ gcry_cipher_decrypt (parm->hd, parm->lastblock, blklen,
+ parm->helpblock, blklen);
+ parm->any_data = 1;
+ }
+ *inused = orig_inlen - inlen;
+ return 0;
+ }
+
+
+ if (inlen > maxoutlen)
+ inlen = maxoutlen;
+ if (inlen % blklen)
+ { /* store the remainder away */
+ parm->helpblocklen = inlen%blklen;
+ inlen = inlen/blklen*blklen;
+ memcpy (parm->helpblock, (const char*)inbuf+inlen, parm->helpblocklen);
+ }
+
+ *inused = inlen + parm->helpblocklen;
+ if (inlen)
+ {
+ assert (inlen >= blklen);
+ if (parm->any_data)
+ {
+ gcry_cipher_decrypt (parm->hd, (char*)outbuf+blklen, inlen,
+ inbuf, inlen);
+ memcpy (outbuf, parm->lastblock, blklen);
+ memcpy (parm->lastblock,(char*)outbuf+inlen, blklen);
+ *outlen = inlen;
+ }
+ else
+ {
+ gcry_cipher_decrypt (parm->hd, outbuf, inlen, inbuf, inlen);
+ memcpy (parm->lastblock, (char*)outbuf+inlen-blklen, blklen);
+ *outlen = inlen - blklen;
+ parm->any_data = 1;
+ }
+ }
+ else
+ *outlen = 0;
+ return 0;
+}
+
+
+
+/* Perform a decrypt operation. */
+int
+gpgsm_decrypt (ctrl_t ctrl, int in_fd, FILE *out_fp)
+{
+ int rc;
+ Base64Context b64reader = NULL;
+ Base64Context b64writer = NULL;
+ ksba_reader_t reader;
+ ksba_writer_t writer;
+ ksba_cms_t cms = NULL;
+ ksba_stop_reason_t stopreason;
+ KEYDB_HANDLE kh;
+ int recp;
+ FILE *in_fp = NULL;
+ struct decrypt_filter_parm_s dfparm;
+
+ memset (&dfparm, 0, sizeof dfparm);
+
+ audit_set_type (ctrl->audit, AUDIT_TYPE_DECRYPT);
+
+ kh = keydb_new (0);
+ if (!kh)
+ {
+ log_error (_("failed to allocated keyDB handle\n"));
+ rc = gpg_error (GPG_ERR_GENERAL);
+ goto leave;
+ }
+
+
+ in_fp = fdopen ( dup (in_fd), "rb");
+ if (!in_fp)
+ {
+ rc = gpg_error (gpg_err_code_from_errno (errno));
+ log_error ("fdopen() failed: %s\n", strerror (errno));
+ goto leave;
+ }
+
+ rc = gpgsm_create_reader (&b64reader, ctrl, in_fp, 0, &reader);
+ if (rc)
+ {
+ log_error ("can't create reader: %s\n", gpg_strerror (rc));
+ goto leave;
+ }
+
+ rc = gpgsm_create_writer (&b64writer, ctrl, out_fp, NULL, &writer);
+ if (rc)
+ {
+ log_error ("can't create writer: %s\n", gpg_strerror (rc));
+ goto leave;
+ }
+
+ rc = ksba_cms_new (&cms);
+ if (rc)
+ goto leave;
+
+ rc = ksba_cms_set_reader_writer (cms, reader, writer);
+ if (rc)
+ {
+ log_debug ("ksba_cms_set_reader_writer failed: %s\n",
+ gpg_strerror (rc));
+ goto leave;
+ }
+
+ audit_log (ctrl->audit, AUDIT_SETUP_READY);
+
+ /* Parser loop. */
+ do
+ {
+ rc = ksba_cms_parse (cms, &stopreason);
+ if (rc)
+ {
+ log_debug ("ksba_cms_parse failed: %s\n", gpg_strerror (rc));
+ goto leave;
+ }
+
+ if (stopreason == KSBA_SR_BEGIN_DATA
+ || stopreason == KSBA_SR_DETACHED_DATA)
+ {
+ int algo, mode;
+ const char *algoid;
+ int any_key = 0;
+
+ audit_log (ctrl->audit, AUDIT_GOT_DATA);
+
+ algoid = ksba_cms_get_content_oid (cms, 2/* encryption algo*/);
+ algo = gcry_cipher_map_name (algoid);
+ mode = gcry_cipher_mode_from_oid (algoid);
+ if (!algo || !mode)
+ {
+ rc = gpg_error (GPG_ERR_UNSUPPORTED_ALGORITHM);
+ log_error ("unsupported algorithm `%s'\n", algoid? algoid:"?");
+ if (algoid && !strcmp (algoid, "1.2.840.113549.3.2"))
+ log_info (_("(this is the RC2 algorithm)\n"));
+ else if (!algoid)
+ log_info (_("(this does not seem to be an encrypted"
+ " message)\n"));
+ {
+ char numbuf[50];
+ sprintf (numbuf, "%d", rc);
+ gpgsm_status2 (ctrl, STATUS_ERROR, "decrypt.algorithm",
+ numbuf, algoid?algoid:"?", NULL);
+ audit_log_s (ctrl->audit, AUDIT_BAD_DATA_CIPHER_ALGO, algoid);
+ }
+
+ /* If it seems that this is not an encrypted message we
+ return a more sensible error code. */
+ if (!algoid)
+ rc = gpg_error (GPG_ERR_NO_DATA);
+
+ goto leave;
+ }
+
+ audit_log_i (ctrl->audit, AUDIT_DATA_CIPHER_ALGO, algo);
+ dfparm.algo = algo;
+ dfparm.mode = mode;
+ dfparm.blklen = gcry_cipher_get_algo_blklen (algo);
+ if (dfparm.blklen > sizeof (dfparm.helpblock))
+ return gpg_error (GPG_ERR_BUG);
+
+ rc = ksba_cms_get_content_enc_iv (cms,
+ dfparm.iv,
+ sizeof (dfparm.iv),
+ &dfparm.ivlen);
+ if (rc)
+ {
+ log_error ("error getting IV: %s\n", gpg_strerror (rc));
+ goto leave;
+ }
+
+ for (recp=0; !any_key; recp++)
+ {
+ char *issuer;
+ ksba_sexp_t serial;
+ ksba_sexp_t enc_val;
+ char *hexkeygrip = NULL;
+ char *desc = NULL;
+ char kidbuf[16+1];
+
+ *kidbuf = 0;
+
+ rc = ksba_cms_get_issuer_serial (cms, recp, &issuer, &serial);
+ if (rc == -1 && recp)
+ break; /* no more recipients */
+ audit_log_i (ctrl->audit, AUDIT_NEW_RECP, recp);
+ if (rc)
+ log_error ("recp %d - error getting info: %s\n",
+ recp, gpg_strerror (rc));
+ else
+ {
+ ksba_cert_t cert = NULL;
+
+ log_debug ("recp %d - issuer: `%s'\n",
+ recp, issuer? issuer:"[NONE]");
+ log_debug ("recp %d - serial: ", recp);
+ gpgsm_dump_serial (serial);
+ log_printf ("\n");
+
+ if (ctrl->audit)
+ {
+ char *tmpstr = gpgsm_format_sn_issuer (serial, issuer);
+ audit_log_s (ctrl->audit, AUDIT_RECP_NAME, tmpstr);
+ xfree (tmpstr);
+ }
+
+ keydb_search_reset (kh);
+ rc = keydb_search_issuer_sn (kh, issuer, serial);
+ if (rc)
+ {
+ log_error ("failed to find the certificate: %s\n",
+ gpg_strerror(rc));
+ goto oops;
+ }
+
+ rc = keydb_get_cert (kh, &cert);
+ if (rc)
+ {
+ log_error ("failed to get cert: %s\n", gpg_strerror (rc));
+ goto oops;
+ }
+
+ /* Print the ENC_TO status line. Note that we can
+ do so only if we have the certificate. This is
+ in contrast to gpg where the keyID is commonly
+ included in the encrypted messages. It is too
+ cumbersome to retrieve the used algorithm, thus
+ we don't print it for now. We also record the
+ keyid for later use. */
+ {
+ unsigned long kid[2];
+
+ kid[0] = gpgsm_get_short_fingerprint (cert, kid+1);
+ snprintf (kidbuf, sizeof kidbuf, "%08lX%08lX",
+ kid[1], kid[0]);
+ gpgsm_status2 (ctrl, STATUS_ENC_TO,
+ kidbuf, "0", "0", NULL);
+ }
+
+ /* Put the certificate into the audit log. */
+ audit_log_cert (ctrl->audit, AUDIT_SAVE_CERT, cert, 0);
+
+ /* Just in case there is a problem with the own
+ certificate we print this message - should never
+ happen of course */
+ rc = gpgsm_cert_use_decrypt_p (cert);
+ if (rc)
+ {
+ char numbuf[50];
+ sprintf (numbuf, "%d", rc);
+ gpgsm_status2 (ctrl, STATUS_ERROR, "decrypt.keyusage",
+ numbuf, NULL);
+ rc = 0;
+ }
+
+ hexkeygrip = gpgsm_get_keygrip_hexstring (cert);
+ desc = gpgsm_format_keydesc (cert);
+
+ oops:
+ xfree (issuer);
+ xfree (serial);
+ ksba_cert_release (cert);
+ }
+
+ if (!hexkeygrip)
+ ;
+ else if (!(enc_val = ksba_cms_get_enc_val (cms, recp)))
+ log_error ("recp %d - error getting encrypted session key\n",
+ recp);
+ else
+ {
+ rc = prepare_decryption (ctrl,
+ hexkeygrip, desc, enc_val, &dfparm);
+ xfree (enc_val);
+ if (rc)
+ {
+ log_info ("decrypting session key failed: %s\n",
+ gpg_strerror (rc));
+ if (gpg_err_code (rc) == GPG_ERR_NO_SECKEY && *kidbuf)
+ gpgsm_status2 (ctrl, STATUS_NO_SECKEY, kidbuf, NULL);
+ }
+ else
+ { /* setup the bulk decrypter */
+ any_key = 1;
+ ksba_writer_set_filter (writer,
+ decrypt_filter,
+ &dfparm);
+ }
+ audit_log_ok (ctrl->audit, AUDIT_RECP_RESULT, rc);
+ }
+ xfree (hexkeygrip);
+ xfree (desc);
+ }
+
+ /* If we write an audit log add the unused recipients to the
+ log as well. */
+ if (ctrl->audit && any_key)
+ {
+ for (;; recp++)
+ {
+ char *issuer;
+ ksba_sexp_t serial;
+ int tmp_rc;
+
+ tmp_rc = ksba_cms_get_issuer_serial (cms, recp,
+ &issuer, &serial);
+ if (tmp_rc == -1)
+ break; /* no more recipients */
+ audit_log_i (ctrl->audit, AUDIT_NEW_RECP, recp);
+ if (tmp_rc)
+ log_error ("recp %d - error getting info: %s\n",
+ recp, gpg_strerror (rc));
+ else
+ {
+ char *tmpstr = gpgsm_format_sn_issuer (serial, issuer);
+ audit_log_s (ctrl->audit, AUDIT_RECP_NAME, tmpstr);
+ xfree (tmpstr);
+ xfree (issuer);
+ xfree (serial);
+ }
+ }
+ }
+
+ if (!any_key)
+ {
+ rc = gpg_error (GPG_ERR_NO_SECKEY);
+ goto leave;
+ }
+ }
+ else if (stopreason == KSBA_SR_END_DATA)
+ {
+ ksba_writer_set_filter (writer, NULL, NULL);
+ if (dfparm.any_data)
+ { /* write the last block with padding removed */
+ int i, npadding = dfparm.lastblock[dfparm.blklen-1];
+ if (!npadding || npadding > dfparm.blklen)
+ {
+ log_error ("invalid padding with value %d\n", npadding);
+ rc = gpg_error (GPG_ERR_INV_DATA);
+ goto leave;
+ }
+ rc = ksba_writer_write (writer,
+ dfparm.lastblock,
+ dfparm.blklen - npadding);
+ if (rc)
+ goto leave;
+
+ for (i=dfparm.blklen - npadding; i < dfparm.blklen; i++)
+ {
+ if (dfparm.lastblock[i] != npadding)
+ {
+ log_error ("inconsistent padding\n");
+ rc = gpg_error (GPG_ERR_INV_DATA);
+ goto leave;
+ }
+ }
+ }
+ }
+
+ }
+ while (stopreason != KSBA_SR_READY);
+
+ rc = gpgsm_finish_writer (b64writer);
+ if (rc)
+ {
+ log_error ("write failed: %s\n", gpg_strerror (rc));
+ goto leave;
+ }
+ gpgsm_status (ctrl, STATUS_DECRYPTION_OKAY, NULL);
+
+
+ leave:
+ audit_log_ok (ctrl->audit, AUDIT_DECRYPTION_RESULT, rc);
+ if (rc)
+ {
+ gpgsm_status (ctrl, STATUS_DECRYPTION_FAILED, NULL);
+ log_error ("message decryption failed: %s <%s>\n",
+ gpg_strerror (rc), gpg_strsource (rc));
+ }
+ ksba_cms_release (cms);
+ gpgsm_destroy_reader (b64reader);
+ gpgsm_destroy_writer (b64writer);
+ keydb_release (kh);
+ if (in_fp)
+ fclose (in_fp);
+ if (dfparm.hd)
+ gcry_cipher_close (dfparm.hd);
+ return rc;
+}
+
+
diff --git a/sm/delete.c b/sm/delete.c
new file mode 100644
index 0000000..fd49ebe
--- /dev/null
+++ b/sm/delete.c
@@ -0,0 +1,182 @@
+/* delete.c - Delete certificates from the keybox.
+ * Copyright (C) 2002, 2009 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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuPG is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <time.h>
+#include <assert.h>
+
+#include "gpgsm.h"
+#include <gcrypt.h>
+#include <ksba.h>
+
+#include "keydb.h"
+#include "i18n.h"
+
+
+/* Delete a certificate or an secret key from a key database. */
+static int
+delete_one (ctrl_t ctrl, const char *username)
+{
+ int rc = 0;
+ KEYDB_SEARCH_DESC desc;
+ KEYDB_HANDLE kh = NULL;
+ ksba_cert_t cert = NULL;
+ int duplicates = 0;
+ int is_ephem = 0;
+
+ rc = keydb_classify_name (username, &desc);
+ if (rc)
+ {
+ log_error (_("certificate `%s' not found: %s\n"),
+ username, gpg_strerror (rc));
+ gpgsm_status2 (ctrl, STATUS_DELETE_PROBLEM, "1", NULL);
+ goto leave;
+ }
+
+ kh = keydb_new (0);
+ if (!kh)
+ {
+ log_error ("keydb_new failed\n");
+ goto leave;
+ }
+
+ /* If the key is specified in a unique way, include ephemeral keys
+ in the search. */
+ if ( desc.mode == KEYDB_SEARCH_MODE_FPR
+ || desc.mode == KEYDB_SEARCH_MODE_FPR20
+ || desc.mode == KEYDB_SEARCH_MODE_FPR16
+ || desc.mode == KEYDB_SEARCH_MODE_KEYGRIP )
+ {
+ is_ephem = 1;
+ keydb_set_ephemeral (kh, 1);
+ }
+
+ rc = keydb_search (kh, &desc, 1);
+ if (!rc)
+ rc = keydb_get_cert (kh, &cert);
+ if (!rc && !is_ephem)
+ {
+ unsigned char fpr[20];
+
+ gpgsm_get_fingerprint (cert, 0, fpr, NULL);
+
+ next_ambigious:
+ rc = keydb_search (kh, &desc, 1);
+ if (rc == -1)
+ rc = 0;
+ else if (!rc)
+ {
+ ksba_cert_t cert2 = NULL;
+ unsigned char fpr2[20];
+
+ /* We ignore all duplicated certificates which might have
+ been inserted due to program bugs. */
+ if (!keydb_get_cert (kh, &cert2))
+ {
+ gpgsm_get_fingerprint (cert2, 0, fpr2, NULL);
+ ksba_cert_release (cert2);
+ if (!memcmp (fpr, fpr2, 20))
+ {
+ duplicates++;
+ goto next_ambigious;
+ }
+ }
+ rc = gpg_error (GPG_ERR_AMBIGUOUS_NAME);
+ }
+ }
+ if (rc)
+ {
+ if (rc == -1)
+ rc = gpg_error (GPG_ERR_NO_PUBKEY);
+ log_error (_("certificate `%s' not found: %s\n"),
+ username, gpg_strerror (rc));
+ gpgsm_status2 (ctrl, STATUS_DELETE_PROBLEM, "3", NULL);
+ goto leave;
+ }
+
+ /* We need to search again to get back to the right position. */
+ rc = keydb_lock (kh);
+ if (rc)
+ {
+ log_error (_("error locking keybox: %s\n"), gpg_strerror (rc));
+ goto leave;
+ }
+
+ do
+ {
+ keydb_search_reset (kh);
+ rc = keydb_search (kh, &desc, 1);
+ if (rc)
+ {
+ log_error ("problem re-searching certificate: %s\n",
+ gpg_strerror (rc));
+ goto leave;
+ }
+
+ rc = keydb_delete (kh, duplicates ? 0 : 1);
+ if (rc)
+ goto leave;
+ if (opt.verbose)
+ {
+ if (duplicates)
+ log_info (_("duplicated certificate `%s' deleted\n"), username);
+ else
+ log_info (_("certificate `%s' deleted\n"), username);
+ }
+ }
+ while (duplicates--);
+
+ leave:
+ keydb_release (kh);
+ ksba_cert_release (cert);
+ return rc;
+}
+
+
+
+/* Delete the certificates specified by NAMES. */
+int
+gpgsm_delete (ctrl_t ctrl, strlist_t names)
+{
+ int rc;
+
+ if (!names)
+ {
+ log_error ("nothing to delete\n");
+ return gpg_error (GPG_ERR_NO_DATA);
+ }
+
+ for (; names; names=names->next )
+ {
+ rc = delete_one (ctrl, names->d);
+ if (rc)
+ {
+ log_error (_("deleting certificate \"%s\" failed: %s\n"),
+ names->d, gpg_strerror (rc) );
+ return rc;
+ }
+ }
+
+ return 0;
+}
diff --git a/sm/encrypt.c b/sm/encrypt.c
new file mode 100644
index 0000000..a526a64
--- /dev/null
+++ b/sm/encrypt.c
@@ -0,0 +1,513 @@
+/* encrypt.c - Encrypt a message
+ * Copyright (C) 2001, 2003, 2004, 2007, 2008 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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuPG is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <time.h>
+#include <assert.h>
+
+#include "gpgsm.h"
+#include <gcrypt.h>
+#include <ksba.h>
+
+#include "keydb.h"
+#include "i18n.h"
+
+
+struct dek_s {
+ const char *algoid;
+ int algo;
+ gcry_cipher_hd_t chd;
+ char key[32];
+ int keylen;
+ char iv[32];
+ int ivlen;
+};
+typedef struct dek_s *DEK;
+
+struct encrypt_cb_parm_s {
+ FILE *fp;
+ DEK dek;
+ int eof_seen;
+ int ready;
+ int readerror;
+ int bufsize;
+ unsigned char *buffer;
+ int buflen;
+};
+
+
+
+
+
+/* Initialize the data encryption key (session key). */
+static int
+init_dek (DEK dek)
+{
+ int rc=0, mode, i;
+
+ dek->algo = gcry_cipher_map_name (dek->algoid);
+ mode = gcry_cipher_mode_from_oid (dek->algoid);
+ if (!dek->algo || !mode)
+ {
+ log_error ("unsupported algorithm `%s'\n", dek->algoid);
+ return gpg_error (GPG_ERR_UNSUPPORTED_ALGORITHM);
+ }
+
+ /* Extra check for algorithms we consider to be too weak for
+ encryption, although we support them for decryption. Note that
+ there is another check below discriminating on the key length. */
+ switch (dek->algo)
+ {
+ case GCRY_CIPHER_DES:
+ case GCRY_CIPHER_RFC2268_40:
+ log_error ("cipher algorithm `%s' not allowed: too weak\n",
+ gcry_cipher_algo_name (dek->algo));
+ return gpg_error (GPG_ERR_UNSUPPORTED_ALGORITHM);
+ default:
+ break;
+ }
+
+ dek->keylen = gcry_cipher_get_algo_keylen (dek->algo);
+ if (!dek->keylen || dek->keylen > sizeof (dek->key))
+ return gpg_error (GPG_ERR_BUG);
+
+ dek->ivlen = gcry_cipher_get_algo_blklen (dek->algo);
+ if (!dek->ivlen || dek->ivlen > sizeof (dek->iv))
+ return gpg_error (GPG_ERR_BUG);
+
+ /* Make sure we don't use weak keys. */
+ if (dek->keylen < 100/8)
+ {
+ log_error ("key length of `%s' too small\n", dek->algoid);
+ return gpg_error (GPG_ERR_UNSUPPORTED_ALGORITHM);
+ }
+
+ rc = gcry_cipher_open (&dek->chd, dek->algo, mode, GCRY_CIPHER_SECURE);
+ if (rc)
+ {
+ log_error ("failed to create cipher context: %s\n", gpg_strerror (rc));
+ return rc;
+ }
+
+ for (i=0; i < 8; i++)
+ {
+ gcry_randomize (dek->key, dek->keylen, GCRY_STRONG_RANDOM );
+ rc = gcry_cipher_setkey (dek->chd, dek->key, dek->keylen);
+ if (gpg_err_code (rc) != GPG_ERR_WEAK_KEY)
+ break;
+ log_info(_("weak key created - retrying\n") );
+ }
+ if (rc)
+ {
+ log_error ("failed to set the key: %s\n", gpg_strerror (rc));
+ gcry_cipher_close (dek->chd);
+ dek->chd = NULL;
+ return rc;
+ }
+
+ gcry_create_nonce (dek->iv, dek->ivlen);
+ rc = gcry_cipher_setiv (dek->chd, dek->iv, dek->ivlen);
+ if (rc)
+ {
+ log_error ("failed to set the IV: %s\n", gpg_strerror (rc));
+ gcry_cipher_close (dek->chd);
+ dek->chd = NULL;
+ return rc;
+ }
+
+ return 0;
+}
+
+
+static int
+encode_session_key (DEK dek, gcry_sexp_t * r_data)
+{
+ gcry_sexp_t data;
+ char *p;
+ int rc;
+
+ p = xtrymalloc (64 + 2 * dek->keylen);
+ if (!p)
+ return gpg_error_from_syserror ();
+ strcpy (p, "(data\n (flags pkcs1)\n (value #");
+ bin2hex (dek->key, dek->keylen, p + strlen (p));
+ strcat (p, "#))\n");
+ rc = gcry_sexp_sscan (&data, NULL, p, strlen (p));
+ xfree (p);
+ *r_data = data;
+ return rc;
+}
+
+
+/* Encrypt the DEK under the key contained in CERT and return it as a
+ canonical S-Exp in encval. */
+static int
+encrypt_dek (const DEK dek, ksba_cert_t cert, unsigned char **encval)
+{
+ gcry_sexp_t s_ciph, s_data, s_pkey;
+ int rc;
+ ksba_sexp_t buf;
+ size_t len;
+
+ *encval = NULL;
+
+ /* get the key from the cert */
+ buf = ksba_cert_get_public_key (cert);
+ if (!buf)
+ {
+ log_error ("no public key for recipient\n");
+ return gpg_error (GPG_ERR_NO_PUBKEY);
+ }
+ len = gcry_sexp_canon_len (buf, 0, NULL, NULL);
+ if (!len)
+ {
+ log_error ("libksba did not return a proper S-Exp\n");
+ return gpg_error (GPG_ERR_BUG);
+ }
+ rc = gcry_sexp_sscan (&s_pkey, NULL, (char*)buf, len);
+ xfree (buf); buf = NULL;
+ if (rc)
+ {
+ log_error ("gcry_sexp_scan failed: %s\n", gpg_strerror (rc));
+ return rc;
+ }
+
+ /* Put the encoded cleartext into a simple list. */
+ s_data = NULL; /* (avoid compiler warning) */
+ rc = encode_session_key (dek, &s_data);
+ if (rc)
+ {
+ log_error ("encode_session_key failed: %s\n", gpg_strerror (rc));
+ return rc;
+ }
+
+ /* pass it to libgcrypt */
+ rc = gcry_pk_encrypt (&s_ciph, s_data, s_pkey);
+ gcry_sexp_release (s_data);
+ gcry_sexp_release (s_pkey);
+
+ /* Reformat it. */
+ rc = make_canon_sexp (s_ciph, encval, NULL);
+ gcry_sexp_release (s_ciph);
+ return rc;
+}
+
+
+
+/* do the actual encryption */
+static int
+encrypt_cb (void *cb_value, char *buffer, size_t count, size_t *nread)
+{
+ struct encrypt_cb_parm_s *parm = cb_value;
+ int blklen = parm->dek->ivlen;
+ unsigned char *p;
+ size_t n;
+
+ *nread = 0;
+ if (!buffer)
+ return -1; /* not supported */
+
+ if (parm->ready)
+ return -1;
+
+ if (count < blklen)
+ BUG ();
+
+ if (!parm->eof_seen)
+ { /* fillup the buffer */
+ p = parm->buffer;
+ for (n=parm->buflen; n < parm->bufsize; n++)
+ {
+ int c = getc (parm->fp);
+ if (c == EOF)
+ {
+ if (ferror (parm->fp))
+ {
+ parm->readerror = errno;
+ return -1;
+ }
+ parm->eof_seen = 1;
+ break;
+ }
+ p[n] = c;
+ }
+ parm->buflen = n;
+ }
+
+ n = parm->buflen < count? parm->buflen : count;
+ n = n/blklen * blklen;
+ if (n)
+ { /* encrypt the stuff */
+ gcry_cipher_encrypt (parm->dek->chd, buffer, n, parm->buffer, n);
+ *nread = n;
+ /* Who cares about cycles, take the easy way and shift the buffer */
+ parm->buflen -= n;
+ memmove (parm->buffer, parm->buffer+n, parm->buflen);
+ }
+ else if (parm->eof_seen)
+ { /* no complete block but eof: add padding */
+ /* fixme: we should try to do this also in the above code path */
+ int i, npad = blklen - (parm->buflen % blklen);
+ p = parm->buffer;
+ for (n=parm->buflen, i=0; n < parm->bufsize && i < npad; n++, i++)
+ p[n] = npad;
+ gcry_cipher_encrypt (parm->dek->chd, buffer, n, parm->buffer, n);
+ *nread = n;
+ parm->ready = 1;
+ }
+
+ return 0;
+}
+
+
+
+
+/* Perform an encrypt operation.
+
+ Encrypt the data received on DATA-FD and write it to OUT_FP. The
+ recipients are take from the certificate given in recplist; if this
+ is NULL it will be encrypted for a default recipient */
+int
+gpgsm_encrypt (ctrl_t ctrl, certlist_t recplist, int data_fd, FILE *out_fp)
+{
+ int rc = 0;
+ Base64Context b64writer = NULL;
+ gpg_error_t err;
+ ksba_writer_t writer;
+ ksba_reader_t reader = NULL;
+ ksba_cms_t cms = NULL;
+ ksba_stop_reason_t stopreason;
+ KEYDB_HANDLE kh = NULL;
+ struct encrypt_cb_parm_s encparm;
+ DEK dek = NULL;
+ int recpno;
+ FILE *data_fp = NULL;
+ certlist_t cl;
+ int count;
+
+ memset (&encparm, 0, sizeof encparm);
+
+ audit_set_type (ctrl->audit, AUDIT_TYPE_ENCRYPT);
+
+ /* Check that the certificate list is not empty and that at least
+ one certificate is not flagged as encrypt_to; i.e. is a real
+ recipient. */
+ for (cl = recplist; cl; cl = cl->next)
+ if (!cl->is_encrypt_to)
+ break;
+ if (!cl)
+ {
+ log_error(_("no valid recipients given\n"));
+ gpgsm_status (ctrl, STATUS_NO_RECP, "0");
+ audit_log_i (ctrl->audit, AUDIT_GOT_RECIPIENTS, 0);
+ rc = gpg_error (GPG_ERR_NO_PUBKEY);
+ goto leave;
+ }
+
+ for (count = 0, cl = recplist; cl; cl = cl->next)
+ count++;
+ audit_log_i (ctrl->audit, AUDIT_GOT_RECIPIENTS, count);
+
+ kh = keydb_new (0);
+ if (!kh)
+ {
+ log_error (_("failed to allocated keyDB handle\n"));
+ rc = gpg_error (GPG_ERR_GENERAL);
+ goto leave;
+ }
+
+ data_fp = fdopen ( dup (data_fd), "rb");
+ if (!data_fp)
+ {
+ rc = gpg_error (gpg_err_code_from_errno (errno));
+ log_error ("fdopen() failed: %s\n", strerror (errno));
+ goto leave;
+ }
+
+ err = ksba_reader_new (&reader);
+ if (err)
+ rc = err;
+ if (!rc)
+ rc = ksba_reader_set_cb (reader, encrypt_cb, &encparm);
+ if (rc)
+ goto leave;
+
+ encparm.fp = data_fp;
+
+ ctrl->pem_name = "ENCRYPTED MESSAGE";
+ rc = gpgsm_create_writer (&b64writer, ctrl, out_fp, NULL, &writer);
+ if (rc)
+ {
+ log_error ("can't create writer: %s\n", gpg_strerror (rc));
+ goto leave;
+ }
+
+ err = ksba_cms_new (&cms);
+ if (err)
+ {
+ rc = err;
+ goto leave;
+ }
+
+ err = ksba_cms_set_reader_writer (cms, reader, writer);
+ if (err)
+ {
+ log_debug ("ksba_cms_set_reader_writer failed: %s\n",
+ gpg_strerror (err));
+ rc = err;
+ goto leave;
+ }
+
+ audit_log (ctrl->audit, AUDIT_GOT_DATA);
+
+ /* We are going to create enveloped data with uninterpreted data as
+ inner content */
+ err = ksba_cms_set_content_type (cms, 0, KSBA_CT_ENVELOPED_DATA);
+ if (!err)
+ err = ksba_cms_set_content_type (cms, 1, KSBA_CT_DATA);
+ if (err)
+ {
+ log_debug ("ksba_cms_set_content_type failed: %s\n",
+ gpg_strerror (err));
+ rc = err;
+ goto leave;
+ }
+
+ /* Create a session key */
+ dek = xtrycalloc_secure (1, sizeof *dek);
+ if (!dek)
+ rc = out_of_core ();
+ else
+ {
+ dek->algoid = opt.def_cipher_algoid;
+ rc = init_dek (dek);
+ }
+ if (rc)
+ {
+ log_error ("failed to create the session key: %s\n",
+ gpg_strerror (rc));
+ goto leave;
+ }
+
+ err = ksba_cms_set_content_enc_algo (cms, dek->algoid, dek->iv, dek->ivlen);
+ if (err)
+ {
+ log_error ("ksba_cms_set_content_enc_algo failed: %s\n",
+ gpg_strerror (err));
+ rc = err;
+ goto leave;
+ }
+
+ encparm.dek = dek;
+ /* Use a ~8k (AES) or ~4k (3DES) buffer */
+ encparm.bufsize = 500 * dek->ivlen;
+ encparm.buffer = xtrymalloc (encparm.bufsize);
+ if (!encparm.buffer)
+ {
+ rc = out_of_core ();
+ goto leave;
+ }
+
+ audit_log_s (ctrl->audit, AUDIT_SESSION_KEY, dek->algoid);
+
+ /* Gather certificates of recipients, encrypt the session key for
+ each and store them in the CMS object */
+ for (recpno = 0, cl = recplist; cl; recpno++, cl = cl->next)
+ {
+ unsigned char *encval;
+
+ rc = encrypt_dek (dek, cl->cert, &encval);
+ if (rc)
+ {
+ audit_log_cert (ctrl->audit, AUDIT_ENCRYPTED_TO, cl->cert, rc);
+ log_error ("encryption failed for recipient no. %d: %s\n",
+ recpno, gpg_strerror (rc));
+ goto leave;
+ }
+
+ err = ksba_cms_add_recipient (cms, cl->cert);
+ if (err)
+ {
+ audit_log_cert (ctrl->audit, AUDIT_ENCRYPTED_TO, cl->cert, err);
+ log_error ("ksba_cms_add_recipient failed: %s\n",
+ gpg_strerror (err));
+ rc = err;
+ xfree (encval);
+ goto leave;
+ }
+
+ err = ksba_cms_set_enc_val (cms, recpno, encval);
+ xfree (encval);
+ audit_log_cert (ctrl->audit, AUDIT_ENCRYPTED_TO, cl->cert, err);
+ if (err)
+ {
+ log_error ("ksba_cms_set_enc_val failed: %s\n",
+ gpg_strerror (err));
+ rc = err;
+ goto leave;
+ }
+ }
+
+ /* Main control loop for encryption. */
+ recpno = 0;
+ do
+ {
+ err = ksba_cms_build (cms, &stopreason);
+ if (err)
+ {
+ log_debug ("ksba_cms_build failed: %s\n", gpg_strerror (err));
+ rc = err;
+ goto leave;
+ }
+ }
+ while (stopreason != KSBA_SR_READY);
+
+ if (encparm.readerror)
+ {
+ log_error ("error reading input: %s\n", strerror (encparm.readerror));
+ rc = gpg_error (gpg_err_code_from_errno (encparm.readerror));
+ goto leave;
+ }
+
+
+ rc = gpgsm_finish_writer (b64writer);
+ if (rc)
+ {
+ log_error ("write failed: %s\n", gpg_strerror (rc));
+ goto leave;
+ }
+ audit_log (ctrl->audit, AUDIT_ENCRYPTION_DONE);
+ log_info ("encrypted data created\n");
+
+ leave:
+ ksba_cms_release (cms);
+ gpgsm_destroy_writer (b64writer);
+ ksba_reader_release (reader);
+ keydb_release (kh);
+ xfree (dek);
+ if (data_fp)
+ fclose (data_fp);
+ xfree (encparm.buffer);
+ return rc;
+}
diff --git a/sm/export.c b/sm/export.c
new file mode 100644
index 0000000..fcf1dcc
--- /dev/null
+++ b/sm/export.c
@@ -0,0 +1,749 @@
+/* export.c - Export certificates and private keys.
+ * Copyright (C) 2002, 2003, 2004, 2007, 2009 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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuPG is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <time.h>
+#include <assert.h>
+
+#include "gpgsm.h"
+#include <gcrypt.h>
+#include <ksba.h>
+
+#include "keydb.h"
+#include "exechelp.h"
+#include "i18n.h"
+#include "sysutils.h"
+
+
+
+/* A table to store a fingerprint as used in a duplicates table. We
+ don't need to hash here because a fingerprint is already a perfect
+ hash value. This we use the most significant bits to index the
+ table and then use a linked list for the overflow. Possible
+ enhancement for very large number of certificates: Add a second
+ level table and then resort to a linked list. */
+struct duptable_s
+{
+ struct duptable_s *next;
+
+ /* Note that we only need to store 19 bytes because the first byte
+ is implictly given by the table index (we require at least 8
+ bits). */
+ unsigned char fpr[19];
+};
+typedef struct duptable_s *duptable_t;
+#define DUPTABLE_BITS 12
+#define DUPTABLE_SIZE (1 << DUPTABLE_BITS)
+
+
+static void print_short_info (ksba_cert_t cert, FILE *fp, estream_t stream);
+static gpg_error_t export_p12 (ctrl_t ctrl,
+ const unsigned char *certimg, size_t certimglen,
+ const char *prompt, const char *keygrip,
+ FILE **retfp);
+
+
+/* Create a table used to indetify duplicated certificates. */
+static duptable_t *
+create_duptable (void)
+{
+ return xtrycalloc (DUPTABLE_SIZE, sizeof (duptable_t));
+}
+
+static void
+destroy_duptable (duptable_t *table)
+{
+ int idx;
+ duptable_t t, t2;
+
+ if (table)
+ {
+ for (idx=0; idx < DUPTABLE_SIZE; idx++)
+ for (t = table[idx]; t; t = t2)
+ {
+ t2 = t->next;
+ xfree (t);
+ }
+ xfree (table);
+ }
+}
+
+/* Insert the 20 byte fingerprint FPR into TABLE. Sets EXITS to true
+ if the fingerprint already exists in the table. */
+static gpg_error_t
+insert_duptable (duptable_t *table, unsigned char *fpr, int *exists)
+{
+ size_t idx;
+ duptable_t t;
+
+ *exists = 0;
+ idx = fpr[0];
+#if DUPTABLE_BITS > 16 || DUPTABLE_BITS < 8
+#error cannot handle a table larger than 16 bits or smaller than 8 bits
+#elif DUPTABLE_BITS > 8
+ idx <<= (DUPTABLE_BITS - 8);
+ idx |= (fpr[1] & ~(~0 << 4));
+#endif
+
+ for (t = table[idx]; t; t = t->next)
+ if (!memcmp (t->fpr, fpr+1, 19))
+ break;
+ if (t)
+ {
+ *exists = 1;
+ return 0;
+ }
+ /* Insert that fingerprint. */
+ t = xtrymalloc (sizeof *t);
+ if (!t)
+ return gpg_error_from_syserror ();
+ memcpy (t->fpr, fpr+1, 19);
+ t->next = table[idx];
+ table[idx] = t;
+ return 0;
+}
+
+
+
+
+/* Export all certificates or just those given in NAMES. If STREAM is
+ not NULL the output is send to this extended stream. */
+void
+gpgsm_export (ctrl_t ctrl, strlist_t names, FILE *fp, estream_t stream)
+{
+ KEYDB_HANDLE hd = NULL;
+ KEYDB_SEARCH_DESC *desc = NULL;
+ int ndesc;
+ Base64Context b64writer = NULL;
+ ksba_writer_t writer;
+ strlist_t sl;
+ ksba_cert_t cert = NULL;
+ int rc=0;
+ int count = 0;
+ int i;
+ duptable_t *dtable;
+
+
+ dtable = create_duptable ();
+ if (!dtable)
+ {
+ log_error ("creating duplicates table failed: %s\n", strerror (errno));
+ goto leave;
+ }
+
+ hd = keydb_new (0);
+ if (!hd)
+ {
+ log_error ("keydb_new failed\n");
+ goto leave;
+ }
+
+ if (!names)
+ ndesc = 1;
+ else
+ {
+ for (sl=names, ndesc=0; sl; sl = sl->next, ndesc++)
+ ;
+ }
+
+ desc = xtrycalloc (ndesc, sizeof *desc);
+ if (!ndesc)
+ {
+ log_error ("allocating memory for export failed: %s\n",
+ gpg_strerror (out_of_core ()));
+ goto leave;
+ }
+
+ if (!names)
+ desc[0].mode = KEYDB_SEARCH_MODE_FIRST;
+ else
+ {
+ for (ndesc=0, sl=names; sl; sl = sl->next)
+ {
+ rc = keydb_classify_name (sl->d, desc+ndesc);
+ if (rc)
+ {
+ log_error ("key `%s' not found: %s\n",
+ sl->d, gpg_strerror (rc));
+ rc = 0;
+ }
+ else
+ ndesc++;
+ }
+ }
+
+ /* If all specifications are done by fingerprint or keygrip, we
+ switch to ephemeral mode so that _all_ currently available and
+ matching certificates are exported. */
+ if (names && ndesc)
+ {
+ for (i=0; (i < ndesc
+ && (desc[i].mode == KEYDB_SEARCH_MODE_FPR
+ || desc[i].mode == KEYDB_SEARCH_MODE_FPR20
+ || desc[i].mode == KEYDB_SEARCH_MODE_FPR16
+ || desc[i].mode == KEYDB_SEARCH_MODE_KEYGRIP)); i++)
+ ;
+ if (i == ndesc)
+ keydb_set_ephemeral (hd, 1);
+ }
+
+ while (!(rc = keydb_search (hd, desc, ndesc)))
+ {
+ unsigned char fpr[20];
+ int exists;
+
+ if (!names)
+ desc[0].mode = KEYDB_SEARCH_MODE_NEXT;
+
+ rc = keydb_get_cert (hd, &cert);
+ if (rc)
+ {
+ log_error ("keydb_get_cert failed: %s\n", gpg_strerror (rc));
+ goto leave;
+ }
+
+ gpgsm_get_fingerprint (cert, 0, fpr, NULL);
+ rc = insert_duptable (dtable, fpr, &exists);
+ if (rc)
+ {
+ log_error ("inserting into duplicates table failed: %s\n",
+ gpg_strerror (rc));
+ goto leave;
+ }
+
+ if (!exists && count && !ctrl->create_pem)
+ {
+ log_info ("exporting more than one certificate "
+ "is not possible in binary mode\n");
+ log_info ("ignoring other certificates\n");
+ break;
+ }
+
+ if (!exists)
+ {
+ const unsigned char *image;
+ size_t imagelen;
+
+ image = ksba_cert_get_image (cert, &imagelen);
+ if (!image)
+ {
+ log_error ("ksba_cert_get_image failed\n");
+ goto leave;
+ }
+
+
+ if (ctrl->create_pem)
+ {
+ if (count)
+ {
+ if (stream)
+ es_putc ('\n', stream);
+ else
+ putc ('\n', fp);
+ }
+ print_short_info (cert, fp, stream);
+ if (stream)
+ es_putc ('\n', stream);
+ else
+ putc ('\n', fp);
+ }
+ count++;
+
+ if (!b64writer)
+ {
+ ctrl->pem_name = "CERTIFICATE";
+ rc = gpgsm_create_writer (&b64writer, ctrl, fp, stream, &writer);
+ if (rc)
+ {
+ log_error ("can't create writer: %s\n", gpg_strerror (rc));
+ goto leave;
+ }
+ }
+
+ rc = ksba_writer_write (writer, image, imagelen);
+ if (rc)
+ {
+ log_error ("write error: %s\n", gpg_strerror (rc));
+ goto leave;
+ }
+
+ if (ctrl->create_pem)
+ {
+ /* We want one certificate per PEM block */
+ rc = gpgsm_finish_writer (b64writer);
+ if (rc)
+ {
+ log_error ("write failed: %s\n", gpg_strerror (rc));
+ goto leave;
+ }
+ gpgsm_destroy_writer (b64writer);
+ b64writer = NULL;
+ }
+ }
+
+ ksba_cert_release (cert);
+ cert = NULL;
+ }
+ if (rc && rc != -1)
+ log_error ("keydb_search failed: %s\n", gpg_strerror (rc));
+ else if (b64writer)
+ {
+ rc = gpgsm_finish_writer (b64writer);
+ if (rc)
+ {
+ log_error ("write failed: %s\n", gpg_strerror (rc));
+ goto leave;
+ }
+ }
+
+ leave:
+ gpgsm_destroy_writer (b64writer);
+ ksba_cert_release (cert);
+ xfree (desc);
+ keydb_release (hd);
+ destroy_duptable (dtable);
+}
+
+
+/* Export a certificates and its private key. */
+void
+gpgsm_p12_export (ctrl_t ctrl, const char *name, FILE *fp)
+{
+ KEYDB_HANDLE hd;
+ KEYDB_SEARCH_DESC *desc = NULL;
+ Base64Context b64writer = NULL;
+ ksba_writer_t writer;
+ ksba_cert_t cert = NULL;
+ int rc=0;
+ const unsigned char *image;
+ size_t imagelen;
+ char *keygrip = NULL;
+ char *prompt;
+ char buffer[1024];
+ int nread;
+ FILE *datafp = NULL;
+
+
+ hd = keydb_new (0);
+ if (!hd)
+ {
+ log_error ("keydb_new failed\n");
+ goto leave;
+ }
+
+ desc = xtrycalloc (1, sizeof *desc);
+ if (!desc)
+ {
+ log_error ("allocating memory for export failed: %s\n",
+ gpg_strerror (out_of_core ()));
+ goto leave;
+ }
+
+ rc = keydb_classify_name (name, desc);
+ if (rc)
+ {
+ log_error ("key `%s' not found: %s\n",
+ name, gpg_strerror (rc));
+ goto leave;
+ }
+
+ /* Lookup the certificate and make sure that it is unique. */
+ rc = keydb_search (hd, desc, 1);
+ if (!rc)
+ {
+ rc = keydb_get_cert (hd, &cert);
+ if (rc)
+ {
+ log_error ("keydb_get_cert failed: %s\n", gpg_strerror (rc));
+ goto leave;
+ }
+
+ next_ambiguous:
+ rc = keydb_search (hd, desc, 1);
+ if (!rc)
+ {
+ ksba_cert_t cert2 = NULL;
+
+ if (!keydb_get_cert (hd, &cert2))
+ {
+ if (gpgsm_certs_identical_p (cert, cert2))
+ {
+ ksba_cert_release (cert2);
+ goto next_ambiguous;
+ }
+ ksba_cert_release (cert2);
+ }
+ rc = gpg_error (GPG_ERR_AMBIGUOUS_NAME);
+ }
+ else if (rc == -1 || gpg_err_code (rc) == GPG_ERR_EOF)
+ rc = 0;
+ if (rc)
+ {
+ log_error ("key `%s' not found: %s\n",
+ name, gpg_strerror (rc));
+ goto leave;
+ }
+ }
+
+ keygrip = gpgsm_get_keygrip_hexstring (cert);
+ if (!keygrip || gpgsm_agent_havekey (ctrl, keygrip))
+ {
+ /* Note, that the !keygrip case indicates a bad certificate. */
+ rc = gpg_error (GPG_ERR_NO_SECKEY);
+ log_error ("can't export key `%s': %s\n", name, gpg_strerror (rc));
+ goto leave;
+ }
+
+ image = ksba_cert_get_image (cert, &imagelen);
+ if (!image)
+ {
+ log_error ("ksba_cert_get_image failed\n");
+ goto leave;
+ }
+
+ if (ctrl->create_pem)
+ {
+ print_short_info (cert, fp, NULL);
+ putc ('\n', fp);
+ }
+
+ if (opt.p12_charset && ctrl->create_pem)
+ {
+ fprintf (fp, "The passphrase is %s encoded.\n\n",
+ opt.p12_charset);
+ }
+
+ ctrl->pem_name = "PKCS12";
+ rc = gpgsm_create_writer (&b64writer, ctrl, fp, NULL, &writer);
+ if (rc)
+ {
+ log_error ("can't create writer: %s\n", gpg_strerror (rc));
+ goto leave;
+ }
+
+
+ prompt = gpgsm_format_keydesc (cert);
+ rc = export_p12 (ctrl, image, imagelen, prompt, keygrip, &datafp);
+ xfree (prompt);
+ if (rc)
+ goto leave;
+ rewind (datafp);
+ while ( (nread = fread (buffer, 1, sizeof buffer, datafp)) > 0 )
+ if ((rc = ksba_writer_write (writer, buffer, nread)))
+ {
+ log_error ("write failed: %s\n", gpg_strerror (rc));
+ goto leave;
+ }
+ if (ferror (datafp))
+ {
+ rc = gpg_error_from_errno (rc);
+ log_error ("error reading temporary file: %s\n", gpg_strerror (rc));
+ goto leave;
+ }
+
+ if (ctrl->create_pem)
+ {
+ /* We want one certificate per PEM block */
+ rc = gpgsm_finish_writer (b64writer);
+ if (rc)
+ {
+ log_error ("write failed: %s\n", gpg_strerror (rc));
+ goto leave;
+ }
+ gpgsm_destroy_writer (b64writer);
+ b64writer = NULL;
+ }
+
+ ksba_cert_release (cert);
+ cert = NULL;
+
+ leave:
+ if (datafp)
+ fclose (datafp);
+ gpgsm_destroy_writer (b64writer);
+ ksba_cert_release (cert);
+ xfree (desc);
+ keydb_release (hd);
+}
+
+
+/* Call either es_putc or the plain putc. */
+static void
+do_putc (int value, FILE *fp, estream_t stream)
+{
+ if (stream)
+ es_putc (value, stream);
+ else
+ putc (value, fp);
+}
+
+/* Call either es_fputs or the plain fputs. */
+static void
+do_fputs (const char *string, FILE *fp, estream_t stream)
+{
+ if (stream)
+ es_fputs (string, stream);
+ else
+ fputs (string, fp);
+}
+
+
+/* Print some info about the certifciate CERT to FP or STREAM */
+static void
+print_short_info (ksba_cert_t cert, FILE *fp, estream_t stream)
+{
+ char *p;
+ ksba_sexp_t sexp;
+ int idx;
+
+ for (idx=0; (p = ksba_cert_get_issuer (cert, idx)); idx++)
+ {
+ do_fputs ((!idx
+ ? "Issuer ...: "
+ : "\n aka ...: "), fp, stream);
+ if (stream)
+ gpgsm_es_print_name (stream, p);
+ else
+ gpgsm_print_name (fp, p);
+ xfree (p);
+ }
+ do_putc ('\n', fp, stream);
+
+ do_fputs ("Serial ...: ", fp, stream);
+ sexp = ksba_cert_get_serial (cert);
+ if (sexp)
+ {
+ int len;
+ const unsigned char *s = sexp;
+
+ if (*s == '(')
+ {
+ s++;
+ for (len=0; *s && *s != ':' && digitp (s); s++)
+ len = len*10 + atoi_1 (s);
+ if (*s == ':')
+ {
+ if (stream)
+ es_write_hexstring (stream, s+1, len, 0, NULL);
+ else
+ print_hexstring (fp, s+1, len, 0);
+ }
+ }
+ xfree (sexp);
+ }
+ do_putc ('\n', fp, stream);
+
+ for (idx=0; (p = ksba_cert_get_subject (cert, idx)); idx++)
+ {
+ do_fputs ((!idx
+ ? "Subject ..: "
+ : "\n aka ..: "), fp, stream);
+ if (stream)
+ gpgsm_es_print_name (stream, p);
+ else
+ gpgsm_print_name (fp, p);
+ xfree (p);
+ }
+ do_putc ('\n', fp, stream);
+}
+
+
+static gpg_error_t
+popen_protect_tool (ctrl_t ctrl, const char *pgmname,
+ FILE *infile, FILE *outfile, FILE **statusfile,
+ const char *prompt, const char *keygrip,
+ pid_t *pid)
+{
+ const char *argv[22];
+ int i=0;
+
+ /* Make sure that the agent is running so that the protect tool is
+ able to ask for a passphrase. This has only an effect under W32
+ where the agent is started on demand; sending a NOP does not harm
+ on other platforms. This is not really necessary anymore because
+ the protect tool does this now by itself; it does not harm either.*/
+ gpgsm_agent_send_nop (ctrl);
+
+ argv[i++] = "--homedir";
+ argv[i++] = opt.homedir;
+ argv[i++] = "--p12-export";
+ argv[i++] = "--have-cert";
+ argv[i++] = "--prompt";
+ argv[i++] = prompt?prompt:"";
+ argv[i++] = "--enable-status-msg";
+ if (opt.p12_charset)
+ {
+ argv[i++] = "--p12-charset";
+ argv[i++] = opt.p12_charset;
+ }
+ if (opt.agent_program)
+ {
+ argv[i++] = "--agent-program";
+ argv[i++] = opt.agent_program;
+ }
+ argv[i++] = "--",
+ argv[i++] = keygrip,
+ argv[i] = NULL;
+ assert (i < sizeof argv);
+
+ return gnupg_spawn_process (pgmname, argv, infile, outfile,
+ setup_pinentry_env, (128|64),
+ statusfile, pid);
+}
+
+
+static gpg_error_t
+export_p12 (ctrl_t ctrl, const unsigned char *certimg, size_t certimglen,
+ const char *prompt, const char *keygrip,
+ FILE **retfp)
+{
+ const char *pgmname;
+ gpg_error_t err = 0, child_err = 0;
+ int c, cont_line;
+ unsigned int pos;
+ FILE *infp = NULL, *outfp = NULL, *fp = NULL;
+ char buffer[1024];
+ pid_t pid = -1;
+ int bad_pass = 0;
+
+ if (!opt.protect_tool_program || !*opt.protect_tool_program)
+ pgmname = gnupg_module_name (GNUPG_MODULE_NAME_PROTECT_TOOL);
+ else
+ pgmname = opt.protect_tool_program;
+
+ infp = gnupg_tmpfile ();
+ if (!infp)
+ {
+ err = gpg_error_from_syserror ();
+ log_error (_("error creating temporary file: %s\n"), strerror (errno));
+ goto cleanup;
+ }
+
+ if (fwrite (certimg, certimglen, 1, infp) != 1)
+ {
+ err = gpg_error_from_syserror ();
+ log_error (_("error writing to temporary file: %s\n"),
+ strerror (errno));
+ goto cleanup;
+ }
+
+ outfp = gnupg_tmpfile ();
+ if (!outfp)
+ {
+ err = gpg_error_from_syserror ();
+ log_error (_("error creating temporary file: %s\n"), strerror (errno));
+ goto cleanup;
+ }
+
+ err = popen_protect_tool (ctrl,
+ pgmname, infp, outfp, &fp, prompt, keygrip, &pid);
+ if (err)
+ {
+ pid = -1;
+ goto cleanup;
+ }
+ fclose (infp);
+ infp = NULL;
+
+ /* Read stderr of the protect tool. */
+ pos = 0;
+ cont_line = 0;
+ while ((c=getc (fp)) != EOF)
+ {
+ /* fixme: We could here grep for status information of the
+ protect tool to figure out better error codes for
+ CHILD_ERR. */
+ buffer[pos++] = c;
+ if (pos >= sizeof buffer - 5 || c == '\n')
+ {
+ buffer[pos - (c == '\n')] = 0;
+ if (cont_line)
+ log_printf ("%s", buffer);
+ else
+ {
+ if (!strncmp (buffer, "gpg-protect-tool: [PROTECT-TOOL:] ",34))
+ {
+ char *p, *pend;
+
+ p = buffer + 34;
+ pend = strchr (p, ' ');
+ if (pend)
+ *pend = 0;
+ if ( !strcmp (p, "bad-passphrase"))
+ bad_pass++;
+ }
+ else
+ log_info ("%s", buffer);
+ }
+ pos = 0;
+ cont_line = (c != '\n');
+ }
+ }
+
+ if (pos)
+ {
+ buffer[pos] = 0;
+ if (cont_line)
+ log_printf ("%s\n", buffer);
+ else
+ log_info ("%s\n", buffer);
+ }
+ else if (cont_line)
+ log_printf ("\n");
+
+ /* If we found no error in the output of the child, setup a suitable
+ error code, which will later be reset if the exit status of the
+ child is 0. */
+ if (!child_err)
+ child_err = gpg_error (GPG_ERR_DECRYPT_FAILED);
+
+ cleanup:
+ if (infp)
+ fclose (infp);
+ if (fp)
+ fclose (fp);
+ if (pid != -1)
+ {
+ if (!gnupg_wait_process (pgmname, pid, NULL))
+ child_err = 0;
+ }
+ if (!err)
+ err = child_err;
+ if (err)
+ {
+ if (outfp)
+ fclose (outfp);
+ }
+ else
+ *retfp = outfp;
+ if (bad_pass)
+ {
+ /* During export this is the passphrase used to unprotect the
+ key and not the pkcs#12 thing as in export. Therefore we can
+ issue the regular passphrase status. FIXME: replace the all
+ zero keyid by a regular one. */
+ gpgsm_status (ctrl, STATUS_BAD_PASSPHRASE, "0000000000000000");
+ }
+ return err;
+}
+
diff --git a/sm/fingerprint.c b/sm/fingerprint.c
new file mode 100644
index 0000000..4704f59
--- /dev/null
+++ b/sm/fingerprint.c
@@ -0,0 +1,347 @@
+/* fingerprint.c - Get the fingerprint
+ * 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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuPG is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <time.h>
+#include <assert.h>
+
+
+#include "gpgsm.h"
+#include <gcrypt.h>
+#include <ksba.h>
+
+/* Return the fingerprint of the certificate (we can't put this into
+ libksba because we need libgcrypt support). The caller must
+ provide an array of sufficient length or NULL so that the function
+ allocates the array. If r_len is not NULL, the length of the
+ digest is returned; well, this can also be done by using
+ gcry_md_get_algo_dlen(). If algo is 0, a SHA-1 will be used.
+
+ If there is a problem , the function does never return NULL but a
+ digest of all 0xff.
+ */
+unsigned char *
+gpgsm_get_fingerprint (ksba_cert_t cert, int algo,
+ unsigned char *array, int *r_len)
+{
+ gcry_md_hd_t md;
+ int rc, len;
+
+ if (!algo)
+ algo = GCRY_MD_SHA1;
+
+ len = gcry_md_get_algo_dlen (algo);
+ assert (len);
+ if (!array)
+ array = xmalloc (len);
+
+ if (r_len)
+ *r_len = len;
+
+ /* Fist check whether we have cached the fingerprint. */
+ if (algo == GCRY_MD_SHA1)
+ {
+ size_t buflen;
+
+ assert (len >= 20);
+ if (!ksba_cert_get_user_data (cert, "sha1-fingerprint",
+ array, len, &buflen)
+ && buflen == 20)
+ return array;
+ }
+
+ /* No, need to compute it. */
+ rc = gcry_md_open (&md, algo, 0);
+ if (rc)
+ {
+ log_error ("md_open failed: %s\n", gpg_strerror (rc));
+ memset (array, 0xff, len); /* better return an invalid fpr than NULL */
+ return array;
+ }
+
+ rc = ksba_cert_hash (cert, 0, HASH_FNC, md);
+ if (rc)
+ {
+ log_error ("ksba_cert_hash failed: %s\n", gpg_strerror (rc));
+ gcry_md_close (md);
+ memset (array, 0xff, len); /* better return an invalid fpr than NULL */
+ return array;
+ }
+ gcry_md_final (md);
+ memcpy (array, gcry_md_read(md, algo), len );
+ gcry_md_close (md);
+
+ /* Cache an SHA-1 fingerprint. */
+ if ( algo == GCRY_MD_SHA1 )
+ ksba_cert_set_user_data (cert, "sha1-fingerprint", array, 20);
+
+ return array;
+}
+
+
+/* Return an allocated buffer with the formatted fingerprint */
+char *
+gpgsm_get_fingerprint_string (ksba_cert_t cert, int algo)
+{
+ unsigned char digest[MAX_DIGEST_LEN];
+ char *buf;
+ int len;
+
+ if (!algo)
+ algo = GCRY_MD_SHA1;
+
+ len = gcry_md_get_algo_dlen (algo);
+ assert (len <= MAX_DIGEST_LEN );
+ gpgsm_get_fingerprint (cert, algo, digest, NULL);
+ buf = xmalloc (len*3+1);
+ bin2hexcolon (digest, len, buf);
+ return buf;
+}
+
+/* Return an allocated buffer with the formatted fingerprint as one
+ large hexnumber */
+char *
+gpgsm_get_fingerprint_hexstring (ksba_cert_t cert, int algo)
+{
+ unsigned char digest[MAX_DIGEST_LEN];
+ char *buf;
+ int len;
+
+ if (!algo)
+ algo = GCRY_MD_SHA1;
+
+ len = gcry_md_get_algo_dlen (algo);
+ assert (len <= MAX_DIGEST_LEN );
+ gpgsm_get_fingerprint (cert, algo, digest, NULL);
+ buf = xmalloc (len*2+1);
+ bin2hex (digest, len, buf);
+ return buf;
+}
+
+/* Return a certificate ID. These are the last 4 bytes of the SHA-1
+ fingerprint. If R_HIGH is not NULL the next 4 bytes are stored
+ there. */
+unsigned long
+gpgsm_get_short_fingerprint (ksba_cert_t cert, unsigned long *r_high)
+{
+ unsigned char digest[20];
+
+ gpgsm_get_fingerprint (cert, GCRY_MD_SHA1, digest, NULL);
+ if (r_high)
+ *r_high = ((digest[12]<<24)|(digest[13]<<16)|(digest[14]<< 8)|digest[15]);
+ return ((digest[16]<<24)|(digest[17]<<16)|(digest[18]<< 8)|digest[19]);
+}
+
+
+/* Return the so called KEYGRIP which is the SHA-1 hash of the public
+ key parameters expressed as an canoncial encoded S-Exp. ARRAY must
+ be 20 bytes long. Returns ARRAY or a newly allocated buffer if ARRAY was
+ given as NULL. May return NULL on error. */
+unsigned char *
+gpgsm_get_keygrip (ksba_cert_t cert, unsigned char *array)
+{
+ gcry_sexp_t s_pkey;
+ int rc;
+ ksba_sexp_t p;
+ size_t n;
+
+ p = ksba_cert_get_public_key (cert);
+ if (!p)
+ return NULL; /* oops */
+
+ if (DBG_X509)
+ log_debug ("get_keygrip for public key\n");
+ n = gcry_sexp_canon_len (p, 0, NULL, NULL);
+ if (!n)
+ {
+ log_error ("libksba did not return a proper S-Exp\n");
+ return NULL;
+ }
+ rc = gcry_sexp_sscan ( &s_pkey, NULL, (char*)p, n);
+ xfree (p);
+ if (rc)
+ {
+ log_error ("gcry_sexp_scan failed: %s\n", gpg_strerror (rc));
+ return NULL;
+ }
+ array = gcry_pk_get_keygrip (s_pkey, array);
+ gcry_sexp_release (s_pkey);
+ if (!array)
+ {
+ rc = gpg_error (GPG_ERR_GENERAL);
+ log_error ("can't calculate keygrip\n");
+ return NULL;
+ }
+ if (DBG_X509)
+ log_printhex ("keygrip=", array, 20);
+
+ return array;
+}
+
+/* Return an allocated buffer with the keygrip of CERT encoded as a
+ hexstring. NULL is returned in case of error. */
+char *
+gpgsm_get_keygrip_hexstring (ksba_cert_t cert)
+{
+ unsigned char grip[20];
+ char *buf;
+
+ if (!gpgsm_get_keygrip (cert, grip))
+ return NULL;
+ buf = xtrymalloc (20*2+1);
+ if (buf)
+ bin2hex (grip, 20, buf);
+ return buf;
+}
+
+
+/* Return the PK algorithm used by CERT as well as the length in bits
+ of the public key at NBITS. */
+int
+gpgsm_get_key_algo_info (ksba_cert_t cert, unsigned int *nbits)
+{
+ gcry_sexp_t s_pkey;
+ int rc;
+ ksba_sexp_t p;
+ size_t n;
+ gcry_sexp_t l1, l2;
+ const char *name;
+ char namebuf[128];
+
+ if (nbits)
+ *nbits = 0;
+
+ p = ksba_cert_get_public_key (cert);
+ if (!p)
+ return 0;
+ n = gcry_sexp_canon_len (p, 0, NULL, NULL);
+ if (!n)
+ {
+ xfree (p);
+ return 0;
+ }
+ rc = gcry_sexp_sscan (&s_pkey, NULL, (char *)p, n);
+ xfree (p);
+ if (rc)
+ return 0;
+
+ if (nbits)
+ *nbits = gcry_pk_get_nbits (s_pkey);
+
+ /* Breaking the algorithm out of the S-exp is a bit of a challenge ... */
+ l1 = gcry_sexp_find_token (s_pkey, "public-key", 0);
+ if (!l1)
+ {
+ gcry_sexp_release (s_pkey);
+ return 0;
+ }
+ l2 = gcry_sexp_cadr (l1);
+ gcry_sexp_release (l1);
+ l1 = l2;
+ name = gcry_sexp_nth_data (l1, 0, &n);
+ if (name)
+ {
+ if (n > sizeof namebuf -1)
+ n = sizeof namebuf -1;
+ memcpy (namebuf, name, n);
+ namebuf[n] = 0;
+ }
+ else
+ *namebuf = 0;
+ gcry_sexp_release (l1);
+ gcry_sexp_release (s_pkey);
+ return gcry_pk_map_name (namebuf);
+}
+
+
+
+
+/* For certain purposes we need a certificate id which has an upper
+ limit of the size. We use the hash of the issuer name and the
+ serial number for this. In most cases the serial number is not
+ that large and the resulting string can be passed on an assuan
+ command line. Everything is hexencoded with the serialnumber
+ delimited from the hash by a dot.
+
+ The caller must free the string.
+*/
+char *
+gpgsm_get_certid (ksba_cert_t cert)
+{
+ ksba_sexp_t serial;
+ char *p;
+ char *endp;
+ unsigned char hash[20];
+ unsigned long n;
+ char *certid;
+ int i;
+
+ p = ksba_cert_get_issuer (cert, 0);
+ if (!p)
+ return NULL; /* Ooops: No issuer */
+ gcry_md_hash_buffer (GCRY_MD_SHA1, hash, p, strlen (p));
+ xfree (p);
+
+ serial = ksba_cert_get_serial (cert);
+ if (!serial)
+ return NULL; /* oops: no serial number */
+ p = (char *)serial;
+ if (*p != '(')
+ {
+ log_error ("Ooops: invalid serial number\n");
+ xfree (serial);
+ return NULL;
+ }
+ p++;
+ n = strtoul (p, &endp, 10);
+ p = endp;
+ if (*p != ':')
+ {
+ log_error ("Ooops: invalid serial number (no colon)\n");
+ xfree (serial);
+ return NULL;
+ }
+ p++;
+
+ certid = xtrymalloc ( 40 + 1 + n*2 + 1);
+ if (!certid)
+ {
+ xfree (serial);
+ return NULL; /* out of core */
+ }
+
+ for (i=0, endp = certid; i < 20; i++, endp += 2 )
+ sprintf (endp, "%02X", hash[i]);
+ *endp++ = '.';
+ for (i=0; i < n; i++, endp += 2)
+ sprintf (endp, "%02X", ((unsigned char*)p)[i]);
+ *endp = 0;
+
+ xfree (serial);
+ return certid;
+}
+
+
+
+
diff --git a/sm/gpgsm.c b/sm/gpgsm.c
new file mode 100644
index 0000000..484ce9d
--- /dev/null
+++ b/sm/gpgsm.c
@@ -0,0 +1,2181 @@
+/* gpgsm.c - GnuPG for S/MIME
+ * Copyright (C) 2001, 2002, 2003, 2004, 2005,
+ * 2006, 2007, 2008 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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuPG is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <fcntl.h>
+/*#include <mcheck.h>*/
+
+#include "gpgsm.h"
+#include <gcrypt.h>
+#include <assuan.h> /* malloc hooks */
+
+#include "../kbx/keybox.h" /* malloc hooks */
+#include "i18n.h"
+#include "keydb.h"
+#include "sysutils.h"
+#include "gc-opt-flags.h"
+
+
+#ifndef O_BINARY
+#define O_BINARY 0
+#endif
+
+enum cmd_and_opt_values {
+ aNull = 0,
+ oArmor = 'a',
+ aDetachedSign = 'b',
+ aSym = 'c',
+ aDecrypt = 'd',
+ aEncr = 'e',
+ aListKeys = 'k',
+ aListSecretKeys = 'K',
+ oDryRun = 'n',
+ oOutput = 'o',
+ oQuiet = 'q',
+ oRecipient = 'r',
+ aSign = 's',
+ oUser = 'u',
+ oVerbose = 'v',
+ oBatch = 500,
+ aClearsign,
+ aKeygen,
+ aSignEncr,
+ aDeleteKey,
+ aImport,
+ aVerify,
+ aListExternalKeys,
+ aListChain,
+ aSendKeys,
+ aRecvKeys,
+ aExport,
+ aExportSecretKeyP12,
+ aServer,
+ aLearnCard,
+ aCallDirmngr,
+ aCallProtectTool,
+ aPasswd,
+ aGPGConfList,
+ aGPGConfTest,
+ aDumpKeys,
+ aDumpChain,
+ aDumpSecretKeys,
+ aDumpExternalKeys,
+ aKeydbClearSomeCertFlags,
+ aFingerprint,
+
+ oOptions,
+ oDebug,
+ oDebugLevel,
+ oDebugAll,
+ oDebugNone,
+ oDebugWait,
+ oDebugAllowCoreDump,
+ oDebugNoChainValidation,
+ oDebugIgnoreExpiration,
+ oFixedPassphrase,
+ oLogFile,
+ oNoLogFile,
+ oAuditLog,
+ oHtmlAuditLog,
+
+ oEnableSpecialFilenames,
+
+ oAgentProgram,
+ oDisplay,
+ oTTYname,
+ oTTYtype,
+ oLCctype,
+ oLCmessages,
+ oXauthority,
+
+ oPreferSystemDirmngr,
+ oDirmngrProgram,
+ oDisableDirmngr,
+ oProtectToolProgram,
+ oFakedSystemTime,
+
+
+ oAssumeArmor,
+ oAssumeBase64,
+ oAssumeBinary,
+
+ oBase64,
+ oNoArmor,
+ oP12Charset,
+
+ oDisableCRLChecks,
+ oEnableCRLChecks,
+ oDisableTrustedCertCRLCheck,
+ oEnableTrustedCertCRLCheck,
+ oForceCRLRefresh,
+
+ oDisableOCSP,
+ oEnableOCSP,
+
+ oIncludeCerts,
+ oPolicyFile,
+ oDisablePolicyChecks,
+ oEnablePolicyChecks,
+ oAutoIssuerKeyRetrieve,
+
+ oWithFingerprint,
+ oWithMD5Fingerprint,
+ oAnswerYes,
+ oAnswerNo,
+ oKeyring,
+ oDefaultKey,
+ oDefRecipient,
+ oDefRecipientSelf,
+ oNoDefRecipient,
+ oStatusFD,
+ oCipherAlgo,
+ oDigestAlgo,
+ oExtraDigestAlgo,
+ oNoVerbose,
+ oNoSecmemWarn,
+ oNoDefKeyring,
+ oNoGreeting,
+ oNoTTY,
+ oNoOptions,
+ oNoBatch,
+ oHomedir,
+ oWithColons,
+ oWithKeyData,
+ oWithValidation,
+ oWithEphemeralKeys,
+ oSkipVerify,
+ oValidationModel,
+ oKeyServer,
+ oEncryptTo,
+ oNoEncryptTo,
+ oLoggerFD,
+ oDisableCipherAlgo,
+ oDisablePubkeyAlgo,
+ oIgnoreTimeConflict,
+ oNoRandomSeedFile,
+ oNoCommonCertsImport,
+ oIgnoreCertExtension
+ };
+
+
+static ARGPARSE_OPTS opts[] = {
+
+ ARGPARSE_group (300, N_("@Commands:\n ")),
+
+ ARGPARSE_c (aSign, "sign", N_("make a signature")),
+ ARGPARSE_c (aClearsign, "clearsign", N_("make a clear text signature") ),
+ ARGPARSE_c (aDetachedSign, "detach-sign", N_("make a detached signature")),
+ ARGPARSE_c (aEncr, "encrypt", N_("encrypt data")),
+ ARGPARSE_c (aSym, "symmetric", N_("encryption only with symmetric cipher")),
+ ARGPARSE_c (aDecrypt, "decrypt", N_("decrypt data (default)")),
+ ARGPARSE_c (aVerify, "verify", N_("verify a signature")),
+ ARGPARSE_c (aListKeys, "list-keys", N_("list keys")),
+ ARGPARSE_c (aListExternalKeys, "list-external-keys",
+ N_("list external keys")),
+ ARGPARSE_c (aListSecretKeys, "list-secret-keys", N_("list secret keys")),
+ ARGPARSE_c (aListChain, "list-chain", N_("list certificate chain")),
+ ARGPARSE_c (aFingerprint, "fingerprint", N_("list keys and fingerprints")),
+ ARGPARSE_c (aKeygen, "gen-key", N_("generate a new key pair")),
+ ARGPARSE_c (aDeleteKey, "delete-keys",
+ N_("remove keys from the public keyring")),
+ ARGPARSE_c (aSendKeys, "send-keys", N_("export keys to a key server")),
+ ARGPARSE_c (aRecvKeys, "recv-keys", N_("import keys from a key server")),
+ ARGPARSE_c (aImport, "import", N_("import certificates")),
+ ARGPARSE_c (aExport, "export", N_("export certificates")),
+ ARGPARSE_c (aExportSecretKeyP12, "export-secret-key-p12", "@"),
+ ARGPARSE_c (aLearnCard, "learn-card", N_("register a smartcard")),
+ ARGPARSE_c (aServer, "server", N_("run in server mode")),
+ ARGPARSE_c (aCallDirmngr, "call-dirmngr",
+ N_("pass a command to the dirmngr")),
+ ARGPARSE_c (aCallProtectTool, "call-protect-tool",
+ N_("invoke gpg-protect-tool")),
+ ARGPARSE_c (aPasswd, "passwd", N_("change a passphrase")),
+ ARGPARSE_c (aGPGConfList, "gpgconf-list", "@"),
+ ARGPARSE_c (aGPGConfTest, "gpgconf-test", "@"),
+
+ ARGPARSE_c (aDumpKeys, "dump-cert", "@"),
+ ARGPARSE_c (aDumpKeys, "dump-keys", "@"),
+ ARGPARSE_c (aDumpChain, "dump-chain", "@"),
+ ARGPARSE_c (aDumpExternalKeys, "dump-external-keys", "@"),
+ ARGPARSE_c (aDumpSecretKeys, "dump-secret-keys", "@"),
+ ARGPARSE_c (aKeydbClearSomeCertFlags, "keydb-clear-some-cert-flags", "@"),
+
+ ARGPARSE_group (301, N_("@\nOptions:\n ")),
+
+ ARGPARSE_s_n (oArmor, "armor", N_("create ascii armored output")),
+ ARGPARSE_s_n (oArmor, "armour", "@"),
+ ARGPARSE_s_n (oBase64, "base64", N_("create base-64 encoded output")),
+
+ ARGPARSE_s_s (oP12Charset, "p12-charset", "@"),
+
+ ARGPARSE_s_n (oAssumeArmor, "assume-armor",
+ N_("assume input is in PEM format")),
+ ARGPARSE_s_n (oAssumeBase64, "assume-base64",
+ N_("assume input is in base-64 format")),
+ ARGPARSE_s_n (oAssumeBinary, "assume-binary",
+ N_("assume input is in binary format")),
+
+ ARGPARSE_s_s (oRecipient, "recipient", N_("|USER-ID|encrypt for USER-ID")),
+
+ ARGPARSE_s_n (oPreferSystemDirmngr,"prefer-system-dirmngr",
+ N_("use system's dirmngr if available")),
+
+ ARGPARSE_s_n (oDisableCRLChecks, "disable-crl-checks",
+ N_("never consult a CRL")),
+ ARGPARSE_s_n (oEnableCRLChecks, "enable-crl-checks", "@"),
+ ARGPARSE_s_n (oDisableTrustedCertCRLCheck,
+ "disable-trusted-cert-crl-check", "@"),
+ ARGPARSE_s_n (oEnableTrustedCertCRLCheck,
+ "enable-trusted-cert-crl-check", "@"),
+
+ ARGPARSE_s_n (oForceCRLRefresh, "force-crl-refresh", "@"),
+
+ ARGPARSE_s_n (oDisableOCSP, "disable-ocsp", "@"),
+ ARGPARSE_s_n (oEnableOCSP, "enable-ocsp", N_("check validity using OCSP")),
+
+ ARGPARSE_s_s (oValidationModel, "validation-model", "@"),
+
+ ARGPARSE_s_i (oIncludeCerts, "include-certs",
+ N_("|N|number of certificates to include") ),
+
+ ARGPARSE_s_s (oPolicyFile, "policy-file",
+ N_("|FILE|take policy information from FILE")),
+
+ ARGPARSE_s_n (oDisablePolicyChecks, "disable-policy-checks",
+ N_("do not check certificate policies")),
+ ARGPARSE_s_n (oEnablePolicyChecks, "enable-policy-checks", "@"),
+
+ ARGPARSE_s_n (oAutoIssuerKeyRetrieve, "auto-issuer-key-retrieve",
+ N_("fetch missing issuer certificates")),
+
+ ARGPARSE_s_s (oEncryptTo, "encrypt-to", "@"),
+ ARGPARSE_s_n (oNoEncryptTo, "no-encrypt-to", "@"),
+
+ ARGPARSE_s_s (oUser, "local-user",
+ N_("|USER-ID|use USER-ID to sign or decrypt")),
+
+ ARGPARSE_s_s (oOutput, "output", N_("|FILE|write output to FILE")),
+ ARGPARSE_s_n (oVerbose, "verbose", N_("verbose")),
+ ARGPARSE_s_n (oQuiet, "quiet", N_("be somewhat more quiet")),
+ ARGPARSE_s_n (oNoTTY, "no-tty", N_("don't use the terminal at all")),
+ ARGPARSE_s_s (oLogFile, "log-file",
+ N_("|FILE|write a server mode log to FILE")),
+ ARGPARSE_s_n (oNoLogFile, "no-log-file", "@"),
+ ARGPARSE_s_i (oLoggerFD, "logger-fd", "@"),
+
+ ARGPARSE_s_s (oAuditLog, "audit-log",
+ N_("|FILE|write an audit log to FILE")),
+ ARGPARSE_s_s (oHtmlAuditLog, "html-audit-log", "@"),
+ ARGPARSE_s_n (oDryRun, "dry-run", N_("do not make any changes")),
+ ARGPARSE_s_n (oBatch, "batch", N_("batch mode: never ask")),
+ ARGPARSE_s_n (oAnswerYes, "yes", N_("assume yes on most questions")),
+ ARGPARSE_s_n (oAnswerNo, "no", N_("assume no on most questions")),
+
+ ARGPARSE_s_s (oKeyring, "keyring",
+ N_("|FILE|add keyring to the list of keyrings")),
+
+ ARGPARSE_s_s (oDefaultKey, "default-key",
+ N_("|USER-ID|use USER-ID as default secret key")),
+
+ /* Not yet used: */
+ /* ARGPARSE_s_s (oDefRecipient, "default-recipient", */
+ /* N_("|NAME|use NAME as default recipient")), */
+ /* ARGPARSE_s_n (oDefRecipientSelf, "default-recipient-self", */
+ /* N_("use the default key as default recipient")), */
+ /* ARGPARSE_s_n (oNoDefRecipient, "no-default-recipient", "@"), */
+
+ ARGPARSE_s_s (oKeyServer, "keyserver",
+ N_("|SPEC|use this keyserver to lookup keys")),
+ ARGPARSE_s_s (oOptions, "options", N_("|FILE|read options from FILE")),
+
+ ARGPARSE_p_u (oDebug, "debug", "@"),
+ ARGPARSE_s_s (oDebugLevel, "debug-level",
+ N_("|LEVEL|set the debugging level to LEVEL")),
+ ARGPARSE_s_n (oDebugAll, "debug-all", "@"),
+ ARGPARSE_s_n (oDebugNone, "debug-none", "@"),
+ ARGPARSE_s_i (oDebugWait, "debug-wait", "@"),
+ ARGPARSE_s_n (oDebugAllowCoreDump, "debug-allow-core-dump", "@"),
+ ARGPARSE_s_n (oDebugNoChainValidation, "debug-no-chain-validation", "@"),
+ ARGPARSE_s_n (oDebugIgnoreExpiration, "debug-ignore-expiration", "@"),
+ ARGPARSE_s_s (oFixedPassphrase, "fixed-passphrase", "@"),
+
+ ARGPARSE_s_i (oStatusFD, "status-fd",
+ N_("|FD|write status info to this FD")),
+
+ ARGPARSE_s_s (oCipherAlgo, "cipher-algo",
+ N_("|NAME|use cipher algorithm NAME")),
+ ARGPARSE_s_s (oDigestAlgo, "digest-algo",
+ N_("|NAME|use message digest algorithm NAME")),
+ ARGPARSE_s_s (oExtraDigestAlgo, "extra-digest-algo", "@"),
+
+
+ ARGPARSE_group (302, N_(
+ "@\n(See the man page for a complete listing of all commands and options)\n"
+ )),
+
+ ARGPARSE_group (303, N_("@\nExamples:\n\n"
+ " -se -r Bob [file] sign and encrypt for user Bob\n"
+ " --clearsign [file] make a clear text signature\n"
+ " --detach-sign [file] make a detached signature\n"
+ " --list-keys [names] show keys\n"
+ " --fingerprint [names] show fingerprints\n" )),
+
+ /* Hidden options. */
+ ARGPARSE_s_n (oNoVerbose, "no-verbose", "@"),
+ ARGPARSE_s_n (oEnableSpecialFilenames, "enable-special-filenames", "@"),
+ ARGPARSE_s_n (oNoSecmemWarn, "no-secmem-warning", "@"),
+ ARGPARSE_s_n (oNoArmor, "no-armor", "@"),
+ ARGPARSE_s_n (oNoArmor, "no-armour", "@"),
+ ARGPARSE_s_n (oNoDefKeyring, "no-default-keyring", "@"),
+ ARGPARSE_s_n (oNoGreeting, "no-greeting", "@"),
+ ARGPARSE_s_n (oNoOptions, "no-options", "@"),
+ ARGPARSE_s_s (oHomedir, "homedir", "@"),
+ ARGPARSE_s_s (oAgentProgram, "agent-program", "@"),
+ ARGPARSE_s_s (oDisplay, "display", "@"),
+ ARGPARSE_s_s (oTTYname, "ttyname", "@"),
+ ARGPARSE_s_s (oTTYtype, "ttytype", "@"),
+ ARGPARSE_s_s (oLCctype, "lc-ctype", "@"),
+ ARGPARSE_s_s (oLCmessages, "lc-messages", "@"),
+ ARGPARSE_s_s (oXauthority, "xauthority", "@"),
+ ARGPARSE_s_s (oDirmngrProgram, "dirmngr-program", "@"),
+ ARGPARSE_s_n (oDisableDirmngr, "disable-dirmngr", "@"),
+ ARGPARSE_s_s (oProtectToolProgram, "protect-tool-program", "@"),
+ ARGPARSE_s_s (oFakedSystemTime, "faked-system-time", "@"),
+ ARGPARSE_s_n (oNoBatch, "no-batch", "@"),
+ ARGPARSE_s_n (oWithColons, "with-colons", "@"),
+ ARGPARSE_s_n (oWithKeyData,"with-key-data", "@"),
+ ARGPARSE_s_n (oWithValidation, "with-validation", "@"),
+ ARGPARSE_s_n (oWithMD5Fingerprint, "with-md5-fingerprint", "@"),
+ ARGPARSE_s_n (oWithEphemeralKeys, "with-ephemeral-keys", "@"),
+ ARGPARSE_s_n (oSkipVerify, "skip-verify", "@"),
+ ARGPARSE_s_n (oWithFingerprint, "with-fingerprint", "@"),
+ ARGPARSE_s_s (oDisableCipherAlgo, "disable-cipher-algo", "@"),
+ ARGPARSE_s_s (oDisablePubkeyAlgo, "disable-pubkey-algo", "@"),
+ ARGPARSE_s_n (oIgnoreTimeConflict, "ignore-time-conflict", "@"),
+ ARGPARSE_s_n (oNoRandomSeedFile, "no-random-seed-file", "@"),
+ ARGPARSE_s_n (oNoCommonCertsImport, "no-common-certs-import", "@"),
+ ARGPARSE_s_s (oIgnoreCertExtension, "ignore-cert-extension", "@"),
+
+ /* Command aliases. */
+ ARGPARSE_c (aListKeys, "list-key", "@"),
+ ARGPARSE_c (aListChain, "list-sig", "@"),
+ ARGPARSE_c (aListChain, "list-sigs", "@"),
+ ARGPARSE_c (aListChain, "check-sig", "@"),
+ ARGPARSE_c (aListChain, "check-sigs", "@"),
+ ARGPARSE_c (aDeleteKey, "delete-key", "@"),
+
+ ARGPARSE_end ()
+};
+
+
+
+
+/* Global variable to keep an error count. */
+int gpgsm_errors_seen = 0;
+
+/* It is possible that we are currentlu running under setuid permissions */
+static int maybe_setuid = 1;
+
+/* Helper to implement --debug-level and --debug*/
+static const char *debug_level;
+static unsigned int debug_value;
+
+/* Option --enable-special-filenames */
+static int allow_special_filenames;
+
+/* Default value for include-certs. We need an extra macro for
+ gpgconf-list because the variable will be changed by the command
+ line option. */
+#define DEFAULT_INCLUDE_CERTS -2 /* Include all certs but root. */
+static int default_include_certs = DEFAULT_INCLUDE_CERTS;
+
+/* Whether the chain mode shall be used for validation. */
+static int default_validation_model;
+
+
+static char *build_list (const char *text,
+ 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 emergency_cleanup (void);
+static int check_special_filename (const char *fname, int for_write);
+static int open_read (const char *filename);
+static estream_t open_es_fread (const char *filename);
+static FILE *open_fwrite (const char *filename);
+static estream_t open_es_fwrite (const char *filename);
+static void run_protect_tool (int argc, char **argv);
+
+static int
+our_pk_test_algo (int algo)
+{
+ switch (algo)
+ {
+ case GCRY_PK_RSA:
+ case GCRY_PK_ECDSA:
+ return gcry_pk_test_algo (algo);
+ default:
+ return 1;
+ }
+}
+
+static int
+our_cipher_test_algo (int algo)
+{
+ switch (algo)
+ {
+ case GCRY_CIPHER_3DES:
+ case GCRY_CIPHER_AES128:
+ case GCRY_CIPHER_AES192:
+ case GCRY_CIPHER_AES256:
+ case GCRY_CIPHER_SERPENT128:
+ case GCRY_CIPHER_SERPENT192:
+ case GCRY_CIPHER_SERPENT256:
+ case GCRY_CIPHER_SEED:
+ case GCRY_CIPHER_CAMELLIA128:
+ case GCRY_CIPHER_CAMELLIA192:
+ case GCRY_CIPHER_CAMELLIA256:
+ return gcry_cipher_test_algo (algo);
+ default:
+ return 1;
+ }
+}
+
+
+static int
+our_md_test_algo (int algo)
+{
+ switch (algo)
+ {
+ case GCRY_MD_MD5:
+ case GCRY_MD_SHA1:
+ case GCRY_MD_RMD160:
+ case GCRY_MD_SHA224:
+ case GCRY_MD_SHA256:
+ case GCRY_MD_SHA384:
+ case GCRY_MD_SHA512:
+ case GCRY_MD_WHIRLPOOL:
+ return gcry_md_test_algo (algo);
+ default:
+ return 1;
+ }
+}
+
+
+static char *
+make_libversion (const char *libname, const char *(*getfnc)(const char*))
+{
+ const char *s;
+ char *result;
+
+ if (maybe_setuid)
+ {
+ gcry_control (GCRYCTL_INIT_SECMEM, 0, 0); /* Drop setuid. */
+ maybe_setuid = 0;
+ }
+ s = getfnc (NULL);
+ result = xmalloc (strlen (libname) + 1 + strlen (s) + 1);
+ strcpy (stpcpy (stpcpy (result, libname), " "), s);
+ return result;
+}
+
+
+static const char *
+my_strusage( int level )
+{
+ static char *digests, *pubkeys, *ciphers;
+ static char *ver_gcry, *ver_ksba;
+ const char *p;
+
+ switch (level)
+ {
+ case 11: p = "gpgsm (GnuPG)";
+ break;
+ case 13: p = VERSION; break;
+ case 17: p = PRINTABLE_OS_NAME; break;
+ case 19: p = _("Please report bugs to <@EMAIL@>.\n"); break;
+
+ case 1:
+ case 40: p = _("Usage: gpgsm [options] [files] (-h for help)");
+ break;
+ case 41:
+ p = _("Syntax: gpgsm [options] [files]\n"
+ "sign, check, encrypt or decrypt using the S/MIME protocol\n"
+ "default operation depends on the input data\n");
+ break;
+
+ case 20:
+ if (!ver_gcry)
+ ver_gcry = make_libversion ("libgcrypt", gcry_check_version);
+ p = ver_gcry;
+ break;
+ case 21:
+ if (!ver_ksba)
+ ver_ksba = make_libversion ("libksba", ksba_check_version);
+ p = ver_ksba;
+ break;
+
+ case 31: p = "\nHome: "; break;
+ case 32: p = opt.homedir; break;
+ case 33: p = _("\nSupported algorithms:\n"); break;
+ case 34:
+ if (!ciphers)
+ ciphers = build_list ("Cipher: ", gcry_cipher_algo_name,
+ our_cipher_test_algo );
+ p = ciphers;
+ break;
+ case 35:
+ if (!pubkeys)
+ pubkeys = build_list ("Pubkey: ", gcry_pk_algo_name,
+ our_pk_test_algo );
+ p = pubkeys;
+ break;
+ case 36:
+ if (!digests)
+ digests = build_list("Hash: ", gcry_md_algo_name, our_md_test_algo );
+ p = digests;
+ break;
+
+ default: p = NULL; break;
+ }
+ return p;
+}
+
+
+static char *
+build_list (const char *text, const char * (*mapf)(int), int (*chkf)(int))
+{
+ int i;
+ size_t n=strlen(text)+2;
+ char *list, *p;
+
+ if (maybe_setuid) {
+ gcry_control (GCRYCTL_DROP_PRIVS); /* drop setuid */
+ }
+
+ for (i=1; i < 400; i++ )
+ if (!chkf(i))
+ n += strlen(mapf(i)) + 2;
+ list = xmalloc (21 + n);
+ *list = 0;
+ for (p=NULL, i=1; i < 400; i++)
+ {
+ if (!chkf(i))
+ {
+ if( !p )
+ p = stpcpy (list, text );
+ else
+ p = stpcpy (p, ", ");
+ p = stpcpy (p, mapf(i) );
+ }
+ }
+ if (p)
+ p = stpcpy(p, "\n" );
+ return list;
+}
+
+
+/* Set the file pointer into binary mode if required. */
+static void
+set_binary (FILE *fp)
+{
+#ifdef HAVE_DOSISH_SYSTEM
+ setmode (fileno (fp), O_BINARY);
+#else
+ (void)fp;
+#endif
+}
+
+
+
+static void
+wrong_args (const char *text)
+{
+ fputs (_("usage: gpgsm [options] "), stderr);
+ fputs (text, stderr);
+ putc ('\n', stderr);
+ gpgsm_exit (2);
+}
+
+
+static void
+set_opt_session_env (const char *name, const char *value)
+{
+ gpg_error_t err;
+
+ err = session_env_setenv (opt.session_env, name, value);
+ if (err)
+ log_fatal ("error setting session environment: %s\n",
+ gpg_strerror (err));
+}
+
+
+/* Setup the debugging. With a DEBUG_LEVEL of NULL only the active
+ debug flags are propagated to the subsystems. With DEBUG_LEVEL
+ set, a specific set of debug flags is set; and individual debugging
+ flags will be added on top. */
+static void
+set_debug (void)
+{
+ int numok = (debug_level && digitp (debug_level));
+ int numlvl = numok? atoi (debug_level) : 0;
+
+ if (!debug_level)
+ ;
+ else if (!strcmp (debug_level, "none") || (numok && numlvl < 1))
+ opt.debug = 0;
+ else if (!strcmp (debug_level, "basic") || (numok && numlvl <= 2))
+ opt.debug = DBG_ASSUAN_VALUE;
+ else if (!strcmp (debug_level, "advanced") || (numok && numlvl <= 5))
+ opt.debug = DBG_ASSUAN_VALUE|DBG_X509_VALUE;
+ else if (!strcmp (debug_level, "expert") || (numok && numlvl <= 8))
+ opt.debug = (DBG_ASSUAN_VALUE|DBG_X509_VALUE
+ |DBG_CACHE_VALUE|DBG_CRYPTO_VALUE);
+ else if (!strcmp (debug_level, "guru") || numok)
+ {
+ opt.debug = ~0;
+ /* Unless the "guru" string has been used we don't want to allow
+ hashing debugging. The rationale is that people tend to
+ select the highest debug value and would then clutter their
+ disk with debug files which may reveal confidential data. */
+ if (numok)
+ opt.debug &= ~(DBG_HASHING_VALUE);
+ }
+ else
+ {
+ log_error (_("invalid debug-level `%s' given\n"), debug_level);
+ gpgsm_exit (2);
+ }
+
+ opt.debug |= debug_value;
+
+ if (opt.debug && !opt.verbose)
+ opt.verbose = 1;
+ if (opt.debug)
+ opt.quiet = 0;
+
+ if (opt.debug & DBG_MPI_VALUE)
+ gcry_control (GCRYCTL_SET_DEBUG_FLAGS, 2);
+ if (opt.debug & DBG_CRYPTO_VALUE )
+ gcry_control (GCRYCTL_SET_DEBUG_FLAGS, 1);
+ gcry_control (GCRYCTL_SET_VERBOSITY, (int)opt.verbose);
+
+ if (opt.debug)
+ log_info ("enabled debug flags:%s%s%s%s%s%s%s%s\n",
+ (opt.debug & DBG_X509_VALUE )? " x509":"",
+ (opt.debug & DBG_MPI_VALUE )? " mpi":"",
+ (opt.debug & DBG_CRYPTO_VALUE )? " crypto":"",
+ (opt.debug & DBG_MEMORY_VALUE )? " memory":"",
+ (opt.debug & DBG_CACHE_VALUE )? " cache":"",
+ (opt.debug & DBG_MEMSTAT_VALUE)? " memstat":"",
+ (opt.debug & DBG_HASHING_VALUE)? " hashing":"",
+ (opt.debug & DBG_ASSUAN_VALUE )? " assuan":"" );
+}
+
+
+
+static void
+set_cmd (enum cmd_and_opt_values *ret_cmd, enum cmd_and_opt_values new_cmd)
+{
+ enum cmd_and_opt_values cmd = *ret_cmd;
+
+ if (!cmd || cmd == new_cmd)
+ cmd = new_cmd;
+ else if ( cmd == aSign && new_cmd == aEncr )
+ cmd = aSignEncr;
+ else if ( cmd == aEncr && new_cmd == aSign )
+ cmd = aSignEncr;
+ else if ( (cmd == aSign && new_cmd == aClearsign)
+ || (cmd == aClearsign && new_cmd == aSign) )
+ cmd = aClearsign;
+ else
+ {
+ log_error(_("conflicting commands\n"));
+ gpgsm_exit(2);
+ }
+
+ *ret_cmd = cmd;
+}
+
+
+/* Helper to add recipients to a list. */
+static void
+do_add_recipient (ctrl_t ctrl, const char *name,
+ certlist_t *recplist, int is_encrypt_to, int recp_required)
+{
+ int rc = gpgsm_add_to_certlist (ctrl, name, 0, recplist, is_encrypt_to);
+ if (rc)
+ {
+ if (recp_required)
+ {
+ log_error ("can't encrypt to `%s': %s\n", name, gpg_strerror (rc));
+ gpgsm_status2 (ctrl, STATUS_INV_RECP,
+ get_inv_recpsgnr_code (rc), name, NULL);
+ }
+ else
+ log_info (_("NOTE: won't be able to encrypt to `%s': %s\n"),
+ name, gpg_strerror (rc));
+ }
+}
+
+
+static void
+parse_validation_model (const char *model)
+{
+ int i = gpgsm_parse_validation_model (model);
+ if (i == -1)
+ log_error (_("unknown validation model `%s'\n"), model);
+ else
+ default_validation_model = i;
+}
+
+
+/* Release the list of SERVERS. As usual it is okay to call this
+ function with SERVERS passed as NULL. */
+void
+keyserver_list_free (struct keyserver_spec *servers)
+{
+ while (servers)
+ {
+ struct keyserver_spec *tmp = servers->next;
+ xfree (servers->host);
+ xfree (servers->user);
+ if (servers->pass)
+ memset (servers->pass, 0, strlen (servers->pass));
+ xfree (servers->pass);
+ xfree (servers->base);
+ xfree (servers);
+ servers = tmp;
+ }
+}
+
+/* See also dirmngr ldapserver_parse_one(). */
+struct keyserver_spec *
+parse_keyserver_line (char *line,
+ const char *filename, unsigned int lineno)
+{
+ char *p;
+ char *endp;
+ struct keyserver_spec *server;
+ int fieldno;
+ int fail = 0;
+
+ /* Parse the colon separated fields. */
+ server = xcalloc (1, sizeof *server);
+ for (fieldno = 1, p = line; p; p = endp, fieldno++ )
+ {
+ endp = strchr (p, ':');
+ if (endp)
+ *endp++ = '\0';
+ trim_spaces (p);
+ switch (fieldno)
+ {
+ case 1:
+ if (*p)
+ server->host = xstrdup (p);
+ else
+ {
+ log_error (_("%s:%u: no hostname given\n"),
+ filename, lineno);
+ fail = 1;
+ }
+ break;
+
+ case 2:
+ if (*p)
+ server->port = atoi (p);
+ break;
+
+ case 3:
+ if (*p)
+ server->user = xstrdup (p);
+ break;
+
+ case 4:
+ if (*p && !server->user)
+ {
+ log_error (_("%s:%u: password given without user\n"),
+ filename, lineno);
+ fail = 1;
+ }
+ else if (*p)
+ server->pass = xstrdup (p);
+ break;
+
+ case 5:
+ if (*p)
+ server->base = xstrdup (p);
+ break;
+
+ default:
+ /* (We silently ignore extra fields.) */
+ break;
+ }
+ }
+
+ if (fail)
+ {
+ log_info (_("%s:%u: skipping this line\n"), filename, lineno);
+ keyserver_list_free (server);
+ }
+
+ return server;
+}
+
+
+int
+main ( int argc, char **argv)
+{
+ ARGPARSE_ARGS pargs;
+ int orig_argc;
+ char **orig_argv;
+ /* char *username;*/
+ int may_coredump;
+ strlist_t sl, remusr= NULL, locusr=NULL;
+ strlist_t nrings=NULL;
+ int detached_sig = 0;
+ FILE *configfp = NULL;
+ char *configname = NULL;
+ unsigned configlineno;
+ int parse_debug = 0;
+ int no_more_options = 0;
+ int default_config =1;
+ int default_keyring = 1;
+ char *logfile = NULL;
+ char *auditlog = NULL;
+ char *htmlauditlog = NULL;
+ int greeting = 0;
+ int nogreeting = 0;
+ int debug_wait = 0;
+ int use_random_seed = 1;
+ int no_common_certs_import = 0;
+ int with_fpr = 0;
+ const char *forced_digest_algo = NULL;
+ const char *extra_digest_algo = NULL;
+ enum cmd_and_opt_values cmd = 0;
+ struct server_control_s ctrl;
+ certlist_t recplist = NULL;
+ certlist_t signerlist = NULL;
+ int do_not_setup_keys = 0;
+ int recp_required = 0;
+ estream_t auditfp = NULL;
+ estream_t htmlauditfp = NULL;
+ struct assuan_malloc_hooks malloc_hooks;
+
+ /*mtrace();*/
+
+ gnupg_reopen_std ("gpgsm");
+ /* trap_unaligned ();*/
+ gnupg_rl_initialize ();
+ set_strusage (my_strusage);
+ gcry_control (GCRYCTL_SUSPEND_SECMEM_WARN);
+ /* We don't need any locking in libgcrypt unless we use any kind of
+ threading. */
+ gcry_control (GCRYCTL_DISABLE_INTERNAL_LOCKING);
+
+ /* 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_prefix ("gpgsm", 1);
+
+ /* Make sure that our subsystems are ready. */
+ i18n_init();
+ init_common_subsystems ();
+
+ /* Check that the libraries are suitable. Do it here because the
+ option parse may need services of the library */
+ if (!gcry_check_version (NEED_LIBGCRYPT_VERSION) )
+ log_fatal (_("%s is too old (need %s, have %s)\n"), "libgcrypt",
+ NEED_LIBGCRYPT_VERSION, gcry_check_version (NULL) );
+ if (!ksba_check_version (NEED_KSBA_VERSION) )
+ log_fatal (_("%s is too old (need %s, have %s)\n"), "libksba",
+ NEED_KSBA_VERSION, ksba_check_version (NULL) );
+
+
+ gcry_control (GCRYCTL_USE_SECURE_RNDPOOL);
+
+ may_coredump = disable_core_dumps ();
+
+ gnupg_init_signals (0, emergency_cleanup);
+
+ create_dotlock (NULL); /* register locking cleanup */
+
+ opt.session_env = session_env_new ();
+ if (!opt.session_env)
+ log_fatal ("error allocating session environment block: %s\n",
+ strerror (errno));
+
+ /* Note: If you change this default cipher algorithm , please
+ remember to update the Gpgconflist entry as well. */
+ opt.def_cipher_algoid = "3DES"; /*des-EDE3-CBC*/
+
+ opt.homedir = default_homedir ();
+
+ /* First check whether we have a config file on the commandline */
+ orig_argc = argc;
+ orig_argv = argv;
+ pargs.argc = &argc;
+ pargs.argv = &argv;
+ pargs.flags= 1|(1<<6); /* do not remove the args, ignore version */
+ while (arg_parse( &pargs, opts))
+ {
+ if (pargs.r_opt == oDebug || pargs.r_opt == oDebugAll)
+ parse_debug++;
+ else if (pargs.r_opt == oOptions)
+ { /* yes there is one, so we do not try the default one but
+ read the config file when it is encountered at the
+ commandline */
+ default_config = 0;
+ }
+ else if (pargs.r_opt == oNoOptions)
+ default_config = 0; /* --no-options */
+ else if (pargs.r_opt == oHomedir)
+ opt.homedir = pargs.r.ret_str;
+ else if (pargs.r_opt == aCallProtectTool)
+ break; /* This break makes sure that --version and --help are
+ passed to the protect-tool. */
+ }
+
+
+ /* Initialize the secure memory. */
+ gcry_control (GCRYCTL_INIT_SECMEM, 16384, 0);
+ maybe_setuid = 0;
+
+ /*
+ Now we are now working under our real uid
+ */
+
+ ksba_set_malloc_hooks (gcry_malloc, gcry_realloc, gcry_free );
+
+ malloc_hooks.malloc = gcry_malloc;
+ malloc_hooks.realloc = gcry_realloc;
+ malloc_hooks.free = gcry_free;
+ assuan_set_malloc_hooks (&malloc_hooks);
+ assuan_set_assuan_log_prefix (log_get_prefix (NULL));
+ assuan_set_gpg_err_source (GPG_ERR_SOURCE_DEFAULT);
+
+ keybox_set_malloc_hooks (gcry_malloc, gcry_realloc, gcry_free);
+
+ /* Setup a default control structure for command line mode */
+ memset (&ctrl, 0, sizeof ctrl);
+ gpgsm_init_default_ctrl (&ctrl);
+ ctrl.no_server = 1;
+ ctrl.status_fd = -1; /* No status output. */
+ ctrl.autodetect_encoding = 1;
+
+ /* Set the default option file */
+ if (default_config )
+ configname = make_filename (opt.homedir, "gpgsm.conf", NULL);
+ /* Set the default policy file */
+ opt.policy_file = make_filename (opt.homedir, "policies.txt", NULL);
+
+ argc = orig_argc;
+ argv = orig_argv;
+ pargs.argc = &argc;
+ pargs.argv = &argv;
+ pargs.flags = 1; /* do not remove the args */
+
+ next_pass:
+ if (configname) {
+ configlineno = 0;
+ configfp = fopen (configname, "r");
+ if (!configfp)
+ {
+ if (default_config)
+ {
+ if (parse_debug)
+ log_info (_("NOTE: no default option file `%s'\n"), configname);
+ }
+ else
+ {
+ log_error (_("option file `%s': %s\n"), configname, strerror(errno));
+ gpgsm_exit(2);
+ }
+ xfree(configname);
+ configname = NULL;
+ }
+ if (parse_debug && configname)
+ log_info (_("reading options from `%s'\n"), configname);
+ default_config = 0;
+ }
+
+ while (!no_more_options
+ && optfile_parse (configfp, configname, &configlineno, &pargs, opts))
+ {
+ switch (pargs.r_opt)
+ {
+ case aGPGConfList:
+ case aGPGConfTest:
+ set_cmd (&cmd, pargs.r_opt);
+ do_not_setup_keys = 1;
+ nogreeting = 1;
+ break;
+
+ case aServer:
+ opt.batch = 1;
+ set_cmd (&cmd, aServer);
+ break;
+
+ case aCallDirmngr:
+ opt.batch = 1;
+ set_cmd (&cmd, aCallDirmngr);
+ do_not_setup_keys = 1;
+ break;
+
+ case aCallProtectTool:
+ opt.batch = 1;
+ set_cmd (&cmd, aCallProtectTool);
+ no_more_options = 1; /* Stop parsing. */
+ do_not_setup_keys = 1;
+ break;
+
+ case aDeleteKey:
+ set_cmd (&cmd, aDeleteKey);
+ /*greeting=1;*/
+ do_not_setup_keys = 1;
+ break;
+
+ case aDetachedSign:
+ detached_sig = 1;
+ set_cmd (&cmd, aSign );
+ break;
+
+ case aKeygen:
+ set_cmd (&cmd, aKeygen);
+ greeting=1;
+ do_not_setup_keys = 1;
+ break;
+
+ case aImport:
+ case aSendKeys:
+ case aRecvKeys:
+ case aExport:
+ case aExportSecretKeyP12:
+ case aDumpKeys:
+ case aDumpChain:
+ case aDumpExternalKeys:
+ case aDumpSecretKeys:
+ case aListKeys:
+ case aListExternalKeys:
+ case aListSecretKeys:
+ case aListChain:
+ case aLearnCard:
+ case aPasswd:
+ case aKeydbClearSomeCertFlags:
+ do_not_setup_keys = 1;
+ set_cmd (&cmd, pargs.r_opt);
+ break;
+
+ case aEncr:
+ recp_required = 1;
+ set_cmd (&cmd, pargs.r_opt);
+ break;
+
+ case aSym:
+ case aDecrypt:
+ case aSign:
+ case aClearsign:
+ case aVerify:
+ set_cmd (&cmd, pargs.r_opt);
+ break;
+
+ /* Output encoding selection. */
+ case oArmor:
+ ctrl.create_pem = 1;
+ break;
+ case oBase64:
+ ctrl.create_pem = 0;
+ ctrl.create_base64 = 1;
+ break;
+ case oNoArmor:
+ ctrl.create_pem = 0;
+ ctrl.create_base64 = 0;
+ break;
+
+ case oP12Charset:
+ opt.p12_charset = pargs.r.ret_str;
+ break;
+
+ /* Input encoding selection. */
+ case oAssumeArmor:
+ ctrl.autodetect_encoding = 0;
+ ctrl.is_pem = 1;
+ ctrl.is_base64 = 0;
+ break;
+ case oAssumeBase64:
+ ctrl.autodetect_encoding = 0;
+ ctrl.is_pem = 0;
+ ctrl.is_base64 = 1;
+ break;
+ case oAssumeBinary:
+ ctrl.autodetect_encoding = 0;
+ ctrl.is_pem = 0;
+ ctrl.is_base64 = 0;
+ break;
+
+ case oDisableCRLChecks:
+ opt.no_crl_check = 1;
+ break;
+ case oEnableCRLChecks:
+ opt.no_crl_check = 0;
+ break;
+ case oDisableTrustedCertCRLCheck:
+ opt.no_trusted_cert_crl_check = 1;
+ break;
+ case oEnableTrustedCertCRLCheck:
+ opt.no_trusted_cert_crl_check = 0;
+ break;
+ case oForceCRLRefresh:
+ opt.force_crl_refresh = 1;
+ break;
+
+ case oDisableOCSP:
+ ctrl.use_ocsp = opt.enable_ocsp = 0;
+ break;
+ case oEnableOCSP:
+ ctrl.use_ocsp = opt.enable_ocsp = 1;
+ break;
+
+ case oIncludeCerts:
+ ctrl.include_certs = default_include_certs = pargs.r.ret_int;
+ break;
+
+ case oPolicyFile:
+ xfree (opt.policy_file);
+ if (*pargs.r.ret_str)
+ opt.policy_file = xstrdup (pargs.r.ret_str);
+ else
+ opt.policy_file = NULL;
+ break;
+
+ case oDisablePolicyChecks:
+ opt.no_policy_check = 1;
+ break;
+ case oEnablePolicyChecks:
+ opt.no_policy_check = 0;
+ break;
+
+ case oAutoIssuerKeyRetrieve:
+ opt.auto_issuer_key_retrieve = 1;
+ break;
+
+ case oOutput: opt.outfile = pargs.r.ret_str; break;
+
+
+ case oQuiet: opt.quiet = 1; break;
+ case oNoTTY: /* fixme:tty_no_terminal(1);*/ break;
+ case oDryRun: opt.dry_run = 1; break;
+
+ case oVerbose:
+ opt.verbose++;
+ gcry_control (GCRYCTL_SET_VERBOSITY, (int)opt.verbose);
+ break;
+ case oNoVerbose:
+ opt.verbose = 0;
+ gcry_control (GCRYCTL_SET_VERBOSITY, (int)opt.verbose);
+ break;
+
+ case oLogFile: logfile = pargs.r.ret_str; break;
+ case oNoLogFile: logfile = NULL; break;
+
+ case oAuditLog: auditlog = pargs.r.ret_str; break;
+ case oHtmlAuditLog: htmlauditlog = pargs.r.ret_str; break;
+
+ case oBatch:
+ opt.batch = 1;
+ greeting = 0;
+ break;
+ case oNoBatch: opt.batch = 0; 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 oDebug: debug_value |= pargs.r.ret_ulong; break;
+ case oDebugAll: debug_value = ~0; break;
+ case oDebugNone: debug_value = 0; break;
+ case oDebugLevel: debug_level = pargs.r.ret_str; break;
+ case oDebugWait: debug_wait = pargs.r.ret_int; break;
+ case oDebugAllowCoreDump:
+ may_coredump = enable_core_dumps ();
+ break;
+ case oDebugNoChainValidation: opt.no_chain_validation = 1; break;
+ case oDebugIgnoreExpiration: opt.ignore_expiration = 1; break;
+ case oFixedPassphrase: opt.fixed_passphrase = pargs.r.ret_str; break;
+
+ case oStatusFD: ctrl.status_fd = pargs.r.ret_int; break;
+ case oLoggerFD: log_set_fd (pargs.r.ret_int ); break;
+ case oWithMD5Fingerprint:
+ opt.with_md5_fingerprint=1; /*fall thru*/
+ case oWithFingerprint:
+ with_fpr=1; /*fall thru*/
+ case aFingerprint:
+ opt.fingerprint++;
+ break;
+
+ case oOptions:
+ /* config files may not be nested (silently ignore them) */
+ if (!configfp)
+ {
+ xfree(configname);
+ configname = xstrdup (pargs.r.ret_str);
+ goto next_pass;
+ }
+ break;
+ case oNoOptions: break; /* no-options */
+ case oHomedir: opt.homedir = pargs.r.ret_str; break;
+ case oAgentProgram: opt.agent_program = pargs.r.ret_str; break;
+
+ case oDisplay:
+ set_opt_session_env ("DISPLAY", pargs.r.ret_str);
+ break;
+ case oTTYname:
+ set_opt_session_env ("GPG_TTY", pargs.r.ret_str);
+ break;
+ case oTTYtype:
+ set_opt_session_env ("TERM", pargs.r.ret_str);
+ break;
+ case oXauthority:
+ set_opt_session_env ("XAUTHORITY", pargs.r.ret_str);
+ break;
+
+ case oLCctype: opt.lc_ctype = xstrdup (pargs.r.ret_str); break;
+ case oLCmessages: opt.lc_messages = xstrdup (pargs.r.ret_str); break;
+
+ case oDirmngrProgram: opt.dirmngr_program = pargs.r.ret_str; break;
+ case oDisableDirmngr: opt.disable_dirmngr = 1; break;
+ case oPreferSystemDirmngr: opt.prefer_system_dirmngr = 1; break;
+ case oProtectToolProgram:
+ opt.protect_tool_program = pargs.r.ret_str;
+ break;
+
+ case oFakedSystemTime:
+ {
+ time_t faked_time = isotime2epoch (pargs.r.ret_str);
+ if (faked_time == (time_t)(-1))
+ faked_time = (time_t)strtoul (pargs.r.ret_str, NULL, 10);
+ gnupg_set_time (faked_time, 0);
+ }
+ break;
+
+ case oNoDefKeyring: default_keyring = 0; break;
+ case oNoGreeting: nogreeting = 1; break;
+
+ case oDefaultKey:
+ if (*pargs.r.ret_str)
+ {
+ xfree (opt.local_user);
+ opt.local_user = xstrdup (pargs.r.ret_str);
+ }
+ break;
+ case oDefRecipient:
+ if (*pargs.r.ret_str)
+ opt.def_recipient = xstrdup (pargs.r.ret_str);
+ break;
+ case oDefRecipientSelf:
+ xfree (opt.def_recipient);
+ opt.def_recipient = NULL;
+ opt.def_recipient_self = 1;
+ break;
+ case oNoDefRecipient:
+ xfree (opt.def_recipient);
+ opt.def_recipient = NULL;
+ opt.def_recipient_self = 0;
+ break;
+
+ case oWithKeyData: opt.with_key_data=1; /* fall thru */
+ case oWithColons: ctrl.with_colons = 1; break;
+ case oWithValidation: ctrl.with_validation=1; break;
+ case oWithEphemeralKeys: ctrl.with_ephemeral_keys=1; break;
+
+ case oSkipVerify: opt.skip_verify=1; break;
+
+ case oNoEncryptTo: opt.no_encrypt_to = 1; break;
+ case oEncryptTo: /* Store the recipient in the second list */
+ sl = add_to_strlist (&remusr, pargs.r.ret_str);
+ sl->flags = 1;
+ break;
+
+ case oRecipient: /* store the recipient */
+ add_to_strlist ( &remusr, pargs.r.ret_str);
+ break;
+
+ case oUser: /* Store the local users, the first one is the default */
+ if (!opt.local_user)
+ opt.local_user = xstrdup (pargs.r.ret_str);
+ add_to_strlist (&locusr, pargs.r.ret_str);
+ break;
+
+ case oNoSecmemWarn:
+ gcry_control (GCRYCTL_DISABLE_SECMEM_WARN);
+ break;
+
+ case oCipherAlgo:
+ opt.def_cipher_algoid = pargs.r.ret_str;
+ break;
+
+ case oDisableCipherAlgo:
+ {
+ int algo = gcry_cipher_map_name (pargs.r.ret_str);
+ gcry_cipher_ctl (NULL, GCRYCTL_DISABLE_ALGO, &algo, sizeof algo);
+ }
+ break;
+ case oDisablePubkeyAlgo:
+ {
+ int algo = gcry_pk_map_name (pargs.r.ret_str);
+ gcry_pk_ctl (GCRYCTL_DISABLE_ALGO,&algo, sizeof algo );
+ }
+ break;
+
+ case oDigestAlgo:
+ forced_digest_algo = pargs.r.ret_str;
+ break;
+
+ case oExtraDigestAlgo:
+ extra_digest_algo = pargs.r.ret_str;
+ break;
+
+ case oIgnoreTimeConflict: opt.ignore_time_conflict = 1; break;
+ case oNoRandomSeedFile: use_random_seed = 0; break;
+ case oNoCommonCertsImport: no_common_certs_import = 1; break;
+
+ case oEnableSpecialFilenames: allow_special_filenames =1; break;
+
+ case oValidationModel: parse_validation_model (pargs.r.ret_str); break;
+
+ case oKeyServer:
+ {
+ struct keyserver_spec *keyserver;
+ keyserver = parse_keyserver_line (pargs.r.ret_str,
+ configname, configlineno);
+ if (! keyserver)
+ log_error (_("could not parse keyserver\n"));
+ else
+ {
+ /* FIXME: Keep last next pointer. */
+ struct keyserver_spec **next_p = &opt.keyserver;
+ while (*next_p)
+ next_p = &(*next_p)->next;
+ *next_p = keyserver;
+ }
+ }
+ break;
+
+ case oIgnoreCertExtension:
+ add_to_strlist (&opt.ignored_cert_extensions, pargs.r.ret_str);
+ break;
+
+ default:
+ pargs.err = configfp? ARGPARSE_PRINT_WARNING:ARGPARSE_PRINT_ERROR;
+ break;
+ }
+ }
+
+ if (configfp)
+ {
+ fclose (configfp);
+ configfp = NULL;
+ /* Keep a copy of the config filename. */
+ opt.config_filename = configname;
+ configname = NULL;
+ goto next_pass;
+ }
+ xfree (configname);
+ configname = NULL;
+
+ if (!opt.config_filename)
+ opt.config_filename = make_filename (opt.homedir, "gpgsm.conf", NULL);
+
+ if (log_get_errorcount(0))
+ gpgsm_exit(2);
+
+ /* Now that we have the options parsed we need to update the default
+ control structure. */
+ gpgsm_init_default_ctrl (&ctrl);
+
+ if (nogreeting)
+ greeting = 0;
+
+ if (greeting)
+ {
+ fprintf(stderr, "%s %s; %s\n",
+ strusage(11), strusage(13), strusage(14) );
+ fprintf(stderr, "%s\n", strusage(15) );
+ }
+# ifdef IS_DEVELOPMENT_VERSION
+ if (!opt.batch)
+ {
+ log_info ("NOTE: THIS IS A DEVELOPMENT VERSION!\n");
+ log_info ("It is only intended for test purposes and should NOT be\n");
+ log_info ("used in a production environment or with production keys!\n");
+ }
+# endif
+
+ if (may_coredump && !opt.quiet)
+ log_info (_("WARNING: program may create a core file!\n"));
+
+/* if (opt.qualsig_approval && !opt.quiet) */
+/* log_info (_("This software has offically been approved to " */
+/* "create and verify\n" */
+/* "qualified signatures according to German law.\n")); */
+
+ if (logfile && cmd == aServer)
+ {
+ log_set_file (logfile);
+ log_set_prefix (NULL, 1|2|4);
+ }
+
+ if (gnupg_faked_time_p ())
+ {
+ gnupg_isotime_t tbuf;
+
+ log_info (_("WARNING: running with faked system time: "));
+ gnupg_get_isotime (tbuf);
+ dump_isotime (tbuf);
+ log_printf ("\n");
+ }
+
+/*FIXME if (opt.batch) */
+/* tty_batchmode (1); */
+
+ gcry_control (GCRYCTL_RESUME_SECMEM_WARN);
+
+ set_debug ();
+
+ /* Although we alwasy use gpgsm_exit, we better install a regualr
+ exit handler so that at least the secure memory gets wiped
+ out. */
+ if (atexit (emergency_cleanup))
+ {
+ log_error ("atexit failed\n");
+ gpgsm_exit (2);
+ }
+
+ /* Must do this after dropping setuid, because the mapping functions
+ may try to load an module and we may have disabled an algorithm.
+ We remap the commonly used algorithms to the OIDs for
+ convenience. We need to work with the OIDs because they are used
+ to check whether the encryption mode is actually available. */
+ if (!strcmp (opt.def_cipher_algoid, "3DES") )
+ opt.def_cipher_algoid = "1.2.840.113549.3.7";
+ else if (!strcmp (opt.def_cipher_algoid, "AES")
+ || !strcmp (opt.def_cipher_algoid, "AES128"))
+ opt.def_cipher_algoid = "2.16.840.1.101.3.4.1.2";
+ else if (!strcmp (opt.def_cipher_algoid, "AES256") )
+ opt.def_cipher_algoid = "2.16.840.1.101.3.4.1.42";
+ else if (!strcmp (opt.def_cipher_algoid, "SERPENT")
+ || !strcmp (opt.def_cipher_algoid, "SERPENT128") )
+ opt.def_cipher_algoid = "1.3.6.1.4.1.11591.13.2.2";
+ else if (!strcmp (opt.def_cipher_algoid, "SERPENT192") )
+ opt.def_cipher_algoid = "1.3.6.1.4.1.11591.13.2.22";
+ else if (!strcmp (opt.def_cipher_algoid, "SERPENT192") )
+ opt.def_cipher_algoid = "1.3.6.1.4.1.11591.13.2.42";
+ else if (!strcmp (opt.def_cipher_algoid, "SEED") )
+ opt.def_cipher_algoid = "1.2.410.200004.1.4";
+ else if (!strcmp (opt.def_cipher_algoid, "CAMELLIA")
+ || !strcmp (opt.def_cipher_algoid, "CAMELLIA128") )
+ opt.def_cipher_algoid = "1.2.392.200011.61.1.1.1.2";
+ else if (!strcmp (opt.def_cipher_algoid, "CAMELLIA192") )
+ opt.def_cipher_algoid = "1.2.392.200011.61.1.1.1.3";
+ else if (!strcmp (opt.def_cipher_algoid, "CAMELLIA256") )
+ opt.def_cipher_algoid = "1.2.392.200011.61.1.1.1.4";
+
+ if (cmd != aGPGConfList)
+ {
+ if ( !gcry_cipher_map_name (opt.def_cipher_algoid)
+ || !gcry_cipher_mode_from_oid (opt.def_cipher_algoid))
+ log_error (_("selected cipher algorithm is invalid\n"));
+
+ if (forced_digest_algo)
+ {
+ opt.forced_digest_algo = gcry_md_map_name (forced_digest_algo);
+ if (our_md_test_algo(opt.forced_digest_algo) )
+ log_error (_("selected digest algorithm is invalid\n"));
+ }
+ if (extra_digest_algo)
+ {
+ opt.extra_digest_algo = gcry_md_map_name (extra_digest_algo);
+ if (our_md_test_algo (opt.extra_digest_algo) )
+ log_error (_("selected digest algorithm is invalid\n"));
+ }
+ }
+
+ if (log_get_errorcount(0))
+ gpgsm_exit(2);
+
+ /* Set the random seed file. */
+ if (use_random_seed)
+ {
+ char *p = make_filename (opt.homedir, "random_seed", NULL);
+ gcry_control (GCRYCTL_SET_RANDOM_SEED_FILE, p);
+ xfree(p);
+ }
+
+ if (!cmd && opt.fingerprint && !with_fpr)
+ set_cmd (&cmd, aListKeys);
+
+ /* Add default keybox. */
+ if (!nrings && default_keyring)
+ {
+ int created;
+
+ keydb_add_resource ("pubring.kbx", 0, 0, &created);
+ if (created && !no_common_certs_import)
+ {
+ /* Import the standard certificates for a new default keybox. */
+ char *filelist[2];
+
+ filelist[0] = make_filename (gnupg_datadir (),"com-certs.pem", NULL);
+ filelist[1] = NULL;
+ if (!access (filelist[0], F_OK))
+ {
+ log_info (_("importing common certificates `%s'\n"),
+ filelist[0]);
+ gpgsm_import_files (&ctrl, 1, filelist, open_read);
+ }
+ xfree (filelist[0]);
+ }
+ }
+ for (sl = nrings; sl; sl = sl->next)
+ keydb_add_resource (sl->d, 0, 0, NULL);
+ FREE_STRLIST(nrings);
+
+
+ /* Prepare the audit log feature for certain commands. */
+ if (auditlog || htmlauditlog)
+ {
+ switch (cmd)
+ {
+ case aEncr:
+ case aSign:
+ case aDecrypt:
+ case aVerify:
+ audit_release (ctrl.audit);
+ ctrl.audit = audit_new ();
+ if (auditlog)
+ auditfp = open_es_fwrite (auditlog);
+ if (htmlauditlog)
+ htmlauditfp = open_es_fwrite (htmlauditlog);
+ break;
+ default:
+ break;
+ }
+ }
+
+
+ if (!do_not_setup_keys)
+ {
+ for (sl = locusr; sl ; sl = sl->next)
+ {
+ int rc = gpgsm_add_to_certlist (&ctrl, sl->d, 1, &signerlist, 0);
+ if (rc)
+ {
+ log_error (_("can't sign using `%s': %s\n"),
+ sl->d, gpg_strerror (rc));
+ gpgsm_status2 (&ctrl, STATUS_INV_SGNR,
+ get_inv_recpsgnr_code (rc), sl->d, NULL);
+ gpgsm_status2 (&ctrl, STATUS_INV_RECP,
+ get_inv_recpsgnr_code (rc), sl->d, NULL);
+ }
+ }
+
+ /* Build the recipient list. We first add the regular ones and then
+ the encrypt-to ones because the underlying function will silently
+ ignore duplicates and we can't allow to keep a duplicate which is
+ flagged as encrypt-to as the actually encrypt function would then
+ complain about no (regular) recipients. */
+ for (sl = remusr; sl; sl = sl->next)
+ if (!(sl->flags & 1))
+ do_add_recipient (&ctrl, sl->d, &recplist, 0, recp_required);
+ if (!opt.no_encrypt_to)
+ {
+ for (sl = remusr; sl; sl = sl->next)
+ if ((sl->flags & 1))
+ do_add_recipient (&ctrl, sl->d, &recplist, 1, recp_required);
+ }
+ }
+
+ if (log_get_errorcount(0))
+ gpgsm_exit(1); /* Must stop for invalid recipients. */
+
+ /* Dispatch command. */
+ switch (cmd)
+ {
+ case aGPGConfList:
+ { /* List options and default values in the GPG Conf format. */
+ char *config_filename_esc = percent_escape (opt.config_filename, NULL);
+
+ printf ("gpgconf-gpgsm.conf:%lu:\"%s\n",
+ GC_OPT_FLAG_DEFAULT, config_filename_esc);
+ xfree (config_filename_esc);
+
+ printf ("verbose:%lu:\n", GC_OPT_FLAG_NONE);
+ printf ("quiet:%lu:\n", GC_OPT_FLAG_NONE);
+ printf ("debug-level:%lu:\"none:\n", GC_OPT_FLAG_DEFAULT);
+ printf ("log-file:%lu:\n", GC_OPT_FLAG_NONE);
+ printf ("disable-crl-checks:%lu:\n", GC_OPT_FLAG_NONE);
+ printf ("disable-trusted-cert-crl-check:%lu:\n", GC_OPT_FLAG_NONE);
+ printf ("enable-ocsp:%lu:\n", GC_OPT_FLAG_NONE);
+ printf ("include-certs:%lu:%d:\n", GC_OPT_FLAG_DEFAULT,
+ DEFAULT_INCLUDE_CERTS);
+ printf ("disable-policy-checks:%lu:\n", GC_OPT_FLAG_NONE);
+ printf ("auto-issuer-key-retrieve:%lu:\n", GC_OPT_FLAG_NONE);
+ printf ("disable-dirmngr:%lu:\n", GC_OPT_FLAG_NONE);
+#ifndef HAVE_W32_SYSTEM
+ printf ("prefer-system-dirmngr:%lu:\n", GC_OPT_FLAG_NONE);
+#endif
+ printf ("cipher-algo:%lu:\"3DES:\n", GC_OPT_FLAG_DEFAULT);
+ printf ("p12-charset:%lu:\n", GC_OPT_FLAG_DEFAULT);
+ printf ("default-key:%lu:\n", GC_OPT_FLAG_DEFAULT);
+ printf ("encrypt-to:%lu:\n", GC_OPT_FLAG_DEFAULT);
+ printf ("keyserver:%lu:\n", GC_OPT_FLAG_NONE);
+
+ /* The next one is an info only item and should match what
+ proc_parameters actually implements. */
+ printf ("default_pubkey_algo:%lu:\"%s:\n", GC_OPT_FLAG_DEFAULT,
+ "RSA-2048");
+ }
+ break;
+ case aGPGConfTest:
+ /* This is merely a dummy command to test whether the
+ configuration file is valid. */
+ break;
+
+ case aServer:
+ if (debug_wait)
+ {
+ log_debug ("waiting for debugger - my pid is %u .....\n",
+ (unsigned int)getpid());
+ gnupg_sleep (debug_wait);
+ log_debug ("... okay\n");
+ }
+ gpgsm_server (recplist);
+ break;
+
+ case aCallDirmngr:
+ if (!argc)
+ wrong_args ("--call-dirmngr <command> {args}");
+ else
+ if (gpgsm_dirmngr_run_command (&ctrl, *argv, argc-1, argv+1))
+ gpgsm_exit (1);
+ break;
+
+ case aCallProtectTool:
+ run_protect_tool (argc, argv);
+ break;
+
+ case aEncr: /* Encrypt the given file. */
+ {
+ FILE *fp = open_fwrite (opt.outfile?opt.outfile:"-");
+
+ set_binary (stdin);
+
+ if (!argc) /* Source is stdin. */
+ gpgsm_encrypt (&ctrl, recplist, 0, fp);
+ else if (argc == 1) /* Source is the given file. */
+ gpgsm_encrypt (&ctrl, recplist, open_read (*argv), fp);
+ else
+ wrong_args ("--encrypt [datafile]");
+
+ if (fp != stdout)
+ fclose (fp);
+ }
+ break;
+
+ case aSign: /* Sign the given file. */
+ {
+ FILE *fp = open_fwrite (opt.outfile?opt.outfile:"-");
+
+ /* Fixme: We should also allow to concatenate multiple files for
+ signing because that is what gpg does.*/
+ set_binary (stdin);
+ if (!argc) /* Create from stdin. */
+ gpgsm_sign (&ctrl, signerlist, 0, detached_sig, fp);
+ else if (argc == 1) /* From file. */
+ gpgsm_sign (&ctrl, signerlist,
+ open_read (*argv), detached_sig, fp);
+ else
+ wrong_args ("--sign [datafile]");
+
+ if (fp != stdout)
+ fclose (fp);
+ }
+ break;
+
+ case aSignEncr: /* sign and encrypt the given file */
+ log_error ("this command has not yet been implemented\n");
+ break;
+
+ case aClearsign: /* make a clearsig */
+ log_error ("this command has not yet been implemented\n");
+ break;
+
+ case aVerify:
+ {
+ FILE *fp = NULL;
+
+ set_binary (stdin);
+ if (argc == 2 && opt.outfile)
+ log_info ("option --output ignored for a detached signature\n");
+ else if (opt.outfile)
+ fp = open_fwrite (opt.outfile);
+
+ if (!argc)
+ gpgsm_verify (&ctrl, 0, -1, fp); /* normal signature from stdin */
+ else if (argc == 1)
+ gpgsm_verify (&ctrl, open_read (*argv), -1, fp); /* std signature */
+ else if (argc == 2) /* detached signature (sig, detached) */
+ gpgsm_verify (&ctrl, open_read (*argv), open_read (argv[1]), NULL);
+ else
+ wrong_args ("--verify [signature [detached_data]]");
+
+ if (fp && fp != stdout)
+ fclose (fp);
+ }
+ break;
+
+ case aDecrypt:
+ {
+ FILE *fp = open_fwrite (opt.outfile?opt.outfile:"-");
+
+ set_binary (stdin);
+ if (!argc)
+ gpgsm_decrypt (&ctrl, 0, fp); /* from stdin */
+ else if (argc == 1)
+ gpgsm_decrypt (&ctrl, open_read (*argv), fp); /* from file */
+ else
+ wrong_args ("--decrypt [filename]");
+ if (fp != stdout)
+ fclose (fp);
+ }
+ break;
+
+ case aDeleteKey:
+ for (sl=NULL; argc; argc--, argv++)
+ add_to_strlist (&sl, *argv);
+ gpgsm_delete (&ctrl, sl);
+ free_strlist(sl);
+ break;
+
+ case aListChain:
+ case aDumpChain:
+ ctrl.with_chain = 1;
+ case aListKeys:
+ case aDumpKeys:
+ case aListExternalKeys:
+ case aDumpExternalKeys:
+ case aListSecretKeys:
+ case aDumpSecretKeys:
+ {
+ unsigned int mode;
+ estream_t fp;
+
+ switch (cmd)
+ {
+ case aListChain:
+ case aListKeys: mode = (0 | 0 | (1<<6)); break;
+ case aDumpChain:
+ case aDumpKeys: mode = (256 | 0 | (1<<6)); break;
+ case aListExternalKeys: mode = (0 | 0 | (1<<7)); break;
+ case aDumpExternalKeys: mode = (256 | 0 | (1<<7)); break;
+ case aListSecretKeys: mode = (0 | 2 | (1<<6)); break;
+ case aDumpSecretKeys: mode = (256 | 2 | (1<<6)); break;
+ default: BUG();
+ }
+
+ fp = open_es_fwrite (opt.outfile?opt.outfile:"-");
+ for (sl=NULL; argc; argc--, argv++)
+ add_to_strlist (&sl, *argv);
+ gpgsm_list_keys (&ctrl, sl, fp, mode);
+ free_strlist(sl);
+ es_fclose (fp);
+ }
+ break;
+
+
+ case aKeygen: /* Generate a key; well kind of. */
+ {
+ estream_t fpin = NULL;
+ FILE *fpout;
+
+ if (opt.batch)
+ {
+ if (!argc) /* Create from stdin. */
+ fpin = open_es_fread ("-");
+ else if (argc == 1) /* From file. */
+ fpin = open_es_fread (*argv);
+ else
+ wrong_args ("--gen-key --batch [parmfile]");
+ }
+
+ fpout = open_fwrite (opt.outfile?opt.outfile:"-");
+
+ if (fpin)
+ gpgsm_genkey (&ctrl, fpin, fpout);
+ else
+ gpgsm_gencertreq_tty (&ctrl, fpout);
+
+ if (fpout != stdout)
+ fclose (fpout);
+ }
+ break;
+
+
+ case aImport:
+ gpgsm_import_files (&ctrl, argc, argv, open_read);
+ break;
+
+ case aExport:
+ {
+ FILE *fp = open_fwrite (opt.outfile?opt.outfile:"-");
+
+ for (sl=NULL; argc; argc--, argv++)
+ add_to_strlist (&sl, *argv);
+ gpgsm_export (&ctrl, sl, fp, NULL);
+ free_strlist(sl);
+ if (fp != stdout)
+ fclose (fp);
+ }
+ break;
+
+ case aExportSecretKeyP12:
+ {
+ FILE *fp = open_fwrite (opt.outfile?opt.outfile:"-");
+
+ if (argc == 1)
+ gpgsm_p12_export (&ctrl, *argv, fp);
+ else
+ wrong_args ("--export-secret-key-p12 KEY-ID");
+ if (fp != stdout)
+ fclose (fp);
+ }
+ break;
+
+ case aSendKeys:
+ case aRecvKeys:
+ log_error ("this command has not yet been implemented\n");
+ break;
+
+
+ case aLearnCard:
+ if (argc)
+ wrong_args ("--learn-card");
+ else
+ {
+ int rc = gpgsm_agent_learn (&ctrl);
+ if (rc)
+ log_error ("error learning card: %s\n", gpg_strerror (rc));
+ }
+ break;
+
+ case aPasswd:
+ if (argc != 1)
+ wrong_args ("--passwd <key-Id>");
+ else
+ {
+ int rc;
+ ksba_cert_t cert = NULL;
+ char *grip = NULL;
+
+ rc = gpgsm_find_cert (*argv, NULL, &cert);
+ if (rc)
+ ;
+ else if (!(grip = gpgsm_get_keygrip_hexstring (cert)))
+ rc = gpg_error (GPG_ERR_BUG);
+ else
+ {
+ char *desc = gpgsm_format_keydesc (cert);
+ rc = gpgsm_agent_passwd (&ctrl, grip, desc);
+ xfree (desc);
+ }
+ if (rc)
+ log_error ("error changing passphrase: %s\n", gpg_strerror (rc));
+ xfree (grip);
+ ksba_cert_release (cert);
+ }
+ break;
+
+ case aKeydbClearSomeCertFlags:
+ for (sl=NULL; argc; argc--, argv++)
+ add_to_strlist (&sl, *argv);
+ keydb_clear_some_cert_flags (&ctrl, sl);
+ free_strlist(sl);
+ break;
+
+
+ default:
+ log_error (_("invalid command (there is no implicit command)\n"));
+ break;
+ }
+
+ /* Print the audit result if needed. */
+ if ((auditlog && auditfp) || (htmlauditlog && htmlauditfp))
+ {
+ if (auditlog && auditfp)
+ audit_print_result (ctrl.audit, auditfp, 0);
+ if (htmlauditlog && htmlauditfp)
+ audit_print_result (ctrl.audit, htmlauditfp, 1);
+ audit_release (ctrl.audit);
+ ctrl.audit = NULL;
+ es_fclose (auditfp);
+ es_fclose (htmlauditfp);
+ }
+
+ /* cleanup */
+ keyserver_list_free (opt.keyserver);
+ opt.keyserver = NULL;
+ gpgsm_release_certlist (recplist);
+ gpgsm_release_certlist (signerlist);
+ FREE_STRLIST (remusr);
+ FREE_STRLIST (locusr);
+ gpgsm_exit(0);
+ return 8; /*NOTREACHED*/
+}
+
+/* Note: This function is used by signal handlers!. */
+static void
+emergency_cleanup (void)
+{
+ gcry_control (GCRYCTL_TERM_SECMEM );
+}
+
+
+void
+gpgsm_exit (int rc)
+{
+ gcry_control (GCRYCTL_UPDATE_RANDOM_SEED_FILE);
+ 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 );
+ emergency_cleanup ();
+ rc = rc? rc : log_get_errorcount(0)? 2 : gpgsm_errors_seen? 1 : 0;
+ exit (rc);
+}
+
+
+void
+gpgsm_init_default_ctrl (struct server_control_s *ctrl)
+{
+ ctrl->include_certs = default_include_certs;
+ ctrl->use_ocsp = opt.enable_ocsp;
+ ctrl->validation_model = default_validation_model;
+}
+
+
+int
+gpgsm_parse_validation_model (const char *model)
+{
+ if (!ascii_strcasecmp (model, "shell") )
+ return 0;
+ else if ( !ascii_strcasecmp (model, "chain") )
+ return 1;
+ else
+ return -1;
+}
+
+
+/* Check whether the filename has the form "-&nnnn", where n is a
+ non-zero number. Returns this number or -1 if it is not the case. */
+static int
+check_special_filename (const char *fname, int for_write)
+{
+ if (allow_special_filenames
+ && fname && *fname == '-' && fname[1] == '&' ) {
+ int i;
+
+ fname += 2;
+ for (i=0; isdigit (fname[i]); i++ )
+ ;
+ if ( !fname[i] )
+ return translate_sys2libc_fd_int (atoi (fname), for_write);
+ }
+ return -1;
+}
+
+
+
+/* Open the FILENAME for read and return the filedescriptor. Stop
+ with an error message in case of problems. "-" denotes stdin and
+ if special filenames are allowed the given fd is opened instead. */
+static int
+open_read (const char *filename)
+{
+ int fd;
+
+ if (filename[0] == '-' && !filename[1])
+ {
+ set_binary (stdin);
+ return 0; /* stdin */
+ }
+ fd = check_special_filename (filename, 0);
+ if (fd != -1)
+ return fd;
+ fd = open (filename, O_RDONLY | O_BINARY);
+ if (fd == -1)
+ {
+ log_error (_("can't open `%s': %s\n"), filename, strerror (errno));
+ gpgsm_exit (2);
+ }
+ return fd;
+}
+
+/* Same as open_read but return an estream_t. */
+static estream_t
+open_es_fread (const char *filename)
+{
+ int fd;
+ estream_t fp;
+
+ if (filename[0] == '-' && !filename[1])
+ fd = fileno (stdin);
+ else
+ fd = check_special_filename (filename, 0);
+ if (fd != -1)
+ {
+ fp = es_fdopen_nc (fd, "rb");
+ if (!fp)
+ {
+ log_error ("es_fdopen(%d) failed: %s\n", fd, strerror (errno));
+ gpgsm_exit (2);
+ }
+ return fp;
+ }
+ fp = es_fopen (filename, "rb");
+ if (!fp)
+ {
+ log_error (_("can't open `%s': %s\n"), filename, strerror (errno));
+ gpgsm_exit (2);
+ }
+ return fp;
+}
+
+
+/* Open FILENAME for fwrite and return the stream. Stop with an error
+ message in case of problems. "-" denotes stdout and if special
+ filenames are allowed the given fd is opened instead. Caller must
+ close the returned stream unless it is stdout. */
+static FILE *
+open_fwrite (const char *filename)
+{
+ int fd;
+ FILE *fp;
+
+ if (filename[0] == '-' && !filename[1])
+ {
+ set_binary (stdout);
+ return stdout;
+ }
+
+ fd = check_special_filename (filename, 1);
+ if (fd != -1)
+ {
+ fp = fdopen (dup (fd), "wb");
+ if (!fp)
+ {
+ log_error ("fdopen(%d) failed: %s\n", fd, strerror (errno));
+ gpgsm_exit (2);
+ }
+ set_binary (fp);
+ return fp;
+ }
+ fp = fopen (filename, "wb");
+ if (!fp)
+ {
+ log_error (_("can't open `%s': %s\n"), filename, strerror (errno));
+ gpgsm_exit (2);
+ }
+ return fp;
+}
+
+
+/* Open FILENAME for fwrite and return an extended stream. Stop with
+ an error message in case of problems. "-" denotes stdout and if
+ special filenames are allowed the given fd is opened instead.
+ Caller must close the returned stream. */
+static estream_t
+open_es_fwrite (const char *filename)
+{
+ int fd;
+ estream_t fp;
+
+ if (filename[0] == '-' && !filename[1])
+ {
+ fflush (stdout);
+ fp = es_fdopen_nc (fileno(stdout), "wb");
+ return fp;
+ }
+
+ fd = check_special_filename (filename, 1);
+ if (fd != -1)
+ {
+ fp = es_fdopen_nc (fd, "wb");
+ if (!fp)
+ {
+ log_error ("es_fdopen(%d) failed: %s\n", fd, strerror (errno));
+ gpgsm_exit (2);
+ }
+ return fp;
+ }
+ fp = es_fopen (filename, "wb");
+ if (!fp)
+ {
+ log_error (_("can't open `%s': %s\n"), filename, strerror (errno));
+ gpgsm_exit (2);
+ }
+ return fp;
+}
+
+
+static void
+run_protect_tool (int argc, char **argv)
+{
+#ifndef HAVE_W32_SYSTEM
+ const char *pgm;
+ char **av;
+ int i;
+
+ if (!opt.protect_tool_program || !*opt.protect_tool_program)
+ pgm = gnupg_module_name (GNUPG_MODULE_NAME_PROTECT_TOOL);
+ else
+ pgm = opt.protect_tool_program;
+
+ av = xcalloc (argc+2, sizeof *av);
+ av[0] = strrchr (pgm, '/');
+ if (!av[0])
+ av[0] = xstrdup (pgm);
+ for (i=1; argc; i++, argc--, argv++)
+ av[i] = *argv;
+ av[i] = NULL;
+ execv (pgm, av);
+ log_error ("error executing `%s': %s\n", pgm, strerror (errno));
+#endif /*HAVE_W32_SYSTEM*/
+ gpgsm_exit (2);
+}
diff --git a/sm/gpgsm.h b/sm/gpgsm.h
new file mode 100644
index 0000000..c4a261b
--- /dev/null
+++ b/sm/gpgsm.h
@@ -0,0 +1,423 @@
+/* gpgsm.h - Global definitions for GpgSM
+ * Copyright (C) 2001, 2003, 2004, 2007, 2009 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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuPG is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef GPGSM_H
+#define GPGSM_H
+
+#ifdef GPG_ERR_SOURCE_DEFAULT
+#error GPG_ERR_SOURCE_DEFAULT already defined
+#endif
+#define GPG_ERR_SOURCE_DEFAULT GPG_ERR_SOURCE_GPGSM
+#include <gpg-error.h>
+
+
+#include <ksba.h>
+#include "../common/util.h"
+#include "../common/status.h"
+#include "../common/estream.h"
+#include "../common/audit.h"
+#include "../common/session-env.h"
+
+
+#define MAX_DIGEST_LEN 64
+
+struct keyserver_spec
+{
+ struct keyserver_spec *next;
+
+ char *host;
+ int port;
+ char *user;
+ char *pass;
+ char *base;
+};
+
+
+/* A large struct named "opt" to keep global flags. */
+struct
+{
+ unsigned int debug; /* debug flags (DBG_foo_VALUE) */
+ int verbose; /* verbosity level */
+ int quiet; /* be as quiet as possible */
+ int batch; /* run in batch mode, i.e w/o any user interaction */
+ int answer_yes; /* assume yes on most questions */
+ int answer_no; /* assume no on most questions */
+ int dry_run; /* don't change any persistent data */
+
+ const char *homedir; /* Configuration directory name */
+ const char *config_filename; /* Name of the used config file. */
+ const char *agent_program;
+
+ session_env_t session_env;
+ char *lc_ctype;
+ char *lc_messages;
+
+ const char *dirmngr_program;
+ int prefer_system_dirmngr; /* Prefer using a system wide drimngr. */
+ int disable_dirmngr; /* Do not do any dirmngr calls. */
+ const char *protect_tool_program;
+ char *outfile; /* name of output file */
+
+ int with_key_data;/* include raw key in the column delimted output */
+
+ int fingerprint; /* list fingerprints in all key listings */
+
+ int with_md5_fingerprint; /* Also print an MD5 fingerprint for
+ standard key listings. */
+
+ int armor; /* force base64 armoring (see also ctrl.with_base64) */
+ int no_armor; /* don't try to figure out whether data is base64 armored*/
+
+ const char *p12_charset; /* Use this charset for encoding the
+ pkcs#12 passphrase. */
+
+
+ const char *def_cipher_algoid; /* cipher algorithm to use if
+ nothing else is specified */
+
+ int def_compress_algo; /* Ditto for compress algorithm */
+
+ int forced_digest_algo; /* User forced hash algorithm. */
+
+ char *def_recipient; /* userID of the default recipient */
+ int def_recipient_self; /* The default recipient is the default key */
+
+ int no_encrypt_to; /* Ignore all as encrypt to marked recipients. */
+
+ char *local_user; /* NULL or argument to -u */
+
+ int extra_digest_algo; /* A digest algorithm also used for
+ verification of signatures. */
+
+ int always_trust; /* Trust the given keys even if there is no
+ valid certification chain */
+ int skip_verify; /* do not check signatures on data */
+
+ int lock_once; /* Keep lock once they are set */
+
+ int ignore_time_conflict; /* Ignore certain time conflicts */
+
+ int no_crl_check; /* Don't do a CRL check */
+ int no_trusted_cert_crl_check; /* Don't run a CRL check for trusted certs. */
+ int force_crl_refresh; /* Force refreshing the CRL. */
+ int enable_ocsp; /* Default to use OCSP checks. */
+
+ char *policy_file; /* full pathname of policy file */
+ int no_policy_check; /* ignore certificate policies */
+ int no_chain_validation; /* Bypass all cert chain validity tests */
+ int ignore_expiration; /* Ignore the notAfter validity checks. */
+ char *fixed_passphrase; /* Passphrase used by regression tests. */
+
+ int auto_issuer_key_retrieve; /* try to retrieve a missing issuer key. */
+
+ int qualsig_approval; /* Set to true if this software has
+ officially been approved to create an
+ verify qualified signatures. This is a
+ runtime option in case we want to check
+ the integrity of the software at
+ runtime. */
+
+ struct keyserver_spec *keyserver;
+
+ /* A list of certificate extension OIDs which are ignored so that
+ one can claim that a critical extension has been handled. One
+ OID per string. */
+ strlist_t ignored_cert_extensions;
+
+} opt;
+
+/* Debug values and macros. */
+#define DBG_X509_VALUE 1 /* debug x.509 data reading/writing */
+#define DBG_MPI_VALUE 2 /* debug mpi details */
+#define DBG_CRYPTO_VALUE 4 /* debug low level crypto */
+#define DBG_MEMORY_VALUE 32 /* debug memory allocation stuff */
+#define DBG_CACHE_VALUE 64 /* debug the caching */
+#define DBG_MEMSTAT_VALUE 128 /* show memory statistics */
+#define DBG_HASHING_VALUE 512 /* debug hashing operations */
+#define DBG_ASSUAN_VALUE 1024 /* debug assuan communication */
+
+#define DBG_X509 (opt.debug & DBG_X509_VALUE)
+#define DBG_CRYPTO (opt.debug & DBG_CRYPTO_VALUE)
+#define DBG_MEMORY (opt.debug & DBG_MEMORY_VALUE)
+#define DBG_CACHE (opt.debug & DBG_CACHE_VALUE)
+#define DBG_HASHING (opt.debug & DBG_HASHING_VALUE)
+#define DBG_ASSUAN (opt.debug & DBG_ASSUAN_VALUE)
+
+/* Forward declaration for an object defined in server.c */
+struct server_local_s;
+
+/* Session control object. This object is passed down to most
+ functions. Note that the default values for it are set by
+ gpgsm_init_default_ctrl(). */
+struct server_control_s
+{
+ int no_server; /* We are not running under server control */
+ int status_fd; /* Only for non-server mode */
+ struct server_local_s *server_local;
+
+ audit_ctx_t audit; /* NULL or a context for the audit subsystem. */
+ int agent_seen; /* Flag indicating that the gpg-agent has been
+ accessed. */
+
+ int with_colons; /* Use column delimited output format */
+ int with_chain; /* Include the certifying certs in a listing */
+ int with_validation;/* Validate each key while listing. */
+ int with_ephemeral_keys; /* Include ephemeral flagged keys in the
+ keylisting. */
+
+ int autodetect_encoding; /* Try to detect the input encoding */
+ int is_pem; /* Is in PEM format */
+ int is_base64; /* is in plain base-64 format */
+
+ int create_base64; /* Create base64 encoded output */
+ int create_pem; /* create PEM output */
+ const char *pem_name; /* PEM name to use */
+
+ int include_certs; /* -1 to send all certificates in the chain
+ along with a signature or the number of
+ certificates up the chain (0 = none, 1 = only
+ signer) */
+ int use_ocsp; /* Set to true if OCSP should be used. */
+ int validation_model; /* Set to 1 for the chain model. */
+};
+
+
+/* Data structure used in base64.c. */
+typedef struct base64_context_s *Base64Context;
+
+
+/* An object to keep a list of certificates. */
+struct certlist_s
+{
+ struct certlist_s *next;
+ ksba_cert_t cert;
+ int is_encrypt_to; /* True if the certificate has been set through
+ the --encrypto-to option. */
+ int hash_algo; /* Used to track the hash algorithm to use. */
+ const char *hash_algo_oid; /* And the corresponding OID. */
+};
+typedef struct certlist_s *certlist_t;
+
+
+/* A structure carrying information about trusted root certificates. */
+struct rootca_flags_s
+{
+ unsigned int valid:1; /* The rest of the structure has valid
+ information. */
+ unsigned int relax:1; /* Relax checking of root certificates. */
+ unsigned int chain_model:1; /* Root requires the use of the chain model. */
+};
+
+
+
+/*-- gpgsm.c --*/
+void gpgsm_exit (int rc);
+void gpgsm_init_default_ctrl (struct server_control_s *ctrl);
+int gpgsm_parse_validation_model (const char *model);
+
+/*-- server.c --*/
+void gpgsm_server (certlist_t default_recplist);
+gpg_error_t gpgsm_status (ctrl_t ctrl, int no, const char *text);
+gpg_error_t gpgsm_status2 (ctrl_t ctrl, int no, ...) GNUPG_GCC_A_SENTINEL(0);
+gpg_error_t gpgsm_status_with_err_code (ctrl_t ctrl, int no, const char *text,
+ gpg_err_code_t ec);
+gpg_error_t gpgsm_proxy_pinentry_notify (ctrl_t ctrl,
+ const unsigned char *line);
+
+/*-- fingerprint --*/
+unsigned char *gpgsm_get_fingerprint (ksba_cert_t cert, int algo,
+ unsigned char *array, int *r_len);
+char *gpgsm_get_fingerprint_string (ksba_cert_t cert, int algo);
+char *gpgsm_get_fingerprint_hexstring (ksba_cert_t cert, int algo);
+unsigned long gpgsm_get_short_fingerprint (ksba_cert_t cert,
+ unsigned long *r_high);
+unsigned char *gpgsm_get_keygrip (ksba_cert_t cert, unsigned char *array);
+char *gpgsm_get_keygrip_hexstring (ksba_cert_t cert);
+int gpgsm_get_key_algo_info (ksba_cert_t cert, unsigned int *nbits);
+char *gpgsm_get_certid (ksba_cert_t cert);
+
+
+/*-- base64.c --*/
+int gpgsm_create_reader (Base64Context *ctx,
+ ctrl_t ctrl, FILE *fp, int allow_multi_pem,
+ ksba_reader_t *r_reader);
+int gpgsm_reader_eof_seen (Base64Context ctx);
+void gpgsm_destroy_reader (Base64Context ctx);
+int gpgsm_create_writer (Base64Context *ctx,
+ ctrl_t ctrl, FILE *fp, estream_t stream,
+ ksba_writer_t *r_writer);
+int gpgsm_finish_writer (Base64Context ctx);
+void gpgsm_destroy_writer (Base64Context ctx);
+
+
+/*-- certdump.c --*/
+void gpgsm_print_serial (estream_t fp, ksba_const_sexp_t p);
+void gpgsm_print_time (estream_t fp, ksba_isotime_t t);
+void gpgsm_print_name2 (FILE *fp, const char *string, int translate);
+void gpgsm_print_name (FILE *fp, const char *string);
+void gpgsm_es_print_name (estream_t fp, const char *string);
+void gpgsm_es_print_name2 (estream_t fp, const char *string, int translate);
+
+void gpgsm_cert_log_name (const char *text, ksba_cert_t cert);
+
+void gpgsm_dump_cert (const char *text, ksba_cert_t cert);
+void gpgsm_dump_serial (ksba_const_sexp_t p);
+void gpgsm_dump_time (ksba_isotime_t t);
+void gpgsm_dump_string (const char *string);
+
+char *gpgsm_format_serial (ksba_const_sexp_t p);
+char *gpgsm_format_name2 (const char *name, int translate);
+char *gpgsm_format_name (const char *name);
+char *gpgsm_format_sn_issuer (ksba_sexp_t sn, const char *issuer);
+
+char *gpgsm_fpr_and_name_for_status (ksba_cert_t cert);
+
+char *gpgsm_format_keydesc (ksba_cert_t cert);
+
+
+/*-- certcheck.c --*/
+int gpgsm_check_cert_sig (ksba_cert_t issuer_cert, ksba_cert_t cert);
+int gpgsm_check_cms_signature (ksba_cert_t cert, ksba_const_sexp_t sigval,
+ gcry_md_hd_t md, int hash_algo, int *r_pkalgo);
+/* fixme: move create functions to another file */
+int gpgsm_create_cms_signature (ctrl_t ctrl,
+ ksba_cert_t cert, gcry_md_hd_t md, int mdalgo,
+ unsigned char **r_sigval);
+
+
+/*-- certchain.c --*/
+
+/* Flags used with gpgsm_validate_chain. */
+#define VALIDATE_FLAG_NO_DIRMNGR 1
+#define VALIDATE_FLAG_CHAIN_MODEL 2
+
+
+int gpgsm_walk_cert_chain (ctrl_t ctrl,
+ ksba_cert_t start, ksba_cert_t *r_next);
+int gpgsm_is_root_cert (ksba_cert_t cert);
+int gpgsm_validate_chain (ctrl_t ctrl, ksba_cert_t cert,
+ ksba_isotime_t checktime,
+ ksba_isotime_t r_exptime,
+ int listmode, estream_t listfp,
+ unsigned int flags, unsigned int *retflags);
+int gpgsm_basic_cert_check (ctrl_t ctrl, ksba_cert_t cert);
+
+/*-- certlist.c --*/
+int gpgsm_cert_use_sign_p (ksba_cert_t cert);
+int gpgsm_cert_use_encrypt_p (ksba_cert_t cert);
+int gpgsm_cert_use_verify_p (ksba_cert_t cert);
+int gpgsm_cert_use_decrypt_p (ksba_cert_t cert);
+int gpgsm_cert_use_cert_p (ksba_cert_t cert);
+int gpgsm_cert_use_ocsp_p (ksba_cert_t cert);
+int gpgsm_certs_identical_p (ksba_cert_t cert_a, ksba_cert_t cert_b);
+int gpgsm_add_cert_to_certlist (ctrl_t ctrl, ksba_cert_t cert,
+ certlist_t *listaddr, int is_encrypt_to);
+int gpgsm_add_to_certlist (ctrl_t ctrl, const char *name, int secret,
+ certlist_t *listaddr, int is_encrypt_to);
+void gpgsm_release_certlist (certlist_t list);
+int gpgsm_find_cert (const char *name, ksba_sexp_t keyid, ksba_cert_t *r_cert);
+
+/*-- keylist.c --*/
+gpg_error_t gpgsm_list_keys (ctrl_t ctrl, strlist_t names,
+ estream_t fp, unsigned int mode);
+
+/*-- import.c --*/
+int gpgsm_import (ctrl_t ctrl, int in_fd, int reimport_mode);
+int gpgsm_import_files (ctrl_t ctrl, int nfiles, char **files,
+ int (*of)(const char *fname));
+
+/*-- export.c --*/
+void gpgsm_export (ctrl_t ctrl, strlist_t names, FILE *fp, estream_t stream);
+void gpgsm_p12_export (ctrl_t ctrl, const char *name, FILE *fp);
+
+/*-- delete.c --*/
+int gpgsm_delete (ctrl_t ctrl, strlist_t names);
+
+/*-- verify.c --*/
+int gpgsm_verify (ctrl_t ctrl, int in_fd, int data_fd, FILE *out_fp);
+
+/*-- sign.c --*/
+int gpgsm_get_default_cert (ctrl_t ctrl, ksba_cert_t *r_cert);
+int gpgsm_sign (ctrl_t ctrl, certlist_t signerlist,
+ int data_fd, int detached, FILE *out_fp);
+
+/*-- encrypt.c --*/
+int gpgsm_encrypt (ctrl_t ctrl, certlist_t recplist, int in_fd, FILE *out_fp);
+
+/*-- decrypt.c --*/
+int gpgsm_decrypt (ctrl_t ctrl, int in_fd, FILE *out_fp);
+
+/*-- certreqgen.c --*/
+int gpgsm_genkey (ctrl_t ctrl, estream_t in_stream, FILE *out_fp);
+
+/*-- certreqgen-ui.c --*/
+void gpgsm_gencertreq_tty (ctrl_t ctrl, FILE *out_fp);
+
+
+/*-- qualified.c --*/
+gpg_error_t gpgsm_is_in_qualified_list (ctrl_t ctrl, ksba_cert_t cert,
+ char *country);
+gpg_error_t gpgsm_qualified_consent (ctrl_t ctrl, ksba_cert_t cert);
+gpg_error_t gpgsm_not_qualified_warning (ctrl_t ctrl, ksba_cert_t cert);
+
+/*-- call-agent.c --*/
+int gpgsm_agent_pksign (ctrl_t ctrl, const char *keygrip, const char *desc,
+ unsigned char *digest,
+ size_t digestlen,
+ int digestalgo,
+ unsigned char **r_buf, size_t *r_buflen);
+int gpgsm_scd_pksign (ctrl_t ctrl, const char *keyid, const char *desc,
+ unsigned char *digest, size_t digestlen, int digestalgo,
+ unsigned char **r_buf, size_t *r_buflen);
+int gpgsm_agent_pkdecrypt (ctrl_t ctrl, const char *keygrip, const char *desc,
+ ksba_const_sexp_t ciphertext,
+ char **r_buf, size_t *r_buflen);
+int gpgsm_agent_genkey (ctrl_t ctrl,
+ ksba_const_sexp_t keyparms, ksba_sexp_t *r_pubkey);
+int gpgsm_agent_readkey (ctrl_t ctrl, int fromcard, const char *hexkeygrip,
+ ksba_sexp_t *r_pubkey);
+int gpgsm_agent_scd_serialno (ctrl_t ctrl, char **r_serialno);
+int gpgsm_agent_scd_keypairinfo (ctrl_t ctrl, strlist_t *r_list);
+int gpgsm_agent_istrusted (ctrl_t ctrl, ksba_cert_t cert, const char *hexfpr,
+ struct rootca_flags_s *rootca_flags);
+int gpgsm_agent_havekey (ctrl_t ctrl, const char *hexkeygrip);
+int gpgsm_agent_marktrusted (ctrl_t ctrl, ksba_cert_t cert);
+int gpgsm_agent_learn (ctrl_t ctrl);
+int gpgsm_agent_passwd (ctrl_t ctrl, const char *hexkeygrip, const char *desc);
+gpg_error_t gpgsm_agent_get_confirmation (ctrl_t ctrl, const char *desc);
+gpg_error_t gpgsm_agent_send_nop (ctrl_t ctrl);
+gpg_error_t gpgsm_agent_keyinfo (ctrl_t ctrl, const char *hexkeygrip,
+ char **r_serialno);
+
+/*-- call-dirmngr.c --*/
+int gpgsm_dirmngr_isvalid (ctrl_t ctrl,
+ ksba_cert_t cert, ksba_cert_t issuer_cert,
+ int use_ocsp);
+int gpgsm_dirmngr_lookup (ctrl_t ctrl, strlist_t names, int cache_only,
+ void (*cb)(void*, ksba_cert_t), void *cb_value);
+int gpgsm_dirmngr_run_command (ctrl_t ctrl, const char *command,
+ int argc, char **argv);
+
+
+/*-- misc.c --*/
+void setup_pinentry_env (void);
+
+
+
+#endif /*GPGSM_H*/
diff --git a/sm/import.c b/sm/import.c
new file mode 100644
index 0000000..287e723
--- /dev/null
+++ b/sm/import.c
@@ -0,0 +1,807 @@
+/* import.c - Import certificates
+ * Copyright (C) 2001, 2003, 2004, 2009 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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuPG is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <time.h>
+#include <assert.h>
+#include <unistd.h>
+
+#include "gpgsm.h"
+#include <gcrypt.h>
+#include <ksba.h>
+
+#include "keydb.h"
+#include "exechelp.h"
+#include "i18n.h"
+#include "sysutils.h"
+#include "../kbx/keybox.h" /* for KEYBOX_FLAG_* */
+
+
+struct stats_s {
+ unsigned long count;
+ unsigned long imported;
+ unsigned long unchanged;
+ unsigned long not_imported;
+ unsigned long secret_read;
+ unsigned long secret_imported;
+ unsigned long secret_dups;
+ };
+
+
+static gpg_error_t parse_p12 (ctrl_t ctrl, ksba_reader_t reader, FILE **retfp,
+ struct stats_s *stats);
+
+
+
+static void
+print_imported_status (ctrl_t ctrl, ksba_cert_t cert, int new_cert)
+{
+ char *fpr;
+
+ fpr = gpgsm_get_fingerprint_hexstring (cert, GCRY_MD_SHA1);
+ if (new_cert)
+ gpgsm_status2 (ctrl, STATUS_IMPORTED, fpr, "[X.509]", NULL);
+
+ gpgsm_status2 (ctrl, STATUS_IMPORT_OK,
+ new_cert? "1":"0", fpr, NULL);
+
+ xfree (fpr);
+}
+
+
+/* Print an IMPORT_PROBLEM status. REASON is one of:
+ 0 := "No specific reason given".
+ 1 := "Invalid Certificate".
+ 2 := "Issuer Certificate missing".
+ 3 := "Certificate Chain too long".
+ 4 := "Error storing certificate".
+*/
+static void
+print_import_problem (ctrl_t ctrl, ksba_cert_t cert, int reason)
+{
+ char *fpr = NULL;
+ char buf[25];
+ int i;
+
+ sprintf (buf, "%d", reason);
+ if (cert)
+ {
+ fpr = gpgsm_get_fingerprint_hexstring (cert, GCRY_MD_SHA1);
+ /* detetect an error (all high) value */
+ for (i=0; fpr[i] == 'F'; i++)
+ ;
+ if (!fpr[i])
+ {
+ xfree (fpr);
+ fpr = NULL;
+ }
+ }
+ gpgsm_status2 (ctrl, STATUS_IMPORT_PROBLEM, buf, fpr, NULL);
+ xfree (fpr);
+}
+
+
+void
+print_imported_summary (ctrl_t ctrl, struct stats_s *stats)
+{
+ char buf[14*25];
+
+ if (!opt.quiet)
+ {
+ log_info (_("total number processed: %lu\n"), stats->count);
+ if (stats->imported)
+ {
+ log_info (_(" imported: %lu"), stats->imported );
+ log_printf ("\n");
+ }
+ if (stats->unchanged)
+ log_info (_(" unchanged: %lu\n"), stats->unchanged);
+ if (stats->secret_read)
+ log_info (_(" secret keys read: %lu\n"), stats->secret_read );
+ if (stats->secret_imported)
+ log_info (_(" secret keys imported: %lu\n"), stats->secret_imported );
+ if (stats->secret_dups)
+ log_info (_(" secret keys unchanged: %lu\n"), stats->secret_dups );
+ if (stats->not_imported)
+ log_info (_(" not imported: %lu\n"), stats->not_imported);
+ }
+
+ sprintf(buf, "%lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu",
+ stats->count,
+ 0l /*stats->no_user_id*/,
+ stats->imported,
+ 0l /*stats->imported_rsa*/,
+ stats->unchanged,
+ 0l /*stats->n_uids*/,
+ 0l /*stats->n_subk*/,
+ 0l /*stats->n_sigs*/,
+ 0l /*stats->n_revoc*/,
+ stats->secret_read,
+ stats->secret_imported,
+ stats->secret_dups,
+ 0l /*stats->skipped_new_keys*/,
+ stats->not_imported
+ );
+ gpgsm_status (ctrl, STATUS_IMPORT_RES, buf);
+}
+
+
+
+static void
+check_and_store (ctrl_t ctrl, struct stats_s *stats,
+ ksba_cert_t cert, int depth)
+{
+ int rc;
+
+ if (stats)
+ stats->count++;
+ if ( depth >= 50 )
+ {
+ log_error (_("certificate chain too long\n"));
+ if (stats)
+ stats->not_imported++;
+ print_import_problem (ctrl, cert, 3);
+ return;
+ }
+
+ /* Some basic checks, but don't care about missing certificates;
+ this is so that we are able to import entire certificate chains
+ w/o requiring a special order (i.e. root-CA first). This used
+ to be different but because gpgsm_verify even imports
+ certificates without any checks, it doesn't matter much and the
+ code gets much cleaner. A housekeeping function to remove
+ certificates w/o an anchor would be nice, though.
+
+ Optionally we do a full validation in addition to the basic test.
+ */
+ rc = gpgsm_basic_cert_check (ctrl, cert);
+ if (!rc && ctrl->with_validation)
+ rc = gpgsm_validate_chain (ctrl, cert, "", NULL, 0, NULL, 0, NULL);
+ if (!rc || (!ctrl->with_validation
+ && (gpg_err_code (rc) == GPG_ERR_MISSING_CERT
+ || gpg_err_code (rc) == GPG_ERR_MISSING_ISSUER_CERT)))
+ {
+ int existed;
+
+ if (!keydb_store_cert (cert, 0, &existed))
+ {
+ ksba_cert_t next = NULL;
+
+ if (!existed)
+ {
+ print_imported_status (ctrl, cert, 1);
+ if (stats)
+ stats->imported++;
+ }
+ else
+ {
+ print_imported_status (ctrl, cert, 0);
+ if (stats)
+ stats->unchanged++;
+ }
+
+ if (opt.verbose > 1 && existed)
+ {
+ if (depth)
+ log_info ("issuer certificate already in DB\n");
+ else
+ log_info ("certificate already in DB\n");
+ }
+ else if (opt.verbose && !existed)
+ {
+ if (depth)
+ log_info ("issuer certificate imported\n");
+ else
+ log_info ("certificate imported\n");
+ }
+
+ /* Now lets walk up the chain and import all certificates up
+ the chain. This is required in case we already stored
+ parent certificates in the ephemeral keybox. Do not
+ update the statistics, though. */
+ if (!gpgsm_walk_cert_chain (ctrl, cert, &next))
+ {
+ check_and_store (ctrl, NULL, next, depth+1);
+ ksba_cert_release (next);
+ }
+ }
+ else
+ {
+ log_error (_("error storing certificate\n"));
+ if (stats)
+ stats->not_imported++;
+ print_import_problem (ctrl, cert, 4);
+ }
+ }
+ else
+ {
+ log_error (_("basic certificate checks failed - not imported\n"));
+ if (stats)
+ stats->not_imported++;
+ /* We keep the test for GPG_ERR_MISSING_CERT only in case
+ GPG_ERR_MISSING_CERT has been used instead of the newer
+ GPG_ERR_MISSING_ISSUER_CERT. */
+ print_import_problem
+ (ctrl, cert,
+ gpg_err_code (rc) == GPG_ERR_MISSING_ISSUER_CERT? 2 :
+ gpg_err_code (rc) == GPG_ERR_MISSING_CERT? 2 :
+ gpg_err_code (rc) == GPG_ERR_BAD_CERT? 1 : 0);
+ }
+}
+
+
+
+
+static int
+import_one (ctrl_t ctrl, struct stats_s *stats, int in_fd)
+{
+ int rc;
+ Base64Context b64reader = NULL;
+ ksba_reader_t reader;
+ ksba_cert_t cert = NULL;
+ ksba_cms_t cms = NULL;
+ FILE *fp = NULL;
+ ksba_content_type_t ct;
+ int any = 0;
+
+ fp = fdopen ( dup (in_fd), "rb");
+ if (!fp)
+ {
+ rc = gpg_error (gpg_err_code_from_errno (errno));
+ log_error ("fdopen() failed: %s\n", strerror (errno));
+ goto leave;
+ }
+
+ rc = gpgsm_create_reader (&b64reader, ctrl, fp, 1, &reader);
+ if (rc)
+ {
+ log_error ("can't create reader: %s\n", gpg_strerror (rc));
+ goto leave;
+ }
+
+
+ /* We need to loop here to handle multiple PEM objects in one
+ file. */
+ do
+ {
+ ksba_cms_release (cms); cms = NULL;
+ ksba_cert_release (cert); cert = NULL;
+
+ ct = ksba_cms_identify (reader);
+ if (ct == KSBA_CT_SIGNED_DATA)
+ { /* This is probably a signed-only message - import the certs */
+ ksba_stop_reason_t stopreason;
+ int i;
+
+ rc = ksba_cms_new (&cms);
+ if (rc)
+ goto leave;
+
+ rc = ksba_cms_set_reader_writer (cms, reader, NULL);
+ if (rc)
+ {
+ log_error ("ksba_cms_set_reader_writer failed: %s\n",
+ gpg_strerror (rc));
+ goto leave;
+ }
+
+ do
+ {
+ rc = ksba_cms_parse (cms, &stopreason);
+ if (rc)
+ {
+ log_error ("ksba_cms_parse failed: %s\n", gpg_strerror (rc));
+ goto leave;
+ }
+
+ if (stopreason == KSBA_SR_BEGIN_DATA)
+ log_info ("not a certs-only message\n");
+ }
+ while (stopreason != KSBA_SR_READY);
+
+ for (i=0; (cert=ksba_cms_get_cert (cms, i)); i++)
+ {
+ check_and_store (ctrl, stats, cert, 0);
+ ksba_cert_release (cert);
+ cert = NULL;
+ }
+ if (!i)
+ log_error ("no certificate found\n");
+ else
+ any = 1;
+ }
+ else if (ct == KSBA_CT_PKCS12)
+ { /* This seems to be a pkcs12 message. We use an external
+ tool to parse the message and to store the private keys.
+ We need to use a another reader here to parse the
+ certificate we included in the p12 file; then we continue
+ to look for other pkcs12 files (works only if they are in
+ PEM format. */
+ FILE *certfp;
+ Base64Context b64p12rdr;
+ ksba_reader_t p12rdr;
+
+ rc = parse_p12 (ctrl, reader, &certfp, stats);
+ if (!rc)
+ {
+ any = 1;
+
+ rewind (certfp);
+ rc = gpgsm_create_reader (&b64p12rdr, ctrl, certfp, 1, &p12rdr);
+ if (rc)
+ {
+ log_error ("can't create reader: %s\n", gpg_strerror (rc));
+ fclose (certfp);
+ goto leave;
+ }
+
+ do
+ {
+ ksba_cert_release (cert); cert = NULL;
+ rc = ksba_cert_new (&cert);
+ if (!rc)
+ {
+ rc = ksba_cert_read_der (cert, p12rdr);
+ if (!rc)
+ check_and_store (ctrl, stats, cert, 0);
+ }
+ ksba_reader_clear (p12rdr, NULL, NULL);
+ }
+ while (!rc && !gpgsm_reader_eof_seen (b64p12rdr));
+
+ if (gpg_err_code (rc) == GPG_ERR_EOF)
+ rc = 0;
+ gpgsm_destroy_reader (b64p12rdr);
+ fclose (certfp);
+ if (rc)
+ goto leave;
+ }
+ }
+ else if (ct == KSBA_CT_NONE)
+ { /* Failed to identify this message - assume a certificate */
+
+ rc = ksba_cert_new (&cert);
+ if (rc)
+ goto leave;
+
+ rc = ksba_cert_read_der (cert, reader);
+ if (rc)
+ goto leave;
+
+ check_and_store (ctrl, stats, cert, 0);
+ any = 1;
+ }
+ else
+ {
+ log_error ("can't extract certificates from input\n");
+ rc = gpg_error (GPG_ERR_NO_DATA);
+ }
+
+ ksba_reader_clear (reader, NULL, NULL);
+ }
+ while (!gpgsm_reader_eof_seen (b64reader));
+
+ leave:
+ if (any && gpg_err_code (rc) == GPG_ERR_EOF)
+ rc = 0;
+ ksba_cms_release (cms);
+ ksba_cert_release (cert);
+ gpgsm_destroy_reader (b64reader);
+ if (fp)
+ fclose (fp);
+ return rc;
+}
+
+
+
+/* Re-import certifciates. IN_FD is a list of linefeed delimited
+ fingerprints t re-import. The actual re-import is done by clearing
+ the ephemeral flag. */
+static int
+reimport_one (ctrl_t ctrl, struct stats_s *stats, int in_fd)
+{
+ gpg_error_t err = 0;
+ estream_t fp = NULL;
+ char line[100]; /* Sufficient for a fingerprint. */
+ KEYDB_HANDLE kh;
+ KEYDB_SEARCH_DESC desc;
+ ksba_cert_t cert = NULL;
+ unsigned int flags;
+
+ kh = keydb_new (0);
+ if (!kh)
+ {
+ err = gpg_error (GPG_ERR_ENOMEM);;
+ log_error (_("failed to allocate keyDB handle\n"));
+ goto leave;
+ }
+ keydb_set_ephemeral (kh, 1);
+
+ fp = es_fdopen_nc (in_fd, "r");
+ if (!fp)
+ {
+ err = gpg_error_from_syserror ();
+ log_error ("es_fdopen(%d) failed: %s\n", in_fd, gpg_strerror (err));
+ goto leave;
+ }
+
+ while (es_fgets (line, DIM(line)-1, fp) )
+ {
+ if (*line && line[strlen(line)-1] != '\n')
+ {
+ err = gpg_error (GPG_ERR_LINE_TOO_LONG);
+ goto leave;
+ }
+ trim_spaces (line);
+ if (!*line)
+ continue;
+
+ stats->count++;
+
+ err = keydb_classify_name (line, &desc);
+ if (err)
+ {
+ print_import_problem (ctrl, NULL, 0);
+ stats->not_imported++;
+ continue;
+ }
+
+ keydb_search_reset (kh);
+ err = keydb_search (kh, &desc, 1);
+ if (err)
+ {
+ print_import_problem (ctrl, NULL, 0);
+ stats->not_imported++;
+ continue;
+ }
+
+ ksba_cert_release (cert);
+ cert = NULL;
+ err = keydb_get_cert (kh, &cert);
+ if (err)
+ {
+ log_error ("keydb_get_cert() failed: %s\n", gpg_strerror (err));
+ print_import_problem (ctrl, NULL, 1);
+ stats->not_imported++;
+ continue;
+ }
+
+ err = keydb_get_flags (kh, KEYBOX_FLAG_BLOB, 0, &flags);
+ if (err)
+ {
+ log_error (_("error getting stored flags: %s\n"), gpg_strerror (err));
+ print_imported_status (ctrl, cert, 0);
+ stats->not_imported++;
+ continue;
+ }
+ if ( !(flags & KEYBOX_FLAG_BLOB_EPHEMERAL) )
+ {
+ print_imported_status (ctrl, cert, 0);
+ stats->unchanged++;
+ continue;
+ }
+
+ err = keydb_set_cert_flags (cert, 1, KEYBOX_FLAG_BLOB, 0,
+ KEYBOX_FLAG_BLOB_EPHEMERAL, 0);
+ if (err)
+ {
+ log_error ("clearing ephemeral flag failed: %s\n",
+ gpg_strerror (err));
+ print_import_problem (ctrl, cert, 0);
+ stats->not_imported++;
+ continue;
+ }
+
+ print_imported_status (ctrl, cert, 1);
+ stats->imported++;
+ }
+ err = 0;
+ if (es_ferror (fp))
+ {
+ err = gpg_error_from_syserror ();
+ log_error ("error reading fd %d: %s\n", in_fd, gpg_strerror (err));
+ goto leave;
+ }
+
+ leave:
+ ksba_cert_release (cert);
+ keydb_release (kh);
+ es_fclose (fp);
+ return err;
+}
+
+
+
+int
+gpgsm_import (ctrl_t ctrl, int in_fd, int reimport_mode)
+{
+ int rc;
+ struct stats_s stats;
+
+ memset (&stats, 0, sizeof stats);
+ if (reimport_mode)
+ rc = reimport_one (ctrl, &stats, in_fd);
+ else
+ rc = import_one (ctrl, &stats, in_fd);
+ print_imported_summary (ctrl, &stats);
+ /* If we never printed an error message do it now so that a command
+ line invocation will return with an error (log_error keeps a
+ global errorcount) */
+ if (rc && !log_get_errorcount (0))
+ log_error (_("error importing certificate: %s\n"), gpg_strerror (rc));
+ return rc;
+}
+
+
+int
+gpgsm_import_files (ctrl_t ctrl, int nfiles, char **files,
+ int (*of)(const char *fname))
+{
+ int rc = 0;
+ struct stats_s stats;
+
+ memset (&stats, 0, sizeof stats);
+
+ if (!nfiles)
+ rc = import_one (ctrl, &stats, 0);
+ else
+ {
+ for (; nfiles && !rc ; nfiles--, files++)
+ {
+ int fd = of (*files);
+ rc = import_one (ctrl, &stats, fd);
+ close (fd);
+ if (rc == -1)
+ rc = 0;
+ }
+ }
+ print_imported_summary (ctrl, &stats);
+ /* If we never printed an error message do it now so that a command
+ line invocation will return with an error (log_error keeps a
+ global errorcount) */
+ if (rc && !log_get_errorcount (0))
+ log_error (_("error importing certificate: %s\n"), gpg_strerror (rc));
+ return rc;
+}
+
+
+/* Fork and exec the protecttool, connect the file descriptor of
+ INFILE to stdin, return a new stream in STATUSFILE, write the
+ output to OUTFILE and the pid of the process in PID. Returns 0 on
+ success or an error code. */
+static gpg_error_t
+popen_protect_tool (ctrl_t ctrl, const char *pgmname,
+ FILE *infile, FILE *outfile, FILE **statusfile, pid_t *pid)
+{
+ const char *argv[22];
+ int i=0;
+
+ /* Make sure that the agent is running so that the protect tool is
+ able to ask for a passphrase. This has only an effect under W32
+ where the agent is started on demand; sending a NOP does not harm
+ on other platforms. This is not really necessary anymore because
+ the protect tool does this now by itself; it does not harm either. */
+ gpgsm_agent_send_nop (ctrl);
+
+ argv[i++] = "--homedir";
+ argv[i++] = opt.homedir;
+ argv[i++] = "--p12-import";
+ argv[i++] = "--store";
+ argv[i++] = "--no-fail-on-exist";
+ argv[i++] = "--enable-status-msg";
+ if (opt.fixed_passphrase)
+ {
+ argv[i++] = "--passphrase";
+ argv[i++] = opt.fixed_passphrase;
+ }
+ if (opt.agent_program)
+ {
+ argv[i++] = "--agent-program";
+ argv[i++] = opt.agent_program;
+ }
+ argv[i++] = "--",
+ argv[i] = NULL;
+ assert (i < sizeof argv);
+
+ return gnupg_spawn_process (pgmname, argv, infile, outfile,
+ setup_pinentry_env, (128 | 64),
+ statusfile, pid);
+}
+
+
+/* Assume that the reader is at a pkcs#12 message and try to import
+ certificates from that stupid format. We will also store secret
+ keys. All of the pkcs#12 parsing and key storing is handled by the
+ gpg-protect-tool, we merely have to take care of receiving the
+ certificates. On success RETFP returns a temporary file with
+ certificates. */
+static gpg_error_t
+parse_p12 (ctrl_t ctrl, ksba_reader_t reader,
+ FILE **retfp, struct stats_s *stats)
+{
+ const char *pgmname;
+ gpg_error_t err = 0, child_err = 0;
+ int c, cont_line;
+ unsigned int pos;
+ FILE *tmpfp, *certfp = NULL, *fp = NULL;
+ char buffer[1024];
+ size_t nread;
+ pid_t pid = -1;
+ int bad_pass = 0;
+
+ if (!opt.protect_tool_program || !*opt.protect_tool_program)
+ pgmname = gnupg_module_name (GNUPG_MODULE_NAME_PROTECT_TOOL);
+ else
+ pgmname = opt.protect_tool_program;
+
+ *retfp = NULL;
+
+ /* To avoid an extra feeder process or doing selects and because
+ gpg-protect-tool will anyway parse the entire pkcs#12 message in
+ memory, we simply use tempfiles here and pass them to
+ the gpg-protect-tool. */
+ tmpfp = gnupg_tmpfile ();
+ if (!tmpfp)
+ {
+ err = gpg_error_from_syserror ();
+ log_error (_("error creating temporary file: %s\n"), strerror (errno));
+ goto cleanup;
+ }
+ while (!(err = ksba_reader_read (reader, buffer, sizeof buffer, &nread)))
+ {
+ if (nread && fwrite (buffer, nread, 1, tmpfp) != 1)
+ {
+ err = gpg_error_from_syserror ();
+ log_error (_("error writing to temporary file: %s\n"),
+ strerror (errno));
+ goto cleanup;
+ }
+ }
+ if (gpg_err_code (err) == GPG_ERR_EOF)
+ err = 0;
+ if (err)
+ {
+ log_error (_("error reading input: %s\n"), gpg_strerror (err));
+ goto cleanup;
+ }
+
+ certfp = gnupg_tmpfile ();
+ if (!certfp)
+ {
+ err = gpg_error_from_syserror ();
+ log_error (_("error creating temporary file: %s\n"), strerror (errno));
+ goto cleanup;
+ }
+
+ err = popen_protect_tool (ctrl, pgmname, tmpfp, certfp, &fp, &pid);
+ if (err)
+ {
+ pid = -1;
+ goto cleanup;
+ }
+ fclose (tmpfp);
+ tmpfp = NULL;
+
+ /* Read stderr of the protect tool. */
+ pos = 0;
+ cont_line = 0;
+ while ((c=getc (fp)) != EOF)
+ {
+ /* fixme: We could here grep for status information of the
+ protect tool to figure out better error codes for
+ CHILD_ERR. */
+ buffer[pos++] = c;
+ if (pos >= sizeof buffer - 5 || c == '\n')
+ {
+ buffer[pos - (c == '\n')] = 0;
+ if (cont_line)
+ log_printf ("%s", buffer);
+ else
+ {
+ if (!strncmp (buffer, "gpg-protect-tool: [PROTECT-TOOL:] ",34))
+ {
+ char *p, *pend;
+
+ p = buffer + 34;
+ pend = strchr (p, ' ');
+ if (pend)
+ *pend = 0;
+ if ( !strcmp (p, "secretkey-stored"))
+ {
+ stats->count++;
+ stats->secret_read++;
+ stats->secret_imported++;
+ }
+ else if ( !strcmp (p, "secretkey-exists"))
+ {
+ stats->count++;
+ stats->secret_read++;
+ stats->secret_dups++;
+ }
+ else if ( !strcmp (p, "bad-passphrase"))
+ {
+
+ }
+ }
+ else
+ {
+ log_info ("%s", buffer);
+ if (!strncmp (buffer, "gpg-protect-tool: "
+ "possibly bad passphrase given",46))
+ bad_pass++;
+ }
+ }
+ pos = 0;
+ cont_line = (c != '\n');
+ }
+ }
+
+ if (pos)
+ {
+ buffer[pos] = 0;
+ if (cont_line)
+ log_printf ("%s\n", buffer);
+ else
+ log_info ("%s\n", buffer);
+ }
+
+
+ /* If we found no error in the output of the child, setup a suitable
+ error code, which will later be reset if the exit status of the
+ child is 0. */
+ if (!child_err)
+ child_err = gpg_error (GPG_ERR_DECRYPT_FAILED);
+
+ cleanup:
+ if (tmpfp)
+ fclose (tmpfp);
+ if (fp)
+ fclose (fp);
+ if (pid != -1)
+ {
+ if (!gnupg_wait_process (pgmname, pid, NULL))
+ child_err = 0;
+ }
+ if (!err)
+ err = child_err;
+ if (err)
+ {
+ if (certfp)
+ fclose (certfp);
+ }
+ else
+ *retfp = certfp;
+
+ if (bad_pass)
+ {
+ /* We only write a plain error code and not direct
+ BAD_PASSPHRASE because the pkcs12 parser might issue this
+ message multiple times, BAD_PASSPHRASE in general requires a
+ keyID and parts of the import might actually succeed so that
+ IMPORT_PROBLEM is also not appropriate. */
+ gpgsm_status_with_err_code (ctrl, STATUS_ERROR,
+ "import.parsep12", GPG_ERR_BAD_PASSPHRASE);
+ }
+
+ return err;
+}
diff --git a/sm/keydb.c b/sm/keydb.c
new file mode 100644
index 0000000..37f791e
--- /dev/null
+++ b/sm/keydb.c
@@ -0,0 +1,1535 @@
+/* keydb.c - key database dispatcher
+ * Copyright (C) 2001, 2003, 2004 Free Software Foundation, Inc.
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuPG is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <assert.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include "gpgsm.h"
+#include "../kbx/keybox.h"
+#include "keydb.h"
+#include "i18n.h"
+
+static int active_handles;
+
+typedef enum {
+ KEYDB_RESOURCE_TYPE_NONE = 0,
+ KEYDB_RESOURCE_TYPE_KEYBOX
+} KeydbResourceType;
+#define MAX_KEYDB_RESOURCES 20
+
+struct resource_item {
+ KeydbResourceType type;
+ union {
+ KEYBOX_HANDLE kr;
+ } u;
+ void *token;
+ int secret;
+ DOTLOCK lockhandle;
+};
+
+static struct resource_item all_resources[MAX_KEYDB_RESOURCES];
+static int used_resources;
+
+struct keydb_handle {
+ int locked;
+ int found;
+ int current;
+ int is_ephemeral;
+ 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 keybox file).
+ * The first keybox which is added by this function is created if it
+ * does not exist. If AUTO_CREATED is not NULL it will be set to true
+ * if the function has created a a new keybox.
+ */
+int
+keydb_add_resource (const char *url, int force, int secret, int *auto_created)
+{
+ static int any_secret, any_public;
+ const char *resname = url;
+ char *filename = NULL;
+ int rc = 0;
+ FILE *fp;
+ KeydbResourceType rt = KEYDB_RESOURCE_TYPE_NONE;
+
+ if (auto_created)
+ *auto_created = 0;
+
+ /* Do we have an URL?
+ gnupg-kbx:filename := this is a plain keybox
+ filename := See what is is, but create as plain keybox.
+ */
+ if (strlen (resname) > 10)
+ {
+ if (!strncmp (resname, "gnupg-kbx:", 10) )
+ {
+ rt = KEYDB_RESOURCE_TYPE_KEYBOX;
+ resname += 10;
+ }
+#if !defined(HAVE_DRIVE_LETTERS) && !defined(__riscos__)
+ else if (strchr (resname, ':'))
+ {
+ log_error ("invalid key resource URL `%s'\n", url );
+ rc = gpg_error (GPG_ERR_GENERAL);
+ goto leave;
+ }
+#endif /* !HAVE_DRIVE_LETTERS && !__riscos__ */
+ }
+
+ if (*resname != DIRSEP_C )
+ { /* do tilde expansion etc */
+ if (strchr(resname, DIRSEP_C) )
+ filename = make_filename (resname, NULL);
+ else
+ filename = make_filename (opt.homedir, resname, NULL);
+ }
+ else
+ filename = xstrdup (resname);
+
+ if (!force)
+ force = secret? !any_secret : !any_public;
+
+ /* see whether we can determine the filetype */
+ if (rt == KEYDB_RESOURCE_TYPE_NONE)
+ {
+ FILE *fp2 = fopen( filename, "rb" );
+
+ if (fp2) {
+ u32 magic;
+
+ /* FIXME: check for the keybox magic */
+ if (fread( &magic, 4, 1, fp2) == 1 )
+ {
+ if (magic == 0x13579ace || magic == 0xce9a5713)
+ ; /* GDBM magic - no more support */
+ else
+ rt = KEYDB_RESOURCE_TYPE_KEYBOX;
+ }
+ else /* maybe empty: assume ring */
+ rt = KEYDB_RESOURCE_TYPE_KEYBOX;
+ fclose (fp2);
+ }
+ else /* no file yet: create ring */
+ rt = KEYDB_RESOURCE_TYPE_KEYBOX;
+ }
+
+ switch (rt)
+ {
+ case KEYDB_RESOURCE_TYPE_NONE:
+ log_error ("unknown type of key resource `%s'\n", url );
+ rc = gpg_error (GPG_ERR_GENERAL);
+ goto leave;
+
+ case KEYDB_RESOURCE_TYPE_KEYBOX:
+ fp = fopen (filename, "rb");
+ if (!fp && !force)
+ {
+ rc = gpg_error (gpg_err_code_from_errno (errno));
+ goto leave;
+ }
+
+ if (!fp)
+ { /* no file */
+#if 0 /* no autocreate of the homedirectory yet */
+ {
+ char *last_slash_in_filename;
+
+ 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 can
+ read the options file in on startup */
+ try_make_homedir (filename);
+ rc = gpg_error (GPG_ERR_FILE_OPEN_ERROR);
+ *last_slash_in_filename = DIRSEP_C;
+ goto leave;
+ }
+ *last_slash_in_filename = DIRSEP_C;
+ }
+#endif
+ fp = fopen (filename, "w");
+ if (!fp)
+ {
+ rc = gpg_error (gpg_err_code_from_errno (errno));
+ log_error (_("error creating keybox `%s': %s\n"),
+ filename, strerror(errno));
+ if (errno == ENOENT)
+ log_info (_("you may want to start the gpg-agent first\n"));
+ goto leave;
+ }
+
+ if (!opt.quiet)
+ log_info (_("keybox `%s' created\n"), filename);
+ if (auto_created)
+ *auto_created = 1;
+ }
+ fclose (fp);
+ fp = NULL;
+ /* now register the file */
+ {
+
+ void *token = keybox_register_file (filename, secret);
+ if (!token)
+ ; /* already registered - ignore it */
+ else if (used_resources >= MAX_KEYDB_RESOURCES)
+ rc = gpg_error (GPG_ERR_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;
+
+ all_resources[used_resources].lockhandle
+ = create_dotlock (filename);
+ if (!all_resources[used_resources].lockhandle)
+ log_fatal ( _("can't create lock for `%s'\n"), filename);
+
+ /* Do a compress run if needed and the file is not locked. */
+ if (!make_dotlock (all_resources[used_resources].lockhandle, 0))
+ {
+ KEYBOX_HANDLE kbxhd = keybox_new (token, secret);
+
+ if (kbxhd)
+ {
+ keybox_compress (kbxhd);
+ keybox_release (kbxhd);
+ }
+ release_dotlock (all_resources[used_resources].lockhandle);
+ }
+
+ used_resources++;
+ }
+ }
+
+
+ break;
+ default:
+ log_error ("resource type of `%s' not supported\n", url);
+ rc = gpg_error (GPG_ERR_NOT_SUPPORTED);
+ goto leave;
+ }
+
+ /* fixme: check directory permissions and print a warning */
+
+ leave:
+ if (rc)
+ log_error ("keyblock resource `%s': %s\n", filename, gpg_strerror(rc));
+ else if (secret)
+ any_secret = 1;
+ else
+ any_public = 1;
+ xfree (filename);
+ return rc;
+}
+
+
+KEYDB_HANDLE
+keydb_new (int secret)
+{
+ KEYDB_HANDLE hd;
+ int i, j;
+
+ hd = xcalloc (1, 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_KEYBOX:
+ 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].lockhandle = all_resources[i].lockhandle;
+ hd->active[j].u.kr = keybox_new (all_resources[i].token, secret);
+ if (!hd->active[j].u.kr)
+ {
+ xfree (hd);
+ return NULL; /* fixme: release all previously allocated handles*/
+ }
+ j++;
+ break;
+ }
+ }
+ hd->used = j;
+
+ active_handles++;
+ return hd;
+}
+
+void
+keydb_release (KEYDB_HANDLE hd)
+{
+ int i;
+
+ if (!hd)
+ return;
+ assert (active_handles > 0);
+ active_handles--;
+
+ unlock_all (hd);
+ for (i=0; i < hd->used; i++)
+ {
+ switch (hd->active[i].type)
+ {
+ case KEYDB_RESOURCE_TYPE_NONE:
+ break;
+ case KEYDB_RESOURCE_TYPE_KEYBOX:
+ keybox_release (hd->active[i].u.kr);
+ break;
+ }
+ }
+
+ xfree (hd);
+}
+
+
+/* Return the name of the current resource. This is function first
+ looks for the last found found, then for the current search
+ position, and last returns the first available resource. The
+ returned string is only valid as long as the handle exists. This
+ function does only return NULL if no handle is specified, in all
+ other error cases an empty string is returned. */
+const char *
+keydb_get_resource_name (KEYDB_HANDLE hd)
+{
+ int idx;
+ const char *s = NULL;
+
+ if (!hd)
+ return NULL;
+
+ if ( hd->found >= 0 && hd->found < hd->used)
+ idx = hd->found;
+ else if ( hd->current >= 0 && hd->current < hd->used)
+ idx = hd->current;
+ else
+ idx = 0;
+
+ switch (hd->active[idx].type)
+ {
+ case KEYDB_RESOURCE_TYPE_NONE:
+ s = NULL;
+ break;
+ case KEYDB_RESOURCE_TYPE_KEYBOX:
+ s = keybox_get_resource_name (hd->active[idx].u.kr);
+ break;
+ }
+
+ return s? s: "";
+}
+
+/* Switch the handle into ephemeral mode and return the orginal value. */
+int
+keydb_set_ephemeral (KEYDB_HANDLE hd, int yes)
+{
+ int i;
+
+ if (!hd)
+ return 0;
+
+ yes = !!yes;
+ if (hd->is_ephemeral != yes)
+ {
+ for (i=0; i < hd->used; i++)
+ {
+ switch (hd->active[i].type)
+ {
+ case KEYDB_RESOURCE_TYPE_NONE:
+ break;
+ case KEYDB_RESOURCE_TYPE_KEYBOX:
+ keybox_set_ephemeral (hd->active[i].u.kr, yes);
+ break;
+ }
+ }
+ }
+
+ i = hd->is_ephemeral;
+ hd->is_ephemeral = yes;
+ return i;
+}
+
+
+/* If the keyring has not yet been locked, lock it now. This
+ operation is required before any update operation; it is optional
+ for an insert operation. The lock is released with
+ keydb_released. */
+gpg_error_t
+keydb_lock (KEYDB_HANDLE hd)
+{
+ if (!hd)
+ return gpg_error (GPG_ERR_INV_HANDLE);
+ if (hd->locked)
+ return 0; /* Already locked. */
+ return lock_all (hd);
+}
+
+
+
+static int
+lock_all (KEYDB_HANDLE hd)
+{
+ int i, rc = 0;
+
+ /* Fixme: This locking scheme may lead to deadlock if the resources
+ are not added in the same order by all processes. We are
+ currently only allowing one resource so it is not a problem. */
+ for (i=0; i < hd->used; i++)
+ {
+ switch (hd->active[i].type)
+ {
+ case KEYDB_RESOURCE_TYPE_NONE:
+ break;
+ case KEYDB_RESOURCE_TYPE_KEYBOX:
+ if (hd->active[i].lockhandle)
+ rc = make_dotlock (hd->active[i].lockhandle, -1);
+ break;
+ }
+ if (rc)
+ 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_KEYBOX:
+ if (hd->active[i].lockhandle)
+ release_dotlock (hd->active[i].lockhandle);
+ break;
+ }
+ }
+ }
+ else
+ hd->locked = 1;
+
+ /* make_dotlock () does not yet guarantee that errno is set, thus
+ we can't rely on the error reason and will simply use
+ EACCES. */
+ return rc? gpg_error (GPG_ERR_EACCES) : 0;
+}
+
+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_KEYBOX:
+ if (hd->active[i].lockhandle)
+ release_dotlock (hd->active[i].lockhandle);
+ break;
+ }
+ }
+ hd->locked = 0;
+}
+
+
+#if 0
+/*
+ * Return the last found keybox. 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_KEYBOX:
+ rc = keybox_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;
+
+ if (!hd->locked)
+ return gpg_error (GPG_ERR_NOT_LOCKED);
+
+ switch (hd->active[hd->found].type) {
+ case KEYDB_RESOURCE_TYPE_NONE:
+ rc = G10ERR_GENERAL; /* oops */
+ break;
+ case KEYDB_RESOURCE_TYPE_KEYBOX:
+ rc = keybox_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_KEYBOX:
+ rc = keybox_insert_keyblock (hd->active[idx].u.kr, kb);
+ break;
+ }
+
+ unlock_all (hd);
+ return rc;
+}
+
+#endif /*disabled code*/
+
+
+
+/*
+ Return the last found object. 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_cert (KEYDB_HANDLE hd, ksba_cert_t *r_cert)
+{
+ int rc = 0;
+
+ if (!hd)
+ return gpg_error (GPG_ERR_INV_VALUE);
+
+ if ( hd->found < 0 || hd->found >= hd->used)
+ return -1; /* nothing found */
+
+ switch (hd->active[hd->found].type)
+ {
+ case KEYDB_RESOURCE_TYPE_NONE:
+ rc = gpg_error (GPG_ERR_GENERAL); /* oops */
+ break;
+ case KEYDB_RESOURCE_TYPE_KEYBOX:
+ rc = keybox_get_cert (hd->active[hd->found].u.kr, r_cert);
+ break;
+ }
+
+ return rc;
+}
+
+/* Return a flag of the last found object. WHICH is the flag requested;
+ it should be one of the KEYBOX_FLAG_ values. If the operation is
+ successful, the flag value will be stored at the address given by
+ VALUE. Return 0 on success or an error code. */
+gpg_error_t
+keydb_get_flags (KEYDB_HANDLE hd, int which, int idx, unsigned int *value)
+{
+ int err = 0;
+
+ if (!hd)
+ return gpg_error (GPG_ERR_INV_VALUE);
+
+ if ( hd->found < 0 || hd->found >= hd->used)
+ return gpg_error (GPG_ERR_NOTHING_FOUND);
+
+ switch (hd->active[hd->found].type)
+ {
+ case KEYDB_RESOURCE_TYPE_NONE:
+ err = gpg_error (GPG_ERR_GENERAL); /* oops */
+ break;
+ case KEYDB_RESOURCE_TYPE_KEYBOX:
+ err = keybox_get_flags (hd->active[hd->found].u.kr, which, idx, value);
+ break;
+ }
+
+ return err;
+}
+
+/* Set a flag of the last found object. WHICH is the flag to be set; it
+ should be one of the KEYBOX_FLAG_ values. If the operation is
+ successful, the flag value will be stored in the keybox. Note,
+ that some flag values can't be updated and thus may return an
+ error, some other flag values may be masked out before an update.
+ Returns 0 on success or an error code. */
+gpg_error_t
+keydb_set_flags (KEYDB_HANDLE hd, int which, int idx, unsigned int value)
+{
+ int err = 0;
+
+ if (!hd)
+ return gpg_error (GPG_ERR_INV_VALUE);
+
+ if ( hd->found < 0 || hd->found >= hd->used)
+ return gpg_error (GPG_ERR_NOTHING_FOUND);
+
+ if (!hd->locked)
+ return gpg_error (GPG_ERR_NOT_LOCKED);
+
+ switch (hd->active[hd->found].type)
+ {
+ case KEYDB_RESOURCE_TYPE_NONE:
+ err = gpg_error (GPG_ERR_GENERAL); /* oops */
+ break;
+ case KEYDB_RESOURCE_TYPE_KEYBOX:
+ err = keybox_set_flags (hd->active[hd->found].u.kr, which, idx, value);
+ break;
+ }
+
+ return err;
+}
+
+/*
+ * Insert a new Certificate into one of the resources.
+ */
+int
+keydb_insert_cert (KEYDB_HANDLE hd, ksba_cert_t cert)
+{
+ int rc = -1;
+ int idx;
+ unsigned char digest[20];
+
+ if (!hd)
+ return gpg_error (GPG_ERR_INV_VALUE);
+
+ 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 gpg_error (GPG_ERR_GENERAL);
+
+ if (!hd->locked)
+ return gpg_error (GPG_ERR_NOT_LOCKED);
+
+ gpgsm_get_fingerprint (cert, GCRY_MD_SHA1, digest, NULL); /* kludge*/
+
+ switch (hd->active[idx].type)
+ {
+ case KEYDB_RESOURCE_TYPE_NONE:
+ rc = gpg_error (GPG_ERR_GENERAL);
+ break;
+ case KEYDB_RESOURCE_TYPE_KEYBOX:
+ rc = keybox_insert_cert (hd->active[idx].u.kr, cert, digest);
+ break;
+ }
+
+ unlock_all (hd);
+ return rc;
+}
+
+
+
+/* Update the current keyblock with KB. */
+int
+keydb_update_cert (KEYDB_HANDLE hd, ksba_cert_t cert)
+{
+ int rc = 0;
+ unsigned char digest[20];
+
+ if (!hd)
+ return gpg_error (GPG_ERR_INV_VALUE);
+
+ 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;
+
+ gpgsm_get_fingerprint (cert, GCRY_MD_SHA1, digest, NULL); /* kludge*/
+
+ switch (hd->active[hd->found].type)
+ {
+ case KEYDB_RESOURCE_TYPE_NONE:
+ rc = gpg_error (GPG_ERR_GENERAL); /* oops */
+ break;
+ case KEYDB_RESOURCE_TYPE_KEYBOX:
+ rc = keybox_update_cert (hd->active[hd->found].u.kr, cert, digest);
+ break;
+ }
+
+ unlock_all (hd);
+ return rc;
+}
+
+
+/*
+ * The current keyblock or cert will be deleted.
+ */
+int
+keydb_delete (KEYDB_HANDLE hd, int unlock)
+{
+ int rc = -1;
+
+ if (!hd)
+ return gpg_error (GPG_ERR_INV_VALUE);
+
+ if ( hd->found < 0 || hd->found >= hd->used)
+ return -1; /* nothing found */
+
+ if( opt.dry_run )
+ return 0;
+
+ if (!hd->locked)
+ return gpg_error (GPG_ERR_NOT_LOCKED);
+
+ switch (hd->active[hd->found].type)
+ {
+ case KEYDB_RESOURCE_TYPE_NONE:
+ rc = gpg_error (GPG_ERR_GENERAL);
+ break;
+ case KEYDB_RESOURCE_TYPE_KEYBOX:
+ rc = keybox_delete (hd->active[hd->found].u.kr);
+ break;
+ }
+
+ if (unlock)
+ 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;
+
+ (void)reserved;
+
+ if (!hd)
+ return gpg_error (GPG_ERR_INV_VALUE);
+
+ 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_KEYBOX:
+ if (keybox_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;
+
+ 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_KEYBOX:
+/* rc = keybox_rebuild_cache (all_resources[i].token); */
+/* if (rc) */
+/* log_error (_("failed to rebuild keybox 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 gpg_error (GPG_ERR_INV_VALUE);
+
+ 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_KEYBOX:
+ rc = keybox_search_reset (hd->active[i].u.kr);
+ break;
+ }
+ }
+ return rc; /* fixme: we need to map error codes or share them with
+ all modules*/
+}
+
+/*
+ * 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 gpg_error (GPG_ERR_INV_VALUE);
+
+ 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_KEYBOX:
+ rc = keybox_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;
+
+ (void)kid;
+
+ 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, 20);
+ return keydb_search (hd, &desc, 1);
+}
+
+int
+keydb_search_issuer (KEYDB_HANDLE hd, const char *issuer)
+{
+ KEYDB_SEARCH_DESC desc;
+ int rc;
+
+ memset (&desc, 0, sizeof desc);
+ desc.mode = KEYDB_SEARCH_MODE_ISSUER;
+ desc.u.name = issuer;
+ rc = keydb_search (hd, &desc, 1);
+ return rc;
+}
+
+int
+keydb_search_issuer_sn (KEYDB_HANDLE hd,
+ const char *issuer, ksba_const_sexp_t serial)
+{
+ KEYDB_SEARCH_DESC desc;
+ int rc;
+ const unsigned char *s;
+
+ memset (&desc, 0, sizeof desc);
+ desc.mode = KEYDB_SEARCH_MODE_ISSUER_SN;
+ s = serial;
+ if (*s !='(')
+ return gpg_error (GPG_ERR_INV_VALUE);
+ s++;
+ for (desc.snlen = 0; digitp (s); s++)
+ desc.snlen = 10*desc.snlen + atoi_1 (s);
+ if (*s !=':')
+ return gpg_error (GPG_ERR_INV_VALUE);
+ desc.sn = s+1;
+ desc.u.name = issuer;
+ rc = keydb_search (hd, &desc, 1);
+ return rc;
+}
+
+int
+keydb_search_subject (KEYDB_HANDLE hd, const char *name)
+{
+ KEYDB_SEARCH_DESC desc;
+ int rc;
+
+ memset (&desc, 0, sizeof desc);
+ desc.mode = KEYDB_SEARCH_MODE_SUBJECT;
+ desc.u.name = name;
+ rc = keydb_search (hd, &desc, 1);
+ return rc;
+}
+
+
+static int
+classify_user_id (const char *name,
+ KEYDB_SEARCH_DESC *desc,
+ int *force_exact )
+{
+ 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 about trailing white space? */
+ for(s = name; *s && spacep (s); s++ )
+ ;
+
+ switch (*s)
+ {
+ case 0: /* empty string is an error */
+ return 0;
+
+ case '.': /* an email address, compare from end */
+ mode = KEYDB_SEARCH_MODE_MAILEND;
+ s++;
+ desc->u.name = s;
+ break;
+
+ case '<': /* an email address */
+ mode = KEYDB_SEARCH_MODE_MAIL;
+ s++;
+ desc->u.name = s;
+ break;
+
+ case '@': /* part of an email address */
+ mode = KEYDB_SEARCH_MODE_MAILSUB;
+ s++;
+ desc->u.name = s;
+ break;
+
+ case '=': /* exact compare */
+ mode = KEYDB_SEARCH_MODE_EXACT;
+ s++;
+ desc->u.name = s;
+ break;
+
+ case '*': /* case insensitive substring search */
+ mode = KEYDB_SEARCH_MODE_SUBSTR;
+ s++;
+ desc->u.name = s;
+ break;
+
+ case '+': /* compare individual words */
+ mode = KEYDB_SEARCH_MODE_WORDS;
+ s++;
+ desc->u.name = s;
+ break;
+
+ case '/': /* subject's DN */
+ s++;
+ if (!*s || spacep (s))
+ return 0; /* no DN or prefixed with a space */
+ desc->u.name = s;
+ mode = KEYDB_SEARCH_MODE_SUBJECT;
+ break;
+
+ case '#':
+ {
+ const char *si;
+
+ s++;
+ if ( *s == '/')
+ { /* "#/" indicates an issuer's DN */
+ s++;
+ if (!*s || spacep (s))
+ return 0; /* no DN or prefixed with a space */
+ desc->u.name = s;
+ mode = KEYDB_SEARCH_MODE_ISSUER;
+ }
+ else
+ { /* serialnumber + optional issuer ID */
+ for (si=s; *si && *si != '/'; si++)
+ {
+ if (!strchr("01234567890abcdefABCDEF", *si))
+ return 0; /* invalid digit in serial number*/
+ }
+ desc->sn = (const unsigned char*)s;
+ desc->snlen = -1;
+ if (!*si)
+ mode = KEYDB_SEARCH_MODE_SN;
+ else
+ {
+ s = si+1;
+ if (!*s || spacep (s))
+ return 0; /* no DN or prefixed with a space */
+ desc->u.name = s;
+ mode = KEYDB_SEARCH_MODE_ISSUER_SN;
+ }
+ }
+ }
+ break;
+
+ case ':': /*Unified fingerprint */
+ {
+ const char *se, *si;
+ int i;
+
+ se = strchr (++s,':');
+ if (!se)
+ return 0;
+ for (i=0,si=s; si < se; si++, i++ )
+ {
+ if (!strchr("01234567890abcdefABCDEF", *si))
+ return 0; /* invalid digit */
+ }
+ if (i != 32 && i != 40)
+ return 0; /* invalid length of fpr*/
+ for (i=0,si=s; si < se; i++, si +=2)
+ desc->u.fpr[i] = hextobyte(si);
+ for (; i < 20; i++)
+ desc->u.fpr[i]= 0;
+ s = se + 1;
+ mode = KEYDB_SEARCH_MODE_FPR;
+ }
+ break;
+
+ case '&': /* Keygrip*/
+ {
+ if (hex2bin (s+1, desc->u.grip, 20) < 0)
+ return 0; /* Invalid. */
+ mode = KEYDB_SEARCH_MODE_KEYGRIP;
+ }
+ break;
+
+ default:
+ if (s[0] == '0' && s[1] == 'x')
+ {
+ hexprefix = 1;
+ s += 2;
+ }
+
+ 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] && !spacep (s+hexlength))
+ {
+ if (hexprefix) /* a "0x" prefix without correct */
+ return 0; /* termination is an error */
+ /* The first chars looked like a hex number, but really is
+ not */
+ hexlength = 0;
+ }
+
+ if (*force_exact)
+ hexlength--; /* remove the bang */
+
+ if (hexlength == 8
+ || (!hexprefix && hexlength == 9 && *s == '0'))
+ { /* short keyid */
+ unsigned long kid;
+ if (hexlength == 9)
+ s++;
+ kid = strtoul( s, NULL, 16 );
+ desc->u.kid[4] = kid >> 24;
+ desc->u.kid[5] = kid >> 16;
+ desc->u.kid[6] = kid >> 8;
+ desc->u.kid[7] = kid;
+ mode = KEYDB_SEARCH_MODE_SHORT_KID;
+ }
+ else if (hexlength == 16
+ || (!hexprefix && hexlength == 17 && *s == '0'))
+ { /* complete keyid */
+ unsigned long kid0, kid1;
+ char buf[9];
+ if (hexlength == 17)
+ s++;
+ mem2str(buf, s, 9 );
+ kid0 = strtoul (buf, NULL, 16);
+ kid1 = strtoul (s+8, NULL, 16);
+ desc->u.kid[0] = kid0 >> 24;
+ desc->u.kid[1] = kid0 >> 16;
+ desc->u.kid[2] = kid0 >> 8;
+ desc->u.kid[3] = kid0;
+ desc->u.kid[4] = kid1 >> 24;
+ desc->u.kid[5] = kid1 >> 16;
+ desc->u.kid[6] = kid1 >> 8;
+ desc->u.kid[7] = kid1;
+ mode = KEYDB_SEARCH_MODE_LONG_KID;
+ }
+ else if (hexlength == 32
+ || (!hexprefix && hexlength == 33 && *s == '0'))
+ { /* md5 fingerprint */
+ int i;
+ if (hexlength == 33)
+ s++;
+ memset(desc->u.fpr+16, 0, 4);
+ for (i=0; i < 16; i++, s+=2)
+ {
+ int c = hextobyte(s);
+ if (c == -1)
+ return 0;
+ desc->u.fpr[i] = c;
+ }
+ mode = KEYDB_SEARCH_MODE_FPR16;
+ }
+ else if (hexlength == 40
+ || (!hexprefix && hexlength == 41 && *s == '0'))
+ { /* sha1/rmd160 fingerprint */
+ int i;
+ if (hexlength == 41)
+ s++;
+ for (i=0; i < 20; i++, s+=2)
+ {
+ int c = hextobyte(s);
+ if (c == -1)
+ return 0;
+ desc->u.fpr[i] = c;
+ }
+ mode = KEYDB_SEARCH_MODE_FPR20;
+ }
+ else if (!hexprefix)
+ {
+ /* The fingerprint in an X.509 listing is often delimited by
+ colons, so we try to single this case out. */
+ mode = 0;
+ hexlength = strspn (s, ":0123456789abcdefABCDEF");
+ if (hexlength == 59 && (!s[hexlength] || spacep (s+hexlength)))
+ {
+ int i;
+
+ for (i=0; i < 20; i++, s += 3)
+ {
+ int c = hextobyte(s);
+ if (c == -1 || (i < 19 && s[2] != ':'))
+ break;
+ desc->u.fpr[i] = c;
+ }
+ if (i == 20)
+ mode = KEYDB_SEARCH_MODE_FPR20;
+ }
+ if (!mode) /* default is substring search */
+ {
+ *force_exact = 0;
+ desc->u.name = s;
+ mode = KEYDB_SEARCH_MODE_SUBSTR;
+ }
+ }
+ else
+ { /* hex number with a prefix but a wrong length */
+ return 0;
+ }
+ }
+
+ desc->mode = mode;
+ return mode;
+}
+
+
+int
+keydb_classify_name (const char *name, KEYDB_SEARCH_DESC *desc)
+{
+ int dummy;
+ KEYDB_SEARCH_DESC dummy_desc;
+
+ if (!desc)
+ desc = &dummy_desc;
+
+ if (!classify_user_id (name, desc, &dummy))
+ return gpg_error (GPG_ERR_INV_NAME);
+ return 0;
+}
+
+
+/* Store the certificate in the key DB but make sure that it does not
+ already exists. We do this simply by comparing the fingerprint.
+ If EXISTED is not NULL it will be set to true if the certificate
+ was already in the DB. */
+int
+keydb_store_cert (ksba_cert_t cert, int ephemeral, int *existed)
+{
+ KEYDB_HANDLE kh;
+ int rc;
+ unsigned char fpr[20];
+
+ if (existed)
+ *existed = 0;
+
+ if (!gpgsm_get_fingerprint (cert, 0, fpr, NULL))
+ {
+ log_error (_("failed to get the fingerprint\n"));
+ return gpg_error (GPG_ERR_GENERAL);
+ }
+
+ kh = keydb_new (0);
+ if (!kh)
+ {
+ log_error (_("failed to allocate keyDB handle\n"));
+ return gpg_error (GPG_ERR_ENOMEM);;
+ }
+
+ if (ephemeral)
+ keydb_set_ephemeral (kh, 1);
+
+ rc = lock_all (kh);
+ if (rc)
+ return rc;
+
+ rc = keydb_search_fpr (kh, fpr);
+ if (rc != -1)
+ {
+ keydb_release (kh);
+ if (!rc)
+ {
+ if (existed)
+ *existed = 1;
+ return 0; /* okay */
+ }
+ log_error (_("problem looking for existing certificate: %s\n"),
+ gpg_strerror (rc));
+ return rc;
+ }
+
+ rc = keydb_locate_writable (kh, 0);
+ if (rc)
+ {
+ log_error (_("error finding writable keyDB: %s\n"), gpg_strerror (rc));
+ keydb_release (kh);
+ return rc;
+ }
+
+ rc = keydb_insert_cert (kh, cert);
+ if (rc)
+ {
+ log_error (_("error storing certificate: %s\n"), gpg_strerror (rc));
+ keydb_release (kh);
+ return rc;
+ }
+ keydb_release (kh);
+ return 0;
+}
+
+
+/* This is basically keydb_set_flags but it implements a complete
+ transaction by locating the certificate in the DB and updating the
+ flags. */
+gpg_error_t
+keydb_set_cert_flags (ksba_cert_t cert, int ephemeral,
+ int which, int idx,
+ unsigned int mask, unsigned int value)
+{
+ KEYDB_HANDLE kh;
+ gpg_error_t err;
+ unsigned char fpr[20];
+ unsigned int old_value;
+
+ if (!gpgsm_get_fingerprint (cert, 0, fpr, NULL))
+ {
+ log_error (_("failed to get the fingerprint\n"));
+ return gpg_error (GPG_ERR_GENERAL);
+ }
+
+ kh = keydb_new (0);
+ if (!kh)
+ {
+ log_error (_("failed to allocate keyDB handle\n"));
+ return gpg_error (GPG_ERR_ENOMEM);;
+ }
+
+ if (ephemeral)
+ keydb_set_ephemeral (kh, 1);
+
+ err = keydb_lock (kh);
+ if (err)
+ {
+ log_error (_("error locking keybox: %s\n"), gpg_strerror (err));
+ keydb_release (kh);
+ return err;
+ }
+
+ err = keydb_search_fpr (kh, fpr);
+ if (err)
+ {
+ if (err == -1)
+ err = gpg_error (GPG_ERR_NOT_FOUND);
+ else
+ log_error (_("problem re-searching certificate: %s\n"),
+ gpg_strerror (err));
+ keydb_release (kh);
+ return err;
+ }
+
+ err = keydb_get_flags (kh, which, idx, &old_value);
+ if (err)
+ {
+ log_error (_("error getting stored flags: %s\n"), gpg_strerror (err));
+ keydb_release (kh);
+ return err;
+ }
+
+ value = ((old_value & ~mask) | (value & mask));
+
+ if (value != old_value)
+ {
+ err = keydb_set_flags (kh, which, idx, value);
+ if (err)
+ {
+ log_error (_("error storing flags: %s\n"), gpg_strerror (err));
+ keydb_release (kh);
+ return err;
+ }
+ }
+
+ keydb_release (kh);
+ return 0;
+}
+
+
+/* Reset all the certificate flags we have stored with the certificates
+ for performance reasons. */
+void
+keydb_clear_some_cert_flags (ctrl_t ctrl, strlist_t names)
+{
+ gpg_error_t err;
+ KEYDB_HANDLE hd = NULL;
+ KEYDB_SEARCH_DESC *desc = NULL;
+ int ndesc;
+ strlist_t sl;
+ int rc=0;
+ unsigned int old_value, value;
+
+ (void)ctrl;
+
+ hd = keydb_new (0);
+ if (!hd)
+ {
+ log_error ("keydb_new failed\n");
+ goto leave;
+ }
+
+ if (!names)
+ ndesc = 1;
+ else
+ {
+ for (sl=names, ndesc=0; sl; sl = sl->next, ndesc++)
+ ;
+ }
+
+ desc = xtrycalloc (ndesc, sizeof *desc);
+ if (!ndesc)
+ {
+ log_error ("allocating memory failed: %s\n",
+ gpg_strerror (out_of_core ()));
+ goto leave;
+ }
+
+ if (!names)
+ desc[0].mode = KEYDB_SEARCH_MODE_FIRST;
+ else
+ {
+ for (ndesc=0, sl=names; sl; sl = sl->next)
+ {
+ rc = keydb_classify_name (sl->d, desc+ndesc);
+ if (rc)
+ {
+ log_error ("key `%s' not found: %s\n",
+ sl->d, gpg_strerror (rc));
+ rc = 0;
+ }
+ else
+ ndesc++;
+ }
+ }
+
+ err = keydb_lock (hd);
+ if (err)
+ {
+ log_error (_("error locking keybox: %s\n"), gpg_strerror (err));
+ goto leave;
+ }
+
+ while (!(rc = keydb_search (hd, desc, ndesc)))
+ {
+ if (!names)
+ desc[0].mode = KEYDB_SEARCH_MODE_NEXT;
+
+ err = keydb_get_flags (hd, KEYBOX_FLAG_VALIDITY, 0, &old_value);
+ if (err)
+ {
+ log_error (_("error getting stored flags: %s\n"),
+ gpg_strerror (err));
+ goto leave;
+ }
+
+ value = (old_value & ~VALIDITY_REVOKED);
+ if (value != old_value)
+ {
+ err = keydb_set_flags (hd, KEYBOX_FLAG_VALIDITY, 0, value);
+ if (err)
+ {
+ log_error (_("error storing flags: %s\n"), gpg_strerror (err));
+ goto leave;
+ }
+ }
+ }
+ if (rc && rc != -1)
+ log_error ("keydb_search failed: %s\n", gpg_strerror (rc));
+
+ leave:
+ xfree (desc);
+ keydb_release (hd);
+}
+
+
diff --git a/sm/keydb.h b/sm/keydb.h
new file mode 100644
index 0000000..a440c50
--- /dev/null
+++ b/sm/keydb.h
@@ -0,0 +1,86 @@
+/* keydb.h - Key database
+ * Copyright (C) 1998, 1999, 2000, 2001 Free Software Foundation, Inc.
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuPG is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef GNUPG_KEYDB_H
+#define GNUPG_KEYDB_H
+
+#include <ksba.h>
+
+#include "../kbx/keybox-search-desc.h"
+
+typedef struct keydb_handle *KEYDB_HANDLE;
+
+/* Flag value used with KEYBOX_FLAG_VALIDITY. */
+#define VALIDITY_REVOKED (1<<5)
+
+
+/*-- keydb.c --*/
+int keydb_add_resource (const char *url, int force, int secret,
+ int *auto_created);
+KEYDB_HANDLE keydb_new (int secret);
+void keydb_release (KEYDB_HANDLE hd);
+int keydb_set_ephemeral (KEYDB_HANDLE hd, int yes);
+const char *keydb_get_resource_name (KEYDB_HANDLE hd);
+gpg_error_t keydb_lock (KEYDB_HANDLE hd);
+
+#if 0 /* pgp stuff */
+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);
+#endif
+
+gpg_error_t keydb_get_flags (KEYDB_HANDLE hd, int which, int idx,
+ unsigned int *value);
+gpg_error_t keydb_set_flags (KEYDB_HANDLE hd, int which, int idx,
+ unsigned int value);
+int keydb_get_cert (KEYDB_HANDLE hd, ksba_cert_t *r_cert);
+int keydb_insert_cert (KEYDB_HANDLE hd, ksba_cert_t cert);
+int keydb_update_cert (KEYDB_HANDLE hd, ksba_cert_t cert);
+
+int keydb_delete (KEYDB_HANDLE hd, int unlock);
+
+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);
+int keydb_search_issuer (KEYDB_HANDLE hd, const char *issuer);
+int keydb_search_issuer_sn (KEYDB_HANDLE hd,
+ const char *issuer, const unsigned char *serial);
+int keydb_search_subject (KEYDB_HANDLE hd, const char *issuer);
+
+int keydb_classify_name (const char *name, KEYDB_SEARCH_DESC *desc);
+
+int keydb_store_cert (ksba_cert_t cert, int ephemeral, int *existed);
+gpg_error_t keydb_set_cert_flags (ksba_cert_t cert, int ephemeral,
+ int which, int idx,
+ unsigned int mask, unsigned int value);
+
+void keydb_clear_some_cert_flags (ctrl_t ctrl, strlist_t names);
+
+
+#endif /*GNUPG_KEYDB_H*/
+
+
+
+
diff --git a/sm/keylist.c b/sm/keylist.c
new file mode 100644
index 0000000..9b8538c
--- /dev/null
+++ b/sm/keylist.c
@@ -0,0 +1,1562 @@
+/* keylist.c - Print certificates in various formats.
+ * Copyright (C) 1998, 1999, 2000, 2001, 2003,
+ * 2004, 2005, 2008, 2009 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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuPG is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <time.h>
+#include <assert.h>
+
+#include "gpgsm.h"
+
+#include <gcrypt.h>
+#include <ksba.h>
+
+#include "keydb.h"
+#include "../kbx/keybox.h" /* for KEYBOX_FLAG_* */
+#include "i18n.h"
+#include "tlv.h"
+
+struct list_external_parm_s
+{
+ ctrl_t ctrl;
+ estream_t fp;
+ int print_header;
+ int with_colons;
+ int with_chain;
+ int raw_mode;
+};
+
+
+/* This table is to map Extended Key Usage OIDs to human readable
+ names. */
+struct
+{
+ const char *oid;
+ const char *name;
+} key_purpose_map[] = {
+ { "1.3.6.1.5.5.7.3.1", "serverAuth" },
+ { "1.3.6.1.5.5.7.3.2", "clientAuth" },
+ { "1.3.6.1.5.5.7.3.3", "codeSigning" },
+ { "1.3.6.1.5.5.7.3.4", "emailProtection" },
+ { "1.3.6.1.5.5.7.3.5", "ipsecEndSystem" },
+ { "1.3.6.1.5.5.7.3.6", "ipsecTunnel" },
+ { "1.3.6.1.5.5.7.3.7", "ipsecUser" },
+ { "1.3.6.1.5.5.7.3.8", "timeStamping" },
+ { "1.3.6.1.5.5.7.3.9", "ocspSigning" },
+ { "1.3.6.1.5.5.7.3.10", "dvcs" },
+ { "1.3.6.1.5.5.7.3.11", "sbgpCertAAServerAuth" },
+ { "1.3.6.1.5.5.7.3.13", "eapOverPPP" },
+ { "1.3.6.1.5.5.7.3.14", "wlanSSID" },
+
+ { "2.16.840.1.113730.4.1", "serverGatedCrypto.ns" }, /* Netscape. */
+ { "1.3.6.1.4.1.311.10.3.3", "serverGatedCrypto.ms"}, /* Microsoft. */
+
+ { "1.3.6.1.5.5.7.48.1.5", "ocspNoCheck" },
+
+ { NULL, NULL }
+};
+
+
+/* Do not print this extension in the list of extensions. This is set
+ for oids which are already available via ksba fucntions. */
+#define OID_FLAG_SKIP 1
+/* The extension is a simple UTF8String and should be printed. */
+#define OID_FLAG_UTF8 2
+
+/* A table mapping OIDs to a descriptive string. */
+static struct
+{
+ char *oid;
+ char *name;
+ unsigned int flag; /* A flag as described above. */
+} oidtranstbl[] = {
+
+ /* Algorithms. */
+ { "1.2.840.10040.4.1", "dsa" },
+ { "1.2.840.10040.4.3", "dsaWithSha1" },
+
+ { "1.2.840.113549.1.1.1", "rsaEncryption" },
+ { "1.2.840.113549.1.1.2", "md2WithRSAEncryption" },
+ { "1.2.840.113549.1.1.3", "md4WithRSAEncryption" },
+ { "1.2.840.113549.1.1.4", "md5WithRSAEncryption" },
+ { "1.2.840.113549.1.1.5", "sha1WithRSAEncryption" },
+ { "1.2.840.113549.1.1.7", "rsaOAEP" },
+ { "1.2.840.113549.1.1.8", "rsaOAEP-MGF" },
+ { "1.2.840.113549.1.1.9", "rsaOAEP-pSpecified" },
+ { "1.2.840.113549.1.1.10", "rsaPSS" },
+ { "1.2.840.113549.1.1.11", "sha256WithRSAEncryption" },
+ { "1.2.840.113549.1.1.12", "sha384WithRSAEncryption" },
+ { "1.2.840.113549.1.1.13", "sha512WithRSAEncryption" },
+
+ { "1.3.14.3.2.26", "sha1" },
+ { "1.3.14.3.2.29", "sha-1WithRSAEncryption" },
+ { "1.3.36.3.3.1.2", "rsaSignatureWithripemd160" },
+
+
+ /* Telesec extensions. */
+ { "0.2.262.1.10.12.0", "certExtensionLiabilityLimitationExt" },
+ { "0.2.262.1.10.12.1", "telesecCertIdExt" },
+ { "0.2.262.1.10.12.2", "telesecPolicyIdentifier" },
+ { "0.2.262.1.10.12.3", "telesecPolicyQualifierID" },
+ { "0.2.262.1.10.12.4", "telesecCRLFilteredExt" },
+ { "0.2.262.1.10.12.5", "telesecCRLFilterExt"},
+ { "0.2.262.1.10.12.6", "telesecNamingAuthorityExt" },
+#define OIDSTR_restriction \
+ "1.3.36.8.3.8"
+ { OIDSTR_restriction, "restriction", OID_FLAG_UTF8 },
+
+
+ /* PKIX private extensions. */
+ { "1.3.6.1.5.5.7.1.1", "authorityInfoAccess" },
+ { "1.3.6.1.5.5.7.1.2", "biometricInfo" },
+ { "1.3.6.1.5.5.7.1.3", "qcStatements" },
+ { "1.3.6.1.5.5.7.1.4", "acAuditIdentity" },
+ { "1.3.6.1.5.5.7.1.5", "acTargeting" },
+ { "1.3.6.1.5.5.7.1.6", "acAaControls" },
+ { "1.3.6.1.5.5.7.1.7", "sbgp-ipAddrBlock" },
+ { "1.3.6.1.5.5.7.1.8", "sbgp-autonomousSysNum" },
+ { "1.3.6.1.5.5.7.1.9", "sbgp-routerIdentifier" },
+ { "1.3.6.1.5.5.7.1.10", "acProxying" },
+ { "1.3.6.1.5.5.7.1.11", "subjectInfoAccess" },
+
+ { "1.3.6.1.5.5.7.48.1", "ocsp" },
+ { "1.3.6.1.5.5.7.48.2", "caIssuers" },
+ { "1.3.6.1.5.5.7.48.3", "timeStamping" },
+ { "1.3.6.1.5.5.7.48.5", "caRepository" },
+
+ /* X.509 id-ce */
+ { "2.5.29.14", "subjectKeyIdentifier", OID_FLAG_SKIP},
+ { "2.5.29.15", "keyUsage", OID_FLAG_SKIP},
+ { "2.5.29.16", "privateKeyUsagePeriod" },
+ { "2.5.29.17", "subjectAltName", OID_FLAG_SKIP},
+ { "2.5.29.18", "issuerAltName", OID_FLAG_SKIP},
+ { "2.5.29.19", "basicConstraints", OID_FLAG_SKIP},
+ { "2.5.29.20", "cRLNumber" },
+ { "2.5.29.21", "cRLReason" },
+ { "2.5.29.22", "expirationDate" },
+ { "2.5.29.23", "instructionCode" },
+ { "2.5.29.24", "invalidityDate" },
+ { "2.5.29.27", "deltaCRLIndicator" },
+ { "2.5.29.28", "issuingDistributionPoint" },
+ { "2.5.29.29", "certificateIssuer" },
+ { "2.5.29.30", "nameConstraints" },
+ { "2.5.29.31", "cRLDistributionPoints", OID_FLAG_SKIP},
+ { "2.5.29.32", "certificatePolicies", OID_FLAG_SKIP},
+ { "2.5.29.32.0", "anyPolicy" },
+ { "2.5.29.33", "policyMappings" },
+ { "2.5.29.35", "authorityKeyIdentifier", OID_FLAG_SKIP},
+ { "2.5.29.36", "policyConstraints" },
+ { "2.5.29.37", "extKeyUsage", OID_FLAG_SKIP},
+ { "2.5.29.46", "freshestCRL" },
+ { "2.5.29.54", "inhibitAnyPolicy" },
+
+ /* Netscape certificate extensions. */
+ { "2.16.840.1.113730.1.1", "netscape-cert-type" },
+ { "2.16.840.1.113730.1.2", "netscape-base-url" },
+ { "2.16.840.1.113730.1.3", "netscape-revocation-url" },
+ { "2.16.840.1.113730.1.4", "netscape-ca-revocation-url" },
+ { "2.16.840.1.113730.1.7", "netscape-cert-renewal-url" },
+ { "2.16.840.1.113730.1.8", "netscape-ca-policy-url" },
+ { "2.16.840.1.113730.1.9", "netscape-homePage-url" },
+ { "2.16.840.1.113730.1.10", "netscape-entitylogo" },
+ { "2.16.840.1.113730.1.11", "netscape-userPicture" },
+ { "2.16.840.1.113730.1.12", "netscape-ssl-server-name" },
+ { "2.16.840.1.113730.1.13", "netscape-comment" },
+
+ /* GnuPG extensions */
+ { "1.3.6.1.4.1.11591.2.1.1", "pkaAddress" },
+
+ /* Extensions used by the Bundesnetzagentur. */
+ { "1.3.6.1.4.1.8301.3.5", "validityModel" },
+
+ { NULL }
+};
+
+
+/* Return the description for OID; if no description is available
+ NULL is returned. */
+static const char *
+get_oid_desc (const char *oid, unsigned int *flag)
+{
+ int i;
+
+ if (oid)
+ for (i=0; oidtranstbl[i].oid; i++)
+ if (!strcmp (oidtranstbl[i].oid, oid))
+ {
+ if (flag)
+ *flag = oidtranstbl[i].flag;
+ return oidtranstbl[i].name;
+ }
+ if (flag)
+ *flag = 0;
+ return NULL;
+}
+
+
+static void
+print_key_data (ksba_cert_t cert, estream_t fp)
+{
+#if 0
+ int n = pk ? pubkey_get_npkey( pk->pubkey_algo ) : 0;
+ int i;
+
+ for(i=0; i < n; i++ )
+ {
+ es_fprintf (fp, "pkd:%d:%u:", i, mpi_get_nbits( pk->pkey[i] ) );
+ mpi_print(stdout, pk->pkey[i], 1 );
+ putchar(':');
+ putchar('\n');
+ }
+#else
+ (void)cert;
+ (void)fp;
+#endif
+}
+
+static void
+print_capabilities (ksba_cert_t cert, estream_t fp)
+{
+ gpg_error_t err;
+ unsigned int use;
+ size_t buflen;
+ char buffer[1];
+
+ err = ksba_cert_get_user_data (cert, "is_qualified",
+ &buffer, sizeof (buffer), &buflen);
+ if (!err && buflen)
+ {
+ if (*buffer)
+ es_putc ('q', fp);
+ }
+ else if (gpg_err_code (err) == GPG_ERR_NOT_FOUND)
+ ; /* Don't know - will not get marked as 'q' */
+ else
+ log_debug ("get_user_data(is_qualified) failed: %s\n",
+ gpg_strerror (err));
+
+ err = ksba_cert_get_key_usage (cert, &use);
+ if (gpg_err_code (err) == GPG_ERR_NO_DATA)
+ {
+ es_putc ('e', fp);
+ es_putc ('s', fp);
+ es_putc ('c', fp);
+ es_putc ('E', fp);
+ es_putc ('S', fp);
+ es_putc ('C', fp);
+ return;
+ }
+ if (err)
+ {
+ log_error (_("error getting key usage information: %s\n"),
+ gpg_strerror (err));
+ return;
+ }
+
+ if ((use & (KSBA_KEYUSAGE_KEY_ENCIPHERMENT|KSBA_KEYUSAGE_DATA_ENCIPHERMENT)))
+ es_putc ('e', fp);
+ if ((use & (KSBA_KEYUSAGE_DIGITAL_SIGNATURE|KSBA_KEYUSAGE_NON_REPUDIATION)))
+ es_putc ('s', fp);
+ if ((use & KSBA_KEYUSAGE_KEY_CERT_SIGN))
+ es_putc ('c', fp);
+ if ((use & (KSBA_KEYUSAGE_KEY_ENCIPHERMENT|KSBA_KEYUSAGE_DATA_ENCIPHERMENT)))
+ es_putc ('E', fp);
+ if ((use & (KSBA_KEYUSAGE_DIGITAL_SIGNATURE|KSBA_KEYUSAGE_NON_REPUDIATION)))
+ es_putc ('S', fp);
+ if ((use & KSBA_KEYUSAGE_KEY_CERT_SIGN))
+ es_putc ('C', fp);
+
+ es_putc (':', fp);
+}
+
+
+static void
+print_time (gnupg_isotime_t t, estream_t fp)
+{
+ if (!t || !*t)
+ ;
+ else
+ es_fputs (t, fp);
+}
+
+
+/* Return an allocated string with the email address extracted from a
+ DN. Note hat we use this code also in ../kbx/keybox-blob.c. */
+static char *
+email_kludge (const char *name)
+{
+ const char *p, *string;
+ unsigned char *buf;
+ int n;
+
+ string = name;
+ for (;;)
+ {
+ p = strstr (string, "1.2.840.113549.1.9.1=#");
+ if (!p)
+ return NULL;
+ if (p == name || (p > string+1 && p[-1] == ',' && p[-2] != '\\'))
+ {
+ name = p + 22;
+ break;
+ }
+ string = p + 22;
+ }
+
+
+ /* This looks pretty much like an email address in the subject's DN
+ we use this to add an additional user ID entry. This way,
+ OpenSSL generated keys get a nicer and usable listing. */
+ for (n=0, p=name; hexdigitp (p) && hexdigitp (p+1); p +=2, n++)
+ ;
+ if (!n)
+ return NULL;
+ buf = xtrymalloc (n+3);
+ if (!buf)
+ return NULL; /* oops, out of core */
+ *buf = '<';
+ for (n=1, p=name; hexdigitp (p); p +=2, n++)
+ buf[n] = xtoi_2 (p);
+ buf[n++] = '>';
+ buf[n] = 0;
+ return (char*)buf;
+}
+
+
+
+
+/* List one certificate in colon mode */
+static void
+list_cert_colon (ctrl_t ctrl, ksba_cert_t cert, unsigned int validity,
+ estream_t fp, int have_secret)
+{
+ int rc;
+ int idx;
+ char truststring[2];
+ char *p;
+ ksba_sexp_t sexp;
+ char *fpr;
+ ksba_isotime_t t;
+ gpg_error_t valerr;
+ int algo;
+ unsigned int nbits;
+ const char *chain_id;
+ char *chain_id_buffer = NULL;
+ int is_root = 0;
+ char *kludge_uid;
+
+ if (ctrl->with_validation)
+ valerr = gpgsm_validate_chain (ctrl, cert, "", NULL, 1, NULL, 0, NULL);
+ else
+ valerr = 0;
+
+
+ /* We need to get the fingerprint and the chaining ID in advance. */
+ fpr = gpgsm_get_fingerprint_hexstring (cert, GCRY_MD_SHA1);
+ {
+ ksba_cert_t next;
+
+ rc = gpgsm_walk_cert_chain (ctrl, cert, &next);
+ if (!rc) /* We known the issuer's certificate. */
+ {
+ p = gpgsm_get_fingerprint_hexstring (next, GCRY_MD_SHA1);
+ chain_id_buffer = p;
+ chain_id = chain_id_buffer;
+ ksba_cert_release (next);
+ }
+ else if (rc == -1) /* We have reached the root certificate. */
+ {
+ chain_id = fpr;
+ is_root = 1;
+ }
+ else
+ chain_id = NULL;
+ }
+
+
+ es_fputs (have_secret? "crs:":"crt:", fp);
+
+ /* Note: We can't use multiple flags, like "ei", because the
+ validation check does only return one error. */
+ truststring[0] = 0;
+ truststring[1] = 0;
+ if ((validity & VALIDITY_REVOKED)
+ || gpg_err_code (valerr) == GPG_ERR_CERT_REVOKED)
+ *truststring = 'r';
+ else if (gpg_err_code (valerr) == GPG_ERR_CERT_EXPIRED)
+ *truststring = 'e';
+ else
+ {
+ /* Lets also check whether the certificate under question
+ expired. This is merely a hack until we found a proper way
+ to store the expiration flag in the keybox. */
+ ksba_isotime_t current_time, not_after;
+
+ gnupg_get_isotime (current_time);
+ if (!opt.ignore_expiration
+ && !ksba_cert_get_validity (cert, 1, not_after)
+ && *not_after && strcmp (current_time, not_after) > 0 )
+ *truststring = 'e';
+ else if (valerr)
+ *truststring = 'i';
+ else if (ctrl->with_validation && !is_root)
+ *truststring = 'f';
+ }
+
+ /* If we have no truststring yet (i.e. the certificate might be
+ good) and this is a root certificate, we ask the agent whether
+ this is a trusted root certificate. */
+ if (!*truststring && is_root)
+ {
+ struct rootca_flags_s dummy_flags;
+
+ rc = gpgsm_agent_istrusted (ctrl, cert, NULL, &dummy_flags);
+ if (!rc)
+ *truststring = 'u'; /* Yes, we trust this one (ultimately). */
+ else if (gpg_err_code (rc) == GPG_ERR_NOT_TRUSTED)
+ *truststring = 'n'; /* No, we do not trust this one. */
+ /* (in case of an error we can't tell anything.) */
+ }
+
+ if (*truststring)
+ es_fputs (truststring, fp);
+
+ algo = gpgsm_get_key_algo_info (cert, &nbits);
+ es_fprintf (fp, ":%u:%d:%s:", nbits, algo, fpr+24);
+
+ /* We assume --fixed-list-mode for gpgsm */
+ ksba_cert_get_validity (cert, 0, t);
+ print_time (t, fp);
+ es_putc (':', fp);
+ ksba_cert_get_validity (cert, 1, t);
+ print_time ( t, fp);
+ es_putc (':', fp);
+ /* Field 8, serial number: */
+ if ((sexp = ksba_cert_get_serial (cert)))
+ {
+ int len;
+ const unsigned char *s = sexp;
+
+ if (*s == '(')
+ {
+ s++;
+ for (len=0; *s && *s != ':' && digitp (s); s++)
+ len = len*10 + atoi_1 (s);
+ if (*s == ':')
+ for (s++; len; len--, s++)
+ es_fprintf (fp,"%02X", *s);
+ }
+ xfree (sexp);
+ }
+ es_putc (':', fp);
+ /* Field 9, ownertrust - not used here */
+ es_putc (':', fp);
+ /* field 10, old user ID - we use it here for the issuer DN */
+ if ((p = ksba_cert_get_issuer (cert,0)))
+ {
+ es_write_sanitized (fp, p, strlen (p), ":", NULL);
+ xfree (p);
+ }
+ es_putc (':', fp);
+ /* Field 11, signature class - not used */
+ es_putc (':', fp);
+ /* Field 12, capabilities: */
+ print_capabilities (cert, fp);
+ /* Field 13, not used: */
+ es_putc (':', fp);
+ if (have_secret)
+ {
+ char *cardsn;
+
+ p = gpgsm_get_keygrip_hexstring (cert);
+ if (!gpgsm_agent_keyinfo (ctrl, p, &cardsn) && cardsn)
+ {
+ /* Field 14, not used: */
+ es_putc (':', fp);
+ /* Field 15: Token serial number. */
+ es_fputs (cardsn, fp);
+ es_putc (':', fp);
+ }
+ xfree (cardsn);
+ xfree (p);
+ }
+ es_putc ('\n', fp);
+
+ /* FPR record */
+ es_fprintf (fp, "fpr:::::::::%s:::", fpr);
+ /* Print chaining ID (field 13)*/
+ if (chain_id)
+ es_fputs (chain_id, fp);
+ es_putc (':', fp);
+ es_putc ('\n', fp);
+ xfree (fpr); fpr = NULL; chain_id = NULL;
+ xfree (chain_id_buffer); chain_id_buffer = NULL;
+
+ if (opt.with_key_data)
+ {
+ if ( (p = gpgsm_get_keygrip_hexstring (cert)))
+ {
+ es_fprintf (fp, "grp:::::::::%s:\n", p);
+ xfree (p);
+ }
+ print_key_data (cert, fp);
+ }
+
+ kludge_uid = NULL;
+ for (idx=0; (p = ksba_cert_get_subject (cert,idx)); idx++)
+ {
+ /* In the case that the same email address is in the subject DN
+ as well as in an alternate subject name we avoid printing it
+ a second time. */
+ if (kludge_uid && !strcmp (kludge_uid, p))
+ continue;
+
+ es_fprintf (fp, "uid:%s::::::::", truststring);
+ es_write_sanitized (fp, p, strlen (p), ":", NULL);
+ es_putc (':', fp);
+ es_putc (':', fp);
+ es_putc ('\n', fp);
+ if (!idx)
+ {
+ /* It would be better to get the faked email address from
+ the keydb. But as long as we don't have a way to pass
+ the meta data back, we just check it the same way as the
+ code used to create the keybox meta data does */
+ kludge_uid = email_kludge (p);
+ if (kludge_uid)
+ {
+ es_fprintf (fp, "uid:%s::::::::", truststring);
+ es_write_sanitized (fp, kludge_uid, strlen (kludge_uid),
+ ":", NULL);
+ es_putc (':', fp);
+ es_putc (':', fp);
+ es_putc ('\n', fp);
+ }
+ }
+ xfree (p);
+ }
+ xfree (kludge_uid);
+}
+
+
+static void
+print_name_raw (estream_t fp, const char *string)
+{
+ if (!string)
+ es_fputs ("[error]", fp);
+ else
+ es_write_sanitized (fp, string, strlen (string), NULL, NULL);
+}
+
+static void
+print_names_raw (estream_t fp, int indent, ksba_name_t name)
+{
+ int idx;
+ const char *s;
+ int indent_all;
+
+ if ((indent_all = (indent < 0)))
+ indent = - indent;
+
+ if (!name)
+ {
+ es_fputs ("none\n", fp);
+ return;
+ }
+
+ for (idx=0; (s = ksba_name_enum (name, idx)); idx++)
+ {
+ char *p = ksba_name_get_uri (name, idx);
+ es_fprintf (fp, "%*s", idx||indent_all?indent:0, "");
+ es_write_sanitized (fp, p?p:s, strlen (p?p:s), NULL, NULL);
+ es_putc ('\n', fp);
+ xfree (p);
+ }
+}
+
+
+static void
+print_utf8_extn_raw (estream_t fp, int indent,
+ const unsigned char *der, size_t derlen)
+{
+ gpg_error_t err;
+ int class, tag, constructed, ndef;
+ size_t objlen, hdrlen;
+
+ if (indent < 0)
+ indent = - indent;
+
+ err = parse_ber_header (&der, &derlen, &class, &tag, &constructed,
+ &ndef, &objlen, &hdrlen);
+ if (!err && (objlen > derlen || tag != TAG_UTF8_STRING))
+ err = gpg_error (GPG_ERR_INV_OBJ);
+ if (err)
+ {
+ es_fprintf (fp, "%*s[%s]\n", indent, "", gpg_strerror (err));
+ return;
+ }
+ es_fprintf (fp, "%*s(%.*s)\n", indent, "", (int)objlen, der);
+}
+
+
+static void
+print_utf8_extn (estream_t fp, int indent,
+ const unsigned char *der, size_t derlen)
+{
+ gpg_error_t err;
+ int class, tag, constructed, ndef;
+ size_t objlen, hdrlen;
+ int indent_all;
+
+ if ((indent_all = (indent < 0)))
+ indent = - indent;
+
+ err = parse_ber_header (&der, &derlen, &class, &tag, &constructed,
+ &ndef, &objlen, &hdrlen);
+ if (!err && (objlen > derlen || tag != TAG_UTF8_STRING))
+ err = gpg_error (GPG_ERR_INV_OBJ);
+ if (err)
+ {
+ es_fprintf (fp, "%*s[%s%s]\n",
+ indent_all? indent:0, "", _("Error - "), gpg_strerror (err));
+ return;
+ }
+ es_fprintf (fp, "%*s\"", indent_all? indent:0, "");
+ /* Fixme: we should implement word wrapping */
+ es_write_sanitized (fp, der, objlen, "\"", NULL);
+ es_fputs ("\"\n", fp);
+}
+
+
+/* List one certificate in raw mode useful to have a closer look at
+ the certificate. This one does no beautification and only minimal
+ output sanitation. It is mainly useful for debugging. */
+static void
+list_cert_raw (ctrl_t ctrl, KEYDB_HANDLE hd,
+ ksba_cert_t cert, estream_t fp, int have_secret,
+ int with_validation)
+{
+ gpg_error_t err;
+ size_t off, len;
+ ksba_sexp_t sexp, keyid;
+ char *dn;
+ ksba_isotime_t t;
+ int idx, i;
+ int is_ca, chainlen;
+ unsigned int kusage;
+ char *string, *p, *pend;
+ const char *oid, *s;
+ ksba_name_t name, name2;
+ unsigned int reason;
+ const unsigned char *cert_der = NULL;
+
+ (void)have_secret;
+
+ es_fprintf (fp, " ID: 0x%08lX\n",
+ gpgsm_get_short_fingerprint (cert, NULL));
+
+ sexp = ksba_cert_get_serial (cert);
+ es_fputs (" S/N: ", fp);
+ gpgsm_print_serial (fp, sexp);
+ ksba_free (sexp);
+ es_putc ('\n', fp);
+
+ dn = ksba_cert_get_issuer (cert, 0);
+ es_fputs (" Issuer: ", fp);
+ print_name_raw (fp, dn);
+ ksba_free (dn);
+ es_putc ('\n', fp);
+ for (idx=1; (dn = ksba_cert_get_issuer (cert, idx)); idx++)
+ {
+ es_fputs (" aka: ", fp);
+ print_name_raw (fp, dn);
+ ksba_free (dn);
+ es_putc ('\n', fp);
+ }
+
+ dn = ksba_cert_get_subject (cert, 0);
+ es_fputs (" Subject: ", fp);
+ print_name_raw (fp, dn);
+ ksba_free (dn);
+ es_putc ('\n', fp);
+ for (idx=1; (dn = ksba_cert_get_subject (cert, idx)); idx++)
+ {
+ es_fputs (" aka: ", fp);
+ print_name_raw (fp, dn);
+ ksba_free (dn);
+ es_putc ('\n', fp);
+ }
+
+ dn = gpgsm_get_fingerprint_string (cert, 0);
+ es_fprintf (fp, " sha1_fpr: %s\n", dn?dn:"error");
+ xfree (dn);
+
+ dn = gpgsm_get_fingerprint_string (cert, GCRY_MD_MD5);
+ es_fprintf (fp, " md5_fpr: %s\n", dn?dn:"error");
+ xfree (dn);
+
+ dn = gpgsm_get_certid (cert);
+ es_fprintf (fp, " certid: %s\n", dn?dn:"error");
+ xfree (dn);
+
+ dn = gpgsm_get_keygrip_hexstring (cert);
+ es_fprintf (fp, " keygrip: %s\n", dn?dn:"error");
+ xfree (dn);
+
+ ksba_cert_get_validity (cert, 0, t);
+ es_fputs (" notBefore: ", fp);
+ gpgsm_print_time (fp, t);
+ es_putc ('\n', fp);
+ es_fputs (" notAfter: ", fp);
+ ksba_cert_get_validity (cert, 1, t);
+ gpgsm_print_time (fp, t);
+ es_putc ('\n', fp);
+
+ oid = ksba_cert_get_digest_algo (cert);
+ s = get_oid_desc (oid, NULL);
+ es_fprintf (fp, " hashAlgo: %s%s%s%s\n", oid, s?" (":"",s?s:"",s?")":"");
+
+ {
+ const char *algoname;
+ unsigned int nbits;
+
+ algoname = gcry_pk_algo_name (gpgsm_get_key_algo_info (cert, &nbits));
+ es_fprintf (fp, " keyType: %u bit %s\n",
+ nbits, algoname? algoname:"?");
+ }
+
+ /* subjectKeyIdentifier */
+ es_fputs (" subjKeyId: ", fp);
+ err = ksba_cert_get_subj_key_id (cert, NULL, &keyid);
+ if (!err || gpg_err_code (err) == GPG_ERR_NO_DATA)
+ {
+ if (gpg_err_code (err) == GPG_ERR_NO_DATA)
+ es_fputs ("[none]\n", fp);
+ else
+ {
+ gpgsm_print_serial (fp, keyid);
+ ksba_free (keyid);
+ es_putc ('\n', fp);
+ }
+ }
+ else
+ es_fputs ("[?]\n", fp);
+
+
+ /* authorityKeyIdentifier */
+ es_fputs (" authKeyId: ", fp);
+ err = ksba_cert_get_auth_key_id (cert, &keyid, &name, &sexp);
+ if (!err || gpg_err_code (err) == GPG_ERR_NO_DATA)
+ {
+ if (gpg_err_code (err) == GPG_ERR_NO_DATA || !name)
+ es_fputs ("[none]\n", fp);
+ else
+ {
+ gpgsm_print_serial (fp, sexp);
+ ksba_free (sexp);
+ es_putc ('\n', fp);
+ print_names_raw (fp, -15, name);
+ ksba_name_release (name);
+ }
+ if (keyid)
+ {
+ es_fputs (" authKeyId.ki: ", fp);
+ gpgsm_print_serial (fp, keyid);
+ ksba_free (keyid);
+ es_putc ('\n', fp);
+ }
+ }
+ else
+ es_fputs ("[?]\n", fp);
+
+ es_fputs (" keyUsage:", fp);
+ err = ksba_cert_get_key_usage (cert, &kusage);
+ if (gpg_err_code (err) != GPG_ERR_NO_DATA)
+ {
+ if (err)
+ es_fprintf (fp, " [error: %s]", gpg_strerror (err));
+ else
+ {
+ if ( (kusage & KSBA_KEYUSAGE_DIGITAL_SIGNATURE))
+ es_fputs (" digitalSignature", fp);
+ if ( (kusage & KSBA_KEYUSAGE_NON_REPUDIATION))
+ es_fputs (" nonRepudiation", fp);
+ if ( (kusage & KSBA_KEYUSAGE_KEY_ENCIPHERMENT))
+ es_fputs (" keyEncipherment", fp);
+ if ( (kusage & KSBA_KEYUSAGE_DATA_ENCIPHERMENT))
+ es_fputs (" dataEncipherment", fp);
+ if ( (kusage & KSBA_KEYUSAGE_KEY_AGREEMENT))
+ es_fputs (" keyAgreement", fp);
+ if ( (kusage & KSBA_KEYUSAGE_KEY_CERT_SIGN))
+ es_fputs (" certSign", fp);
+ if ( (kusage & KSBA_KEYUSAGE_CRL_SIGN))
+ es_fputs (" crlSign", fp);
+ if ( (kusage & KSBA_KEYUSAGE_ENCIPHER_ONLY))
+ es_fputs (" encipherOnly", fp);
+ if ( (kusage & KSBA_KEYUSAGE_DECIPHER_ONLY))
+ es_fputs (" decipherOnly", fp);
+ }
+ es_putc ('\n', fp);
+ }
+ else
+ es_fputs (" [none]\n", fp);
+
+ es_fputs (" extKeyUsage: ", fp);
+ err = ksba_cert_get_ext_key_usages (cert, &string);
+ if (gpg_err_code (err) != GPG_ERR_NO_DATA)
+ {
+ if (err)
+ es_fprintf (fp, "[error: %s]", gpg_strerror (err));
+ else
+ {
+ p = string;
+ while (p && (pend=strchr (p, ':')))
+ {
+ *pend++ = 0;
+ for (i=0; key_purpose_map[i].oid; i++)
+ if ( !strcmp (key_purpose_map[i].oid, p) )
+ break;
+ es_fputs (key_purpose_map[i].oid?key_purpose_map[i].name:p, fp);
+ p = pend;
+ if (*p != 'C')
+ es_fputs (" (suggested)", fp);
+ if ((p = strchr (p, '\n')))
+ {
+ p++;
+ es_fputs ("\n ", fp);
+ }
+ }
+ xfree (string);
+ }
+ es_putc ('\n', fp);
+ }
+ else
+ es_fputs ("[none]\n", fp);
+
+
+ es_fputs (" policies: ", fp);
+ err = ksba_cert_get_cert_policies (cert, &string);
+ if (gpg_err_code (err) != GPG_ERR_NO_DATA)
+ {
+ if (err)
+ es_fprintf (fp, "[error: %s]", gpg_strerror (err));
+ else
+ {
+ p = string;
+ while (p && (pend=strchr (p, ':')))
+ {
+ *pend++ = 0;
+ for (i=0; key_purpose_map[i].oid; i++)
+ if ( !strcmp (key_purpose_map[i].oid, p) )
+ break;
+ es_fputs (p, fp);
+ p = pend;
+ if (*p == 'C')
+ es_fputs (" (critical)", fp);
+ if ((p = strchr (p, '\n')))
+ {
+ p++;
+ es_fputs ("\n ", fp);
+ }
+ }
+ xfree (string);
+ }
+ es_putc ('\n', fp);
+ }
+ else
+ es_fputs ("[none]\n", fp);
+
+ es_fputs (" chainLength: ", fp);
+ err = ksba_cert_is_ca (cert, &is_ca, &chainlen);
+ if (err || is_ca)
+ {
+ if (gpg_err_code (err) == GPG_ERR_NO_VALUE )
+ es_fprintf (fp, "[none]");
+ else if (err)
+ es_fprintf (fp, "[error: %s]", gpg_strerror (err));
+ else if (chainlen == -1)
+ es_fputs ("unlimited", fp);
+ else
+ es_fprintf (fp, "%d", chainlen);
+ es_putc ('\n', fp);
+ }
+ else
+ es_fputs ("not a CA\n", fp);
+
+
+ /* CRL distribution point */
+ for (idx=0; !(err=ksba_cert_get_crl_dist_point (cert, idx, &name, &name2,
+ &reason)) ;idx++)
+ {
+ es_fputs (" crlDP: ", fp);
+ print_names_raw (fp, 15, name);
+ if (reason)
+ {
+ es_fputs (" reason: ", fp);
+ if ( (reason & KSBA_CRLREASON_UNSPECIFIED))
+ es_fputs (" unused", fp);
+ if ( (reason & KSBA_CRLREASON_KEY_COMPROMISE))
+ es_fputs (" keyCompromise", fp);
+ if ( (reason & KSBA_CRLREASON_CA_COMPROMISE))
+ es_fputs (" caCompromise", fp);
+ if ( (reason & KSBA_CRLREASON_AFFILIATION_CHANGED))
+ es_fputs (" affiliationChanged", fp);
+ if ( (reason & KSBA_CRLREASON_SUPERSEDED))
+ es_fputs (" superseded", fp);
+ if ( (reason & KSBA_CRLREASON_CESSATION_OF_OPERATION))
+ es_fputs (" cessationOfOperation", fp);
+ if ( (reason & KSBA_CRLREASON_CERTIFICATE_HOLD))
+ es_fputs (" certificateHold", fp);
+ es_putc ('\n', fp);
+ }
+ es_fputs (" issuer: ", fp);
+ print_names_raw (fp, 23, name2);
+ ksba_name_release (name);
+ ksba_name_release (name2);
+ }
+ if (err && gpg_err_code (err) != GPG_ERR_EOF
+ && gpg_err_code (err) != GPG_ERR_NO_VALUE)
+ es_fputs (" crlDP: [error]\n", fp);
+ else if (!idx)
+ es_fputs (" crlDP: [none]\n", fp);
+
+
+ /* authorityInfoAccess. */
+ for (idx=0; !(err=ksba_cert_get_authority_info_access (cert, idx, &string,
+ &name)); idx++)
+ {
+ es_fputs (" authInfo: ", fp);
+ s = get_oid_desc (string, NULL);
+ es_fprintf (fp, "%s%s%s%s\n", string, s?" (":"", s?s:"", s?")":"");
+ print_names_raw (fp, -15, name);
+ ksba_name_release (name);
+ ksba_free (string);
+ }
+ if (err && gpg_err_code (err) != GPG_ERR_EOF
+ && gpg_err_code (err) != GPG_ERR_NO_VALUE)
+ es_fputs (" authInfo: [error]\n", fp);
+ else if (!idx)
+ es_fputs (" authInfo: [none]\n", fp);
+
+ /* subjectInfoAccess. */
+ for (idx=0; !(err=ksba_cert_get_subject_info_access (cert, idx, &string,
+ &name)); idx++)
+ {
+ es_fputs (" subjectInfo: ", fp);
+ s = get_oid_desc (string, NULL);
+ es_fprintf (fp, "%s%s%s%s\n", string, s?" (":"", s?s:"", s?")":"");
+ print_names_raw (fp, -15, name);
+ ksba_name_release (name);
+ ksba_free (string);
+ }
+ if (err && gpg_err_code (err) != GPG_ERR_EOF
+ && gpg_err_code (err) != GPG_ERR_NO_VALUE)
+ es_fputs (" subjInfo: [error]\n", fp);
+ else if (!idx)
+ es_fputs (" subjInfo: [none]\n", fp);
+
+
+ for (idx=0; !(err=ksba_cert_get_extension (cert, idx,
+ &oid, &i, &off, &len));idx++)
+ {
+ unsigned int flag;
+
+ s = get_oid_desc (oid, &flag);
+ if ((flag & OID_FLAG_SKIP))
+ continue;
+
+ es_fprintf (fp, " %s: %s%s%s%s [%d octets]\n",
+ i? "critExtn":" extn",
+ oid, s?" (":"", s?s:"", s?")":"", (int)len);
+ if ((flag & OID_FLAG_UTF8))
+ {
+ if (!cert_der)
+ cert_der = ksba_cert_get_image (cert, NULL);
+ assert (cert_der);
+ print_utf8_extn_raw (fp, -15, cert_der+off, len);
+ }
+ }
+
+
+ if (with_validation)
+ {
+ err = gpgsm_validate_chain (ctrl, cert, "", NULL, 1, fp, 0, NULL);
+ if (!err)
+ es_fprintf (fp, " [certificate is good]\n");
+ else
+ es_fprintf (fp, " [certificate is bad: %s]\n", gpg_strerror (err));
+ }
+
+ if (hd)
+ {
+ unsigned int blobflags;
+
+ err = keydb_get_flags (hd, KEYBOX_FLAG_BLOB, 0, &blobflags);
+ if (err)
+ es_fprintf (fp, " [error getting keyflags: %s]\n",gpg_strerror (err));
+ else if ((blobflags & KEYBOX_FLAG_BLOB_EPHEMERAL))
+ es_fprintf (fp, " [stored as ephemeral]\n");
+ }
+
+}
+
+
+
+
+/* List one certificate in standard mode */
+static void
+list_cert_std (ctrl_t ctrl, ksba_cert_t cert, estream_t fp, int have_secret,
+ int with_validation)
+{
+ gpg_error_t err;
+ ksba_sexp_t sexp;
+ char *dn;
+ ksba_isotime_t t;
+ int idx, i;
+ int is_ca, chainlen;
+ unsigned int kusage;
+ char *string, *p, *pend;
+ size_t off, len;
+ const char *oid;
+ const unsigned char *cert_der = NULL;
+
+
+ es_fprintf (fp, " ID: 0x%08lX\n",
+ gpgsm_get_short_fingerprint (cert, NULL));
+
+ sexp = ksba_cert_get_serial (cert);
+ es_fputs (" S/N: ", fp);
+ gpgsm_print_serial (fp, sexp);
+ ksba_free (sexp);
+ es_putc ('\n', fp);
+
+ dn = ksba_cert_get_issuer (cert, 0);
+ es_fputs (" Issuer: ", fp);
+ gpgsm_es_print_name (fp, dn);
+ ksba_free (dn);
+ es_putc ('\n', fp);
+ for (idx=1; (dn = ksba_cert_get_issuer (cert, idx)); idx++)
+ {
+ es_fputs (" aka: ", fp);
+ gpgsm_es_print_name (fp, dn);
+ ksba_free (dn);
+ es_putc ('\n', fp);
+ }
+
+ dn = ksba_cert_get_subject (cert, 0);
+ es_fputs (" Subject: ", fp);
+ gpgsm_es_print_name (fp, dn);
+ ksba_free (dn);
+ es_putc ('\n', fp);
+ for (idx=1; (dn = ksba_cert_get_subject (cert, idx)); idx++)
+ {
+ es_fputs (" aka: ", fp);
+ gpgsm_es_print_name (fp, dn);
+ ksba_free (dn);
+ es_putc ('\n', fp);
+ }
+
+ ksba_cert_get_validity (cert, 0, t);
+ es_fputs (" validity: ", fp);
+ gpgsm_print_time (fp, t);
+ es_fputs (" through ", fp);
+ ksba_cert_get_validity (cert, 1, t);
+ gpgsm_print_time (fp, t);
+ es_putc ('\n', fp);
+
+
+ {
+ const char *algoname;
+ unsigned int nbits;
+
+ algoname = gcry_pk_algo_name (gpgsm_get_key_algo_info (cert, &nbits));
+ es_fprintf (fp, " key type: %u bit %s\n",
+ nbits, algoname? algoname:"?");
+ }
+
+
+ err = ksba_cert_get_key_usage (cert, &kusage);
+ if (gpg_err_code (err) != GPG_ERR_NO_DATA)
+ {
+ es_fputs (" key usage:", fp);
+ if (err)
+ es_fprintf (fp, " [error: %s]", gpg_strerror (err));
+ else
+ {
+ if ( (kusage & KSBA_KEYUSAGE_DIGITAL_SIGNATURE))
+ es_fputs (" digitalSignature", fp);
+ if ( (kusage & KSBA_KEYUSAGE_NON_REPUDIATION))
+ es_fputs (" nonRepudiation", fp);
+ if ( (kusage & KSBA_KEYUSAGE_KEY_ENCIPHERMENT))
+ es_fputs (" keyEncipherment", fp);
+ if ( (kusage & KSBA_KEYUSAGE_DATA_ENCIPHERMENT))
+ es_fputs (" dataEncipherment", fp);
+ if ( (kusage & KSBA_KEYUSAGE_KEY_AGREEMENT))
+ es_fputs (" keyAgreement", fp);
+ if ( (kusage & KSBA_KEYUSAGE_KEY_CERT_SIGN))
+ es_fputs (" certSign", fp);
+ if ( (kusage & KSBA_KEYUSAGE_CRL_SIGN))
+ es_fputs (" crlSign", fp);
+ if ( (kusage & KSBA_KEYUSAGE_ENCIPHER_ONLY))
+ es_fputs (" encipherOnly", fp);
+ if ( (kusage & KSBA_KEYUSAGE_DECIPHER_ONLY))
+ es_fputs (" decipherOnly", fp);
+ }
+ es_putc ('\n', fp);
+ }
+
+ err = ksba_cert_get_ext_key_usages (cert, &string);
+ if (gpg_err_code (err) != GPG_ERR_NO_DATA)
+ {
+ es_fputs ("ext key usage: ", fp);
+ if (err)
+ es_fprintf (fp, "[error: %s]", gpg_strerror (err));
+ else
+ {
+ p = string;
+ while (p && (pend=strchr (p, ':')))
+ {
+ *pend++ = 0;
+ for (i=0; key_purpose_map[i].oid; i++)
+ if ( !strcmp (key_purpose_map[i].oid, p) )
+ break;
+ es_fputs (key_purpose_map[i].oid?key_purpose_map[i].name:p, fp);
+ p = pend;
+ if (*p != 'C')
+ es_fputs (" (suggested)", fp);
+ if ((p = strchr (p, '\n')))
+ {
+ p++;
+ es_fputs (", ", fp);
+ }
+ }
+ xfree (string);
+ }
+ es_putc ('\n', fp);
+ }
+
+ /* Print restrictions. */
+ for (idx=0; !(err=ksba_cert_get_extension (cert, idx,
+ &oid, NULL, &off, &len));idx++)
+ {
+ if (!strcmp (oid, OIDSTR_restriction) )
+ {
+ if (!cert_der)
+ cert_der = ksba_cert_get_image (cert, NULL);
+ assert (cert_der);
+ es_fputs (" restriction: ", fp);
+ print_utf8_extn (fp, 15, cert_der+off, len);
+ }
+ }
+
+ /* Print policies. */
+ err = ksba_cert_get_cert_policies (cert, &string);
+ if (gpg_err_code (err) != GPG_ERR_NO_DATA)
+ {
+ es_fputs (" policies: ", fp);
+ if (err)
+ es_fprintf (fp, "[error: %s]", gpg_strerror (err));
+ else
+ {
+ for (p=string; *p; p++)
+ {
+ if (*p == '\n')
+ *p = ',';
+ }
+ es_write_sanitized (fp, string, strlen (string), NULL, NULL);
+ xfree (string);
+ }
+ es_putc ('\n', fp);
+ }
+
+ err = ksba_cert_is_ca (cert, &is_ca, &chainlen);
+ if (err || is_ca)
+ {
+ es_fputs (" chain length: ", fp);
+ if (gpg_err_code (err) == GPG_ERR_NO_VALUE )
+ es_fprintf (fp, "none");
+ else if (err)
+ es_fprintf (fp, "[error: %s]", gpg_strerror (err));
+ else if (chainlen == -1)
+ es_fputs ("unlimited", fp);
+ else
+ es_fprintf (fp, "%d", chainlen);
+ es_putc ('\n', fp);
+ }
+
+ if (opt.with_md5_fingerprint)
+ {
+ dn = gpgsm_get_fingerprint_string (cert, GCRY_MD_MD5);
+ es_fprintf (fp, " md5 fpr: %s\n", dn?dn:"error");
+ xfree (dn);
+ }
+
+ dn = gpgsm_get_fingerprint_string (cert, 0);
+ es_fprintf (fp, " fingerprint: %s\n", dn?dn:"error");
+ xfree (dn);
+
+ if (have_secret)
+ {
+ char *cardsn;
+
+ p = gpgsm_get_keygrip_hexstring (cert);
+ if (!gpgsm_agent_keyinfo (ctrl, p, &cardsn) && cardsn)
+ es_fprintf (fp, " card s/n: %s\n", cardsn);
+ xfree (cardsn);
+ xfree (p);
+ }
+
+ if (with_validation)
+ {
+ gpg_error_t tmperr;
+ size_t buflen;
+ char buffer[1];
+
+ err = gpgsm_validate_chain (ctrl, cert, "", NULL, 1, fp, 0, NULL);
+ tmperr = ksba_cert_get_user_data (cert, "is_qualified",
+ &buffer, sizeof (buffer), &buflen);
+ if (!tmperr && buflen)
+ {
+ if (*buffer)
+ es_fputs (" [qualified]\n", fp);
+ }
+ else if (gpg_err_code (tmperr) == GPG_ERR_NOT_FOUND)
+ ; /* Don't know - will not get marked as 'q' */
+ else
+ log_debug ("get_user_data(is_qualified) failed: %s\n",
+ gpg_strerror (tmperr));
+
+ if (!err)
+ es_fprintf (fp, " [certificate is good]\n");
+ else
+ es_fprintf (fp, " [certificate is bad: %s]\n", gpg_strerror (err));
+ }
+}
+
+
+/* Same as standard mode mode list all certifying certs too. */
+static void
+list_cert_chain (ctrl_t ctrl, KEYDB_HANDLE hd,
+ ksba_cert_t cert, int raw_mode,
+ estream_t fp, int with_validation)
+{
+ ksba_cert_t next = NULL;
+
+ if (raw_mode)
+ list_cert_raw (ctrl, hd, cert, fp, 0, with_validation);
+ else
+ list_cert_std (ctrl, cert, fp, 0, with_validation);
+ ksba_cert_ref (cert);
+ while (!gpgsm_walk_cert_chain (ctrl, cert, &next))
+ {
+ ksba_cert_release (cert);
+ es_fputs ("Certified by\n", fp);
+ if (raw_mode)
+ list_cert_raw (ctrl, hd, next, fp, 0, with_validation);
+ else
+ list_cert_std (ctrl, next, fp, 0, with_validation);
+ cert = next;
+ }
+ ksba_cert_release (cert);
+ es_putc ('\n', fp);
+}
+
+
+
+/* List all internal keys or just the keys given as NAMES. MODE is a
+ bit vector to specify what keys are to be included; see
+ gpgsm_list_keys (below) for details. If RAW_MODE is true, the raw
+ output mode will be used instead of the standard beautified one.
+ */
+static gpg_error_t
+list_internal_keys (ctrl_t ctrl, strlist_t names, estream_t fp,
+ unsigned int mode, int raw_mode)
+{
+ KEYDB_HANDLE hd;
+ KEYDB_SEARCH_DESC *desc = NULL;
+ strlist_t sl;
+ int ndesc;
+ ksba_cert_t cert = NULL;
+ ksba_cert_t lastcert = NULL;
+ gpg_error_t rc = 0;
+ const char *lastresname, *resname;
+ int have_secret;
+ int want_ephemeral = ctrl->with_ephemeral_keys;
+
+ hd = keydb_new (0);
+ if (!hd)
+ {
+ log_error ("keydb_new failed\n");
+ rc = gpg_error (GPG_ERR_GENERAL);
+ goto leave;
+ }
+
+ if (!names)
+ ndesc = 1;
+ else
+ {
+ for (sl=names, ndesc=0; sl; sl = sl->next, ndesc++)
+ ;
+ }
+
+ desc = xtrycalloc (ndesc, sizeof *desc);
+ if (!ndesc)
+ {
+ rc = gpg_error_from_syserror ();
+ log_error ("out of core\n");
+ goto leave;
+ }
+
+ if (!names)
+ desc[0].mode = KEYDB_SEARCH_MODE_FIRST;
+ else
+ {
+ for (ndesc=0, sl=names; sl; sl = sl->next)
+ {
+ rc = keydb_classify_name (sl->d, desc+ndesc);
+ if (rc)
+ {
+ log_error ("key `%s' not found: %s\n",
+ sl->d, gpg_strerror (rc));
+ rc = 0;
+ }
+ else
+ ndesc++;
+ }
+
+ }
+
+ /* If all specifications are done by fingerprint or keygrip, we
+ switch to ephemeral mode so that _all_ currently available and
+ matching certificates are listed. */
+ if (!want_ephemeral && names && ndesc)
+ {
+ int i;
+
+ for (i=0; (i < ndesc
+ && (desc[i].mode == KEYDB_SEARCH_MODE_FPR
+ || desc[i].mode == KEYDB_SEARCH_MODE_FPR20
+ || desc[i].mode == KEYDB_SEARCH_MODE_FPR16
+ || desc[i].mode == KEYDB_SEARCH_MODE_KEYGRIP)); i++)
+ ;
+ if (i == ndesc)
+ want_ephemeral = 1;
+ }
+
+ if (want_ephemeral)
+ keydb_set_ephemeral (hd, 1);
+
+ /* 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 */
+
+ /* Suppress duplicates at least when they follow each other. */
+ lastresname = NULL;
+ while (!(rc = keydb_search (hd, desc, ndesc)))
+ {
+ unsigned int validity;
+
+ if (!names)
+ desc[0].mode = KEYDB_SEARCH_MODE_NEXT;
+
+ rc = keydb_get_flags (hd, KEYBOX_FLAG_VALIDITY, 0, &validity);
+ if (rc)
+ {
+ log_error ("keydb_get_flags failed: %s\n", gpg_strerror (rc));
+ goto leave;
+ }
+ rc = keydb_get_cert (hd, &cert);
+ if (rc)
+ {
+ log_error ("keydb_get_cert failed: %s\n", gpg_strerror (rc));
+ goto leave;
+ }
+ /* Skip duplicated certificates, at least if they follow each
+ others. This works best if a single key is searched for and
+ expected. FIXME: Non-sequential duplicates remain. */
+ if (gpgsm_certs_identical_p (cert, lastcert))
+ {
+ ksba_cert_release (cert);
+ cert = NULL;
+ continue;
+ }
+
+ resname = keydb_get_resource_name (hd);
+
+ if (lastresname != resname )
+ {
+ int i;
+
+ if (ctrl->no_server)
+ {
+ es_fprintf (fp, "%s\n", resname );
+ for (i=strlen(resname); i; i-- )
+ es_putc ('-', fp);
+ es_putc ('\n', fp);
+ lastresname = resname;
+ }
+ }
+
+ have_secret = 0;
+ if (mode)
+ {
+ char *p = gpgsm_get_keygrip_hexstring (cert);
+ if (p)
+ {
+ rc = gpgsm_agent_havekey (ctrl, p);
+ if (!rc)
+ have_secret = 1;
+ else if ( gpg_err_code (rc) != GPG_ERR_NO_SECKEY)
+ goto leave;
+ rc = 0;
+ xfree (p);
+ }
+ }
+
+ if (!mode
+ || ((mode & 1) && !have_secret)
+ || ((mode & 2) && have_secret) )
+ {
+ if (ctrl->with_colons)
+ list_cert_colon (ctrl, cert, validity, fp, have_secret);
+ else if (ctrl->with_chain)
+ list_cert_chain (ctrl, hd, cert,
+ raw_mode, fp, ctrl->with_validation);
+ else
+ {
+ if (raw_mode)
+ list_cert_raw (ctrl, hd, cert, fp, have_secret,
+ ctrl->with_validation);
+ else
+ list_cert_std (ctrl, cert, fp, have_secret,
+ ctrl->with_validation);
+ es_putc ('\n', fp);
+ }
+ }
+
+ ksba_cert_release (lastcert);
+ lastcert = cert;
+ cert = NULL;
+ }
+ if (gpg_err_code (rc) == GPG_ERR_EOF || rc == -1 )
+ rc = 0;
+ if (rc)
+ log_error ("keydb_search failed: %s\n", gpg_strerror (rc));
+
+ leave:
+ ksba_cert_release (cert);
+ ksba_cert_release (lastcert);
+ xfree (desc);
+ keydb_release (hd);
+ return rc;
+}
+
+
+
+static void
+list_external_cb (void *cb_value, ksba_cert_t cert)
+{
+ struct list_external_parm_s *parm = cb_value;
+
+ if (keydb_store_cert (cert, 1, NULL))
+ log_error ("error storing certificate as ephemeral\n");
+
+ if (parm->print_header)
+ {
+ const char *resname = "[external keys]";
+ int i;
+
+ es_fprintf (parm->fp, "%s\n", resname );
+ for (i=strlen(resname); i; i-- )
+ es_putc('-', parm->fp);
+ es_putc ('\n', parm->fp);
+ parm->print_header = 0;
+ }
+
+ if (parm->with_colons)
+ list_cert_colon (parm->ctrl, cert, 0, parm->fp, 0);
+ else if (parm->with_chain)
+ list_cert_chain (parm->ctrl, NULL, cert, parm->raw_mode, parm->fp, 0);
+ else
+ {
+ if (parm->raw_mode)
+ list_cert_raw (parm->ctrl, NULL, cert, parm->fp, 0, 0);
+ else
+ list_cert_std (parm->ctrl, cert, parm->fp, 0, 0);
+ es_putc ('\n', parm->fp);
+ }
+}
+
+
+/* List external keys similar to internal one. Note: mode does not
+ make sense here because it would be unwise to list external secret
+ keys */
+static gpg_error_t
+list_external_keys (ctrl_t ctrl, strlist_t names, estream_t fp, int raw_mode)
+{
+ int rc;
+ struct list_external_parm_s parm;
+
+ parm.fp = fp;
+ parm.ctrl = ctrl,
+ parm.print_header = ctrl->no_server;
+ parm.with_colons = ctrl->with_colons;
+ parm.with_chain = ctrl->with_chain;
+ parm.raw_mode = raw_mode;
+
+ rc = gpgsm_dirmngr_lookup (ctrl, names, 0, list_external_cb, &parm);
+ if (gpg_err_code (rc) == GPG_ERR_EOF || rc == -1
+ || gpg_err_code (rc) == GPG_ERR_NOT_FOUND)
+ rc = 0; /* "Not found" is not an error here. */
+ if (rc)
+ log_error ("listing external keys failed: %s\n", gpg_strerror (rc));
+ return rc;
+}
+
+/* List all keys or just the key given as NAMES.
+ MODE controls the operation mode:
+ Bit 0-2:
+ 0 = list all public keys but don't flag secret ones
+ 1 = list only public keys
+ 2 = list only secret keys
+ 3 = list secret and public keys
+ Bit 6: list internal keys
+ Bit 7: list external keys
+ Bit 8: Do a raw format dump.
+ */
+gpg_error_t
+gpgsm_list_keys (ctrl_t ctrl, strlist_t names, estream_t fp,
+ unsigned int mode)
+{
+ gpg_error_t err = 0;
+
+ if ((mode & (1<<6)))
+ err = list_internal_keys (ctrl, names, fp, (mode & 3), (mode&256));
+ if (!err && (mode & (1<<7)))
+ err = list_external_keys (ctrl, names, fp, (mode&256));
+ return err;
+}
diff --git a/sm/misc.c b/sm/misc.c
new file mode 100644
index 0000000..628b321
--- /dev/null
+++ b/sm/misc.c
@@ -0,0 +1,89 @@
+/* misc.c - Miscellaneous fucntions
+ * Copyright (C) 2004, 2009 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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuPG is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <unistd.h>
+#ifdef HAVE_LOCALE_H
+#include <locale.h>
+#endif
+
+#include "gpgsm.h"
+#include "i18n.h"
+#include "setenv.h"
+
+/* Setup the environment so that the pinentry is able to get all
+ required information. This is used prior to an exec of the
+ protect-tool. */
+void
+setup_pinentry_env (void)
+{
+#ifndef HAVE_W32_SYSTEM
+ char *lc;
+ const char *name, *value;
+ int iterator;
+
+ /* Try to make sure that GPG_TTY has been set. This is needed if we
+ call for example the protect-tools with redirected stdin and thus
+ it won't be able to ge a default by itself. Try to do it here
+ but print a warning. */
+ value = session_env_getenv (opt.session_env, "GPG_TTY");
+ if (value)
+ setenv ("GPG_TTY", value, 1);
+ else if (!(lc=getenv ("GPG_TTY")) || !*lc)
+ {
+ log_error (_("GPG_TTY has not been set - "
+ "using maybe bogus default\n"));
+ lc = ttyname (0);
+ if (!lc)
+ lc = "/dev/tty";
+ setenv ("GPG_TTY", lc, 1);
+ }
+
+ if (opt.lc_ctype)
+ setenv ("LC_CTYPE", opt.lc_ctype, 1);
+#if defined(HAVE_SETLOCALE) && defined(LC_CTYPE)
+ else if ( (lc = setlocale (LC_CTYPE, "")) )
+ setenv ("LC_CTYPE", lc, 1);
+#endif
+
+ if (opt.lc_messages)
+ setenv ("LC_MESSAGES", opt.lc_messages, 1);
+#if defined(HAVE_SETLOCALE) && defined(LC_MESSAGES)
+ else if ( (lc = setlocale (LC_MESSAGES, "")) )
+ setenv ("LC_MESSAGES", lc, 1);
+#endif
+
+ iterator = 0;
+ while ((name = session_env_list_stdenvnames (&iterator, NULL)))
+ {
+ if (!strcmp (name, "GPG_TTY"))
+ continue; /* Already set. */
+ value = session_env_getenv (opt.session_env, name);
+ if (value)
+ setenv (name, value, 1);
+ }
+
+#endif /*!HAVE_W32_SYSTEM*/
+}
+
diff --git a/sm/qualified.c b/sm/qualified.c
new file mode 100644
index 0000000..d0db481
--- /dev/null
+++ b/sm/qualified.c
@@ -0,0 +1,321 @@
+/* qualified.c - Routines related to qualified signatures
+ * Copyright (C) 2005, 2007 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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuPG is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+#include <assert.h>
+#include <errno.h>
+
+#include "gpgsm.h"
+#include "i18n.h"
+#include <ksba.h>
+
+
+/* We open the file only once and keep the open file pointer as well
+ as the name of the file here. Note that, a listname not equal to
+ NULL indicates that this module has been intialized and if the
+ LISTFP is also NULL, no list of qualified signatures exists. */
+static char *listname;
+static FILE *listfp;
+
+
+/* Read the trustlist and return entry by entry. KEY must point to a
+ buffer of at least 41 characters. COUNTRY shall be a buffer of at
+ least 3 characters to receive the country code of that qualified
+ signature (i.e. "de" for German and "be" for Belgium).
+
+ Reading a valid entry returns 0, EOF is indicated by GPG_ERR_EOF
+ and any other error condition is indicated by the appropriate error
+ code. */
+static gpg_error_t
+read_list (char *key, char *country, int *lnr)
+{
+ gpg_error_t err;
+ int c, i, j;
+ char *p, line[256];
+
+ *key = 0;
+ *country = 0;
+
+ if (!listname)
+ {
+ listname = make_filename (gnupg_datadir (), "qualified.txt", NULL);
+ listfp = fopen (listname, "r");
+ if (!listfp && errno != ENOENT)
+ {
+ err = gpg_error_from_syserror ();
+ log_error (_("can't open `%s': %s\n"), listname, gpg_strerror (err));
+ return err;
+ }
+ }
+
+ if (!listfp)
+ return gpg_error (GPG_ERR_EOF);
+
+ do
+ {
+ if (!fgets (line, DIM(line)-1, listfp) )
+ {
+ if (feof (listfp))
+ return gpg_error (GPG_ERR_EOF);
+ return gpg_error_from_syserror ();
+ }
+
+ if (!*line || line[strlen(line)-1] != '\n')
+ {
+ /* Eat until end of line. */
+ while ( (c=getc (listfp)) != EOF && c != '\n')
+ ;
+ return gpg_error (*line? GPG_ERR_LINE_TOO_LONG
+ : GPG_ERR_INCOMPLETE_LINE);
+ }
+ ++*lnr;
+
+ /* Allow for empty lines and spaces */
+ for (p=line; spacep (p); p++)
+ ;
+ }
+ while (!*p || *p == '\n' || *p == '#');
+
+ for (i=j=0; (p[i] == ':' || hexdigitp (p+i)) && j < 40; i++)
+ if ( p[i] != ':' )
+ key[j++] = p[i] >= 'a'? (p[i] & 0xdf): p[i];
+ key[j] = 0;
+ if (j != 40 || !(spacep (p+i) || p[i] == '\n'))
+ {
+ log_error (_("invalid formatted fingerprint in `%s', line %d\n"),
+ listname, *lnr);
+ return gpg_error (GPG_ERR_BAD_DATA);
+ }
+ assert (p[i]);
+ i++;
+ while (spacep (p+i))
+ i++;
+ if ( p[i] >= 'a' && p[i] <= 'z'
+ && p[i+1] >= 'a' && p[i+1] <= 'z'
+ && (spacep (p+i+2) || p[i+2] == '\n'))
+ {
+ country[0] = p[i];
+ country[1] = p[i+1];
+ country[2] = 0;
+ }
+ else
+ {
+ log_error (_("invalid country code in `%s', line %d\n"), listname, *lnr);
+ return gpg_error (GPG_ERR_BAD_DATA);
+ }
+
+ return 0;
+}
+
+
+
+
+/* Check whether the certificate CERT is included in the list of
+ qualified certificates. This list is similar to the "trustlist.txt"
+ as maintained by gpg-agent and includes fingerprints of root
+ certificates to be used for qualified (legally binding like
+ handwritten) signatures. We keep this list system wide and not
+ per user because it is not a decision of the user.
+
+ Returns: 0 if the certificate is included. GPG_ERR_NOT_FOUND if it
+ is not in the list or any other error (e.g. if no list of
+ qualified signatures is available. If COUNTRY has not been passed
+ as NULL a string witha maximum length of 2 will be copied into it;
+ thus the caller needs to provide a buffer of length 3. */
+gpg_error_t
+gpgsm_is_in_qualified_list (ctrl_t ctrl, ksba_cert_t cert, char *country)
+{
+ gpg_error_t err;
+ char *fpr;
+ char key[41];
+ char mycountry[3];
+ int lnr = 0;
+
+ (void)ctrl;
+
+ if (country)
+ *country = 0;
+
+ fpr = gpgsm_get_fingerprint_hexstring (cert, GCRY_MD_SHA1);
+ if (!fpr)
+ return gpg_error (GPG_ERR_GENERAL);
+
+ if (listfp)
+ rewind (listfp);
+ while (!(err = read_list (key, mycountry, &lnr)))
+ {
+ if (!strcmp (key, fpr))
+ break;
+ }
+ if (gpg_err_code (err) == GPG_ERR_EOF)
+ err = gpg_error (GPG_ERR_NOT_FOUND);
+
+ if (!err && country)
+ strcpy (country, mycountry);
+
+ xfree (fpr);
+ return err;
+}
+
+
+/* We know that CERT is a qualified certificate. Ask the user for
+ consent to actually create a signature using this certificate.
+ Returns: 0 for yes, GPG_ERR_CANCEL for no or any otehr error
+ code. */
+gpg_error_t
+gpgsm_qualified_consent (ctrl_t ctrl, ksba_cert_t cert)
+{
+ gpg_error_t err;
+ char *name, *subject, *buffer, *p;
+ const char *s;
+ char *orig_codeset = NULL;
+
+ name = ksba_cert_get_subject (cert, 0);
+ if (!name)
+ return gpg_error (GPG_ERR_GENERAL);
+ subject = gpgsm_format_name2 (name, 0);
+ ksba_free (name); name = NULL;
+
+ orig_codeset = i18n_switchto_utf8 ();
+
+ if (asprintf (&name,
+ _("You are about to create a signature using your "
+ "certificate:\n"
+ "\"%s\"\n"
+ "This will create a qualified signature by law "
+ "equated to a handwritten signature.\n\n%s%s"
+ "Are you really sure that you want to do this?"),
+ subject? subject:"?",
+ opt.qualsig_approval?
+ "":
+ _("Note, that this software is not officially approved "
+ "to create or verify such signatures.\n"),
+ opt.qualsig_approval? "":"\n"
+ ) < 0 )
+ err = gpg_error_from_syserror ();
+ else
+ err = 0;
+
+ i18n_switchback (orig_codeset);
+ xfree (subject);
+
+ if (err)
+ return err;
+
+ buffer = p = xtrymalloc (strlen (name) * 3 + 1);
+ if (!buffer)
+ {
+ err = gpg_error_from_syserror ();
+ free (name);
+ return err;
+ }
+ for (s=name; *s; s++)
+ {
+ if (*s < ' ' || *s == '+')
+ {
+ sprintf (p, "%%%02X", *(unsigned char *)s);
+ p += 3;
+ }
+ else if (*s == ' ')
+ *p++ = '+';
+ else
+ *p++ = *s;
+ }
+ *p = 0;
+ free (name);
+
+
+ err = gpgsm_agent_get_confirmation (ctrl, buffer);
+
+ xfree (buffer);
+ return err;
+}
+
+
+/* Popup a prompt to inform the user that the signature created is not
+ a qualified one. This is of course only done if we know that we
+ have been approved. */
+gpg_error_t
+gpgsm_not_qualified_warning (ctrl_t ctrl, ksba_cert_t cert)
+{
+ gpg_error_t err;
+ char *name, *subject, *buffer, *p;
+ const char *s;
+ char *orig_codeset;
+
+ if (!opt.qualsig_approval)
+ return 0;
+
+ name = ksba_cert_get_subject (cert, 0);
+ if (!name)
+ return gpg_error (GPG_ERR_GENERAL);
+ subject = gpgsm_format_name2 (name, 0);
+ ksba_free (name); name = NULL;
+
+ orig_codeset = i18n_switchto_utf8 ();
+
+ if (asprintf (&name,
+ _("You are about to create a signature using your "
+ "certificate:\n"
+ "\"%s\"\n"
+ "Note, that this certificate will NOT create a "
+ "qualified signature!"),
+ subject? subject:"?") < 0 )
+ err = gpg_error_from_syserror ();
+ else
+ err = 0;
+
+ i18n_switchback (orig_codeset);
+ xfree (subject);
+
+ if (err)
+ return err;
+
+ buffer = p = xtrymalloc (strlen (name) * 3 + 1);
+ if (!buffer)
+ {
+ err = gpg_error_from_syserror ();
+ free (name);
+ return err;
+ }
+ for (s=name; *s; s++)
+ {
+ if (*s < ' ' || *s == '+')
+ {
+ sprintf (p, "%%%02X", *(unsigned char *)s);
+ p += 3;
+ }
+ else if (*s == ' ')
+ *p++ = '+';
+ else
+ *p++ = *s;
+ }
+ *p = 0;
+ free (name);
+
+
+ err = gpgsm_agent_get_confirmation (ctrl, buffer);
+
+ xfree (buffer);
+ return err;
+}
diff --git a/sm/server.c b/sm/server.c
new file mode 100644
index 0000000..fcf47a7
--- /dev/null
+++ b/sm/server.c
@@ -0,0 +1,1464 @@
+/* server.c - Server mode and main entry point
+ * Copyright (C) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008,
+ * 2009, 2010 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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuPG is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+#include <ctype.h>
+#include <unistd.h>
+
+#include "gpgsm.h"
+#include <assuan.h>
+#include "sysutils.h"
+
+#define set_error(e,t) assuan_set_error (ctx, gpg_error (e), (t))
+
+
+/* The filepointer for status message used in non-server mode */
+static FILE *statusfp;
+
+/* Data used to assuciate an Assuan context with local server data */
+struct server_local_s {
+ assuan_context_t assuan_ctx;
+ int message_fd;
+ int list_internal;
+ int list_external;
+ int list_to_output; /* Write keylistings to the output fd. */
+ int enable_audit_log; /* Use an audit log. */
+ certlist_t recplist;
+ certlist_t signerlist;
+ certlist_t default_recplist; /* As set by main() - don't release. */
+ int allow_pinentry_notify; /* Set if pinentry notifications should
+ be passed back to the client. */
+ int no_encrypt_to; /* Local version of option. */
+};
+
+
+/* Cookie definition for assuan data line output. */
+static ssize_t data_line_cookie_write (void *cookie,
+ const void *buffer, size_t size);
+static int data_line_cookie_close (void *cookie);
+static es_cookie_io_functions_t data_line_cookie_functions =
+ {
+ NULL,
+ data_line_cookie_write,
+ NULL,
+ data_line_cookie_close
+ };
+
+
+
+static int command_has_option (const char *cmd, const char *cmdopt);
+
+
+
+
+/* Note that it is sufficient to allocate the target string D as
+ long as the source string S, i.e.: strlen(s)+1; */
+static void
+strcpy_escaped_plus (char *d, const char *s)
+{
+ while (*s)
+ {
+ if (*s == '%' && s[1] && s[2])
+ {
+ s++;
+ *d++ = xtoi_2 (s);
+ s += 2;
+ }
+ else if (*s == '+')
+ *d++ = ' ', s++;
+ else
+ *d++ = *s++;
+ }
+ *d = 0;
+}
+
+
+/* Skip over options.
+ Blanks after the options are also removed. */
+static char *
+skip_options (const char *line)
+{
+ while (spacep (line))
+ line++;
+ while ( *line == '-' && line[1] == '-' )
+ {
+ while (*line && !spacep (line))
+ line++;
+ while (spacep (line))
+ line++;
+ }
+ return (char*)line;
+}
+
+
+/* Check whether the option NAME appears in LINE */
+static int
+has_option (const char *line, const char *name)
+{
+ const char *s;
+ int n = strlen (name);
+
+ s = strstr (line, name);
+ if (s && s >= skip_options (line))
+ return 0;
+ return (s && (s == line || spacep (s-1)) && (!s[n] || spacep (s+n)));
+}
+
+
+/* A write handler used by es_fopencookie to write assuan data
+ lines. */
+static ssize_t
+data_line_cookie_write (void *cookie, const void *buffer, size_t size)
+{
+ assuan_context_t ctx = cookie;
+
+ if (assuan_send_data (ctx, buffer, size))
+ {
+ errno = EIO;
+ return -1;
+ }
+
+ return size;
+}
+
+static int
+data_line_cookie_close (void *cookie)
+{
+ assuan_context_t ctx = cookie;
+
+ if (assuan_send_data (ctx, NULL, 0))
+ {
+ errno = EIO;
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static void
+close_message_fd (ctrl_t ctrl)
+{
+ if (ctrl->server_local->message_fd != -1)
+ {
+ close (ctrl->server_local->message_fd);
+ ctrl->server_local->message_fd = -1;
+ }
+}
+
+
+/* Start a new audit session if this has been enabled. */
+static gpg_error_t
+start_audit_session (ctrl_t ctrl)
+{
+ audit_release (ctrl->audit);
+ ctrl->audit = NULL;
+ if (ctrl->server_local->enable_audit_log && !(ctrl->audit = audit_new ()) )
+ return gpg_error_from_syserror ();
+
+ return 0;
+}
+
+
+static gpg_error_t
+option_handler (assuan_context_t ctx, const char *key, const char *value)
+{
+ ctrl_t ctrl = assuan_get_pointer (ctx);
+ gpg_error_t err = 0;
+
+ if (!strcmp (key, "putenv"))
+ {
+ /* Change the session's environment to be used for the
+ Pinentry. Valid values are:
+ <NAME> Delete envvar NAME
+ <KEY>= Set envvar NAME to the empty string
+ <KEY>=<VALUE> Set envvar NAME to VALUE
+ */
+ err = session_env_putenv (opt.session_env, value);
+ }
+ else if (!strcmp (key, "display"))
+ {
+ err = session_env_setenv (opt.session_env, "DISPLAY", value);
+ }
+ else if (!strcmp (key, "ttyname"))
+ {
+ err = session_env_setenv (opt.session_env, "GPG_TTY", value);
+ }
+ else if (!strcmp (key, "ttytype"))
+ {
+ err = session_env_setenv (opt.session_env, "TERM", value);
+ }
+ else if (!strcmp (key, "lc-ctype"))
+ {
+ xfree (opt.lc_ctype);
+ opt.lc_ctype = xtrystrdup (value);
+ if (!opt.lc_ctype)
+ err = gpg_error_from_syserror ();
+ }
+ else if (!strcmp (key, "lc-messages"))
+ {
+ xfree (opt.lc_messages);
+ opt.lc_messages = xtrystrdup (value);
+ if (!opt.lc_messages)
+ err = gpg_error_from_syserror ();
+ }
+ else if (!strcmp (key, "xauthority"))
+ {
+ err = session_env_setenv (opt.session_env, "XAUTHORITY", value);
+ }
+ else if (!strcmp (key, "pinentry-user-data"))
+ {
+ err = session_env_setenv (opt.session_env, "PINENTRY_USER_DATA", value);
+ }
+ else if (!strcmp (key, "include-certs"))
+ {
+ int i = *value? atoi (value) : -1;
+ if (ctrl->include_certs < -2)
+ err = gpg_error (GPG_ERR_ASS_PARAMETER);
+ else
+ ctrl->include_certs = i;
+ }
+ else if (!strcmp (key, "list-mode"))
+ {
+ int i = *value? atoi (value) : 0;
+ if (!i || i == 1) /* default and mode 1 */
+ {
+ ctrl->server_local->list_internal = 1;
+ ctrl->server_local->list_external = 0;
+ }
+ else if (i == 2)
+ {
+ ctrl->server_local->list_internal = 0;
+ ctrl->server_local->list_external = 1;
+ }
+ else if (i == 3)
+ {
+ ctrl->server_local->list_internal = 1;
+ ctrl->server_local->list_external = 1;
+ }
+ else
+ err = gpg_error (GPG_ERR_ASS_PARAMETER);
+ }
+ else if (!strcmp (key, "list-to-output"))
+ {
+ int i = *value? atoi (value) : 0;
+ ctrl->server_local->list_to_output = i;
+ }
+ else if (!strcmp (key, "with-validation"))
+ {
+ int i = *value? atoi (value) : 0;
+ ctrl->with_validation = i;
+ }
+ else if (!strcmp (key, "validation-model"))
+ {
+ int i = gpgsm_parse_validation_model (value);
+ if ( i >= 0 && i <= 1 )
+ ctrl->validation_model = i;
+ else
+ err = gpg_error (GPG_ERR_ASS_PARAMETER);
+ }
+ else if (!strcmp (key, "with-key-data"))
+ {
+ opt.with_key_data = 1;
+ }
+ else if (!strcmp (key, "enable-audit-log"))
+ {
+ int i = *value? atoi (value) : 0;
+ ctrl->server_local->enable_audit_log = i;
+ }
+ else if (!strcmp (key, "allow-pinentry-notify"))
+ {
+ ctrl->server_local->allow_pinentry_notify = 1;
+ }
+ else if (!strcmp (key, "with-ephemeral-keys"))
+ {
+ int i = *value? atoi (value) : 0;
+ ctrl->with_ephemeral_keys = i;
+ }
+ else if (!strcmp (key, "no-encrypt-to"))
+ {
+ ctrl->server_local->no_encrypt_to = 1;
+ }
+ else
+ err = gpg_error (GPG_ERR_UNKNOWN_OPTION);
+
+ return err;
+}
+
+
+static gpg_error_t
+reset_notify (assuan_context_t ctx, char *line)
+{
+ ctrl_t ctrl = assuan_get_pointer (ctx);
+
+ (void) line;
+
+ gpgsm_release_certlist (ctrl->server_local->recplist);
+ gpgsm_release_certlist (ctrl->server_local->signerlist);
+ ctrl->server_local->recplist = NULL;
+ ctrl->server_local->signerlist = NULL;
+ close_message_fd (ctrl);
+ assuan_close_input_fd (ctx);
+ assuan_close_output_fd (ctx);
+ return 0;
+}
+
+
+static gpg_error_t
+input_notify (assuan_context_t ctx, char *line)
+{
+ ctrl_t ctrl = assuan_get_pointer (ctx);
+
+ ctrl->autodetect_encoding = 0;
+ ctrl->is_pem = 0;
+ ctrl->is_base64 = 0;
+ if (strstr (line, "--armor"))
+ ctrl->is_pem = 1;
+ else if (strstr (line, "--base64"))
+ ctrl->is_base64 = 1;
+ else if (strstr (line, "--binary"))
+ ;
+ else
+ ctrl->autodetect_encoding = 1;
+ return 0;
+}
+
+static gpg_error_t
+output_notify (assuan_context_t ctx, char *line)
+{
+ ctrl_t ctrl = assuan_get_pointer (ctx);
+
+ ctrl->create_pem = 0;
+ ctrl->create_base64 = 0;
+ if (strstr (line, "--armor"))
+ ctrl->create_pem = 1;
+ else if (strstr (line, "--base64"))
+ ctrl->create_base64 = 1; /* just the raw output */
+ return 0;
+}
+
+
+static const char hlp_recipient[] =
+ "RECIPIENT <userID>\n"
+ "\n"
+ "Set the recipient for the encryption. USERID shall be the\n"
+ "internal representation of the key; the server may accept any other\n"
+ "way of specification [we will support this]. If this is a valid and\n"
+ "trusted recipient the server does respond with OK, otherwise the\n"
+ "return is an ERR with the reason why the recipient can't be used,\n"
+ "the encryption will then not be done for this recipient. If the\n"
+ "policy is not to encrypt at all if not all recipients are valid, the\n"
+ "client has to take care of this. All RECIPIENT commands are\n"
+ "cumulative until a RESET or an successful ENCRYPT command.";
+static gpg_error_t
+cmd_recipient (assuan_context_t ctx, char *line)
+{
+ ctrl_t ctrl = assuan_get_pointer (ctx);
+ int rc;
+
+ if (!ctrl->audit)
+ rc = start_audit_session (ctrl);
+ else
+ rc = 0;
+
+ if (!rc)
+ rc = gpgsm_add_to_certlist (ctrl, line, 0,
+ &ctrl->server_local->recplist, 0);
+ if (rc)
+ {
+ gpgsm_status2 (ctrl, STATUS_INV_RECP,
+ get_inv_recpsgnr_code (rc), line, NULL);
+ }
+
+ return rc;
+}
+
+
+static const char hlp_signer[] =
+ "SIGNER <userID>\n"
+ "\n"
+ "Set the signer's keys for the signature creation. USERID should\n"
+ "be the internal representation of the key; the server may accept any\n"
+ "other way of specification [we will support this]. If this is a\n"
+ "valid and usable signing key the server does respond with OK,\n"
+ "otherwise it returns an ERR with the reason why the key can't be\n"
+ "used, the signing will then not be done for this key. If the policy\n"
+ "is not to sign at all if not all signer keys are valid, the client\n"
+ "has to take care of this. All SIGNER commands are cumulative until\n"
+ "a RESET but they are *not* reset by an SIGN command becuase it can\n"
+ "be expected that set of signers are used for more than one sign\n"
+ "operation.";
+static gpg_error_t
+cmd_signer (assuan_context_t ctx, char *line)
+{
+ ctrl_t ctrl = assuan_get_pointer (ctx);
+ int rc;
+
+ rc = gpgsm_add_to_certlist (ctrl, line, 1,
+ &ctrl->server_local->signerlist, 0);
+ if (rc)
+ {
+ gpgsm_status2 (ctrl, STATUS_INV_SGNR,
+ get_inv_recpsgnr_code (rc), line, NULL);
+ /* For compatibiliy reasons we also issue the old code after the
+ new one. */
+ gpgsm_status2 (ctrl, STATUS_INV_RECP,
+ get_inv_recpsgnr_code (rc), line, NULL);
+ }
+ return rc;
+}
+
+
+static const char hlp_encrypt[] =
+ "ENCRYPT \n"
+ "\n"
+ "Do the actual encryption process. Takes the plaintext from the INPUT\n"
+ "command, writes to the ciphertext to the file descriptor set with\n"
+ "the OUTPUT command, take the recipients form all the recipients set\n"
+ "so far. If this command fails the clients should try to delete all\n"
+ "output currently done or otherwise mark it as invalid. GPGSM does\n"
+ "ensure that there won't be any security problem with leftover data\n"
+ "on the output in this case.\n"
+ "\n"
+ "This command should in general not fail, as all necessary checks\n"
+ "have been done while setting the recipients. The input and output\n"
+ "pipes are closed.";
+static gpg_error_t
+cmd_encrypt (assuan_context_t ctx, char *line)
+{
+ ctrl_t ctrl = assuan_get_pointer (ctx);
+ certlist_t cl;
+ int inp_fd, out_fd;
+ FILE *out_fp;
+ int rc;
+
+ (void)line;
+
+ inp_fd = translate_sys2libc_fd (assuan_get_input_fd (ctx), 0);
+ if (inp_fd == -1)
+ return set_error (GPG_ERR_ASS_NO_INPUT, NULL);
+ out_fd = translate_sys2libc_fd (assuan_get_output_fd (ctx), 1);
+ if (out_fd == -1)
+ return set_error (GPG_ERR_ASS_NO_OUTPUT, NULL);
+
+ out_fp = fdopen (dup (out_fd), "w");
+ if (!out_fp)
+ return set_error (GPG_ERR_ASS_GENERAL, "fdopen() failed");
+
+ /* Now add all encrypt-to marked recipients from the default
+ list. */
+ rc = 0;
+ if (!opt.no_encrypt_to && !ctrl->server_local->no_encrypt_to)
+ {
+ for (cl=ctrl->server_local->default_recplist; !rc && cl; cl = cl->next)
+ if (cl->is_encrypt_to)
+ rc = gpgsm_add_cert_to_certlist (ctrl, cl->cert,
+ &ctrl->server_local->recplist, 1);
+ }
+ if (!rc)
+ rc = ctrl->audit? 0 : start_audit_session (ctrl);
+ if (!rc)
+ rc = gpgsm_encrypt (assuan_get_pointer (ctx),
+ ctrl->server_local->recplist,
+ inp_fd, out_fp);
+ fclose (out_fp);
+
+ gpgsm_release_certlist (ctrl->server_local->recplist);
+ ctrl->server_local->recplist = NULL;
+ /* Close and reset the fd */
+ close_message_fd (ctrl);
+ assuan_close_input_fd (ctx);
+ assuan_close_output_fd (ctx);
+ return rc;
+}
+
+
+static const char hlp_decrypt[] =
+ "DECRYPT\n"
+ "\n"
+ "This performs the decrypt operation after doing some check on the\n"
+ "internal state. (e.g. that only needed data has been set). Because\n"
+ "it utilizes the GPG-Agent for the session key decryption, there is\n"
+ "no need to ask the client for a protecting passphrase - GPG-Agent\n"
+ "does take care of this by requesting this from the user.";
+static gpg_error_t
+cmd_decrypt (assuan_context_t ctx, char *line)
+{
+ ctrl_t ctrl = assuan_get_pointer (ctx);
+ int inp_fd, out_fd;
+ FILE *out_fp;
+ int rc;
+
+ (void)line;
+
+ inp_fd = translate_sys2libc_fd (assuan_get_input_fd (ctx), 0);
+ if (inp_fd == -1)
+ return set_error (GPG_ERR_ASS_NO_INPUT, NULL);
+ out_fd = translate_sys2libc_fd (assuan_get_output_fd (ctx), 1);
+ if (out_fd == -1)
+ return set_error (GPG_ERR_ASS_NO_OUTPUT, NULL);
+
+ out_fp = fdopen (dup(out_fd), "w");
+ if (!out_fp)
+ return set_error (GPG_ERR_ASS_GENERAL, "fdopen() failed");
+
+ rc = start_audit_session (ctrl);
+ if (!rc)
+ rc = gpgsm_decrypt (ctrl, inp_fd, out_fp);
+ fclose (out_fp);
+
+ /* close and reset the fd */
+ close_message_fd (ctrl);
+ assuan_close_input_fd (ctx);
+ assuan_close_output_fd (ctx);
+
+ return rc;
+}
+
+
+static const char hlp_verify[] =
+ "VERIFY\n"
+ "\n"
+ "This does a verify operation on the message send to the input FD.\n"
+ "The result is written out using status lines. If an output FD was\n"
+ "given, the signed text will be written to that.\n"
+ "\n"
+ "If the signature is a detached one, the server will inquire about\n"
+ "the signed material and the client must provide it.";
+static gpg_error_t
+cmd_verify (assuan_context_t ctx, char *line)
+{
+ int rc;
+ ctrl_t ctrl = assuan_get_pointer (ctx);
+ int fd = translate_sys2libc_fd (assuan_get_input_fd (ctx), 0);
+ int out_fd = translate_sys2libc_fd (assuan_get_output_fd (ctx), 1);
+ FILE *out_fp = NULL;
+
+ (void)line;
+
+ if (fd == -1)
+ return set_error (GPG_ERR_ASS_NO_INPUT, NULL);
+
+ if (out_fd != -1)
+ {
+ out_fp = fdopen ( dup(out_fd), "w");
+ if (!out_fp)
+ return set_error (GPG_ERR_ASS_GENERAL, "fdopen() failed");
+ }
+
+ rc = start_audit_session (ctrl);
+ if (!rc)
+ rc = gpgsm_verify (assuan_get_pointer (ctx), fd,
+ ctrl->server_local->message_fd, out_fp);
+ if (out_fp)
+ fclose (out_fp);
+
+ /* close and reset the fd */
+ close_message_fd (ctrl);
+ assuan_close_input_fd (ctx);
+ assuan_close_output_fd (ctx);
+
+ return rc;
+}
+
+
+static const char hlp_sign[] =
+ "SIGN [--detached]\n"
+ "\n"
+ "Sign the data set with the INPUT command and write it to the sink\n"
+ "set by OUTPUT. With \"--detached\", a detached signature is\n"
+ "created (surprise).";
+static gpg_error_t
+cmd_sign (assuan_context_t ctx, char *line)
+{
+ ctrl_t ctrl = assuan_get_pointer (ctx);
+ int inp_fd, out_fd;
+ FILE *out_fp;
+ int detached;
+ int rc;
+
+ inp_fd = translate_sys2libc_fd (assuan_get_input_fd (ctx), 0);
+ if (inp_fd == -1)
+ return set_error (GPG_ERR_ASS_NO_INPUT, NULL);
+ out_fd = translate_sys2libc_fd (assuan_get_output_fd (ctx), 1);
+ if (out_fd == -1)
+ return set_error (GPG_ERR_ASS_NO_OUTPUT, NULL);
+
+ detached = has_option (line, "--detached");
+
+ out_fp = fdopen ( dup(out_fd), "w");
+ if (!out_fp)
+ return set_error (GPG_ERR_ASS_GENERAL, "fdopen() failed");
+
+ rc = start_audit_session (ctrl);
+ if (!rc)
+ rc = gpgsm_sign (assuan_get_pointer (ctx), ctrl->server_local->signerlist,
+ inp_fd, detached, out_fp);
+ fclose (out_fp);
+
+ /* close and reset the fd */
+ close_message_fd (ctrl);
+ assuan_close_input_fd (ctx);
+ assuan_close_output_fd (ctx);
+
+ return rc;
+}
+
+
+static const char hlp_import[] =
+ "IMPORT [--re-import]\n"
+ "\n"
+ "Import the certificates read form the input-fd, return status\n"
+ "message for each imported one. The import checks the validity of\n"
+ "the certificate but not of the entire chain. It is possible to\n"
+ "import expired certificates.\n"
+ "\n"
+ "With the option --re-import the input data is expected to a be a LF\n"
+ "separated list of fingerprints. The command will re-import these\n"
+ "certificates, meaning that they are made permanent by removing\n"
+ "their ephemeral flag.";
+static gpg_error_t
+cmd_import (assuan_context_t ctx, char *line)
+{
+ ctrl_t ctrl = assuan_get_pointer (ctx);
+ int rc;
+ int fd = translate_sys2libc_fd (assuan_get_input_fd (ctx), 0);
+ int reimport = has_option (line, "--re-import");
+
+ (void)line;
+
+ if (fd == -1)
+ return set_error (GPG_ERR_ASS_NO_INPUT, NULL);
+
+ rc = gpgsm_import (assuan_get_pointer (ctx), fd, reimport);
+
+ /* close and reset the fd */
+ close_message_fd (ctrl);
+ assuan_close_input_fd (ctx);
+ assuan_close_output_fd (ctx);
+
+ return rc;
+}
+
+
+static const char hlp_export[] =
+ "EXPORT [--data [--armor|--base64]] [--] <pattern>\n"
+ "\n"
+ "Export the certificates selected by PATTERN. With --data the output\n"
+ "is returned using Assuan D lines; the default is to use the sink given\n"
+ "by the last \"OUTPUT\" command. The options --armor or --base64 encode \n"
+ "the output using the PEM respective a plain base-64 format; the default\n"
+ "is a binary format which is only suitable for a single certificate.";
+static gpg_error_t
+cmd_export (assuan_context_t ctx, char *line)
+{
+ ctrl_t ctrl = assuan_get_pointer (ctx);
+ char *p;
+ strlist_t list, sl;
+ int use_data;
+
+ use_data = has_option (line, "--data");
+
+ if (use_data)
+ {
+ /* We need to override any possible setting done by an OUTPUT command. */
+ ctrl->create_pem = has_option (line, "--armor");
+ ctrl->create_base64 = has_option (line, "--base64");
+ }
+
+ line = skip_options (line);
+
+ /* Break the line down into an strlist_t. */
+ list = NULL;
+ for (p=line; *p; line = p)
+ {
+ while (*p && *p != ' ')
+ p++;
+ if (*p)
+ *p++ = 0;
+ if (*line)
+ {
+ sl = xtrymalloc (sizeof *sl + strlen (line));
+ if (!sl)
+ {
+ free_strlist (list);
+ return out_of_core ();
+ }
+ sl->flags = 0;
+ strcpy_escaped_plus (sl->d, line);
+ sl->next = list;
+ list = sl;
+ }
+ }
+
+ if (use_data)
+ {
+ estream_t stream;
+
+ stream = es_fopencookie (ctx, "w", data_line_cookie_functions);
+ if (!stream)
+ {
+ free_strlist (list);
+ return set_error (GPG_ERR_ASS_GENERAL,
+ "error setting up a data stream");
+ }
+ gpgsm_export (ctrl, list, NULL, stream);
+ es_fclose (stream);
+ }
+ else
+ {
+ int fd = translate_sys2libc_fd (assuan_get_output_fd (ctx), 1);
+ FILE *out_fp;
+
+ if (fd == -1)
+ {
+ free_strlist (list);
+ return set_error (GPG_ERR_ASS_NO_OUTPUT, NULL);
+ }
+ out_fp = fdopen ( dup(fd), "w");
+ if (!out_fp)
+ {
+ free_strlist (list);
+ return set_error (GPG_ERR_ASS_GENERAL, "fdopen() failed");
+ }
+
+ gpgsm_export (ctrl, list, out_fp, NULL);
+ fclose (out_fp);
+ }
+
+ free_strlist (list);
+ /* Close and reset the fds. */
+ close_message_fd (ctrl);
+ assuan_close_input_fd (ctx);
+ assuan_close_output_fd (ctx);
+ return 0;
+}
+
+
+
+static const char hlp_delkeys[] =
+ "DELKEYS <patterns>\n"
+ "\n"
+ "Delete the certificates specified by PATTERNS. Each pattern shall be\n"
+ "a percent-plus escaped certificate specification. Usually a\n"
+ "fingerprint will be used for this.";
+static gpg_error_t
+cmd_delkeys (assuan_context_t ctx, char *line)
+{
+ ctrl_t ctrl = assuan_get_pointer (ctx);
+ char *p;
+ strlist_t list, sl;
+ int rc;
+
+ /* break the line down into an strlist_t */
+ list = NULL;
+ for (p=line; *p; line = p)
+ {
+ while (*p && *p != ' ')
+ p++;
+ if (*p)
+ *p++ = 0;
+ if (*line)
+ {
+ sl = xtrymalloc (sizeof *sl + strlen (line));
+ if (!sl)
+ {
+ free_strlist (list);
+ return out_of_core ();
+ }
+ sl->flags = 0;
+ strcpy_escaped_plus (sl->d, line);
+ sl->next = list;
+ list = sl;
+ }
+ }
+
+ rc = gpgsm_delete (ctrl, list);
+ free_strlist (list);
+
+ /* close and reset the fd */
+ close_message_fd (ctrl);
+ assuan_close_input_fd (ctx);
+ assuan_close_output_fd (ctx);
+
+ return rc;
+}
+
+
+
+static const char hlp_output[] =
+ "OUTPUT FD[=<n>]\n"
+ "\n"
+ "Set the file descriptor to write the output data to N. If N is not\n"
+ "given and the operating system supports file descriptor passing, the\n"
+ "file descriptor currently in flight will be used. See also the\n"
+ "\"INPUT\" and \"MESSAGE\" commands.";
+static const char hlp_input[] =
+ "INPUT FD[=<n>]\n"
+ "\n"
+ "Set the file descriptor to read the input data to N. If N is not\n"
+ "given and the operating system supports file descriptor passing, the\n"
+ "file descriptor currently in flight will be used. See also the\n"
+ "\"MESSAGE\" and \"OUTPUT\" commands.";
+static const char hlp_message[] =
+ "MESSAGE FD[=<n>]\n"
+ "\n"
+ "Set the file descriptor to read the message for a detached\n"
+ "signatures to N. If N is not given and the operating system\n"
+ "supports file descriptor passing, the file descriptor currently in\n"
+ "flight will be used. See also the \"INPUT\" and \"OUTPUT\" commands.";
+static gpg_error_t
+cmd_message (assuan_context_t ctx, char *line)
+{
+ int rc;
+ gnupg_fd_t sysfd;
+ int fd;
+ ctrl_t ctrl = assuan_get_pointer (ctx);
+
+ rc = assuan_command_parse_fd (ctx, line, &sysfd);
+ if (rc)
+ return rc;
+ fd = translate_sys2libc_fd (sysfd, 0);
+ if (fd == -1)
+ return set_error (GPG_ERR_ASS_NO_INPUT, NULL);
+ ctrl->server_local->message_fd = fd;
+ return 0;
+}
+
+
+
+static const char hlp_listkeys[] =
+ "LISTKEYS [<patterns>]\n"
+ "LISTSECRETKEYS [<patterns>]\n"
+ "DUMPKEYS [<patterns>]\n"
+ "DUMPSECRETKEYS [<patterns>]\n"
+ "\n"
+ "List all certificates or only those specified by PATTERNS. Each\n"
+ "pattern shall be a percent-plus escaped certificate specification.\n"
+ "The \"SECRET\" versions of the command filter the output to include\n"
+ "only certificates where the secret key is available or a corresponding\n"
+ "smartcard has been registered. The \"DUMP\" versions of the command\n"
+ "are only useful for debugging. The output format is a percent escaped\n"
+ "colon delimited listing as described in the manual.\n"
+ "\n"
+ "These \"OPTION\" command keys effect the output::\n"
+ "\n"
+ " \"list-mode\" set to 0: List only local certificates (default).\n"
+ " 1: Ditto.\n"
+ " 2: List only external certificates.\n"
+ " 3: List local and external certificates.\n"
+ "\n"
+ " \"with-validation\" set to true: Validate each certificate.\n"
+ "\n"
+ " \"with-ephemeral-key\" set to true: Always include ephemeral\n"
+ " certificates.\n"
+ "\n"
+ " \"list-to-output\" set to true: Write output to the file descriptor\n"
+ " given by the last \"OUTPUT\" command.";
+static int
+do_listkeys (assuan_context_t ctx, char *line, int mode)
+{
+ ctrl_t ctrl = assuan_get_pointer (ctx);
+ estream_t fp;
+ char *p;
+ strlist_t list, sl;
+ unsigned int listmode;
+ gpg_error_t err;
+
+ /* Break the line down into an strlist. */
+ list = NULL;
+ for (p=line; *p; line = p)
+ {
+ while (*p && *p != ' ')
+ p++;
+ if (*p)
+ *p++ = 0;
+ if (*line)
+ {
+ sl = xtrymalloc (sizeof *sl + strlen (line));
+ if (!sl)
+ {
+ free_strlist (list);
+ return out_of_core ();
+ }
+ sl->flags = 0;
+ strcpy_escaped_plus (sl->d, line);
+ sl->next = list;
+ list = sl;
+ }
+ }
+
+ if (ctrl->server_local->list_to_output)
+ {
+ int outfd = translate_sys2libc_fd (assuan_get_output_fd (ctx), 1);
+
+ if ( outfd == -1 )
+ return set_error (GPG_ERR_ASS_NO_OUTPUT, NULL);
+ fp = es_fdopen ( dup (outfd), "w");
+ if (!fp)
+ return set_error (GPG_ERR_ASS_GENERAL, "es_fdopen() failed");
+ }
+ else
+ {
+ fp = es_fopencookie (ctx, "w", data_line_cookie_functions);
+ if (!fp)
+ return set_error (GPG_ERR_ASS_GENERAL,
+ "error setting up a data stream");
+ }
+
+ ctrl->with_colons = 1;
+ listmode = mode;
+ if (ctrl->server_local->list_internal)
+ listmode |= (1<<6);
+ if (ctrl->server_local->list_external)
+ listmode |= (1<<7);
+ err = gpgsm_list_keys (assuan_get_pointer (ctx), list, fp, listmode);
+ free_strlist (list);
+ es_fclose (fp);
+ if (ctrl->server_local->list_to_output)
+ assuan_close_output_fd (ctx);
+ return err;
+}
+
+static gpg_error_t
+cmd_listkeys (assuan_context_t ctx, char *line)
+{
+ return do_listkeys (ctx, line, 3);
+}
+
+static gpg_error_t
+cmd_dumpkeys (assuan_context_t ctx, char *line)
+{
+ return do_listkeys (ctx, line, 259);
+}
+
+static gpg_error_t
+cmd_listsecretkeys (assuan_context_t ctx, char *line)
+{
+ return do_listkeys (ctx, line, 2);
+}
+
+static gpg_error_t
+cmd_dumpsecretkeys (assuan_context_t ctx, char *line)
+{
+ return do_listkeys (ctx, line, 258);
+}
+
+
+
+static const char hlp_genkey[] =
+ "GENKEY\n"
+ "\n"
+ "Read the parameters in native format from the input fd and write a\n"
+ "certificate request to the output.";
+static gpg_error_t
+cmd_genkey (assuan_context_t ctx, char *line)
+{
+ ctrl_t ctrl = assuan_get_pointer (ctx);
+ int inp_fd, out_fd;
+ FILE *out_fp;
+ int rc;
+ estream_t in_stream;
+
+ (void)line;
+
+ inp_fd = translate_sys2libc_fd (assuan_get_input_fd (ctx), 0);
+ if (inp_fd == -1)
+ return set_error (GPG_ERR_ASS_NO_INPUT, NULL);
+ out_fd = translate_sys2libc_fd (assuan_get_output_fd (ctx), 1);
+ if (out_fd == -1)
+ return set_error (GPG_ERR_ASS_NO_OUTPUT, NULL);
+
+ in_stream = es_fdopen_nc (inp_fd, "r");
+ if (!in_stream)
+ return set_error (GPG_ERR_ASS_GENERAL, "es_fdopen failed");
+
+ out_fp = fdopen ( dup(out_fd), "w");
+ if (!out_fp)
+ {
+ es_fclose (in_stream);
+ return set_error (GPG_ERR_ASS_GENERAL, "fdopen() failed");
+ }
+ rc = gpgsm_genkey (ctrl, in_stream, out_fp);
+ fclose (out_fp);
+
+ /* close and reset the fds */
+ assuan_close_input_fd (ctx);
+ assuan_close_output_fd (ctx);
+
+ return rc;
+}
+
+
+
+static const char hlp_getauditlog[] =
+ "GETAUDITLOG [--data] [--html]\n"
+ "\n"
+ "If --data is used, the output is send using D-lines and not to the\n"
+ "file descriptor given by an OUTPUT command.\n"
+ "\n"
+ "If --html is used the output is formated as an XHTML block. This is\n"
+ "designed to be incorporated into a HTML document.";
+static gpg_error_t
+cmd_getauditlog (assuan_context_t ctx, char *line)
+{
+ ctrl_t ctrl = assuan_get_pointer (ctx);
+ int out_fd;
+ estream_t out_stream;
+ int opt_data, opt_html;
+ int rc;
+
+ opt_data = has_option (line, "--data");
+ opt_html = has_option (line, "--html");
+ line = skip_options (line);
+
+ if (!ctrl->audit)
+ return gpg_error (GPG_ERR_NO_DATA);
+
+ if (opt_data)
+ {
+ out_stream = es_fopencookie (ctx, "w", data_line_cookie_functions);
+ if (!out_stream)
+ return set_error (GPG_ERR_ASS_GENERAL,
+ "error setting up a data stream");
+ }
+ else
+ {
+ out_fd = translate_sys2libc_fd (assuan_get_output_fd (ctx), 1);
+ if (out_fd == -1)
+ return set_error (GPG_ERR_ASS_NO_OUTPUT, NULL);
+
+ out_stream = es_fdopen_nc ( dup (out_fd), "w");
+ if (!out_stream)
+ {
+ return set_error (GPG_ERR_ASS_GENERAL, "es_fdopen() failed");
+ }
+ }
+
+ audit_print_result (ctrl->audit, out_stream, opt_html);
+ rc = 0;
+
+ es_fclose (out_stream);
+
+ /* Close and reset the fd. */
+ if (!opt_data)
+ assuan_close_output_fd (ctx);
+ return rc;
+}
+
+
+static const char hlp_getinfo[] =
+ "GETINFO <what>\n"
+ "\n"
+ "Multipurpose function to return a variety of information.\n"
+ "Supported values for WHAT are:\n"
+ "\n"
+ " version - Return the version of the program.\n"
+ " pid - Return the process id of the server.\n"
+ " agent-check - Return success if the agent is running.\n"
+ " cmd_has_option CMD OPT\n"
+ " - Returns OK if the command CMD implements the option OPT.";
+static gpg_error_t
+cmd_getinfo (assuan_context_t ctx, char *line)
+{
+ int rc = 0;
+
+ if (!strcmp (line, "version"))
+ {
+ const char *s = VERSION;
+ rc = assuan_send_data (ctx, s, strlen (s));
+ }
+ else if (!strcmp (line, "pid"))
+ {
+ char numbuf[50];
+
+ snprintf (numbuf, sizeof numbuf, "%lu", (unsigned long)getpid ());
+ rc = assuan_send_data (ctx, numbuf, strlen (numbuf));
+ }
+ else if (!strcmp (line, "agent-check"))
+ {
+ ctrl_t ctrl = assuan_get_pointer (ctx);
+ rc = gpgsm_agent_send_nop (ctrl);
+ }
+ else if (!strncmp (line, "cmd_has_option", 14)
+ && (line[14] == ' ' || line[14] == '\t' || !line[14]))
+ {
+ char *cmd, *cmdopt;
+ line += 14;
+ while (*line == ' ' || *line == '\t')
+ line++;
+ if (!*line)
+ rc = gpg_error (GPG_ERR_MISSING_VALUE);
+ else
+ {
+ cmd = line;
+ while (*line && (*line != ' ' && *line != '\t'))
+ line++;
+ if (!*line)
+ rc = gpg_error (GPG_ERR_MISSING_VALUE);
+ else
+ {
+ *line++ = 0;
+ while (*line == ' ' || *line == '\t')
+ line++;
+ if (!*line)
+ rc = gpg_error (GPG_ERR_MISSING_VALUE);
+ else
+ {
+ cmdopt = line;
+ if (!command_has_option (cmd, cmdopt))
+ rc = gpg_error (GPG_ERR_GENERAL);
+ }
+ }
+ }
+ }
+ else
+ rc = set_error (GPG_ERR_ASS_PARAMETER, "unknown value for WHAT");
+
+ return rc;
+}
+
+
+
+static const char hlp_passwd[] =
+ "PASSWD <userID>\n"
+ "\n"
+ "Change the passphrase of the secret key for USERID.";
+static gpg_error_t
+cmd_passwd (assuan_context_t ctx, char *line)
+{
+ ctrl_t ctrl = assuan_get_pointer (ctx);
+ gpg_error_t err;
+ ksba_cert_t cert = NULL;
+ char *grip = NULL;
+
+ line = skip_options (line);
+
+ err = gpgsm_find_cert (line, NULL, &cert);
+ if (err)
+ ;
+ else if (!(grip = gpgsm_get_keygrip_hexstring (cert)))
+ err = gpg_error (GPG_ERR_INTERNAL);
+ else
+ {
+ char *desc = gpgsm_format_keydesc (cert);
+ err = gpgsm_agent_passwd (ctrl, grip, desc);
+ xfree (desc);
+ }
+
+ xfree (grip);
+ ksba_cert_release (cert);
+
+ return err;
+}
+
+
+
+
+
+/* Return true if the command CMD implements the option OPT. */
+static int
+command_has_option (const char *cmd, const char *cmdopt)
+{
+ if (!strcmp (cmd, "IMPORT"))
+ {
+ if (!strcmp (cmdopt, "re-import"))
+ return 1;
+ }
+
+ return 0;
+}
+
+
+/* Tell the assuan library about our commands */
+static int
+register_commands (assuan_context_t ctx)
+{
+ static struct {
+ const char *name;
+ assuan_handler_t handler;
+ const char * const help;
+ } table[] = {
+ { "RECIPIENT", cmd_recipient, hlp_recipient },
+ { "SIGNER", cmd_signer, hlp_signer },
+ { "ENCRYPT", cmd_encrypt, hlp_encrypt },
+ { "DECRYPT", cmd_decrypt, hlp_decrypt },
+ { "VERIFY", cmd_verify, hlp_verify },
+ { "SIGN", cmd_sign, hlp_sign },
+ { "IMPORT", cmd_import, hlp_import },
+ { "EXPORT", cmd_export, hlp_export },
+ { "INPUT", NULL, hlp_input },
+ { "OUTPUT", NULL, hlp_output },
+ { "MESSAGE", cmd_message, hlp_message },
+ { "LISTKEYS", cmd_listkeys, hlp_listkeys },
+ { "DUMPKEYS", cmd_dumpkeys, hlp_listkeys },
+ { "LISTSECRETKEYS",cmd_listsecretkeys, hlp_listkeys },
+ { "DUMPSECRETKEYS",cmd_dumpsecretkeys, hlp_listkeys },
+ { "GENKEY", cmd_genkey, hlp_genkey },
+ { "DELKEYS", cmd_delkeys, hlp_delkeys },
+ { "GETAUDITLOG", cmd_getauditlog, hlp_getauditlog },
+ { "GETINFO", cmd_getinfo, hlp_getinfo },
+ { "PASSWD", cmd_passwd, hlp_passwd },
+ { NULL }
+ };
+ int i, rc;
+
+ for (i=0; table[i].name; i++)
+ {
+ rc = assuan_register_command (ctx, table[i].name, table[i].handler,
+ table[i].help);
+ if (rc)
+ return rc;
+ }
+ return 0;
+}
+
+/* Startup the server. DEFAULT_RECPLIST is the list of recipients as
+ set from the command line or config file. We only require those
+ marked as encrypt-to. */
+void
+gpgsm_server (certlist_t default_recplist)
+{
+ int rc;
+ assuan_fd_t filedes[2];
+ assuan_context_t ctx;
+ struct server_control_s ctrl;
+ static const char hello[] = ("GNU Privacy Guard's S/M server "
+ VERSION " ready");
+
+ memset (&ctrl, 0, sizeof ctrl);
+ gpgsm_init_default_ctrl (&ctrl);
+
+ /* We use a pipe based server so that we can work from scripts.
+ assuan_init_pipe_server will automagically detect when we are
+ called with a socketpair and ignore FIELDES in this case. */
+ filedes[0] = assuan_fdopen (0);
+ filedes[1] = assuan_fdopen (1);
+ rc = assuan_new (&ctx);
+ if (rc)
+ {
+ log_error ("failed to allocate assuan context: %s\n",
+ gpg_strerror (rc));
+ gpgsm_exit (2);
+ }
+
+ rc = assuan_init_pipe_server (ctx, filedes);
+ if (rc)
+ {
+ log_error ("failed to initialize the server: %s\n",
+ gpg_strerror (rc));
+ gpgsm_exit (2);
+ }
+ rc = register_commands (ctx);
+ if (rc)
+ {
+ log_error ("failed to the register commands with Assuan: %s\n",
+ gpg_strerror(rc));
+ gpgsm_exit (2);
+ }
+ if (opt.verbose || opt.debug)
+ {
+ char *tmp = NULL;
+ const char *s1 = getenv ("GPG_AGENT_INFO");
+ const char *s2 = getenv ("DIRMNGR_INFO");
+
+ if (asprintf (&tmp,
+ "Home: %s\n"
+ "Config: %s\n"
+ "AgentInfo: %s\n"
+ "DirmngrInfo: %s\n"
+ "%s",
+ opt.homedir,
+ opt.config_filename,
+ s1?s1:"[not set]",
+ s2?s2:"[not set]",
+ hello) > 0)
+ {
+ assuan_set_hello_line (ctx, tmp);
+ free (tmp);
+ }
+ }
+ else
+ assuan_set_hello_line (ctx, hello);
+
+ assuan_register_reset_notify (ctx, reset_notify);
+ assuan_register_input_notify (ctx, input_notify);
+ assuan_register_output_notify (ctx, output_notify);
+ assuan_register_option_handler (ctx, option_handler);
+
+ assuan_set_pointer (ctx, &ctrl);
+ ctrl.server_local = xcalloc (1, sizeof *ctrl.server_local);
+ ctrl.server_local->assuan_ctx = ctx;
+ ctrl.server_local->message_fd = -1;
+ ctrl.server_local->list_internal = 1;
+ ctrl.server_local->list_external = 0;
+ ctrl.server_local->default_recplist = default_recplist;
+
+ if (DBG_ASSUAN)
+ assuan_set_log_stream (ctx, log_get_stream ());
+
+ for (;;)
+ {
+ rc = assuan_accept (ctx);
+ if (rc == -1)
+ {
+ break;
+ }
+ else if (rc)
+ {
+ log_info ("Assuan accept problem: %s\n", gpg_strerror (rc));
+ break;
+ }
+
+ rc = assuan_process (ctx);
+ if (rc)
+ {
+ log_info ("Assuan processing failed: %s\n", gpg_strerror (rc));
+ continue;
+ }
+ }
+
+ gpgsm_release_certlist (ctrl.server_local->recplist);
+ ctrl.server_local->recplist = NULL;
+ gpgsm_release_certlist (ctrl.server_local->signerlist);
+ ctrl.server_local->signerlist = NULL;
+ xfree (ctrl.server_local);
+
+ audit_release (ctrl.audit);
+ ctrl.audit = NULL;
+
+ assuan_release (ctx);
+}
+
+
+
+gpg_error_t
+gpgsm_status2 (ctrl_t ctrl, int no, ...)
+{
+ gpg_error_t err = 0;
+ va_list arg_ptr;
+ const char *text;
+
+ va_start (arg_ptr, no);
+
+ if (ctrl->no_server && ctrl->status_fd == -1)
+ ; /* No status wanted. */
+ else if (ctrl->no_server)
+ {
+ if (!statusfp)
+ {
+ if (ctrl->status_fd == 1)
+ statusfp = stdout;
+ else if (ctrl->status_fd == 2)
+ statusfp = stderr;
+ else
+ statusfp = fdopen (ctrl->status_fd, "w");
+
+ if (!statusfp)
+ {
+ log_fatal ("can't open fd %d for status output: %s\n",
+ ctrl->status_fd, strerror(errno));
+ }
+ }
+
+ fputs ("[GNUPG:] ", statusfp);
+ fputs (get_status_string (no), statusfp);
+
+ while ( (text = va_arg (arg_ptr, const char*) ))
+ {
+ 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);
+ }
+ else
+ {
+ assuan_context_t ctx = ctrl->server_local->assuan_ctx;
+ char buf[950], *p;
+ size_t n;
+
+ p = buf;
+ n = 0;
+ while ( (text = va_arg (arg_ptr, const char *)) )
+ {
+ if (n)
+ {
+ *p++ = ' ';
+ n++;
+ }
+ for ( ; *text && n < DIM (buf)-2; n++)
+ *p++ = *text++;
+ }
+ *p = 0;
+ err = assuan_write_status (ctx, get_status_string (no), buf);
+ }
+
+ va_end (arg_ptr);
+ return err;
+}
+
+gpg_error_t
+gpgsm_status (ctrl_t ctrl, int no, const char *text)
+{
+ return gpgsm_status2 (ctrl, no, text, NULL);
+}
+
+gpg_error_t
+gpgsm_status_with_err_code (ctrl_t ctrl, int no, const char *text,
+ gpg_err_code_t ec)
+{
+ char buf[30];
+
+ sprintf (buf, "%u", (unsigned int)ec);
+ if (text)
+ return gpgsm_status2 (ctrl, no, text, buf, NULL);
+ else
+ return gpgsm_status2 (ctrl, no, buf, NULL);
+}
+
+
+/* Helper to notify the client about Pinentry events. Because that
+ might disturb some older clients, this is only done when enabled
+ via an option. Returns an gpg error code. */
+gpg_error_t
+gpgsm_proxy_pinentry_notify (ctrl_t ctrl, const unsigned char *line)
+{
+ if (!ctrl || !ctrl->server_local
+ || !ctrl->server_local->allow_pinentry_notify)
+ return 0;
+ return assuan_inquire (ctrl->server_local->assuan_ctx, line, NULL, NULL, 0);
+}
+
+
+
diff --git a/sm/sign.c b/sm/sign.c
new file mode 100644
index 0000000..fd7c4ff
--- /dev/null
+++ b/sm/sign.c
@@ -0,0 +1,780 @@
+/* sign.c - Sign a message
+ * Copyright (C) 2001, 2002, 2003, 2008 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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuPG is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <time.h>
+#include <assert.h>
+
+#include "gpgsm.h"
+#include <gcrypt.h>
+#include <ksba.h>
+
+#include "keydb.h"
+#include "i18n.h"
+
+
+/* Hash the data and return if something was hashed. Return -1 on error. */
+static int
+hash_data (int fd, gcry_md_hd_t md)
+{
+ FILE *fp;
+ char buffer[4096];
+ int nread;
+ int rc = 0;
+
+ fp = fdopen ( dup (fd), "rb");
+ if (!fp)
+ {
+ log_error ("fdopen(%d) failed: %s\n", fd, strerror (errno));
+ return -1;
+ }
+
+ do
+ {
+ nread = fread (buffer, 1, DIM(buffer), fp);
+ gcry_md_write (md, buffer, nread);
+ }
+ while (nread);
+ if (ferror (fp))
+ {
+ log_error ("read error on fd %d: %s\n", fd, strerror (errno));
+ rc = -1;
+ }
+ fclose (fp);
+ return rc;
+}
+
+static int
+hash_and_copy_data (int fd, gcry_md_hd_t md, ksba_writer_t writer)
+{
+ gpg_error_t err;
+ FILE *fp;
+ char buffer[4096];
+ int nread;
+ int rc = 0;
+ int any = 0;
+
+ fp = fdopen ( dup (fd), "rb");
+ if (!fp)
+ {
+ gpg_error_t tmperr = gpg_error (gpg_err_code_from_errno (errno));
+ log_error ("fdopen(%d) failed: %s\n", fd, strerror (errno));
+ return tmperr;
+ }
+
+ do
+ {
+ nread = fread (buffer, 1, DIM(buffer), fp);
+ if (nread)
+ {
+ any = 1;
+ gcry_md_write (md, buffer, nread);
+ err = ksba_writer_write_octet_string (writer, buffer, nread, 0);
+ if (err)
+ {
+ log_error ("write failed: %s\n", gpg_strerror (err));
+ rc = err;
+ }
+ }
+ }
+ while (nread && !rc);
+ if (ferror (fp))
+ {
+ rc = gpg_error (gpg_err_code_from_errno (errno));
+ log_error ("read error on fd %d: %s\n", fd, strerror (errno));
+ }
+ fclose (fp);
+ if (!any)
+ {
+ /* We can't allow to sign an empty message because it does not
+ make much sense and more seriously, ksba-cms_build has
+ already written the tag for data and now expects an octet
+ string but an octet string of zeize 0 is illegal. */
+ log_error ("cannot sign an empty message\n");
+ rc = gpg_error (GPG_ERR_NO_DATA);
+ }
+ if (!rc)
+ {
+ err = ksba_writer_write_octet_string (writer, NULL, 0, 1);
+ if (err)
+ {
+ log_error ("write failed: %s\n", gpg_strerror (err));
+ rc = err;
+ }
+ }
+
+ return rc;
+}
+
+
+/* Get the default certificate which is defined as the first
+ certificate capable of signing returned by the keyDB and has a
+ secret key available. */
+int
+gpgsm_get_default_cert (ctrl_t ctrl, ksba_cert_t *r_cert)
+{
+ KEYDB_HANDLE hd;
+ ksba_cert_t cert = NULL;
+ int rc;
+ char *p;
+
+ hd = keydb_new (0);
+ if (!hd)
+ return gpg_error (GPG_ERR_GENERAL);
+ rc = keydb_search_first (hd);
+ if (rc)
+ {
+ keydb_release (hd);
+ return rc;
+ }
+
+ do
+ {
+ rc = keydb_get_cert (hd, &cert);
+ if (rc)
+ {
+ log_error ("keydb_get_cert failed: %s\n", gpg_strerror (rc));
+ keydb_release (hd);
+ return rc;
+ }
+
+ if (!gpgsm_cert_use_sign_p (cert))
+ {
+ p = gpgsm_get_keygrip_hexstring (cert);
+ if (p)
+ {
+ if (!gpgsm_agent_havekey (ctrl, p))
+ {
+ xfree (p);
+ keydb_release (hd);
+ *r_cert = cert;
+ return 0; /* got it */
+ }
+ xfree (p);
+ }
+ }
+
+ ksba_cert_release (cert);
+ cert = NULL;
+ }
+ while (!(rc = keydb_search_next (hd)));
+ if (rc && rc != -1)
+ log_error ("keydb_search_next failed: %s\n", gpg_strerror (rc));
+
+ ksba_cert_release (cert);
+ keydb_release (hd);
+ return rc;
+}
+
+
+static ksba_cert_t
+get_default_signer (ctrl_t ctrl)
+{
+ KEYDB_SEARCH_DESC desc;
+ ksba_cert_t cert = NULL;
+ KEYDB_HANDLE kh = NULL;
+ int rc;
+
+ if (!opt.local_user)
+ {
+ rc = gpgsm_get_default_cert (ctrl, &cert);
+ if (rc)
+ {
+ if (rc != -1)
+ log_debug ("failed to find default certificate: %s\n",
+ gpg_strerror (rc));
+ return NULL;
+ }
+ return cert;
+ }
+
+ rc = keydb_classify_name (opt.local_user, &desc);
+ if (rc)
+ {
+ log_error ("failed to find default signer: %s\n", gpg_strerror (rc));
+ return NULL;
+ }
+
+ kh = keydb_new (0);
+ if (!kh)
+ return NULL;
+
+ rc = keydb_search (kh, &desc, 1);
+ if (rc)
+ {
+ log_debug ("failed to find default certificate: rc=%d\n", rc);
+ }
+ else
+ {
+ rc = keydb_get_cert (kh, &cert);
+ if (rc)
+ {
+ log_debug ("failed to get cert: rc=%d\n", rc);
+ }
+ }
+
+ keydb_release (kh);
+ return cert;
+}
+
+/* Depending on the options in CTRL add the certificate CERT as well as
+ other certificate up in the chain to the Root-CA to the CMS
+ object. */
+static int
+add_certificate_list (ctrl_t ctrl, ksba_cms_t cms, ksba_cert_t cert)
+{
+ gpg_error_t err;
+ int rc = 0;
+ ksba_cert_t next = NULL;
+ int n;
+ int not_root = 0;
+
+ ksba_cert_ref (cert);
+
+ n = ctrl->include_certs;
+ log_debug ("adding certificates at level %d\n", n);
+ if (n == -2)
+ {
+ not_root = 1;
+ n = -1;
+ }
+ if (n < 0 || n > 50)
+ n = 50; /* We better apply an upper bound */
+
+ /* First add my own certificate unless we don't want any certificate
+ included at all. */
+ if (n)
+ {
+ if (not_root && gpgsm_is_root_cert (cert))
+ err = 0;
+ else
+ err = ksba_cms_add_cert (cms, cert);
+ if (err)
+ goto ksba_failure;
+ if (n>0)
+ n--;
+ }
+ /* Walk the chain to include all other certificates. Note that a -1
+ used for N makes sure that there is no limit and all certs get
+ included. */
+ while ( n-- && !(rc = gpgsm_walk_cert_chain (ctrl, cert, &next)) )
+ {
+ if (not_root && gpgsm_is_root_cert (next))
+ err = 0;
+ else
+ err = ksba_cms_add_cert (cms, next);
+ ksba_cert_release (cert);
+ cert = next; next = NULL;
+ if (err)
+ goto ksba_failure;
+ }
+ ksba_cert_release (cert);
+
+ return rc == -1? 0: rc;
+
+ ksba_failure:
+ ksba_cert_release (cert);
+ log_error ("ksba_cms_add_cert failed: %s\n", gpg_strerror (err));
+ return err;
+}
+
+
+
+
+/* Perform a sign operation.
+
+ Sign the data received on DATA-FD in embedded mode or in detached
+ mode when DETACHED is true. Write the signature to OUT_FP. The
+ keys used to sign are taken from SIGNERLIST or the default one will
+ be used if the value of this argument is NULL. */
+int
+gpgsm_sign (ctrl_t ctrl, certlist_t signerlist,
+ int data_fd, int detached, FILE *out_fp)
+{
+ int i, rc;
+ gpg_error_t err;
+ Base64Context b64writer = NULL;
+ ksba_writer_t writer;
+ ksba_cms_t cms = NULL;
+ ksba_stop_reason_t stopreason;
+ KEYDB_HANDLE kh = NULL;
+ gcry_md_hd_t data_md = NULL;
+ int signer;
+ const char *algoid;
+ int algo;
+ ksba_isotime_t signed_at;
+ certlist_t cl;
+ int release_signerlist = 0;
+
+ audit_set_type (ctrl->audit, AUDIT_TYPE_SIGN);
+
+ kh = keydb_new (0);
+ if (!kh)
+ {
+ log_error (_("failed to allocated keyDB handle\n"));
+ rc = gpg_error (GPG_ERR_GENERAL);
+ goto leave;
+ }
+
+ ctrl->pem_name = "SIGNED MESSAGE";
+ rc = gpgsm_create_writer (&b64writer, ctrl, out_fp, NULL, &writer);
+ if (rc)
+ {
+ log_error ("can't create writer: %s\n", gpg_strerror (rc));
+ goto leave;
+ }
+
+ err = ksba_cms_new (&cms);
+ if (err)
+ {
+ rc = err;
+ goto leave;
+ }
+
+ err = ksba_cms_set_reader_writer (cms, NULL, writer);
+ if (err)
+ {
+ log_debug ("ksba_cms_set_reader_writer failed: %s\n",
+ gpg_strerror (err));
+ rc = err;
+ goto leave;
+ }
+
+ /* We are going to create signed data with data as encap. content */
+ err = ksba_cms_set_content_type (cms, 0, KSBA_CT_SIGNED_DATA);
+ if (!err)
+ err = ksba_cms_set_content_type (cms, 1, KSBA_CT_DATA);
+ if (err)
+ {
+ log_debug ("ksba_cms_set_content_type failed: %s\n",
+ gpg_strerror (err));
+ rc = err;
+ goto leave;
+ }
+
+ /* If no list of signers is given, use the default certificate. */
+ if (!signerlist)
+ {
+ ksba_cert_t cert = get_default_signer (ctrl);
+ if (!cert)
+ {
+ log_error ("no default signer found\n");
+ gpgsm_status2 (ctrl, STATUS_INV_SGNR,
+ get_inv_recpsgnr_code (GPG_ERR_NO_SECKEY), NULL);
+ rc = gpg_error (GPG_ERR_GENERAL);
+ goto leave;
+ }
+
+ /* Although we don't check for ambigious specification we will
+ check that the signer's certificate is usable and valid. */
+ rc = gpgsm_cert_use_sign_p (cert);
+ if (!rc)
+ rc = gpgsm_validate_chain (ctrl, cert, "", NULL, 0, NULL, 0, NULL);
+ if (rc)
+ {
+ char *tmpfpr;
+
+ tmpfpr = gpgsm_get_fingerprint_hexstring (cert, 0);
+ gpgsm_status2 (ctrl, STATUS_INV_SGNR,
+ get_inv_recpsgnr_code (rc), tmpfpr, NULL);
+ xfree (tmpfpr);
+ goto leave;
+ }
+
+ /* That one is fine - create signerlist. */
+ signerlist = xtrycalloc (1, sizeof *signerlist);
+ if (!signerlist)
+ {
+ rc = out_of_core ();
+ ksba_cert_release (cert);
+ goto leave;
+ }
+ signerlist->cert = cert;
+ release_signerlist = 1;
+ }
+
+ /* Figure out the hash algorithm to use. We do not want to use the
+ one for the certificate but if possible an OID for the plain
+ algorithm. */
+ if (opt.forced_digest_algo && opt.verbose)
+ log_info ("user requested hash algorithm %d\n", opt.forced_digest_algo);
+ for (i=0, cl=signerlist; cl; cl = cl->next, i++)
+ {
+ const char *oid;
+
+ if (opt.forced_digest_algo)
+ {
+ oid = NULL;
+ cl->hash_algo = opt.forced_digest_algo;
+ }
+ else
+ {
+ oid = ksba_cert_get_digest_algo (cl->cert);
+ cl->hash_algo = oid ? gcry_md_map_name (oid) : 0;
+ }
+ switch (cl->hash_algo)
+ {
+ case GCRY_MD_SHA1: oid = "1.3.14.3.2.26"; break;
+ case GCRY_MD_RMD160: oid = "1.3.36.3.2.1"; break;
+ case GCRY_MD_SHA224: oid = "2.16.840.1.101.3.4.2.4"; break;
+ case GCRY_MD_SHA256: oid = "2.16.840.1.101.3.4.2.1"; break;
+ case GCRY_MD_SHA384: oid = "2.16.840.1.101.3.4.2.2"; break;
+ case GCRY_MD_SHA512: oid = "2.16.840.1.101.3.4.2.3"; break;
+/* case GCRY_MD_WHIRLPOOL: oid = "No OID yet"; break; */
+
+ case GCRY_MD_MD5: /* We don't want to use MD5. */
+ case 0: /* No algorithm found in cert. */
+ default: /* Other algorithms. */
+ log_info (_("hash algorithm %d (%s) for signer %d not supported;"
+ " using %s\n"),
+ cl->hash_algo, oid? oid: "?", i,
+ gcry_md_algo_name (GCRY_MD_SHA1));
+ cl->hash_algo = GCRY_MD_SHA1;
+ oid = "1.3.14.3.2.26";
+ break;
+ }
+ cl->hash_algo_oid = oid;
+ }
+
+ if (opt.verbose)
+ {
+ for (i=0, cl=signerlist; cl; cl = cl->next, i++)
+ log_info (_("hash algorithm used for signer %d: %s (%s)\n"),
+ i, gcry_md_algo_name (cl->hash_algo), cl->hash_algo_oid);
+ }
+
+
+ /* Gather certificates of signers and store them in the CMS object. */
+ for (cl=signerlist; cl; cl = cl->next)
+ {
+ rc = gpgsm_cert_use_sign_p (cl->cert);
+ if (rc)
+ goto leave;
+
+ err = ksba_cms_add_signer (cms, cl->cert);
+ if (err)
+ {
+ log_error ("ksba_cms_add_signer failed: %s\n", gpg_strerror (err));
+ rc = err;
+ goto leave;
+ }
+ rc = add_certificate_list (ctrl, cms, cl->cert);
+ if (rc)
+ {
+ log_error ("failed to store list of certificates: %s\n",
+ gpg_strerror(rc));
+ goto leave;
+ }
+ /* Set the hash algorithm we are going to use */
+ err = ksba_cms_add_digest_algo (cms, cl->hash_algo_oid);
+ if (err)
+ {
+ log_debug ("ksba_cms_add_digest_algo failed: %s\n",
+ gpg_strerror (err));
+ rc = err;
+ goto leave;
+ }
+ }
+
+
+ /* Check whether one of the certificates is qualified. Note that we
+ already validated the certificate and thus the user data stored
+ flag must be available. */
+ for (cl=signerlist; cl; cl = cl->next)
+ {
+ size_t buflen;
+ char buffer[1];
+
+ err = ksba_cert_get_user_data (cl->cert, "is_qualified",
+ &buffer, sizeof (buffer), &buflen);
+ if (err || !buflen)
+ {
+ log_error (_("checking for qualified certificate failed: %s\n"),
+ gpg_strerror (err));
+ rc = err;
+ goto leave;
+ }
+ if (*buffer)
+ err = gpgsm_qualified_consent (ctrl, cl->cert);
+ else
+ err = gpgsm_not_qualified_warning (ctrl, cl->cert);
+ if (err)
+ {
+ rc = err;
+ goto leave;
+ }
+ }
+
+ /* Prepare hashing (actually we are figuring out what we have set
+ above). */
+ rc = gcry_md_open (&data_md, 0, 0);
+ if (rc)
+ {
+ log_error ("md_open failed: %s\n", gpg_strerror (rc));
+ goto leave;
+ }
+ if (DBG_HASHING)
+ gcry_md_start_debug (data_md, "sign.data");
+
+ for (i=0; (algoid=ksba_cms_get_digest_algo_list (cms, i)); i++)
+ {
+ algo = gcry_md_map_name (algoid);
+ if (!algo)
+ {
+ log_error ("unknown hash algorithm `%s'\n", algoid? algoid:"?");
+ rc = gpg_error (GPG_ERR_BUG);
+ goto leave;
+ }
+ gcry_md_enable (data_md, algo);
+ audit_log_i (ctrl->audit, AUDIT_DATA_HASH_ALGO, algo);
+ }
+
+ audit_log (ctrl->audit, AUDIT_SETUP_READY);
+
+ if (detached)
+ { /* We hash the data right now so that we can store the message
+ digest. ksba_cms_build() takes this as an flag that detached
+ data is expected. */
+ unsigned char *digest;
+ size_t digest_len;
+
+ if (!hash_data (data_fd, data_md))
+ audit_log (ctrl->audit, AUDIT_GOT_DATA);
+ for (cl=signerlist,signer=0; cl; cl = cl->next, signer++)
+ {
+ digest = gcry_md_read (data_md, cl->hash_algo);
+ digest_len = gcry_md_get_algo_dlen (cl->hash_algo);
+ if ( !digest || !digest_len )
+ {
+ log_error ("problem getting the hash of the data\n");
+ rc = gpg_error (GPG_ERR_BUG);
+ goto leave;
+ }
+ err = ksba_cms_set_message_digest (cms, signer, digest, digest_len);
+ if (err)
+ {
+ log_error ("ksba_cms_set_message_digest failed: %s\n",
+ gpg_strerror (err));
+ rc = err;
+ goto leave;
+ }
+ }
+ }
+
+ gnupg_get_isotime (signed_at);
+ for (cl=signerlist,signer=0; cl; cl = cl->next, signer++)
+ {
+ err = ksba_cms_set_signing_time (cms, signer, signed_at);
+ if (err)
+ {
+ log_error ("ksba_cms_set_signing_time failed: %s\n",
+ gpg_strerror (err));
+ rc = err;
+ goto leave;
+ }
+ }
+
+ /* We need to write at least a minimal list of our capabilities to
+ try to convince some MUAs to use 3DES and not the crippled
+ RC2. Our list is:
+
+ aes128-CBC
+ des-EDE3-CBC
+ */
+ err = ksba_cms_add_smime_capability (cms, "2.16.840.1.101.3.4.1.2", NULL, 0);
+ if (!err)
+ err = ksba_cms_add_smime_capability (cms, "1.2.840.113549.3.7", NULL, 0);
+ if (err)
+ {
+ log_error ("ksba_cms_add_smime_capability failed: %s\n",
+ gpg_strerror (err));
+ goto leave;
+ }
+
+
+ /* Main building loop. */
+ do
+ {
+ err = ksba_cms_build (cms, &stopreason);
+ if (err)
+ {
+ log_debug ("ksba_cms_build failed: %s\n", gpg_strerror (err));
+ rc = err;
+ goto leave;
+ }
+
+ if (stopreason == KSBA_SR_BEGIN_DATA)
+ {
+ /* Hash the data and store the message digest. */
+ unsigned char *digest;
+ size_t digest_len;
+
+ assert (!detached);
+
+ rc = hash_and_copy_data (data_fd, data_md, writer);
+ if (rc)
+ goto leave;
+ audit_log (ctrl->audit, AUDIT_GOT_DATA);
+ for (cl=signerlist,signer=0; cl; cl = cl->next, signer++)
+ {
+ digest = gcry_md_read (data_md, cl->hash_algo);
+ digest_len = gcry_md_get_algo_dlen (cl->hash_algo);
+ if ( !digest || !digest_len )
+ {
+ log_error ("problem getting the hash of the data\n");
+ rc = gpg_error (GPG_ERR_BUG);
+ goto leave;
+ }
+ err = ksba_cms_set_message_digest (cms, signer,
+ digest, digest_len);
+ if (err)
+ {
+ log_error ("ksba_cms_set_message_digest failed: %s\n",
+ gpg_strerror (err));
+ rc = err;
+ goto leave;
+ }
+ }
+ }
+ else if (stopreason == KSBA_SR_NEED_SIG)
+ {
+ /* Compute the signature for all signers. */
+ gcry_md_hd_t md;
+
+ rc = gcry_md_open (&md, 0, 0);
+ if (rc)
+ {
+ log_error ("md_open failed: %s\n", gpg_strerror (rc));
+ goto leave;
+ }
+ if (DBG_HASHING)
+ gcry_md_start_debug (md, "sign.attr");
+ ksba_cms_set_hash_function (cms, HASH_FNC, md);
+ for (cl=signerlist,signer=0; cl; cl = cl->next, signer++)
+ {
+ unsigned char *sigval = NULL;
+ char *buf, *fpr;
+
+ audit_log_i (ctrl->audit, AUDIT_NEW_SIG, signer);
+ if (signer)
+ gcry_md_reset (md);
+ {
+ certlist_t cl_tmp;
+
+ for (cl_tmp=signerlist; cl_tmp; cl_tmp = cl_tmp->next)
+ {
+ gcry_md_enable (md, cl_tmp->hash_algo);
+ audit_log_i (ctrl->audit, AUDIT_ATTR_HASH_ALGO,
+ cl_tmp->hash_algo);
+ }
+ }
+
+ rc = ksba_cms_hash_signed_attrs (cms, signer);
+ if (rc)
+ {
+ log_debug ("hashing signed attrs failed: %s\n",
+ gpg_strerror (rc));
+ gcry_md_close (md);
+ goto leave;
+ }
+
+ rc = gpgsm_create_cms_signature (ctrl, cl->cert,
+ md, cl->hash_algo, &sigval);
+ if (rc)
+ {
+ audit_log_cert (ctrl->audit, AUDIT_SIGNED_BY, cl->cert, rc);
+ gcry_md_close (md);
+ goto leave;
+ }
+
+ err = ksba_cms_set_sig_val (cms, signer, sigval);
+ xfree (sigval);
+ if (err)
+ {
+ audit_log_cert (ctrl->audit, AUDIT_SIGNED_BY, cl->cert, err);
+ log_error ("failed to store the signature: %s\n",
+ gpg_strerror (err));
+ rc = err;
+ gcry_md_close (md);
+ goto leave;
+ }
+
+ /* write a status message */
+ fpr = gpgsm_get_fingerprint_hexstring (cl->cert, GCRY_MD_SHA1);
+ if (!fpr)
+ {
+ rc = gpg_error (GPG_ERR_ENOMEM);
+ gcry_md_close (md);
+ goto leave;
+ }
+ rc = 0;
+ {
+ int pkalgo = gpgsm_get_key_algo_info (cl->cert, NULL);
+ buf = xtryasprintf ("%c %d %d 00 %s %s",
+ detached? 'D':'S',
+ pkalgo,
+ cl->hash_algo,
+ signed_at,
+ fpr);
+ if (!buf)
+ rc = gpg_error_from_syserror ();
+ }
+ xfree (fpr);
+ if (rc)
+ {
+ gcry_md_close (md);
+ goto leave;
+ }
+ gpgsm_status (ctrl, STATUS_SIG_CREATED, buf);
+ xfree (buf);
+ audit_log_cert (ctrl->audit, AUDIT_SIGNED_BY, cl->cert, 0);
+ }
+ gcry_md_close (md);
+ }
+ }
+ while (stopreason != KSBA_SR_READY);
+
+ rc = gpgsm_finish_writer (b64writer);
+ if (rc)
+ {
+ log_error ("write failed: %s\n", gpg_strerror (rc));
+ goto leave;
+ }
+
+ audit_log (ctrl->audit, AUDIT_SIGNING_DONE);
+ log_info ("signature created\n");
+
+
+ leave:
+ if (rc)
+ log_error ("error creating signature: %s <%s>\n",
+ gpg_strerror (rc), gpg_strsource (rc) );
+ if (release_signerlist)
+ gpgsm_release_certlist (signerlist);
+ ksba_cms_release (cms);
+ gpgsm_destroy_writer (b64writer);
+ keydb_release (kh);
+ gcry_md_close (data_md);
+ return rc;
+}
diff --git a/sm/verify.c b/sm/verify.c
new file mode 100644
index 0000000..c8663e3
--- /dev/null
+++ b/sm/verify.c
@@ -0,0 +1,660 @@
+/* verify.c - Verify a messages signature
+ * Copyright (C) 2001, 2002, 2003, 2007 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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuPG is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <time.h>
+#include <assert.h>
+
+#include "gpgsm.h"
+#include <gcrypt.h>
+#include <ksba.h>
+
+#include "keydb.h"
+#include "i18n.h"
+
+static char *
+strtimestamp_r (ksba_isotime_t atime)
+{
+ char *buffer = xmalloc (15);
+
+ if (!atime || !*atime)
+ strcpy (buffer, "none");
+ else
+ sprintf (buffer, "%.4s-%.2s-%.2s", atime, atime+4, atime+6);
+ return buffer;
+}
+
+
+
+/* Hash the data for a detached signature. Returns 0 on success. */
+static gpg_error_t
+hash_data (int fd, gcry_md_hd_t md)
+{
+ gpg_error_t err = 0;
+ FILE *fp;
+ char buffer[4096];
+ int nread;
+
+ fp = fdopen ( dup (fd), "rb");
+ if (!fp)
+ {
+ err = gpg_error_from_syserror ();
+ log_error ("fdopen(%d) failed: %s\n", fd, gpg_strerror (err));
+ return err;
+ }
+
+ do
+ {
+ nread = fread (buffer, 1, DIM(buffer), fp);
+ gcry_md_write (md, buffer, nread);
+ }
+ while (nread);
+ if (ferror (fp))
+ {
+ err = gpg_error_from_syserror ();
+ log_error ("read error on fd %d: %s\n", fd, gpg_strerror (err));
+ }
+ fclose (fp);
+ return err;
+}
+
+
+
+
+/* Perform a verify operation. To verify detached signatures, data_fd
+ must be different than -1. With OUT_FP given and a non-detached
+ signature, the signed material is written to that stream. */
+int
+gpgsm_verify (ctrl_t ctrl, int in_fd, int data_fd, FILE *out_fp)
+{
+ int i, rc;
+ Base64Context b64reader = NULL;
+ Base64Context b64writer = NULL;
+ ksba_reader_t reader;
+ ksba_writer_t writer = NULL;
+ ksba_cms_t cms = NULL;
+ ksba_stop_reason_t stopreason;
+ ksba_cert_t cert;
+ KEYDB_HANDLE kh;
+ gcry_md_hd_t data_md = NULL;
+ int signer;
+ const char *algoid;
+ int algo;
+ int is_detached;
+ FILE *fp = NULL;
+ char *p;
+
+ audit_set_type (ctrl->audit, AUDIT_TYPE_VERIFY);
+
+ kh = keydb_new (0);
+ if (!kh)
+ {
+ log_error (_("failed to allocated keyDB handle\n"));
+ rc = gpg_error (GPG_ERR_GENERAL);
+ goto leave;
+ }
+
+
+ fp = fdopen ( dup (in_fd), "rb");
+ if (!fp)
+ {
+ rc = gpg_error (gpg_err_code_from_errno (errno));
+ log_error ("fdopen() failed: %s\n", strerror (errno));
+ goto leave;
+ }
+
+ rc = gpgsm_create_reader (&b64reader, ctrl, fp, 0, &reader);
+ if (rc)
+ {
+ log_error ("can't create reader: %s\n", gpg_strerror (rc));
+ goto leave;
+ }
+
+ if (out_fp)
+ {
+ rc = gpgsm_create_writer (&b64writer, ctrl, out_fp, NULL, &writer);
+ if (rc)
+ {
+ log_error ("can't create writer: %s\n", gpg_strerror (rc));
+ goto leave;
+ }
+ }
+
+ rc = ksba_cms_new (&cms);
+ if (rc)
+ goto leave;
+
+ rc = ksba_cms_set_reader_writer (cms, reader, writer);
+ if (rc)
+ {
+ log_error ("ksba_cms_set_reader_writer failed: %s\n",
+ gpg_strerror (rc));
+ goto leave;
+ }
+
+ rc = gcry_md_open (&data_md, 0, 0);
+ if (rc)
+ {
+ log_error ("md_open failed: %s\n", gpg_strerror (rc));
+ goto leave;
+ }
+ if (DBG_HASHING)
+ gcry_md_start_debug (data_md, "vrfy.data");
+
+ audit_log (ctrl->audit, AUDIT_SETUP_READY);
+
+ is_detached = 0;
+ do
+ {
+ rc = ksba_cms_parse (cms, &stopreason);
+ if (rc)
+ {
+ log_error ("ksba_cms_parse failed: %s\n", gpg_strerror (rc));
+ goto leave;
+ }
+
+ if (stopreason == KSBA_SR_NEED_HASH)
+ {
+ is_detached = 1;
+ audit_log (ctrl->audit, AUDIT_DETACHED_SIGNATURE);
+ if (opt.verbose)
+ log_info ("detached signature\n");
+ }
+
+ if (stopreason == KSBA_SR_NEED_HASH
+ || stopreason == KSBA_SR_BEGIN_DATA)
+ {
+ audit_log (ctrl->audit, AUDIT_GOT_DATA);
+
+ /* We are now able to enable the hash algorithms */
+ for (i=0; (algoid=ksba_cms_get_digest_algo_list (cms, i)); i++)
+ {
+ algo = gcry_md_map_name (algoid);
+ if (!algo)
+ {
+ log_error ("unknown hash algorithm `%s'\n",
+ algoid? algoid:"?");
+ if (algoid
+ && ( !strcmp (algoid, "1.2.840.113549.1.1.2")
+ ||!strcmp (algoid, "1.2.840.113549.2.2")))
+ log_info (_("(this is the MD2 algorithm)\n"));
+ audit_log_s (ctrl->audit, AUDIT_BAD_DATA_HASH_ALGO, algoid);
+ }
+ else
+ {
+ if (DBG_X509)
+ log_debug ("enabling hash algorithm %d (%s)\n",
+ algo, algoid? algoid:"");
+ gcry_md_enable (data_md, algo);
+ audit_log_i (ctrl->audit, AUDIT_DATA_HASH_ALGO, algo);
+ }
+ }
+ if (opt.extra_digest_algo)
+ {
+ if (DBG_X509)
+ log_debug ("enabling extra hash algorithm %d\n",
+ opt.extra_digest_algo);
+ gcry_md_enable (data_md, opt.extra_digest_algo);
+ audit_log_i (ctrl->audit, AUDIT_DATA_HASH_ALGO,
+ opt.extra_digest_algo);
+ }
+ if (is_detached)
+ {
+ if (data_fd == -1)
+ {
+ log_info ("detached signature w/o data "
+ "- assuming certs-only\n");
+ audit_log (ctrl->audit, AUDIT_CERT_ONLY_SIG);
+ }
+ else
+ audit_log_ok (ctrl->audit, AUDIT_DATA_HASHING,
+ hash_data (data_fd, data_md));
+ }
+ else
+ {
+ ksba_cms_set_hash_function (cms, HASH_FNC, data_md);
+ }
+ }
+ else if (stopreason == KSBA_SR_END_DATA)
+ { /* The data bas been hashed */
+ audit_log_ok (ctrl->audit, AUDIT_DATA_HASHING, 0);
+ }
+ }
+ while (stopreason != KSBA_SR_READY);
+
+ if (b64writer)
+ {
+ rc = gpgsm_finish_writer (b64writer);
+ if (rc)
+ {
+ log_error ("write failed: %s\n", gpg_strerror (rc));
+ audit_log_ok (ctrl->audit, AUDIT_WRITE_ERROR, rc);
+ goto leave;
+ }
+ }
+
+ if (data_fd != -1 && !is_detached)
+ {
+ log_error ("data given for a non-detached signature\n");
+ rc = gpg_error (GPG_ERR_CONFLICT);
+ audit_log (ctrl->audit, AUDIT_USAGE_ERROR);
+ goto leave;
+ }
+
+ for (i=0; (cert=ksba_cms_get_cert (cms, i)); i++)
+ {
+ /* Fixme: it might be better to check the validity of the
+ certificate first before entering it into the DB. This way
+ we would avoid cluttering the DB with invalid
+ certificates. */
+ audit_log_cert (ctrl->audit, AUDIT_SAVE_CERT, cert,
+ keydb_store_cert (cert, 0, NULL));
+ ksba_cert_release (cert);
+ }
+
+ cert = NULL;
+ for (signer=0; ; signer++)
+ {
+ char *issuer = NULL;
+ ksba_sexp_t sigval = NULL;
+ ksba_isotime_t sigtime, keyexptime;
+ ksba_sexp_t serial;
+ char *msgdigest = NULL;
+ size_t msgdigestlen;
+ char *ctattr;
+ int sigval_hash_algo;
+ int info_pkalgo;
+ unsigned int verifyflags;
+
+ rc = ksba_cms_get_issuer_serial (cms, signer, &issuer, &serial);
+ if (!signer && gpg_err_code (rc) == GPG_ERR_NO_DATA
+ && data_fd == -1 && is_detached)
+ {
+ log_info ("certs-only message accepted\n");
+ rc = 0;
+ break;
+ }
+ if (rc)
+ {
+ if (signer && rc == -1)
+ rc = 0;
+ break;
+ }
+
+ gpgsm_status (ctrl, STATUS_NEWSIG, NULL);
+ audit_log_i (ctrl->audit, AUDIT_NEW_SIG, signer);
+
+ if (DBG_X509)
+ {
+ log_debug ("signer %d - issuer: `%s'\n",
+ signer, issuer? issuer:"[NONE]");
+ log_debug ("signer %d - serial: ", signer);
+ gpgsm_dump_serial (serial);
+ log_printf ("\n");
+ }
+ if (ctrl->audit)
+ {
+ char *tmpstr = gpgsm_format_sn_issuer (serial, issuer);
+ audit_log_s (ctrl->audit, AUDIT_SIG_NAME, tmpstr);
+ xfree (tmpstr);
+ }
+
+ rc = ksba_cms_get_signing_time (cms, signer, sigtime);
+ if (gpg_err_code (rc) == GPG_ERR_NO_DATA)
+ *sigtime = 0;
+ else if (rc)
+ {
+ log_error ("error getting signing time: %s\n", gpg_strerror (rc));
+ *sigtime = 0; /* (we can't encode an error in the time string.) */
+ }
+
+ rc = ksba_cms_get_message_digest (cms, signer,
+ &msgdigest, &msgdigestlen);
+ if (!rc)
+ {
+ size_t is_enabled;
+
+ algoid = ksba_cms_get_digest_algo (cms, signer);
+ algo = gcry_md_map_name (algoid);
+ if (DBG_X509)
+ log_debug ("signer %d - digest algo: %d\n", signer, algo);
+ is_enabled = sizeof algo;
+ if ( gcry_md_info (data_md, GCRYCTL_IS_ALGO_ENABLED,
+ &algo, &is_enabled)
+ || !is_enabled)
+ {
+ log_error ("digest algo %d (%s) has not been enabled\n",
+ algo, algoid?algoid:"");
+ audit_log_s (ctrl->audit, AUDIT_SIG_STATUS, "unsupported");
+ goto next_signer;
+ }
+ }
+ else if (gpg_err_code (rc) == GPG_ERR_NO_DATA)
+ {
+ assert (!msgdigest);
+ rc = 0;
+ algoid = NULL;
+ algo = 0;
+ }
+ else /* real error */
+ {
+ audit_log_s (ctrl->audit, AUDIT_SIG_STATUS, "error");
+ break;
+ }
+
+ rc = ksba_cms_get_sigattr_oids (cms, signer,
+ "1.2.840.113549.1.9.3", &ctattr);
+ if (!rc)
+ {
+ const char *s;
+
+ if (DBG_X509)
+ log_debug ("signer %d - content-type attribute: %s",
+ signer, ctattr);
+
+ s = ksba_cms_get_content_oid (cms, 1);
+ if (!s || strcmp (ctattr, s))
+ {
+ log_error ("content-type attribute does not match "
+ "actual content-type\n");
+ ksba_free (ctattr);
+ ctattr = NULL;
+ audit_log_s (ctrl->audit, AUDIT_SIG_STATUS, "bad");
+ goto next_signer;
+ }
+ ksba_free (ctattr);
+ ctattr = NULL;
+ }
+ else if (rc != -1)
+ {
+ log_error ("error getting content-type attribute: %s\n",
+ gpg_strerror (rc));
+ audit_log_s (ctrl->audit, AUDIT_SIG_STATUS, "bad");
+ goto next_signer;
+ }
+ rc = 0;
+
+
+ sigval = ksba_cms_get_sig_val (cms, signer);
+ if (!sigval)
+ {
+ log_error ("no signature value available\n");
+ audit_log_s (ctrl->audit, AUDIT_SIG_STATUS, "bad");
+ goto next_signer;
+ }
+ sigval_hash_algo = hash_algo_from_sigval (sigval);
+ if (DBG_X509)
+ {
+ log_debug ("signer %d - signature available (sigval hash=%d)",
+ signer, sigval_hash_algo);
+/* log_printhex ("sigval ", sigval, */
+/* gcry_sexp_canon_len (sigval, 0, NULL, NULL)); */
+ }
+ if (!sigval_hash_algo)
+ sigval_hash_algo = algo; /* Fallback used e.g. with old libksba. */
+
+ /* Find the certificate of the signer */
+ keydb_search_reset (kh);
+ rc = keydb_search_issuer_sn (kh, issuer, serial);
+ if (rc)
+ {
+ if (rc == -1)
+ {
+ log_error ("certificate not found\n");
+ rc = gpg_error (GPG_ERR_NO_PUBKEY);
+ }
+ else
+ log_error ("failed to find the certificate: %s\n",
+ gpg_strerror(rc));
+ {
+ char numbuf[50];
+ sprintf (numbuf, "%d", rc);
+
+ gpgsm_status2 (ctrl, STATUS_ERROR, "verify.findkey",
+ numbuf, NULL);
+ }
+ audit_log_s (ctrl->audit, AUDIT_SIG_STATUS, "no-cert");
+ goto next_signer;
+ }
+
+ rc = keydb_get_cert (kh, &cert);
+ if (rc)
+ {
+ log_error ("failed to get cert: %s\n", gpg_strerror (rc));
+ audit_log_s (ctrl->audit, AUDIT_SIG_STATUS, "error");
+ goto next_signer;
+ }
+
+ log_info (_("Signature made "));
+ if (*sigtime)
+ dump_isotime (sigtime);
+ else
+ log_printf (_("[date not given]"));
+ log_printf (_(" using certificate ID 0x%08lX\n"),
+ gpgsm_get_short_fingerprint (cert, NULL));
+
+ audit_log_i (ctrl->audit, AUDIT_DATA_HASH_ALGO, algo);
+
+ if (msgdigest)
+ { /* Signed attributes are available. */
+ gcry_md_hd_t md;
+ unsigned char *s;
+
+ /* Check that the message digest in the signed attributes
+ matches the one we calculated on the data. */
+ s = gcry_md_read (data_md, algo);
+ if ( !s || !msgdigestlen
+ || gcry_md_get_algo_dlen (algo) != msgdigestlen
+ || !s || memcmp (s, msgdigest, msgdigestlen) )
+ {
+ char *fpr;
+
+ log_error (_("invalid signature: message digest attribute "
+ "does not match computed one\n"));
+ if (DBG_X509)
+ {
+ if (msgdigest)
+ log_printhex ("message: ", msgdigest, msgdigestlen);
+ if (s)
+ log_printhex ("computed: ",
+ s, gcry_md_get_algo_dlen (algo));
+ }
+ fpr = gpgsm_fpr_and_name_for_status (cert);
+ gpgsm_status (ctrl, STATUS_BADSIG, fpr);
+ xfree (fpr);
+ audit_log_s (ctrl->audit, AUDIT_SIG_STATUS, "bad");
+ goto next_signer;
+ }
+
+ audit_log_i (ctrl->audit, AUDIT_ATTR_HASH_ALGO, sigval_hash_algo);
+ rc = gcry_md_open (&md, sigval_hash_algo, 0);
+ if (rc)
+ {
+ log_error ("md_open failed: %s\n", gpg_strerror (rc));
+ audit_log_s (ctrl->audit, AUDIT_SIG_STATUS, "error");
+ goto next_signer;
+ }
+ if (DBG_HASHING)
+ gcry_md_start_debug (md, "vrfy.attr");
+
+ ksba_cms_set_hash_function (cms, HASH_FNC, md);
+ rc = ksba_cms_hash_signed_attrs (cms, signer);
+ if (rc)
+ {
+ log_error ("hashing signed attrs failed: %s\n",
+ gpg_strerror (rc));
+ gcry_md_close (md);
+ audit_log_s (ctrl->audit, AUDIT_SIG_STATUS, "error");
+ goto next_signer;
+ }
+ rc = gpgsm_check_cms_signature (cert, sigval, md,
+ sigval_hash_algo, &info_pkalgo);
+ gcry_md_close (md);
+ }
+ else
+ {
+ rc = gpgsm_check_cms_signature (cert, sigval, data_md,
+ algo, &info_pkalgo);
+ }
+
+ if (rc)
+ {
+ char *fpr;
+
+ log_error ("invalid signature: %s\n", gpg_strerror (rc));
+ fpr = gpgsm_fpr_and_name_for_status (cert);
+ gpgsm_status (ctrl, STATUS_BADSIG, fpr);
+ xfree (fpr);
+ audit_log_s (ctrl->audit, AUDIT_SIG_STATUS, "bad");
+ goto next_signer;
+ }
+ rc = gpgsm_cert_use_verify_p (cert); /*(this displays an info message)*/
+ if (rc)
+ {
+ gpgsm_status_with_err_code (ctrl, STATUS_ERROR, "verify.keyusage",
+ gpg_err_code (rc));
+ rc = 0;
+ }
+
+ if (DBG_X509)
+ log_debug ("signature okay - checking certs\n");
+ audit_log (ctrl->audit, AUDIT_VALIDATE_CHAIN);
+ rc = gpgsm_validate_chain (ctrl, cert,
+ *sigtime? sigtime : "19700101T000000",
+ keyexptime, 0,
+ NULL, 0, &verifyflags);
+ {
+ char *fpr, *buf, *tstr;
+
+ fpr = gpgsm_fpr_and_name_for_status (cert);
+ if (gpg_err_code (rc) == GPG_ERR_CERT_EXPIRED)
+ {
+ gpgsm_status (ctrl, STATUS_EXPKEYSIG, fpr);
+ rc = 0;
+ }
+ else
+ gpgsm_status (ctrl, STATUS_GOODSIG, fpr);
+
+ xfree (fpr);
+
+ fpr = gpgsm_get_fingerprint_hexstring (cert, GCRY_MD_SHA1);
+ tstr = strtimestamp_r (sigtime);
+ buf = xasprintf ("%s %s %s %s 0 0 %d %d 00", fpr, tstr,
+ *sigtime? sigtime : "0",
+ *keyexptime? keyexptime : "0",
+ info_pkalgo, algo);
+ xfree (tstr);
+ xfree (fpr);
+ gpgsm_status (ctrl, STATUS_VALIDSIG, buf);
+ xfree (buf);
+ }
+
+ audit_log_ok (ctrl->audit, AUDIT_CHAIN_STATUS, rc);
+ if (rc) /* of validate_chain */
+ {
+ log_error ("invalid certification chain: %s\n", gpg_strerror (rc));
+ if (gpg_err_code (rc) == GPG_ERR_BAD_CERT_CHAIN
+ || gpg_err_code (rc) == GPG_ERR_BAD_CERT
+ || gpg_err_code (rc) == GPG_ERR_BAD_CA_CERT
+ || gpg_err_code (rc) == GPG_ERR_CERT_REVOKED)
+ gpgsm_status_with_err_code (ctrl, STATUS_TRUST_NEVER, NULL,
+ gpg_err_code (rc));
+ else
+ gpgsm_status_with_err_code (ctrl, STATUS_TRUST_UNDEFINED, NULL,
+ gpg_err_code (rc));
+ audit_log_s (ctrl->audit, AUDIT_SIG_STATUS, "bad");
+ goto next_signer;
+ }
+
+ audit_log_s (ctrl->audit, AUDIT_SIG_STATUS, "good");
+
+ for (i=0; (p = ksba_cert_get_subject (cert, i)); i++)
+ {
+ log_info (!i? _("Good signature from")
+ : _(" aka"));
+ log_printf (" \"");
+ gpgsm_print_name (log_get_stream (), p);
+ log_printf ("\"\n");
+ ksba_free (p);
+ }
+
+ /* Print a note if this is a qualified signature. */
+ {
+ size_t qualbuflen;
+ char qualbuffer[1];
+
+ rc = ksba_cert_get_user_data (cert, "is_qualified", &qualbuffer,
+ sizeof (qualbuffer), &qualbuflen);
+ if (!rc && qualbuflen)
+ {
+ if (*qualbuffer)
+ {
+ log_info (_("This is a qualified signature\n"));
+ if (!opt.qualsig_approval)
+ log_info
+ (_("Note, that this software is not officially approved "
+ "to create or verify such signatures.\n"));
+ }
+ }
+ else if (gpg_err_code (rc) != GPG_ERR_NOT_FOUND)
+ log_error ("get_user_data(is_qualified) failed: %s\n",
+ gpg_strerror (rc));
+ }
+
+ gpgsm_status (ctrl, STATUS_TRUST_FULLY,
+ (verifyflags & VALIDATE_FLAG_CHAIN_MODEL)?
+ "0 chain": "0 shell");
+
+
+ next_signer:
+ rc = 0;
+ xfree (issuer);
+ xfree (serial);
+ xfree (sigval);
+ xfree (msgdigest);
+ ksba_cert_release (cert);
+ cert = NULL;
+ }
+ rc = 0;
+
+ leave:
+ ksba_cms_release (cms);
+ gpgsm_destroy_reader (b64reader);
+ gpgsm_destroy_writer (b64writer);
+ keydb_release (kh);
+ gcry_md_close (data_md);
+ if (fp)
+ fclose (fp);
+
+ if (rc)
+ {
+ char numbuf[50];
+ sprintf (numbuf, "%d", rc );
+ gpgsm_status2 (ctrl, STATUS_ERROR, "verify.leave",
+ numbuf, NULL);
+ }
+
+ return rc;
+}
+