aboutsummaryrefslogtreecommitdiffstats
path: root/sm
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--sm/ChangeLog816
-rw-r--r--sm/Makefile.am55
-rw-r--r--sm/call-agent.c713
-rw-r--r--sm/call-dirmngr.c632
-rw-r--r--sm/certchain.c793
-rw-r--r--sm/certcheck.c300
-rw-r--r--sm/certdump.c457
-rw-r--r--sm/certlist.c315
-rw-r--r--sm/certreqgen.c699
-rw-r--r--sm/decrypt.c506
-rw-r--r--sm/delete.c165
-rw-r--r--sm/encrypt.c550
-rw-r--r--sm/export.c249
-rw-r--r--sm/fingerprint.c271
-rw-r--r--sm/gpgsm.c1458
-rw-r--r--sm/gpgsm.h274
-rw-r--r--sm/import.c349
-rw-r--r--sm/keydb.c1282
-rw-r--r--sm/keylist.c617
-rw-r--r--sm/server.c1070
-rw-r--r--sm/sign.c621
-rw-r--r--sm/verify.c550
22 files changed, 12742 insertions, 0 deletions
diff --git a/sm/ChangeLog b/sm/ChangeLog
new file mode 100644
index 000000000..59a6b3271
--- /dev/null
+++ b/sm/ChangeLog
@@ -0,0 +1,816 @@
+2003-07-31 Werner Koch <[email protected]>
+
+ * Makefile.am (gpgsm_LDADD): Added INTLLIBS.
+
+2003-07-29 Werner Koch <[email protected]>
+
+ * 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 <[email protected]>
+
+ Adjusted for gcry_mpi_print and gcry_mpi_scan API change.
+
+2003-06-24 Werner Koch <[email protected]>
+
+ * 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 <[email protected]>
+
+ * 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 <[email protected]>
+
+ 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 <[email protected]>
+
+ * Makefile.am: Use libassuan. Don't override LDFLAGS anymore.
+ * server.c (register_commands): Adjust for new Assuan semantics.
+
+2002-12-03 Werner Koch <[email protected]>
+
+ * 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 <[email protected]>
+
+ * verify.c (gpgsm_verify): Handle content-type attribute.
+
+2002-11-13 Werner Koch <[email protected]>
+
+ * 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 <[email protected]>
+
+ * 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 <[email protected]>
+
+ * certcheck.c (gpgsm_check_cms_signature): Don't double free
+ s_sig but free s_pkey at leave.
+
+2002-11-10 Werner Koch <[email protected]>
+
+ * gpgsm.c: Removed duplicate --list-secret-key entry.
+
+2002-09-19 Werner Koch <[email protected]>
+
+ * 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 <[email protected]>
+
+ * gpgsm.c (main): Disable the internal libgcrypt locking.
+
+2002-08-21 Werner Koch <[email protected]>
+
+ * 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 <[email protected]>
+
+ * 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 <[email protected]>
+
+ * 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 <[email protected]>
+
+ * 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 <[email protected]>
+
+ * 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 <[email protected]>
+
+ * 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 <[email protected]>
+
+ * 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 <[email protected]>
+
+ * certcheck.c (gpgsm_check_cert_sig): Implement proper cleanup.
+ (gpgsm_check_cms_signature): Ditto.
+
+2002-07-22 Werner Koch <[email protected]>
+
+ * 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 <[email protected]>
+
+ * 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 <[email protected]>
+
+ * 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 <[email protected]>
+
+ * 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 <[email protected]>
+
+ * server.c (cmd_recipient): Add more reason codes.
+
+2002-06-27 Werner Koch <[email protected]>
+
+ * 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 <[email protected]>
+
+ * import.c (gpgsm_import): Print an STATUS_IMPORTED.
+
+ * gpgsm.c: --debug-no-path-validation does not take an argument.
+
+2002-06-25 Werner Koch <[email protected]>
+
+ * 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 <[email protected]>
+
+ * 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 <[email protected]>
+
+ * 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 <[email protected]>
+
+ * 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 <[email protected]>
+
+ * certreqgen.c (read_parameters): Improved error handling.
+ (gpgsm_genkey): Print error message.
+
+2002-06-13 Werner Koch <[email protected]>
+
+ * gpgsm.c (main): New option --log-file.
+
+2002-06-12 Werner Koch <[email protected]>
+
+ * 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 <[email protected]>
+
+ * 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 <[email protected]>
+
+ * 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 <[email protected]>
+
+ * gpgsm.c (main): Don't print the "go ahead" message for an
+ invalid command.
+
+2002-05-23 Werner Koch <[email protected]>
+
+ * import.c (gpgsm_import): Add error messages.
+
+2002-05-21 Werner Koch <[email protected]>
+
+ * 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 <[email protected]>
+
+ * 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 <[email protected]>
+
+ * 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 <[email protected]>
+
+ * 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 <[email protected]>
+
+ * call-agent.c (start_agent): Make copies of old locales and check
+ for setlocale.
+
+2002-04-25 Marcus Brinkmann <[email protected]>
+
+ * call-agent.c (start_agent): Fix error handling logic so the
+ locale is always correctly reset.
+
+2002-04-25 Marcus Brinkmann <[email protected]>
+
+ * 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 <[email protected]>
+
+ * 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 <[email protected]>
+
+ * certreqgen.c (gpgsm_genkey): Write status output on success.
+
+2002-04-15 Werner Koch <[email protected]>
+
+ * 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 <[email protected]>
+
+ * 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 <[email protected]>
+
+ * 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 <[email protected]>
+
+ * export.c: New.
+ * gpgsm.c: Add command --export.
+ * server.c (cmd_export): New.
+
+2002-03-13 Werner Koch <[email protected]>
+
+ * decrypt.c (gpgsm_decrypt): Allow multiple recipients.
+
+2002-03-12 Werner Koch <[email protected]>
+
+ * certpath.c (check_cert_policy): Print the policy list.
+
+ * verify.c (gpgsm_verify): Detect certs-only message.
+
+2002-03-11 Werner Koch <[email protected]>
+
+ * 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 <[email protected]>
+
+ * 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 <[email protected]>
+
+ * 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 <[email protected]>
+
+ * call-dirmngr.c, call-agent.c: Add 2 more arguments to all uses
+ of assuan_transact.
+
+2002-02-25 Werner Koch <[email protected]>
+
+ * 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 <[email protected]>
+
+ * 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 <[email protected]>
+
+ * 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 <[email protected]>
+
+ * 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 <[email protected]>
+
+ * 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 <[email protected]>
+
+ * sign.c (gpgsm_sign): Do not release certificate twice.
+
+2002-01-29 Werner Koch <[email protected]>
+
+ * 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 <[email protected]>
+
+ * certreqgen.c (create_request): Store the email address in the req.
+
+2002-01-25 Werner Koch <[email protected]>
+
+ * 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 <[email protected]>
+
+ * 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 <[email protected]>
+
+ * 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 <[email protected]>
+
+ * 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 <[email protected]>
+
+ * server.c (option_handler): New.
+ (gpgsm_server): Register it with assuan.
+
+2002-01-19 Werner Koch <[email protected]>
+
+ * 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 <[email protected]>
+
+ * 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 <[email protected]>
+
+ * call-dirmngr.c (inq_certificate): Changed for new interface semantic.
+ * certlist.c (gpgsm_find_cert): New.
+
+2002-01-13 Werner Koch <[email protected]>
+
+ * fingerprint.c (gpgsm_get_certid): Print the serial and not the
+ hash after the dot.
+
+2002-01-11 Werner Koch <[email protected]>
+
+ * 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 <[email protected]>
+
+ * base64.c (gpgsm_create_writer): Allow to set the object name
+
+2002-01-08 Werner Koch <[email protected]>
+
+ * 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 <[email protected]>
+
+ * server.c (rc_to_assuan_status): Removed and changed all callers
+ to use map_to_assuan_status.
+
+2001-12-20 Werner Koch <[email protected]>
+
+ * 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 <[email protected]>
+
+ * call-agent.c (start_agent): Add new argument to assuan_pipe_connect.
+
+2001-12-18 Werner Koch <[email protected]>
+
+ * 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 <[email protected]>
+
+ * 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 <[email protected]>
+
+ * 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 <[email protected]>
+
+ * 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 <[email protected]>
+
+ * call-agent.c (LINELENGTH): Removed.
+ (gpgsm_agent_pksign): Use ASSUAN_LINELENGTH, not LINELENGTH.
+ (gpgsm_agent_pkdecrypt): Likewise.
+
+2001-12-13 Werner Koch <[email protected]>
+
+ * keylist.c (list_cert_colon): Print alternative names of subject
+ and a few other values.
+
+2001-12-12 Werner Koch <[email protected]>
+
+ * gpgsm.c (main): New options --assume-{armor,base64,binary}.
+ * base64.c (base64_reader_cb): Fixed non-autodetection mode.
+
+2001-12-04 Werner Koch <[email protected]>
+
+ * 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 <[email protected]>
+
+ * 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 <[email protected]>
+
+ * 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 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 000000000..6345573e5
--- /dev/null
+++ b/sm/Makefile.am
@@ -0,0 +1,55 @@
+# 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 2 of the License, or
+# (at your option) any later version.
+#
+# GnuPG is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+
+## Process this file with automake to produce Makefile.in
+
+localedir = $(datadir)/locale
+INCLUDES = -I../intl -DLOCALEDIR=\"$(localedir)\"
+
+bin_PROGRAMS = gpgsm
+
+AM_CPPFLAGS = -I$(top_srcdir)/common -I$(top_srcdir)/intl \
+ $(LIBGCRYPT_CFLAGS) $(LIBASSUAN_CFLAGS) $(KSBA_CFLAGS)
+
+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
+
+
+gpgsm_LDADD = ../jnlib/libjnlib.a ../kbx/libkeybox.a ../common/libcommon.a \
+ $(LIBGCRYPT_LIBS) $(LIBASSUAN_LIBS) $(KSBA_LIBS) -lgpg-error \
+ @INTLLIBS@
diff --git a/sm/call-agent.c b/sm/call-agent.c
new file mode 100644
index 000000000..4d26e3450
--- /dev/null
+++ b/sm/call-agent.c
@@ -0,0 +1,713 @@
+/* call-agent.c - divert operations to the agent
+ * 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 2 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuPG is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ */
+
+#include <config.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <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 "keydb.h" /* fixme: Move this to import.c */
+#include "../common/membuf.h"
+
+
+static ASSUAN_CONTEXT agent_ctx = NULL;
+static int force_pipe_server = 0;
+
+struct cipher_parm_s {
+ ASSUAN_CONTEXT ctx;
+ const char *ciphertext;
+ size_t ciphertextlen;
+};
+
+struct genkey_parm_s {
+ ASSUAN_CONTEXT ctx;
+ const char *sexp;
+ size_t sexplen;
+};
+
+struct learn_parm_s {
+ int error;
+ ASSUAN_CONTEXT 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 (void)
+{
+ int rc = 0;
+ char *infostr, *p;
+ ASSUAN_CONTEXT ctx;
+ char *dft_display = NULL;
+ char *dft_ttyname = NULL;
+ char *dft_ttytype = NULL;
+ char *old_lc = NULL;
+ char *dft_lc = NULL;
+
+ if (agent_ctx)
+ return 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 */
+
+ infostr = force_pipe_server? NULL : getenv ("GPG_AGENT_INFO");
+ if (!infostr)
+ {
+ const char *pgmname;
+ const char *argv[3];
+ int no_close_list[3];
+ int i;
+
+ if (opt.verbose)
+ log_info (_("no running gpg-agent - starting one\n"));
+
+ 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;
+ }
+
+ if (!opt.agent_program || !*opt.agent_program)
+ opt.agent_program = GNUPG_DEFAULT_AGENT;
+ if ( !(pgmname = strrchr (opt.agent_program, '/')))
+ pgmname = opt.agent_program;
+ else
+ pgmname++;
+
+ argv[0] = pgmname;
+ argv[1] = "--server";
+ argv[2] = NULL;
+
+ i=0;
+ if (log_get_fd () != -1)
+ no_close_list[i++] = log_get_fd ();
+ no_close_list[i++] = fileno (stderr);
+ no_close_list[i] = -1;
+
+ /* connect to the agent and perform initial handshaking */
+ rc = assuan_pipe_connect (&ctx, opt.agent_program, (char**)argv,
+ no_close_list);
+ }
+ else
+ {
+ int prot;
+ int pid;
+
+ infostr = xstrdup (infostr);
+ if ( !(p = strchr (infostr, ':')) || p == infostr)
+ {
+ log_error (_("malformed GPG_AGENT_INFO environment variable\n"));
+ xfree (infostr);
+ force_pipe_server = 1;
+ return start_agent ();
+ }
+ *p++ = 0;
+ pid = atoi (p);
+ while (*p && *p != ':')
+ p++;
+ prot = *p? atoi (p+1) : 0;
+ if (prot != 1)
+ {
+ log_error (_("gpg-agent protocol version %d is not supported\n"),
+ prot);
+ xfree (infostr);
+ force_pipe_server = 1;
+ return start_agent ();
+ }
+
+ rc = assuan_socket_connect (&ctx, infostr, pid);
+ xfree (infostr);
+ if (rc == ASSUAN_Connect_Failed)
+ {
+ log_error (_("can't connect to the agent - trying fall back\n"));
+ force_pipe_server = 1;
+ return start_agent ();
+ }
+ }
+
+ if (rc)
+ {
+ log_error ("can't connect to the agent: %s\n", assuan_strerror (rc));
+ return gpg_error (GPG_ERR_NO_AGENT);
+ }
+ agent_ctx = ctx;
+
+ if (DBG_ASSUAN)
+ log_debug ("connection to agent established\n");
+
+ rc = assuan_transact (agent_ctx, "RESET", NULL, NULL, NULL, NULL, NULL, NULL);
+ if (rc)
+ return map_assuan_err (rc);
+
+ dft_display = getenv ("DISPLAY");
+ if (opt.display || dft_display)
+ {
+ char *optstr;
+ if (asprintf (&optstr, "OPTION display=%s",
+ opt.display ? opt.display : dft_display) < 0)
+ return OUT_OF_CORE (errno);
+ rc = assuan_transact (agent_ctx, optstr, NULL, NULL, NULL, NULL, NULL,
+ NULL);
+ free (optstr);
+ if (rc)
+ return map_assuan_err (rc);
+ }
+ if (!opt.ttyname)
+ {
+ dft_ttyname = getenv ("GPG_TTY");
+ if ((!dft_ttyname || !*dft_ttyname) && ttyname (0))
+ dft_ttyname = ttyname (0);
+ }
+ if (opt.ttyname || dft_ttyname)
+ {
+ char *optstr;
+ if (asprintf (&optstr, "OPTION ttyname=%s",
+ opt.ttyname ? opt.ttyname : dft_ttyname) < 0)
+ return OUT_OF_CORE (errno);
+ rc = assuan_transact (agent_ctx, optstr, NULL, NULL, NULL, NULL, NULL,
+ NULL);
+ free (optstr);
+ if (rc)
+ return map_assuan_err (rc);
+ }
+ dft_ttytype = getenv ("TERM");
+ if (opt.ttytype || (dft_ttyname && dft_ttytype))
+ {
+ char *optstr;
+ if (asprintf (&optstr, "OPTION ttytype=%s",
+ opt.ttyname ? opt.ttytype : dft_ttytype) < 0)
+ return OUT_OF_CORE (errno);
+ rc = assuan_transact (agent_ctx, optstr, NULL, NULL, NULL, NULL, NULL,
+ NULL);
+ free (optstr);
+ if (rc)
+ return map_assuan_err (rc);
+ }
+#if defined(HAVE_SETLOCALE) && defined(LC_CTYPE)
+ old_lc = setlocale (LC_CTYPE, NULL);
+ if (old_lc)
+ {
+ old_lc = strdup (old_lc);
+ if (!old_lc)
+ return OUT_OF_CORE (errno);
+ }
+ dft_lc = setlocale (LC_CTYPE, "");
+#endif
+ if (opt.lc_ctype || (dft_ttyname && dft_lc))
+ {
+ char *optstr;
+ if (asprintf (&optstr, "OPTION lc-ctype=%s",
+ opt.lc_ctype ? opt.lc_ctype : dft_lc) < 0)
+ rc = OUT_OF_CORE (errno);
+ else
+ {
+ rc = assuan_transact (agent_ctx, optstr, NULL, NULL, NULL, NULL, NULL,
+ NULL);
+ free (optstr);
+ if (rc)
+ rc = map_assuan_err (rc);
+ }
+ }
+#if defined(HAVE_SETLOCALE) && defined(LC_CTYPE)
+ if (old_lc)
+ {
+ setlocale (LC_CTYPE, old_lc);
+ free (old_lc);
+ }
+#endif
+ if (rc)
+ return rc;
+#if defined(HAVE_SETLOCALE) && defined(LC_MESSAGES)
+ old_lc = setlocale (LC_MESSAGES, NULL);
+ if (old_lc)
+ {
+ old_lc = strdup (old_lc);
+ if (!old_lc)
+ return OUT_OF_CORE (errno);
+ }
+ dft_lc = setlocale (LC_MESSAGES, "");
+#endif
+ if (opt.lc_messages || (dft_ttyname && dft_lc))
+ {
+ char *optstr;
+ if (asprintf (&optstr, "OPTION lc-messages=%s",
+ opt.lc_messages ? opt.lc_messages : dft_lc) < 0)
+ rc = OUT_OF_CORE (errno);
+ else
+ {
+ rc = assuan_transact (agent_ctx, optstr, NULL, NULL, NULL, NULL, NULL,
+ NULL);
+ free (optstr);
+ if (rc)
+ rc = map_assuan_err (rc);
+ }
+ }
+#if defined(HAVE_SETLOCALE) && defined(LC_MESSAGES)
+ if (old_lc)
+ {
+ setlocale (LC_MESSAGES, old_lc);
+ free (old_lc);
+ }
+#endif
+
+ return rc;
+}
+
+
+static AssuanError
+membuf_data_cb (void *opaque, const void *buffer, size_t length)
+{
+ membuf_t *data = opaque;
+
+ if (buffer)
+ put_membuf (data, buffer, length);
+ return 0;
+}
+
+
+
+
+/* Call the agent to do a sign operation using the key identified by
+ the hex string KEYGRIP. */
+int
+gpgsm_agent_pksign (const char *keygrip,
+ unsigned char *digest, size_t digestlen, int digestalgo,
+ 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 ();
+ 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 map_assuan_err (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 map_assuan_err (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 map_assuan_err (rc);
+
+ init_membuf (&data, 1024);
+ rc = assuan_transact (agent_ctx, "PKSIGN",
+ membuf_data_cb, &data, NULL, NULL, NULL, NULL);
+ if (rc)
+ {
+ xfree (get_membuf (&data, &len));
+ return map_assuan_err (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 (errno);
+}
+
+
+
+
+/* Handle a CIPHERTEXT inquiry. Note, we only send the data,
+ assuan_transact talkes care of flushing and writing the end */
+static AssuanError
+inq_ciphertext_cb (void *opaque, const char *keyword)
+{
+ struct cipher_parm_s *parm = opaque;
+ AssuanError rc;
+
+ assuan_begin_confidential (parm->ctx);
+ rc = assuan_send_data (parm->ctx, parm->ciphertext, parm->ciphertextlen);
+ assuan_end_confidential (parm->ctx);
+ return rc;
+}
+
+
+/* Call the agent to do a decrypt operation using the key identified by
+ the hex string KEYGRIP. */
+int
+gpgsm_agent_pkdecrypt (const char *keygrip,
+ KsbaConstSexp 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 *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 ();
+ if (rc)
+ return rc;
+
+ rc = assuan_transact (agent_ctx, "RESET", NULL, NULL, NULL, NULL, NULL, NULL);
+ if (rc)
+ return map_assuan_err (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 map_assuan_err (rc);
+
+ init_membuf (&data, 1024);
+ 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 map_assuan_err (rc);
+ }
+
+ put_membuf (&data, "", 1); /* make sure it is 0 terminated */
+ buf = get_membuf (&data, &len);
+ if (!buf)
+ return gpg_error (GPG_ERR_ENOMEM);
+ /* FIXME: We would better a return a full S-exp and not just a part */
+ assert (len);
+ len--; /* remove the terminating 0 */
+ n = strtoul (buf, &endp, 10);
+ if (!n || *endp != ':')
+ return gpg_error (GPG_ERR_INV_SEXP);
+ endp++;
+ if (endp-buf+n > len)
+ return gpg_error (GPG_ERR_INV_SEXP); /* oops len does not
+ match internal len*/
+ 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 AssuanError
+inq_genkey_parms (void *opaque, const char *keyword)
+{
+ struct genkey_parm_s *parm = opaque;
+ AssuanError rc;
+
+ rc = assuan_send_data (parm->ctx, parm->sexp, parm->sexplen);
+ return rc;
+}
+
+
+
+/* Call the agent to generate a newkey */
+int
+gpgsm_agent_genkey (KsbaConstSexp keyparms, KsbaSexp *r_pubkey)
+{
+ int rc;
+ struct genkey_parm_s gk_parm;
+ membuf_t data;
+ size_t len;
+ char *buf;
+
+ *r_pubkey = NULL;
+ rc = start_agent ();
+ if (rc)
+ return rc;
+
+ rc = assuan_transact (agent_ctx, "RESET", NULL, NULL, NULL, NULL, NULL, NULL);
+ if (rc)
+ return map_assuan_err (rc);
+
+ init_membuf (&data, 1024);
+ 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 map_assuan_err (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;
+}
+
+
+/* Ask the agent whether the certificate is in the list of trusted
+ keys */
+int
+gpgsm_agent_istrusted (KsbaCert cert)
+{
+ int rc;
+ char *fpr;
+ char line[ASSUAN_LINELENGTH];
+
+ rc = start_agent ();
+ 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);
+ }
+
+ 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, NULL, NULL);
+ return map_assuan_err (rc);
+}
+
+/* Ask the agent to mark CERT as a trusted Root-CA one */
+int
+gpgsm_agent_marktrusted (KsbaCert cert)
+{
+ int rc;
+ char *fpr, *dn;
+ char line[ASSUAN_LINELENGTH];
+
+ rc = start_agent ();
+ 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);
+ }
+ snprintf (line, DIM(line)-1, "MARKTRUSTED %s S %s", fpr, dn);
+ line[DIM(line)-1] = 0;
+ ksba_free (dn);
+ xfree (fpr);
+
+ rc = assuan_transact (agent_ctx, line, NULL, NULL, NULL, NULL, NULL, NULL);
+ return map_assuan_err (rc);
+}
+
+
+
+/* Ask the agent whether the a corresponding secret key is available
+ for the given keygrip */
+int
+gpgsm_agent_havekey (const char *hexkeygrip)
+{
+ int rc;
+ char line[ASSUAN_LINELENGTH];
+
+ rc = start_agent ();
+ 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 map_assuan_err (rc);
+}
+
+
+static AssuanError
+learn_cb (void *opaque, const void *buffer, size_t length)
+{
+ struct learn_parm_s *parm = opaque;
+ size_t len;
+ char *buf;
+ KsbaCert 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;
+ }
+
+
+ /* FIXME: this should go into import.c */
+ cert = ksba_cert_new ();
+ if (!cert)
+ {
+ parm->error = gpg_error (GPG_ERR_ENOMEM);
+ return 0;
+ }
+ rc = ksba_cert_init_from_mem (cert, buf, len);
+ if (rc)
+ {
+ log_error ("failed to parse a certificate: %s\n", ksba_strerror (rc));
+ ksba_cert_release (cert);
+ parm->error = map_ksba_err (rc);
+ return 0;
+ }
+
+ rc = gpgsm_basic_cert_check (cert);
+ if (gpg_err_code (rc) == GPG_ERR_MISSING_CERT)
+ { /* For later use we store it in the ephemeral database. */
+ log_info ("issuer certificate missing - storing as ephemeral\n");
+ keydb_store_cert (cert, 1, NULL);
+ }
+ else if (rc)
+ 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 ()
+{
+ int rc;
+ struct learn_parm_s learn_parm;
+ membuf_t data;
+ size_t len;
+
+ rc = start_agent ();
+ if (rc)
+ return rc;
+
+ init_membuf (&data, 4096);
+ learn_parm.error = 0;
+ learn_parm.ctx = agent_ctx;
+ learn_parm.data = &data;
+ rc = assuan_transact (agent_ctx, "LEARN --send",
+ learn_cb, &learn_parm,
+ NULL, NULL, NULL, NULL);
+ xfree (get_membuf (&data, &len));
+ if (rc)
+ return map_assuan_err (rc);
+ return learn_parm.error;
+}
+
+
+/* Ask the agent to change the passphrase of the key identified by HEXKEYGRIP. */
+int
+gpgsm_agent_passwd (const char *hexkeygrip)
+{
+ int rc;
+ char line[ASSUAN_LINELENGTH];
+
+ rc = start_agent ();
+ if (rc)
+ return rc;
+
+ if (!hexkeygrip || strlen (hexkeygrip) != 40)
+ return gpg_error (GPG_ERR_INV_VALUE);
+
+ snprintf (line, DIM(line)-1, "PASSWD %s", hexkeygrip);
+ line[DIM(line)-1] = 0;
+
+ rc = assuan_transact (agent_ctx, line, NULL, NULL, NULL, NULL, NULL, NULL);
+ return map_assuan_err (rc);
+}
+
diff --git a/sm/call-dirmngr.c b/sm/call-dirmngr.c
new file mode 100644
index 000000000..b182b246c
--- /dev/null
+++ b/sm/call-dirmngr.c
@@ -0,0 +1,632 @@
+/* call-dirmngr.c - communication with the dromngr
+ * Copyright (C) 2002, 2003 Free Software Foundation, Inc.
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuPG is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ */
+
+#include <config.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <time.h>
+#include <assert.h>
+#include <ctype.h>
+
+#include "gpgsm.h"
+#include <gcrypt.h>
+#include <assuan.h>
+
+#include "i18n.h"
+
+struct membuf {
+ size_t len;
+ size_t size;
+ char *buf;
+ int out_of_core;
+};
+
+
+
+static ASSUAN_CONTEXT dirmngr_ctx = NULL;
+static int force_pipe_server = 0;
+
+struct inq_certificate_parm_s {
+ ASSUAN_CONTEXT ctx;
+ KsbaCert cert;
+};
+
+struct lookup_parm_s {
+ CTRL ctrl;
+ ASSUAN_CONTEXT ctx;
+ void (*cb)(void *, KsbaCert);
+ void *cb_value;
+ struct membuf data;
+ int error;
+};
+
+struct run_command_parm_s {
+ ASSUAN_CONTEXT ctx;
+};
+
+
+/* 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;
+}
+
+
+
+
+
+/* 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 (void)
+{
+ int rc;
+ char *infostr, *p;
+ ASSUAN_CONTEXT ctx;
+
+ if (dirmngr_ctx)
+ return 0; /* fixme: We need a context for each thread or serialize
+ the access to the dirmngr */
+
+ infostr = force_pipe_server? NULL : getenv ("DIRMNGR_INFO");
+ if (!infostr)
+ {
+ const char *pgmname;
+ const char *argv[3];
+ int no_close_list[3];
+ int i;
+
+ if (opt.verbose)
+ log_info (_("no running dirmngr - starting one\n"));
+
+ 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;
+ }
+
+ if (!opt.dirmngr_program || !*opt.dirmngr_program)
+ opt.dirmngr_program = GNUPG_DEFAULT_DIRMNGR;
+ if ( !(pgmname = strrchr (opt.dirmngr_program, '/')))
+ pgmname = opt.dirmngr_program;
+ else
+ pgmname++;
+
+ argv[0] = pgmname;
+ argv[1] = "--server";
+ argv[2] = NULL;
+
+ i=0;
+ if (log_get_fd () != -1)
+ no_close_list[i++] = log_get_fd ();
+ no_close_list[i++] = fileno (stderr);
+ no_close_list[i] = -1;
+
+ /* connect to the agent and perform initial handshaking */
+ rc = assuan_pipe_connect (&ctx, opt.dirmngr_program, (char**)argv,
+ no_close_list);
+ }
+ else
+ {
+ int prot;
+ int pid;
+
+ infostr = xstrdup (infostr);
+ if ( !(p = strchr (infostr, ':')) || p == infostr)
+ {
+ log_error (_("malformed DIRMNGR_INFO environment variable\n"));
+ xfree (infostr);
+ force_pipe_server = 1;
+ return start_dirmngr ();
+ }
+ *p++ = 0;
+ pid = atoi (p);
+ while (*p && *p != ':')
+ 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 ();
+ }
+
+ rc = assuan_socket_connect (&ctx, infostr, pid);
+ xfree (infostr);
+ if (rc == ASSUAN_Connect_Failed)
+ {
+ log_error (_("can't connect to the dirmngr - trying fall back\n"));
+ force_pipe_server = 1;
+ return start_dirmngr ();
+ }
+ }
+
+ if (rc)
+ {
+ log_error ("can't connect to the dirmngr: %s\n", assuan_strerror (rc));
+ return gpg_error (GPG_ERR_NO_DIRMNGR);
+ }
+ dirmngr_ctx = ctx;
+
+ if (DBG_ASSUAN)
+ log_debug ("connection to dirmngr established\n");
+ return 0;
+}
+
+
+
+/* Handle a SENDCERT inquiry. */
+static AssuanError
+inq_certificate (void *opaque, const char *line)
+{
+ struct inq_certificate_parm_s *parm = opaque;
+ AssuanError rc;
+ const unsigned char *der;
+ size_t derlen;
+
+ if (!(!strncmp (line, "SENDCERT", 8) && (line[8] == ' ' || !line[8])))
+ {
+ log_error ("unsupported inquiry `%s'\n", line);
+ return ASSUAN_Inquire_Unknown;
+ }
+ line += 8;
+
+ if (!*line)
+ { /* send the current certificate */
+ der = ksba_cert_get_image (parm->cert, &derlen);
+ if (!der)
+ rc = ASSUAN_Inquire_Error;
+ else
+ rc = assuan_send_data (parm->ctx, der, derlen);
+ }
+ else
+ { /* send the given certificate */
+ int err;
+ KsbaCert cert;
+
+ err = gpgsm_find_cert (line, &cert);
+ if (err)
+ {
+ log_error ("certificate not found: %s\n", gpg_strerror (err));
+ rc = ASSUAN_Inquire_Error;
+ }
+ else
+ {
+ der = ksba_cert_get_image (cert, &derlen);
+ if (!der)
+ rc = ASSUAN_Inquire_Error;
+ else
+ rc = assuan_send_data (parm->ctx, der, derlen);
+ ksba_cert_release (cert);
+ }
+ }
+
+ return rc;
+}
+
+
+
+/* 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
+ */
+int
+gpgsm_dirmngr_isvalid (KsbaCert cert)
+{
+ int rc;
+ char *certid;
+ char line[ASSUAN_LINELENGTH];
+ struct inq_certificate_parm_s parm;
+
+ rc = start_dirmngr ();
+ if (rc)
+ return rc;
+
+ certid = gpgsm_get_certid (cert);
+ if (!certid)
+ {
+ log_error ("error getting the certificate ID\n");
+ 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\n", fpr);
+ xfree (fpr);
+ }
+
+ parm.ctx = dirmngr_ctx;
+ parm.cert = cert;
+
+ snprintf (line, DIM(line)-1, "ISVALID %s", certid);
+ line[DIM(line)-1] = 0;
+ xfree (certid);
+
+ rc = assuan_transact (dirmngr_ctx, line, NULL, NULL,
+ inq_certificate, &parm, NULL, NULL);
+ if (opt.verbose > 1)
+ log_info ("response of dirmngr: %s\n", rc? assuan_strerror (rc): "okay");
+ return map_assuan_err (rc);
+}
+
+
+
+/* Lookup helpers*/
+static AssuanError
+lookup_cb (void *opaque, const void *buffer, size_t length)
+{
+ struct lookup_parm_s *parm = opaque;
+ size_t len;
+ char *buf;
+ KsbaCert 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;
+ }
+
+ cert = ksba_cert_new ();
+ if (!cert)
+ {
+ parm->error = gpg_error (GPG_ERR_ENOMEM);
+ return 0;
+ }
+ rc = ksba_cert_init_from_mem (cert, buf, len);
+ if (rc)
+ {
+ log_error ("failed to parse a certificate: %s\n", ksba_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 names)
+{
+ STRLIST 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 (n=0, 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 AssuanError
+lookup_status_cb (void *opaque, const char *line)
+{
+ struct lookup_parm_s *parm = opaque;
+
+ 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 Directroy Managers 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. */
+int
+gpgsm_dirmngr_lookup (CTRL ctrl, STRLIST names,
+ void (*cb)(void*, KsbaCert), void *cb_value)
+{
+ int rc;
+ char *pattern;
+ char line[ASSUAN_LINELENGTH];
+ struct lookup_parm_s parm;
+ size_t len;
+
+ rc = start_dirmngr ();
+ if (rc)
+ return rc;
+
+ pattern = pattern_from_strlist (names);
+ if (!pattern)
+ return OUT_OF_CORE (errno);
+ snprintf (line, DIM(line)-1, "LOOKUP %s", pattern);
+ line[DIM(line)-1] = 0;
+ xfree (pattern);
+
+ parm.ctrl = ctrl;
+ parm.ctx = dirmngr_ctx;
+ parm.cb = cb;
+ parm.cb_value = cb_value;
+ parm.error = 0;
+ init_membuf (&parm.data, 4096);
+
+ rc = assuan_transact (dirmngr_ctx, line, lookup_cb, &parm,
+ NULL, NULL, lookup_status_cb, &parm);
+ xfree (get_membuf (&parm.data, &len));
+ if (rc)
+ return map_assuan_err (rc);
+ return parm.error;
+}
+
+
+
+/* Run Command helpers*/
+
+/* Fairly simple callback to write all output of dirmngr to stdout. */
+static AssuanError
+run_command_cb (void *opaque, const void *buffer, size_t length)
+{
+ 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 AssuanError
+run_command_inq_cb (void *opaque, const char *line)
+{
+ struct run_command_parm_s *parm = opaque;
+ AssuanError rc = 0;
+
+ if ( !strncmp (line, "SENDCERT", 8) && (line[8] == ' ' || !line[8]) )
+ { /* send the given certificate */
+ int err;
+ KsbaCert cert;
+ const unsigned char *der;
+ size_t derlen;
+
+ line += 8;
+ if (!*line)
+ return ASSUAN_Inquire_Error;
+
+ err = gpgsm_find_cert (line, &cert);
+ if (err)
+ {
+ log_error ("certificate not found: %s\n", gpg_strerror (err));
+ rc = ASSUAN_Inquire_Error;
+ }
+ else
+ {
+ der = ksba_cert_get_image (cert, &derlen);
+ if (!der)
+ rc = ASSUAN_Inquire_Error;
+ 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 = ASSUAN_Inquire_Unknown;
+ }
+
+ return rc;
+}
+
+static AssuanError
+run_command_status_cb (void *opaque, const char *line)
+{
+ if (opt.verbose)
+ {
+ log_info ("dirmngr status: %s\n", line);
+ }
+ 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 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 ();
+ 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)
+ return OUT_OF_CORE (errno);
+
+ 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", *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, NULL);
+ xfree (line);
+ log_info ("response of dirmngr: %s\n", rc? assuan_strerror (rc): "okay");
+ return map_assuan_err (rc);
+}
diff --git a/sm/certchain.c b/sm/certchain.c
new file mode 100644
index 000000000..6323c725e
--- /dev/null
+++ b/sm/certchain.c
@@ -0,0 +1,793 @@
+/* certchain.c - certificate chain validation
+ * 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 2 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuPG is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ */
+
+#include <config.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <time.h>
+#include <assert.h>
+
+#include "gpgsm.h"
+#include <gcrypt.h>
+#include <ksba.h>
+
+#include "keydb.h"
+#include "i18n.h"
+
+static int
+unknown_criticals (KsbaCert cert)
+{
+ static const char *known[] = {
+ "2.5.29.15", /* keyUsage */
+ "2.5.29.19", /* basic Constraints */
+ "2.5.29.32", /* certificatePolicies */
+ NULL
+ };
+ int rc = 0, i, idx, crit;
+ const char *oid;
+ KsbaError err;
+
+ 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++)
+ ;
+ if (!known[i])
+ {
+ log_error (_("critical certificate extension %s is not supported\n"),
+ oid);
+ rc = gpg_error (GPG_ERR_UNSUPPORTED_CERT);
+ }
+ }
+ if (err && err != -1)
+ rc = map_ksba_err (err);
+
+ return rc;
+}
+
+static int
+allowed_ca (KsbaCert cert, int *chainlen)
+{
+ KsbaError err;
+ int flag;
+
+ err = ksba_cert_is_ca (cert, &flag, chainlen);
+ if (err)
+ return map_ksba_err (err);
+ if (!flag)
+ {
+ log_error (_("issuer certificate is not marked as a CA\n"));
+ return gpg_error (GPG_ERR_BAD_CA_CERT);
+ }
+ return 0;
+}
+
+
+static int
+check_cert_policy (KsbaCert cert)
+{
+ KsbaError err;
+ char *policies;
+ FILE *fp;
+ int any_critical;
+
+ err = ksba_cert_get_cert_policies (cert, &policies);
+ if (err == KSBA_No_Data)
+ return 0; /* no policy given */
+ if (err)
+ return map_ksba_err (err);
+
+ /* STRING is a line delimited list of certifiate 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)
+ 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)
+ {
+ log_error ("critical marked policy without configured policies\n");
+ return gpg_error (GPG_ERR_NO_POLICY_MATCH);
+ }
+ return 0;
+ }
+
+ fp = fopen (opt.policy_file, "r");
+ if (!fp)
+ {
+ log_error ("failed to open `%s': %s\n",
+ opt.policy_file, strerror (errno));
+ xfree (policies);
+ 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;
+
+ xfree (policies);
+ if (feof (fp))
+ {
+ fclose (fp);
+ /* with no critical policies this is only a warning */
+ if (!any_critical)
+ {
+ log_info (_("note: certificate policy not allowed\n"));
+ return 0;
+ }
+ log_error (_("certificate policy not allowed\n"));
+ return gpg_error (GPG_ERR_NO_POLICY_MATCH);
+ }
+ tmperr = gpg_error (gpg_err_code_from_errno (errno));
+ 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;
+ }
+ }
+}
+
+
+static void
+find_up_store_certs_cb (void *cb_value, KsbaCert cert)
+{
+ if (keydb_store_cert (cert, 1, NULL))
+ log_error ("error storing issuer certificate as ephemeral\n");
+ ++*(int*)cb_value;
+}
+
+
+static int
+find_up (KEYDB_HANDLE kh, KsbaCert cert, const char *issuer)
+{
+ KsbaName authid;
+ KsbaSexp authidno;
+ int rc = -1;
+
+ if (!ksba_cert_get_auth_key_id (cert, NULL, &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);
+ if (rc == -1)
+ { /* And try the ephemeral DB. */
+ 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);
+ }
+ }
+ /* print a note so that the user does not feel too helpless when
+ an issuer certificate was found and gpgsm prints BAD
+ signature becuase it is not the correct one. */
+ if (rc == -1)
+ {
+ log_info ("issuer certificate (#");
+ gpgsm_dump_serial (authidno);
+ log_printf ("/");
+ gpgsm_dump_string (s);
+ log_printf (") not found\n");
+ }
+ else if (rc)
+ log_error ("failed to find authorityKeyIdentifier: rc=%d\n", rc);
+ ksba_name_release (authid);
+ xfree (authidno);
+ /* Fixme: don't know how to do dirmngr lookup with serial+issuer. */
+ }
+
+ if (rc) /* not found via authorithyKeyIdentifier, try regular issuer name */
+ rc = keydb_search_subject (kh, issuer);
+ if (rc == -1)
+ {
+ /* Not found, lets see whether we have one in the ephemeral key DB. */
+ int old = keydb_set_ephemeral (kh, 1);
+ if (!old)
+ {
+ keydb_search_reset (kh);
+ rc = keydb_search_subject (kh, issuer);
+ }
+ keydb_set_ephemeral (kh, old);
+ }
+
+ if (rc == -1 && opt.auto_issuer_key_retrieve)
+ {
+ STRLIST names = NULL;
+ int count = 0;
+ char *pattern;
+ const char *s;
+
+ if (opt.verbose)
+ log_info (_("looking up issuer at external location\n"));
+ /* dirmngr is confused about unknown attributes so has a quick
+ and ugly hack we locate the CN and use this and the
+ following. Fixme: we should have far better parsing in the
+ dirmngr. */
+ s = strstr (issuer, "CN=");
+ if (!s || s == issuer || s[-1] != ',')
+ s = issuer;
+
+ pattern = xtrymalloc (strlen (s)+2);
+ if (!pattern)
+ return OUT_OF_CORE (errno);
+ strcpy (stpcpy (pattern, "/"), s);
+ add_to_strlist (&names, pattern);
+ xfree (pattern);
+ rc = gpgsm_dirmngr_lookup (NULL, names, 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);
+ keydb_search_reset (kh);
+ rc = keydb_search_subject (kh, issuer);
+ keydb_set_ephemeral (kh, old);
+ }
+ }
+ 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 (KsbaCert start, KsbaCert *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 (!strcmp (issuer, subject))
+ {
+ rc = -1; /* we are at the root */
+ goto leave;
+ }
+
+ rc = find_up (kh, start, issuer);
+ 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_CERT);
+ goto leave;
+ }
+
+ rc = keydb_get_cert (kh, r_next);
+ if (rc)
+ {
+ log_error ("failed to get cert: rc=%d\n", rc);
+ rc = gpg_error (GPG_ERR_GENERAL);
+ }
+
+ leave:
+ xfree (issuer);
+ xfree (subject);
+ keydb_release (kh);
+ return rc;
+}
+
+
+/* Check whether the CERT is a root certificate. Returns True if this
+ is the case. */
+int
+gpgsm_is_root_cert (KsbaCert cert)
+{
+ char *issuer;
+ char *subject;
+ int yes;
+
+ issuer = ksba_cert_get_issuer (cert, 0);
+ subject = ksba_cert_get_subject (cert, 0);
+ yes = (issuer && subject && !strcmp (issuer, subject));
+ xfree (issuer);
+ xfree (subject);
+ return yes;
+}
+
+
+/* Validate a chain and optionally return the nearest expiration time
+ in R_EXPTIME */
+int
+gpgsm_validate_chain (CTRL ctrl, KsbaCert cert, time_t *r_exptime)
+{
+ int rc = 0, depth = 0, maxdepth;
+ char *issuer = NULL;
+ char *subject = NULL;
+ KEYDB_HANDLE kh = keydb_new (0);
+ KsbaCert subject_cert = NULL, issuer_cert = NULL;
+ time_t current_time = gnupg_get_time ();
+ time_t exptime = 0;
+ 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;
+
+ if (r_exptime)
+ *r_exptime = 0;
+
+ if (opt.no_chain_validation)
+ {
+ log_info ("WARNING: bypassing certificate chain validation\n");
+ return 0;
+ }
+
+ if (!kh)
+ {
+ log_error (_("failed to allocated keyDB handle\n"));
+ rc = gpg_error (GPG_ERR_GENERAL);
+ goto leave;
+ }
+
+ if (DBG_X509)
+ gpgsm_dump_cert ("subject", cert);
+
+ subject_cert = cert;
+ maxdepth = 50;
+
+ for (;;)
+ {
+ xfree (issuer);
+ xfree (subject);
+ issuer = ksba_cert_get_issuer (subject_cert, 0);
+ subject = ksba_cert_get_subject (subject_cert, 0);
+
+ if (!issuer)
+ {
+ log_error ("no issuer found in certificate\n");
+ rc = gpg_error (GPG_ERR_BAD_CERT);
+ goto leave;
+ }
+
+ {
+ time_t not_before, not_after;
+
+ not_before = ksba_cert_get_validity (subject_cert, 0);
+ not_after = ksba_cert_get_validity (subject_cert, 1);
+ if (not_before == (time_t)(-1) || not_after == (time_t)(-1))
+ {
+ log_error ("certificate with invalid validity\n");
+ rc = gpg_error (GPG_ERR_BAD_CERT);
+ goto leave;
+ }
+
+ if (not_after)
+ {
+ if (!exptime)
+ exptime = not_after;
+ else if (not_after < exptime)
+ exptime = not_after;
+ }
+
+ if (not_before && current_time < not_before)
+ {
+ log_error ("certificate too young; valid from ");
+ gpgsm_dump_time (not_before);
+ log_printf ("\n");
+ rc = gpg_error (GPG_ERR_CERT_TOO_YOUNG);
+ goto leave;
+ }
+ if (not_after && current_time > not_after)
+ {
+ log_error ("certificate has expired at ");
+ gpgsm_dump_time (not_after);
+ log_printf ("\n");
+ any_expired = 1;
+ }
+ }
+
+ rc = unknown_criticals (subject_cert);
+ if (rc)
+ goto leave;
+
+ if (!opt.no_policy_check)
+ {
+ rc = check_cert_policy (subject_cert);
+ if (gpg_err_code (rc) == GPG_ERR_NO_POLICY_MATCH)
+ {
+ any_no_policy_match = 1;
+ rc = 1;
+ }
+ else if (rc)
+ goto leave;
+ }
+
+ if (!opt.no_crl_check)
+ {
+ rc = gpgsm_dirmngr_isvalid (subject_cert);
+ if (rc)
+ {
+ switch (rc)
+ {
+ case GPG_ERR_CERT_REVOKED:
+ log_error (_("the certificate has been revoked\n"));
+ any_revoked = 1;
+ break;
+ case GPG_ERR_NO_CRL_KNOWN:
+ log_error (_("no CRL found for certificate\n"));
+ any_no_crl = 1;
+ break;
+ case GPG_ERR_CRL_TOO_OLD:
+ log_error (_("the available CRL is too old\n"));
+ log_info (_("please make sure that the "
+ "\"dirmngr\" is properly installed\n"));
+ any_crl_too_old = 1;
+ break;
+ default:
+ log_error (_("checking the CRL failed: %s\n"),
+ gpg_strerror (rc));
+ goto leave;
+ }
+ rc = 0;
+ }
+ }
+
+ if (subject && !strcmp (issuer, subject))
+ {
+ if (gpgsm_check_cert_sig (subject_cert, subject_cert) )
+ {
+ log_error ("selfsigned certificate has a BAD signatures\n");
+ rc = gpg_error (depth? GPG_ERR_BAD_CERT_CHAIN
+ : GPG_ERR_BAD_CERT);
+ goto leave;
+ }
+ rc = allowed_ca (subject_cert, NULL);
+ if (rc)
+ goto leave;
+
+ rc = gpgsm_agent_istrusted (subject_cert);
+ if (!rc)
+ ;
+ else if (gpg_err_code (rc) == GPG_ERR_NOT_TRUSTED)
+ {
+ int rc2;
+
+ char *fpr = gpgsm_get_fingerprint_string (subject_cert,
+ GCRY_MD_SHA1);
+ log_info (_("root certificate is not marked trusted\n"));
+ log_info (_("fingerprint=%s\n"), fpr? fpr : "?");
+ xfree (fpr);
+ rc2 = gpgsm_agent_marktrusted (subject_cert);
+ if (!rc2)
+ {
+ log_info (_("root certificate has now"
+ " been marked as trusted\n"));
+ rc = 0;
+ }
+ else
+ {
+ gpgsm_dump_cert ("issuer", subject_cert);
+ log_info ("after checking the fingerprint, you may want "
+ "to enter it manually into "
+ "\"~/.gnupg-test/trustlist.txt\"\n");
+ }
+ }
+ else
+ {
+ log_error (_("checking the trust list failed: %s\n"),
+ gpg_strerror (rc));
+ }
+
+ break; /* okay, a self-signed certicate is an end-point */
+ }
+
+ depth++;
+ if (depth > maxdepth)
+ {
+ log_error (_("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 (kh, subject_cert, issuer);
+ 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_CERT);
+ goto leave;
+ }
+
+ ksba_cert_release (issuer_cert); issuer_cert = NULL;
+ rc = keydb_get_cert (kh, &issuer_cert);
+ if (rc)
+ {
+ log_error ("failed to get cert: rc=%d\n", rc);
+ rc = gpg_error (GPG_ERR_GENERAL);
+ goto leave;
+ }
+
+ if (DBG_X509)
+ {
+ log_debug ("got issuer's certificate:\n");
+ gpgsm_dump_cert ("issuer", issuer_cert);
+ }
+
+ if (gpgsm_check_cert_sig (issuer_cert, subject_cert) )
+ {
+ log_error ("certificate has a BAD signatures\n");
+ rc = gpg_error (GPG_ERR_BAD_CERT_CHAIN);
+ goto leave;
+ }
+
+ {
+ int chainlen;
+ rc = allowed_ca (issuer_cert, &chainlen);
+ if (rc)
+ goto leave;
+ if (chainlen >= 0 && (depth - 1) > chainlen)
+ {
+ log_error (_("certificate chain longer than allowed by CA (%d)\n"),
+ chainlen);
+ rc = gpg_error (GPG_ERR_BAD_CERT_CHAIN);
+ goto leave;
+ }
+ }
+
+ 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);
+ rc = 0;
+ }
+
+ if (opt.verbose)
+ log_info ("certificate is good\n");
+
+ keydb_search_reset (kh);
+ subject_cert = issuer_cert;
+ issuer_cert = NULL;
+ }
+
+ if (opt.no_policy_check)
+ log_info ("policies not checked due to --disable-policy-checks option\n");
+ if (opt.no_crl_check)
+ log_info ("CRLs not checked due to --disable-crl-checks option\n");
+
+ 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_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);
+ else if (any_expired)
+ rc = gpg_error (GPG_ERR_CERT_EXPIRED);
+ }
+
+ leave:
+ if (r_exptime)
+ *r_exptime = exptime;
+ xfree (issuer);
+ keydb_release (kh);
+ ksba_cert_release (issuer_cert);
+ if (subject_cert != cert)
+ ksba_cert_release (subject_cert);
+ 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 (KsbaCert cert)
+{
+ int rc = 0;
+ char *issuer = NULL;
+ char *subject = NULL;
+ KEYDB_HANDLE kh = keydb_new (0);
+ KsbaCert issuer_cert = NULL;
+
+ if (opt.no_chain_validation)
+ {
+ log_info ("WARNING: bypassing basic certificate checks\n");
+ return 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 (subject && !strcmp (issuer, subject))
+ {
+ if (gpgsm_check_cert_sig (cert, cert) )
+ {
+ log_error ("selfsigned certificate has a BAD signatures\n");
+ rc = gpg_error (GPG_ERR_BAD_CERT);
+ goto leave;
+ }
+ }
+ else
+ {
+ /* find the next cert up the tree */
+ keydb_search_reset (kh);
+ rc = find_up (kh, cert, issuer);
+ 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_CERT);
+ goto leave;
+ }
+
+ ksba_cert_release (issuer_cert); issuer_cert = NULL;
+ rc = keydb_get_cert (kh, &issuer_cert);
+ if (rc)
+ {
+ log_error ("failed to get cert: rc=%d\n", rc);
+ rc = gpg_error (GPG_ERR_GENERAL);
+ goto leave;
+ }
+
+ if (gpgsm_check_cert_sig (issuer_cert, cert) )
+ {
+ log_error ("certificate has a BAD signatures\n");
+ rc = gpg_error (GPG_ERR_BAD_CERT);
+ goto leave;
+ }
+ if (opt.verbose)
+ log_info ("certificate is good\n");
+ }
+
+ leave:
+ xfree (issuer);
+ keydb_release (kh);
+ ksba_cert_release (issuer_cert);
+ return rc;
+}
+
diff --git a/sm/certcheck.c b/sm/certcheck.c
new file mode 100644
index 000000000..35509c67e
--- /dev/null
+++ b/sm/certcheck.c
@@ -0,0 +1,300 @@
+/* certcheck.c - check one certificate
+ * 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 2 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuPG is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ */
+
+#include <config.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <time.h>
+#include <assert.h>
+
+#include "gpgsm.h"
+#include <gcrypt.h>
+#include <ksba.h>
+
+#include "keydb.h"
+#include "i18n.h"
+
+
+static int
+do_encode_md (gcry_md_hd_t md, int algo, unsigned int nbits,
+ gcry_mpi_t *r_val)
+{
+ int nframe = (nbits+7) / 8;
+ byte *frame;
+ int i, n;
+ byte asn[100];
+ size_t asnlen;
+ size_t len;
+
+ asnlen = DIM(asn);
+ 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 (errno);
+ 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_X509)
+ {
+ 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;
+}
+
+
+/*
+ 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 (KsbaCert issuer_cert, KsbaCert cert)
+{
+ const char *algoid;
+ gcry_md_hd_t md;
+ int rc, algo;
+ gcry_mpi_t frame;
+ KsbaSexp 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:"?");
+ 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", ksba_strerror (rc));
+ gcry_md_close (md);
+ return map_ksba_err (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_X509)
+ {
+ 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, 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, 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, gcry_pk_get_nbits (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_CRYPTO)
+ 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 (KsbaCert cert, KsbaConstSexp sigval,
+ gcry_md_hd_t md, int algo)
+{
+ int rc;
+ KsbaSexp p;
+ gcry_mpi_t frame;
+ gcry_sexp_t s_sig, s_hash, s_pkey;
+ size_t n;
+
+ 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, 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_X509)
+ log_printhex ("public key: ", p, n);
+
+ rc = gcry_sexp_sscan ( &s_pkey, NULL, 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;
+ }
+
+
+ rc = do_encode_md (md, algo, gcry_pk_get_nbits (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_CRYPTO)
+ 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 (KsbaCert cert, gcry_md_hd_t md, int mdalgo,
+ char **r_sigval)
+{
+ int rc;
+ char *grip;
+ size_t siglen;
+
+ grip = gpgsm_get_keygrip_hexstring (cert);
+ if (!grip)
+ return gpg_error (GPG_ERR_BAD_CERT);
+
+ rc = gpgsm_agent_pksign (grip, gcry_md_read(md, mdalgo),
+ gcry_md_get_algo_dlen (mdalgo), mdalgo,
+ r_sigval, &siglen);
+ xfree (grip);
+ return rc;
+}
+
+
+
diff --git a/sm/certdump.c b/sm/certdump.c
new file mode 100644
index 000000000..703e07186
--- /dev/null
+++ b/sm/certdump.c
@@ -0,0 +1,457 @@
+/* certdump.c - Dump a certificate for debugging
+ * Copyright (C) 2001 Free Software Foundation, Inc.
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuPG is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ */
+
+#include <config.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <time.h>
+#include <assert.h>
+
+#include "gpgsm.h"
+#include <gcrypt.h>
+#include <ksba.h>
+
+#include "keydb.h"
+#include "i18n.h"
+
+struct dn_array_s {
+ char *key;
+ char *value;
+};
+
+
+/* print the first element of an S-Expression */
+void
+gpgsm_print_serial (FILE *fp, KsbaConstSexp p)
+{
+ unsigned long n;
+ KsbaConstSexp endp;
+
+ if (!p)
+ fputs (_("none"), fp);
+ else if (*p != '(')
+ fputs ("[Internal error - not an S-expression]", fp);
+ else
+ {
+ p++;
+ n = strtoul (p, (char**)&endp, 10);
+ p = endp;
+ if (*p!=':')
+ fputs ("[Internal Error - invalid S-expression]", fp);
+ else
+ {
+ for (p++; n; n--, p++)
+ fprintf (fp, "%02X", *p);
+ }
+ }
+}
+
+
+void
+gpgsm_dump_serial (KsbaConstSexp p)
+{
+ unsigned long n;
+ KsbaConstSexp endp;
+
+ if (!p)
+ log_printf ("none");
+ else if (*p != '(')
+ log_printf ("ERROR - not an S-expression");
+ else
+ {
+ p++;
+ n = strtoul (p, (char**)&endp, 10);
+ p = endp;
+ if (*p!=':')
+ log_printf ("ERROR - invalid S-expression");
+ else
+ {
+ for (p++; n; n--, p++)
+ log_printf ("%02X", *p);
+ }
+ }
+}
+
+void
+gpgsm_print_time (FILE *fp, time_t t)
+{
+ if (!t)
+ fputs (_("none"), fp);
+ else if ( t == (time_t)(-1) )
+ fputs ("[Error - Invalid time]", fp);
+ else
+ {
+ struct tm *tp;
+
+ tp = gmtime (&t);
+ fprintf (fp, "%04d-%02d-%02d %02d:%02d:%02d Z",
+ 1900+tp->tm_year, tp->tm_mon+1, tp->tm_mday,
+ tp->tm_hour, tp->tm_min, tp->tm_sec);
+ assert (!tp->tm_isdst);
+ }
+}
+
+void
+gpgsm_dump_time (time_t t)
+{
+
+ if (!t)
+ log_printf (_("[none]"));
+ else if ( t == (time_t)(-1) )
+ log_printf (_("[error]"));
+ else
+ {
+ struct tm *tp;
+
+ tp = gmtime (&t);
+ log_printf ("%04d-%02d-%02d %02d:%02d:%02d",
+ 1900+tp->tm_year, tp->tm_mon+1, tp->tm_mday,
+ tp->tm_hour, tp->tm_min, tp->tm_sec);
+ assert (!tp->tm_isdst);
+ }
+}
+
+
+
+
+void
+gpgsm_dump_string (const char *string)
+{
+
+ if (!string)
+ log_printf ("[error]");
+ else
+ {
+ const unsigned char *s;
+
+ for (s=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 ( " ]");
+ }
+ }
+}
+
+
+void
+gpgsm_dump_cert (const char *text, KsbaCert cert)
+{
+ KsbaSexp sexp;
+ unsigned char *p;
+ char *dn;
+ time_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");
+
+ t = ksba_cert_get_validity (cert, 0);
+ log_debug (" notBefore: ");
+ gpgsm_dump_time (t);
+ log_printf ("\n");
+ t = ksba_cert_get_validity (cert, 1);
+ log_debug (" notAfter: ");
+ gpgsm_dump_time (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");
+}
+
+
+
+/* helper for the rfc2253 string parser */
+static const unsigned char *
+parse_dn_part (struct dn_array_s *array, const unsigned char *string)
+{
+ const unsigned char *s, *s1;
+ size_t n;
+ unsigned char *p;
+
+ /* parse attributeType */
+ for (s = string+1; *s && *s != '='; s++)
+ ;
+ if (!*s)
+ return NULL; /* error */
+ n = s - string;
+ if (!n)
+ return NULL; /* empty key */
+ array->key = p = xtrymalloc (n+1);
+ if (!array->key)
+ return NULL;
+ memcpy (p, string, n);
+ p[n] = 0;
+ trim_trailing_spaces (p);
+ if ( !strcmp (p, "1.2.840.113549.1.9.1") )
+ strcpy (p, "EMail");
+ 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++ = xtoi_2 (s1);
+ *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 == '#' || *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))
+ {
+ *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);
+ arrayidx++;
+ if (!string)
+ goto failure;
+ while (*string == ' ')
+ string++;
+ 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;
+}
+
+
+static void
+print_dn_part (FILE *fp, struct dn_array_s *dn, const char *key)
+{
+ int any = 0;
+
+ for (; dn->key; dn++)
+ {
+ if (!strcmp (dn->key, key) && dn->value && *dn->value)
+ {
+ putc ('/', fp);
+ if (any)
+ fputs (" + ", fp);
+ else
+ fprintf (fp, "%s=", key);
+ print_sanitized_utf8_string (fp, dn->value, '/');
+ any = 1;
+ }
+ }
+}
+
+/* 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, struct dn_array_s *dn)
+{
+ const char *stdpart[] = {
+ "CN", "OU", "O", "STREET", "L", "ST", "C", "EMail", NULL
+ };
+ int i;
+
+ for (i=0; stdpart[i]; i++)
+ print_dn_part (fp, dn, stdpart[i]);
+
+ /* now print the rest without any specific ordering */
+ for (; dn->key; dn++)
+ {
+ for (i=0; stdpart[i]; i++)
+ {
+ if (!strcmp (dn->key, stdpart[i]))
+ break;
+ }
+ if (!stdpart[i])
+ print_dn_part (fp, dn, dn->key);
+ }
+}
+
+
+
+void
+gpgsm_print_name (FILE *fp, const char *name)
+{
+ const unsigned char *s;
+ int i;
+
+ s = name;
+ if (!s)
+ {
+ fputs (_("[Error - No name]"), fp);
+ }
+ else if (*s == '<')
+ {
+ const unsigned char *s2 = strchr (s+1, '>');
+ if (s2)
+ print_sanitized_utf8_buffer (fp, s + 1, s2 - s - 1, 0);
+ }
+ else if (*s == '(')
+ fputs (_("[Error - unknown encoding]"), fp);
+ 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, dn);
+ for (i=0; dn[i].key; i++)
+ {
+ xfree (dn[i].key);
+ xfree (dn[i].value);
+ }
+ xfree (dn);
+ }
+ }
+}
+
+
+
diff --git a/sm/certlist.c b/sm/certlist.c
new file mode 100644
index 000000000..eedc99025
--- /dev/null
+++ b/sm/certlist.c
@@ -0,0 +1,315 @@
+/* certlist.c - build list of certificates
+ * 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 2 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuPG is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ */
+
+#include <config.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <time.h>
+#include <assert.h>
+
+#include "gpgsm.h"
+#include <gcrypt.h>
+#include <ksba.h>
+
+#include "keydb.h"
+#include "i18n.h"
+
+/* 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) */
+static int
+cert_usage_p (KsbaCert cert, int mode)
+{
+ KsbaError err;
+ unsigned int use;
+
+ err = ksba_cert_get_key_usage (cert, &use);
+ if (err == KSBA_No_Data)
+ {
+ if (opt.verbose && mode < 2)
+ log_info (mode?
+ _("no key usage specified - accepted for encryption\n"):
+ _("no key usage specified - accepted for signing\n"));
+ return 0;
+ }
+ if (err)
+ {
+ log_error (_("error getting key usage information: %s\n"),
+ ksba_strerror (err));
+ return map_ksba_err (err);
+ }
+
+ if (mode == 4)
+ {
+ if ((use & (KSBA_KEYUSAGE_KEY_CERT_SIGN)))
+ return 0;
+ log_info ( _("certificate should have not been used certification\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 (KsbaCert cert)
+{
+ return cert_usage_p (cert, 0);
+}
+
+
+/* Return 0 if the cert is usable for encryption */
+int
+gpgsm_cert_use_encrypt_p (KsbaCert cert)
+{
+ return cert_usage_p (cert, 1);
+}
+
+int
+gpgsm_cert_use_verify_p (KsbaCert cert)
+{
+ return cert_usage_p (cert, 2);
+}
+
+int
+gpgsm_cert_use_decrypt_p (KsbaCert cert)
+{
+ return cert_usage_p (cert, 3);
+}
+
+int
+gpgsm_cert_use_cert_p (KsbaCert cert)
+{
+ return cert_usage_p (cert, 4);
+}
+
+
+static int
+same_subject_issuer (const char *subject, const char *issuer, KsbaCert cert)
+{
+ char *subject2 = ksba_cert_get_subject (cert, 0);
+ char *issuer2 = ksba_cert_get_subject (cert, 0);
+ int tmp;
+
+ tmp = (subject && subject2
+ && !strcmp (subject, subject2)
+ && issuer && issuer2
+ && !strcmp (issuer, issuer2));
+ xfree (subject2);
+ xfree (issuer2);
+ return tmp;
+}
+
+
+
+/* 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
+ avaibale for the certificate. */
+int
+gpgsm_add_to_certlist (CTRL ctrl, const char *name, int secret,
+ CERTLIST *listaddr)
+{
+ int rc;
+ KEYDB_SEARCH_DESC desc;
+ KEYDB_HANDLE kh = NULL;
+ KsbaCert 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 *subject = NULL;
+ char *issuer = NULL;
+
+ get_next:
+ rc = keydb_search (kh, &desc, 1);
+ if (!rc)
+ rc = keydb_get_cert (kh, &cert);
+ if (!rc)
+ {
+ 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;
+ subject = ksba_cert_get_subject (cert, 0);
+ issuer = ksba_cert_get_subject (cert, 0);
+ ksba_cert_release (cert);
+ cert = NULL;
+ goto get_next;
+ }
+ else if (same_subject_issuer (subject, 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)
+ {
+ next_ambigious:
+ rc = keydb_search (kh, &desc, 1);
+ if (rc == -1)
+ rc = 0;
+ else if (!rc)
+ {
+ KsbaCert cert2 = NULL;
+
+ /* We have to ignore ambigious names as long as
+ there only fault is a bad key usage */
+ if (!keydb_get_cert (kh, &cert2))
+ {
+ int tmp = (same_subject_issuer (subject, issuer, cert2)
+ && ((gpg_err_code (
+ secret? gpgsm_cert_use_sign_p (cert2)
+ : gpgsm_cert_use_encrypt_p (cert2)
+ )
+ ) == GPG_ERR_WRONG_KEY_USAGE));
+ ksba_cert_release (cert2);
+ if (tmp)
+ goto next_ambigious;
+ }
+ rc = gpg_error (GPG_ERR_AMBIGUOUS_NAME);
+ }
+ }
+ xfree (subject);
+ xfree (issuer);
+
+ if (!rc && secret)
+ {
+ char *p;
+
+ rc = gpg_error (GPG_ERR_NO_SECKEY);
+ p = gpgsm_get_keygrip_hexstring (cert);
+ if (p)
+ {
+ if (!gpgsm_agent_havekey (p))
+ rc = 0;
+ xfree (p);
+ }
+ }
+ if (!rc)
+ rc = gpgsm_validate_chain (ctrl, cert, NULL);
+ if (!rc)
+ {
+ CERTLIST cl = xtrycalloc (1, sizeof *cl);
+ if (!cl)
+ rc = OUT_OF_CORE (errno);
+ else
+ {
+ cl->cert = cert; cert = NULL;
+ cl->next = *listaddr;
+ *listaddr = cl;
+ }
+ }
+ }
+ }
+
+ keydb_release (kh);
+ ksba_cert_release (cert);
+ return rc == -1? gpg_error (GPG_ERR_NO_PUBKEY): rc;
+}
+
+void
+gpgsm_release_certlist (CERTLIST list)
+{
+ while (list)
+ {
+ CERTLIST 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 */
+int
+gpgsm_find_cert (const char *name, KsbaCert *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
+ {
+ rc = keydb_search (kh, &desc, 1);
+ if (!rc)
+ rc = keydb_get_cert (kh, r_cert);
+ if (!rc)
+ {
+ rc = keydb_search (kh, &desc, 1);
+ if (rc == -1)
+ rc = 0;
+ else
+ {
+ if (!rc)
+ 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.c b/sm/certreqgen.c
new file mode 100644
index 000000000..0dd4fdde9
--- /dev/null
+++ b/sm/certreqgen.c
@@ -0,0 +1,699 @@
+/* certreqgen.c - Generate a key and a certification request
+ * Copyright (C) 2002, 2003 Free Software Foundation, Inc.
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuPG is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ */
+
+/*
+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 1024.
+ 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 ist the email address
+
+Here is an example:
+$ cat >foo <<EOF
+%echo Generating a standard key
+Key-Type: RSA
+Key-Length: 1024
+Name-DN: CN=test cert 1,OU=Aegypten Project,O=g10 Code GmbH,L=Düsseldorf,C=DE
+Name-Email: [email protected]
+# 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,
+ pKEYUSAGE,
+ pNAMEDN,
+ pNAMEEMAIL
+};
+
+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;
+ KsbaWriter writer;
+};
+
+
+static int proc_parameters (struct para_data_s *para,
+ struct reqgen_ctrl_s *outctrl);
+static int create_request (struct para_data_s *para,
+ KsbaConstSexp 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)
+{
+ struct para_data_s *r;
+
+ for (r = para; r && r->key != key; r = r->next)
+ ;
+ return r;
+}
+
+static const char *
+get_parameter_value (struct para_data_s *para, enum para_name key)
+{
+ struct para_data_s *r = get_parameter (para, key);
+ return (r && *r->u.value)? r->u.value : NULL;
+}
+
+static int
+get_parameter_algo (struct para_data_s *para, enum para_name key)
+{
+ struct para_data_s *r = get_parameter (para, key);
+ 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);
+ 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);
+
+ if (!r)
+ return 0;
+
+ 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 (FILE *fp, KsbaWriter writer)
+{
+ static struct {
+ const char *name;
+ enum para_name key;
+ } keywords[] = {
+ { "Key-Type", pKEYTYPE},
+ { "Key-Length", pKEYLENGTH },
+ { "Key-Usage", pKEYUSAGE },
+ { "Name-DN", pNAMEDN },
+ { "Name-Email", pNAMEEMAIL },
+ { 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 (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 (; !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 (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 (para, &outctrl);
+ if (rc)
+ goto leave;
+ any = 1;
+ release_parameter_list (para);
+ para = NULL;
+ }
+ else
+ {
+ 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 (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 (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 (struct para_data_s *para, struct reqgen_ctrl_s *outctrl)
+{
+ struct para_data_s *r;
+ const char *s;
+ int i;
+ unsigned int nbits;
+ char numbuf[20];
+ unsigned char keyparms[100];
+ int rc;
+ KsbaSexp public;
+
+ /* check that we have all required parameters */
+ assert (get_parameter (para, pKEYTYPE));
+
+ /* We can only use RSA for now. There is a with pkcs-10 on how to
+ use ElGamal becuase it is expected that a PK algorithm can always
+ be used for signing. */
+ i = get_parameter_algo (para, pKEYTYPE);
+ if (i < 1 || i != GCRY_PK_RSA )
+ {
+ r = get_parameter (para, pKEYTYPE);
+ log_error ("line %d: invalid algorithm\n", r->lnr);
+ return gpg_error (GPG_ERR_INV_PARAMETER);
+ }
+
+ /* check the keylength */
+ if (!get_parameter (para, pKEYLENGTH))
+ nbits = 1024;
+ else
+ nbits = get_parameter_uint (para, pKEYLENGTH);
+ if (nbits < 512 || nbits > 4096)
+ {
+ r = get_parameter (para, pKEYTYPE);
+ log_error ("line %d: invalid key length %u (valid are 512 to 4096)\n",
+ r->lnr, nbits);
+ return gpg_error (GPG_ERR_INV_PARAMETER);
+ }
+
+ /* check the usage */
+ if (parse_parameter_usage (para, pKEYUSAGE))
+ 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)))
+ {
+ r = get_parameter (para, pKEYTYPE);
+ log_error ("line %d: no subject name given\n", r->lnr);
+ return gpg_error (GPG_ERR_INV_PARAMETER);
+ }
+ /* fixme check s */
+
+ /* check that the optional email address is okay */
+ if ((s=get_parameter_value (para, pNAMEEMAIL)))
+ {
+ if (has_invalid_email_chars (s)
+ || *s == '@'
+ || s[strlen(s)-1] == '@'
+ || s[strlen(s)-1] == '.'
+ || strstr(s, ".."))
+ {
+ r = get_parameter (para, pKEYTYPE);
+ log_error ("line %d: not a valid email address\n", r->lnr);
+ return gpg_error (GPG_ERR_INV_PARAMETER);
+ }
+ }
+
+ sprintf (numbuf, "%u", nbits);
+ snprintf (keyparms, DIM (keyparms)-1,
+ "(6:genkey(3:rsa(5:nbits%d:%s)))", strlen (numbuf), numbuf);
+ rc = gpgsm_agent_genkey (keyparms, &public);
+ if (rc)
+ {
+ r = get_parameter (para, pKEYTYPE);
+ log_error ("line %d: key generation failed: %s\n",
+ r->lnr, gpg_strerror (rc));
+ return rc;
+ }
+
+ rc = create_request (para, public, outctrl);
+ xfree (public);
+
+ return rc;
+}
+
+
+/* Parameters are checked, the key pair has been created. Now
+ generate the request and write it out */
+static int
+create_request (struct para_data_s *para, KsbaConstSexp public,
+ struct reqgen_ctrl_s *outctrl)
+{
+ KsbaCertreq cr;
+ KsbaError err;
+ gcry_md_hd_t md;
+ KsbaStopReason stopreason;
+ int rc = 0;
+ const char *s;
+
+ cr = ksba_certreq_new ();
+ if (!cr)
+ return gpg_error (GPG_ERR_ENOMEM);
+
+ 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));
+ if (err)
+ {
+ log_error ("error setting the subject's name: %s\n",
+ ksba_strerror (err));
+ rc = map_ksba_err (err);
+ goto leave;
+ }
+
+ s = get_parameter_value (para, pNAMEEMAIL);
+ if (s)
+ {
+ char *buf;
+
+ buf = xtrymalloc (strlen (s) + 3);
+ if (!buf)
+ {
+ rc = OUT_OF_CORE (errno);
+ 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",
+ ksba_strerror (err));
+ rc = map_ksba_err (err);
+ goto leave;
+ }
+ }
+
+
+ err = ksba_certreq_set_public_key (cr, public);
+ if (err)
+ {
+ log_error ("error setting the public key: %s\n",
+ ksba_strerror (err));
+ rc = map_ksba_err (err);
+ goto leave;
+ }
+
+ do
+ {
+ err = ksba_certreq_build (cr, &stopreason);
+ if (err)
+ {
+ log_error ("ksba_certreq_build failed: %s\n", ksba_strerror (err));
+ rc = map_ksba_err (err);
+ goto leave;
+ }
+ if (stopreason == KSBA_SR_NEED_SIG)
+ {
+ gcry_sexp_t s_pkey;
+ size_t n;
+ unsigned char grip[20], hexgrip[41];
+ 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");
+ err = gpg_error (GPG_ERR_BUG);
+ goto leave;
+ }
+ rc = gcry_sexp_sscan (&s_pkey, NULL, 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);
+ for (n=0; n < 20; n++)
+ sprintf (hexgrip+n*2, "%02X", grip[n]);
+
+ rc = gpgsm_agent_pksign (hexgrip,
+ gcry_md_read(md, GCRY_MD_SHA1),
+ gcry_md_get_algo_dlen (GCRY_MD_SHA1),
+ GCRY_MD_SHA1,
+ &sigval, &siglen);
+ 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",
+ ksba_strerror (err));
+ rc = map_ksba_err (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_fd. Multiple
+ keys may be created */
+int
+gpgsm_genkey (CTRL ctrl, int in_fd, FILE *out_fp)
+{
+ int rc;
+ FILE *in_fp;
+ Base64Context b64writer = NULL;
+ KsbaWriter writer;
+
+ in_fp = fdopen (dup (in_fd), "rb");
+ if (!in_fp)
+ {
+ gpg_error_t tmperr = gpg_error (gpg_err_code_from_errno (errno));
+ log_error ("fdopen() failed: %s\n", strerror (errno));
+ return tmperr;
+ }
+
+ ctrl->pem_name = "NEW CERTIFICATE REQUEST";
+ rc = gpgsm_create_writer (&b64writer, ctrl, out_fp, &writer);
+ if (rc)
+ {
+ log_error ("can't create writer: %s\n", gpg_strerror (rc));
+ goto leave;
+ }
+
+ rc = read_parameters (in_fp, writer);
+ if (rc)
+ {
+ log_error ("error creating certificate request: %s\n",
+ gpg_strerror (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);
+ fclose (in_fp);
+ return rc;
+}
+
diff --git a/sm/decrypt.c b/sm/decrypt.c
new file mode 100644
index 000000000..17483aa49
--- /dev/null
+++ b/sm/decrypt.c
@@ -0,0 +1,506 @@
+/* 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 2 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuPG is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ */
+
+#include <config.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <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 (const char *hexkeygrip, KsbaConstSexp enc_val,
+ struct decrypt_filter_parm_s *parm)
+{
+ char *seskey = NULL;
+ size_t n, seskeylen;
+ int rc;
+
+ rc = gpgsm_agent_pkdecrypt (hexkeygrip, 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. fixme! */
+ }
+ 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 rightway 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 process or fewer bytes
+ are written. */
+static KsbaError
+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 KSBA_Bug;
+
+ if (maxoutlen < 2*parm->blklen)
+ return KSBA_Bug;
+ /* make some space becuase 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 KSBA_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 ctrl, int in_fd, FILE *out_fp)
+{
+ int rc;
+ KsbaError err;
+ Base64Context b64reader = NULL;
+ Base64Context b64writer = NULL;
+ KsbaReader reader;
+ KsbaWriter writer;
+ KsbaCMS cms = NULL;
+ KsbaStopReason stopreason;
+ KEYDB_HANDLE kh;
+ int recp;
+ FILE *in_fp = NULL;
+ struct decrypt_filter_parm_s dfparm;
+
+ memset (&dfparm, 0, sizeof dfparm);
+
+ 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, &reader);
+ if (rc)
+ {
+ log_error ("can't create reader: %s\n", gpg_strerror (rc));
+ goto leave;
+ }
+
+ rc = gpgsm_create_writer (&b64writer, ctrl, out_fp, &writer);
+ if (rc)
+ {
+ log_error ("can't create writer: %s\n", gpg_strerror (rc));
+ goto leave;
+ }
+
+ cms = ksba_cms_new ();
+ if (!cms)
+ {
+ rc = gpg_error (GPG_ERR_ENOMEM);
+ goto leave;
+ }
+
+ err = ksba_cms_set_reader_writer (cms, reader, writer);
+ if (err)
+ {
+ log_debug ("ksba_cms_set_reader_writer failed: %s\n",
+ ksba_strerror (err));
+ rc = map_ksba_err (err);
+ goto leave;
+ }
+
+ /* parser loop */
+ do
+ {
+ err = ksba_cms_parse (cms, &stopreason);
+ if (err)
+ {
+ log_debug ("ksba_cms_parse failed: %s\n", ksba_strerror (err));
+ rc = map_ksba_err (err);
+ goto leave;
+ }
+
+ if (stopreason == KSBA_SR_BEGIN_DATA
+ || stopreason == KSBA_SR_DETACHED_DATA)
+ {
+ int algo, mode;
+ const char *algoid;
+ int any_key = 0;
+
+ 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);
+ }
+
+ goto leave;
+ }
+ 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", ksba_strerror (err));
+ rc = map_ksba_err (err);
+ goto leave;
+ }
+
+ for (recp=0; !any_key; recp++)
+ {
+ char *issuer;
+ KsbaSexp serial;
+ KsbaSexp enc_val;
+ char *hexkeygrip = NULL;
+
+ err = ksba_cms_get_issuer_serial (cms, recp, &issuer, &serial);
+ if (err == -1 && recp)
+ break; /* no more recipients */
+ if (err)
+ log_error ("recp %d - error getting info: %s\n",
+ recp, ksba_strerror (err));
+ else
+ {
+ KsbaCert 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");
+
+ 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;
+ }
+ /* 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);
+
+ 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 (hexkeygrip, enc_val, &dfparm);
+ xfree (enc_val);
+ if (rc)
+ {
+ log_debug ("decrypting session key failed: %s\n",
+ gpg_strerror (rc));
+ }
+ else
+ { /* setup the bulk decrypter */
+ any_key = 1;
+ ksba_writer_set_filter (writer,
+ decrypt_filter,
+ &dfparm);
+ }
+ }
+ }
+ 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)
+ {
+ rc = map_ksba_err (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:
+ if (rc)
+ gpgsm_status (ctrl, STATUS_DECRYPTION_FAILED, NULL);
+ 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 000000000..53eff864c
--- /dev/null
+++ b/sm/delete.c
@@ -0,0 +1,165 @@
+/* delete.c
+ * Copyright (C) 2002 Free Software Foundation, Inc.
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuPG is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ */
+
+#include <config.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <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 ctrl, const char *username)
+{
+ int rc = 0;
+ KEYDB_SEARCH_DESC desc;
+ KEYDB_HANDLE kh = NULL;
+ KsbaCert cert = NULL;
+ int duplicates = 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;
+ }
+
+
+ rc = keydb_search (kh, &desc, 1);
+ if (!rc)
+ rc = keydb_get_cert (kh, &cert);
+ if (!rc)
+ {
+ 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)
+ {
+ KsbaCert cert2 = NULL;
+ 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. */
+ 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);
+ 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 ctrl, STRLIST 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 000000000..725a81b70
--- /dev/null
+++ b/sm/encrypt.c
@@ -0,0 +1,550 @@
+/* encrypt.c - Encrypt 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 2 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuPG is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ */
+
+#include <config.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <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 encryptionkey (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);
+ }
+
+ 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);
+
+ if (dek->keylen < 100/8)
+ { /* make sure we don't use weak keys */
+ 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_randomize (dek->iv, dek->ivlen, GCRY_STRONG_RANDOM);
+ 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;
+}
+
+
+/* Encode the session key. NBITS is the number of bits which should be
+ used for packing the session key. returns: An mpi with the session
+ key (caller must free) */
+static gcry_mpi_t
+encode_session_key (DEK dek, unsigned int nbits)
+{
+ int nframe = (nbits+7) / 8;
+ byte *p;
+ byte *frame;
+ int i,n;
+ gcry_mpi_t a;
+
+ if (dek->keylen + 7 > nframe || !nframe)
+ log_bug ("can't encode a %d bit key in a %d bits frame\n",
+ dek->keylen*8, nbits );
+
+ /* We encode the session key in this way:
+ *
+ * 0 2 RND(n bytes) 0 KEY(k bytes)
+ *
+ * (But how can we store the leading 0 - the external representaion
+ * of MPIs doesn't allow leading zeroes =:-)
+ *
+ * RND are non-zero random bytes.
+ * KEY is the encryption key (session key)
+ */
+
+ frame = gcry_xmalloc_secure (nframe);
+ n = 0;
+ frame[n++] = 0;
+ frame[n++] = 2;
+ i = nframe - 3 - dek->keylen;
+ assert (i > 0);
+ p = gcry_random_bytes_secure (i, GCRY_STRONG_RANDOM);
+ /* replace zero bytes by new values */
+ for (;;)
+ {
+ int j, k;
+ byte *pp;
+
+ /* count the zero bytes */
+ for(j=k=0; j < i; j++ )
+ {
+ if( !p[j] )
+ k++;
+ }
+ if( !k )
+ break; /* okay: no zero bytes */
+
+ k += k/128; /* better get some more */
+ pp = gcry_random_bytes_secure (k, GCRY_STRONG_RANDOM);
+ for (j=0; j < i && k; j++)
+ {
+ if( !p[j] )
+ p[j] = pp[--k];
+ }
+ xfree (pp);
+ }
+ memcpy (frame+n, p, i);
+ xfree (p);
+
+ n += i;
+ frame[n++] = 0;
+ memcpy (frame+n, dek->key, dek->keylen);
+ n += dek->keylen;
+ assert (n == nframe);
+ if (gcry_mpi_scan (&a, GCRYMPI_FMT_USG, frame, n, &nframe) )
+ BUG ();
+ gcry_free(frame);
+
+ return a;
+}
+
+
+
+/* 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, KsbaCert cert, char **encval)
+{
+ gcry_sexp_t s_ciph, s_data, s_pkey;
+ int rc;
+ KsbaSexp 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, 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 */
+ {
+ /* fixme: actually the pkcs-1 encoding should go into libgcrypt */
+ gcry_mpi_t data = encode_session_key (dek, gcry_pk_get_nbits (s_pkey));
+ if (!data)
+ {
+ gcry_mpi_release (data);
+ return gpg_error (GPG_ERR_GENERAL);
+ }
+ if (gcry_sexp_build (&s_data, NULL, "%m", data))
+ BUG ();
+ gcry_mpi_release (data);
+ }
+
+ /* 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 */
+ len = gcry_sexp_sprint (s_ciph, GCRYSEXP_FMT_CANON, NULL, 0);
+ assert (len);
+ buf = xtrymalloc (len);
+ if (!buf)
+ {
+ gpg_error_t tmperr = OUT_OF_CORE (errno);
+ gcry_sexp_release (s_ciph);
+ return tmperr;
+ }
+ len = gcry_sexp_sprint (s_ciph, GCRYSEXP_FMT_CANON, buf, len);
+ assert (len);
+
+ *encval = buf;
+ return 0;
+}
+
+
+
+/* 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 ctrl, CERTLIST recplist, int data_fd, FILE *out_fp)
+{
+ int rc = 0;
+ Base64Context b64writer = NULL;
+ KsbaError err;
+ KsbaWriter writer;
+ KsbaReader reader = NULL;
+ KsbaCMS cms = NULL;
+ KsbaStopReason stopreason;
+ KEYDB_HANDLE kh = NULL;
+ struct encrypt_cb_parm_s encparm;
+ DEK dek = NULL;
+ int recpno;
+ FILE *data_fp = NULL;
+ CERTLIST cl;
+
+ memset (&encparm, 0, sizeof encparm);
+
+ if (!recplist)
+ {
+ log_error(_("no valid recipients given\n"));
+ gpgsm_status (ctrl, STATUS_NO_RECP, "0");
+ rc = gpg_error (GPG_ERR_NO_PUBKEY);
+ goto leave;
+ }
+
+ 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;
+ }
+
+ reader = ksba_reader_new ();
+ if (!reader)
+ rc = KSBA_Out_Of_Core;
+ if (!rc)
+ rc = ksba_reader_set_cb (reader, encrypt_cb, &encparm);
+ if (rc)
+ {
+ rc = map_ksba_err (rc);
+ goto leave;
+ }
+ encparm.fp = data_fp;
+
+ ctrl->pem_name = "ENCRYPTED MESSAGE";
+ rc = gpgsm_create_writer (&b64writer, ctrl, out_fp, &writer);
+ if (rc)
+ {
+ log_error ("can't create writer: %s\n", gpg_strerror (rc));
+ goto leave;
+ }
+
+ cms = ksba_cms_new ();
+ if (!cms)
+ {
+ rc = gpg_error (GPG_ERR_ENOMEM);
+ goto leave;
+ }
+
+ err = ksba_cms_set_reader_writer (cms, reader, writer);
+ if (err)
+ {
+ log_debug ("ksba_cms_set_reader_writer failed: %s\n",
+ ksba_strerror (err));
+ rc = map_ksba_err (err);
+ goto leave;
+ }
+
+ /* 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",
+ ksba_strerror (err));
+ rc = map_ksba_err (err);
+ goto leave;
+ }
+
+ /* create a session key */
+ dek = xtrycalloc (1, sizeof *dek); /* hmmm: should we put it into secmem?*/
+ if (!dek)
+ rc = OUT_OF_CORE (errno);
+ 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",
+ ksba_strerror (err));
+ rc = map_ksba_err (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 (errno);
+ goto leave;
+ }
+
+ /* 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)
+ {
+ char *encval;
+
+ rc = encrypt_dek (dek, cl->cert, &encval);
+ if (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)
+ {
+ log_error ("ksba_cms_add_recipient failed: %s\n",
+ ksba_strerror (err));
+ rc = map_ksba_err (err);
+ xfree (encval);
+ goto leave;
+ }
+
+ err = ksba_cms_set_enc_val (cms, recpno, encval);
+ xfree (encval);
+ if (err)
+ {
+ log_error ("ksba_cms_set_enc_val failed: %s\n",
+ ksba_strerror (err));
+ rc = map_ksba_err (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", ksba_strerror (err));
+ rc = map_ksba_err (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;
+ }
+ 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 000000000..93a55debc
--- /dev/null
+++ b/sm/export.c
@@ -0,0 +1,249 @@
+/* export.c
+ * Copyright (C) 2002, 2003 Free Software Foundation, Inc.
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuPG is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ */
+
+#include <config.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <time.h>
+#include <assert.h>
+
+#include "gpgsm.h"
+#include <gcrypt.h>
+#include <ksba.h>
+
+#include "keydb.h"
+
+static void print_short_info (KsbaCert cert, FILE *fp);
+
+
+
+/* Export all certificates or just those given in NAMES. */
+void
+gpgsm_export (CTRL ctrl, STRLIST names, FILE *fp)
+{
+ KEYDB_HANDLE hd;
+ KEYDB_SEARCH_DESC *desc = NULL;
+ int ndesc;
+ Base64Context b64writer = NULL;
+ KsbaWriter writer;
+ STRLIST sl;
+ KsbaCert cert = NULL;
+ int rc=0;
+ int count = 0;
+ int i;
+
+ 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 (errno)));
+ 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, we switch to
+ ephemeral mode so that _all_ currently available and matching
+ certificates are exported.
+
+ fixme: we should in this case keep a list of certificates to
+ avoid accidential export of duplicate certificates. */
+ 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)); i++)
+ ;
+ if (i == ndesc)
+ keydb_set_ephemeral (hd, 1);
+ }
+
+ while (!(rc = keydb_search (hd, desc, ndesc)))
+ {
+ const unsigned char *image;
+ size_t imagelen;
+
+ 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;
+ }
+
+ 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)
+ putc ('\n', fp);
+ print_short_info (cert, fp);
+ putc ('\n', fp);
+ }
+ count++;
+
+ if (!b64writer)
+ {
+ ctrl->pem_name = "CERTIFICATE";
+ rc = gpgsm_create_writer (&b64writer, ctrl, fp, &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", ksba_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);
+}
+
+
+/* Print some info about the certifciate CERT to FP */
+static void
+print_short_info (KsbaCert cert, FILE *fp)
+{
+ char *p;
+ KsbaSexp sexp;
+ int idx;
+
+ for (idx=0; (p = ksba_cert_get_issuer (cert, idx)); idx++)
+ {
+ fputs (!idx? "Issuer ...: "
+ : "\n aka ...: ", fp);
+ gpgsm_print_name (fp, p);
+ xfree (p);
+ }
+ putc ('\n', fp);
+
+ fputs ("Serial ...: ", fp);
+ 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 == ':')
+ for (s++; len; len--, s++)
+ fprintf (fp, "%02X", *s);
+ }
+ xfree (sexp);
+ }
+ putc ('\n', fp);
+
+ for (idx=0; (p = ksba_cert_get_subject (cert, idx)); idx++)
+ {
+ fputs (!idx? "Subject ..: "
+ : "\n aka ..: ", fp);
+ gpgsm_print_name (fp, p);
+ xfree (p);
+ }
+ putc ('\n', fp);
+}
+
+
+
+
+
+
diff --git a/sm/fingerprint.c b/sm/fingerprint.c
new file mode 100644
index 000000000..028c08aab
--- /dev/null
+++ b/sm/fingerprint.c
@@ -0,0 +1,271 @@
+/* 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 2 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuPG is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ */
+
+#include <config.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <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.
+ */
+char *
+gpgsm_get_fingerprint (KsbaCert cert, int algo, 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;
+
+ 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", ksba_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 );
+ return array;
+}
+
+
+/* Return an allocated buffer with the formatted fingerprint */
+char *
+gpgsm_get_fingerprint_string (KsbaCert cert, int algo)
+{
+ unsigned char digest[MAX_DIGEST_LEN];
+ char *buf;
+ int len, i;
+
+ 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);
+ *buf = 0;
+ for (i=0; i < len; i++ )
+ sprintf (buf+strlen(buf), i? ":%02X":"%02X", digest[i]);
+ return buf;
+}
+
+/* Return an allocated buffer with the formatted fingerprint as one
+ large hexnumber */
+char *
+gpgsm_get_fingerprint_hexstring (KsbaCert cert, int algo)
+{
+ unsigned char digest[MAX_DIGEST_LEN];
+ char *buf;
+ int len, i;
+
+ 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);
+ *buf = 0;
+ for (i=0; i < len; i++ )
+ sprintf (buf+strlen(buf), "%02X", digest[i]);
+ return buf;
+}
+
+/* Return a certificate ID. These are the last 4 bytes of the SHA-1
+ fingerprint. */
+unsigned long
+gpgsm_get_short_fingerprint (KsbaCert cert)
+{
+ unsigned char digest[20];
+
+ gpgsm_get_fingerprint (cert, GCRY_MD_SHA1, digest, NULL);
+ 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 the array or a newly allocated one if the
+ passed one was NULL */
+char *
+gpgsm_get_keygrip (KsbaCert cert, char *array)
+{
+ gcry_sexp_t s_pkey;
+ int rc;
+ KsbaSexp 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: %s\n", p);
+ 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, 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 in from of an
+ hexstring. NULL is returned in case of error */
+char *
+gpgsm_get_keygrip_hexstring (KsbaCert cert)
+{
+ unsigned char grip[20];
+ char *buf, *p;
+ int i;
+
+ gpgsm_get_keygrip (cert, grip);
+ buf = p = xmalloc (20*2+1);
+ for (i=0; i < 20; i++, p += 2 )
+ sprintf (p, "%02X", grip[i]);
+ return buf;
+}
+
+
+
+/* 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
+ delimted from the has by a dot.
+
+ The caller must free the string.
+*/
+char *
+gpgsm_get_certid (KsbaCert cert)
+{
+ KsbaSexp serial;
+ unsigned 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 = 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", p[i]);
+ *endp = 0;
+
+ xfree (serial);
+ return certid;
+}
+
+
+
+
+
+
diff --git a/sm/gpgsm.c b/sm/gpgsm.c
new file mode 100644
index 000000000..c392886ba
--- /dev/null
+++ b/sm/gpgsm.c
@@ -0,0 +1,1458 @@
+/* gpgsm.c - GnuPG for S/MIME
+ * 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 2 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuPG is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ */
+
+#include <config.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <fcntl.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"
+
+enum cmd_and_opt_values {
+ aNull = 0,
+ oArmor = 'a',
+ aDetachedSign = 'b',
+ aSym = 'c',
+ aDecrypt = 'd',
+ aEncr = 'e',
+ oInteractive = 'i',
+ oKOption = 'k',
+ oDryRun = 'n',
+ oOutput = 'o',
+ oQuiet = 'q',
+ oRecipient = 'r',
+ aSign = 's',
+ oTextmodeShort= 't',
+ oUser = 'u',
+ oVerbose = 'v',
+ oCompress = 'z',
+ oNotation = 'N',
+ oBatch = 500,
+ aClearsign,
+ aStore,
+ aKeygen,
+ aSignEncr,
+ aSignKey,
+ aLSignKey,
+ aListPackets,
+ aEditKey,
+ aDeleteKey,
+ aImport,
+ aVerify,
+ aVerifyFiles,
+ aListKeys,
+ aListExternalKeys,
+ aListSigs,
+ aListSecretKeys,
+ aSendKeys,
+ aRecvKeys,
+ aExport,
+ aCheckKeys, /* nyi */
+ aServer,
+ aLearnCard,
+ aCallDirmngr,
+ aCallProtectTool,
+ aPasswd,
+
+ oOptions,
+ oDebug,
+ oDebugAll,
+ oDebugWait,
+ oDebugNoChainValidation,
+ oLogFile,
+
+ oEnableSpecialFilenames,
+
+ oAgentProgram,
+ oDisplay,
+ oTTYname,
+ oTTYtype,
+ oLCctype,
+ oLCmessages,
+
+ oDirmngrProgram,
+ oFakedSystemTime,
+
+
+ oAssumeArmor,
+ oAssumeBase64,
+ oAssumeBinary,
+
+ oBase64,
+ oNoArmor,
+
+ oDisableCRLChecks,
+ oEnableCRLChecks,
+
+ oIncludeCerts,
+ oPolicyFile,
+ oDisablePolicyChecks,
+ oEnablePolicyChecks,
+ oAutoIssuerKeyRetrieve,
+
+
+ oTextmode,
+ oFingerprint,
+ oWithFingerprint,
+ oAnswerYes,
+ oAnswerNo,
+ oKeyring,
+ oSecretKeyring,
+ oDefaultKey,
+ oDefRecipient,
+ oDefRecipientSelf,
+ oNoDefRecipient,
+ oStatusFD,
+ oNoComment,
+ oNoVersion,
+ oEmitVersion,
+ oCompletesNeeded,
+ oMarginalsNeeded,
+ oMaxCertDepth,
+ oLoadExtension,
+ oRFC1991,
+ oOpenPGP,
+ oCipherAlgo,
+ oDigestAlgo,
+ oCompressAlgo,
+ oCommandFD,
+ oNoVerbose,
+ oTrustDBName,
+ oNoSecmemWarn,
+ oNoDefKeyring,
+ oNoGreeting,
+ oNoTTY,
+ oNoOptions,
+ oNoBatch,
+ oHomedir,
+ oWithColons,
+ oWithKeyData,
+ oSkipVerify,
+ oCompressKeys,
+ oCompressSigs,
+ oAlwaysTrust,
+ oRunAsShmCP,
+ oSetFilename,
+ oSetPolicyURL,
+ oUseEmbeddedFilename,
+ oComment,
+ oDefaultComment,
+ oThrowKeyid,
+ oForceV3Sigs,
+ oForceMDC,
+ oS2KMode,
+ oS2KDigest,
+ oS2KCipher,
+ oCharset,
+ oNotDashEscaped,
+ oEscapeFrom,
+ oLockOnce,
+ oLockMultiple,
+ oLockNever,
+ oKeyServer,
+ oEncryptTo,
+ oNoEncryptTo,
+ oLoggerFD,
+ oUtf8Strings,
+ oNoUtf8Strings,
+ oDisableCipherAlgo,
+ oDisablePubkeyAlgo,
+ oAllowNonSelfsignedUID,
+ oAllowFreeformUID,
+ oNoLiteral,
+ oSetFilesize,
+ oHonorHttpProxy,
+ oFastListMode,
+ oListOnly,
+ oIgnoreTimeConflict,
+ oNoRandomSeedFile,
+ oNoAutoKeyRetrieve,
+ oUseAgent,
+ oMergeOnly,
+ oTryAllSecrets,
+ oTrustedKey,
+ oEmuMDEncodeBug,
+ aDummy
+ };
+
+
+static ARGPARSE_OPTS opts[] = {
+
+ { 300, NULL, 0, N_("@Commands:\n ") },
+
+ { aSign, "sign", 256, N_("|[file]|make a signature")},
+ { aClearsign, "clearsign", 256, N_("|[file]|make a clear text signature") },
+ { aDetachedSign, "detach-sign", 256, N_("make a detached signature")},
+ { aEncr, "encrypt", 256, N_("encrypt data")},
+ { aSym, "symmetric", 256, N_("encryption only with symmetric cipher")},
+ { aDecrypt, "decrypt", 256, N_("decrypt data (default)")},
+ { aVerify, "verify" , 256, N_("verify a signature")},
+ { aVerifyFiles, "verify-files" , 256, "@" },
+ { aListKeys, "list-keys", 256, N_("list keys")},
+ { aListExternalKeys, "list-external-keys", 256, N_("list external keys")},
+ { aListSecretKeys, "list-secret-keys", 256, N_("list secret keys")},
+ { aListSigs, "list-sigs", 256, N_("list certificate chain")},
+ { aListSigs, "check-sigs",256, "@"},
+ { oFingerprint, "fingerprint", 256, N_("list keys and fingerprints")},
+ { aKeygen, "gen-key", 256, N_("generate a new key pair")},
+ { aDeleteKey, "delete-key",256, N_("remove key from the public keyring")},
+ { aSendKeys, "send-keys" , 256, N_("export keys to a key server") },
+ { aRecvKeys, "recv-keys" , 256, N_("import keys from a key server") },
+ { aImport, "import", 256 , N_("import certificates")},
+ { aExport, "export", 256 , N_("export certificates")},
+ { aLearnCard, "learn-card", 256 ,N_("register a smartcard")},
+ { aServer, "server", 256, N_("run in server mode")},
+ { aCallDirmngr, "call-dirmngr", 256, N_("pass a command to the dirmngr")},
+ { aCallProtectTool, "call-protect-tool", 256,
+ N_("invoke gpg-protect-tool")},
+ { aPasswd, "passwd", 256, N_("change a passphrase")},
+
+ { 301, NULL, 0, N_("@\nOptions:\n ") },
+
+ { oArmor, "armor", 0, N_("create ascii armored output")},
+ { oArmor, "armour", 0, "@" },
+ { oBase64, "base64", 0, N_("create base-64 encoded output")},
+
+ { oAssumeArmor, "assume-armor", 0, N_("assume input is in PEM format")},
+ { oAssumeBase64, "assume-base64", 0,
+ N_("assume input is in base-64 format")},
+ { oAssumeBinary, "assume-binary", 0,
+ N_("assume input is in binary format")},
+
+ { oRecipient, "recipient", 2, N_("|NAME|encrypt for NAME")},
+
+
+ { oDisableCRLChecks, "disable-crl-checks", 0, N_("never consult a CRL")},
+ { oEnableCRLChecks, "enable-crl-checks", 0, "@"},
+
+ { oIncludeCerts, "include-certs", 1,
+ N_("|N|number of certificates to include") },
+
+ { oPolicyFile, "policy-file", 2,
+ N_("|FILE|take policy information from FILE") },
+
+ { oDisablePolicyChecks, "disable-policy-checks", 0,
+ N_("do not check certificate policies")},
+ { oEnablePolicyChecks, "enable-policy-checks", 0, "@"},
+
+ { oAutoIssuerKeyRetrieve, "auto-issuer-key-retrieve", 0,
+ N_("fetch missing issuer certificates")},
+
+#if 0
+ { oDefRecipient, "default-recipient" ,2,
+ N_("|NAME|use NAME as default recipient")},
+ { oDefRecipientSelf, "default-recipient-self" ,0,
+ N_("use the default key as default recipient")},
+ { oNoDefRecipient, "no-default-recipient", 0, "@" },
+ { oEncryptTo, "encrypt-to", 2, "@" },
+ { oNoEncryptTo, "no-encrypt-to", 0, "@" },
+
+#endif
+ { oUser, "local-user",2, N_("use this user-id to sign or decrypt")},
+
+#if 0
+ { oCompress, NULL, 1, N_("|N|set compress level N (0 disables)") },
+ { oTextmodeShort, NULL, 0, "@"},
+ { oTextmode, "textmode", 0, N_("use canonical text mode")},
+#endif
+
+ { oOutput, "output", 2, N_("use as output file")},
+ { oVerbose, "verbose", 0, N_("verbose") },
+ { oQuiet, "quiet", 0, N_("be somewhat more quiet") },
+ { oNoTTY, "no-tty", 0, N_("don't use the terminal at all") },
+ { oLogFile, "log-file" ,2, N_("use a log file for the server")},
+#if 0
+ { oForceV3Sigs, "force-v3-sigs", 0, N_("force v3 signatures") },
+ { oForceMDC, "force-mdc", 0, N_("always use a MDC for encryption") },
+#endif
+ { oDryRun, "dry-run", 0, N_("do not make any changes") },
+ /*{ oInteractive, "interactive", 0, N_("prompt before overwriting") }, */
+ /*{ oUseAgent, "use-agent",0, N_("use the gpg-agent")},*/
+ { oBatch, "batch", 0, N_("batch mode: never ask")},
+ { oAnswerYes, "yes", 0, N_("assume yes on most questions")},
+ { oAnswerNo, "no", 0, N_("assume no on most questions")},
+
+ { oKeyring, "keyring" ,2, N_("add this keyring to the list of keyrings")},
+ { oSecretKeyring, "secret-keyring" ,2, N_("add this secret keyring to the list")},
+ { oDefaultKey, "default-key" ,2, N_("|NAME|use NAME as default secret key")},
+ { oKeyServer, "keyserver",2, N_("|HOST|use this keyserver to lookup keys")},
+ { oCharset, "charset" , 2, N_("|NAME|set terminal charset to NAME") },
+ { oOptions, "options" , 2, N_("read options from file")},
+
+ { oDebug, "debug" ,4|16, "@"},
+ { oDebugAll, "debug-all" ,0, "@"},
+ { oDebugWait, "debug-wait" ,1, "@"},
+ { oDebugNoChainValidation, "debug-no-chain-validation" ,0, "@"},
+ { oStatusFD, "status-fd" ,1, N_("|FD|write status info to this FD") },
+ { aDummy, "no-comment", 0, "@"},
+ { aDummy, "completes-needed", 1, "@"},
+ { aDummy, "marginals-needed", 1, "@"},
+ { oMaxCertDepth, "max-cert-depth", 1, "@" },
+ { aDummy, "trusted-key", 2, "@"},
+ { oLoadExtension, "load-extension" ,2,
+ N_("|FILE|load extension module FILE")},
+ { aDummy, "rfc1991", 0, "@"},
+ { aDummy, "openpgp", 0, "@"},
+ { aDummy, "s2k-mode", 1, "@"},
+ { aDummy, "s2k-digest-algo",2, "@"},
+ { aDummy, "s2k-cipher-algo",2, "@"},
+ { oCipherAlgo, "cipher-algo", 2 , N_("|NAME|use cipher algorithm NAME")},
+ { oDigestAlgo, "digest-algo", 2 ,
+ N_("|NAME|use message digest algorithm NAME")},
+#if 0
+ { oCompressAlgo, "compress-algo", 1 , N_("|N|use compress algorithm N")},
+#endif
+ { aDummy, "throw-keyid", 0, "@"},
+ { aDummy, "notation-data", 2, "@"},
+
+ { 302, NULL, 0, N_(
+ "@\n(See the man page for a complete listing of all commands and options)\n"
+ )},
+
+ { 303, NULL, 0, N_("@\nExamples:\n\n"
+ " -se -r Bob [file] sign and encrypt for user Bob\n"
+ " --clearsign [file] make a clear text signature\n"
+ " --detach-sign [file] make a detached signature\n"
+ " --list-keys [names] show keys\n"
+ " --fingerprint [names] show fingerprints\n" ) },
+
+ /* hidden options */
+ { oNoVerbose, "no-verbose", 0, "@"},
+
+ { oEnableSpecialFilenames, "enable-special-filenames", 0, "@" },
+
+
+ { oTrustDBName, "trustdb-name", 2, "@" },
+ { oNoSecmemWarn, "no-secmem-warning", 0, "@" },
+ { oNoArmor, "no-armor", 0, "@"},
+ { oNoArmor, "no-armour", 0, "@"},
+ { oNoDefKeyring, "no-default-keyring", 0, "@" },
+ { oNoGreeting, "no-greeting", 0, "@" },
+ { oNoOptions, "no-options", 0, "@" }, /* shortcut for --options /dev/null */
+ { oHomedir, "homedir", 2, "@" }, /* defaults to "~/.gnupg" */
+ { oAgentProgram, "agent-program", 2 , "@" },
+ { oDisplay, "display", 2, "@" },
+ { oTTYname, "ttyname", 2, "@" },
+ { oTTYtype, "ttytype", 2, "@" },
+ { oLCctype, "lc-ctype", 2, "@" },
+ { oLCmessages, "lc-messages", 2, "@" },
+ { oDirmngrProgram, "dirmngr-program", 2 , "@" },
+ { oFakedSystemTime, "faked-system-time", 4, "@" }, /* (epoch time) */
+
+
+ { oNoBatch, "no-batch", 0, "@" },
+ { oWithColons, "with-colons", 0, "@"},
+ { oWithKeyData,"with-key-data", 0, "@"},
+ { aListKeys, "list-key", 0, "@" }, /* alias */
+ { aListSigs, "list-sig", 0, "@" }, /* alias */
+ { aListSigs, "check-sig",0, "@" }, /* alias */
+ { oSkipVerify, "skip-verify",0, "@" },
+ { oCompressKeys, "compress-keys",0, "@"},
+ { oCompressSigs, "compress-sigs",0, "@"},
+ { oAlwaysTrust, "always-trust", 0, "@"},
+ { oNoVersion, "no-version", 0, "@"},
+ { oLockOnce, "lock-once", 0, "@" },
+ { oLockMultiple, "lock-multiple", 0, "@" },
+ { oLockNever, "lock-never", 0, "@" },
+ { oLoggerFD, "logger-fd",1, "@" },
+ { oWithFingerprint, "with-fingerprint", 0, "@" },
+ { oDisableCipherAlgo, "disable-cipher-algo", 2, "@" },
+ { oDisablePubkeyAlgo, "disable-pubkey-algo", 2, "@" },
+ { oHonorHttpProxy,"honor-http-proxy", 0, "@" },
+ { oListOnly, "list-only", 0, "@"},
+ { oIgnoreTimeConflict, "ignore-time-conflict", 0, "@" },
+ { oNoRandomSeedFile, "no-random-seed-file", 0, "@" },
+{0} };
+
+
+
+int gpgsm_errors_seen = 0;
+
+/* It is possible that we are currentlu running under setuid permissions */
+static int maybe_setuid = 1;
+
+/* Option --enable-special-filenames */
+static int allow_special_filenames;
+
+
+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);
+static int open_read (const char *filename);
+static FILE *open_fwrite (const char *filename);
+static void run_protect_tool (int argc, char **argv);
+
+
+static int
+our_pk_test_algo (int algo)
+{
+ return 1;
+}
+
+static int
+our_cipher_test_algo (int algo)
+{
+ return 1;
+}
+
+static int
+our_md_test_algo (int algo)
+{
+ return 1;
+}
+
+static const char *
+my_strusage( int level )
+{
+ static char *digests, *pubkeys, *ciphers;
+ 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 <" PACKAGE_BUGREPORT ">.\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 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 < 110; i++ )
+ if (!chkf(i))
+ n += strlen(mapf(i)) + 2;
+ list = xmalloc (21 + n);
+ *list = 0;
+ for (p=NULL, i=1; i < 110; 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;
+}
+
+
+static void
+i18n_init(void)
+{
+#ifdef USE_SIMPLE_GETTEXT
+ set_gettext_file (PACKAGE);
+#else
+# ifdef ENABLE_NLS
+# ifdef HAVE_LC_MESSAGES
+ setlocale (LC_TIME, "");
+ setlocale (LC_MESSAGES, "");
+# else
+ setlocale (LC_ALL, "" );
+# endif
+ bindtextdomain (PACKAGE, LOCALEDIR);
+ textdomain (PACKAGE);
+# endif
+#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_debug(void)
+{
+ 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);
+}
+
+
+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;
+}
+
+
+int
+main ( int argc, char **argv)
+{
+ ARGPARSE_ARGS pargs;
+ int orig_argc;
+ char **orig_argv;
+ const char *fname;
+ /* char *username;*/
+ int may_coredump;
+ STRLIST sl, remusr= NULL, locusr=NULL;
+ STRLIST 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;
+ int greeting = 0;
+ int nogreeting = 0;
+ int debug_wait = 0;
+ int use_random_seed = 1;
+ int with_fpr = 0;
+ char *def_digest_string = NULL;
+ enum cmd_and_opt_values cmd = 0;
+ struct server_control_s ctrl;
+ CERTLIST recplist = NULL;
+ CERTLIST signerlist = NULL;
+
+ /* trap_unaligned ();*/
+ 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);
+ /* 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( _("libgcrypt is too old (need %s, have %s)\n"),
+ NEED_LIBGCRYPT_VERSION, gcry_check_version (NULL) );
+ }
+ if (!ksba_check_version (NEED_KSBA_VERSION) )
+ {
+ log_fatal( _("libksba is too old (need %s, have %s)\n"),
+ 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 */
+ i18n_init();
+
+ opt.def_cipher_algoid = "1.2.840.113549.3.7"; /*des-EDE3-CBC*/
+#ifdef __MINGW32__
+ opt.homedir = read_w32_registry_string ( NULL,
+ "Software\\GNU\\GnuPG", "HomeDir" );
+#else
+ opt.homedir = getenv ("GNUPGHOME");
+#endif
+ if (!opt.homedir || !*opt.homedir )
+ opt.homedir = GNUPG_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 );
+ assuan_set_malloc_hooks (gcry_malloc, gcry_realloc, gcry_free);
+ 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; /* not status output */
+ ctrl.autodetect_encoding = 1;
+
+ /* set the default option file */
+ if (default_config )
+ configname = make_filename (opt.homedir, "gpgsm.conf", NULL);
+ /* cet 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 aServer:
+ opt.batch = 1;
+ set_cmd (&cmd, aServer);
+ break;
+ case aCallDirmngr:
+ opt.batch = 1;
+ set_cmd (&cmd, aCallDirmngr);
+ break;
+
+ case aCallProtectTool:
+ opt.batch = 1;
+ set_cmd (&cmd, aCallProtectTool);
+ no_more_options = 1; /* Stop parsing. */
+ break;
+
+ case aCheckKeys: set_cmd (&cmd, aCheckKeys); break;
+ case aImport: set_cmd (&cmd, aImport); break;
+ case aSendKeys: set_cmd (&cmd, aSendKeys); break;
+ case aRecvKeys: set_cmd (&cmd, aRecvKeys); break;
+ case aExport: set_cmd (&cmd, aExport); break;
+ case aListKeys: set_cmd (&cmd, aListKeys); break;
+ case aListExternalKeys: set_cmd (&cmd, aListExternalKeys); break;
+ case aListSecretKeys: set_cmd (&cmd, aListSecretKeys); break;
+ case aListSigs: set_cmd (&cmd, aListSigs); break;
+
+ case aLearnCard: set_cmd (&cmd, aLearnCard); break;
+
+ case aPasswd: set_cmd (&cmd, aPasswd); break;
+
+ case aDeleteKey:
+ set_cmd (&cmd, aDeleteKey);
+ /*greeting=1;*/
+ break;
+
+ case aDetachedSign:
+ detached_sig = 1;
+ set_cmd (&cmd, aSign );
+ break;
+
+ case aSym: set_cmd (&cmd, aSym); break;
+ case aDecrypt: set_cmd (&cmd, aDecrypt); break;
+ case aEncr: set_cmd (&cmd, aEncr); break;
+ case aSign: set_cmd (&cmd, aSign ); break;
+ case aKeygen: set_cmd (&cmd, aKeygen); greeting=1; break;
+ case aClearsign: set_cmd (&cmd, aClearsign); break;
+ case aVerify: set_cmd (&cmd, aVerify); 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;
+
+ /* 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 oIncludeCerts: ctrl.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 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: opt.debug |= pargs.r.ret_ulong; break;
+ case oDebugAll: opt.debug = ~0; break;
+ case oDebugWait: debug_wait = pargs.r.ret_int; break;
+ case oDebugNoChainValidation: opt.no_chain_validation = 1; break;
+
+ case oStatusFD: ctrl.status_fd = pargs.r.ret_int; break;
+ case oLoggerFD: log_set_fd (pargs.r.ret_int ); break;
+ case oWithFingerprint:
+ with_fpr=1; /*fall thru*/
+ case oFingerprint:
+ 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: opt.display = xstrdup (pargs.r.ret_str); break;
+ case oTTYname: opt.ttyname = xstrdup (pargs.r.ret_str); break;
+ case oTTYtype: opt.ttytype = xstrdup (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 oFakedSystemTime:
+ gnupg_set_time ( (time_t)pargs.r.ret_ulong, 0);
+ break;
+
+ case oNoDefKeyring: default_keyring = 0; break;
+ case oNoGreeting: nogreeting = 1; break;
+
+ case oDefaultKey:
+ /* fixme:opt.def_secret_key = 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 oSkipVerify: opt.skip_verify=1; break;
+
+ case oNoEncryptTo: /*fixme: 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 oTextmodeShort: /*fixme:opt.textmode = 2;*/ break;
+ case oTextmode: /*fixme:opt.textmode=1;*/ break;
+
+ case oUser: /* store the local users, the first one is the default */
+ if (!opt.local_user)
+ opt.local_user = 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 oIgnoreTimeConflict: opt.ignore_time_conflict = 1; break;
+ case oNoRandomSeedFile: use_random_seed = 0; break;
+
+ case oEnableSpecialFilenames: allow_special_filenames =1; break;
+
+
+ case aDummy:
+ break;
+ default:
+ pargs.err = configfp? 1:2;
+ break;
+ }
+ }
+
+ if (configfp)
+ {
+ fclose (configfp);
+ configfp = NULL;
+ xfree (configname);
+ configname = NULL;
+ goto next_pass;
+ }
+
+ xfree (configname);
+ configname = NULL;
+
+ if (log_get_errorcount(0))
+ gpgsm_exit(2);
+
+ 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 (logfile && cmd == aServer)
+ {
+ log_set_file (logfile);
+ log_set_prefix (NULL, 1|2|4);
+ }
+
+ if (gnupg_faked_time_p ())
+ {
+ log_info (_("WARNING: running with faked system time: "));
+ gpgsm_dump_time (gnupg_get_time ());
+ log_printf ("\n");
+ }
+
+/*FIXME if (opt.batch) */
+/* tty_batchmode (1); */
+
+ gcry_control (GCRYCTL_RESUME_SECMEM_WARN);
+
+ set_debug ();
+
+ /* FIXME: should set filenames of libgcrypt explicitly
+ * gpg_opt_homedir = opt.homedir; */
+
+ /* must do this after dropping setuid, because the mapping functions
+ may try to load an module and we may have disabled an algorithm */
+ 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 (def_digest_string)
+ {
+ opt.def_digest_algo = gcry_md_map_name (def_digest_string);
+ xfree (def_digest_string);
+ def_digest_string = NULL;
+ if (our_md_test_algo(opt.def_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);
+
+ if (!nrings && default_keyring) /* add default keybox */
+ keydb_add_resource ("pubring.kbx", 0, 0);
+ for (sl = nrings; sl; sl = sl->next)
+ keydb_add_resource (sl->d, 0, 0);
+ FREE_STRLIST(nrings);
+
+
+ for (sl = locusr; sl; sl = sl->next)
+ {
+ int rc = gpgsm_add_to_certlist (&ctrl, sl->d, 1, &signerlist);
+ if (rc)
+ {
+ log_error (_("can't sign using `%s': %s\n"),
+ sl->d, gpg_strerror (rc));
+ gpgsm_status2 (&ctrl, STATUS_INV_RECP,
+ gpg_err_code (rc) == -1? "1":
+ gpg_err_code (rc) == GPG_ERR_NO_PUBKEY? "1":
+ gpg_err_code (rc) == GPG_ERR_AMBIGUOUS_NAME? "2":
+ gpg_err_code (rc) == GPG_ERR_WRONG_KEY_USAGE? "3":
+ gpg_err_code (rc) == GPG_ERR_CERT_REVOKED? "4":
+ gpg_err_code (rc) == GPG_ERR_CERT_EXPIRED? "5":
+ gpg_err_code (rc) == GPG_ERR_NO_CRL_KNOWN? "6":
+ gpg_err_code (rc) == GPG_ERR_CRL_TOO_OLD? "7":
+ gpg_err_code (rc) == GPG_ERR_NO_POLICY_MATCH? "8":
+ gpg_err_code (rc) == GPG_ERR_NO_SECKEY? "9":
+ "0",
+ sl->d, NULL);
+ }
+ }
+ for (sl = remusr; sl; sl = sl->next)
+ {
+ int rc = gpgsm_add_to_certlist (&ctrl, sl->d, 0, &recplist);
+ if (rc)
+ {
+ log_error (_("can't encrypt to `%s': %s\n"),
+ sl->d, gpg_strerror (rc));
+ gpgsm_status2 (&ctrl, STATUS_INV_RECP,
+ gpg_err_code (rc) == -1? "1":
+ gpg_err_code (rc) == GPG_ERR_NO_PUBKEY? "1":
+ gpg_err_code (rc) == GPG_ERR_AMBIGUOUS_NAME? "2":
+ gpg_err_code (rc) == GPG_ERR_WRONG_KEY_USAGE? "3":
+ gpg_err_code (rc) == GPG_ERR_CERT_REVOKED? "4":
+ gpg_err_code (rc) == GPG_ERR_CERT_EXPIRED? "5":
+ gpg_err_code (rc) == GPG_ERR_NO_CRL_KNOWN? "6":
+ gpg_err_code (rc) == GPG_ERR_CRL_TOO_OLD? "7":
+ gpg_err_code (rc) == GPG_ERR_NO_POLICY_MATCH? "8":
+ "0",
+ sl->d, NULL);
+ }
+ }
+ if (log_get_errorcount(0))
+ gpgsm_exit(1); /* must stop for invalid recipients */
+
+
+
+ fname = argc? *argv : NULL;
+
+ switch (cmd)
+ {
+ case aServer:
+ if (debug_wait)
+ {
+ log_debug ("waiting for debugger - my pid is %u .....\n",
+ (unsigned int)getpid());
+ sleep (debug_wait);
+ log_debug ("... okay\n");
+ }
+ gpgsm_server ();
+ 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 */
+ if (!argc)
+ gpgsm_encrypt (&ctrl, recplist, 0, stdout); /* from stdin */
+ else if (argc == 1)
+ gpgsm_encrypt (&ctrl, recplist, open_read (*argv), stdout); /* from file */
+ else
+ wrong_args (_("--encrypt [datafile]"));
+ break;
+
+ case aSign: /* sign the given file */
+ /* FIXME: We don't handle --output yet. We should also allow
+ to concatenate multiple files for signing because that is
+ what gpg does.*/
+ if (!argc)
+ gpgsm_sign (&ctrl, signerlist,
+ 0, detached_sig, stdout); /* create from stdin */
+ else if (argc == 1)
+ gpgsm_sign (&ctrl, signerlist,
+ open_read (*argv), detached_sig, stdout); /* from file */
+ else
+ wrong_args (_("--sign [datafile]"));
+ 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;
+
+ 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 aVerifyFiles:
+ log_error ("this command has not yet been implemented\n");
+ break;
+
+ case aDecrypt:
+ if (!argc)
+ gpgsm_decrypt (&ctrl, 0, stdout); /* from stdin */
+ else if (argc == 1)
+ gpgsm_decrypt (&ctrl, open_read (*argv), stdout); /* from file */
+ else
+ wrong_args (_("--decrypt [filename]"));
+ break;
+
+ case aDeleteKey:
+ for (sl=NULL; argc; argc--, argv++)
+ add_to_strlist (&sl, *argv);
+ gpgsm_delete (&ctrl, sl);
+ free_strlist(sl);
+ break;
+
+ case aListSigs:
+ ctrl.with_chain = 1;
+ case aListKeys:
+ for (sl=NULL; argc; argc--, argv++)
+ add_to_strlist (&sl, *argv);
+ gpgsm_list_keys (&ctrl, sl, stdout, (0 | (1<<6)));
+ free_strlist(sl);
+ break;
+
+ case aListExternalKeys:
+ for (sl=NULL; argc; argc--, argv++)
+ add_to_strlist (&sl, *argv);
+ gpgsm_list_keys (&ctrl, sl, stdout, (0 | (1<<7)));
+ free_strlist(sl);
+ break;
+
+ case aListSecretKeys:
+ for (sl=NULL; argc; argc--, argv++)
+ add_to_strlist (&sl, *argv);
+ gpgsm_list_keys (&ctrl, sl, stdout, (2 | (1<<6)));
+ free_strlist(sl);
+ break;
+
+ case aKeygen: /* generate a key */
+ log_error ("this function is not yet available from the commandline\n");
+ break;
+
+ case aImport:
+ gpgsm_import_files (&ctrl, argc, argv, open_read);
+ break;
+
+ case aExport:
+ for (sl=NULL; argc; argc--, argv++)
+ add_to_strlist (&sl, *argv);
+ gpgsm_export (&ctrl, sl, stdout);
+ free_strlist(sl);
+ 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 ();
+ 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;
+ KsbaCert cert = NULL;
+ char *grip = NULL;
+
+ rc = gpgsm_find_cert (*argv, &cert);
+ if (rc)
+ ;
+ else if (!(grip = gpgsm_get_keygrip_hexstring (cert)))
+ rc = gpg_error (GPG_ERR_BUG);
+ else
+ rc = gpgsm_agent_passwd (grip);
+ if (rc)
+ log_error ("error changing passphrase: %s\n", gpg_strerror (rc));
+ xfree (grip);
+ ksba_cert_release (cert);
+ }
+ break;
+
+ default:
+ log_error ("invalid command (there is no implicit command)\n");
+ break;
+ }
+
+ /* cleanup */
+ gpgsm_release_certlist (recplist);
+ gpgsm_release_certlist (signerlist);
+ FREE_STRLIST(remusr);
+ FREE_STRLIST(locusr);
+ gpgsm_exit(0);
+ return 8; /*NEVER REACHED*/
+}
+
+/* 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 = 1; /* only include the signer's cert */
+}
+
+
+
+/* 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)
+{
+ if (allow_special_filenames
+ && fname && *fname == '-' && fname[1] == '&' ) {
+ int i;
+
+ fname += 2;
+ for (i=0; isdigit (fname[i]); i++ )
+ ;
+ if ( !fname[i] )
+ return atoi (fname);
+ }
+ 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])
+ return 0; /* stdin */
+ fd = check_special_filename (filename);
+ if (fd != -1)
+ return fd;
+ fd = open (filename, O_RDONLY);
+ if (fd == -1)
+ {
+ log_error (_("can't open `%s': %s\n"), filename, strerror (errno));
+ gpgsm_exit (2);
+ }
+ return fd;
+}
+
+/* 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])
+ return stdout;
+
+ fd = check_special_filename (filename);
+ if (fd != -1)
+ {
+ fp = fdopen (dup (fd), "wb");
+ if (!fp)
+ {
+ log_error ("fdopen(%d) failed: %s\n", fd, strerror (errno));
+ gpgsm_exit (2);
+ }
+ return fp;
+ }
+ fp = 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)
+{
+ char *pgm = GNUPG_PROTECT_TOOL;
+ char **av;
+ int i;
+
+ av = xcalloc (argc+2, sizeof *av);
+ av[0] = strrchr (pgm, '/');
+ if (!av[0])
+ av[0] = 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));
+ gpgsm_exit (2);
+}
+
diff --git a/sm/gpgsm.h b/sm/gpgsm.h
new file mode 100644
index 000000000..f996d578c
--- /dev/null
+++ b/sm/gpgsm.h
@@ -0,0 +1,274 @@
+/* gpgsm.h - Global definitions for GpgSM
+ * 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 2 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuPG is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ */
+
+#ifndef 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/errors.h"
+
+#define OUT_OF_CORE(a) (gpg_error (gpg_err_code_from_errno ((a))))
+
+#define MAX_DIGEST_LEN 24
+
+/* A large struct name "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 *agent_program;
+ char *display;
+ char *ttyname;
+ char *ttytype;
+ char *lc_ctype;
+ char *lc_messages;
+
+ const char *dirmngr_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 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 *def_cipher_algoid; /* cipher algorithm to use if
+ nothing else is specified */
+
+ int def_digest_algo; /* Ditto for hash algorithm */
+ int def_compress_algo; /* Ditto for compress algorithm */
+
+ char *def_recipient; /* userID of the default recipient */
+ int def_recipient_self; /* The default recipient is the default key */
+
+ char *local_user; /* NULL or argument to -u */
+
+ 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 */
+
+ 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 auto_issuer_key_retrieve; /* try to retrieve a missing issuer key. */
+} opt;
+
+
+#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)
+
+struct server_local_s;
+
+/* Note that the default values for this 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;
+ int with_colons; /* use column delimited output format */
+ int with_chain; /* include the certifying certs in a listing */
+
+ 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) */
+};
+typedef struct server_control_s *CTRL;
+
+/* data structure used in base64.c */
+typedef struct base64_context_s *Base64Context;
+
+
+struct certlist_s {
+ struct certlist_s *next;
+ KsbaCert cert;
+};
+typedef struct certlist_s *CERTLIST;
+
+/*-- gpgsm.c --*/
+void gpgsm_exit (int rc);
+void gpgsm_init_default_ctrl (struct server_control_s *ctrl);
+
+/*-- server.c --*/
+void gpgsm_server (void);
+void gpgsm_status (CTRL ctrl, int no, const char *text);
+void gpgsm_status2 (CTRL ctrl, int no, ...);
+void gpgsm_status_with_err_code (CTRL ctrl, int no, const char *text,
+ gpg_err_code_t ec);
+
+/*-- fingerprint --*/
+char *gpgsm_get_fingerprint (KsbaCert cert, int algo, char *array, int *r_len);
+char *gpgsm_get_fingerprint_string (KsbaCert cert, int algo);
+char *gpgsm_get_fingerprint_hexstring (KsbaCert cert, int algo);
+unsigned long gpgsm_get_short_fingerprint (KsbaCert cert);
+char *gpgsm_get_keygrip (KsbaCert cert, char *array);
+char *gpgsm_get_keygrip_hexstring (KsbaCert cert);
+char *gpgsm_get_certid (KsbaCert cert);
+
+
+/*-- base64.c --*/
+int gpgsm_create_reader (Base64Context *ctx,
+ CTRL ctrl, FILE *fp, KsbaReader *r_reader);
+void gpgsm_destroy_reader (Base64Context ctx);
+int gpgsm_create_writer (Base64Context *ctx,
+ CTRL ctrl, FILE *fp, KsbaWriter *r_writer);
+int gpgsm_finish_writer (Base64Context ctx);
+void gpgsm_destroy_writer (Base64Context ctx);
+
+
+/*-- certdump.c --*/
+void gpgsm_print_serial (FILE *fp, KsbaConstSexp p);
+void gpgsm_print_time (FILE *fp, time_t t);
+void gpgsm_print_name (FILE *fp, const char *string);
+
+void gpgsm_dump_cert (const char *text, KsbaCert cert);
+void gpgsm_dump_serial (KsbaConstSexp p);
+void gpgsm_dump_time (time_t t);
+void gpgsm_dump_string (const char *string);
+
+
+
+/*-- certcheck.c --*/
+int gpgsm_check_cert_sig (KsbaCert issuer_cert, KsbaCert cert);
+int gpgsm_check_cms_signature (KsbaCert cert, KsbaConstSexp sigval,
+ gcry_md_hd_t md, int hash_algo);
+/* fixme: move create functions to another file */
+int gpgsm_create_cms_signature (KsbaCert cert, gcry_md_hd_t md, int mdalgo,
+ char **r_sigval);
+
+
+/*-- certchain.c --*/
+int gpgsm_walk_cert_chain (KsbaCert start, KsbaCert *r_next);
+int gpgsm_is_root_cert (KsbaCert cert);
+int gpgsm_validate_chain (CTRL ctrl, KsbaCert cert, time_t *r_exptime);
+int gpgsm_basic_cert_check (KsbaCert cert);
+
+/*-- certlist.c --*/
+int gpgsm_cert_use_sign_p (KsbaCert cert);
+int gpgsm_cert_use_encrypt_p (KsbaCert cert);
+int gpgsm_cert_use_verify_p (KsbaCert cert);
+int gpgsm_cert_use_decrypt_p (KsbaCert cert);
+int gpgsm_cert_use_cert_p (KsbaCert cert);
+int gpgsm_add_to_certlist (CTRL ctrl, const char *name, int secret,
+ CERTLIST *listaddr);
+void gpgsm_release_certlist (CERTLIST list);
+int gpgsm_find_cert (const char *name, KsbaCert *r_cert);
+
+/*-- keylist.c --*/
+void gpgsm_list_keys (CTRL ctrl, STRLIST names, FILE *fp, unsigned int mode);
+
+/*-- import.c --*/
+int gpgsm_import (CTRL ctrl, int in_fd);
+int gpgsm_import_files (CTRL ctrl, int nfiles, char **files,
+ int (*of)(const char *fname));
+
+/*-- export.c --*/
+void gpgsm_export (CTRL ctrl, STRLIST names, FILE *fp);
+
+/*-- delete.c --*/
+int gpgsm_delete (CTRL ctrl, STRLIST names);
+
+/*-- verify.c --*/
+int gpgsm_verify (CTRL ctrl, int in_fd, int data_fd, FILE *out_fp);
+
+/*-- sign.c --*/
+int gpgsm_get_default_cert (KsbaCert *r_cert);
+int gpgsm_sign (CTRL ctrl, CERTLIST signerlist,
+ int data_fd, int detached, FILE *out_fp);
+
+/*-- encrypt.c --*/
+int gpgsm_encrypt (CTRL ctrl, CERTLIST recplist, int in_fd, FILE *out_fp);
+
+/*-- decrypt.c --*/
+int gpgsm_decrypt (CTRL ctrl, int in_fd, FILE *out_fp);
+
+/*-- certreqgen.c --*/
+int gpgsm_genkey (CTRL ctrl, int in_fd, FILE *out_fp);
+
+/*-- call-agent.c --*/
+int gpgsm_agent_pksign (const char *keygrip,
+ unsigned char *digest,
+ size_t digestlen,
+ int digestalgo,
+ char **r_buf, size_t *r_buflen);
+int gpgsm_agent_pkdecrypt (const char *keygrip,
+ KsbaConstSexp ciphertext,
+ char **r_buf, size_t *r_buflen);
+int gpgsm_agent_genkey (KsbaConstSexp keyparms, KsbaSexp *r_pubkey);
+int gpgsm_agent_istrusted (KsbaCert cert);
+int gpgsm_agent_havekey (const char *hexkeygrip);
+int gpgsm_agent_marktrusted (KsbaCert cert);
+int gpgsm_agent_learn (void);
+int gpgsm_agent_passwd (const char *hexkeygrip);
+
+/*-- call-dirmngr.c --*/
+int gpgsm_dirmngr_isvalid (KsbaCert cert);
+int gpgsm_dirmngr_lookup (CTRL ctrl, STRLIST names,
+ void (*cb)(void*, KsbaCert), void *cb_value);
+int gpgsm_dirmngr_run_command (CTRL ctrl, const char *command,
+ int argc, char **argv);
+
+
+
+
+
+#endif /*GPGSM_H*/
diff --git a/sm/import.c b/sm/import.c
new file mode 100644
index 000000000..17dc3d66c
--- /dev/null
+++ b/sm/import.c
@@ -0,0 +1,349 @@
+/* import.c - Import certificates
+ * 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 2 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuPG is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ */
+
+#include <config.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <time.h>
+#include <assert.h>
+
+#include "gpgsm.h"
+#include <gcrypt.h>
+#include <ksba.h>
+
+#include "keydb.h"
+#include "i18n.h"
+
+struct stats_s {
+ unsigned long count;
+ unsigned long imported;
+ unsigned long unchanged;
+ unsigned long not_imported;
+};
+
+
+
+static void
+print_imported_status (CTRL ctrl, KsbaCert cert)
+{
+ char *fpr;
+
+ fpr = gpgsm_get_fingerprint_hexstring (cert, GCRY_MD_SHA1);
+ gpgsm_status2 (ctrl, STATUS_IMPORTED, fpr, "[X.509]", 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 ctrl, KsbaCert 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 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->not_imported)
+ log_info (_(" not imported: %lu\n"), stats->not_imported);
+ }
+
+ sprintf (buf, "%lu 0 %lu 0 %lu 0 0 0 0 0 0 0 0 %lu",
+ stats->count,
+ stats->imported,
+ stats->unchanged,
+ stats->not_imported
+ );
+ gpgsm_status (ctrl, STATUS_IMPORT_RES, buf);
+}
+
+
+
+static void
+check_and_store (CTRL ctrl, struct stats_s *stats, KsbaCert cert, int depth)
+{
+ int rc;
+
+ stats->count++;
+ if ( depth >= 50 )
+ {
+ log_error (_("certificate chain too long\n"));
+ stats->not_imported++;
+ print_import_problem (ctrl, cert, 3);
+ return;
+ }
+
+ rc = gpgsm_basic_cert_check (cert);
+ if (!rc)
+ {
+ int existed;
+
+ if (!keydb_store_cert (cert, 0, &existed))
+ {
+ KsbaCert next = NULL;
+
+ if (!existed)
+ {
+ print_imported_status (ctrl, cert);
+ stats->imported++;
+ }
+ else
+ 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.*/
+ else if (!gpgsm_walk_cert_chain (cert, &next))
+ {
+ check_and_store (ctrl, stats, next, depth+1);
+ ksba_cert_release (next);
+ }
+ }
+ else
+ {
+ log_error (_("error storing certificate\n"));
+ stats->not_imported++;
+ print_import_problem (ctrl, cert, 4);
+ }
+ }
+ else
+ {
+ log_error (_("basic certificate checks failed - not imported\n"));
+ stats->not_imported++;
+ print_import_problem (ctrl, cert,
+ gpg_err_code (rc) == GPG_ERR_MISSING_CERT? 2 :
+ gpg_err_code (rc) == GPG_ERR_BAD_CERT? 1 : 0);
+ }
+}
+
+
+
+
+static int
+import_one (CTRL ctrl, struct stats_s *stats, int in_fd)
+{
+ int rc;
+ Base64Context b64reader = NULL;
+ KsbaReader reader;
+ KsbaCert cert = NULL;
+ KsbaCMS cms = NULL;
+ FILE *fp = NULL;
+ KsbaContentType ct;
+
+ 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, &reader);
+ if (rc)
+ {
+ log_error ("can't create reader: %s\n", gpg_strerror (rc));
+ goto leave;
+ }
+
+ ct = ksba_cms_identify (reader);
+ if (ct == KSBA_CT_SIGNED_DATA)
+ { /* This is probably a signed-only message - import the certs */
+ KsbaStopReason stopreason;
+ int i;
+
+ cms = ksba_cms_new ();
+ if (!cms)
+ {
+ rc = gpg_error (GPG_ERR_ENOMEM);
+ goto leave;
+ }
+
+ rc = ksba_cms_set_reader_writer (cms, reader, NULL);
+ if (rc)
+ {
+ log_error ("ksba_cms_set_reader_writer failed: %s\n",
+ ksba_strerror (rc));
+ rc = map_ksba_err (rc);
+ goto leave;
+ }
+
+
+ do
+ {
+ rc = ksba_cms_parse (cms, &stopreason);
+ if (rc)
+ {
+ log_error ("ksba_cms_parse failed: %s\n", ksba_strerror (rc));
+ rc = map_ksba_err (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 if (ct == KSBA_CT_NONE)
+ { /* Failed to identify this message - assume a certificate */
+
+ cert = ksba_cert_new ();
+ if (!cert)
+ {
+ rc = gpg_error (GPG_ERR_ENOMEM);
+ goto leave;
+ }
+
+ rc = ksba_cert_read_der (cert, reader);
+ if (rc)
+ {
+ rc = map_ksba_err (rc);
+ goto leave;
+ }
+
+ check_and_store (ctrl, stats, cert, 0);
+ }
+ else
+ {
+ log_error ("can't extract certificates from input\n");
+ rc = gpg_error (GPG_ERR_NO_DATA);
+ }
+
+ leave:
+ ksba_cms_release (cms);
+ ksba_cert_release (cert);
+ gpgsm_destroy_reader (b64reader);
+ if (fp)
+ fclose (fp);
+ return rc;
+}
+
+
+int
+gpgsm_import (CTRL ctrl, int in_fd)
+{
+ int rc;
+ struct stats_s stats;
+
+ memset (&stats, 0, sizeof stats);
+ 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 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;
+}
+
+
diff --git a/sm/keydb.c b/sm/keydb.c
new file mode 100644
index 000000000..fe6556549
--- /dev/null
+++ b/sm/keydb.c
@@ -0,0 +1,1282 @@
+/* keydb.c - key database dispatcher
+ * 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 2 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuPG is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ */
+
+#include <config.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <assert.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include "gpgsm.h"
+#include "../kbx/keybox.h"
+#include "keydb.h"
+#include "i18n.h"
+
+#define DIRSEP_C '/'
+
+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.
+ * Note: this function may be called before secure memory is
+ * available.
+ */
+int
+keydb_add_resource (const char *url, int force, int secret)
+{
+ static int any_secret, any_public;
+ const char *resname = url;
+ char *filename = NULL;
+ int rc = 0;
+ FILE *fp;
+ KeydbResourceType rt = KEYDB_RESOURCE_TYPE_NONE;
+ const char *created_fname = NULL;
+
+ /* 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));
+ goto leave;
+ }
+
+ if (!opt.quiet)
+ log_info (_("keybox `%s' created\n"), filename);
+ created_fname = filename;
+ }
+ 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);
+
+ 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;
+}
+
+
+
+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 sequence by all processes. We are
+ cuurently 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;
+
+ return rc;
+}
+
+static void
+unlock_all (KEYDB_HANDLE hd)
+{
+ int i;
+
+ if (!hd->locked)
+ return;
+
+ for (i=hd->used-1; i >= 0; i--)
+ {
+ switch (hd->active[i].type)
+ {
+ case KEYDB_RESOURCE_TYPE_NONE:
+ break;
+ case KEYDB_RESOURCE_TYPE_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;
+
+ rc = lock_all (hd);
+ if (rc)
+ return rc;
+
+ switch (hd->active[hd->found].type) {
+ case KEYDB_RESOURCE_TYPE_NONE:
+ rc = G10ERR_GENERAL; /* oops */
+ break;
+ case KEYDB_RESOURCE_TYPE_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 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_cert (KEYDB_HANDLE hd, KsbaCert *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;
+}
+
+/*
+ * Insert a new Certificate into one of the resources.
+ */
+int
+keydb_insert_cert (KEYDB_HANDLE hd, KsbaCert cert)
+{
+ int rc = -1;
+ int idx;
+ 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);
+
+ rc = lock_all (hd);
+ if (rc)
+ return rc;
+
+ 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, KsbaCert cert)
+{
+ int rc = 0;
+ 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 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;
+
+ rc = lock_all (hd);
+ if (rc)
+ return rc;
+
+ 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;
+ }
+
+ unlock_all (hd);
+ return rc;
+}
+
+
+
+/*
+ * Locate the default writable key resource, so that the next
+ * operation (which is only relevant for inserts) will be done on this
+ * resource.
+ */
+int
+keydb_locate_writable (KEYDB_HANDLE hd, const char *reserved)
+{
+ int rc;
+
+ if (!hd)
+ return 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;
+
+ 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, KsbaConstSexp 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
+hextobyte (const unsigned char *s)
+{
+ int c;
+
+ if( *s >= '0' && *s <= '9' )
+ c = 16 * (*s - '0');
+ else if ( *s >= 'A' && *s <= 'F' )
+ c = 16 * (10 + *s - 'A');
+ else if ( *s >= 'a' && *s <= 'f' )
+ c = 16 * (10 + *s - 'a');
+ else
+ return -1;
+ s++;
+ if ( *s >= '0' && *s <= '9' )
+ c += *s - '0';
+ else if ( *s >= 'A' && *s <= 'F' )
+ c += 10 + *s - 'A';
+ else if ( *s >= 'a' && *s <= 'f' )
+ c += 10 + *s - 'a';
+ else
+ return -1;
+ return c;
+}
+
+
+static 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 = 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;
+
+ 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 (KsbaCert 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 = 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;
+}
+
+
+
diff --git a/sm/keylist.c b/sm/keylist.c
new file mode 100644
index 000000000..634bda292
--- /dev/null
+++ b/sm/keylist.c
@@ -0,0 +1,617 @@
+/* keylist.c
+ * Copyright (C) 1998, 1999, 2000, 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 2 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuPG is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ */
+
+#include <config.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <time.h>
+#include <assert.h>
+
+#include "gpgsm.h"
+
+#include <gcrypt.h>
+#include <ksba.h>
+
+#include "keydb.h"
+#include "i18n.h"
+
+struct list_external_parm_s {
+ FILE *fp;
+ int print_header;
+ int with_colons;
+ int with_chain;
+};
+
+
+
+static void
+print_key_data (KsbaCert cert, FILE *fp)
+{
+#if 0
+ int n = pk ? pubkey_get_npkey( pk->pubkey_algo ) : 0;
+ int i;
+
+ for(i=0; i < n; i++ )
+ {
+ fprintf (fp, "pkd:%d:%u:", i, mpi_get_nbits( pk->pkey[i] ) );
+ mpi_print(stdout, pk->pkey[i], 1 );
+ putchar(':');
+ putchar('\n');
+ }
+#endif
+}
+
+static void
+print_capabilities (KsbaCert cert, FILE *fp)
+{
+ KsbaError err;
+ unsigned int use;
+
+ err = ksba_cert_get_key_usage (cert, &use);
+ if (err == KSBA_No_Data)
+ {
+ putc ('e', fp);
+ putc ('s', fp);
+ putc ('c', fp);
+ putc ('E', fp);
+ putc ('S', fp);
+ putc ('C', fp);
+ return;
+ }
+ if (err)
+ {
+ log_error (_("error getting key usage information: %s\n"),
+ ksba_strerror (err));
+ return;
+ }
+
+ if ((use & (KSBA_KEYUSAGE_KEY_ENCIPHERMENT|KSBA_KEYUSAGE_DATA_ENCIPHERMENT)))
+ putc ('e', fp);
+ if ((use & (KSBA_KEYUSAGE_DIGITAL_SIGNATURE|KSBA_KEYUSAGE_NON_REPUDIATION)))
+ putc ('s', fp);
+ if ((use & KSBA_KEYUSAGE_KEY_CERT_SIGN))
+ putc ('c', fp);
+ if ((use & (KSBA_KEYUSAGE_KEY_ENCIPHERMENT|KSBA_KEYUSAGE_DATA_ENCIPHERMENT)))
+ putc ('E', fp);
+ if ((use & (KSBA_KEYUSAGE_DIGITAL_SIGNATURE|KSBA_KEYUSAGE_NON_REPUDIATION)))
+ putc ('S', fp);
+ if ((use & KSBA_KEYUSAGE_KEY_CERT_SIGN))
+ putc ('C', fp);
+}
+
+
+static void
+print_time (time_t t, FILE *fp)
+{
+ if (!t)
+ ;
+ else if ( t == (time_t)(-1) )
+ putc ('?', fp);
+ else
+ fprintf (fp, "%lu", (unsigned long)t);
+}
+
+
+/* return an allocated string with the email address extracted from a
+ DN */
+static char *
+email_kludge (const char *name)
+{
+ const unsigned char *p;
+ unsigned char *buf;
+ int n;
+
+ if (strncmp (name, "1.2.840.113549.1.9.1=#", 22))
+ return NULL;
+ /* 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 */
+ name += 22;
+ for (n=0, p=name; hexdigitp (p) && hexdigitp (p+1); p +=2, n++)
+ ;
+ if (*p != '#' || !n)
+ return NULL;
+ buf = xtrymalloc (n+3);
+ if (!buf)
+ return NULL; /* oops, out of core */
+ *buf = '<';
+ for (n=1, p=name; *p != '#'; p +=2, n++)
+ buf[n] = xtoi_2 (p);
+ buf[n++] = '>';
+ buf[n] = 0;
+ return buf;
+}
+
+
+
+
+/* List one certificate in colon mode */
+static void
+list_cert_colon (KsbaCert cert, FILE *fp, int have_secret)
+{
+ int idx, trustletter = 0;
+ char *p;
+ KsbaSexp sexp;
+ char *fpr;
+
+ fputs (have_secret? "crs:":"crt:", fp);
+ trustletter = 0;
+#if 0
+ if (is_not_valid (cert))
+ putc ('i', fp);
+ else if ( is_revoked (cert) )
+ putc ('r', fp);
+ else if ( has_expired (cert))
+ putcr ('e', fp);
+ else
+#endif
+ {
+ trustletter = '?'; /*get_validity_info ( pk, NULL );*/
+ putc (trustletter, fp);
+ }
+
+ fpr = gpgsm_get_fingerprint_hexstring (cert, GCRY_MD_SHA1);
+ fprintf (fp, ":%u:%d:%s:",
+ /*keylen_of_cert (cert)*/1024,
+ /* pubkey_algo_of_cert (cert)*/1,
+ fpr+24);
+
+ /* we assume --fixed-list-mode for gpgsm */
+ print_time ( ksba_cert_get_validity (cert, 0), fp);
+ putc (':', fp);
+ print_time ( ksba_cert_get_validity (cert, 1), fp);
+ 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++)
+ fprintf (fp,"%02X", *s);
+ }
+ xfree (sexp);
+ }
+ putc (':', fp);
+ /* field 9, ownertrust - not used here */
+ putc (':', fp);
+ /* field 10, old user ID - we use it here for the issuer DN */
+ if ((p = ksba_cert_get_issuer (cert,0)))
+ {
+ print_sanitized_string (fp, p, ':');
+ xfree (p);
+ }
+ putc (':', fp);
+ /* field 11, signature class - not used */
+ putc (':', fp);
+ /* field 12, capabilities: */
+ print_capabilities (cert, fp);
+ putc (':', fp);
+ putc ('\n', fp);
+
+ /* FPR record */
+ fprintf (fp, "fpr:::::::::%s:::", fpr);
+ xfree (fpr); fpr = NULL;
+ /* print chaining ID (field 13)*/
+ {
+ KsbaCert next;
+
+ if (!gpgsm_walk_cert_chain (cert, &next))
+ {
+ p = gpgsm_get_fingerprint_hexstring (next, GCRY_MD_SHA1);
+ fputs (p, fp);
+ xfree (p);
+ ksba_cert_release (next);
+ }
+ }
+ putc (':', fp);
+ putc ('\n', fp);
+
+
+ if (opt.with_key_data)
+ {
+ if ( (p = gpgsm_get_keygrip_hexstring (cert)))
+ {
+ fprintf (fp, "grp:::::::::%s:\n", p);
+ xfree (p);
+ }
+ print_key_data (cert, fp);
+ }
+
+ for (idx=0; (p = ksba_cert_get_subject (cert,idx)); idx++)
+ {
+ fprintf (fp, "uid:%c::::::::", trustletter);
+ print_sanitized_string (fp, p, ':');
+ putc (':', fp);
+ putc (':', fp);
+ 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 */
+ char *pp = email_kludge (p);
+ if (pp)
+ {
+ fprintf (fp, "uid:%c::::::::", trustletter);
+ print_sanitized_string (fp, pp, ':');
+ putc (':', fp);
+ putc (':', fp);
+ putc ('\n', fp);
+ xfree (pp);
+ }
+ }
+ xfree (p);
+ }
+}
+
+
+/* List one certificate in standard mode */
+static void
+list_cert_std (KsbaCert cert, FILE *fp, int have_secret)
+{
+ KsbaError kerr;
+ KsbaSexp sexp;
+ char *dn;
+ time_t t;
+ int idx;
+ int is_ca, chainlen;
+ unsigned int kusage;
+ char *string, *p;
+
+ sexp = ksba_cert_get_serial (cert);
+ fputs ("Serial number: ", fp);
+ gpgsm_print_serial (fp, sexp);
+ ksba_free (sexp);
+ putc ('\n', fp);
+
+ dn = ksba_cert_get_issuer (cert, 0);
+ fputs (" Issuer: ", fp);
+ gpgsm_print_name (fp, dn);
+ ksba_free (dn);
+ putc ('\n', fp);
+ for (idx=1; (dn = ksba_cert_get_issuer (cert, idx)); idx++)
+ {
+ fputs (" aka: ", fp);
+ gpgsm_print_name (fp, dn);
+ ksba_free (dn);
+ putc ('\n', fp);
+ }
+
+ dn = ksba_cert_get_subject (cert, 0);
+ fputs (" Subject: ", fp);
+ gpgsm_print_name (fp, dn);
+ ksba_free (dn);
+ putc ('\n', fp);
+ for (idx=1; (dn = ksba_cert_get_subject (cert, idx)); idx++)
+ {
+ fputs (" aka: ", fp);
+ gpgsm_print_name (fp, dn);
+ ksba_free (dn);
+ putc ('\n', fp);
+ }
+
+ t = ksba_cert_get_validity (cert, 0);
+ fputs (" validity: ", fp);
+ gpgsm_print_time (fp, t);
+ fputs (" through ", fp);
+ t = ksba_cert_get_validity (cert, 1);
+ gpgsm_print_time (fp, t);
+ putc ('\n', fp);
+
+ kerr = ksba_cert_get_key_usage (cert, &kusage);
+ if (kerr != KSBA_No_Data)
+ {
+ fputs (" key usage:", fp);
+ if (kerr)
+ fprintf (fp, " [error: %s]", ksba_strerror (kerr));
+ else
+ {
+ if ( (kusage & KSBA_KEYUSAGE_DIGITAL_SIGNATURE))
+ fputs (" digitalSignature", fp);
+ if ( (kusage & KSBA_KEYUSAGE_NON_REPUDIATION))
+ fputs (" nonRepudiation", fp);
+ if ( (kusage & KSBA_KEYUSAGE_KEY_ENCIPHERMENT))
+ fputs (" keyEncipherment", fp);
+ if ( (kusage & KSBA_KEYUSAGE_DATA_ENCIPHERMENT))
+ fputs (" dataEncipherment", fp);
+ if ( (kusage & KSBA_KEYUSAGE_KEY_AGREEMENT))
+ fputs (" keyAgreement", fp);
+ if ( (kusage & KSBA_KEYUSAGE_KEY_CERT_SIGN))
+ fputs (" certSign", fp);
+ if ( (kusage & KSBA_KEYUSAGE_CRL_SIGN))
+ fputs (" crlSign", fp);
+ if ( (kusage & KSBA_KEYUSAGE_ENCIPHER_ONLY))
+ fputs (" encipherOnly", fp);
+ if ( (kusage & KSBA_KEYUSAGE_DECIPHER_ONLY))
+ fputs (" decipherOnly", fp);
+ }
+ putc ('\n', fp);
+ }
+
+ kerr = ksba_cert_get_cert_policies (cert, &string);
+ if (kerr != KSBA_No_Data)
+ {
+ fputs (" policies: ", fp);
+ if (kerr)
+ fprintf (fp, "[error: %s]", ksba_strerror (kerr));
+ else
+ {
+ for (p=string; *p; p++)
+ {
+ if (*p == '\n')
+ *p = ',';
+ }
+ print_sanitized_string (fp, string, 0);
+ xfree (string);
+ }
+ putc ('\n', fp);
+ }
+
+ kerr = ksba_cert_is_ca (cert, &is_ca, &chainlen);
+ if (kerr || is_ca)
+ {
+ fputs (" chain length: ", fp);
+ if (kerr)
+ fprintf (fp, "[error: %s]", ksba_strerror (kerr));
+ else if (chainlen == -1)
+ fputs ("unlimited", fp);
+ else
+ fprintf (fp, "%d", chainlen);
+ putc ('\n', fp);
+ }
+
+
+ dn = gpgsm_get_fingerprint_string (cert, 0);
+ fprintf (fp, " fingerprint: %s\n", dn?dn:"error");
+ xfree (dn);
+}
+
+/* Same as standard mode mode list all certifying certts too */
+static void
+list_cert_chain (KsbaCert cert, FILE *fp)
+{
+ KsbaCert next = NULL;
+
+ list_cert_std (cert, fp, 0);
+ ksba_cert_ref (cert);
+ while (!gpgsm_walk_cert_chain (cert, &next))
+ {
+ ksba_cert_release (cert);
+ fputs ("Certified by\n", fp);
+ list_cert_std (next, fp, 0);
+ cert = next;
+ }
+ ksba_cert_release (cert);
+ putc ('\n', fp);
+}
+
+
+
+/* List all internal keys or just the key given as NAMES.
+ */
+static void
+list_internal_keys (CTRL ctrl, STRLIST names, FILE *fp, unsigned int mode)
+{
+ KEYDB_HANDLE hd;
+ KEYDB_SEARCH_DESC *desc = NULL;
+ STRLIST sl;
+ int ndesc;
+ KsbaCert cert = NULL;
+ int rc=0;
+ const char *lastresname, *resname;
+ int have_secret;
+
+ 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 ("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++;
+ }
+
+ }
+
+ /* 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 */
+
+ lastresname = NULL;
+ while (!(rc = keydb_search (hd, desc, ndesc)))
+ {
+ 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;
+ }
+
+ resname = keydb_get_resource_name (hd);
+
+ if (lastresname != resname )
+ {
+ int i;
+
+ if (ctrl->no_server)
+ {
+ fprintf (fp, "%s\n", resname );
+ for (i=strlen(resname); i; i-- )
+ putchar('-');
+ putc ('\n', fp);
+ lastresname = resname;
+ }
+ }
+
+ have_secret = 0;
+ if (mode)
+ {
+ char *p = gpgsm_get_keygrip_hexstring (cert);
+ if (p)
+ {
+ if (!gpgsm_agent_havekey (p))
+ have_secret = 1;
+ xfree (p);
+ }
+ }
+
+ if (!mode
+ || ((mode & 1) && !have_secret)
+ || ((mode & 2) && have_secret) )
+ {
+ if (ctrl->with_colons)
+ list_cert_colon (cert, fp, have_secret);
+ else if (ctrl->with_chain)
+ list_cert_chain (cert, fp);
+ else
+ {
+ list_cert_std (cert, fp, have_secret);
+ putc ('\n', fp);
+ }
+ }
+ ksba_cert_release (cert);
+ cert = NULL;
+ }
+ if (rc && rc != -1)
+ log_error ("keydb_search failed: %s\n", gpg_strerror (rc));
+
+ leave:
+ ksba_cert_release (cert);
+ xfree (desc);
+ keydb_release (hd);
+}
+
+
+
+static void
+list_external_cb (void *cb_value, KsbaCert 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;
+
+ fprintf (parm->fp, "%s\n", resname );
+ for (i=strlen(resname); i; i-- )
+ putchar('-');
+ putc ('\n', parm->fp);
+ parm->print_header = 0;
+ }
+
+ if (parm->with_colons)
+ list_cert_colon (cert, parm->fp, 0);
+ else if (parm->with_chain)
+ list_cert_chain (cert, parm->fp);
+ else
+ {
+ list_cert_std (cert, parm->fp, 0);
+ 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 void
+list_external_keys (CTRL ctrl, STRLIST names, FILE *fp)
+{
+ int rc;
+ struct list_external_parm_s parm;
+
+ parm.fp = fp;
+ parm.print_header = ctrl->no_server;
+ parm.with_colons = ctrl->with_colons;
+ parm.with_chain = ctrl->with_chain;
+
+ rc = gpgsm_dirmngr_lookup (ctrl, names, list_external_cb, &parm);
+ if (rc)
+ log_error ("listing external keys failed: %s\n", gpg_strerror (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
+ */
+void
+gpgsm_list_keys (CTRL ctrl, STRLIST names, FILE *fp, unsigned int mode)
+{
+ if ((mode & (1<<6)))
+ list_internal_keys (ctrl, names, fp, (mode & 3));
+ if ((mode & (1<<7)))
+ list_external_keys (ctrl, names, fp);
+}
diff --git a/sm/server.c b/sm/server.c
new file mode 100644
index 000000000..dda150964
--- /dev/null
+++ b/sm/server.c
@@ -0,0 +1,1070 @@
+/* server.c - Server mode and main entry point
+ * 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 2 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuPG is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ */
+
+#include <config.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+#include <ctype.h>
+#include <unistd.h>
+
+#include <assuan.h>
+
+#include "gpgsm.h"
+
+#define set_error(e,t) assuan_set_error (ctx, ASSUAN_ ## 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 assuan_ctx;
+ int message_fd;
+ int list_internal;
+ int list_external;
+ CERTLIST recplist;
+ CERTLIST signerlist;
+};
+
+
+
+/* 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 unsigned 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;
+}
+
+
+
+
+/* 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);
+ return (s && (s == line || spacep (s-1)) && (!s[n] || spacep (s+n)));
+}
+
+
+static void
+close_message_fd (CTRL ctrl)
+{
+ if (ctrl->server_local->message_fd != -1)
+ {
+ close (ctrl->server_local->message_fd);
+ ctrl->server_local->message_fd = -1;
+ }
+}
+
+
+static int
+option_handler (ASSUAN_CONTEXT ctx, const char *key, const char *value)
+{
+ CTRL ctrl = assuan_get_pointer (ctx);
+
+ if (!strcmp (key, "include-certs"))
+ {
+ int i = *value? atoi (value) : -1;
+ if (ctrl->include_certs < -2)
+ return ASSUAN_Parameter_Error;
+ ctrl->include_certs = i;
+ }
+ else if (!strcmp (key, "display"))
+ {
+ if (opt.display)
+ free (opt.display);
+ opt.display = strdup (value);
+ if (!opt.display)
+ return ASSUAN_Out_Of_Core;
+ }
+ else if (!strcmp (key, "ttyname"))
+ {
+ if (opt.ttyname)
+ free (opt.ttyname);
+ opt.ttyname = strdup (value);
+ if (!opt.ttyname)
+ return ASSUAN_Out_Of_Core;
+ }
+ else if (!strcmp (key, "ttytype"))
+ {
+ if (opt.ttytype)
+ free (opt.ttytype);
+ opt.ttytype = strdup (value);
+ if (!opt.ttytype)
+ return ASSUAN_Out_Of_Core;
+ }
+ else if (!strcmp (key, "lc-ctype"))
+ {
+ if (opt.lc_ctype)
+ free (opt.lc_ctype);
+ opt.lc_ctype = strdup (value);
+ if (!opt.lc_ctype)
+ return ASSUAN_Out_Of_Core;
+ }
+ else if (!strcmp (key, "lc-messages"))
+ {
+ if (opt.lc_messages)
+ free (opt.lc_messages);
+ opt.lc_messages = strdup (value);
+ if (!opt.lc_messages)
+ return ASSUAN_Out_Of_Core;
+ }
+ 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
+ return ASSUAN_Parameter_Error;
+ }
+ else
+ return ASSUAN_Invalid_Option;
+
+ return 0;
+}
+
+
+
+
+static void
+reset_notify (ASSUAN_CONTEXT ctx)
+{
+ CTRL ctrl = assuan_get_pointer (ctx);
+
+ 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);
+}
+
+
+static void
+input_notify (ASSUAN_CONTEXT ctx, const char *line)
+{
+ CTRL 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;
+}
+
+static void
+output_notify (ASSUAN_CONTEXT ctx, const char *line)
+{
+ CTRL 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 */
+}
+
+
+
+/* RECIPIENT <userID>
+
+ Set the recipient for the encryption. <userID> should be the
+ internal representation of the key; the server may accept any other
+ way of specification [we will support this]. If this is a valid and
+ trusted recipient the server does respond with OK, otherwise the
+ return is an ERR with the reason why the recipient can't be used,
+ the encryption will then not be done for this recipient. IF the
+ policy is not to encrypt at all if not all recipients are valid, the
+ client has to take care of this. All RECIPIENT commands are
+ cumulative until a RESET or an successful ENCRYPT command. */
+static int
+cmd_recipient (ASSUAN_CONTEXT ctx, char *line)
+{
+ CTRL ctrl = assuan_get_pointer (ctx);
+ int rc;
+
+ rc = gpgsm_add_to_certlist (ctrl, line, 0, &ctrl->server_local->recplist);
+ if (rc)
+ {
+ gpg_err_code_t r = gpg_err_code (rc);
+ gpgsm_status2 (ctrl, STATUS_INV_RECP,
+ r == -1? "1":
+ r == GPG_ERR_NO_PUBKEY? "1":
+ r == GPG_ERR_AMBIGUOUS_NAME? "2":
+ r == GPG_ERR_WRONG_KEY_USAGE? "3":
+ r == GPG_ERR_CERT_REVOKED? "4":
+ r == GPG_ERR_CERT_EXPIRED? "5":
+ r == GPG_ERR_NO_CRL_KNOWN? "6":
+ r == GPG_ERR_CRL_TOO_OLD? "7":
+ r == GPG_ERR_NO_POLICY_MATCH? "8":
+ "0",
+ line, NULL);
+ }
+
+ return map_to_assuan_status (rc);
+}
+
+/* SIGNER <userID>
+
+ Set the signer's keys for the signature creation. <userID> should
+ be the internal representation of the key; the server may accept any
+ other way of specification [we will support this]. If this is a
+ valid and usable signing key the server does respond with OK,
+ otherwise it returns an ERR with the reason why the key can't be
+ used, the signing will then not be done for this key. If the policy
+ is not to sign at all if not all signer keys are valid, the client
+ has to take care of this. All SIGNER commands are cumulative until
+ a RESET but they are *not* reset by an SIGN command becuase it can
+ be expected that set of signers are used for more than one sign
+ operation.
+
+ Note that this command returns an INV_RECP status which is a bit
+ strange, but they are very similar. */
+static int
+cmd_signer (ASSUAN_CONTEXT ctx, char *line)
+{
+ CTRL ctrl = assuan_get_pointer (ctx);
+ int rc;
+
+ rc = gpgsm_add_to_certlist (ctrl, line, 1, &ctrl->server_local->signerlist);
+ if (rc)
+ {
+ gpg_err_code_t r = gpg_err_code (rc);
+ gpgsm_status2 (ctrl, STATUS_INV_RECP,
+ r == -1? "1":
+ r == GPG_ERR_NO_PUBKEY? "1":
+ r == GPG_ERR_AMBIGUOUS_NAME? "2":
+ r == GPG_ERR_WRONG_KEY_USAGE? "3":
+ r == GPG_ERR_CERT_REVOKED? "4":
+ r == GPG_ERR_CERT_EXPIRED? "5":
+ r == GPG_ERR_NO_CRL_KNOWN? "6":
+ r == GPG_ERR_CRL_TOO_OLD? "7":
+ r == GPG_ERR_NO_POLICY_MATCH? "8":
+ r == GPG_ERR_NO_SECKEY? "9":
+ "0",
+ line, NULL);
+ }
+ return map_to_assuan_status (rc);
+}
+
+
+/* ENCRYPT
+
+ Do the actual encryption process. Takes the plaintext from the INPUT
+ command, writes to the ciphertext to the file descriptor set with
+ the OUTPUT command, take the recipients form all the recipients set
+ so far. If this command fails the clients should try to delete all
+ output currently done or otherwise mark it as invalid. GPGSM does
+ ensure that there won't be any security problem with leftover data
+ on the output in this case.
+
+ This command should in general not fail, as all necessary checks
+ have been done while setting the recipients. The input and output
+ pipes are closed. */
+static int
+cmd_encrypt (ASSUAN_CONTEXT ctx, char *line)
+{
+ CTRL ctrl = assuan_get_pointer (ctx);
+ int inp_fd, out_fd;
+ FILE *out_fp;
+ int rc;
+
+ inp_fd = assuan_get_input_fd (ctx);
+ if (inp_fd == -1)
+ return set_error (No_Input, NULL);
+ out_fd = assuan_get_output_fd (ctx);
+ if (out_fd == -1)
+ return set_error (No_Output, NULL);
+
+ out_fp = fdopen ( dup(out_fd), "w");
+ if (!out_fp)
+ return set_error (General_Error, "fdopen() failed");
+ 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 map_to_assuan_status (rc);
+}
+
+/* DECRYPT
+
+ This performs the decrypt operation after doing some check on the
+ internal state. (e.g. that only needed data has been set). Because
+ it utilizes the GPG-Agent for the session key decryption, there is
+ no need to ask the client for a protecting passphrase - GpgAgent
+ does take care of this by requesting this from the user. */
+static int
+cmd_decrypt (ASSUAN_CONTEXT ctx, char *line)
+{
+ CTRL ctrl = assuan_get_pointer (ctx);
+ int inp_fd, out_fd;
+ FILE *out_fp;
+ int rc;
+
+ inp_fd = assuan_get_input_fd (ctx);
+ if (inp_fd == -1)
+ return set_error (No_Input, NULL);
+ out_fd = assuan_get_output_fd (ctx);
+ if (out_fd == -1)
+ return set_error (No_Output, NULL);
+
+ out_fp = fdopen ( dup(out_fd), "w");
+ if (!out_fp)
+ return set_error (General_Error, "fdopen() failed");
+ 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 map_to_assuan_status (rc);
+}
+
+
+/* VERIFY
+
+ This does a verify operation on the message send to the input-FD.
+ The result is written out using status lines. If an output FD was
+ given, the signed text will be written to that.
+
+ If the signature is a detached one, the server will inquire about
+ the signed material and the client must provide it.
+ */
+static int
+cmd_verify (ASSUAN_CONTEXT ctx, char *line)
+{
+ int rc;
+ CTRL ctrl = assuan_get_pointer (ctx);
+ int fd = assuan_get_input_fd (ctx);
+ int out_fd = assuan_get_output_fd (ctx);
+ FILE *out_fp = NULL;
+
+ if (fd == -1)
+ return set_error (No_Input, NULL);
+
+ if (out_fd != -1)
+ {
+ out_fp = fdopen ( dup(out_fd), "w");
+ if (!out_fp)
+ return set_error (General_Error, "fdopen() failed");
+ }
+
+ 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 map_to_assuan_status (rc);
+}
+
+
+/* SIGN [--detached]
+
+ Sign the data set with the INPUT command and write it to the sink
+ set by OUTPUT. With "--detached" specified, a detached signature is
+ created (surprise). */
+static int
+cmd_sign (ASSUAN_CONTEXT ctx, char *line)
+{
+ CTRL ctrl = assuan_get_pointer (ctx);
+ int inp_fd, out_fd;
+ FILE *out_fp;
+ int detached;
+ int rc;
+
+ inp_fd = assuan_get_input_fd (ctx);
+ if (inp_fd == -1)
+ return set_error (No_Input, NULL);
+ out_fd = assuan_get_output_fd (ctx);
+ if (out_fd == -1)
+ return set_error (No_Output, NULL);
+
+ detached = has_option (line, "--detached");
+
+ out_fp = fdopen ( dup(out_fd), "w");
+ if (!out_fp)
+ return set_error (General_Error, "fdopen() failed");
+
+ 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 map_to_assuan_status (rc);
+}
+
+
+/* IMPORT
+
+ Import the certificates read form the input-fd, return status
+ message for each imported one. The import checks the validity of
+ the certificate but not of the entire chain. It is possible to
+ import expired certificates. */
+static int
+cmd_import (ASSUAN_CONTEXT ctx, char *line)
+{
+ CTRL ctrl = assuan_get_pointer (ctx);
+ int rc;
+ int fd = assuan_get_input_fd (ctx);
+
+ if (fd == -1)
+ return set_error (No_Input, NULL);
+
+ rc = gpgsm_import (assuan_get_pointer (ctx), fd);
+
+ /* close and reset the fd */
+ close_message_fd (ctrl);
+ assuan_close_input_fd (ctx);
+ assuan_close_output_fd (ctx);
+
+ return map_to_assuan_status (rc);
+}
+
+
+static int
+cmd_export (ASSUAN_CONTEXT ctx, char *line)
+{
+ CTRL ctrl = assuan_get_pointer (ctx);
+ int fd = assuan_get_output_fd (ctx);
+ FILE *out_fp;
+ char *p;
+ STRLIST list, sl;
+
+ if (fd == -1)
+ return set_error (No_Output, NULL);
+
+ /* 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 ASSUAN_Out_Of_Core;
+ }
+ sl->flags = 0;
+ strcpy_escaped_plus (sl->d, line);
+ sl->next = list;
+ list = sl;
+ }
+ }
+
+ out_fp = fdopen ( dup(fd), "w");
+ if (!out_fp)
+ {
+ free_strlist (list);
+ return set_error (General_Error, "fdopen() failed");
+ }
+
+ gpgsm_export (ctrl, list, out_fp);
+ fclose (out_fp);
+ free_strlist (list);
+ /* close and reset the fd */
+ close_message_fd (ctrl);
+ assuan_close_input_fd (ctx);
+ assuan_close_output_fd (ctx);
+ return 0;
+}
+
+
+static int
+cmd_delkeys (ASSUAN_CONTEXT ctx, char *line)
+{
+ CTRL ctrl = assuan_get_pointer (ctx);
+ char *p;
+ STRLIST list, sl;
+ int rc;
+
+ /* 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 ASSUAN_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 map_to_assuan_status (rc);
+}
+
+
+
+/* MESSAGE FD=<n>
+
+ Set the file descriptor to read a message which is used with
+ detached signatures */
+static int
+cmd_message (ASSUAN_CONTEXT ctx, char *line)
+{
+ char *endp;
+ int fd;
+ CTRL ctrl = assuan_get_pointer (ctx);
+
+ if (strncmp (line, "FD=", 3))
+ return set_error (Syntax_Error, "FD=<n> expected");
+ line += 3;
+ if (!digitp (line))
+ return set_error (Syntax_Error, "number required");
+ fd = strtoul (line, &endp, 10);
+ if (*endp)
+ return set_error (Syntax_Error, "garbage found");
+ if (fd == -1)
+ return set_error (No_Input, NULL);
+
+ ctrl->server_local->message_fd = fd;
+ return 0;
+}
+
+
+static int
+do_listkeys (ASSUAN_CONTEXT ctx, char *line, int mode)
+{
+ CTRL ctrl = assuan_get_pointer (ctx);
+ FILE *fp = assuan_get_data_fp (ctx);
+ char *p;
+ STRLIST list, sl;
+ unsigned int listmode;
+
+ if (!fp)
+ return set_error (General_Error, "no data stream");
+
+ /* 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 ASSUAN_Out_Of_Core;
+ }
+ sl->flags = 0;
+ strcpy_escaped_plus (sl->d, line);
+ sl->next = list;
+ list = sl;
+ }
+ }
+
+ ctrl->with_colons = 1;
+ listmode = mode;
+ if (ctrl->server_local->list_internal)
+ listmode |= (1<<6);
+ if (ctrl->server_local->list_external)
+ listmode |= (1<<7);
+ gpgsm_list_keys (assuan_get_pointer (ctx), list, fp, listmode);
+ free_strlist (list);
+ return 0;
+}
+
+static int
+cmd_listkeys (ASSUAN_CONTEXT ctx, char *line)
+{
+ return do_listkeys (ctx, line, 3);
+}
+
+static int
+cmd_listsecretkeys (ASSUAN_CONTEXT ctx, char *line)
+{
+ return do_listkeys (ctx, line, 2);
+}
+
+
+/* GENKEY
+
+ Read the parameters in native format from the input fd and write a
+ certificate request to the output.
+ */
+static int
+cmd_genkey (ASSUAN_CONTEXT ctx, char *line)
+{
+ CTRL ctrl = assuan_get_pointer (ctx);
+ int inp_fd, out_fd;
+ FILE *out_fp;
+ int rc;
+
+ inp_fd = assuan_get_input_fd (ctx);
+ if (inp_fd == -1)
+ return set_error (No_Input, NULL);
+ out_fd = assuan_get_output_fd (ctx);
+ if (out_fd == -1)
+ return set_error (No_Output, NULL);
+
+ out_fp = fdopen ( dup(out_fd), "w");
+ if (!out_fp)
+ return set_error (General_Error, "fdopen() failed");
+ rc = gpgsm_genkey (ctrl, inp_fd, out_fp);
+ fclose (out_fp);
+
+ /* close and reset the fds */
+ assuan_close_input_fd (ctx);
+ assuan_close_output_fd (ctx);
+
+ return map_to_assuan_status (rc);
+}
+
+
+
+
+
+/* Tell the assuan library about our commands */
+static int
+register_commands (ASSUAN_CONTEXT ctx)
+{
+ static struct {
+ const char *name;
+ int (*handler)(ASSUAN_CONTEXT, char *line);
+ } table[] = {
+ { "RECIPIENT", cmd_recipient },
+ { "SIGNER", cmd_signer },
+ { "ENCRYPT", cmd_encrypt },
+ { "DECRYPT", cmd_decrypt },
+ { "VERIFY", cmd_verify },
+ { "SIGN", cmd_sign },
+ { "IMPORT", cmd_import },
+ { "EXPORT", cmd_export },
+ { "INPUT", NULL },
+ { "OUTPUT", NULL },
+ { "MESSAGE", cmd_message },
+ { "LISTKEYS", cmd_listkeys },
+ { "LISTSECRETKEYS",cmd_listsecretkeys },
+ { "GENKEY", cmd_genkey },
+ { "DELKEYS", cmd_delkeys },
+ { NULL }
+ };
+ int i, rc;
+
+ for (i=0; table[i].name; i++)
+ {
+ rc = assuan_register_command (ctx, table[i].name, table[i].handler);
+ if (rc)
+ return rc;
+ }
+ return 0;
+}
+
+/* Startup the server */
+void
+gpgsm_server (void)
+{
+ int rc;
+ int filedes[2];
+ ASSUAN_CONTEXT ctx;
+ struct server_control_s ctrl;
+
+ memset (&ctrl, 0, sizeof ctrl);
+ gpgsm_init_default_ctrl (&ctrl);
+
+ /* For now we use a simple pipe based server so that we can work
+ from scripts. We will later add options to run as a daemon and
+ wait for requests on a Unix domain socket */
+ filedes[0] = 0;
+ filedes[1] = 1;
+ rc = assuan_init_pipe_server (&ctx, filedes);
+ if (rc)
+ {
+ log_error ("failed to initialize the server: %s\n",
+ assuan_strerror(rc));
+ gpgsm_exit (2);
+ }
+ rc = register_commands (ctx);
+ if (rc)
+ {
+ log_error ("failed to the register commands with Assuan: %s\n",
+ assuan_strerror(rc));
+ gpgsm_exit (2);
+ }
+ assuan_set_hello_line (ctx, "GNU Privacy Guard's S/M server ready");
+
+ 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;
+
+ 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", assuan_strerror (rc));
+ break;
+ }
+
+ rc = assuan_process (ctx);
+ if (rc)
+ {
+ log_info ("Assuan processing failed: %s\n", assuan_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;
+
+ assuan_deinit_server (ctx);
+}
+
+
+static const char *
+get_status_string ( int no )
+{
+ const char *s;
+
+ switch (no)
+ {
+ case STATUS_ENTER : s = "ENTER"; break;
+ case STATUS_LEAVE : s = "LEAVE"; break;
+ case STATUS_ABORT : s = "ABORT"; break;
+ case STATUS_GOODSIG: s = "GOODSIG"; break;
+ case STATUS_SIGEXPIRED: s = "SIGEXPIRED"; break;
+ case STATUS_KEYREVOKED: s = "KEYREVOKED"; break;
+ case STATUS_BADSIG : s = "BADSIG"; break;
+ case STATUS_ERRSIG : s = "ERRSIG"; break;
+ case STATUS_BADARMOR : s = "BADARMOR"; break;
+ case STATUS_RSA_OR_IDEA : s= "RSA_OR_IDEA"; break;
+ case STATUS_TRUST_UNDEFINED: s = "TRUST_UNDEFINED"; break;
+ case STATUS_TRUST_NEVER : s = "TRUST_NEVER"; break;
+ case STATUS_TRUST_MARGINAL : s = "TRUST_MARGINAL"; break;
+ case STATUS_TRUST_FULLY : s = "TRUST_FULLY"; break;
+ case STATUS_TRUST_ULTIMATE : s = "TRUST_ULTIMATE"; break;
+ case STATUS_GET_BOOL : s = "GET_BOOL"; break;
+ case STATUS_GET_LINE : s = "GET_LINE"; break;
+ case STATUS_GET_HIDDEN : s = "GET_HIDDEN"; break;
+ case STATUS_GOT_IT : s = "GOT_IT"; break;
+ case STATUS_SHM_INFO : s = "SHM_INFO"; break;
+ case STATUS_SHM_GET : s = "SHM_GET"; break;
+ case STATUS_SHM_GET_BOOL : s = "SHM_GET_BOOL"; break;
+ case STATUS_SHM_GET_HIDDEN : s = "SHM_GET_HIDDEN"; break;
+ case STATUS_NEED_PASSPHRASE: s = "NEED_PASSPHRASE"; break;
+ case STATUS_VALIDSIG : s = "VALIDSIG"; break;
+ case STATUS_SIG_ID : s = "SIG_ID"; break;
+ case STATUS_ENC_TO : s = "ENC_TO"; break;
+ case STATUS_NODATA : s = "NODATA"; break;
+ case STATUS_BAD_PASSPHRASE : s = "BAD_PASSPHRASE"; break;
+ case STATUS_NO_PUBKEY : s = "NO_PUBKEY"; break;
+ case STATUS_NO_SECKEY : s = "NO_SECKEY"; break;
+ case STATUS_NEED_PASSPHRASE_SYM: s = "NEED_PASSPHRASE_SYM"; break;
+ case STATUS_DECRYPTION_FAILED: s = "DECRYPTION_FAILED"; break;
+ case STATUS_DECRYPTION_OKAY: s = "DECRYPTION_OKAY"; break;
+ case STATUS_MISSING_PASSPHRASE: s = "MISSING_PASSPHRASE"; break;
+ case STATUS_GOOD_PASSPHRASE : s = "GOOD_PASSPHRASE"; break;
+ case STATUS_GOODMDC : s = "GOODMDC"; break;
+ case STATUS_BADMDC : s = "BADMDC"; break;
+ case STATUS_ERRMDC : s = "ERRMDC"; break;
+ case STATUS_IMPORTED : s = "IMPORTED"; break;
+ case STATUS_IMPORT_RES : s = "IMPORT_RES"; break;
+ case STATUS_FILE_START : s = "FILE_START"; break;
+ case STATUS_FILE_DONE : s = "FILE_DONE"; break;
+ case STATUS_FILE_ERROR : s = "FILE_ERROR"; break;
+ case STATUS_BEGIN_DECRYPTION:s = "BEGIN_DECRYPTION"; break;
+ case STATUS_END_DECRYPTION : s = "END_DECRYPTION"; break;
+ case STATUS_BEGIN_ENCRYPTION:s = "BEGIN_ENCRYPTION"; break;
+ case STATUS_END_ENCRYPTION : s = "END_ENCRYPTION"; break;
+ case STATUS_DELETE_PROBLEM : s = "DELETE_PROBLEM"; break;
+ case STATUS_PROGRESS : s = "PROGRESS"; break;
+ case STATUS_SIG_CREATED : s = "SIG_CREATED"; break;
+ case STATUS_SESSION_KEY : s = "SESSION_KEY"; break;
+ case STATUS_NOTATION_NAME : s = "NOTATION_NAME" ; break;
+ case STATUS_NOTATION_DATA : s = "NOTATION_DATA" ; break;
+ case STATUS_POLICY_URL : s = "POLICY_URL" ; break;
+ case STATUS_BEGIN_STREAM : s = "BEGIN_STREAM"; break;
+ case STATUS_END_STREAM : s = "END_STREAM"; break;
+ case STATUS_KEY_CREATED : s = "KEY_CREATED"; break;
+ case STATUS_UNEXPECTED : s = "UNEXPECTED"; break;
+ case STATUS_INV_RECP : s = "INV_RECP"; break;
+ case STATUS_NO_RECP : s = "NO_RECP"; break;
+ case STATUS_ALREADY_SIGNED : s = "ALREADY_SIGNED"; break;
+ case STATUS_EXPSIG : s = "EXPSIG"; break;
+ case STATUS_EXPKEYSIG : s = "EXPKEYSIG"; break;
+ case STATUS_TRUNCATED : s = "TRUNCATED"; break;
+ case STATUS_ERROR : s = "ERROR"; break;
+ case STATUS_IMPORT_PROBLEM : s = "IMPORT_PROBLEM"; break;
+ default: s = "?"; break;
+ }
+ return s;
+}
+
+
+void
+gpgsm_status2 (CTRL ctrl, int no, ...)
+{
+ va_list arg_ptr;
+ const char *text;
+
+ va_start (arg_ptr, no);
+
+ if (ctrl->no_server)
+ {
+ if (ctrl->status_fd == -1)
+ return; /* no status wanted */
+ 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 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;
+ assuan_write_status (ctx, get_status_string (no), buf);
+ }
+
+ va_end (arg_ptr);
+}
+
+void
+gpgsm_status (CTRL ctrl, int no, const char *text)
+{
+ gpgsm_status2 (ctrl, no, text, NULL);
+}
+
+void
+gpgsm_status_with_err_code (CTRL ctrl, int no, const char *text,
+ gpg_err_code_t ec)
+{
+ char buf[30];
+
+ sprintf (buf, "%u", (unsigned int)ec);
+ if (text)
+ gpgsm_status2 (ctrl, no, text, buf, NULL);
+ else
+ gpgsm_status2 (ctrl, no, buf, NULL);
+}
+
+#if 0
+/*
+ * Write a status line with a buffer using %XX escapes. If WRAP is >
+ * 0 wrap the line after this length. If STRING is not NULL it will
+ * be prepended to the buffer, no escaping is done for string.
+ * A wrap of -1 forces spaces not to be encoded as %20.
+ */
+void
+write_status_text_and_buffer ( int no, const char *string,
+ const char *buffer, size_t len, int wrap )
+{
+ const char *s, *text;
+ int esc, first;
+ int lower_limit = ' ';
+ size_t n, count, dowrap;
+
+ if( !statusfp )
+ return; /* not enabled */
+
+ if (wrap == -1) {
+ lower_limit--;
+ wrap = 0;
+ }
+
+ text = get_status_string (no);
+ count = dowrap = first = 1;
+ do {
+ if (dowrap) {
+ fprintf (statusfp, "[GNUPG:] %s ", text );
+ count = dowrap = 0;
+ if (first && string) {
+ fputs (string, statusfp);
+ count += strlen (string);
+ }
+ first = 0;
+ }
+ for (esc=0, s=buffer, n=len; n && !esc; s++, n-- ) {
+ if ( *s == '%' || *(const byte*)s <= lower_limit
+ || *(const byte*)s == 127 )
+ esc = 1;
+ if ( wrap && ++count > wrap ) {
+ dowrap=1;
+ break;
+ }
+ }
+ if (esc) {
+ s--; n++;
+ }
+ if (s != buffer)
+ fwrite (buffer, s-buffer, 1, statusfp );
+ if ( esc ) {
+ fprintf (statusfp, "%%%02X", *(const byte*)s );
+ s++; n--;
+ }
+ buffer = s;
+ len = n;
+ if ( dowrap && len )
+ putc ( '\n', statusfp );
+ } while ( len );
+
+ putc ('\n',statusfp);
+ fflush (statusfp);
+}
+#endif
diff --git a/sm/sign.c b/sm/sign.c
new file mode 100644
index 000000000..0afb52b62
--- /dev/null
+++ b/sm/sign.c
@@ -0,0 +1,621 @@
+/* sign.c - Sign a message
+ * 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 2 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuPG is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ */
+
+#include <config.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <time.h>
+#include <assert.h>
+
+#include "gpgsm.h"
+#include <gcrypt.h>
+#include <ksba.h>
+
+#include "keydb.h"
+#include "i18n.h"
+
+
+static void
+hash_data (int fd, gcry_md_hd_t md)
+{
+ FILE *fp;
+ char buffer[4096];
+ int nread;
+
+ fp = fdopen ( dup (fd), "rb");
+ if (!fp)
+ {
+ log_error ("fdopen(%d) failed: %s\n", fd, strerror (errno));
+ return;
+ }
+
+ 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));
+ fclose (fp);
+}
+
+static int
+hash_and_copy_data (int fd, gcry_md_hd_t md, KsbaWriter writer)
+{
+ KsbaError 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", ksba_strerror (err));
+ rc = map_ksba_err (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", ksba_strerror (err));
+ rc = map_ksba_err (err);
+ }
+ }
+
+ return rc;
+}
+
+
+/* Get the default certificate which is defined as the first one our
+ keyDB retruns and has a secret key available */
+int
+gpgsm_get_default_cert (KsbaCert *r_cert)
+{
+ KEYDB_HANDLE hd;
+ KsbaCert 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;
+ }
+
+ p = gpgsm_get_keygrip_hexstring (cert);
+ if (p)
+ {
+ if (!gpgsm_agent_havekey (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 KsbaCert
+get_default_signer (void)
+{
+ KEYDB_SEARCH_DESC desc;
+ KsbaCert cert = NULL;
+ KEYDB_HANDLE kh = NULL;
+ int rc;
+
+ if (!opt.local_user)
+ {
+ rc = gpgsm_get_default_cert (&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 ctrl, KsbaCMS cms, KsbaCert cert)
+{
+ KsbaError err;
+ int rc = 0;
+ KsbaCert next = NULL;
+ int n;
+ int not_root = 0;
+
+ ksba_cert_ref (cert);
+
+ n = ctrl->include_certs;
+ if (n == -2)
+ {
+ not_root = 1;
+ n = -1;
+ }
+ if (n < 0 || n > 50)
+ n = 50; /* We better apply an upper bound */
+
+ 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;
+ }
+ while ( n-- && !(rc = gpgsm_walk_cert_chain (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", ksba_strerror (err));
+ return map_ksba_err (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 ctrl, CERTLIST signerlist,
+ int data_fd, int detached, FILE *out_fp)
+{
+ int i, rc;
+ KsbaError err;
+ Base64Context b64writer = NULL;
+ KsbaWriter writer;
+ KsbaCMS cms = NULL;
+ KsbaStopReason stopreason;
+ KEYDB_HANDLE kh = NULL;
+ gcry_md_hd_t data_md = NULL;
+ int signer;
+ const char *algoid;
+ int algo;
+ time_t signed_at;
+ CERTLIST cl;
+ int release_signerlist = 0;
+
+ 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, &writer);
+ if (rc)
+ {
+ log_error ("can't create writer: %s\n", gpg_strerror (rc));
+ goto leave;
+ }
+
+ cms = ksba_cms_new ();
+ if (!cms)
+ {
+ rc = gpg_error (GPG_ERR_ENOMEM);
+ goto leave;
+ }
+
+ err = ksba_cms_set_reader_writer (cms, NULL, writer);
+ if (err)
+ {
+ log_debug ("ksba_cms_set_reader_writer failed: %s\n",
+ ksba_strerror (err));
+ rc = map_ksba_err (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",
+ ksba_strerror (err));
+ rc = map_ksba_err (err);
+ goto leave;
+ }
+
+ /* If no list of signers is given, use a default one. */
+ if (!signerlist)
+ {
+ KsbaCert cert = get_default_signer ();
+ if (!cert)
+ {
+ log_error ("no default signer found\n");
+ rc = gpg_error (GPG_ERR_GENERAL);
+ goto leave;
+ }
+ signerlist = xtrycalloc (1, sizeof *signerlist);
+ if (!signerlist)
+ {
+ rc = OUT_OF_CORE (errno);
+ ksba_cert_release (cert);
+ goto leave;
+ }
+ signerlist->cert = cert;
+ release_signerlist = 1;
+ }
+
+
+ /* 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", ksba_strerror (err));
+ rc = map_ksba_err (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, "1.3.14.3.2.26" /*SHA-1*/);
+ if (err)
+ {
+ log_debug ("ksba_cms_add_digest_algo failed: %s\n",
+ ksba_strerror (err));
+ rc = map_ksba_err (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);
+ }
+
+ 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;
+ /* Fixme do this for all signers and get the algo to use from
+ the signer's certificate - does not make mich sense, bu we
+ should do this consistent as we have already done it above */
+ algo = GCRY_MD_SHA1;
+ hash_data (data_fd, data_md);
+ digest = gcry_md_read (data_md, algo);
+ digest_len = gcry_md_get_algo_dlen (algo);
+ if ( !digest || !digest_len)
+ {
+ log_error ("problem getting the hash of the data\n");
+ rc = gpg_error (GPG_ERR_BUG);
+ goto leave;
+ }
+ for (cl=signerlist,signer=0; cl; cl = cl->next, signer++)
+ {
+ err = ksba_cms_set_message_digest (cms, signer, digest, digest_len);
+ if (err)
+ {
+ log_error ("ksba_cms_set_message_digest failed: %s\n",
+ ksba_strerror (err));
+ rc = map_ksba_err (err);
+ goto leave;
+ }
+ }
+ }
+
+ signed_at = gnupg_get_time ();
+ 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",
+ ksba_strerror (err));
+ rc = map_ksba_err (err);
+ goto leave;
+ }
+ }
+
+ do
+ {
+ err = ksba_cms_build (cms, &stopreason);
+ if (err)
+ {
+ log_debug ("ksba_cms_build failed: %s\n", ksba_strerror (err));
+ rc = map_ksba_err (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);
+ /* Fixme: get the algo to use from the signer's certificate
+ - does not make much sense, but we should do this
+ consistent as we have already done it above. Code is
+ mostly duplicated above. */
+
+ algo = GCRY_MD_SHA1;
+ rc = hash_and_copy_data (data_fd, data_md, writer);
+ if (rc)
+ goto leave;
+ digest = gcry_md_read (data_md, algo);
+ digest_len = gcry_md_get_algo_dlen (algo);
+ if ( !digest || !digest_len)
+ {
+ log_error ("problem getting the hash of the data\n");
+ rc = gpg_error (GPG_ERR_BUG);
+ goto leave;
+ }
+ for (cl=signerlist,signer=0; cl; cl = cl->next, signer++)
+ {
+ err = ksba_cms_set_message_digest (cms, signer,
+ digest, digest_len);
+ if (err)
+ {
+ log_error ("ksba_cms_set_message_digest failed: %s\n",
+ ksba_strerror (err));
+ rc = map_ksba_err (err);
+ goto leave;
+ }
+ }
+ }
+ else if (stopreason == KSBA_SR_NEED_SIG)
+ { /* calculate the signature for all signers */
+ gcry_md_hd_t md;
+
+ algo = GCRY_MD_SHA1;
+ rc = gcry_md_open (&md, algo, 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++)
+ {
+ char *sigval = NULL;
+ char *buf, *fpr;
+
+ if (signer)
+ gcry_md_reset (md);
+ rc = ksba_cms_hash_signed_attrs (cms, signer);
+ if (rc)
+ {
+ log_debug ("hashing signed attrs failed: %s\n",
+ ksba_strerror (rc));
+ gcry_md_close (md);
+ goto leave;
+ }
+
+ rc = gpgsm_create_cms_signature (cl->cert, md, algo, &sigval);
+ if (rc)
+ {
+ gcry_md_close (md);
+ goto leave;
+ }
+
+ err = ksba_cms_set_sig_val (cms, signer, sigval);
+ xfree (sigval);
+ if (err)
+ {
+ log_error ("failed to store the signature: %s\n",
+ ksba_strerror (err));
+ rc = map_ksba_err (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 = asprintf (&buf, "%c %d %d 00 %lu %s",
+ detached? 'D':'S',
+ GCRY_PK_RSA, /* FIXME: get pk algo from cert */
+ algo,
+ (ulong)signed_at,
+ fpr);
+ xfree (fpr);
+ if (rc < 0)
+ {
+ rc = gpg_error (GPG_ERR_ENOMEM);
+ gcry_md_close (md);
+ goto leave;
+ }
+ rc = 0;
+ gpgsm_status (ctrl, STATUS_SIG_CREATED, buf);
+ free (buf); /* yes, we must use the regular free() here */
+ }
+ 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;
+ }
+
+ log_info ("signature created\n");
+
+
+ leave:
+ 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 000000000..6dd4f4e5b
--- /dev/null
+++ b/sm/verify.c
@@ -0,0 +1,550 @@
+/* verify.c - Verify a messages signature
+ * 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 2 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuPG is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ */
+
+#include <config.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <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 (time_t atime)
+{
+ char *buffer = xmalloc (15);
+
+ if (atime < 0)
+ strcpy (buffer, "????" "-??" "-??");
+ else if (!atime)
+ strcpy (buffer, "none");
+ else
+ {
+ struct tm *tp;
+
+ tp = gmtime( &atime );
+ sprintf (buffer, "%04d-%02d-%02d",
+ 1900+tp->tm_year, tp->tm_mon+1, tp->tm_mday);
+ }
+ return buffer;
+}
+
+
+
+/* Hash the data for a detached signature */
+static void
+hash_data (int fd, gcry_md_hd_t md)
+{
+ FILE *fp;
+ char buffer[4096];
+ int nread;
+
+ fp = fdopen ( dup (fd), "rb");
+ if (!fp)
+ {
+ log_error ("fdopen(%d) failed: %s\n", fd, strerror (errno));
+ return;
+ }
+
+ 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));
+ fclose (fp);
+}
+
+
+
+
+/* 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 ctrl, int in_fd, int data_fd, FILE *out_fp)
+{
+ int i, rc;
+ Base64Context b64reader = NULL;
+ Base64Context b64writer = NULL;
+ KsbaError err;
+ KsbaReader reader;
+ KsbaWriter writer = NULL;
+ KsbaCMS cms = NULL;
+ KsbaStopReason stopreason;
+ KsbaCert 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;
+
+ 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, &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, &writer);
+ if (rc)
+ {
+ log_error ("can't create writer: %s\n", gpg_strerror (rc));
+ goto leave;
+ }
+ }
+
+ cms = ksba_cms_new ();
+ if (!cms)
+ {
+ rc = gpg_error (GPG_ERR_ENOMEM);
+ goto leave;
+ }
+
+ err = ksba_cms_set_reader_writer (cms, reader, writer);
+ if (err)
+ {
+ log_error ("ksba_cms_set_reader_writer failed: %s\n",
+ ksba_strerror (err));
+ rc = map_ksba_err (err);
+ 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");
+
+ is_detached = 0;
+ do
+ {
+ err = ksba_cms_parse (cms, &stopreason);
+ if (err)
+ {
+ log_error ("ksba_cms_parse failed: %s\n", ksba_strerror (err));
+ rc = map_ksba_err (err);
+ goto leave;
+ }
+
+ if (stopreason == KSBA_SR_NEED_HASH)
+ {
+ is_detached = 1;
+ if (opt.verbose)
+ log_info ("detached signature\n");
+ }
+
+ if (stopreason == KSBA_SR_NEED_HASH
+ || stopreason == KSBA_SR_BEGIN_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:"?");
+ else
+ gcry_md_enable (data_md, algo);
+ }
+ if (is_detached)
+ {
+ if (data_fd == -1)
+ log_info ("detached signature w/o data "
+ "- assuming certs-only\n");
+ else
+ 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 */
+
+ }
+ }
+ while (stopreason != KSBA_SR_READY);
+
+ if (b64writer)
+ {
+ rc = gpgsm_finish_writer (b64writer);
+ if (rc)
+ {
+ log_error ("write failed: %s\n", gpg_strerror (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);
+ 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. */
+ keydb_store_cert (cert, 0, NULL);
+ ksba_cert_release (cert);
+ }
+
+ cert = NULL;
+ err = 0;
+ for (signer=0; ; signer++)
+ {
+ char *issuer = NULL;
+ KsbaSexp sigval = NULL;
+ time_t sigtime, keyexptime;
+ KsbaSexp serial;
+ char *msgdigest = NULL;
+ size_t msgdigestlen;
+ char *ctattr;
+
+ err = ksba_cms_get_issuer_serial (cms, signer, &issuer, &serial);
+ if (!signer && err == KSBA_No_Data && data_fd == -1 && is_detached)
+ {
+ log_info ("certs-only message accepted\n");
+ err = 0;
+ break;
+ }
+ if (err)
+ {
+ if (signer && err == -1)
+ err = 0;
+ break;
+ }
+ 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");
+ }
+
+ err = ksba_cms_get_signing_time (cms, signer, &sigtime);
+ if (err == KSBA_No_Data)
+ sigtime = 0;
+ else if (err)
+ {
+ log_error ("error getting signing time: %s\n", ksba_strerror (err));
+ sigtime = (time_t)-1;
+ }
+
+ err = ksba_cms_get_message_digest (cms, signer,
+ &msgdigest, &msgdigestlen);
+ if (!err)
+ {
+ 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);
+ if ( !gcry_md_info (data_md, GCRYCTL_IS_ALGO_ENABLED, &algo, NULL) )
+ {
+ log_error ("digest algo %d has not been enabled\n", algo);
+ goto next_signer;
+ }
+ }
+ else if (err == KSBA_No_Data)
+ {
+ assert (!msgdigest);
+ err = 0;
+ algoid = NULL;
+ algo = 0;
+ }
+ else /* real error */
+ break;
+
+ err = ksba_cms_get_sigattr_oids (cms, signer,
+ "1.2.840.113549.1.9.3",&ctattr);
+ if (!err)
+ {
+ 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;
+ goto next_signer;
+ }
+ ksba_free (ctattr);
+ ctattr = NULL;
+ }
+ else if (err != -1)
+ {
+ log_error ("error getting content-type attribute: %s\n",
+ ksba_strerror (err));
+ goto next_signer;
+ }
+ err = 0;
+
+
+ sigval = ksba_cms_get_sig_val (cms, signer);
+ if (!sigval)
+ {
+ log_error ("no signature value available\n");
+ goto next_signer;
+ }
+ if (DBG_X509)
+ log_debug ("signer %d - signature available", signer);
+
+ /* 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);
+ }
+ /* fixme: we might want to append the issuer and serial
+ using our standard notation */
+ goto next_signer;
+ }
+
+ rc = keydb_get_cert (kh, &cert);
+ if (rc)
+ {
+ log_error ("failed to get cert: %s\n", gpg_strerror (rc));
+ goto next_signer;
+ }
+
+ log_info (_("Signature made "));
+ if (sigtime)
+ gpgsm_dump_time (sigtime);
+ else
+ log_printf (_("[date not given]"));
+ log_printf (_(" using certificate ID %08lX\n"),
+ gpgsm_get_short_fingerprint (cert));
+
+
+ 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 calculated one\n");
+ fpr = gpgsm_get_fingerprint_hexstring (cert, GCRY_MD_SHA1);
+ gpgsm_status (ctrl, STATUS_BADSIG, fpr);
+ xfree (fpr);
+ goto next_signer;
+ }
+
+ rc = gcry_md_open (&md, algo, 0);
+ if (rc)
+ {
+ log_error ("md_open failed: %s\n", gpg_strerror (rc));
+ 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",
+ ksba_strerror (rc));
+ gcry_md_close (md);
+ goto next_signer;
+ }
+ rc = gpgsm_check_cms_signature (cert, sigval, md, algo);
+ gcry_md_close (md);
+ }
+ else
+ {
+ rc = gpgsm_check_cms_signature (cert, sigval, data_md, algo);
+ }
+
+ if (rc)
+ {
+ char *fpr;
+
+ log_error ("invalid signature: %s\n", gpg_strerror (rc));
+ fpr = gpgsm_get_fingerprint_hexstring (cert, GCRY_MD_SHA1);
+ gpgsm_status (ctrl, STATUS_BADSIG, fpr);
+ xfree (fpr);
+ 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");
+ rc = gpgsm_validate_chain (ctrl, cert, &keyexptime);
+ if (gpg_err_code (rc) == GPG_ERR_CERT_EXPIRED)
+ {
+ gpgsm_status (ctrl, STATUS_EXPKEYSIG, NULL);
+ rc = 0;
+ }
+ else
+ gpgsm_status (ctrl, STATUS_GOODSIG, NULL);
+
+ {
+ char *buf, *fpr, *tstr;
+
+ fpr = gpgsm_get_fingerprint_hexstring (cert, GCRY_MD_SHA1);
+ tstr = strtimestamp_r (sigtime);
+ buf = xmalloc ( strlen(fpr) + strlen (tstr) + 120);
+ sprintf (buf, "%s %s %lu %lu", fpr, tstr,
+ (unsigned long)sigtime, (unsigned long)keyexptime );
+ xfree (tstr);
+ xfree (fpr);
+ gpgsm_status (ctrl, STATUS_VALIDSIG, buf);
+ xfree (buf);
+ }
+
+ 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));
+ goto next_signer;
+ }
+
+ 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);
+ }
+
+ gpgsm_status (ctrl, STATUS_TRUST_FULLY, NULL);
+
+
+ next_signer:
+ rc = 0;
+ xfree (issuer);
+ xfree (serial);
+ xfree (sigval);
+ xfree (msgdigest);
+ ksba_cert_release (cert);
+ cert = NULL;
+ }
+ rc = 0;
+ if (err)
+ {
+ log_error ("ksba error: %s\n", ksba_strerror (err));
+ rc = map_ksba_err (rc);
+ }
+
+
+
+ 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;
+}
+