From c13b76ca6a495cda6e51934a7788f48ca6f3bb0e Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Thu, 9 Jan 2003 12:59:25 +0000 Subject: [PATCH] Updated from latest NewPG project --- agent/ChangeLog | 73 +++++++++++++++ agent/agent.h | 31 ++++--- agent/cache.c | 2 +- agent/command.c | 203 +++++++++++++++++++++++++++-------------- agent/divert-scd.c | 23 ++--- agent/findkey.c | 12 ++- agent/genkey.c | 50 ++++++++-- agent/gpg-agent.c | 187 ++++++++++++++++++++++++++++++++----- agent/pkdecrypt.c | 4 +- agent/pksign.c | 8 +- agent/protect-tool.c | 2 + agent/query.c | 50 ++++++---- agent/simple-pwquery.c | 7 +- agent/trustlist.c | 6 +- common/ChangeLog | 11 +++ common/isascii.c | 29 ++++++ common/putc_unlocked.c | 31 +++++++ common/signal.c | 4 +- kbx/ChangeLog | 11 ++- kbx/Makefile.am | 4 +- kbx/keybox-search.c | 5 +- sm/ChangeLog | 32 +++++++ sm/call-agent.c | 30 +++++- sm/call-dirmngr.c | 140 +++++++++++++++++++++++++++- sm/certcheck.c | 3 +- sm/gpgsm.c | 86 ++++++++++++++++- sm/gpgsm.h | 3 + sm/verify.c | 30 ++++++ 28 files changed, 902 insertions(+), 175 deletions(-) create mode 100644 common/isascii.c create mode 100644 common/putc_unlocked.c diff --git a/agent/ChangeLog b/agent/ChangeLog index c19193b23..dc1bedf55 100644 --- a/agent/ChangeLog +++ b/agent/ChangeLog @@ -1,3 +1,76 @@ +2002-12-04 Werner Koch + + * gpg-agent.c: New variable config_filename. + (parse_rereadable_options): New. + (main): Use it here. Add setting of default values, set + config_filename. + (reread_configuration): Filled with actual code. + +2002-12-03 Werner Koch + + * protect-tool.c (read_key): Don't run make_canonical on a NULL buffer. + + * command.c (parse_hexstring): New. + (cmd_sethash): Use it. + (parse_keygrip): New. + (cmd_havekey, cmd_sigkey): Use it. + (cmd_passwd): New. + * genkey.c (agent_protect_and_store): New. + (store_key): Add arg FORCE. + (agent_genkey): Pass false to this force of store_key. + +2002-11-13 Werner Koch + + * gpg-agent.c (main): Switch all messages to utf-8. + + * simple-pwquery.c (agent_send_all_options): Use $GPG_TTY and + stdin with ttyname. + + * cache.c (new_data): Uiih - /sizeof d/sizeof *d/. + +2002-11-10 Werner Koch + + * command.c (option_handler): Fix keep_tty check. + +2002-11-06 Werner Koch + + * gpg-agent.c (main): Make sure we have a default ttyname. + * command.c (option_handler): Check opt.keep_tty here + * query.c (start_pinentry): but not anymore here. + +2002-11-05 Werner Koch + + * agent.h (opt,server_control_s): Move display and lc_ variables + to the control struct so that they are per connection. + * gpg-agent.c (agent_init_default_ctrl): New. + (main): Assign those command line options to new default_* variables. + Reset DISPLAY in server mode so that tehre is no implicit default. + * command.c (start_command_handler): Initialize and deinitialize + the control values. + (option_handler): Work on the ctrl values and not on the opt. + * query.c (start_pinentry): New argument CTRL to set the display + connection specific. Changed all callers to pass this value. + (agent_askpin,agent_get_passphrase,agent_get_confirmation): Add + CTRL arg and pass it ot start_pinentry. + * command.c (cmd_get_passphrase): Pass CTRL argument. + * trustlist.c (agent_marktrusted): Add CTRL argument + * command.c (cmd_marktrusted): Pass CTRL argument + * divert-scd.c (ask_for_card): Add CTRL arg. + (divert_pksign,divert_pkdecrypt): Ditto. Changed caller. + (getpin_cb): Use OPAQUE to pass the CTRL variable. Changed both + users. + * findkey.c (unprotect): Add CTRL arg. + (agent_key_from_file): Ditto. + + * query.c (unlock_pinentry): Disconnect the pinentry so that we + start a new one for each request. This is required to support + clients with different environments (e.g. X magic cookies). + +2002-09-05 Neal H. Walfield + + * gpg-agent.c (main) [USE_GNU_PTH]: No need to call + assuan_set_io_func as assuan is smart. + 2002-09-25 Werner Koch * gpg-agent.c (handle_signal): Flush cache on SIGHUP. diff --git a/agent/agent.h b/agent/agent.h index 24267b06a..bd7b36a5d 100644 --- a/agent/agent.h +++ b/agent/agent.h @@ -1,5 +1,5 @@ /* agent.h - Global definitions for the agent - * Copyright (C) 2001 Free Software Foundation, Inc. + * Copyright (C) 2001, 2002 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -36,11 +36,6 @@ struct { int batch; /* batch mode */ const char *homedir; /* configuration directory name */ const char *pinentry_program; - char *display; - char *ttyname; - char *ttytype; - char *lc_ctype; - char *lc_messages; const char *scdaemon_program; int no_grab; /* don't let the pinentry grab the keyboard */ unsigned long def_cache_ttl; @@ -73,6 +68,11 @@ struct server_local_s; struct server_control_s { struct server_local_s *server_local; + char *display; + char *ttyname; + char *ttytype; + char *lc_ctype; + char *lc_messages; struct { int algo; unsigned char value[MAX_DIGEST_LEN]; @@ -107,6 +107,7 @@ enum { /*-- gpg-agent.c --*/ void agent_exit (int rc); /* also implemented in other tools */ +void agent_init_default_ctrl (struct server_control_s *ctrl); /*-- command.c --*/ void start_command_handler (int, int); @@ -114,16 +115,17 @@ void start_command_handler (int, int); /*-- findkey.c --*/ int agent_write_private_key (const unsigned char *grip, const void *buffer, size_t length, int force); -GCRY_SEXP agent_key_from_file (const unsigned char *grip, +GCRY_SEXP agent_key_from_file (CTRL ctrl, const unsigned char *grip, unsigned char **shadow_info, int ignore_cache); int agent_key_available (const unsigned char *grip); /*-- query.c --*/ -int agent_askpin (const char *desc_text, struct pin_entry_info_s *pininfo); -int agent_get_passphrase (char **retpass, +int agent_askpin (CTRL ctrl, + const char *desc_text, struct pin_entry_info_s *pininfo); +int agent_get_passphrase (CTRL ctrl, char **retpass, const char *desc, const char *prompt, const char *errtext); -int agent_get_confirmation (const char *desc, const char *ok, +int agent_get_confirmation (CTRL ctrl, const char *desc, const char *ok, const char *cancel); /*-- cache.c --*/ @@ -143,6 +145,7 @@ int agent_pkdecrypt (CTRL ctrl, const char *ciphertext, size_t ciphertextlen, /*-- genkey.c --*/ int agent_genkey (CTRL ctrl, const char *keyparam, size_t keyparmlen, FILE *outfp); +int agent_protect_and_store (CTRL ctrl, GCRY_SEXP s_skey); /*-- protect.c --*/ int agent_protect (const unsigned char *plainkey, const char *passphrase, @@ -160,13 +163,15 @@ int agent_get_shadow_info (const unsigned char *shadowkey, /*-- trustlist.c --*/ int agent_istrusted (const char *fpr); int agent_listtrusted (void *assuan_context); -int agent_marktrusted (const char *name, const char *fpr, int flag); +int agent_marktrusted (CTRL ctrl, const char *name, const char *fpr, int flag); /*-- divert-scd.c --*/ -int divert_pksign (const unsigned char *digest, size_t digestlen, int algo, +int divert_pksign (CTRL ctrl, + const unsigned char *digest, size_t digestlen, int algo, const unsigned char *shadow_info, unsigned char **r_sig); -int divert_pkdecrypt (const unsigned char *cipher, +int divert_pkdecrypt (CTRL ctrl, + const unsigned char *cipher, const unsigned char *shadow_info, char **r_buf, size_t *r_len); diff --git a/agent/cache.c b/agent/cache.c index 8f5bf9d3d..b6ab55085 100644 --- a/agent/cache.c +++ b/agent/cache.c @@ -67,7 +67,7 @@ new_data (const void *data, size_t length) secure storage provider*/ total = length + 32 - (length % 32); - d = gcry_malloc_secure (sizeof d + total - 1); + d = gcry_malloc_secure (sizeof *d + total - 1); if (d) { d->totallen = total; diff --git a/agent/command.c b/agent/command.c index 55ee8b84a..3573c27f8 100644 --- a/agent/command.c +++ b/agent/command.c @@ -77,6 +77,48 @@ has_option (const char *line, const char *name) return (s && (s == line || spacep (s-1)) && (!s[n] || spacep (s+n))); } +/* Parse a hex string. Return an Assuan error code or 0 on success and the + length of the parsed string in LEN. */ +static int +parse_hexstring (ASSUAN_CONTEXT ctx, const char *string, size_t *len) +{ + const char *p; + size_t n; + + /* parse the hash value */ + for (p=string, n=0; hexdigitp (p); p++, n++) + ; + if (*p) + return set_error (Parameter_Error, "invalid hexstring"); + if ((n&1)) + return set_error (Parameter_Error, "odd number of digits"); + *len = n; + return 0; +} + +/* Parse the keygrip in STRING into the provided buffer BUF. BUF must + provide space for 20 bytes. BUF is not changed if the fucntions + returns an error. */ +static int +parse_keygrip (ASSUAN_CONTEXT ctx, const char *string, unsigned char *buf) +{ + int rc; + size_t n; + const unsigned char *p; + + rc = parse_hexstring (ctx, string, &n); + if (rc) + return rc; + n /= 2; + if (n != 20) + return set_error (Parameter_Error, "invalid length of keygrip"); + + for (p=string, n=0; n < 20; p += 2, n++) + buf[n] = xtoi_2 (p); + + return 0; +} + @@ -136,6 +178,7 @@ cmd_listtrusted (ASSUAN_CONTEXT ctx, char *line) static int cmd_marktrusted (ASSUAN_CONTEXT ctx, char *line) { + CTRL ctrl = assuan_get_pointer (ctx); int rc, n, i; char *p; char fpr[41]; @@ -164,7 +207,7 @@ cmd_marktrusted (ASSUAN_CONTEXT ctx, char *line) while (spacep (p)) p++; - rc = agent_marktrusted (p, fpr, flag); + rc = agent_marktrusted (ctrl, p, fpr, flag); if (rc) log_error ("command marktrusted failed: %s\n", gnupg_strerror (rc)); return map_to_assuan_status (rc); @@ -179,23 +222,12 @@ cmd_marktrusted (ASSUAN_CONTEXT ctx, char *line) static int cmd_havekey (ASSUAN_CONTEXT ctx, char *line) { - int n; - char *p; + int rc; unsigned char buf[20]; - /* parse the hash value */ - for (p=line,n=0; hexdigitp (p); p++, n++) - ; - if (*p) - return set_error (Parameter_Error, "invalid hexstring"); - if ((n&1)) - return set_error (Parameter_Error, "odd number of digits"); - n /= 2; - if (n != 20) - return set_error (Parameter_Error, "invalid length of keygrip"); - - for (p=line, n=0; n < 20; p += 2, n++) - buf[n] = xtoi_2 (p); + rc = parse_keygrip (ctx, line, buf); + if (rc) + return rc; if (agent_key_available (buf)) return ASSUAN_No_Secret_Key; @@ -211,29 +243,17 @@ cmd_havekey (ASSUAN_CONTEXT ctx, char *line) static int cmd_sigkey (ASSUAN_CONTEXT ctx, char *line) { - int n; - char *p; + int rc; CTRL ctrl = assuan_get_pointer (ctx); - unsigned char *buf; - /* parse the hash value */ - for (p=line,n=0; hexdigitp (p); p++, n++) - ; - if (*p) - return set_error (Parameter_Error, "invalid hexstring"); - if ((n&1)) - return set_error (Parameter_Error, "odd number of digits"); - n /= 2; - if (n != 20) - return set_error (Parameter_Error, "invalid length of keygrip"); - - buf = ctrl->keygrip; - for (p=line, n=0; n < 20; p += 2, n++) - buf[n] = xtoi_2 (p); + rc = parse_keygrip (ctx, line, ctrl->keygrip); + if (rc) + return rc; ctrl->have_keygrip = 1; return 0; } + /* SETHASH The client can use this command to tell the server about the data @@ -241,7 +261,8 @@ cmd_sigkey (ASSUAN_CONTEXT ctx, char *line) static int cmd_sethash (ASSUAN_CONTEXT ctx, char *line) { - int n; + int rc; + size_t n; char *p; CTRL ctrl = assuan_get_pointer (ctx); unsigned char *buf; @@ -257,12 +278,9 @@ cmd_sethash (ASSUAN_CONTEXT ctx, char *line) ctrl->digest.algo = algo; /* parse the hash value */ - for (p=line,n=0; hexdigitp (p); p++, n++) - ; - if (*p) - return set_error (Parameter_Error, "invalid hexstring"); - if ((n&1)) - return set_error (Parameter_Error, "odd number of digits"); + rc = parse_hexstring (ctx, line, &n); + if (rc) + return rc; n /= 2; if (n != 16 && n != 20 && n != 24 && n != 32) return set_error (Parameter_Error, "unsupported length of hash"); @@ -386,6 +404,7 @@ plus_to_blank (char *s) static int cmd_get_passphrase (ASSUAN_CONTEXT ctx, char *line) { + CTRL ctrl = assuan_get_pointer (ctx); int rc; const char *pw; char *response; @@ -459,7 +478,7 @@ cmd_get_passphrase (ASSUAN_CONTEXT ctx, char *line) if (desc) plus_to_blank (desc); - rc = agent_get_passphrase (&response, desc, prompt, errtext); + rc = agent_get_passphrase (ctrl, &response, desc, prompt, errtext); if (!rc) { if (cacheid) @@ -519,53 +538,93 @@ cmd_learn (ASSUAN_CONTEXT ctx, char *line) } + +/* PASSWD + + Change the passphrase/PID for the key identified by keygrip in LINE. */ +static int +cmd_passwd (ASSUAN_CONTEXT ctx, char *line) +{ + CTRL ctrl = assuan_get_pointer (ctx); + int rc; + unsigned char grip[20]; + GCRY_SEXP s_skey = NULL; + unsigned char *shadow_info = NULL; + + rc = parse_keygrip (ctx, line, grip); + if (rc) + return rc; /* we can't jump to leave because this is already an + Assuan error code. */ + + s_skey = agent_key_from_file (ctrl, grip, &shadow_info, 1); + if (!s_skey && !shadow_info) + rc = seterr (No_Secret_Key); + else if (!s_skey) + { + log_error ("changing a smartcard PIN is not yet supported\n"); + rc = seterr (Not_Implemented); + } + else + rc = agent_protect_and_store (ctrl, s_skey); + + gcry_sexp_release (s_skey); + xfree (shadow_info); + if (rc) + log_error ("command passwd failed: %s\n", gnupg_strerror (rc)); + return map_to_assuan_status (rc); +} + + static int option_handler (ASSUAN_CONTEXT ctx, const char *key, const char *value) { CTRL ctrl = assuan_get_pointer (ctx); - /* FIXME: We should not change opt. here. It is not a problem right - now but as soon as we are allowing concurrent connections we mess - things up */ if (!strcmp (key, "display")) { - if (opt.display) - free (opt.display); - opt.display = strdup (value); - if (!opt.display) + if (ctrl->display) + free (ctrl->display); + ctrl->display = strdup (value); + if (!ctrl->display) return ASSUAN_Out_Of_Core; } else if (!strcmp (key, "ttyname")) { - if (opt.ttyname) - free (opt.ttyname); - opt.ttyname = strdup (value); - if (!opt.ttyname) - return ASSUAN_Out_Of_Core; + if (!opt.keep_tty) + { + if (ctrl->ttyname) + free (ctrl->ttyname); + ctrl->ttyname = strdup (value); + if (!ctrl->ttyname) + return ASSUAN_Out_Of_Core; + } } else if (!strcmp (key, "ttytype")) { - if (opt.ttytype) - free (opt.ttytype); - opt.ttytype = strdup (value); - if (!opt.ttytype) - return ASSUAN_Out_Of_Core; + if (!opt.keep_tty) + { + if (ctrl->ttytype) + free (ctrl->ttytype); + ctrl->ttytype = strdup (value); + if (!ctrl->ttytype) + return ASSUAN_Out_Of_Core; + } } else if (!strcmp (key, "lc-ctype")) { - if (opt.lc_ctype) - free (opt.lc_ctype); - opt.lc_ctype = strdup (value); - if (!opt.lc_ctype) + if (ctrl->lc_ctype) + free (ctrl->lc_ctype); + ctrl->lc_ctype = strdup (value); + if (!ctrl->lc_ctype) return ASSUAN_Out_Of_Core; } else if (!strcmp (key, "lc-messages")) { - if (opt.lc_messages) - free (opt.lc_messages); - opt.lc_messages = strdup (value); - if (!opt.lc_messages) + if (ctrl->lc_messages) + free (ctrl->lc_messages); + ctrl->lc_messages = strdup (value); + if (!ctrl->lc_messages) return ASSUAN_Out_Of_Core; } else if (!strcmp (key, "use-cache-for-signing")) @@ -599,6 +658,7 @@ register_commands (ASSUAN_CONTEXT ctx) { "LISTTRUSTED", 0, cmd_listtrusted }, { "MARKTRUSTED", 0, cmd_marktrusted }, { "LEARN", 0, cmd_learn }, + { "PASSWD", 0, cmd_passwd }, { "", ASSUAN_CMD_INPUT, NULL }, { "", ASSUAN_CMD_OUTPUT, NULL }, { NULL } @@ -630,6 +690,7 @@ start_command_handler (int listen_fd, int fd) struct server_control_s ctrl; memset (&ctrl, 0, sizeof ctrl); + agent_init_default_ctrl (&ctrl); if (listen_fd == -1 && fd == -1) { @@ -693,5 +754,15 @@ start_command_handler (int listen_fd, int fd) assuan_deinit_server (ctx); + if (ctrl.display) + free (ctrl.display); + if (ctrl.ttyname) + free (ctrl.ttyname); + if (ctrl.ttytype) + free (ctrl.ttytype); + if (ctrl.lc_ctype) + free (ctrl.lc_ctype); + if (ctrl.lc_messages) + free (ctrl.lc_messages); } diff --git a/agent/divert-scd.c b/agent/divert-scd.c index 40b6bcfa3..38f8173c0 100644 --- a/agent/divert-scd.c +++ b/agent/divert-scd.c @@ -34,7 +34,7 @@ static int -ask_for_card (const unsigned char *shadow_info, char **r_kid) +ask_for_card (CTRL ctrl, const unsigned char *shadow_info, char **r_kid) { int rc, i; const unsigned char *s; @@ -119,7 +119,7 @@ ask_for_card (const unsigned char *shadow_info, char **r_kid) } else { - rc = agent_get_confirmation (desc, NULL, NULL); + rc = agent_get_confirmation (ctrl, desc, NULL, NULL); free (desc); } } @@ -174,8 +174,7 @@ getpin_cb (void *opaque, const char *info, char *buf, size_t maxbuf) struct pin_entry_info_s *pi; int rc; char *desc; - - assert (!opaque); + CTRL ctrl = opaque; if (maxbuf < 2) return GNUPG_Invalid_Value; @@ -195,7 +194,7 @@ getpin_cb (void *opaque, const char *info, char *buf, size_t maxbuf) info? info:"", info? "')":"") < 0) desc = NULL; - rc = agent_askpin (desc?desc:info, pi); + rc = agent_askpin (ctrl, desc?desc:info, pi); free (desc); if (!rc) { @@ -210,7 +209,8 @@ getpin_cb (void *opaque, const char *info, char *buf, size_t maxbuf) int -divert_pksign (const unsigned char *digest, size_t digestlen, int algo, +divert_pksign (CTRL ctrl, + const unsigned char *digest, size_t digestlen, int algo, const unsigned char *shadow_info, unsigned char **r_sig) { int rc; @@ -220,7 +220,7 @@ divert_pksign (const unsigned char *digest, size_t digestlen, int algo, unsigned char *data; size_t ndata; - rc = ask_for_card (shadow_info, &kid); + rc = ask_for_card (ctrl, shadow_info, &kid); if (rc) return rc; @@ -229,7 +229,7 @@ divert_pksign (const unsigned char *digest, size_t digestlen, int algo, if (rc) return rc; - rc = agent_card_pksign (kid, getpin_cb, NULL, + rc = agent_card_pksign (kid, getpin_cb, ctrl, data, ndata, &sigval, &siglen); if (!rc) *r_sig = sigval; @@ -244,7 +244,8 @@ divert_pksign (const unsigned char *digest, size_t digestlen, int algo, key identified by SHADOW_INFO and return the plaintext in an allocated buffer in R_BUF. */ int -divert_pkdecrypt (const unsigned char *cipher, +divert_pkdecrypt (CTRL ctrl, + const unsigned char *cipher, const unsigned char *shadow_info, char **r_buf, size_t *r_len) { @@ -288,11 +289,11 @@ divert_pkdecrypt (const unsigned char *cipher, ciphertext = s; ciphertextlen = n; - rc = ask_for_card (shadow_info, &kid); + rc = ask_for_card (ctrl, shadow_info, &kid); if (rc) return rc; - rc = agent_card_pkdecrypt (kid, getpin_cb, NULL, + rc = agent_card_pkdecrypt (kid, getpin_cb, ctrl, ciphertext, ciphertextlen, &plaintext, &plaintextlen); if (!rc) diff --git a/agent/findkey.c b/agent/findkey.c index 8ec230fa0..96a1f360c 100644 --- a/agent/findkey.c +++ b/agent/findkey.c @@ -131,9 +131,10 @@ try_unprotect_cb (struct pin_entry_info_s *pi) /* Unprotect the canconical encoded S-expression key in KEYBUF. GRIP should be the hex encoded keygrip of that key to be used with the - cahing mechanism. */ + caching mechanism. */ static int -unprotect (unsigned char **keybuf, const unsigned char *grip, int ignore_cache) +unprotect (CTRL ctrl, + unsigned char **keybuf, const unsigned char *grip, int ignore_cache) { struct pin_entry_info_s *pi; struct try_unprotect_arg_s arg; @@ -176,7 +177,7 @@ unprotect (unsigned char **keybuf, const unsigned char *grip, int ignore_cache) arg.unprotected_key = NULL; pi->check_cb_arg = &arg; - rc = agent_askpin (NULL, pi); + rc = agent_askpin (ctrl, NULL, pi); if (!rc) { assert (arg.unprotected_key); @@ -197,7 +198,8 @@ unprotect (unsigned char **keybuf, const unsigned char *grip, int ignore_cache) With IGNORE_CACHE passed as true the passphrase is not taken from the cache.*/ GCRY_SEXP -agent_key_from_file (const unsigned char *grip, unsigned char **shadow_info, +agent_key_from_file (CTRL ctrl, + const unsigned char *grip, unsigned char **shadow_info, int ignore_cache) { int i, rc; @@ -271,7 +273,7 @@ agent_key_from_file (const unsigned char *grip, unsigned char **shadow_info, case PRIVATE_KEY_CLEAR: break; /* no unprotection needed */ case PRIVATE_KEY_PROTECTED: - rc = unprotect (&buf, grip, ignore_cache); + rc = unprotect (ctrl, &buf, grip, ignore_cache); if (rc) log_error ("failed to unprotect the secret key: %s\n", gnupg_strerror (rc)); diff --git a/agent/genkey.c b/agent/genkey.c index dccac94da..6def5cd8a 100644 --- a/agent/genkey.c +++ b/agent/genkey.c @@ -30,7 +30,7 @@ #include "i18n.h" static int -store_key (GCRY_SEXP private, const char *passphrase) +store_key (GCRY_SEXP private, const char *passphrase, int force) { int rc; char *buf; @@ -65,13 +65,13 @@ store_key (GCRY_SEXP private, const char *passphrase) buf = p; } - rc = agent_write_private_key (grip, buf, len, 0); + rc = agent_write_private_key (grip, buf, len, force); xfree (buf); return rc; } /* Callback function to compare the first entered PIN with the one - currently beeing entered. */ + currently being entered. */ static int reenter_compare_cb (struct pin_entry_info_s *pi) { @@ -119,9 +119,9 @@ agent_genkey (CTRL ctrl, const char *keyparam, size_t keyparamlen, pi2->check_cb = reenter_compare_cb; pi2->check_cb_arg = pi->pin; - rc = agent_askpin (text1, pi); + rc = agent_askpin (ctrl, text1, pi); if (!rc) - rc = agent_askpin (text2, pi2); + rc = agent_askpin (ctrl, text2, pi2); if (rc) return rc; if (!*pi->pin) @@ -162,7 +162,7 @@ agent_genkey (CTRL ctrl, const char *keyparam, size_t keyparamlen, /* store the secret key */ log_debug ("storing private key\n"); - rc = store_key (s_private, pi? pi->pin:NULL); + rc = store_key (s_private, pi? pi->pin:NULL, 0); xfree (pi); pi = NULL; gcry_sexp_release (s_private); if (rc) @@ -198,3 +198,41 @@ agent_genkey (CTRL ctrl, const char *keyparam, size_t keyparamlen, return 0; } + + +/* Apply a new passpahrse to the key S_SKEY and store it. */ +int +agent_protect_and_store (CTRL ctrl, GCRY_SEXP s_skey) +{ + struct pin_entry_info_s *pi, *pi2; + int rc; + + { + const char *text1 = _("Please enter the new passphrase"); + const char *text2 = _("Please re-enter this passphrase"); + + pi = gcry_calloc_secure (2, sizeof (*pi) + 100); + pi2 = pi + (sizeof *pi + 100); + pi->max_length = 100; + pi->max_tries = 3; + pi2->max_length = 100; + pi2->max_tries = 3; + pi2->check_cb = reenter_compare_cb; + pi2->check_cb_arg = pi->pin; + + rc = agent_askpin (ctrl, text1, pi); + if (!rc) + rc = agent_askpin (ctrl, text2, pi2); + if (rc) + return rc; + if (!*pi->pin) + { + xfree (pi); + pi = NULL; /* User does not want a passphrase. */ + } + } + + rc = store_key (s_skey, pi? pi->pin:NULL, 1); + xfree (pi); + return 0; +} diff --git a/agent/gpg-agent.c b/agent/gpg-agent.c index 3c823e5f6..780dfaddd 100644 --- a/agent/gpg-agent.c +++ b/agent/gpg-agent.c @@ -137,6 +137,18 @@ static int maybe_setuid = 1; /* Name of the communication socket */ static char socket_name[128]; +/* Default values for options passed to the pinentry. */ +static char *default_display; +static char *default_ttyname; +static char *default_ttytype; +static char *default_lc_ctype; +static char *default_lc_messages; + +/* Name of a config file, which will be reread on a HUP if it is not NULL. */ +static char *config_filename; + + +/* Local prototypes. */ static void create_directories (void); #ifdef USE_GNU_PTH static void handle_connections (int listen_fd); @@ -248,6 +260,51 @@ cleanup_sh (int sig) raise( sig ); } + +/* Handle options which are allowed to be reset after program start. + Return true when the current option in PARGS could be handled and + false if not. As a special feature, passing a value of NULL for + PARGS, resets the options to the default. */ +static int +parse_rereadable_options (ARGPARSE_ARGS *pargs) +{ + if (!pargs) + { /* reset mode */ + opt.quiet = 0; + opt.verbose = 0; + opt.debug = 0; + opt.no_grab = 0; + opt.pinentry_program = NULL; + opt.scdaemon_program = NULL; + opt.def_cache_ttl = 10*60; /* default to 10 minutes */ + opt.ignore_cache_for_signing = 0; + return 1; + } + + switch (pargs->r_opt) + { + case oQuiet: opt.quiet = 1; break; + case oVerbose: opt.verbose++; break; + + case oDebug: opt.debug |= pargs->r.ret_ulong; break; + case oDebugAll: opt.debug = ~0; break; + + case oNoGrab: opt.no_grab = 1; break; + + case oPinentryProgram: opt.pinentry_program = pargs->r.ret_str; break; + case oScdaemonProgram: opt.scdaemon_program = pargs->r.ret_str; break; + + case oDefCacheTTL: opt.def_cache_ttl = pargs->r.ret_ulong; break; + + case oIgnoreCacheForSigning: opt.ignore_cache_for_signing = 1; break; + + default: + return 0; /* not handled */ + } + return 1; /* handled */ +} + + int main (int argc, char **argv ) { @@ -288,15 +345,14 @@ main (int argc, char **argv ) } assuan_set_malloc_hooks (gcry_malloc, gcry_realloc, gcry_free); -#ifdef USE_GNU_PTH - assuan_set_io_func (pth_read, pth_write); -#endif gcry_set_log_handler (my_gcry_logger, NULL); gcry_control (GCRYCTL_USE_SECURE_RNDPOOL); may_coredump = disable_core_dumps (); + parse_rereadable_options (NULL); /* Reset them to default values. */ + shell = getenv ("SHELL"); if (shell && strlen (shell) >= 3 && !strcmp (shell+strlen (shell)-3, "csh") ) csh_style = 1; @@ -304,7 +360,6 @@ main (int argc, char **argv ) opt.homedir = getenv("GNUPGHOME"); if (!opt.homedir || !*opt.homedir) opt.homedir = GNUPG_DEFAULT_HOMEDIR; - opt.def_cache_ttl = 10*60; /* default to 10 minutes */ /* check whether we have a config file on the commandline */ @@ -375,14 +430,12 @@ main (int argc, char **argv ) while (optfile_parse( configfp, configname, &configlineno, &pargs, opts) ) { + if (parse_rereadable_options (&pargs)) + continue; /* Already handled */ switch (pargs.r_opt) { - case oQuiet: opt.quiet = 1; break; - case oVerbose: opt.verbose++; break; case oBatch: opt.batch=1; break; - case oDebug: opt.debug |= pargs.r.ret_ulong; break; - case oDebugAll: opt.debug = ~0; break; case oDebugWait: debug_wait = pargs.r.ret_int; break; case oOptions: @@ -399,7 +452,6 @@ main (int argc, char **argv ) case oNoOptions: break; /* no-options */ case oHomedir: opt.homedir = pargs.r.ret_str; break; case oNoDetach: nodetach = 1; break; - case oNoGrab: opt.no_grab = 1; break; case oLogFile: logfile = pargs.r.ret_str; break; case oCsh: csh_style = 1; break; case oSh: csh_style = 0; break; @@ -407,16 +459,12 @@ main (int argc, char **argv ) case oDaemon: is_daemon = 1; break; case oDisablePth: disable_pth = 1; break; - case oPinentryProgram: opt.pinentry_program = pargs.r.ret_str; break; - case oDisplay: opt.display = xstrdup (pargs.r.ret_str); break; - case oTTYname: opt.ttyname = xstrdup (pargs.r.ret_str); break; - case oTTYtype: opt.ttytype = xstrdup (pargs.r.ret_str); break; - case oLCctype: opt.lc_ctype = xstrdup (pargs.r.ret_str); break; - case oLCmessages: opt.lc_messages = xstrdup (pargs.r.ret_str); break; - case oScdaemonProgram: opt.scdaemon_program = pargs.r.ret_str; break; - case oDefCacheTTL: opt.def_cache_ttl = pargs.r.ret_ulong; break; + case oDisplay: default_display = xstrdup (pargs.r.ret_str); break; + case oTTYname: default_ttyname = xstrdup (pargs.r.ret_str); break; + case oTTYtype: default_ttytype = xstrdup (pargs.r.ret_str); break; + case oLCctype: default_lc_ctype = xstrdup (pargs.r.ret_str); break; + case oLCmessages: default_lc_messages = xstrdup (pargs.r.ret_str); break; - case oIgnoreCacheForSigning: opt.ignore_cache_for_signing = 1; break; case oKeepTTY: opt.keep_tty = 1; break; case oKeepDISPLAY: opt.keep_display = 1; break; @@ -427,7 +475,8 @@ main (int argc, char **argv ) { fclose( configfp ); configfp = NULL; - xfree(configname); + /* Keep a copy of the name so that it can be read on SIGHUP. */ + config_filename = configname; configname = NULL; goto next_pass; } @@ -465,7 +514,22 @@ main (int argc, char **argv ) sleep (debug_wait); log_debug ("... okay\n"); } + + if (!pipe_server && !is_daemon) + log_info (_("please use the option `--daemon'" + " to run the program in the background\n")); +#ifdef ENABLE_NLS + /* gpg-agent usdually does not ooutput any messages becuase it runs + in the background. For log files it is acceptable to have + messages always encoded in utf-8. We switch here to utf-8, so + that commands like --help still give native messages. It is far + easier to swicthnonly once instead of for every message and it + actually helps when more then one thread is active (avoids + required an extra copy step). */ + bind_textdomain_codeset (PACKAGE, "UTF-8"); +#endif + /* now start with logging to a file if this is desired */ if (logfile) { @@ -473,16 +537,18 @@ main (int argc, char **argv ) log_set_prefix (NULL, 1|2|4); } + /* Make sure that we have a default ttyname. */ + if (!default_ttyname && ttyname (1)) + default_ttyname = xstrdup (ttyname (1)); + if (!default_ttytype && getenv ("TERM")) + default_ttytype = xstrdup (getenv ("TERM")); if (pipe_server) { /* this is the simple pipe based server */ start_command_handler (-1, -1); } else if (!is_daemon) - { - log_info (_("please use the option `--daemon'" - " to run the program in the background\n")); - } + ; else { /* regular server mode */ int fd; @@ -491,6 +557,13 @@ main (int argc, char **argv ) struct sockaddr_un serv_addr; char *p; + /* Remove the DISPLAY variable so that a pinentry does not + default to a specific display. There is still a default + display when gpg-agent weas started using --display or a + client requested this using an OPTION command. */ + if (!opt.keep_display) + unsetenv ("DISPLAY"); + *socket_name = 0; snprintf (socket_name, DIM(socket_name)-1, "/tmp/gpg-XXXXXX/S.gpg-agent"); @@ -702,10 +775,76 @@ agent_exit (int rc) } +void +agent_init_default_ctrl (struct server_control_s *ctrl) +{ + /* Note we ignore malloc errors because we can't do much about it + and the request will fail anyway shortly after this + initialization. */ + if (ctrl->display) + free (ctrl->display); + ctrl->display = default_display? strdup (default_display) : NULL; + + if (ctrl->ttyname) + free (ctrl->ttyname); + ctrl->ttyname = default_ttyname? strdup (default_ttyname) : NULL; + + if (ctrl->ttytype) + free (ctrl->ttytype); + ctrl->ttytype = default_ttytype? strdup (default_ttytype) : NULL; + + if (ctrl->lc_ctype) + free (ctrl->lc_ctype); + ctrl->lc_ctype = default_lc_ctype? strdup (default_lc_ctype) : NULL; + + if (ctrl->lc_messages) + free (ctrl->lc_messages); + ctrl->lc_messages = default_lc_messages? strdup (default_lc_messages) : NULL; +} + + +/* Reread parts of the configuration. Note, that this function is + obviously not thread-safe and should only be called from the PTH + signal handler. + + Fixme: Due to the way the argument parsing works, we create a + memory leak here for all string type arguments. There is currently + no clean way to tell whether the memory for the argument has been + allocated or points into the process' original arguments. Unless + we have a mechanism to tell this, we need to live on with this. */ static void reread_configuration (void) { - /* FIXME: Move parts of the option parsing to here. */ + ARGPARSE_ARGS pargs; + FILE *fp; + unsigned int configlineno = 0; + int dummy; + + if (!config_filename) + return; /* No config file. */ + + fp = fopen (config_filename, "r"); + if (!fp) + { + log_error (_("option file `%s': %s\n"), + config_filename, strerror(errno) ); + return; + } + + parse_rereadable_options (NULL); /* Start from the default values. */ + + memset (&pargs, 0, sizeof pargs); + dummy = 0; + pargs.argc = &dummy; + pargs.flags = 1; /* do not remove the args */ + while (optfile_parse (fp, config_filename, &configlineno, &pargs, opts) ) + { + if (pargs.r_opt < -1) + pargs.err = 1; /* Print a warning. */ + else /* Try to parse this option - ignore unchangeable ones. */ + parse_rereadable_options (&pargs); + } + fclose (fp); } diff --git a/agent/pkdecrypt.c b/agent/pkdecrypt.c index 6add3d467..094736c69 100644 --- a/agent/pkdecrypt.c +++ b/agent/pkdecrypt.c @@ -64,7 +64,7 @@ agent_pkdecrypt (CTRL ctrl, const char *ciphertext, size_t ciphertextlen, log_printhex ("keygrip:", ctrl->keygrip, 20); log_printhex ("cipher: ", ciphertext, ciphertextlen); } - s_skey = agent_key_from_file (ctrl->keygrip, &shadow_info, 0); + s_skey = agent_key_from_file (ctrl, ctrl->keygrip, &shadow_info, 0); if (!s_skey && !shadow_info) { log_error ("failed to read the secret key\n"); @@ -81,7 +81,7 @@ agent_pkdecrypt (CTRL ctrl, const char *ciphertext, size_t ciphertextlen, goto leave; } - rc = divert_pkdecrypt (ciphertext, shadow_info, &buf, &len ); + rc = divert_pkdecrypt (ctrl, ciphertext, shadow_info, &buf, &len ); if (rc) { log_error ("smartcard decryption failed: %s\n", gnupg_strerror (rc)); diff --git a/agent/pksign.c b/agent/pksign.c index 2acc63dd9..1d84d4870 100644 --- a/agent/pksign.c +++ b/agent/pksign.c @@ -1,5 +1,5 @@ /* pksign.c - public key signing (well, acually using a secret key) - * Copyright (C) 2001 Free Software Foundation, Inc. + * Copyright (C) 2001, 2002 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -98,7 +98,8 @@ agent_pksign (CTRL ctrl, FILE *outfp, int ignore_cache) if (!ctrl->have_keygrip) return seterr (No_Secret_Key); - s_skey = agent_key_from_file (ctrl->keygrip, &shadow_info, ignore_cache); + s_skey = agent_key_from_file (ctrl, + ctrl->keygrip, &shadow_info, ignore_cache); if (!s_skey && !shadow_info) { log_error ("failed to read the secret key\n"); @@ -110,7 +111,8 @@ agent_pksign (CTRL ctrl, FILE *outfp, int ignore_cache) { /* divert operation to the smartcard */ unsigned char *sigbuf; - rc = divert_pksign (ctrl->digest.value, + rc = divert_pksign (ctrl, + ctrl->digest.value, ctrl->digest.valuelen, ctrl->digest.algo, shadow_info, &sigbuf); diff --git a/agent/protect-tool.c b/agent/protect-tool.c index 1d706ea2b..d501abd79 100644 --- a/agent/protect-tool.c +++ b/agent/protect-tool.c @@ -275,6 +275,8 @@ read_key (const char *fname) unsigned char *key; buf = read_file (fname, &buflen); + if (!buf) + return NULL; key = make_canonical (fname, buf, buflen); xfree (buf); return key; diff --git a/agent/query.c b/agent/query.c index ee18a1483..a20e4fe7b 100644 --- a/agent/query.c +++ b/agent/query.c @@ -1,5 +1,5 @@ /* query.c - fork of the pinentry to query stuff from the user - * Copyright (C) 2001 Free Software Foundation, Inc. + * Copyright (C) 2001, 2002 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -56,9 +56,15 @@ struct entry_parm_s { +/* Unlock the pinentry so that another thread can start one and + disconnect that pinentry - we do this after the unlock so that a + stalled pinentry does not block other threads. Fixme: We should + have a timeout in Assuan for the disconnetc operation. */ static int unlock_pinentry (int rc) { + ASSUAN_CONTEXT ctx = entry_ctx; + #ifdef USE_GNU_PTH if (!pth_mutex_release (&entry_lock)) { @@ -67,6 +73,8 @@ unlock_pinentry (int rc) rc = GNUPG_Internal_Error; } #endif + entry_ctx = NULL; + assuan_disconnect (ctx); return rc; } @@ -75,7 +83,7 @@ unlock_pinentry (int rc) pinentry - we will serialize _all_ pinentry calls. */ static int -start_pinentry (void) +start_pinentry (CTRL ctrl) { int rc; const char *pgmname; @@ -96,7 +104,7 @@ start_pinentry (void) return 0; if (opt.verbose) - log_info ("no running PIN Entry - starting it\n"); + log_info ("starting a new PIN Entry\n"); if (fflush (NULL)) { @@ -111,12 +119,11 @@ start_pinentry (void) else pgmname++; - /* FIXME: We must do this thread specific */ argv[0] = pgmname; - if (opt.display && !opt.keep_display) + if (ctrl->display && !opt.keep_display) { argv[1] = "--display"; - argv[2] = opt.display; + argv[2] = ctrl->display; argv[3] = NULL; } else @@ -150,10 +157,10 @@ start_pinentry (void) NULL, NULL, NULL, NULL, NULL, NULL); if (rc) return unlock_pinentry (map_assuan_err (rc)); - if (opt.ttyname && !opt.keep_tty) + if (ctrl->ttyname) { char *optstr; - if (asprintf (&optstr, "OPTION ttyname=%s", opt.ttyname) < 0 ) + if (asprintf (&optstr, "OPTION ttyname=%s", ctrl->ttyname) < 0 ) return unlock_pinentry (GNUPG_Out_Of_Core); rc = assuan_transact (entry_ctx, optstr, NULL, NULL, NULL, NULL, NULL, NULL); @@ -161,30 +168,30 @@ start_pinentry (void) if (rc) return unlock_pinentry (map_assuan_err (rc)); } - if (opt.ttytype && !opt.keep_tty) + if (ctrl->ttytype) { char *optstr; - if (asprintf (&optstr, "OPTION ttytype=%s", opt.ttytype) < 0 ) + if (asprintf (&optstr, "OPTION ttytype=%s", ctrl->ttytype) < 0 ) return unlock_pinentry (GNUPG_Out_Of_Core); rc = assuan_transact (entry_ctx, optstr, NULL, NULL, NULL, NULL, NULL, NULL); if (rc) return unlock_pinentry (map_assuan_err (rc)); } - if (opt.lc_ctype) + if (ctrl->lc_ctype) { char *optstr; - if (asprintf (&optstr, "OPTION lc-ctype=%s", opt.lc_ctype) < 0 ) + if (asprintf (&optstr, "OPTION lc-ctype=%s", ctrl->lc_ctype) < 0 ) return unlock_pinentry (GNUPG_Out_Of_Core); rc = assuan_transact (entry_ctx, optstr, NULL, NULL, NULL, NULL, NULL, NULL); if (rc) return unlock_pinentry (map_assuan_err (rc)); } - if (opt.lc_messages) + if (ctrl->lc_messages) { char *optstr; - if (asprintf (&optstr, "OPTION lc-messages=%s", opt.lc_messages) < 0 ) + if (asprintf (&optstr, "OPTION lc-messages=%s", ctrl->lc_messages) < 0 ) return unlock_pinentry (GNUPG_Out_Of_Core); rc = assuan_transact (entry_ctx, optstr, NULL, NULL, NULL, NULL, NULL, NULL); @@ -230,7 +237,8 @@ all_digitsp( const char *s) number here and repeat it as long as we have invalid formed numbers. */ int -agent_askpin (const char *desc_text, struct pin_entry_info_s *pininfo) +agent_askpin (CTRL ctrl, + const char *desc_text, struct pin_entry_info_s *pininfo) { int rc; char line[ASSUAN_LINELENGTH]; @@ -252,7 +260,7 @@ agent_askpin (const char *desc_text, struct pin_entry_info_s *pininfo) is_pin = desc_text && strstr (desc_text, "PIN"); - rc = start_pinentry (); + rc = start_pinentry (ctrl); if (rc) return rc; @@ -335,7 +343,8 @@ agent_askpin (const char *desc_text, struct pin_entry_info_s *pininfo) passphrase is returned in RETPASS as an hex encoded string to be freed by the caller */ int -agent_get_passphrase (char **retpass, const char *desc, const char *prompt, +agent_get_passphrase (CTRL ctrl, + char **retpass, const char *desc, const char *prompt, const char *errtext) { @@ -349,7 +358,7 @@ agent_get_passphrase (char **retpass, const char *desc, const char *prompt, if (opt.batch) return GNUPG_Bad_Passphrase; - rc = start_pinentry (); + rc = start_pinentry (ctrl); if (rc) return rc; @@ -417,12 +426,13 @@ agent_get_passphrase (char **retpass, const char *desc, const char *prompt, confirmed it, GNUPG_Not_Confirmed for what the text says or an other error. */ int -agent_get_confirmation (const char *desc, const char *ok, const char *cancel) +agent_get_confirmation (CTRL ctrl, + const char *desc, const char *ok, const char *cancel) { int rc; char line[ASSUAN_LINELENGTH]; - rc = start_pinentry (); + rc = start_pinentry (ctrl); if (rc) return rc; diff --git a/agent/simple-pwquery.c b/agent/simple-pwquery.c index 5bb08afec..afdc4e2a4 100644 --- a/agent/simple-pwquery.c +++ b/agent/simple-pwquery.c @@ -177,9 +177,10 @@ agent_send_all_options (int fd) return rc; } - if (ttyname (1)) - dft_ttyname = ttyname (1); - if (dft_ttyname) + dft_ttyname = getenv ("GPG_TTY"); + if ((!dft_ttyname || !*dft_ttyname) && ttyname (0)) + dft_ttyname = ttyname (0); + if (dft_ttyname && *dft_ttyname) { if ((rc=agent_send_option (fd, "ttyname", dft_ttyname))) return rc; diff --git a/agent/trustlist.c b/agent/trustlist.c index b6545f7de..9fc4623c6 100644 --- a/agent/trustlist.c +++ b/agent/trustlist.c @@ -228,7 +228,7 @@ agent_listtrusted (void *assuan_context) whether this is actual wants he want to do. */ int -agent_marktrusted (const char *name, const char *fpr, int flag) +agent_marktrusted (CTRL ctrl, const char *name, const char *fpr, int flag) { int rc; static char key[41]; @@ -254,7 +254,7 @@ agent_marktrusted (const char *name, const char *fpr, int flag) "has the fingerprint:%%0A" " %s", name, fpr) < 0 ) return GNUPG_Out_Of_Core; - rc = agent_get_confirmation (desc, "Correct", "No"); + rc = agent_get_confirmation (ctrl, desc, "Correct", "No"); free (desc); if (rc) return rc; @@ -265,7 +265,7 @@ agent_marktrusted (const char *name, const char *fpr, int flag) "to correctly certify user certificates?", name) < 0 ) return GNUPG_Out_Of_Core; - rc = agent_get_confirmation (desc, "Yes", "No"); + rc = agent_get_confirmation (ctrl, desc, "Yes", "No"); free (desc); if (rc) return rc; diff --git a/common/ChangeLog b/common/ChangeLog index ff886650d..e202658b0 100644 --- a/common/ChangeLog +++ b/common/ChangeLog @@ -1,3 +1,14 @@ +2002-10-31 Neal H. Walfield + + * isascii.c: New file. + * putc_unlocked.c: Likewise. + +2002-10-28 Neal H. Walfield + + * signal.c (caught_fatal_sig): Remove superfluous zero + initializer. + (caught_sigusr1): Likewise. + 2002-09-04 Neal H. Walfield * vasprintf.c (vasprintf) [va_copy]: Use va_copy. diff --git a/common/isascii.c b/common/isascii.c new file mode 100644 index 000000000..565c71664 --- /dev/null +++ b/common/isascii.c @@ -0,0 +1,29 @@ +/* isascii.c - Replacement for isascii. + * 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 + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +int +isascii (int c) +{ + return (((c) & ~0x7f) == 0); +} diff --git a/common/putc_unlocked.c b/common/putc_unlocked.c new file mode 100644 index 000000000..02c646130 --- /dev/null +++ b/common/putc_unlocked.c @@ -0,0 +1,31 @@ +/* putc_unlocked.c - Replacement for putc_unlocked. + * 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 + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include + +int +putc_unlocked (int c, FILE *stream) +{ + return putc (c, stream); +} diff --git a/common/signal.c b/common/signal.c index b150fa90a..dc026c10f 100644 --- a/common/signal.c +++ b/common/signal.c @@ -30,8 +30,8 @@ #include "util.h" -static volatile int caught_fatal_sig = 0; -static volatile int caught_sigusr1 = 0; +static volatile int caught_fatal_sig; +static volatile int caught_sigusr1; static void (*cleanup_fnc)(void); diff --git a/kbx/ChangeLog b/kbx/ChangeLog index 351ec5d13..14c225fea 100644 --- a/kbx/ChangeLog +++ b/kbx/ChangeLog @@ -1,3 +1,12 @@ +2002-11-14 Werner Koch + + * keybox-search.c (blob_cmp_name) : Fixed + length compare; there is no 0 stored since nearly a year. + +2002-10-31 Neal H. Walfield + + * Makefile.am (AM_CPPFLAGS): Fix ytpo. + 2002-08-10 Werner Koch * keybox-search.c (blob_cmp_fpr_part): New. @@ -16,7 +25,7 @@ 2002-06-19 Werner Koch * keybox-init.c (keybox_set_ephemeral): New. - * keybox-blob.c (create_blob_header): Store epheermal flag. + * keybox-blob.c (create_blob_header): Store ephemeral flag. (_keybox_create_x509_blob): Pass epheermal flag on. * keybox-update.c (keybox_insert_cert): Ditto. * keybox-search.c (blob_get_blob_flags): New. diff --git a/kbx/Makefile.am b/kbx/Makefile.am index 4e7170379..78d5df26d 100644 --- a/kbx/Makefile.am +++ b/kbx/Makefile.am @@ -1,5 +1,5 @@ # Keybox Makefile -# Copyright (C) 2001 Free Software Foundation, Inc. +# Copyright (C) 2001, 2002 Free Software Foundation, Inc. # # This file is part of GnuPG. # @@ -23,7 +23,7 @@ localedir = $(datadir)/locale INCLUDES = -I../intl -DLOCALEDIR=\"$(localedir)\" EXTRA_DIST = mkerrors -AM_CPPFLAGS = $(KSBA_CFLAGS) $(LIBGCRYPT_CLFAGS) +AM_CPPFLAGS = $(KSBA_CFLAGS) $(LIBGCRYPT_CFLAGS) BUILT_SOURCES = keybox-errors.c noinst_LIBRARIES = libkeybox.a diff --git a/kbx/keybox-search.c b/kbx/keybox-search.c index 126364f5b..a96b0c205 100644 --- a/kbx/keybox-search.c +++ b/kbx/keybox-search.c @@ -234,9 +234,8 @@ blob_cmp_name (KEYBOXBLOB blob, int idx, len = get32 (buffer+mypos+4); if (off+len > length) return 0; /* error: better stop here out of bounds */ - if (len < 2) - continue; /* empty name or 0 not stored */ - len--; + if (len < 1) + continue; /* empty name */ if (substr) { if (ascii_memcasemem (buffer+off, len, name, namelen)) diff --git a/sm/ChangeLog b/sm/ChangeLog index 8e11e0ddf..0a9150726 100644 --- a/sm/ChangeLog +++ b/sm/ChangeLog @@ -1,3 +1,35 @@ +2002-12-03 Werner Koch + + * 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 + + * verify.c (gpgsm_verify): Handle content-type attribute. + +2002-11-13 Werner Koch + + * 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 + + * 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 + + * certcheck.c (gpgsm_check_cms_signature): Don't double free + s_sig but free s_pkey at leave. + +2002-11-10 Werner Koch + + * gpgsm.c: Removed duplicate --list-secret-key entry. + 2002-09-19 Werner Koch * certcheck.c (gpgsm_check_cert_sig): Add cert hash debugging. diff --git a/sm/call-agent.c b/sm/call-agent.c index 6cb2fb505..d518b2bc5 100644 --- a/sm/call-agent.c +++ b/sm/call-agent.c @@ -248,8 +248,12 @@ start_agent (void) if (rc) return map_assuan_err (rc); } - if (!opt.ttyname && ttyname (1)) - dft_ttyname = ttyname (1); + 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; @@ -749,3 +753,25 @@ gpgsm_agent_learn () 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 GNUPG_Invalid_Value; + + snprintf (line, DIM(line)-1, "PASSWD %s", hexkeygrip); + line[DIM(line)-1] = 0; + + rc = assuan_transact (agent_ctx, line, NULL, NULL, NULL, NULL, NULL, NULL); + return map_assuan_err (rc); +} + diff --git a/sm/call-dirmngr.c b/sm/call-dirmngr.c index a1d94e25b..6c1156a83 100644 --- a/sm/call-dirmngr.c +++ b/sm/call-dirmngr.c @@ -26,6 +26,7 @@ #include #include #include +#include #include @@ -59,7 +60,9 @@ struct lookup_parm_s { int error; }; - +struct run_command_parm_s { + ASSUAN_CONTEXT ctx; +}; /* A simple implementation of a dynamic buffer. Use init_membuf() to @@ -452,7 +455,7 @@ lookup_status_cb (void *opaque, const char *line) } -/* Run the Directroy Managers lookup command using the apptern +/* 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. */ @@ -493,3 +496,136 @@ gpgsm_dirmngr_lookup (CTRL ctrl, STRLIST names, } + +/* 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", gnupg_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 GNUPG_Out_Of_Core; + + p = stpcpy (line, command); + for (i=0; i < argc; i++) + { + *p++ = ' '; + for (s=argv[i]; *s; s++) + { + if (!isascii (*s)) + *p++ = *s; + else if (*s == ' ') + *p++ = '+'; + else if (!isprint (*s) || *s == '+') + { + sprintf (p, "%%%02X", *s); + p += 3; + } + else + *p++ = *s; + } + } + *p = 0; + + rc = assuan_transact (dirmngr_ctx, line, + run_command_cb, NULL, + run_command_inq_cb, &parm, + run_command_status_cb, NULL); + xfree (line); + log_info ("response of dirmngr: %s\n", rc? assuan_strerror (rc): "okay"); + return map_assuan_err (rc); +} diff --git a/sm/certcheck.c b/sm/certcheck.c index 37d2b00df..1f9054b13 100644 --- a/sm/certcheck.c +++ b/sm/certcheck.c @@ -266,13 +266,12 @@ gpgsm_check_cms_signature (KsbaCert cert, KsbaConstSexp sigval, 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", gcry_strerror (rc)); gcry_sexp_release (s_sig); gcry_sexp_release (s_hash); - gcry_sexp_release (s_sig); + gcry_sexp_release (s_pkey); return map_gcry_err (rc); } diff --git a/sm/gpgsm.c b/sm/gpgsm.c index bfcdeb741..1583787a1 100644 --- a/sm/gpgsm.c +++ b/sm/gpgsm.c @@ -77,6 +77,9 @@ enum cmd_and_opt_values { aCheckKeys, /* nyi */ aServer, aLearnCard, + aCallDirmngr, + aCallProtectTool, + aPasswd, oOptions, oDebug, @@ -139,7 +142,6 @@ enum cmd_and_opt_values { oCipherAlgo, oDigestAlgo, oCompressAlgo, - oPasswdFD, oCommandFD, oNoVerbose, oTrustDBName, @@ -219,7 +221,6 @@ static ARGPARSE_OPTS opts[] = { { aListSigs, "list-sigs", 256, N_("list certificate chain")}, { aListSigs, "check-sigs",256, "@"}, { oFingerprint, "fingerprint", 256, N_("list keys and fingerprints")}, - { aListSecretKeys, "list-secret-keys", 256, N_("list secret keys")}, { aKeygen, "gen-key", 256, N_("generate a new key pair")}, { aDeleteKey, "delete-key",256, N_("remove key from the public keyring")}, { aSendKeys, "send-keys" , 256, N_("export keys to a key server") }, @@ -228,7 +229,10 @@ static ARGPARSE_OPTS opts[] = { { aExport, "export", 256 , N_("export certificates")}, { aLearnCard, "learn-card", 256 ,N_("register a smartcard")}, { aServer, "server", 256, N_("run in server mode")}, - { oLogFile, "log-file" ,2, N_("use a log file for the server")}, + { aCallDirmngr, "call-dirmngr", 256, N_("pass a command to the dirmngr")}, + { aCallProtectTool, "call-protect-tool", 256, + N_("invoke gpg-protect-tool")}, + { aPasswd, "passwd", 256, N_("change a passphrase")}, { 301, NULL, 0, N_("@\nOptions:\n ") }, @@ -283,6 +287,7 @@ static ARGPARSE_OPTS opts[] = { { oVerbose, "verbose", 0, N_("verbose") }, { oQuiet, "quiet", 0, N_("be somewhat more quiet") }, { oNoTTY, "no-tty", 0, N_("don't use the terminal at all") }, + { oLogFile, "log-file" ,2, N_("use a log file for the server")}, #if 0 { oForceV3Sigs, "force-v3-sigs", 0, N_("force v3 signatures") }, { oForceMDC, "force-mdc", 0, N_("always use a MDC for encryption") }, @@ -406,6 +411,7 @@ static void emergency_cleanup (void); static int check_special_filename (const char *fname); static int open_read (const char *filename); static FILE *open_fwrite (const char *filename); +static void run_protect_tool (int argc, char **argv); static int @@ -589,6 +595,7 @@ main ( int argc, char **argv) char *configname = NULL; unsigned configlineno; int parse_debug = 0; + int no_more_options = 0; int default_config =1; int default_keyring = 1; char *logfile = NULL; @@ -666,6 +673,9 @@ main ( int argc, char **argv) default_config = 0; /* --no-options */ else if (pargs.r_opt == oHomedir) opt.homedir = pargs.r.ret_str; + else if (pargs.r_opt == aCallProtectTool) + break; /* This break makes sure that --version and --help are + passed to the protect-tool. */ } @@ -724,7 +734,8 @@ main ( int argc, char **argv) default_config = 0; } - while (optfile_parse (configfp, configname, &configlineno, &pargs, opts)) + while (!no_more_options + && optfile_parse (configfp, configname, &configlineno, &pargs, opts)) { switch (pargs.r_opt) { @@ -732,7 +743,17 @@ main ( int argc, char **argv) opt.batch = 1; set_cmd (&cmd, aServer); break; + case aCallDirmngr: + opt.batch = 1; + set_cmd (&cmd, aCallDirmngr); + break; + case aCallProtectTool: + opt.batch = 1; + set_cmd (&cmd, aCallProtectTool); + no_more_options = 1; /* Stop parsing. */ + break; + case aCheckKeys: set_cmd (&cmd, aCheckKeys); break; case aImport: set_cmd (&cmd, aImport); break; case aSendKeys: set_cmd (&cmd, aSendKeys); break; @@ -744,6 +765,8 @@ main ( int argc, char **argv) case aListSigs: set_cmd (&cmd, aListSigs); break; case aLearnCard: set_cmd (&cmd, aLearnCard); break; + + case aPasswd: set_cmd (&cmd, aPasswd); break; case aDeleteKey: set_cmd (&cmd, aDeleteKey); @@ -1128,6 +1151,18 @@ main ( int argc, char **argv) gpgsm_server (); break; + case aCallDirmngr: + if (!argc) + wrong_args (_("--call-dirmngr {args}")); + else + if (gpgsm_dirmngr_run_command (&ctrl, *argv, argc-1, argv+1)) + gpgsm_exit (1); + break; + + case aCallProtectTool: + run_protect_tool (argc, argv); + break; + case aEncr: /* encrypt the given file */ if (!argc) gpgsm_encrypt (&ctrl, recplist, 0, stdout); /* from stdin */ @@ -1258,6 +1293,28 @@ main ( int argc, char **argv) } break; + case aPasswd: + if (argc != 1) + wrong_args ("--passwd "); + else + { + int rc; + KsbaCert cert = NULL; + char *grip = NULL; + + rc = gpgsm_find_cert (*argv, &cert); + if (rc) + ; + else if (!(grip = gpgsm_get_keygrip_hexstring (cert))) + rc = GNUPG_Bug; + else + rc = gpgsm_agent_passwd (grip); + if (rc) + log_error ("error changing passphrase: %s\n", gnupg_strerror (rc)); + xfree (grip); + ksba_cert_release (cert); + } + break; default: log_error ("invalid command (there is no implicit command)\n"); @@ -1386,3 +1443,24 @@ open_fwrite (const char *filename) } return fp; } + + +static void +run_protect_tool (int argc, char **argv) +{ + char *pgm = GNUPG_PROTECT_TOOL; + char **av; + int i; + + av = xcalloc (argc+2, sizeof *av); + av[0] = strrchr (pgm, '/'); + if (!av[0]) + av[0] = pgm; + for (i=1; argc; i++, argc--, argv++) + av[i] = *argv; + av[i] = NULL; + execv (pgm, av); + log_error ("error executing `%s': %s\n", pgm, strerror (errno)); + gpgsm_exit (2); +} + diff --git a/sm/gpgsm.h b/sm/gpgsm.h index 50590206e..dd91e3764 100644 --- a/sm/gpgsm.h +++ b/sm/gpgsm.h @@ -248,11 +248,14 @@ 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); diff --git a/sm/verify.c b/sm/verify.c index df7c8bfe8..3461d68af 100644 --- a/sm/verify.c +++ b/sm/verify.c @@ -257,6 +257,7 @@ gpgsm_verify (CTRL ctrl, int in_fd, int data_fd, FILE *out_fp) 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) @@ -313,6 +314,35 @@ gpgsm_verify (CTRL ctrl, int in_fd, int data_fd, FILE *out_fp) 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) {