1
0
mirror of git://git.gnupg.org/gnupg.git synced 2024-12-23 10:29:58 +01:00
gnupg/agent/call-pinentry.c

1684 lines
49 KiB
C
Raw Normal View History

/* call-pinentry.c - Spawn the pinentry to query stuff from the user
* Copyright (C) 2001, 2002, 2004, 2007, 2008,
* 2010 Free Software Foundation, Inc.
*
* This file is part of GnuPG.
*
* GnuPG is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
2007-07-04 19:49:40 +00:00
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* GnuPG is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see <https://www.gnu.org/licenses/>.
*/
#include <config.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <unistd.h>
#include <sys/stat.h>
2005-11-28 11:52:25 +00:00
#ifndef HAVE_W32_SYSTEM
# include <sys/wait.h>
# include <sys/types.h>
# include <signal.h>
# include <sys/utsname.h>
#endif
#include <npth.h>
#include "agent.h"
2009-09-23 Marcus Brinkmann <marcus@g10code.de> * configure.ac (NEED_LIBASSUAN_API, NEED_LIBASSUAN_VERSION): Update to new API (2, 1.1.0). agent/ 2009-09-23 Marcus Brinkmann <marcus@g10code.de> * gpg-agent.c (parse_rereadable_options): Don't set global assuan log file (there ain't one anymore). (main): Update to new API. (check_own_socket_pid_cb): Return gpg_error_t instead of int. (check_own_socket_thread, check_for_running_agent): Create assuan context before connecting to server. * command.c: Include "scdaemon.h" before <assuan.h> because of GPG_ERR_SOURCE_DEFAULT check. (write_and_clear_outbuf): Use gpg_error_t instead of assuan_error_t. (cmd_geteventcounter, cmd_istrusted, cmd_listtrusted) (cmd_marktrusted, cmd_havekey, cmd_sigkey, cmd_setkeydesc) (cmd_sethash, cmd_pksign, cmd_pkdecrypt, cmd_genkey, cmd_readkey) (cmd_keyinfo, cmd_get_passphrase, cmd_clear_passphrase) (cmd_get_confirmation, cmd_learn, cmd_passwd) (cmd_preset_passphrase, cmd_scd, cmd_getval, cmd_putval) (cmd_updatestartuptty, cmd_killagent, cmd_reloadagent) (cmd_getinfo, option_handler): Return gpg_error_t instead of int. (post_cmd_notify): Change type of ERR to gpg_error_t from int. (io_monitor): Add hook argument. Use symbols for constants. (register_commands): Change return type of HANDLER to gpg_error_t. (start_command_handler): Allocate assuan context before starting server. * call-pinentry.c: Include "scdaemon.h" before <assuan.h> because of GPG_ERR_SOURCE_DEFAULT check. (unlock_pinentry): Call assuan_release instead of assuan_disconnect. (getinfo_pid_cb, getpin_cb): Return gpg_error_t instead of int. (start_pinentry): Allocate assuan context before connecting to server. * call-scd.c (membuf_data_cb, learn_status_cb, get_serialno_cb) (membuf_data_cb, inq_needpin, card_getattr_cb, pass_status_thru) (pass_data_thru): Change return type to gpg_error_t. (start_scd): Allocate assuan context before connecting to server. common/ 2009-09-23 Marcus Brinkmann <marcus@g10code.de> * asshelp.c (start_new_gpg_agent): Allocate assuan context before starting server. g10/ 2009-09-23 Marcus Brinkmann <marcus@g10code.de> * call-agent.c: Include "scdaemon.h" before <assuan.h> because of GPG_ERR_SOURCE_DEFAULT check. (learn_status_cb, dummy_data_cb, get_serialno_cb, default_inq_cb) (learn_status_cb, inq_writecert_parms, inq_writekey_parms) (scd_genkey_cb, membuf_data_cb): Return gpg_error_t instead of int. * gpg.c: Include "scdaemon.h" before <assuan.h> because of GPG_ERR_SOURCE_DEFAULT check. (main): Update to new Assuan API. * server.c: Include "scdaemon.h" before <assuan.h> because of GPG_ERR_SOURCE_DEFAULT check. (option_handler, cmd_recipient, cmd_signer, cmd_encrypt) (cmd_decrypt, cmd_verify, cmd_sign, cmd_import, cmd_export) (cmd_delkeys, cmd_message, do_listkeys, cmd_listkeys) (cmd_listsecretkeys, cmd_genkey, cmd_getinfo): Return gpg_error_t instead of int. (register_commands): Allocate assuan context before starting server. (gpg_server): Allocate assuan_context before starting server. scd/ 2009-09-23 Marcus Brinkmann <marcus@g10code.de> * command.c: Include "scdaemon.h" before <assuan.h> because of GPG_ERR_SOURCE_DEFAULT check. (option_handler, open_card, cmd_serialno, cmd_lean, cmd_readcert) (cmd_readkey, cmd_setdata, cmd_pksign, cmd_pkauth, cmd_pkdecrypt) (cmd_getattr, cmd_setattr, cmd_writecert, cmd_writekey) (cmd_genkey, cmd_random, cmd_passwd, cmd_checkpin, cmd_lock) (cmd_unlock, cmd_getinfo, cmd_restart, cmd_disconnect, cmd_apdu) (cmd_killscd): Return gpg_error_t instead of int. (scd_command_handler): Allocate assuan context before starting server. * scdaemon.c (main): Update to new Assuan API. sm/ 2009-09-23 Marcus Brinkmann <marcus@g10code.de> * gpgsm.c (main): Update to new assuan API. * server.c: Include "gpgsm.h" before <assuan.h> due to check for GPG_ERR_SOURCE_DEFAULT and assuan.h now including gpg-error.h. (option_handler, cmd_recipient, cmd_signer, cmd_encrypt) (cmd_decrypt, cmd_verify, cmd_sign, cmd_import, cmd_export) (cmd_delkeys, cmd_message, cmd_listkeys, cmd_dumpkeys) (cmd_listsecretkeys, cmd_dumpsecretkeys, cmd_genkey) (cmd_getauditlog, cmd_getinfo): Return gpg_error_t instead of int. (register_commands): Same for member HANDLER in table. (gpgsm_server): Allocate assuan context before starting server. * sm/call-dirmngr.c: * call-dirmngr.c (prepare_dirmngr): Check for CTX and error before setting LDAPSERVER. (start_dirmngr_ext): Allocate assuan context before starting server. (inq_certificate, isvalid_status_cb, lookup_cb, lookup_status_cb) (run_command_cb, run_command_inq_cb, run_command_status_cb): Return gpg_error_t instead of int. tools/ 2009-09-23 Marcus Brinkmann <marcus@g10code.de> * gpg-connect-agent.c (getinfo_pid_cb, read_and_print_response) (main): Update to new Assuan API.
2009-09-23 00:01:25 +00:00
#include <assuan.h>
#include "../common/sysutils.h"
#include "../common/i18n.h"
#ifdef _POSIX_OPEN_MAX
#define MAX_OPEN_FDS _POSIX_OPEN_MAX
#else
#define MAX_OPEN_FDS 20
#endif
/* Because access to the pinentry must be serialized (it is and shall
be a global mutually exclusive dialog) we better timeout pending
requests after some time. 1 minute seem to be a reasonable
time. */
#define LOCK_TIMEOUT (1*60)
2005-11-28 11:52:25 +00:00
/* The assuan context of the current pinentry. */
static assuan_context_t entry_ctx;
/* A list of features of the current pinentry. */
static struct
{
/* The Pinentry support RS+US tabbing. This means that a RS (0x1e)
* starts a new tabbing block in which a US (0x1f) followed by a
* colon marks a colon. A pinentry can use this to pretty print
* name value pairs. */
unsigned int tabbing:1;
} entry_features;
2005-11-28 11:52:25 +00:00
/* A mutex used to serialize access to the pinentry. */
static npth_mutex_t entry_lock;
2005-11-28 11:52:25 +00:00
/* The thread ID of the popup working thread. */
static npth_t popup_tid;
2005-11-28 11:52:25 +00:00
/* A flag used in communication between the popup working thread and
its stop function. */
static int popup_finished;
/* Data to be passed to our callbacks, */
struct entry_parm_s
{
int lines;
size_t size;
unsigned char *buffer;
int status;
};
/* This function must be called once to initialize this module. This
has to be done before a second thread is spawned. We can't do the
static initialization because Pth emulation code might not be able
2005-11-28 11:52:25 +00:00
to do a static init; in particular, it is not possible for W32. */
void
initialize_module_call_pinentry (void)
{
static int initialized;
int err;
if (!initialized)
2005-11-28 11:52:25 +00:00
{
err = npth_mutex_init (&entry_lock, NULL);
if (err)
log_fatal ("error initializing mutex: %s\n", strerror (err));
initialized = 1;
2005-11-28 11:52:25 +00:00
}
}
/* This function may be called to print information pertaining to the
current state of this module to the log. */
void
agent_query_dump_state (void)
{
log_info ("agent_query_dump_state: entry_ctx=%p pid=%ld popup_tid=%p\n",
entry_ctx, (long)assuan_get_pid (entry_ctx), (void*)popup_tid);
2005-11-28 11:52:25 +00:00
}
/* Called to make sure that a popup window owned by the current
connection gets closed. */
void
agent_reset_query (ctrl_t ctrl)
{
if (entry_ctx && popup_tid && ctrl->pinentry_active)
2005-11-28 11:52:25 +00:00
{
agent_popup_message_stop (ctrl);
}
}
/* 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 disconnect operation. */
static gpg_error_t
unlock_pinentry (ctrl_t ctrl, gpg_error_t rc)
{
assuan_context_t ctx = entry_ctx;
int err;
if (rc)
{
if (DBG_IPC)
log_debug ("error calling pinentry: %s <%s>\n",
gpg_strerror (rc), gpg_strsource (rc));
/* Change the source of the error to pinentry so that the final
consumer of the error code knows that the problem is with
pinentry. For backward compatibility we do not do that for
some common error codes. */
switch (gpg_err_code (rc))
{
case GPG_ERR_NO_PIN_ENTRY:
case GPG_ERR_CANCELED:
case GPG_ERR_FULLY_CANCELED:
case GPG_ERR_ASS_UNKNOWN_INQUIRE:
case GPG_ERR_ASS_TOO_MUCH_DATA:
case GPG_ERR_NO_PASSPHRASE:
case GPG_ERR_BAD_PASSPHRASE:
case GPG_ERR_BAD_PIN:
break;
case GPG_ERR_CORRUPTED_PROTECTION:
/* This comes from gpg-agent. */
break;
default:
rc = gpg_err_make (GPG_ERR_SOURCE_PINENTRY, gpg_err_code (rc));
break;
}
}
if (--ctrl->pinentry_active == 0)
{
entry_ctx = NULL;
err = npth_mutex_unlock (&entry_lock);
if (err)
{
log_error ("failed to release the entry lock: %s\n", strerror (err));
if (!rc)
rc = gpg_error_from_errno (err);
}
assuan_release (ctx);
}
return rc;
}
/* Helper for at_fork_cb which can also be called by the parent to
* show shich envvars will be set. */
static void
atfork_core (ctrl_t ctrl, int debug_mode)
{
int iterator = 0;
const char *name, *assname, *value;
while ((name = session_env_list_stdenvnames (&iterator, &assname)))
{
/* For all new envvars (!ASSNAME) and the two medium old ones
* which do have an assuan name but are conveyed using
* environment variables, update the environment of the forked
* process. */
if (!assname
|| !strcmp (name, "XAUTHORITY")
|| !strcmp (name, "PINENTRY_USER_DATA"))
{
value = session_env_getenv (ctrl->session_env, name);
if (value)
{
if (debug_mode)
log_debug ("pinentry: atfork used setenv(%s,%s)\n",name,value);
else
gnupg_setenv (name, value, 1);
}
}
}
}
/* To make sure we leave no secrets in our image after forking of the
pinentry, we use this callback. */
static void
atfork_cb (void *opaque, int where)
{
ctrl_t ctrl = opaque;
if (!where)
{
gcry_control (GCRYCTL_TERM_SECMEM);
atfork_core (ctrl, 0);
}
}
/* Status line callback for the FEATURES status. */
static gpg_error_t
getinfo_features_cb (void *opaque, const char *line)
{
const char *args;
char **tokens;
int i;
(void)opaque;
if ((args = has_leading_keyword (line, "FEATURES")))
{
tokens = strtokenize (args, " ");
if (!tokens)
return gpg_error_from_syserror ();
for (i=0; tokens[i]; i++)
if (!strcmp (tokens[i], "tabbing"))
entry_features.tabbing = 1;
xfree (tokens);
}
return 0;
}
2009-09-23 Marcus Brinkmann <marcus@g10code.de> * configure.ac (NEED_LIBASSUAN_API, NEED_LIBASSUAN_VERSION): Update to new API (2, 1.1.0). agent/ 2009-09-23 Marcus Brinkmann <marcus@g10code.de> * gpg-agent.c (parse_rereadable_options): Don't set global assuan log file (there ain't one anymore). (main): Update to new API. (check_own_socket_pid_cb): Return gpg_error_t instead of int. (check_own_socket_thread, check_for_running_agent): Create assuan context before connecting to server. * command.c: Include "scdaemon.h" before <assuan.h> because of GPG_ERR_SOURCE_DEFAULT check. (write_and_clear_outbuf): Use gpg_error_t instead of assuan_error_t. (cmd_geteventcounter, cmd_istrusted, cmd_listtrusted) (cmd_marktrusted, cmd_havekey, cmd_sigkey, cmd_setkeydesc) (cmd_sethash, cmd_pksign, cmd_pkdecrypt, cmd_genkey, cmd_readkey) (cmd_keyinfo, cmd_get_passphrase, cmd_clear_passphrase) (cmd_get_confirmation, cmd_learn, cmd_passwd) (cmd_preset_passphrase, cmd_scd, cmd_getval, cmd_putval) (cmd_updatestartuptty, cmd_killagent, cmd_reloadagent) (cmd_getinfo, option_handler): Return gpg_error_t instead of int. (post_cmd_notify): Change type of ERR to gpg_error_t from int. (io_monitor): Add hook argument. Use symbols for constants. (register_commands): Change return type of HANDLER to gpg_error_t. (start_command_handler): Allocate assuan context before starting server. * call-pinentry.c: Include "scdaemon.h" before <assuan.h> because of GPG_ERR_SOURCE_DEFAULT check. (unlock_pinentry): Call assuan_release instead of assuan_disconnect. (getinfo_pid_cb, getpin_cb): Return gpg_error_t instead of int. (start_pinentry): Allocate assuan context before connecting to server. * call-scd.c (membuf_data_cb, learn_status_cb, get_serialno_cb) (membuf_data_cb, inq_needpin, card_getattr_cb, pass_status_thru) (pass_data_thru): Change return type to gpg_error_t. (start_scd): Allocate assuan context before connecting to server. common/ 2009-09-23 Marcus Brinkmann <marcus@g10code.de> * asshelp.c (start_new_gpg_agent): Allocate assuan context before starting server. g10/ 2009-09-23 Marcus Brinkmann <marcus@g10code.de> * call-agent.c: Include "scdaemon.h" before <assuan.h> because of GPG_ERR_SOURCE_DEFAULT check. (learn_status_cb, dummy_data_cb, get_serialno_cb, default_inq_cb) (learn_status_cb, inq_writecert_parms, inq_writekey_parms) (scd_genkey_cb, membuf_data_cb): Return gpg_error_t instead of int. * gpg.c: Include "scdaemon.h" before <assuan.h> because of GPG_ERR_SOURCE_DEFAULT check. (main): Update to new Assuan API. * server.c: Include "scdaemon.h" before <assuan.h> because of GPG_ERR_SOURCE_DEFAULT check. (option_handler, cmd_recipient, cmd_signer, cmd_encrypt) (cmd_decrypt, cmd_verify, cmd_sign, cmd_import, cmd_export) (cmd_delkeys, cmd_message, do_listkeys, cmd_listkeys) (cmd_listsecretkeys, cmd_genkey, cmd_getinfo): Return gpg_error_t instead of int. (register_commands): Allocate assuan context before starting server. (gpg_server): Allocate assuan_context before starting server. scd/ 2009-09-23 Marcus Brinkmann <marcus@g10code.de> * command.c: Include "scdaemon.h" before <assuan.h> because of GPG_ERR_SOURCE_DEFAULT check. (option_handler, open_card, cmd_serialno, cmd_lean, cmd_readcert) (cmd_readkey, cmd_setdata, cmd_pksign, cmd_pkauth, cmd_pkdecrypt) (cmd_getattr, cmd_setattr, cmd_writecert, cmd_writekey) (cmd_genkey, cmd_random, cmd_passwd, cmd_checkpin, cmd_lock) (cmd_unlock, cmd_getinfo, cmd_restart, cmd_disconnect, cmd_apdu) (cmd_killscd): Return gpg_error_t instead of int. (scd_command_handler): Allocate assuan context before starting server. * scdaemon.c (main): Update to new Assuan API. sm/ 2009-09-23 Marcus Brinkmann <marcus@g10code.de> * gpgsm.c (main): Update to new assuan API. * server.c: Include "gpgsm.h" before <assuan.h> due to check for GPG_ERR_SOURCE_DEFAULT and assuan.h now including gpg-error.h. (option_handler, cmd_recipient, cmd_signer, cmd_encrypt) (cmd_decrypt, cmd_verify, cmd_sign, cmd_import, cmd_export) (cmd_delkeys, cmd_message, cmd_listkeys, cmd_dumpkeys) (cmd_listsecretkeys, cmd_dumpsecretkeys, cmd_genkey) (cmd_getauditlog, cmd_getinfo): Return gpg_error_t instead of int. (register_commands): Same for member HANDLER in table. (gpgsm_server): Allocate assuan context before starting server. * sm/call-dirmngr.c: * call-dirmngr.c (prepare_dirmngr): Check for CTX and error before setting LDAPSERVER. (start_dirmngr_ext): Allocate assuan context before starting server. (inq_certificate, isvalid_status_cb, lookup_cb, lookup_status_cb) (run_command_cb, run_command_inq_cb, run_command_status_cb): Return gpg_error_t instead of int. tools/ 2009-09-23 Marcus Brinkmann <marcus@g10code.de> * gpg-connect-agent.c (getinfo_pid_cb, read_and_print_response) (main): Update to new Assuan API.
2009-09-23 00:01:25 +00:00
static gpg_error_t
getinfo_pid_cb (void *opaque, const void *buffer, size_t length)
{
unsigned long *pid = opaque;
char pidbuf[50];
/* There is only the pid in the server's response. */
if (length >= sizeof pidbuf)
length = sizeof pidbuf -1;
if (length)
{
strncpy (pidbuf, buffer, length);
pidbuf[length] = 0;
*pid = strtoul (pidbuf, NULL, 10);
}
return 0;
}
/* Fork off the pin entry if this has not already been done. Note,
Fix typos found using codespell. * agent/cache.c: Fix typos. * agent/call-pinentry.c: Likewise. * agent/call-scd.c: Likewise. * agent/command-ssh.c: Likewise. * agent/command.c: Likewise. * agent/divert-scd.c: Likewise. * agent/findkey.c: Likewise. * agent/gpg-agent.c: Likewise. * agent/w32main.c: Likewise. * common/argparse.c: Likewise. * common/audit.c: Likewise. * common/audit.h: Likewise. * common/convert.c: Likewise. * common/dotlock.c: Likewise. * common/exechelp-posix.c: Likewise. * common/exechelp-w32.c: Likewise. * common/exechelp-w32ce.c: Likewise. * common/exechelp.h: Likewise. * common/helpfile.c: Likewise. * common/i18n.h: Likewise. * common/iobuf.c: Likewise. * common/iobuf.h: Likewise. * common/localename.c: Likewise. * common/logging.c: Likewise. * common/openpgp-oid.c: Likewise. * common/session-env.c: Likewise. * common/sexputil.c: Likewise. * common/sysutils.c: Likewise. * common/t-sexputil.c: Likewise. * common/ttyio.c: Likewise. * common/util.h: Likewise. * dirmngr/cdblib.c: Likewise. * dirmngr/certcache.c: Likewise. * dirmngr/crlcache.c: Likewise. * dirmngr/dirmngr-client.c: Likewise. * dirmngr/dirmngr.c: Likewise. * dirmngr/dirmngr_ldap.c: Likewise. * dirmngr/dns-stuff.c: Likewise. * dirmngr/http.c: Likewise. * dirmngr/ks-engine-hkp.c: Likewise. * dirmngr/ks-engine-ldap.c: Likewise. * dirmngr/ldap-wrapper.c: Likewise. * dirmngr/ldap.c: Likewise. * dirmngr/misc.c: Likewise. * dirmngr/ocsp.c: Likewise. * dirmngr/validate.c: Likewise. * g10/encrypt.c: Likewise. * g10/getkey.c: Likewise. * g10/gpg.c: Likewise. * g10/gpgv.c: Likewise. * g10/import.c: Likewise. * g10/keydb.c: Likewise. * g10/keydb.h: Likewise. * g10/keygen.c: Likewise. * g10/keyid.c: Likewise. * g10/keylist.c: Likewise. * g10/keyring.c: Likewise. * g10/mainproc.c: Likewise. * g10/misc.c: Likewise. * g10/options.h: Likewise. * g10/packet.h: Likewise. * g10/parse-packet.c: Likewise. * g10/pkclist.c: Likewise. * g10/pkglue.c: Likewise. * g10/plaintext.c: Likewise. * g10/server.c: Likewise. * g10/sig-check.c: Likewise. * g10/sqlite.c: Likewise. * g10/tdbio.c: Likewise. * g10/test-stubs.c: Likewise. * g10/tofu.c: Likewise. * g10/trust.c: Likewise. * g10/trustdb.c: Likewise. * g13/create.c: Likewise. * g13/mountinfo.c: Likewise. * kbx/keybox-blob.c: Likewise. * kbx/keybox-file.c: Likewise. * kbx/keybox-init.c: Likewise. * kbx/keybox-search-desc.h: Likewise. * kbx/keybox-search.c: Likewise. * kbx/keybox-update.c: Likewise. * scd/apdu.c: Likewise. * scd/app-openpgp.c: Likewise. * scd/app-p15.c: Likewise. * scd/app.c: Likewise. * scd/ccid-driver.c: Likewise. * scd/command.c: Likewise. * scd/iso7816.c: Likewise. * sm/base64.c: Likewise. * sm/call-agent.c: Likewise. * sm/call-dirmngr.c: Likewise. * sm/certchain.c: Likewise. * sm/gpgsm.c: Likewise. * sm/import.c: Likewise. * sm/keydb.c: Likewise. * sm/minip12.c: Likewise. * sm/qualified.c: Likewise. * sm/server.c: Likewise. * tools/gpg-check-pattern.c: Likewise. * tools/gpgconf-comp.c: Likewise. * tools/gpgkey2ssh.c: Likewise. * tools/gpgparsemail.c: Likewise. * tools/gpgtar.c: Likewise. * tools/rfc822parse.c: Likewise. * tools/symcryptrun.c: Likewise. Signed-off-by: Justus Winter <justus@g10code.com>
2015-11-16 12:41:46 +01:00
that this function must always be used to acquire the lock for the
pinentry - we will serialize _all_ pinentry calls.
*/
static gpg_error_t
2005-11-28 11:52:25 +00:00
start_pinentry (ctrl_t ctrl)
{
int rc = 0;
const char *full_pgmname;
const char *pgmname;
2006-09-06 16:35:52 +00:00
assuan_context_t ctx;
const char *argv[5];
assuan_fd_t no_close_list[3];
int i;
const char *tmpstr;
unsigned long pinentry_pid;
const char *value;
struct timespec abstime;
char *flavor_version;
int err;
if (ctrl->pinentry_active)
{
/* It's trying to use pinentry recursively. In this situation,
the thread holds ENTRY_LOCK already. */
ctrl->pinentry_active++;
return 0;
}
npth_clock_gettime (&abstime);
abstime.tv_sec += LOCK_TIMEOUT;
err = npth_mutex_timedlock (&entry_lock, &abstime);
if (err)
{
if (err == ETIMEDOUT)
rc = gpg_error (GPG_ERR_TIMEOUT);
else
rc = gpg_error_from_errno (rc);
log_error (_("failed to acquire the pinentry lock: %s\n"),
gpg_strerror (rc));
return rc;
}
2005-11-28 11:52:25 +00:00
if (entry_ctx)
return 0;
if (opt.verbose)
log_info ("starting a new PIN Entry\n");
#ifdef HAVE_W32_SYSTEM
fflush (stdout);
fflush (stderr);
#endif
if (fflush (NULL))
{
#ifndef HAVE_W32_SYSTEM
gpg_error_t tmperr = gpg_error (gpg_err_code_from_errno (errno));
#endif
log_error ("error flushing pending output: %s\n", strerror (errno));
/* At least Windows XP fails here with EBADF. According to docs
and Wine an fflush(NULL) is the same as _flushall. However
the Wine implementation does not flush stdin,stdout and stderr
- see above. Let's try to ignore the error. */
#ifndef HAVE_W32_SYSTEM
return unlock_pinentry (ctrl, tmperr);
#endif
}
full_pgmname = opt.pinentry_program;
if (!full_pgmname || !*full_pgmname)
full_pgmname = gnupg_module_name (GNUPG_MODULE_NAME_PINENTRY);
if ( !(pgmname = strrchr (full_pgmname, '/')))
pgmname = full_pgmname;
else
pgmname++;
/* OS X needs the entire file name in argv[0], so that it can locate
2007-07-04 09:34:28 +00:00
the resource bundle. For other systems we stick to the usual
convention of supplying only the name of the program. */
#ifdef __APPLE__
argv[0] = full_pgmname;
#else /*!__APPLE__*/
argv[0] = pgmname;
#endif /*__APPLE__*/
if (!opt.keep_display
&& (value = session_env_getenv (ctrl->session_env, "DISPLAY")))
{
argv[1] = "--display";
argv[2] = value;
argv[3] = NULL;
}
else
argv[1] = NULL;
i=0;
if (!opt.running_detached)
{
if (log_get_fd () != -1)
no_close_list[i++] = assuan_fd_from_posix_fd (log_get_fd ());
no_close_list[i++] = assuan_fd_from_posix_fd (fileno (stderr));
}
no_close_list[i] = ASSUAN_INVALID_FD;
2009-09-23 Marcus Brinkmann <marcus@g10code.de> * configure.ac (NEED_LIBASSUAN_API, NEED_LIBASSUAN_VERSION): Update to new API (2, 1.1.0). agent/ 2009-09-23 Marcus Brinkmann <marcus@g10code.de> * gpg-agent.c (parse_rereadable_options): Don't set global assuan log file (there ain't one anymore). (main): Update to new API. (check_own_socket_pid_cb): Return gpg_error_t instead of int. (check_own_socket_thread, check_for_running_agent): Create assuan context before connecting to server. * command.c: Include "scdaemon.h" before <assuan.h> because of GPG_ERR_SOURCE_DEFAULT check. (write_and_clear_outbuf): Use gpg_error_t instead of assuan_error_t. (cmd_geteventcounter, cmd_istrusted, cmd_listtrusted) (cmd_marktrusted, cmd_havekey, cmd_sigkey, cmd_setkeydesc) (cmd_sethash, cmd_pksign, cmd_pkdecrypt, cmd_genkey, cmd_readkey) (cmd_keyinfo, cmd_get_passphrase, cmd_clear_passphrase) (cmd_get_confirmation, cmd_learn, cmd_passwd) (cmd_preset_passphrase, cmd_scd, cmd_getval, cmd_putval) (cmd_updatestartuptty, cmd_killagent, cmd_reloadagent) (cmd_getinfo, option_handler): Return gpg_error_t instead of int. (post_cmd_notify): Change type of ERR to gpg_error_t from int. (io_monitor): Add hook argument. Use symbols for constants. (register_commands): Change return type of HANDLER to gpg_error_t. (start_command_handler): Allocate assuan context before starting server. * call-pinentry.c: Include "scdaemon.h" before <assuan.h> because of GPG_ERR_SOURCE_DEFAULT check. (unlock_pinentry): Call assuan_release instead of assuan_disconnect. (getinfo_pid_cb, getpin_cb): Return gpg_error_t instead of int. (start_pinentry): Allocate assuan context before connecting to server. * call-scd.c (membuf_data_cb, learn_status_cb, get_serialno_cb) (membuf_data_cb, inq_needpin, card_getattr_cb, pass_status_thru) (pass_data_thru): Change return type to gpg_error_t. (start_scd): Allocate assuan context before connecting to server. common/ 2009-09-23 Marcus Brinkmann <marcus@g10code.de> * asshelp.c (start_new_gpg_agent): Allocate assuan context before starting server. g10/ 2009-09-23 Marcus Brinkmann <marcus@g10code.de> * call-agent.c: Include "scdaemon.h" before <assuan.h> because of GPG_ERR_SOURCE_DEFAULT check. (learn_status_cb, dummy_data_cb, get_serialno_cb, default_inq_cb) (learn_status_cb, inq_writecert_parms, inq_writekey_parms) (scd_genkey_cb, membuf_data_cb): Return gpg_error_t instead of int. * gpg.c: Include "scdaemon.h" before <assuan.h> because of GPG_ERR_SOURCE_DEFAULT check. (main): Update to new Assuan API. * server.c: Include "scdaemon.h" before <assuan.h> because of GPG_ERR_SOURCE_DEFAULT check. (option_handler, cmd_recipient, cmd_signer, cmd_encrypt) (cmd_decrypt, cmd_verify, cmd_sign, cmd_import, cmd_export) (cmd_delkeys, cmd_message, do_listkeys, cmd_listkeys) (cmd_listsecretkeys, cmd_genkey, cmd_getinfo): Return gpg_error_t instead of int. (register_commands): Allocate assuan context before starting server. (gpg_server): Allocate assuan_context before starting server. scd/ 2009-09-23 Marcus Brinkmann <marcus@g10code.de> * command.c: Include "scdaemon.h" before <assuan.h> because of GPG_ERR_SOURCE_DEFAULT check. (option_handler, open_card, cmd_serialno, cmd_lean, cmd_readcert) (cmd_readkey, cmd_setdata, cmd_pksign, cmd_pkauth, cmd_pkdecrypt) (cmd_getattr, cmd_setattr, cmd_writecert, cmd_writekey) (cmd_genkey, cmd_random, cmd_passwd, cmd_checkpin, cmd_lock) (cmd_unlock, cmd_getinfo, cmd_restart, cmd_disconnect, cmd_apdu) (cmd_killscd): Return gpg_error_t instead of int. (scd_command_handler): Allocate assuan context before starting server. * scdaemon.c (main): Update to new Assuan API. sm/ 2009-09-23 Marcus Brinkmann <marcus@g10code.de> * gpgsm.c (main): Update to new assuan API. * server.c: Include "gpgsm.h" before <assuan.h> due to check for GPG_ERR_SOURCE_DEFAULT and assuan.h now including gpg-error.h. (option_handler, cmd_recipient, cmd_signer, cmd_encrypt) (cmd_decrypt, cmd_verify, cmd_sign, cmd_import, cmd_export) (cmd_delkeys, cmd_message, cmd_listkeys, cmd_dumpkeys) (cmd_listsecretkeys, cmd_dumpsecretkeys, cmd_genkey) (cmd_getauditlog, cmd_getinfo): Return gpg_error_t instead of int. (register_commands): Same for member HANDLER in table. (gpgsm_server): Allocate assuan context before starting server. * sm/call-dirmngr.c: * call-dirmngr.c (prepare_dirmngr): Check for CTX and error before setting LDAPSERVER. (start_dirmngr_ext): Allocate assuan context before starting server. (inq_certificate, isvalid_status_cb, lookup_cb, lookup_status_cb) (run_command_cb, run_command_inq_cb, run_command_status_cb): Return gpg_error_t instead of int. tools/ 2009-09-23 Marcus Brinkmann <marcus@g10code.de> * gpg-connect-agent.c (getinfo_pid_cb, read_and_print_response) (main): Update to new Assuan API.
2009-09-23 00:01:25 +00:00
rc = assuan_new (&ctx);
if (rc)
{
log_error ("can't allocate assuan context: %s\n", gpg_strerror (rc));
return rc;
}
ctrl->pinentry_active = 1;
entry_ctx = ctx;
/* We don't want to log the pinentry communication to make the logs
easier to read. We might want to add a new debug option to enable
pinentry logging. */
#ifdef ASSUAN_NO_LOGGING
assuan_set_flag (ctx, ASSUAN_NO_LOGGING, !opt.debug_pinentry);
#endif
2009-09-23 Marcus Brinkmann <marcus@g10code.de> * configure.ac (NEED_LIBASSUAN_API, NEED_LIBASSUAN_VERSION): Update to new API (2, 1.1.0). agent/ 2009-09-23 Marcus Brinkmann <marcus@g10code.de> * gpg-agent.c (parse_rereadable_options): Don't set global assuan log file (there ain't one anymore). (main): Update to new API. (check_own_socket_pid_cb): Return gpg_error_t instead of int. (check_own_socket_thread, check_for_running_agent): Create assuan context before connecting to server. * command.c: Include "scdaemon.h" before <assuan.h> because of GPG_ERR_SOURCE_DEFAULT check. (write_and_clear_outbuf): Use gpg_error_t instead of assuan_error_t. (cmd_geteventcounter, cmd_istrusted, cmd_listtrusted) (cmd_marktrusted, cmd_havekey, cmd_sigkey, cmd_setkeydesc) (cmd_sethash, cmd_pksign, cmd_pkdecrypt, cmd_genkey, cmd_readkey) (cmd_keyinfo, cmd_get_passphrase, cmd_clear_passphrase) (cmd_get_confirmation, cmd_learn, cmd_passwd) (cmd_preset_passphrase, cmd_scd, cmd_getval, cmd_putval) (cmd_updatestartuptty, cmd_killagent, cmd_reloadagent) (cmd_getinfo, option_handler): Return gpg_error_t instead of int. (post_cmd_notify): Change type of ERR to gpg_error_t from int. (io_monitor): Add hook argument. Use symbols for constants. (register_commands): Change return type of HANDLER to gpg_error_t. (start_command_handler): Allocate assuan context before starting server. * call-pinentry.c: Include "scdaemon.h" before <assuan.h> because of GPG_ERR_SOURCE_DEFAULT check. (unlock_pinentry): Call assuan_release instead of assuan_disconnect. (getinfo_pid_cb, getpin_cb): Return gpg_error_t instead of int. (start_pinentry): Allocate assuan context before connecting to server. * call-scd.c (membuf_data_cb, learn_status_cb, get_serialno_cb) (membuf_data_cb, inq_needpin, card_getattr_cb, pass_status_thru) (pass_data_thru): Change return type to gpg_error_t. (start_scd): Allocate assuan context before connecting to server. common/ 2009-09-23 Marcus Brinkmann <marcus@g10code.de> * asshelp.c (start_new_gpg_agent): Allocate assuan context before starting server. g10/ 2009-09-23 Marcus Brinkmann <marcus@g10code.de> * call-agent.c: Include "scdaemon.h" before <assuan.h> because of GPG_ERR_SOURCE_DEFAULT check. (learn_status_cb, dummy_data_cb, get_serialno_cb, default_inq_cb) (learn_status_cb, inq_writecert_parms, inq_writekey_parms) (scd_genkey_cb, membuf_data_cb): Return gpg_error_t instead of int. * gpg.c: Include "scdaemon.h" before <assuan.h> because of GPG_ERR_SOURCE_DEFAULT check. (main): Update to new Assuan API. * server.c: Include "scdaemon.h" before <assuan.h> because of GPG_ERR_SOURCE_DEFAULT check. (option_handler, cmd_recipient, cmd_signer, cmd_encrypt) (cmd_decrypt, cmd_verify, cmd_sign, cmd_import, cmd_export) (cmd_delkeys, cmd_message, do_listkeys, cmd_listkeys) (cmd_listsecretkeys, cmd_genkey, cmd_getinfo): Return gpg_error_t instead of int. (register_commands): Allocate assuan context before starting server. (gpg_server): Allocate assuan_context before starting server. scd/ 2009-09-23 Marcus Brinkmann <marcus@g10code.de> * command.c: Include "scdaemon.h" before <assuan.h> because of GPG_ERR_SOURCE_DEFAULT check. (option_handler, open_card, cmd_serialno, cmd_lean, cmd_readcert) (cmd_readkey, cmd_setdata, cmd_pksign, cmd_pkauth, cmd_pkdecrypt) (cmd_getattr, cmd_setattr, cmd_writecert, cmd_writekey) (cmd_genkey, cmd_random, cmd_passwd, cmd_checkpin, cmd_lock) (cmd_unlock, cmd_getinfo, cmd_restart, cmd_disconnect, cmd_apdu) (cmd_killscd): Return gpg_error_t instead of int. (scd_command_handler): Allocate assuan context before starting server. * scdaemon.c (main): Update to new Assuan API. sm/ 2009-09-23 Marcus Brinkmann <marcus@g10code.de> * gpgsm.c (main): Update to new assuan API. * server.c: Include "gpgsm.h" before <assuan.h> due to check for GPG_ERR_SOURCE_DEFAULT and assuan.h now including gpg-error.h. (option_handler, cmd_recipient, cmd_signer, cmd_encrypt) (cmd_decrypt, cmd_verify, cmd_sign, cmd_import, cmd_export) (cmd_delkeys, cmd_message, cmd_listkeys, cmd_dumpkeys) (cmd_listsecretkeys, cmd_dumpsecretkeys, cmd_genkey) (cmd_getauditlog, cmd_getinfo): Return gpg_error_t instead of int. (register_commands): Same for member HANDLER in table. (gpgsm_server): Allocate assuan context before starting server. * sm/call-dirmngr.c: * call-dirmngr.c (prepare_dirmngr): Check for CTX and error before setting LDAPSERVER. (start_dirmngr_ext): Allocate assuan context before starting server. (inq_certificate, isvalid_status_cb, lookup_cb, lookup_status_cb) (run_command_cb, run_command_inq_cb, run_command_status_cb): Return gpg_error_t instead of int. tools/ 2009-09-23 Marcus Brinkmann <marcus@g10code.de> * gpg-connect-agent.c (getinfo_pid_cb, read_and_print_response) (main): Update to new Assuan API.
2009-09-23 00:01:25 +00:00
/* Connect to the pinentry and perform initial handshaking. Note
that atfork is used to change the environment for pinentry. We
start the server in detached mode to suppress the console window
under Windows. */
rc = assuan_pipe_connect (entry_ctx, full_pgmname, argv,
no_close_list, atfork_cb, ctrl,
ASSUAN_PIPE_CONNECT_DETACHED);
if (rc)
{
log_error ("can't connect to the PIN entry module '%s': %s\n",
full_pgmname, gpg_strerror (rc));
return unlock_pinentry (ctrl, gpg_error (GPG_ERR_NO_PIN_ENTRY));
}
if (DBG_IPC)
log_debug ("connection to PIN entry established\n");
if (opt.debug_pinentry)
atfork_core (ctrl, 1);
value = session_env_getenv (ctrl->session_env, "PINENTRY_USER_DATA");
if (value != NULL)
{
char *optstr;
if (asprintf (&optstr, "OPTION pinentry-user-data=%s", value) < 0 )
return unlock_pinentry (ctrl, out_of_core ());
rc = assuan_transact (entry_ctx, optstr, NULL, NULL, NULL, NULL, NULL,
NULL);
xfree (optstr);
if (rc && gpg_err_code (rc) != GPG_ERR_UNKNOWN_OPTION)
return unlock_pinentry (ctrl, rc);
}
rc = assuan_transact (entry_ctx,
opt.no_grab? "OPTION no-grab":"OPTION grab",
NULL, NULL, NULL, NULL, NULL, NULL);
if (rc)
{
if (gpg_err_code (rc) == GPG_ERR_NOT_SUPPORTED
|| gpg_err_code (rc) == GPG_ERR_UNKNOWN_OPTION)
{
if (opt.verbose)
log_info ("Option no-grab/grab is ignored by pinentry.\n");
/* Keep going even if the feature is not supported. */
}
else
return unlock_pinentry (ctrl, rc);
}
value = session_env_getenv (ctrl->session_env, "GPG_TTY");
if (value)
{
char *optstr;
if (asprintf (&optstr, "OPTION ttyname=%s", value) < 0 )
return unlock_pinentry (ctrl, out_of_core ());
rc = assuan_transact (entry_ctx, optstr, NULL, NULL, NULL, NULL, NULL,
NULL);
xfree (optstr);
if (rc)
return unlock_pinentry (ctrl, rc);
}
value = session_env_getenv (ctrl->session_env, "TERM");
if (value && *value)
{
char *optstr;
if (asprintf (&optstr, "OPTION ttytype=%s", value) < 0 )
return unlock_pinentry (ctrl, out_of_core ());
rc = assuan_transact (entry_ctx, optstr, NULL, NULL, NULL, NULL, NULL,
NULL);
xfree (optstr);
if (rc)
return unlock_pinentry (ctrl, rc);
}
if (ctrl->lc_ctype)
{
char *optstr;
if (asprintf (&optstr, "OPTION lc-ctype=%s", ctrl->lc_ctype) < 0 )
return unlock_pinentry (ctrl, out_of_core ());
rc = assuan_transact (entry_ctx, optstr, NULL, NULL, NULL, NULL, NULL,
NULL);
xfree (optstr);
if (rc)
return unlock_pinentry (ctrl, rc);
}
if (ctrl->lc_messages)
{
char *optstr;
if (asprintf (&optstr, "OPTION lc-messages=%s", ctrl->lc_messages) < 0 )
return unlock_pinentry (ctrl, out_of_core ());
rc = assuan_transact (entry_ctx, optstr, NULL, NULL, NULL, NULL, NULL,
NULL);
xfree (optstr);
if (rc)
return unlock_pinentry (ctrl, rc);
}
if (opt.allow_external_cache)
{
/* Indicate to the pinentry that it may read from an external cache.
It is essential that the pinentry respect this. If the
cached password is not up to date and retry == 1, then, using
a version of GPG Agent that doesn't support this, won't issue
another pin request and the user won't get a chance to
correct the password. */
rc = assuan_transact (entry_ctx, "OPTION allow-external-password-cache",
NULL, NULL, NULL, NULL, NULL, NULL);
if (rc && gpg_err_code (rc) != GPG_ERR_UNKNOWN_OPTION)
return unlock_pinentry (ctrl, rc);
}
if (opt.allow_emacs_pinentry)
{
/* Indicate to the pinentry that it may read passphrase through
Emacs minibuffer, if possible. */
rc = assuan_transact (entry_ctx, "OPTION allow-emacs-prompt",
NULL, NULL, NULL, NULL, NULL, NULL);
if (rc && gpg_err_code (rc) != GPG_ERR_UNKNOWN_OPTION)
return unlock_pinentry (ctrl, rc);
}
{
/* Provide a few default strings for use by the pinentries. This
2018-11-12 18:13:31 +01:00
* may help a pinentry to avoid implementing localization code.
* Note that gpg-agent has been set to utf-8 so that the strings
* are in the expected encoding. */
static const struct { const char *key, *value; int what; } tbl[] = {
2018-11-12 18:13:31 +01:00
/* TRANSLATORS: These are labels for buttons etc as used in
* Pinentries. In your translation copy the text before the
* second vertical bar verbatim; translate only the following
* text. An underscore indicates that the next letter should be
* used as an accelerator. Double the underscore to have
* pinentry display a literal underscore. */
{ "ok", N_("|pinentry-label|_OK") },
{ "cancel", N_("|pinentry-label|_Cancel") },
{ "yes", N_("|pinentry-label|_Yes") },
{ "no", N_("|pinentry-label|_No") },
{ "prompt", N_("|pinentry-label|PIN:") },
{ "pwmngr", N_("|pinentry-label|_Save in password manager"), 1 },
{ "cf-visi",N_("Do you really want to make your "
"passphrase visible on the screen?") },
{ "tt-visi",N_("|pinentry-tt|Make passphrase visible") },
{ "tt-hide",N_("|pinentry-tt|Hide passphrase") },
{ NULL, NULL}
};
char *optstr;
int idx;
const char *s, *s2;
for (idx=0; tbl[idx].key; idx++)
{
if (!opt.allow_external_cache && tbl[idx].what == 1)
continue; /* No need for it. */
s = L_(tbl[idx].value);
if (*s == '|' && (s2=strchr (s+1,'|')))
s = s2+1;
if (asprintf (&optstr, "OPTION default-%s=%s", tbl[idx].key, s) < 0 )
return unlock_pinentry (ctrl, out_of_core ());
assuan_transact (entry_ctx, optstr, NULL, NULL, NULL, NULL, NULL,
NULL);
xfree (optstr);
}
}
/* Tell the pinentry that we would prefer that the given character
is used as the invisible character by the entry widget. */
if (opt.pinentry_invisible_char)
{
char *optstr;
if ((optstr = xtryasprintf ("OPTION invisible-char=%s",
opt.pinentry_invisible_char)))
{
assuan_transact (entry_ctx, optstr, NULL, NULL, NULL, NULL, NULL,
NULL);
/* We ignore errors because this is just a fancy thing and
older pinentries do not support this feature. */
xfree (optstr);
}
}
if (opt.pinentry_timeout)
{
char *optstr;
if ((optstr = xtryasprintf ("SETTIMEOUT %lu", opt.pinentry_timeout)))
{
assuan_transact (entry_ctx, optstr, NULL, NULL, NULL, NULL, NULL,
NULL);
/* We ignore errors because this is just a fancy thing. */
xfree (optstr);
}
}
/* Tell the pinentry the name of a file it shall touch after having
messed with the tty. This is optional and only supported by
newer pinentries and thus we do no error checking. */
tmpstr = opt.pinentry_touch_file;
if (tmpstr && !strcmp (tmpstr, "/dev/null"))
tmpstr = NULL;
else if (!tmpstr)
tmpstr = get_agent_socket_name ();
if (tmpstr)
{
char *optstr;
if (asprintf (&optstr, "OPTION touch-file=%s", tmpstr ) < 0 )
;
else
{
assuan_transact (entry_ctx, optstr, NULL, NULL, NULL, NULL, NULL,
NULL);
xfree (optstr);
}
}
/* Tell Pinentry about our client. */
if (ctrl->client_pid)
{
char *optstr;
const char *nodename = "";
#ifndef HAVE_W32_SYSTEM
struct utsname utsbuf;
if (!uname (&utsbuf))
nodename = utsbuf.nodename;
#endif /*!HAVE_W32_SYSTEM*/
agent: Send pinentry the uid of connecting process where possible. * agent/agent.h (server_control_s): Add field 'client_uid'. * agent/call-pinentry.c (start_pinentry): Add uid field to assuan option "owner" sent to pinentry. * agent/command-ssh.c (peer_info_s): New static struct. (get_client_pid): Rename to... (get_client_info): Here, and extract uid in addition to pid. (start_command_handler_ssh): Use get_client_info() instead of get_client_pid(). * agent/command.c (start_command_handler): Try assuan_get_peercred, and only fall back to assuan_get_pid when assuan_get_peercred fails. -- This also requires an update to pinentry to handle the new uid field. Distributing the uid as well as the pid makes it harder for a different user on the same machine to take advantage of any race conditions between when a requesting process might ask for something that needs pinentry, and when pinentry gets around to inspecting the state of that process. We put the uid before the nodename because the uid is guaranteed to be a integer (represented in decimal), which makes it much simpler to parse past than the potentially arbitrarily structured nodename. Use a / instead of whitespace to delimit pid/uid at Werner's request. If we were willing to depend on the nodename being whitespace-delimited (as the current, unreleased pinentry code does), then we could add the uid after the nodename. But since no released pinentry depends on this option anyway, i think we should make the more conservative, easily-parseable choice and put the user ID first. Signed-off-by: Daniel Kahn Gillmor <dkg@fifthhorseman.net>
2017-02-05 02:12:25 -05:00
if ((optstr = xtryasprintf ("OPTION owner=%lu/%d %s",
ctrl->client_pid, ctrl->client_uid,
nodename)))
{
assuan_transact (entry_ctx, optstr, NULL, NULL, NULL, NULL, NULL,
NULL);
/* We ignore errors because this is just a fancy thing and
older pinentries do not support this feature. */
xfree (optstr);
}
}
/* Ask the pinentry for its version and flavor and store that as a
* string in MB. This information is useful for helping users to
* figure out Pinentry problems. Note that "flavor" may also return
* a status line with the features; we use a dedicated handler for
* that. */
{
membuf_t mb;
init_membuf (&mb, 256);
if (assuan_transact (entry_ctx, "GETINFO flavor",
put_membuf_cb, &mb,
NULL, NULL,
getinfo_features_cb, NULL))
put_membuf_str (&mb, "unknown");
put_membuf_str (&mb, " ");
if (assuan_transact (entry_ctx, "GETINFO version",
put_membuf_cb, &mb, NULL, NULL, NULL, NULL))
put_membuf_str (&mb, "unknown");
put_membuf_str (&mb, " ");
if (assuan_transact (entry_ctx, "GETINFO ttyinfo",
put_membuf_cb, &mb, NULL, NULL, NULL, NULL))
put_membuf_str (&mb, "? ? ?");
put_membuf (&mb, "", 1);
flavor_version = get_membuf (&mb, NULL);
}
/* Now ask the Pinentry for its PID. If the Pinentry is new enough
it will send the pid back and we will use an inquire to notify
our client. The client may answer the inquiry either with END or
with CAN to cancel the pinentry. */
rc = assuan_transact (entry_ctx, "GETINFO pid",
getinfo_pid_cb, &pinentry_pid,
NULL, NULL, NULL, NULL);
if (rc)
{
log_info ("You may want to update to a newer pinentry\n");
rc = 0;
}
else if (!rc && (pid_t)pinentry_pid == (pid_t)(-1))
log_error ("pinentry did not return a PID\n");
else
{
rc = agent_inq_pinentry_launched (ctrl, pinentry_pid, flavor_version);
2010-10-13 15:57:08 +00:00
if (gpg_err_code (rc) == GPG_ERR_CANCELED
|| gpg_err_code (rc) == GPG_ERR_FULLY_CANCELED)
return unlock_pinentry (ctrl, gpg_err_make (GPG_ERR_SOURCE_DEFAULT,
gpg_err_code (rc)));
rc = 0;
}
xfree (flavor_version);
return rc;
}
/* Returns True if the pinentry is currently active. If WAITSECONDS is
2006-10-19 14:22:06 +00:00
greater than zero the function will wait for this many seconds
before returning. */
int
pinentry_active_p (ctrl_t ctrl, int waitseconds)
{
int err;
(void)ctrl;
2006-10-19 14:22:06 +00:00
if (waitseconds > 0)
{
struct timespec abstime;
2006-10-19 14:22:06 +00:00
int rc;
npth_clock_gettime (&abstime);
abstime.tv_sec += waitseconds;
err = npth_mutex_timedlock (&entry_lock, &abstime);
if (err)
2006-10-19 14:22:06 +00:00
{
if (err == ETIMEDOUT)
2006-10-19 14:22:06 +00:00
rc = gpg_error (GPG_ERR_TIMEOUT);
else
rc = gpg_error (GPG_ERR_INTERNAL);
return rc;
}
}
else
{
err = npth_mutex_trylock (&entry_lock);
if (err)
2006-10-19 14:22:06 +00:00
return gpg_error (GPG_ERR_LOCKED);
}
err = npth_mutex_unlock (&entry_lock);
if (err)
log_error ("failed to release the entry lock at %d: %s\n", __LINE__,
strerror (errno));
2006-10-19 14:22:06 +00:00
return 0;
}
2009-09-23 Marcus Brinkmann <marcus@g10code.de> * configure.ac (NEED_LIBASSUAN_API, NEED_LIBASSUAN_VERSION): Update to new API (2, 1.1.0). agent/ 2009-09-23 Marcus Brinkmann <marcus@g10code.de> * gpg-agent.c (parse_rereadable_options): Don't set global assuan log file (there ain't one anymore). (main): Update to new API. (check_own_socket_pid_cb): Return gpg_error_t instead of int. (check_own_socket_thread, check_for_running_agent): Create assuan context before connecting to server. * command.c: Include "scdaemon.h" before <assuan.h> because of GPG_ERR_SOURCE_DEFAULT check. (write_and_clear_outbuf): Use gpg_error_t instead of assuan_error_t. (cmd_geteventcounter, cmd_istrusted, cmd_listtrusted) (cmd_marktrusted, cmd_havekey, cmd_sigkey, cmd_setkeydesc) (cmd_sethash, cmd_pksign, cmd_pkdecrypt, cmd_genkey, cmd_readkey) (cmd_keyinfo, cmd_get_passphrase, cmd_clear_passphrase) (cmd_get_confirmation, cmd_learn, cmd_passwd) (cmd_preset_passphrase, cmd_scd, cmd_getval, cmd_putval) (cmd_updatestartuptty, cmd_killagent, cmd_reloadagent) (cmd_getinfo, option_handler): Return gpg_error_t instead of int. (post_cmd_notify): Change type of ERR to gpg_error_t from int. (io_monitor): Add hook argument. Use symbols for constants. (register_commands): Change return type of HANDLER to gpg_error_t. (start_command_handler): Allocate assuan context before starting server. * call-pinentry.c: Include "scdaemon.h" before <assuan.h> because of GPG_ERR_SOURCE_DEFAULT check. (unlock_pinentry): Call assuan_release instead of assuan_disconnect. (getinfo_pid_cb, getpin_cb): Return gpg_error_t instead of int. (start_pinentry): Allocate assuan context before connecting to server. * call-scd.c (membuf_data_cb, learn_status_cb, get_serialno_cb) (membuf_data_cb, inq_needpin, card_getattr_cb, pass_status_thru) (pass_data_thru): Change return type to gpg_error_t. (start_scd): Allocate assuan context before connecting to server. common/ 2009-09-23 Marcus Brinkmann <marcus@g10code.de> * asshelp.c (start_new_gpg_agent): Allocate assuan context before starting server. g10/ 2009-09-23 Marcus Brinkmann <marcus@g10code.de> * call-agent.c: Include "scdaemon.h" before <assuan.h> because of GPG_ERR_SOURCE_DEFAULT check. (learn_status_cb, dummy_data_cb, get_serialno_cb, default_inq_cb) (learn_status_cb, inq_writecert_parms, inq_writekey_parms) (scd_genkey_cb, membuf_data_cb): Return gpg_error_t instead of int. * gpg.c: Include "scdaemon.h" before <assuan.h> because of GPG_ERR_SOURCE_DEFAULT check. (main): Update to new Assuan API. * server.c: Include "scdaemon.h" before <assuan.h> because of GPG_ERR_SOURCE_DEFAULT check. (option_handler, cmd_recipient, cmd_signer, cmd_encrypt) (cmd_decrypt, cmd_verify, cmd_sign, cmd_import, cmd_export) (cmd_delkeys, cmd_message, do_listkeys, cmd_listkeys) (cmd_listsecretkeys, cmd_genkey, cmd_getinfo): Return gpg_error_t instead of int. (register_commands): Allocate assuan context before starting server. (gpg_server): Allocate assuan_context before starting server. scd/ 2009-09-23 Marcus Brinkmann <marcus@g10code.de> * command.c: Include "scdaemon.h" before <assuan.h> because of GPG_ERR_SOURCE_DEFAULT check. (option_handler, open_card, cmd_serialno, cmd_lean, cmd_readcert) (cmd_readkey, cmd_setdata, cmd_pksign, cmd_pkauth, cmd_pkdecrypt) (cmd_getattr, cmd_setattr, cmd_writecert, cmd_writekey) (cmd_genkey, cmd_random, cmd_passwd, cmd_checkpin, cmd_lock) (cmd_unlock, cmd_getinfo, cmd_restart, cmd_disconnect, cmd_apdu) (cmd_killscd): Return gpg_error_t instead of int. (scd_command_handler): Allocate assuan context before starting server. * scdaemon.c (main): Update to new Assuan API. sm/ 2009-09-23 Marcus Brinkmann <marcus@g10code.de> * gpgsm.c (main): Update to new assuan API. * server.c: Include "gpgsm.h" before <assuan.h> due to check for GPG_ERR_SOURCE_DEFAULT and assuan.h now including gpg-error.h. (option_handler, cmd_recipient, cmd_signer, cmd_encrypt) (cmd_decrypt, cmd_verify, cmd_sign, cmd_import, cmd_export) (cmd_delkeys, cmd_message, cmd_listkeys, cmd_dumpkeys) (cmd_listsecretkeys, cmd_dumpsecretkeys, cmd_genkey) (cmd_getauditlog, cmd_getinfo): Return gpg_error_t instead of int. (register_commands): Same for member HANDLER in table. (gpgsm_server): Allocate assuan context before starting server. * sm/call-dirmngr.c: * call-dirmngr.c (prepare_dirmngr): Check for CTX and error before setting LDAPSERVER. (start_dirmngr_ext): Allocate assuan context before starting server. (inq_certificate, isvalid_status_cb, lookup_cb, lookup_status_cb) (run_command_cb, run_command_inq_cb, run_command_status_cb): Return gpg_error_t instead of int. tools/ 2009-09-23 Marcus Brinkmann <marcus@g10code.de> * gpg-connect-agent.c (getinfo_pid_cb, read_and_print_response) (main): Update to new Assuan API.
2009-09-23 00:01:25 +00:00
static gpg_error_t
getpin_cb (void *opaque, const void *buffer, size_t length)
{
struct entry_parm_s *parm = opaque;
if (!buffer)
return 0;
/* we expect the pin to fit on one line */
if (parm->lines || length >= parm->size)
2006-09-06 16:35:52 +00:00
return gpg_error (GPG_ERR_ASS_TOO_MUCH_DATA);
/* fixme: we should make sure that the assuan buffer is allocated in
secure memory or read the response byte by byte */
memcpy (parm->buffer, buffer, length);
parm->buffer[length] = 0;
parm->lines++;
return 0;
}
static int
all_digitsp( const char *s)
{
for (; *s && *s >= '0' && *s <= '9'; s++)
;
return !*s;
}
/* Return a new malloced string by unescaping the string S. Escaping
is percent escaping and '+'/space mapping. A binary Nul will
silently be replaced by a 0xFF. Function returns NULL to indicate
an out of memory status. Parsing stops at the end of the string or
a white space character. */
static char *
unescape_passphrase_string (const unsigned char *s)
{
char *buffer, *d;
buffer = d = xtrymalloc_secure (strlen ((const char*)s)+1);
if (!buffer)
return NULL;
while (*s && !spacep (s))
{
if (*s == '%' && s[1] && s[2])
{
s++;
*d = xtoi_2 (s);
if (!*d)
*d = '\xff';
d++;
s += 2;
}
else if (*s == '+')
{
*d++ = ' ';
s++;
}
else
*d++ = *s++;
}
*d = 0;
return buffer;
}
/* Estimate the quality of the passphrase PW and return a value in the
range 0..100. */
static int
estimate_passphrase_quality (const char *pw)
{
int goodlength = opt.min_passphrase_len + opt.min_passphrase_len/3;
int length;
const char *s;
if (goodlength < 1)
return 0;
for (length = 0, s = pw; *s; s++)
if (!spacep (s))
length ++;
if (length > goodlength)
return 100;
return ((length*10) / goodlength)*10;
}
/* Handle the QUALITY inquiry. */
2009-09-23 Marcus Brinkmann <marcus@g10code.de> * configure.ac (NEED_LIBASSUAN_API, NEED_LIBASSUAN_VERSION): Update to new API (2, 1.1.0). agent/ 2009-09-23 Marcus Brinkmann <marcus@g10code.de> * gpg-agent.c (parse_rereadable_options): Don't set global assuan log file (there ain't one anymore). (main): Update to new API. (check_own_socket_pid_cb): Return gpg_error_t instead of int. (check_own_socket_thread, check_for_running_agent): Create assuan context before connecting to server. * command.c: Include "scdaemon.h" before <assuan.h> because of GPG_ERR_SOURCE_DEFAULT check. (write_and_clear_outbuf): Use gpg_error_t instead of assuan_error_t. (cmd_geteventcounter, cmd_istrusted, cmd_listtrusted) (cmd_marktrusted, cmd_havekey, cmd_sigkey, cmd_setkeydesc) (cmd_sethash, cmd_pksign, cmd_pkdecrypt, cmd_genkey, cmd_readkey) (cmd_keyinfo, cmd_get_passphrase, cmd_clear_passphrase) (cmd_get_confirmation, cmd_learn, cmd_passwd) (cmd_preset_passphrase, cmd_scd, cmd_getval, cmd_putval) (cmd_updatestartuptty, cmd_killagent, cmd_reloadagent) (cmd_getinfo, option_handler): Return gpg_error_t instead of int. (post_cmd_notify): Change type of ERR to gpg_error_t from int. (io_monitor): Add hook argument. Use symbols for constants. (register_commands): Change return type of HANDLER to gpg_error_t. (start_command_handler): Allocate assuan context before starting server. * call-pinentry.c: Include "scdaemon.h" before <assuan.h> because of GPG_ERR_SOURCE_DEFAULT check. (unlock_pinentry): Call assuan_release instead of assuan_disconnect. (getinfo_pid_cb, getpin_cb): Return gpg_error_t instead of int. (start_pinentry): Allocate assuan context before connecting to server. * call-scd.c (membuf_data_cb, learn_status_cb, get_serialno_cb) (membuf_data_cb, inq_needpin, card_getattr_cb, pass_status_thru) (pass_data_thru): Change return type to gpg_error_t. (start_scd): Allocate assuan context before connecting to server. common/ 2009-09-23 Marcus Brinkmann <marcus@g10code.de> * asshelp.c (start_new_gpg_agent): Allocate assuan context before starting server. g10/ 2009-09-23 Marcus Brinkmann <marcus@g10code.de> * call-agent.c: Include "scdaemon.h" before <assuan.h> because of GPG_ERR_SOURCE_DEFAULT check. (learn_status_cb, dummy_data_cb, get_serialno_cb, default_inq_cb) (learn_status_cb, inq_writecert_parms, inq_writekey_parms) (scd_genkey_cb, membuf_data_cb): Return gpg_error_t instead of int. * gpg.c: Include "scdaemon.h" before <assuan.h> because of GPG_ERR_SOURCE_DEFAULT check. (main): Update to new Assuan API. * server.c: Include "scdaemon.h" before <assuan.h> because of GPG_ERR_SOURCE_DEFAULT check. (option_handler, cmd_recipient, cmd_signer, cmd_encrypt) (cmd_decrypt, cmd_verify, cmd_sign, cmd_import, cmd_export) (cmd_delkeys, cmd_message, do_listkeys, cmd_listkeys) (cmd_listsecretkeys, cmd_genkey, cmd_getinfo): Return gpg_error_t instead of int. (register_commands): Allocate assuan context before starting server. (gpg_server): Allocate assuan_context before starting server. scd/ 2009-09-23 Marcus Brinkmann <marcus@g10code.de> * command.c: Include "scdaemon.h" before <assuan.h> because of GPG_ERR_SOURCE_DEFAULT check. (option_handler, open_card, cmd_serialno, cmd_lean, cmd_readcert) (cmd_readkey, cmd_setdata, cmd_pksign, cmd_pkauth, cmd_pkdecrypt) (cmd_getattr, cmd_setattr, cmd_writecert, cmd_writekey) (cmd_genkey, cmd_random, cmd_passwd, cmd_checkpin, cmd_lock) (cmd_unlock, cmd_getinfo, cmd_restart, cmd_disconnect, cmd_apdu) (cmd_killscd): Return gpg_error_t instead of int. (scd_command_handler): Allocate assuan context before starting server. * scdaemon.c (main): Update to new Assuan API. sm/ 2009-09-23 Marcus Brinkmann <marcus@g10code.de> * gpgsm.c (main): Update to new assuan API. * server.c: Include "gpgsm.h" before <assuan.h> due to check for GPG_ERR_SOURCE_DEFAULT and assuan.h now including gpg-error.h. (option_handler, cmd_recipient, cmd_signer, cmd_encrypt) (cmd_decrypt, cmd_verify, cmd_sign, cmd_import, cmd_export) (cmd_delkeys, cmd_message, cmd_listkeys, cmd_dumpkeys) (cmd_listsecretkeys, cmd_dumpsecretkeys, cmd_genkey) (cmd_getauditlog, cmd_getinfo): Return gpg_error_t instead of int. (register_commands): Same for member HANDLER in table. (gpgsm_server): Allocate assuan context before starting server. * sm/call-dirmngr.c: * call-dirmngr.c (prepare_dirmngr): Check for CTX and error before setting LDAPSERVER. (start_dirmngr_ext): Allocate assuan context before starting server. (inq_certificate, isvalid_status_cb, lookup_cb, lookup_status_cb) (run_command_cb, run_command_inq_cb, run_command_status_cb): Return gpg_error_t instead of int. tools/ 2009-09-23 Marcus Brinkmann <marcus@g10code.de> * gpg-connect-agent.c (getinfo_pid_cb, read_and_print_response) (main): Update to new Assuan API.
2009-09-23 00:01:25 +00:00
static gpg_error_t
inq_quality (void *opaque, const char *line)
{
assuan_context_t ctx = opaque;
const char *s;
char *pin;
int rc;
int percent;
char numbuf[20];
if ((s = has_leading_keyword (line, "QUALITY")))
{
pin = unescape_passphrase_string (s);
if (!pin)
rc = gpg_error_from_syserror ();
else
{
percent = estimate_passphrase_quality (pin);
if (check_passphrase_constraints (NULL, pin, NULL))
percent = -percent;
snprintf (numbuf, sizeof numbuf, "%d", percent);
rc = assuan_send_data (ctx, numbuf, strlen (numbuf));
xfree (pin);
}
}
else
{
log_error ("unsupported inquiry '%s' from pinentry\n", line);
rc = gpg_error (GPG_ERR_ASS_UNKNOWN_INQUIRE);
}
return rc;
}
/* Helper for agent_askpin and agent_get_passphrase. */
static gpg_error_t
setup_qualitybar (ctrl_t ctrl)
{
int rc;
char line[ASSUAN_LINELENGTH];
char *tmpstr, *tmpstr2;
const char *tooltip;
(void)ctrl;
/* TRANSLATORS: This string is displayed by Pinentry as the label
for the quality bar. */
tmpstr = try_percent_escape (L_("Quality:"), "\t\r\n\f\v");
snprintf (line, DIM(line), "SETQUALITYBAR %s", tmpstr? tmpstr:"");
xfree (tmpstr);
rc = assuan_transact (entry_ctx, line, NULL, NULL, NULL, NULL, NULL, NULL);
if (rc == 103 /*(Old assuan error code)*/
|| gpg_err_code (rc) == GPG_ERR_ASS_UNKNOWN_CMD)
; /* Ignore Unknown Command from old Pinentry versions. */
else if (rc)
return rc;
tmpstr2 = gnupg_get_help_string ("pinentry.qualitybar.tooltip", 0);
if (tmpstr2)
tooltip = tmpstr2;
else
{
/* TRANSLATORS: This string is a tooltip, shown by pinentry when
hovering over the quality bar. Please use an appropriate
string to describe what this is about. The length of the
tooltip is limited to about 900 characters. If you do not
translate this entry, a default english text (see source)
will be used. */
tooltip = L_("pinentry.qualitybar.tooltip");
if (!strcmp ("pinentry.qualitybar.tooltip", tooltip))
tooltip = ("The quality of the text entered above.\n"
"Please ask your administrator for "
"details about the criteria.");
}
tmpstr = try_percent_escape (tooltip, "\t\r\n\f\v");
xfree (tmpstr2);
snprintf (line, DIM(line), "SETQUALITYBAR_TT %s", tmpstr? tmpstr:"");
xfree (tmpstr);
rc = assuan_transact (entry_ctx, line, NULL, NULL, NULL, NULL, NULL, NULL);
if (rc == 103 /*(Old assuan error code)*/
|| gpg_err_code (rc) == GPG_ERR_ASS_UNKNOWN_CMD)
; /* Ignore Unknown Command from old pinentry versions. */
else if (rc)
return rc;
return 0;
}
/* Check the button_info line for a close action. Also check for the
PIN_REPEATED flag. */
static gpg_error_t
pinentry_status_cb (void *opaque, const char *line)
{
unsigned int *flag = opaque;
const char *args;
if ((args = has_leading_keyword (line, "BUTTON_INFO")))
{
if (!strcmp (args, "close"))
*flag |= PINENTRY_STATUS_CLOSE_BUTTON;
}
else if (has_leading_keyword (line, "PIN_REPEATED"))
{
*flag |= PINENTRY_STATUS_PIN_REPEATED;
}
else if (has_leading_keyword (line, "PASSWORD_FROM_CACHE"))
{
*flag |= PINENTRY_STATUS_PASSWORD_FROM_CACHE;
}
return 0;
}
/* Build a SETDESC command line. This is a dedicated function so that
* it can remove control characters which are not supported by the
* current Pinentry. */
static void
build_cmd_setdesc (char *line, size_t linelen, const char *desc)
{
char *src, *dst;
snprintf (line, linelen, "SETDESC %s", desc);
if (!entry_features.tabbing)
{
/* Remove RS and US. */
for (src=dst=line; *src; src++)
if (!strchr ("\x1e\x1f", *src))
*dst++ = *src;
*dst = 0;
}
}
/* Watch the socket's EOF condition, while checking finish of
foreground thread. When EOF condition is detected, terminate
the pinentry process behind the assuan pipe.
*/
static void *
watch_sock (void *arg)
{
pid_t pid = assuan_get_pid (entry_ctx);
while (1)
{
int err;
fd_set fdset;
struct timeval timeout = { 0, 500000 };
gnupg_fd_t sock = *(gnupg_fd_t *)arg;
if (sock == GNUPG_INVALID_FD)
return NULL;
FD_ZERO (&fdset);
FD_SET (FD2INT (sock), &fdset);
err = npth_select (FD2INT (sock)+1, &fdset, NULL, NULL, &timeout);
if (err < 0)
{
if (errno == EINTR)
continue;
else
return NULL;
}
/* Possibly, it's EOF. */
if (err > 0)
break;
}
if (pid == (pid_t)(-1))
; /* No pid available can't send a kill. */
#ifdef HAVE_W32_SYSTEM
/* Older versions of assuan set PID to 0 on Windows to indicate an
invalid value. */
else if (pid != (pid_t) INVALID_HANDLE_VALUE && pid != 0)
TerminateProcess ((HANDLE)pid, 1);
#else
else if (pid > 0)
kill (pid, SIGINT);
#endif
return NULL;
}
static gpg_error_t
watch_sock_start (gnupg_fd_t *sock_p, npth_t *thread_p)
{
npth_attr_t tattr;
int err;
err = npth_attr_init (&tattr);
if (err)
{
log_error ("do_getpin: error npth_attr_init: %s\n", strerror (err));
return gpg_error_from_errno (err);
}
npth_attr_setdetachstate (&tattr, NPTH_CREATE_JOINABLE);
err = npth_create (thread_p, &tattr, watch_sock, sock_p);
npth_attr_destroy (&tattr);
if (err)
{
log_error ("do_getpin: error spawning thread: %s\n", strerror (err));
return gpg_error_from_errno (err);
}
return 0;
}
static void
watch_sock_end (gnupg_fd_t *sock_p, npth_t *thread_p)
{
int err;
*sock_p = GNUPG_INVALID_FD;
err = npth_join (*thread_p, NULL);
if (err)
log_error ("watch_sock_end: error joining thread: %s\n", strerror (err));
}
/* Ask pinentry to get a pin by "GETPIN" command, spawning a thread
detecting the socket's EOF.
*/
static gpg_error_t
do_getpin (ctrl_t ctrl, struct entry_parm_s *parm)
{
gpg_error_t rc;
int saveflag = assuan_get_flag (entry_ctx, ASSUAN_CONFIDENTIAL);
gnupg_fd_t sock_watched = ctrl->thread_startup.fd;
npth_t thread;
rc = watch_sock_start (&sock_watched, &thread);
if (rc)
return rc;
assuan_begin_confidential (entry_ctx);
rc = assuan_transact (entry_ctx, "GETPIN", getpin_cb, parm,
inq_quality, entry_ctx,
pinentry_status_cb, &parm->status);
assuan_set_flag (entry_ctx, ASSUAN_CONFIDENTIAL, saveflag);
/* Most pinentries out in the wild return the old Assuan error code
for canceled which gets translated to an assuan Cancel error and
not to the code for a user cancel. Fix this here. */
if (rc && gpg_err_source (rc) && gpg_err_code (rc) == GPG_ERR_ASS_CANCELED)
rc = gpg_err_make (gpg_err_source (rc), GPG_ERR_CANCELED);
/* Change error code in case the window close button was clicked
to cancel the operation. */
if ((parm->status & PINENTRY_STATUS_CLOSE_BUTTON)
&& gpg_err_code (rc) == GPG_ERR_CANCELED)
rc = gpg_err_make (gpg_err_source (rc), GPG_ERR_FULLY_CANCELED);
watch_sock_end (&sock_watched, &thread);
return rc;
}
/* Call the Entry and ask for the PIN. We do check for a valid PIN
number here and repeat it as long as we have invalid formed
numbers. KEYINFO and CACHE_MODE are used to tell pinentry something
about the key. */
gpg_error_t
agent_askpin (ctrl_t ctrl,
const char *desc_text, const char *prompt_text,
const char *initial_errtext,
struct pin_entry_info_s *pininfo,
const char *keyinfo, cache_mode_t cache_mode)
{
gpg_error_t rc;
char line[ASSUAN_LINELENGTH];
struct entry_parm_s parm;
const char *errtext = NULL;
int is_pin = 0;
if (opt.batch)
return 0; /* fixme: we should return BAD PIN */
if (ctrl->pinentry_mode != PINENTRY_MODE_ASK)
{
if (ctrl->pinentry_mode == PINENTRY_MODE_CANCEL)
return gpg_error (GPG_ERR_CANCELED);
if (ctrl->pinentry_mode == PINENTRY_MODE_LOOPBACK)
{
unsigned char *passphrase;
size_t size;
*pininfo->pin = 0; /* Reset the PIN. */
rc = pinentry_loopback (ctrl, "PASSPHRASE", &passphrase, &size,
pininfo->max_length - 1);
if (rc)
return rc;
memcpy(&pininfo->pin, passphrase, size);
xfree(passphrase);
pininfo->pin[size] = 0;
if (pininfo->check_cb)
{
/* More checks by utilizing the optional callback. */
pininfo->cb_errtext = NULL;
rc = pininfo->check_cb (pininfo);
}
return rc;
}
return gpg_error(GPG_ERR_NO_PIN_ENTRY);
}
if (!pininfo || pininfo->max_length < 1)
return gpg_error (GPG_ERR_INV_VALUE);
if (!desc_text && pininfo->min_digits)
desc_text = L_("Please enter your PIN, so that the secret key "
"can be unlocked for this session");
else if (!desc_text)
desc_text = L_("Please enter your passphrase, so that the secret key "
"can be unlocked for this session");
if (prompt_text)
is_pin = !!strstr (prompt_text, "PIN");
else
is_pin = desc_text && strstr (desc_text, "PIN");
rc = start_pinentry (ctrl);
if (rc)
return rc;
/* If we have a KEYINFO string and are normal, user, or ssh cache
mode, we tell that the Pinentry so it may use it for own caching
purposes. Most pinentries won't have this implemented and thus
we do not error out in this case. */
if (keyinfo && (cache_mode == CACHE_MODE_NORMAL
|| cache_mode == CACHE_MODE_USER
|| cache_mode == CACHE_MODE_SSH))
snprintf (line, DIM(line), "SETKEYINFO %c/%s",
cache_mode == CACHE_MODE_USER? 'u' :
cache_mode == CACHE_MODE_SSH? 's' : 'n',
keyinfo);
else
snprintf (line, DIM(line), "SETKEYINFO --clear");
rc = assuan_transact (entry_ctx, line,
NULL, NULL, NULL, NULL, NULL, NULL);
if (rc && gpg_err_code (rc) != GPG_ERR_ASS_UNKNOWN_CMD)
return unlock_pinentry (ctrl, rc);
build_cmd_setdesc (line, DIM(line), desc_text);
rc = assuan_transact (entry_ctx, line, NULL, NULL, NULL, NULL, NULL, NULL);
if (rc)
return unlock_pinentry (ctrl, rc);
snprintf (line, DIM(line), "SETPROMPT %s",
prompt_text? prompt_text : is_pin? L_("PIN:") : L_("Passphrase:"));
rc = assuan_transact (entry_ctx, line, NULL, NULL, NULL, NULL, NULL, NULL);
if (rc)
return unlock_pinentry (ctrl, rc);
/* If a passphrase quality indicator has been requested and a
minimum passphrase length has not been disabled, send the command
to the pinentry. */
if (pininfo->with_qualitybar && opt.min_passphrase_len )
{
rc = setup_qualitybar (ctrl);
if (rc)
return unlock_pinentry (ctrl, rc);
}
if (initial_errtext)
{
snprintf (line, DIM(line), "SETERROR %s", initial_errtext);
rc = assuan_transact (entry_ctx, line,
NULL, NULL, NULL, NULL, NULL, NULL);
if (rc)
return unlock_pinentry (ctrl, rc);
}
if (pininfo->with_repeat)
{
snprintf (line, DIM(line), "SETREPEATERROR %s",
L_("does not match - try again"));
rc = assuan_transact (entry_ctx, line,
NULL, NULL, NULL, NULL, NULL, NULL);
if (rc)
pininfo->with_repeat = 0; /* Pinentry does not support it. */
}
pininfo->repeat_okay = 0;
pininfo->status = 0;
for (;pininfo->failed_tries < pininfo->max_tries; pininfo->failed_tries++)
{
memset (&parm, 0, sizeof parm);
parm.size = pininfo->max_length;
*pininfo->pin = 0; /* Reset the PIN. */
parm.buffer = (unsigned char*)pininfo->pin;
if (errtext)
{
/* TRANSLATORS: The string is appended to an error message in
the pinentry. The %s is the actual error message, the
two %d give the current and maximum number of tries. */
snprintf (line, DIM(line), L_("SETERROR %s (try %d of %d)"),
errtext, pininfo->failed_tries+1, pininfo->max_tries);
rc = assuan_transact (entry_ctx, line,
NULL, NULL, NULL, NULL, NULL, NULL);
if (rc)
return unlock_pinentry (ctrl, rc);
errtext = NULL;
}
if (pininfo->with_repeat)
{
snprintf (line, DIM(line), "SETREPEAT %s", L_("Repeat:"));
rc = assuan_transact (entry_ctx, line,
NULL, NULL, NULL, NULL, NULL, NULL);
if (rc)
return unlock_pinentry (ctrl, rc);
}
rc = do_getpin (ctrl, &parm);
pininfo->status = parm.status;
2006-09-06 16:35:52 +00:00
if (gpg_err_code (rc) == GPG_ERR_ASS_TOO_MUCH_DATA)
errtext = is_pin? L_("PIN too long")
: L_("Passphrase too long");
else if (rc)
return unlock_pinentry (ctrl, rc);
if (!errtext && pininfo->min_digits)
{
/* do some basic checks on the entered PIN. */
if (!all_digitsp (pininfo->pin))
errtext = L_("Invalid characters in PIN");
else if (pininfo->max_digits
&& strlen (pininfo->pin) > pininfo->max_digits)
errtext = L_("PIN too long");
else if (strlen (pininfo->pin) < pininfo->min_digits)
errtext = L_("PIN too short");
}
if (!errtext && pininfo->check_cb)
{
/* More checks by utilizing the optional callback. */
pininfo->cb_errtext = NULL;
rc = pininfo->check_cb (pininfo);
/* When pinentry cache causes an error, return now. */
if (rc
&& (pininfo->status & PINENTRY_STATUS_PASSWORD_FROM_CACHE))
return unlock_pinentry (ctrl, rc);
if (gpg_err_code (rc) == GPG_ERR_BAD_PASSPHRASE)
{
if (pininfo->cb_errtext)
errtext = pininfo->cb_errtext;
else if (gpg_err_code (rc) == GPG_ERR_BAD_PASSPHRASE
|| gpg_err_code (rc) == GPG_ERR_BAD_PIN)
errtext = (is_pin? L_("Bad PIN") : L_("Bad Passphrase"));
}
else if (rc)
return unlock_pinentry (ctrl, rc);
}
if (!errtext)
{
if (pininfo->with_repeat
&& (pininfo->status & PINENTRY_STATUS_PIN_REPEATED))
pininfo->repeat_okay = 1;
return unlock_pinentry (ctrl, 0); /* okay, got a PIN or passphrase */
}
if ((pininfo->status & PINENTRY_STATUS_PASSWORD_FROM_CACHE))
/* The password was read from the cache. Don't count this
against the retry count. */
pininfo->failed_tries --;
}
return unlock_pinentry (ctrl, gpg_error (pininfo->min_digits? GPG_ERR_BAD_PIN
: GPG_ERR_BAD_PASSPHRASE));
}
2006-10-04 16:45:04 +00:00
/* Ask for the passphrase using the supplied arguments. The returned
passphrase needs to be freed by the caller. */
int
2005-11-28 11:52:25 +00:00
agent_get_passphrase (ctrl_t ctrl,
char **retpass, const char *desc, const char *prompt,
const char *errtext, int with_qualitybar,
const char *keyinfo, cache_mode_t cache_mode)
{
int rc;
char line[ASSUAN_LINELENGTH];
struct entry_parm_s parm;
*retpass = NULL;
if (opt.batch)
return gpg_error (GPG_ERR_BAD_PASSPHRASE);
if (ctrl->pinentry_mode != PINENTRY_MODE_ASK)
{
if (ctrl->pinentry_mode == PINENTRY_MODE_CANCEL)
return gpg_error (GPG_ERR_CANCELED);
if (ctrl->pinentry_mode == PINENTRY_MODE_LOOPBACK)
{
size_t size;
return pinentry_loopback (ctrl, "PASSPHRASE",
(unsigned char **)retpass, &size,
MAX_PASSPHRASE_LEN);
}
return gpg_error (GPG_ERR_NO_PIN_ENTRY);
}
rc = start_pinentry (ctrl);
if (rc)
return rc;
if (!prompt)
prompt = desc && strstr (desc, "PIN")? L_("PIN:"): L_("Passphrase:");
/* If we have a KEYINFO string and are normal, user, or ssh cache
mode, we tell that the Pinentry so it may use it for own caching
purposes. Most pinentries won't have this implemented and thus
we do not error out in this case. */
if (keyinfo && (cache_mode == CACHE_MODE_NORMAL
|| cache_mode == CACHE_MODE_USER
|| cache_mode == CACHE_MODE_SSH))
snprintf (line, DIM(line), "SETKEYINFO %c/%s",
cache_mode == CACHE_MODE_USER? 'u' :
cache_mode == CACHE_MODE_SSH? 's' : 'n',
keyinfo);
else
snprintf (line, DIM(line), "SETKEYINFO --clear");
rc = assuan_transact (entry_ctx, line,
NULL, NULL, NULL, NULL, NULL, NULL);
if (rc && gpg_err_code (rc) != GPG_ERR_ASS_UNKNOWN_CMD)
return unlock_pinentry (ctrl, rc);
if (desc)
build_cmd_setdesc (line, DIM(line), desc);
else
snprintf (line, DIM(line), "RESET");
rc = assuan_transact (entry_ctx, line, NULL, NULL, NULL, NULL, NULL, NULL);
if (rc)
return unlock_pinentry (ctrl, rc);
snprintf (line, DIM(line), "SETPROMPT %s", prompt);
rc = assuan_transact (entry_ctx, line, NULL, NULL, NULL, NULL, NULL, NULL);
if (rc)
return unlock_pinentry (ctrl, rc);
if (with_qualitybar && opt.min_passphrase_len)
{
rc = setup_qualitybar (ctrl);
if (rc)
return unlock_pinentry (ctrl, rc);
}
if (errtext)
{
snprintf (line, DIM(line), "SETERROR %s", errtext);
rc = assuan_transact (entry_ctx, line, NULL, NULL, NULL, NULL, NULL, NULL);
if (rc)
return unlock_pinentry (ctrl, rc);
}
memset (&parm, 0, sizeof parm);
parm.size = ASSUAN_LINELENGTH/2 - 5;
parm.buffer = gcry_malloc_secure (parm.size+10);
if (!parm.buffer)
return unlock_pinentry (ctrl, out_of_core ());
rc = do_getpin (ctrl, &parm);
if (rc)
2006-10-04 16:45:04 +00:00
xfree (parm.buffer);
else
*retpass = parm.buffer;
return unlock_pinentry (ctrl, rc);
}
/* Pop up the PIN-entry, display the text and the prompt and ask the
2005-11-28 11:52:25 +00:00
user to confirm this. We return 0 for success, ie. the user
confirmed it, GPG_ERR_NOT_CONFIRMED for what the text says or an
other error. If WITH_CANCEL it true an extra cancel button is
displayed to allow the user to easily return a GPG_ERR_CANCELED.
if the Pinentry does not support this, the user can still cancel by
closing the Pinentry window. */
int
2005-11-28 11:52:25 +00:00
agent_get_confirmation (ctrl_t ctrl,
const char *desc, const char *ok,
const char *notok, int with_cancel)
{
int rc;
char line[ASSUAN_LINELENGTH];
if (ctrl->pinentry_mode != PINENTRY_MODE_ASK)
{
if (ctrl->pinentry_mode == PINENTRY_MODE_CANCEL)
return gpg_error (GPG_ERR_CANCELED);
if (ctrl->pinentry_mode == PINENTRY_MODE_LOOPBACK)
return pinentry_loopback_confirm (ctrl, desc, 1, ok, notok);
return gpg_error (GPG_ERR_NO_PIN_ENTRY);
}
rc = start_pinentry (ctrl);
if (rc)
return rc;
if (desc)
build_cmd_setdesc (line, DIM(line), desc);
else
snprintf (line, DIM(line), "RESET");
rc = assuan_transact (entry_ctx, line, NULL, NULL, NULL, NULL, NULL, NULL);
2006-10-04 16:45:04 +00:00
/* Most pinentries out in the wild return the old Assuan error code
for canceled which gets translated to an assuan Cancel error and
not to the code for a user cancel. Fix this here. */
if (rc && gpg_err_source (rc) && gpg_err_code (rc) == GPG_ERR_ASS_CANCELED)
rc = gpg_err_make (gpg_err_source (rc), GPG_ERR_CANCELED);
if (rc)
return unlock_pinentry (ctrl, rc);
if (ok)
{
snprintf (line, DIM(line), "SETOK %s", ok);
rc = assuan_transact (entry_ctx,
line, NULL, NULL, NULL, NULL, NULL, NULL);
if (rc)
return unlock_pinentry (ctrl, rc);
}
if (notok)
{
/* Try to use the newer NOTOK feature if a cancel button is
requested. If no cancel button is requested we keep on using
the standard cancel. */
if (with_cancel)
{
snprintf (line, DIM(line), "SETNOTOK %s", notok);
rc = assuan_transact (entry_ctx,
line, NULL, NULL, NULL, NULL, NULL, NULL);
}
else
rc = GPG_ERR_ASS_UNKNOWN_CMD;
if (gpg_err_code (rc) == GPG_ERR_ASS_UNKNOWN_CMD)
{
snprintf (line, DIM(line), "SETCANCEL %s", notok);
rc = assuan_transact (entry_ctx, line,
NULL, NULL, NULL, NULL, NULL, NULL);
}
if (rc)
return unlock_pinentry (ctrl, rc);
}
{
gnupg_fd_t sock_watched = ctrl->thread_startup.fd;
npth_t thread;
2006-10-16 17:36:44 +00:00
rc = watch_sock_start (&sock_watched, &thread);
if (!rc)
{
rc = assuan_transact (entry_ctx, "CONFIRM",
NULL, NULL, NULL, NULL, NULL, NULL);
if (rc && gpg_err_source (rc)
&& gpg_err_code (rc) == GPG_ERR_ASS_CANCELED)
rc = gpg_err_make (gpg_err_source (rc), GPG_ERR_CANCELED);
watch_sock_end (&sock_watched, &thread);
}
return unlock_pinentry (ctrl, rc);
}
}
2005-11-28 11:52:25 +00:00
/* The thread running the popup message. */
static void *
popup_message_thread (void *arg)
{
gpg_error_t rc;
gnupg_fd_t sock_watched = *(gnupg_fd_t *)arg;
npth_t thread;
rc = watch_sock_start (&sock_watched, &thread);
if (rc)
return NULL;
/* We use the --one-button hack instead of the MESSAGE command to
allow the use of old Pinentries. Those old Pinentries will then
show an additional Cancel button but that is mostly a visual
annoyance. */
assuan_transact (entry_ctx, "CONFIRM --one-button",
NULL, NULL, NULL, NULL, NULL, NULL);
watch_sock_end (&sock_watched, &thread);
2005-11-28 11:52:25 +00:00
popup_finished = 1;
return NULL;
}
/* Pop up a message window similar to the confirm one but keep it open
until agent_popup_message_stop has been called. It is crucial for
the caller to make sure that the stop function gets called as soon
as the message is not anymore required because the message is
2005-11-28 11:52:25 +00:00
system modal and all other attempts to use the pinentry will fail
(after a timeout). */
int
agent_popup_message_start (ctrl_t ctrl, const char *desc, const char *ok_btn)
2005-11-28 11:52:25 +00:00
{
int rc;
char line[ASSUAN_LINELENGTH];
npth_attr_t tattr;
int err;
2005-11-28 11:52:25 +00:00
if (ctrl->pinentry_mode != PINENTRY_MODE_ASK)
{
if (ctrl->pinentry_mode == PINENTRY_MODE_CANCEL)
return gpg_error (GPG_ERR_CANCELED);
if (ctrl->pinentry_mode == PINENTRY_MODE_LOOPBACK)
return pinentry_loopback_confirm (ctrl, desc, 0, ok_btn, NULL);
return gpg_error (GPG_ERR_NO_PIN_ENTRY);
}
2005-11-28 11:52:25 +00:00
rc = start_pinentry (ctrl);
if (rc)
return rc;
if (desc)
build_cmd_setdesc (line, DIM(line), desc);
2005-11-28 11:52:25 +00:00
else
snprintf (line, DIM(line), "RESET");
2005-11-28 11:52:25 +00:00
rc = assuan_transact (entry_ctx, line, NULL, NULL, NULL, NULL, NULL, NULL);
if (rc)
return unlock_pinentry (ctrl, rc);
2005-11-28 11:52:25 +00:00
if (ok_btn)
{
snprintf (line, DIM(line), "SETOK %s", ok_btn);
2005-11-28 11:52:25 +00:00
rc = assuan_transact (entry_ctx, line, NULL,NULL,NULL,NULL,NULL,NULL);
if (rc)
return unlock_pinentry (ctrl, rc);
2005-11-28 11:52:25 +00:00
}
err = npth_attr_init (&tattr);
if (err)
return unlock_pinentry (ctrl, gpg_error_from_errno (err));
npth_attr_setdetachstate (&tattr, NPTH_CREATE_JOINABLE);
2005-11-28 11:52:25 +00:00
popup_finished = 0;
err = npth_create (&popup_tid, &tattr, popup_message_thread,
&ctrl->thread_startup.fd);
npth_attr_destroy (&tattr);
if (err)
2005-11-28 11:52:25 +00:00
{
rc = gpg_error_from_errno (err);
2005-11-28 11:52:25 +00:00
log_error ("error spawning popup message handler: %s\n",
strerror (err) );
return unlock_pinentry (ctrl, rc);
2005-11-28 11:52:25 +00:00
}
npth_setname_np (popup_tid, "popup-message");
2005-11-28 11:52:25 +00:00
return 0;
}
/* Close a popup window. */
void
agent_popup_message_stop (ctrl_t ctrl)
{
int rc;
pid_t pid;
(void)ctrl;
if (ctrl->pinentry_mode == PINENTRY_MODE_LOOPBACK)
return;
2005-11-28 11:52:25 +00:00
if (!popup_tid || !entry_ctx)
{
log_debug ("agent_popup_message_stop called with no active popup\n");
return;
2005-11-28 11:52:25 +00:00
}
pid = assuan_get_pid (entry_ctx);
if (pid == (pid_t)(-1))
; /* No pid available can't send a kill. */
else if (popup_finished)
; /* Already finished and ready for joining. */
#ifdef HAVE_W32_SYSTEM
/* Older versions of assuan set PID to 0 on Windows to indicate an
invalid value. */
else if (pid != (pid_t) INVALID_HANDLE_VALUE
&& pid != 0)
{
HANDLE process = (HANDLE) pid;
/* Arbitrary error code. */
TerminateProcess (process, 1);
}
#else
else if (pid > 0)
kill (pid, SIGINT);
#endif
2005-11-28 11:52:25 +00:00
/* Now wait for the thread to terminate. */
rc = npth_join (popup_tid, NULL);
if (rc)
2005-11-28 11:52:25 +00:00
log_debug ("agent_popup_message_stop: pth_join failed: %s\n",
strerror (rc));
/* Thread IDs are opaque, but we try our best here by resetting it
to the same content that a static global variable has. */
memset (&popup_tid, '\0', sizeof (popup_tid));
2005-11-28 11:52:25 +00:00
/* Now we can close the connection. */
unlock_pinentry (ctrl, 0);
2005-11-28 11:52:25 +00:00
}
int
agent_clear_passphrase (ctrl_t ctrl,
const char *keyinfo, cache_mode_t cache_mode)
{
int rc;
char line[ASSUAN_LINELENGTH];
if (! (keyinfo && (cache_mode == CACHE_MODE_NORMAL
|| cache_mode == CACHE_MODE_USER
|| cache_mode == CACHE_MODE_SSH)))
return gpg_error (GPG_ERR_NOT_SUPPORTED);
rc = start_pinentry (ctrl);
if (rc)
return rc;
snprintf (line, DIM(line), "CLEARPASSPHRASE %c/%s",
cache_mode == CACHE_MODE_USER? 'u' :
cache_mode == CACHE_MODE_SSH? 's' : 'n',
keyinfo);
rc = assuan_transact (entry_ctx, line,
NULL, NULL, NULL, NULL, NULL, NULL);
return unlock_pinentry (ctrl, rc);
}