mirror of
git://git.gnupg.org/gnupg.git
synced 2025-07-02 22:46:30 +02:00
This commit was manufactured by cvs2svn to create branch
'GNUPG-1-9-BRANCH'.
This commit is contained in:
parent
eba8c18657
commit
9ca4830a5b
76 changed files with 34899 additions and 0 deletions
816
sm/ChangeLog
Normal file
816
sm/ChangeLog
Normal file
|
@ -0,0 +1,816 @@
|
|||
2003-07-31 Werner Koch <wk@gnupg.org>
|
||||
|
||||
* Makefile.am (gpgsm_LDADD): Added INTLLIBS.
|
||||
|
||||
2003-07-29 Werner Koch <wk@gnupg.org>
|
||||
|
||||
* gpgsm.c (main): Add secmem features and set the random seed file.
|
||||
(gpgsm_exit): Update the random seed file and enable debug output.
|
||||
|
||||
2003-07-27 Werner Koch <wk@gnupg.org>
|
||||
|
||||
Adjusted for gcry_mpi_print and gcry_mpi_scan API change.
|
||||
|
||||
2003-06-24 Werner Koch <wk@gnupg.org>
|
||||
|
||||
* server.c (gpgsm_status_with_err_code): New.
|
||||
* verify.c (gpgsm_verify): Use it here instead of the old
|
||||
tokenizing version.
|
||||
|
||||
* verify.c (strtimestamp): Renamed to strtimestamp_r
|
||||
|
||||
Adjusted for changes in the libgcrypt API. Some more fixes for the
|
||||
libgpg-error stuff.
|
||||
|
||||
2003-06-04 Werner Koch <wk@gnupg.org>
|
||||
|
||||
* call-agent.c (init_membuf,put_membuf,get_membuf): Removed.
|
||||
Include new membuf header and changed used type.
|
||||
|
||||
Renamed error codes from INVALID to INV and removed _ERROR suffixes.
|
||||
|
||||
2003-06-03 Werner Koch <wk@gnupg.org>
|
||||
|
||||
Changed all error codes in all files to the new libgpg-error scheme.
|
||||
|
||||
* gpgsm.h: Include gpg-error.h .
|
||||
* Makefile.am: Link with libgpg-error.
|
||||
|
||||
2003-04-29 Werner Koch <wk@gnupg.org>
|
||||
|
||||
* Makefile.am: Use libassuan. Don't override LDFLAGS anymore.
|
||||
* server.c (register_commands): Adjust for new Assuan semantics.
|
||||
|
||||
2002-12-03 Werner Koch <wk@gnupg.org>
|
||||
|
||||
* call-agent.c (gpgsm_agent_passwd): New.
|
||||
* gpgsm.c (main): New command --passwd and --call-protect-tool
|
||||
(run_protect_tool): New.
|
||||
|
||||
2002-11-25 Werner Koch <wk@gnupg.org>
|
||||
|
||||
* verify.c (gpgsm_verify): Handle content-type attribute.
|
||||
|
||||
2002-11-13 Werner Koch <wk@gnupg.org>
|
||||
|
||||
* call-agent.c (start_agent): Try to use $GPG_TTY instead of
|
||||
ttyname. Changed ttyname to test stdin becuase it can be assumed
|
||||
that output redirection is more common that input redirection.
|
||||
|
||||
2002-11-12 Werner Koch <wk@gnupg.org>
|
||||
|
||||
* gpgsm.c: New command --call-dirmngr.
|
||||
* call-dirmngr.c (gpgsm_dirmngr_run_command)
|
||||
(run_command_inq_cb,run_command_cb)
|
||||
(run_command_status_cb): New.
|
||||
|
||||
2002-11-11 Werner Koch <wk@gnupg.org>
|
||||
|
||||
* certcheck.c (gpgsm_check_cms_signature): Don't double free
|
||||
s_sig but free s_pkey at leave.
|
||||
|
||||
2002-11-10 Werner Koch <wk@gnupg.org>
|
||||
|
||||
* gpgsm.c: Removed duplicate --list-secret-key entry.
|
||||
|
||||
2002-09-19 Werner Koch <wk@gnupg.org>
|
||||
|
||||
* certcheck.c (gpgsm_check_cert_sig): Add cert hash debugging.
|
||||
|
||||
* certchain.c (find_up): Print info when the cert was not found
|
||||
by the autorithyKeyIdentifier.
|
||||
|
||||
2002-09-03 Werner Koch <wk@gnupg.org>
|
||||
|
||||
* gpgsm.c (main): Disable the internal libgcrypt locking.
|
||||
|
||||
2002-08-21 Werner Koch <wk@gnupg.org>
|
||||
|
||||
* import.c (print_imported_summary): Cleaned up. Print new
|
||||
not_imported value.
|
||||
(check_and_store): Update non_imported counter.
|
||||
(print_import_problem): New.
|
||||
(check_and_store): Print error status message.
|
||||
* server.c (get_status_string): Added STATUS_IMPORT_PROBLEM.
|
||||
|
||||
2002-08-20 Werner Koch <wk@gnupg.org>
|
||||
|
||||
* gpgsm.c (main): Use the log file only in server mode.
|
||||
|
||||
* import.c (print_imported_summary): New.
|
||||
(check_and_store): Update the counters, take new argument.
|
||||
(import_one): Factored out core of gpgsm_import.
|
||||
(gpgsm_import): Print counters.
|
||||
(gpgsm_import_files): New.
|
||||
* gpgsm.c (main): Use the new function for import.
|
||||
|
||||
2002-08-19 Werner Koch <wk@gnupg.org>
|
||||
|
||||
* decrypt.c (gpgsm_decrypt): Return a better error status token.
|
||||
* verify.c (gpgsm_verify): Don't error on messages with no signing
|
||||
time or no message digest. This is only the case for messages
|
||||
without any signed attributes.
|
||||
|
||||
2002-08-16 Werner Koch <wk@gnupg.org>
|
||||
|
||||
* certpath.c: Renamed to ..
|
||||
* certchain.c: this. Renamed all all other usages of "path" in the
|
||||
context of certificates to "chain".
|
||||
|
||||
* call-agent.c (learn_cb): Special treatment when the issuer
|
||||
certificate is missing.
|
||||
|
||||
2002-08-10 Werner Koch <wk@gnupg.org>
|
||||
|
||||
* Makefile.am (INCLUDES): Add definition for localedir.
|
||||
|
||||
* keylist.c (list_cert_colon): Print the short fingerprint in the
|
||||
key ID field.
|
||||
* fingerprint.c (gpgsm_get_short_fingerprint): New.
|
||||
* verify.c (gpgsm_verify): Print more verbose info for a good
|
||||
signature.
|
||||
|
||||
2002-08-09 Werner Koch <wk@gnupg.org>
|
||||
|
||||
* decrypt.c (prepare_decryption): Hack to detected already
|
||||
unpkcsedone keys.
|
||||
|
||||
* gpgsm.c (emergency_cleanup): New.
|
||||
(main): Initialize the signal handler.
|
||||
|
||||
* sign.c (gpgsm_sign): Reset the hash context for subsequent
|
||||
signers and release it at the end.
|
||||
|
||||
2002-08-05 Werner Koch <wk@gnupg.org>
|
||||
|
||||
* server.c (cmd_signer): New command "SIGNER"
|
||||
(register_commands): Register it.
|
||||
(cmd_sign): Pass the signer list to gpgsm_sign.
|
||||
* certlist.c (gpgsm_add_to_certlist): Add SECRET argument, check
|
||||
for secret key if set and changed all callers.
|
||||
* sign.c (gpgsm_sign): New argument SIGNERLIST and implemt
|
||||
multiple signers.
|
||||
* gpgsm.c (main): Support more than one -u.
|
||||
|
||||
* server.c (cmd_recipient): Return reason code 1 for No_Public_Key
|
||||
which is actually what gets returned from add_to_certlist.
|
||||
|
||||
2002-07-26 Werner Koch <wk@gnupg.org>
|
||||
|
||||
* certcheck.c (gpgsm_check_cert_sig): Implement proper cleanup.
|
||||
(gpgsm_check_cms_signature): Ditto.
|
||||
|
||||
2002-07-22 Werner Koch <wk@gnupg.org>
|
||||
|
||||
* keydb.c (keydb_add_resource): Register a lock file.
|
||||
(lock_all, unlock_all): Implemented.
|
||||
|
||||
* delete.c: New.
|
||||
* gpgsm.c: Made --delete-key work.
|
||||
* server.c (cmd_delkeys): New.
|
||||
(register_commands): New command DELKEYS.
|
||||
|
||||
* decrypt.c (gpgsm_decrypt): Print a convenience note when RC2 is
|
||||
used and a STATUS_ERROR with the algorithm oid.
|
||||
|
||||
2002-07-03 Werner Koch <wk@gnupg.org>
|
||||
|
||||
* server.c (gpgsm_status2): Insert a blank between all optional
|
||||
arguments when using assuan.
|
||||
* server.c (cmd_recipient): No more need for extra blank in constants.
|
||||
* import.c (print_imported_status): Ditto.
|
||||
* gpgsm.c (main): Ditto.
|
||||
|
||||
2002-07-02 Werner Koch <wk@gnupg.org>
|
||||
|
||||
* verify.c (gpgsm_verify): Extend the STATUS_BADSIG line with
|
||||
the fingerprint.
|
||||
|
||||
* certpath.c (check_cert_policy): Don't use log_error to print a
|
||||
warning.
|
||||
|
||||
* keydb.c (keydb_store_cert): Add optional ar EXISTED and changed
|
||||
all callers.
|
||||
* call-agent.c (learn_cb): Print info message only for real imports.
|
||||
|
||||
* import.c (gpgsm_import): Moved duplicated code to ...
|
||||
(check_and_store): new function. Added magic to import the entire
|
||||
chain. Print status only for real imports and moved printing code
|
||||
to ..
|
||||
(print_imported_status): New.
|
||||
|
||||
* call-dirmngr.c (gpgsm_dirmngr_isvalid): print status of dirmngr
|
||||
call in very verbose mode.
|
||||
|
||||
* gpgsm.c (main): Use the same error codes for STATUS_INV_RECP as
|
||||
with the server mode.
|
||||
|
||||
2002-06-29 Werner Koch <wk@gnupg.org>
|
||||
|
||||
* gpgsm.c: New option --auto-issuer-key-retrieve.
|
||||
* certpath.c (find_up): Try to retrieve an issuer key from an
|
||||
external source and from the ephemeral key DB.
|
||||
(find_up_store_certs_cb): New.
|
||||
|
||||
* keydb.c (keydb_set_ephemeral): Does now return the old
|
||||
state. Call the backend only when required.
|
||||
|
||||
* call-dirmngr.c (start_dirmngr): Use GNUPG_DEFAULT_DIRMNGR.
|
||||
(lookup_status_cb): Issue status only when CTRL is not NULL.
|
||||
(gpgsm_dirmngr_lookup): Document that CTRL is optional.
|
||||
|
||||
* call-agent.c (start_agent): Use GNUPG_DEFAULT_AGENT.
|
||||
|
||||
2002-06-28 Werner Koch <wk@gnupg.org>
|
||||
|
||||
* server.c (cmd_recipient): Add more reason codes.
|
||||
|
||||
2002-06-27 Werner Koch <wk@gnupg.org>
|
||||
|
||||
* certpath.c (gpgsm_basic_cert_check): Use
|
||||
--debug-no-path-validation to also bypass this basic check.
|
||||
|
||||
* gpgsm.c (main): Use GNUPG_DEFAULT_HOMEDIR constant.
|
||||
|
||||
* call-agent.c (start_agent): Create and pass the list of FD to
|
||||
keep in the child to assuan.
|
||||
* call-dirmngr.c (start_dirmngr): Ditto.
|
||||
|
||||
2002-06-26 Werner Koch <wk@gnupg.org>
|
||||
|
||||
* import.c (gpgsm_import): Print an STATUS_IMPORTED.
|
||||
|
||||
* gpgsm.c: --debug-no-path-validation does not take an argument.
|
||||
|
||||
2002-06-25 Werner Koch <wk@gnupg.org>
|
||||
|
||||
* certdump.c (print_dn_part): Always print a leading slash,
|
||||
removed NEED_DELIM arg and changed caller.
|
||||
|
||||
* export.c (gpgsm_export): Print LFs to FP and not stdout.
|
||||
(print_short_info): Ditto. Make use of gpgsm_print_name.
|
||||
|
||||
* server.c (cmd_export): Use output-fd instead of data lines; this
|
||||
was actually the specified way.
|
||||
|
||||
2002-06-24 Werner Koch <wk@gnupg.org>
|
||||
|
||||
* gpgsm.c: Removed duped help entry for --list-keys.
|
||||
|
||||
* gpgsm.c, gpgsm.h: New option --debug-no-path-validation.
|
||||
|
||||
* certpath.c (gpgsm_validate_path): Use it here instead of the
|
||||
debug flag hack.
|
||||
|
||||
* certpath.c (check_cert_policy): Return No_Policy_Match if the
|
||||
policy file could not be opened.
|
||||
|
||||
2002-06-20 Werner Koch <wk@gnupg.org>
|
||||
|
||||
* certlist.c (gpgsm_add_to_certlist): Fixed locating of a
|
||||
certificate with the required key usage.
|
||||
|
||||
* gpgsm.c (main): Fixed a segv when using --outfile without an
|
||||
argument.
|
||||
|
||||
* keylist.c (print_capabilities): Also check for non-repudiation
|
||||
and data encipherment.
|
||||
* certlist.c (cert_usage_p): Test for signing and encryption was
|
||||
swapped. Add a case for certification usage, handle
|
||||
non-repudiation and data encipherment.
|
||||
(gpgsm_cert_use_cert_p): New.
|
||||
(gpgsm_add_to_certlist): Added a CTRL argument and changed all
|
||||
callers to pass it.
|
||||
* certpath.c (gpgsm_validate_path): Use it here to print a status
|
||||
message. Added a CTRL argument and changed all callers to pass it.
|
||||
* decrypt.c (gpgsm_decrypt): Print a status message for wrong key
|
||||
usage.
|
||||
* verify.c (gpgsm_verify): Ditto.
|
||||
* keydb.c (classify_user_id): Allow a colon delimited fingerprint.
|
||||
|
||||
2002-06-19 Werner Koch <wk@gnupg.org>
|
||||
|
||||
* call-agent.c (learn_cb): Use log_info instead of log_error on
|
||||
successful import.
|
||||
|
||||
* keydb.c (keydb_set_ephemeral): New.
|
||||
(keydb_store_cert): New are ephemeral, changed all callers.
|
||||
* keylist.c (list_external_cb): Store cert as ephemeral.
|
||||
* export.c (gpgsm_export): Kludge to export epehmeral certificates.
|
||||
|
||||
* gpgsm.c (main): New command --list-external-keys.
|
||||
|
||||
2002-06-17 Werner Koch <wk@gnupg.org>
|
||||
|
||||
* certreqgen.c (read_parameters): Improved error handling.
|
||||
(gpgsm_genkey): Print error message.
|
||||
|
||||
2002-06-13 Werner Koch <wk@gnupg.org>
|
||||
|
||||
* gpgsm.c (main): New option --log-file.
|
||||
|
||||
2002-06-12 Werner Koch <wk@gnupg.org>
|
||||
|
||||
* call-dirmngr.c (lookup_status_cb): New.
|
||||
(gpgsm_dirmngr_lookup): Use the status CB. Add new arg CTRL and
|
||||
changed caller to pass it.
|
||||
|
||||
* gpgsm.c (open_fwrite): New.
|
||||
(main): Allow --output for --verify.
|
||||
|
||||
* sign.c (hash_and_copy_data): New.
|
||||
(gpgsm_sign): Implemented normal (non-detached) signatures.
|
||||
* gpgsm.c (main): Ditto.
|
||||
|
||||
* certpath.c (gpgsm_validate_path): Special error handling for
|
||||
no policy match.
|
||||
|
||||
2002-06-10 Werner Koch <wk@gnupg.org>
|
||||
|
||||
* server.c (get_status_string): Add STATUS_ERROR.
|
||||
|
||||
* certpath.c (gpgsm_validate_path): Tweaked the error checking to
|
||||
return error codes in a more sensitive way.
|
||||
* verify.c (gpgsm_verify): Send status TRUST_NEVER also for a bad
|
||||
CA certificate and when the certificate has been revoked. Issue
|
||||
TRUST_FULLY even when the cert has expired. Append an error token
|
||||
to these status lines. Issue the new generic error status when a
|
||||
cert was not found and when leaving the function.
|
||||
|
||||
2002-06-04 Werner Koch <wk@gnupg.org>
|
||||
|
||||
* gpgsm.c (main): New command --list-sigs
|
||||
* keylist.c (list_cert_std): New. Use it whenever colon mode is
|
||||
not used.
|
||||
(list_cert_chain): New.
|
||||
|
||||
2002-05-31 Werner Koch <wk@gnupg.org>
|
||||
|
||||
* gpgsm.c (main): Don't print the "go ahead" message for an
|
||||
invalid command.
|
||||
|
||||
2002-05-23 Werner Koch <wk@gnupg.org>
|
||||
|
||||
* import.c (gpgsm_import): Add error messages.
|
||||
|
||||
2002-05-21 Werner Koch <wk@gnupg.org>
|
||||
|
||||
* keylist.c (list_internal_keys): Renamed from gpgsm_list_keys.
|
||||
(list_external_keys): New.
|
||||
(gpgsm_list_keys): Dispatcher for above.
|
||||
* call-dirmngr.c (lookup_cb,pattern_from_strlist)
|
||||
(gpgsm_dirmngr_lookup): New.
|
||||
* server.c (option_handler): Handle new option --list-mode.
|
||||
(do_listkeys): Handle options and actually use the mode argument.
|
||||
(get_status_string): New code TRUNCATED.
|
||||
|
||||
* import.c (gpgsm_import): Try to identify the type of input and
|
||||
handle certs-only messages.
|
||||
|
||||
2002-05-14 Werner Koch <wk@gnupg.org>
|
||||
|
||||
* gpgsm.c: New option --faked-system-time
|
||||
* sign.c (gpgsm_sign): And use it here.
|
||||
* certpath.c (gpgsm_validate_path): Ditto.
|
||||
|
||||
2002-05-03 Werner Koch <wk@gnupg.org>
|
||||
|
||||
* certpath.c (gpgsm_validate_path): Added EXPTIME arg and changed
|
||||
all callers.
|
||||
* verify.c (gpgsm_verify): Tweaked usage of log_debug and
|
||||
log_error. Return EXPSIG status and add expiretime to VALIDSIG.
|
||||
|
||||
2002-04-26 Werner Koch <wk@gnupg.org>
|
||||
|
||||
* gpgsm.h (DBG_AGENT,DBG_AGENT_VALUE): Replaced by DBG_ASSUAN_*.
|
||||
Changed all users.
|
||||
|
||||
* call-agent.c (start_agent): Be more silent without -v.
|
||||
* call-dirmngr.c (start_dirmngr): Ditto.
|
||||
|
||||
2002-04-25 Werner Koch <wk@gnupg.org>
|
||||
|
||||
* call-agent.c (start_agent): Make copies of old locales and check
|
||||
for setlocale.
|
||||
|
||||
2002-04-25 Marcus Brinkmann <marcus@g10code.de>
|
||||
|
||||
* call-agent.c (start_agent): Fix error handling logic so the
|
||||
locale is always correctly reset.
|
||||
|
||||
2002-04-25 Marcus Brinkmann <marcus@g10code.de>
|
||||
|
||||
* server.c (option_handler): Accept display, ttyname, ttytype,
|
||||
lc_ctype and lc_messages options.
|
||||
* gpgsm.c (main): Allocate memory for these options.
|
||||
* gpgsm.h (struct opt): Make corresponding members non-const.
|
||||
|
||||
2002-04-24 Marcus Brinkmann <marcus@g10code.de>
|
||||
|
||||
* gpgsm.h (struct opt): New members display, ttyname, ttytype,
|
||||
lc_ctype, lc_messages.
|
||||
* gpgsm.c (enum cmd_and_opt_values): New members oDisplay,
|
||||
oTTYname, oTTYtype, oLCctype, oLCmessages.
|
||||
(opts): New entries for these options.
|
||||
(main): Handle these new options.
|
||||
* call-agent.c (start_agent): Set the various display and tty
|
||||
parameter after resetting.
|
||||
|
||||
2002-04-18 Werner Koch <wk@gnupg.org>
|
||||
|
||||
* certreqgen.c (gpgsm_genkey): Write status output on success.
|
||||
|
||||
2002-04-15 Werner Koch <wk@gnupg.org>
|
||||
|
||||
* gpgsm.c (main): Check ksba version.
|
||||
|
||||
* certpath.c (find_up): New to use the authorithKeyIdentifier.
|
||||
Use it in all other functions to locate the signing cert..
|
||||
|
||||
2002-04-11 Werner Koch <wk@gnupg.org>
|
||||
|
||||
* certlist.c (cert_usable_p): New.
|
||||
(gpgsm_cert_use_sign_p,gpgsm_cert_use_encrypt_p): New.
|
||||
(gpgsm_cert_use_verify_p,gpgsm_cert_use_decrypt_p): New.
|
||||
(gpgsm_add_to_certlist): Check the key usage.
|
||||
* sign.c (gpgsm_sign): Ditto.
|
||||
* verify.c (gpgsm_verify): Print a message wehn an unsuitable
|
||||
certificate was used.
|
||||
* decrypt.c (gpgsm_decrypt): Ditto
|
||||
* keylist.c (print_capabilities): Determine values from the cert.
|
||||
|
||||
2002-03-28 Werner Koch <wk@gnupg.org>
|
||||
|
||||
* keylist.c (list_cert_colon): Fixed listing of crt record; the
|
||||
issuer is not at the right place. Print a chainingID.
|
||||
* certpath.c (gpgsm_walk_cert_chain): Be a bit more silent on
|
||||
common errors.
|
||||
|
||||
2002-03-21 Werner Koch <wk@gnupg.org>
|
||||
|
||||
* export.c: New.
|
||||
* gpgsm.c: Add command --export.
|
||||
* server.c (cmd_export): New.
|
||||
|
||||
2002-03-13 Werner Koch <wk@gnupg.org>
|
||||
|
||||
* decrypt.c (gpgsm_decrypt): Allow multiple recipients.
|
||||
|
||||
2002-03-12 Werner Koch <wk@gnupg.org>
|
||||
|
||||
* certpath.c (check_cert_policy): Print the policy list.
|
||||
|
||||
* verify.c (gpgsm_verify): Detect certs-only message.
|
||||
|
||||
2002-03-11 Werner Koch <wk@gnupg.org>
|
||||
|
||||
* import.c (gpgsm_import): Print a notice about imported certificates
|
||||
when in verbose mode.
|
||||
|
||||
* gpgsm.c (main): Print INV_RECP status.
|
||||
* server.c (cmd_recipient): Ditto.
|
||||
|
||||
* server.c (gpgsm_status2): New. Allows for a list of strings.
|
||||
(gpgsm_status): Divert to gpgsm_status2.
|
||||
|
||||
* encrypt.c (gpgsm_encrypt): Don't use a default key when no
|
||||
recipients are given. Print a NO_RECP status.
|
||||
|
||||
2002-03-06 Werner Koch <wk@gnupg.org>
|
||||
|
||||
* server.c (cmd_listkeys, cmd_listsecretkeys): Divert to
|
||||
(do_listkeys): new. Add pattern parsing.
|
||||
|
||||
* keylist.c (gpgsm_list_keys): Handle selection pattern.
|
||||
|
||||
* gpgsm.c: New command --learn-card
|
||||
* call-agent.c (learn_cb,gpgsm_agent_learn): New.
|
||||
|
||||
* gpgsm.c (main): Print error messages for non-implemented commands.
|
||||
|
||||
* base64.c (base64_reader_cb): Use case insensitive compare of the
|
||||
Content-Type string to detect plain base-64.
|
||||
|
||||
2002-03-05 Werner Koch <wk@gnupg.org>
|
||||
|
||||
* gpgsm.c, gpgsm.h: Add local_user.
|
||||
* sign.c (gpgsm_get_default_cert): New.
|
||||
(get_default_signer): Use the new function if local_user is not
|
||||
set otherwise used that value.
|
||||
* encrypt.c (get_default_recipient): Removed.
|
||||
(gpgsm_encrypt): Use gpgsm_get_default_cert.
|
||||
|
||||
* verify.c (gpgsm_verify): Better error text for a bad signature
|
||||
found by comparing the hashs.
|
||||
|
||||
2002-02-27 Werner Koch <wk@gnupg.org>
|
||||
|
||||
* call-dirmngr.c, call-agent.c: Add 2 more arguments to all uses
|
||||
of assuan_transact.
|
||||
|
||||
2002-02-25 Werner Koch <wk@gnupg.org>
|
||||
|
||||
* server.c (option_handler): Allow to use -2 for "send all certs
|
||||
except the root cert".
|
||||
* sign.c (add_certificate_list): Implement it here.
|
||||
* certpath.c (gpgsm_is_root_cert): New.
|
||||
|
||||
2002-02-19 Werner Koch <wk@gnupg.org>
|
||||
|
||||
* certpath.c (check_cert_policy): New.
|
||||
(gpgsm_validate_path): And call it from here.
|
||||
* gpgsm.c (main): New options --policy-file,
|
||||
--disable-policy-checks and --enable-policy-checks.
|
||||
* gpgsm.h (opt): Added policy_file, no_policy_checks.
|
||||
|
||||
2002-02-18 Werner Koch <wk@gnupg.org>
|
||||
|
||||
* certpath.c (gpgsm_validate_path): Ask the agent to add the
|
||||
certificate into the trusted list.
|
||||
* call-agent.c (gpgsm_agent_marktrusted): New.
|
||||
|
||||
2002-02-07 Werner Koch <wk@gnupg.org>
|
||||
|
||||
* certlist.c (gpgsm_add_to_certlist): Check that the specified
|
||||
name identifies a certificate unambiguously.
|
||||
(gpgsm_find_cert): Ditto.
|
||||
|
||||
* server.c (cmd_listkeys): Check that the data stream is available.
|
||||
(cmd_listsecretkeys): Ditto.
|
||||
(has_option): New.
|
||||
(cmd_sign): Fix ambiguousity in option recognition.
|
||||
|
||||
* gpgsm.c (main): Enable --logger-fd.
|
||||
|
||||
* encrypt.c (gpgsm_encrypt): Increased buffer size for better
|
||||
performance.
|
||||
|
||||
* call-agent.c (gpgsm_agent_pksign): Check the S-Exp received from
|
||||
the agent.
|
||||
|
||||
* keylist.c (list_cert_colon): Filter out control characters.
|
||||
|
||||
2002-02-06 Werner Koch <wk@gnupg.org>
|
||||
|
||||
* decrypt.c (gpgsm_decrypt): Bail out after an decryption error.
|
||||
|
||||
* server.c (reset_notify): Close input and output FDs.
|
||||
(cmd_encrypt,cmd_decrypt,cmd_verify,cmd_sign.cmd_import)
|
||||
(cmd_genkey): Close the FDs and release the recipient list even in
|
||||
the error case.
|
||||
|
||||
2002-02-01 Marcus Brinkmann <marcus@g10code.de>
|
||||
|
||||
* sign.c (gpgsm_sign): Do not release certificate twice.
|
||||
|
||||
2002-01-29 Werner Koch <wk@gnupg.org>
|
||||
|
||||
* call-agent.c (gpgsm_agent_havekey): New.
|
||||
* keylist.c (list_cert_colon): New arg HAVE_SECRET, print "crs"
|
||||
when we know that the secret key is available.
|
||||
(gpgsm_list_keys): New arg MODE, check whether a secret key is
|
||||
available. Changed all callers.
|
||||
* gpgsm.c (main): New command --list-secret-keys.
|
||||
* server.c (cmd_listsecretkeys): New.
|
||||
(cmd_listkeys): Return secret keys with "crs" record.
|
||||
|
||||
2002-01-28 Werner Koch <wk@gnupg.org>
|
||||
|
||||
* certreqgen.c (create_request): Store the email address in the req.
|
||||
|
||||
2002-01-25 Werner Koch <wk@gnupg.org>
|
||||
|
||||
* gpgsm.c (main): Disable core dumps.
|
||||
|
||||
* sign.c (add_certificate_list): New.
|
||||
(gpgsm_sign): Add the certificates to the CMS object.
|
||||
* certpath.c (gpgsm_walk_cert_chain): New.
|
||||
* gpgsm.h (server_control_s): Add included_certs.
|
||||
* gpgsm.c: Add option --include-certs.
|
||||
(gpgsm_init_default_ctrl): New.
|
||||
(main): Call it.
|
||||
* server.c (gpgsm_server): Ditto.
|
||||
(option_handler): Support --include-certs.
|
||||
|
||||
2002-01-23 Werner Koch <wk@gnupg.org>
|
||||
|
||||
* certpath.c (gpgsm_validate_path): Print the DN of a missing issuer.
|
||||
* certdump.c (gpgsm_dump_string): New.
|
||||
(print_dn): Replaced by above.
|
||||
|
||||
2002-01-22 Werner Koch <wk@gnupg.org>
|
||||
|
||||
* certpath.c (unknown_criticals): New.
|
||||
(allowed_ca): New.
|
||||
(gpgsm_validate_path): Check validity, CA attribute, path length
|
||||
and unknown critical extensions.
|
||||
|
||||
2002-01-21 Werner Koch <wk@gnupg.org>
|
||||
|
||||
* gpgsm.c: Add option --enable-crl-checks.
|
||||
|
||||
* call-agent.c (start_agent): Implemented socket based access.
|
||||
* call-dirmngr.c (start_dirmngr): Ditto.
|
||||
|
||||
2002-01-20 Werner Koch <wk@gnupg.org>
|
||||
|
||||
* server.c (option_handler): New.
|
||||
(gpgsm_server): Register it with assuan.
|
||||
|
||||
2002-01-19 Werner Koch <wk@gnupg.org>
|
||||
|
||||
* server.c (gpgsm_server): Use assuan_deinit_server and setup
|
||||
assuan logging if enabled.
|
||||
* call-agent.c (inq_ciphertext_cb): Don't show the session key in
|
||||
an Assuan log file.
|
||||
|
||||
* gpgsm.c (my_strusage): Take bugreport address from configure.ac
|
||||
|
||||
2002-01-15 Werner Koch <wk@gnupg.org>
|
||||
|
||||
* import.c (gpgsm_import): Just do a basic cert check before
|
||||
storing it.
|
||||
* certpath.c (gpgsm_basic_cert_check): New.
|
||||
|
||||
* keydb.c (keydb_store_cert): New.
|
||||
* import.c (store_cert): Removed and change all caller to use
|
||||
the new function.
|
||||
* verify.c (store_cert): Ditto.
|
||||
|
||||
* certlist.c (gpgsm_add_to_certlist): Validate the path
|
||||
|
||||
* certpath.c (gpgsm_validate_path): Check the trust list.
|
||||
* call-agent.c (gpgsm_agent_istrusted): New.
|
||||
|
||||
2002-01-14 Werner Koch <wk@gnupg.org>
|
||||
|
||||
* call-dirmngr.c (inq_certificate): Changed for new interface semantic.
|
||||
* certlist.c (gpgsm_find_cert): New.
|
||||
|
||||
2002-01-13 Werner Koch <wk@gnupg.org>
|
||||
|
||||
* fingerprint.c (gpgsm_get_certid): Print the serial and not the
|
||||
hash after the dot.
|
||||
|
||||
2002-01-11 Werner Koch <wk@gnupg.org>
|
||||
|
||||
* call-dirmngr.c: New.
|
||||
* certpath.c (gpgsm_validate_path): Check the CRL here.
|
||||
* fingerprint.c (gpgsm_get_certid): New.
|
||||
* gpgsm.c: New options --dirmngr-program and --disable-crl-checks.
|
||||
|
||||
2002-01-10 Werner Koch <wk@gnupg.org>
|
||||
|
||||
* base64.c (gpgsm_create_writer): Allow to set the object name
|
||||
|
||||
2002-01-08 Werner Koch <wk@gnupg.org>
|
||||
|
||||
* keydb.c (spacep): Removed because it is now in util.c
|
||||
|
||||
* server.c (cmd_genkey): New.
|
||||
* certreqgen.c: New. The parameter handling code has been taken
|
||||
from gnupg/g10/keygen.c version 1.0.6.
|
||||
* call-agent.c (gpgsm_agent_genkey): New.
|
||||
|
||||
2002-01-02 Werner Koch <wk@gnupg.org>
|
||||
|
||||
* server.c (rc_to_assuan_status): Removed and changed all callers
|
||||
to use map_to_assuan_status.
|
||||
|
||||
2001-12-20 Werner Koch <wk@gnupg.org>
|
||||
|
||||
* verify.c (gpgsm_verify): Implemented non-detached signature
|
||||
verification. Add OUT_FP arg, initialize a writer and changed all
|
||||
callers.
|
||||
* server.c (cmd_verify): Pass an out_fp if one has been set.
|
||||
|
||||
* base64.c (base64_reader_cb): Try to detect an S/MIME body part.
|
||||
|
||||
* certdump.c (print_sexp): Renamed to gpgsm_dump_serial, made
|
||||
global.
|
||||
(print_time): Renamed to gpgsm_dump_time, made global.
|
||||
(gpgsm_dump_serial): Take a real S-Expression as argument and
|
||||
print the first item.
|
||||
* keylist.c (list_cert_colon): Ditto.
|
||||
* keydb.c (keydb_search_issuer_sn): Ditto.
|
||||
* decrypt.c (print_integer_sexp): Removed and made callers
|
||||
use gpgsm_dump_serial.
|
||||
* verify.c (print_time): Removed, made callers use gpgsm_dump_time.
|
||||
|
||||
2001-12-19 Marcus Brinkmann <marcus@g10code.de>
|
||||
|
||||
* call-agent.c (start_agent): Add new argument to assuan_pipe_connect.
|
||||
|
||||
2001-12-18 Werner Koch <wk@gnupg.org>
|
||||
|
||||
* verify.c (print_integer_sexp): Renamed from print_integer and
|
||||
print the serial number according to the S-Exp rules.
|
||||
* decrypt.c (print_integer_sexp): Ditto.
|
||||
|
||||
2001-12-17 Werner Koch <wk@gnupg.org>
|
||||
|
||||
* keylist.c (list_cert_colon): Changed for new return value of
|
||||
get_serial.
|
||||
* keydb.c (keydb_search_issuer_sn): Ditto.
|
||||
* certcheck.c (gpgsm_check_cert_sig): Likewise for other S-Exp
|
||||
returingin functions.
|
||||
* fingerprint.c (gpgsm_get_keygrip): Ditto.
|
||||
* encrypt.c (encrypt_dek): Ditto
|
||||
* certcheck.c (gpgsm_check_cms_signature): Ditto
|
||||
* decrypt.c (prepare_decryption): Ditto.
|
||||
* call-agent.c (gpgsm_agent_pkdecrypt): Removed arg ciphertextlen,
|
||||
use KsbaSexp type and calculate the length.
|
||||
|
||||
* certdump.c (print_sexp): Remaned from print_integer, changed caller.
|
||||
|
||||
* Makefile.am: Use the LIBGCRYPT and LIBKSBA variables.
|
||||
|
||||
* fingerprint.c (gpgsm_get_keygrip): Use the new
|
||||
gcry_pk_get_keygrip to calculate the grip - note the algorithm and
|
||||
therefore the grip values changed.
|
||||
|
||||
2001-12-15 Werner Koch <wk@gnupg.org>
|
||||
|
||||
* certcheck.c (gpgsm_check_cms_signature): Removed the faked-key
|
||||
kludge.
|
||||
(gpgsm_create_cms_signature): Removed the commented fake key
|
||||
code. This makes the function pretty simple.
|
||||
|
||||
* gpgsm.c (main): Renamed the default key database to "keyring.kbx".
|
||||
|
||||
* decrypt.c (gpgsm_decrypt): Write STATUS_DECRYPTION_*.
|
||||
* sign.c (gpgsm_sign): Write a STATUS_SIG_CREATED.
|
||||
|
||||
2001-12-14 Werner Koch <wk@gnupg.org>
|
||||
|
||||
* keylist.c (list_cert_colon): Kludge to show an email address
|
||||
encoded in the subject's DN.
|
||||
|
||||
* verify.c (gpgsm_verify): Add hash debug helpers
|
||||
* sign.c (gpgsm_sign): Ditto.
|
||||
|
||||
* base64.c (base64_reader_cb): Reset the linelen when we need to
|
||||
skip the line and adjusted test; I somehow forgot about DeMorgan.
|
||||
|
||||
* server.c (cmd_encrypt,cmd_decrypt,cmd_sign,cmd_verify)
|
||||
(cmd_import): Close the FDs on success.
|
||||
(close_message_fd): New.
|
||||
(input_notify): Setting autodetect_encoding to 0 after initializing
|
||||
it to 0 is pretty pointless. Easy to fix.
|
||||
|
||||
* gpgsm.c (main): New option --debug-wait n, so that it is
|
||||
possible to attach gdb when used in server mode.
|
||||
|
||||
* sign.c (get_default_signer): Use keydb_classify_name here.
|
||||
|
||||
2001-12-14 Marcus Brinkmann <marcus@g10code.de>
|
||||
|
||||
* call-agent.c (LINELENGTH): Removed.
|
||||
(gpgsm_agent_pksign): Use ASSUAN_LINELENGTH, not LINELENGTH.
|
||||
(gpgsm_agent_pkdecrypt): Likewise.
|
||||
|
||||
2001-12-13 Werner Koch <wk@gnupg.org>
|
||||
|
||||
* keylist.c (list_cert_colon): Print alternative names of subject
|
||||
and a few other values.
|
||||
|
||||
2001-12-12 Werner Koch <wk@gnupg.org>
|
||||
|
||||
* gpgsm.c (main): New options --assume-{armor,base64,binary}.
|
||||
* base64.c (base64_reader_cb): Fixed non-autodetection mode.
|
||||
|
||||
2001-12-04 Werner Koch <wk@gnupg.org>
|
||||
|
||||
* call-agent.c (read_from_agent): Check for inquire responses.
|
||||
(request_reply): Handle them using a new callback arg, changed all
|
||||
callers.
|
||||
(gpgsm_agent_pkdecrypt): New.
|
||||
|
||||
2001-11-27 Werner Koch <wk@gnupg.org>
|
||||
|
||||
* base64.c: New. Changed all other functions to use this instead
|
||||
of direct creation of ksba_reader/writer.
|
||||
* gpgsm.c (main): Set ctrl.auto_encoding unless --no-armor is used.
|
||||
|
||||
2001-11-26 Werner Koch <wk@gnupg.org>
|
||||
|
||||
* gpgsm.c: New option --agent-program
|
||||
* call-agent.c (start_agent): Allow to override the default path
|
||||
to the agent.
|
||||
|
||||
* keydb.c (keydb_add_resource): Create keybox
|
||||
|
||||
* keylist.c (gpgsm_list_keys): Fixed non-server keylisting.
|
||||
|
||||
* server.c (rc_to_assuan_status): New. Use it for all commands.
|
||||
|
||||
|
||||
Copyright 2001, 2002, 2003 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.
|
55
sm/Makefile.am
Normal file
55
sm/Makefile.am
Normal file
|
@ -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@
|
713
sm/call-agent.c
Normal file
713
sm/call-agent.c
Normal file
|
@ -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);
|
||||
}
|
||||
|
632
sm/call-dirmngr.c
Normal file
632
sm/call-dirmngr.c
Normal file
|
@ -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);
|
||||
}
|
793
sm/certchain.c
Normal file
793
sm/certchain.c
Normal file
|
@ -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;
|
||||
}
|
||||
|
300
sm/certcheck.c
Normal file
300
sm/certcheck.c
Normal file
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
457
sm/certdump.c
Normal file
457
sm/certdump.c
Normal file
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
315
sm/certlist.c
Normal file
315
sm/certlist.c
Normal file
|
@ -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;
|
||||
}
|
||||
|
699
sm/certreqgen.c
Normal file
699
sm/certreqgen.c
Normal file
|
@ -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: joe@foo.bar
|
||||
# Do a commit here, so that we can later print "done" :-)
|
||||
%commit
|
||||
%echo done
|
||||
EOF
|
||||
*/
|
||||
|
||||
|
||||
#include <config.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
#include <time.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "gpgsm.h"
|
||||
#include <gcrypt.h>
|
||||
#include <ksba.h>
|
||||
|
||||
#include "keydb.h"
|
||||
#include "i18n.h"
|
||||
|
||||
|
||||
enum para_name {
|
||||
pKEYTYPE,
|
||||
pKEYLENGTH,
|
||||
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;
|
||||
}
|
||||
|
506
sm/decrypt.c
Normal file
506
sm/decrypt.c
Normal file
|
@ -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;
|
||||
}
|
||||
|
||||
|
165
sm/delete.c
Normal file
165
sm/delete.c
Normal file
|
@ -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;
|
||||
}
|
550
sm/encrypt.c
Normal file
550
sm/encrypt.c
Normal file
|
@ -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;
|
||||
}
|
249
sm/export.c
Normal file
249
sm/export.c
Normal file
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
271
sm/fingerprint.c
Normal file
271
sm/fingerprint.c
Normal file
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
1458
sm/gpgsm.c
Normal file
1458
sm/gpgsm.c
Normal file
File diff suppressed because it is too large
Load diff
274
sm/gpgsm.h
Normal file
274
sm/gpgsm.h
Normal file
|
@ -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*/
|
349
sm/import.c
Normal file
349
sm/import.c
Normal file
|
@ -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;
|
||||
}
|
||||
|
||||
|
1282
sm/keydb.c
Normal file
1282
sm/keydb.c
Normal file
File diff suppressed because it is too large
Load diff
617
sm/keylist.c
Normal file
617
sm/keylist.c
Normal file
|
@ -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);
|
||||
}
|
1070
sm/server.c
Normal file
1070
sm/server.c
Normal file
File diff suppressed because it is too large
Load diff
621
sm/sign.c
Normal file
621
sm/sign.c
Normal file
|
@ -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;
|
||||
}
|
550
sm/verify.c
Normal file
550
sm/verify.c
Normal file
|
@ -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;
|
||||
}
|
||||
|
Loading…
Add table
Add a link
Reference in a new issue