Made some PIN pads work.

Some cleanups for 64 bit CPUs.
This commit is contained in:
Werner Koch 2006-11-20 16:49:41 +00:00
parent 267d4c8fa7
commit 5885142c83
36 changed files with 502 additions and 171 deletions

View File

@ -1,3 +1,7 @@
2006-11-15 Werner Koch <wk@g10code.com>
* autogen.sh: Add convenience option --build-amd64.
2006-11-14 Werner Koch <wk@g10code.com>
* configure.ac (HAVE_ASSUAN_SET_IO_MONITOR): Test for it.

View File

@ -22,7 +22,7 @@
ACLOCAL_AMFLAGS = -I m4 -I gl/m4
AUTOMAKE_OPTIONS = dist-bzip2
DISTCHECK_CONFIGURE_FLAGS = --enable-gpg
DISTCHECK_CONFIGURE_FLAGS = --enable-symcryptrun
EXTRA_DIST = scripts/config.rpath autogen.sh README.SVN
DISTCLEANFILES = g10defs.h

5
NEWS
View File

@ -1,6 +1,11 @@
Noteworthy changes in version 2.0.1
-------------------------------------------------
* Experimental support for the PIN pads of the SPR 532 and the Kaan
Advanced card readers. Add "disable-keypad" scdaemon.conf if you
don't want it. Does currently only work for the OpenPGP card and
the authentication and decrypt keys.
Noteworthy changes in version 2.0.0 (2006-11-11)
-------------------------------------------------

1
THANKS
View File

@ -90,6 +90,7 @@ Greg Troxel gdt at ir.bbn.com
Gregory Steuck steuck at iname.com
Harald Denker harry at hal.westfalen.de
Holger Baust Holger.Baust at freenet-ag.de
Henrik Nordstrom henrik at henriknordstrom.net
Hendrik Buschkamp buschkamp at rheumanet.org
Holger Schurig holger at d.om.org
Holger Smolinski smolinsk at de.ibm.com

View File

@ -1,3 +1,27 @@
2006-11-20 Werner Koch <wk@g10code.com>
* call-pinentry.c (agent_popup_message_stop): Use SIGKILL.
* call-scd.c (inq_needpin): Implement POPUPKEYPADPROMPT and
DISMISSKEYPADPROMPT.
2006-11-15 Werner Koch <wk@g10code.com>
* protect.c (make_shadow_info): Cast printf arg to unsigned int.
* minip12.c (parse_bag_encrypted_data): Ditto.
(parse_bag_data, p12_parse): Ditto.
* command-ssh.c (ssh_identity_register): Changed buffer_n to
size_t.
* agent.h (struct server_control_s): New field thread_startup.
* command.c (start_command_handler): Moved CTRL init code to ..
* gpg-agent.c (start_connection_thread): .. here.
(agent_deinit_default_ctrl): New.
(agent_init_default_ctrl): Made static.
(handle_connections): Allocate CTRL and pass it pth_spawn.
* command-ssh.c (start_command_handler_ssh): Moved CTRL init code
to ..
* gpg-agent.c (start_connection_thread_ssh): .. here.
2006-11-14 Werner Koch <wk@g10code.com>
* command.c (bump_key_eventcounter): New.

View File

@ -112,6 +112,12 @@ struct scd_local_s;
/* Collection of data per session (aka connection). */
struct server_control_s
{
/* Private data used to fire up the connection thread. We use this
structure do avoid an extra allocation for just a few bytes. */
struct {
int fd;
} thread_startup;
/* Private data of the server (command.c). */
struct server_local_s *server_local;
@ -178,16 +184,15 @@ cache_mode_t;
/*-- gpg-agent.c --*/
void agent_exit (int rc) JNLIB_GCC_A_NR; /* Also implemented in other tools */
void agent_init_default_ctrl (struct server_control_s *ctrl);
/*-- command.c --*/
gpg_error_t agent_write_status (ctrl_t ctrl, const char *keyword, ...);
void bump_key_eventcounter (void);
void bump_card_eventcounter (void);
void start_command_handler (int, int);
void start_command_handler (ctrl_t, int, int);
/*-- command-ssh.c --*/
void start_command_handler_ssh (int);
void start_command_handler_ssh (ctrl_t, int);
/*-- findkey.c --*/
int agent_write_private_key (const unsigned char *grip,

View File

@ -636,7 +636,7 @@ popup_message_thread (void *arg)
/* 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 becuase the message is
as the message is not anymore required because the message is
system modal and all other attempts to use the pinentry will fail
(after a timeout). */
int
@ -723,8 +723,9 @@ agent_popup_message_stop (ctrl_t ctrl)
if (rc == pid)
assuan_set_flag (entry_ctx, ASSUAN_NO_WAITPID, 1);
}
else
kill (pid, SIGINT);
else if (pid > 0)
kill (pid, SIGKILL); /* Need to use SIGKILL due to bad
interaction of SIGINT with Pth. */
/* Now wait for the thread to terminate. */
rc = pth_join (popup_tid, NULL);

View File

@ -711,17 +711,19 @@ inq_needpin (void *opaque, const char *line)
rc = assuan_send_data (parm->ctx, pin, pinlen);
xfree (pin);
}
else if (!strncmp (line, "KEYPADINFO", 10) && (line[10] == ' ' || !line[10]))
else if (!strncmp (line, "POPUPKEYPADPROMPT", 17)
&& (line[17] == ' ' || !line[17]))
{
size_t code;
char *endp;
code = strtoul (line+10, &endp, 10);
line = endp;
line += 17;
while (*line == ' ')
line++;
rc = parm->getpin_cb (parm->getpin_cb_arg, line, NULL, code);
rc = parm->getpin_cb (parm->getpin_cb_arg, line, NULL, 1);
}
else if (!strncmp (line, "DISMISSKEYPADPROMPT", 19)
&& (line[19] == ' ' || !line[19]))
{
rc = parm->getpin_cb (parm->getpin_cb_arg, "", NULL, 0);
}
else
{

View File

@ -2329,7 +2329,7 @@ ssh_identity_register (ctrl_t ctrl, gcry_sexp_t key, int ttl)
unsigned char key_grip_raw[20];
char key_grip[41];
unsigned char *buffer = NULL;
unsigned int buffer_n;
size_t buffer_n;
char *description = NULL;
char *comment = NULL;
unsigned int i;
@ -2821,32 +2821,28 @@ ssh_request_process (ctrl_t ctrl, estream_t stream_sock)
/* Start serving client on SOCK_CLIENT. */
void
start_command_handler_ssh (int sock_client)
start_command_handler_ssh (ctrl_t ctrl, int sock_client)
{
struct server_control_s ctrl;
estream_t stream_sock;
gpg_error_t err;
int ret;
/* Setup control structure. */
memset (&ctrl, 0, sizeof (ctrl));
agent_init_default_ctrl (&ctrl);
ctrl.connection_fd = sock_client;
ctrl->connection_fd = sock_client;
/* Because the ssh protocol does not send us information about the
the current TTY setting, we resort here to use those from startup
or those explictly set. */
if (!ctrl.display && opt.startup_display)
ctrl.display = strdup (opt.startup_display);
if (!ctrl.ttyname && opt.startup_ttyname)
ctrl.ttyname = strdup (opt.startup_ttyname);
if (!ctrl.ttytype && opt.startup_ttytype)
ctrl.ttytype = strdup (opt.startup_ttytype);
if (!ctrl.lc_ctype && opt.startup_lc_ctype)
ctrl.lc_ctype = strdup (opt.startup_lc_ctype);
if (!ctrl.lc_messages && opt.startup_lc_messages)
ctrl.lc_messages = strdup (opt.startup_lc_messages);
if (!ctrl->display && opt.startup_display)
ctrl->display = strdup (opt.startup_display);
if (!ctrl->ttyname && opt.startup_ttyname)
ctrl->ttyname = strdup (opt.startup_ttyname);
if (!ctrl->ttytype && opt.startup_ttytype)
ctrl->ttytype = strdup (opt.startup_ttytype);
if (!ctrl->lc_ctype && opt.startup_lc_ctype)
ctrl->lc_ctype = strdup (opt.startup_lc_ctype);
if (!ctrl->lc_messages && opt.startup_lc_messages)
ctrl->lc_messages = strdup (opt.startup_lc_messages);
/* Create stream from socket. */
@ -2870,20 +2866,14 @@ start_command_handler_ssh (int sock_client)
}
/* Main processing loop. */
while ( !ssh_request_process (&ctrl, stream_sock) )
while ( !ssh_request_process (ctrl, stream_sock) )
;
/* Reset the SCD in case it has been used. */
agent_reset_scd (&ctrl);
agent_reset_scd (ctrl);
out:
if (stream_sock)
es_fclose (stream_sock);
free (ctrl.display);
free (ctrl.ttyname);
free (ctrl.ttytype);
free (ctrl.lc_ctype);
free (ctrl.lc_messages);
}

View File

@ -1441,18 +1441,16 @@ register_commands (assuan_context_t ctx)
}
/* Startup the server. If LISTEN_FD and FD is given as -1, this is a simple
piper server, otherwise it is a regular server */
/* Startup the server. If LISTEN_FD and FD is given as -1, this is a
simple piper server, otherwise it is a regular server. CTRL is the
control structure for this connection; it has only the basic
intialization. */
void
start_command_handler (int listen_fd, int fd)
start_command_handler (ctrl_t ctrl, int listen_fd, int fd)
{
int rc;
assuan_context_t ctx;
struct server_control_s ctrl;
memset (&ctrl, 0, sizeof ctrl);
agent_init_default_ctrl (&ctrl);
if (listen_fd == -1 && fd == -1)
{
int filedes[2];
@ -1468,7 +1466,7 @@ start_command_handler (int listen_fd, int fd)
else
{
rc = assuan_init_socket_server_ext (&ctx, fd, 2);
ctrl.connection_fd = fd;
ctrl->connection_fd = fd;
}
if (rc)
{
@ -1484,12 +1482,12 @@ start_command_handler (int listen_fd, int fd)
agent_exit (2);
}
assuan_set_pointer (ctx, &ctrl);
ctrl.server_local = xcalloc (1, sizeof *ctrl.server_local);
ctrl.server_local->assuan_ctx = ctx;
ctrl.server_local->message_fd = -1;
ctrl.server_local->use_cache_for_signing = 1;
ctrl.digest.raw_value = 0;
assuan_set_pointer (ctx, ctrl);
ctrl->server_local = xcalloc (1, sizeof *ctrl->server_local);
ctrl->server_local->assuan_ctx = ctx;
ctrl->server_local->message_fd = -1;
ctrl->server_local->use_cache_for_signing = 1;
ctrl->digest.raw_value = 0;
if (DBG_ASSUAN)
assuan_set_log_stream (ctx, log_get_stream ());
@ -1520,22 +1518,14 @@ start_command_handler (int listen_fd, int fd)
}
/* Reset the SCD if needed. */
agent_reset_scd (&ctrl);
agent_reset_scd (ctrl);
/* Reset the pinentry (in case of popup messages). */
agent_reset_query (&ctrl);
agent_reset_query (ctrl);
/* Cleanup. */
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);
xfree (ctrl.server_local);
xfree (ctrl->server_local);
ctrl->server_local = NULL;
}

View File

@ -211,6 +211,9 @@ static char *create_socket_name (int use_standard_socket,
static int create_server_socket (int is_standard_name, const char *name);
static void create_directories (void);
static void agent_init_default_ctrl (ctrl_t ctrl);
static void agent_deinit_default_ctrl (ctrl_t ctrl);
static void handle_connections (int listen_fd, int listen_fd_ssh);
static int check_for_running_agent (int);
@ -813,8 +816,21 @@ main (int argc, char **argv )
if (pipe_server)
{ /* this is the simple pipe based server */
start_command_handler (-1, -1);
{
/* This is the simple pipe based server */
ctrl_t ctrl;
ctrl = xtrycalloc (1, sizeof *ctrl);
if (!ctrl)
{
log_error ("error allocating connection control data: %s\n",
strerror (errno) );
agent_exit (1);
}
agent_init_default_ctrl (ctrl);
start_command_handler (ctrl, -1, -1);
agent_deinit_default_ctrl (ctrl);
xfree (ctrl);
}
else if (!is_daemon)
; /* NOTREACHED */
@ -1073,8 +1089,8 @@ agent_exit (int rc)
}
void
agent_init_default_ctrl (struct server_control_s *ctrl)
static void
agent_init_default_ctrl (ctrl_t ctrl)
{
ctrl->connection_fd = -1;
@ -1103,6 +1119,21 @@ agent_init_default_ctrl (struct server_control_s *ctrl)
}
static void
agent_deinit_default_ctrl (ctrl_t ctrl)
{
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);
}
/* Reread parts of the configuration. Note, that this function is
obviously not thread-safe and should only be called from the PTH
signal handler.
@ -1437,17 +1468,20 @@ handle_signal (int signo)
static void *
start_connection_thread (void *arg)
{
int fd = (int)arg;
ctrl_t ctrl = arg;
agent_init_default_ctrl (ctrl);
if (opt.verbose)
log_info (_("handler 0x%lx for fd %d started\n"),
(long)pth_self (), fd);
(long)pth_self (), ctrl->thread_startup.fd);
start_command_handler (-1, fd);
start_command_handler (ctrl, -1, ctrl->thread_startup.fd);
if (opt.verbose)
log_info (_("handler 0x%lx for fd %d terminated\n"),
(long)pth_self (), fd);
(long)pth_self (), ctrl->thread_startup.fd);
agent_deinit_default_ctrl (ctrl);
xfree (ctrl);
return NULL;
}
@ -1456,17 +1490,20 @@ start_connection_thread (void *arg)
static void *
start_connection_thread_ssh (void *arg)
{
int fd = (int)arg;
ctrl_t ctrl = arg;
agent_init_default_ctrl (ctrl);
if (opt.verbose)
log_info (_("ssh handler 0x%lx for fd %d started\n"),
(long)pth_self (), fd);
(long)pth_self (), ctrl->thread_startup.fd);
start_command_handler_ssh (fd);
start_command_handler_ssh (ctrl, ctrl->thread_startup.fd);
if (opt.verbose)
log_info (_("ssh handler 0x%lx for fd %d terminated\n"),
(long)pth_self (), fd);
(long)pth_self (), ctrl->thread_startup.fd);
agent_deinit_default_ctrl (ctrl);
xfree (ctrl);
return NULL;
}
@ -1584,24 +1621,35 @@ handle_connections (int listen_fd, int listen_fd_ssh)
if (FD_ISSET (listen_fd, &read_fdset))
{
ctrl_t ctrl;
plen = sizeof paddr;
fd = pth_accept (listen_fd, (struct sockaddr *)&paddr, &plen);
if (fd == -1)
{
log_error ("accept failed: %s\n", strerror (errno));
}
else if ( !(ctrl = xtrycalloc (1, sizeof *ctrl)) )
{
log_error ("error allocating connection control data: %s\n",
strerror (errno) );
close (fd);
}
else
{
char threadname[50];
snprintf (threadname, sizeof threadname-1,
"conn fd=%d (gpg)", fd);
threadname[sizeof threadname -1] = 0;
pth_attr_set (tattr, PTH_ATTR_NAME, threadname);
if (!pth_spawn (tattr, start_connection_thread, (void*)fd))
ctrl->thread_startup.fd = fd;
if (!pth_spawn (tattr, start_connection_thread, ctrl))
{
log_error ("error spawning connection handler: %s\n",
strerror (errno) );
close (fd);
xfree (ctrl);
}
}
fd = -1;
@ -1609,25 +1657,36 @@ handle_connections (int listen_fd, int listen_fd_ssh)
if (listen_fd_ssh != -1 && FD_ISSET (listen_fd_ssh, &read_fdset))
{
ctrl_t ctrl;
plen = sizeof paddr;
fd = pth_accept (listen_fd_ssh, (struct sockaddr *)&paddr, &plen);
if (fd == -1)
{
log_error ("accept failed for ssh: %s\n", strerror (errno));
}
else if ( !(ctrl = xtrycalloc (1, sizeof *ctrl)) )
{
log_error ("error allocating connection control data: %s\n",
strerror (errno) );
close (fd);
}
else
{
char threadname[50];
agent_init_default_ctrl (ctrl);
snprintf (threadname, sizeof threadname-1,
"conn fd=%d (ssh)", fd);
threadname[sizeof threadname -1] = 0;
pth_attr_set (tattr, PTH_ATTR_NAME, threadname);
if (!pth_spawn (tattr, start_connection_thread_ssh, (void*)fd))
ctrl->thread_startup.fd = fd;
if (!pth_spawn (tattr, start_connection_thread_ssh, ctrl) )
{
log_error ("error spawning ssh connection handler: %s\n",
strerror (errno) );
close (fd);
xfree (ctrl);
}
}
fd = -1;

View File

@ -888,7 +888,7 @@ parse_bag_encrypted_data (const unsigned char *buffer, size_t length,
gcry_free (plain);
gcry_free (cram_buffer);
log_error ("encryptedData error at \"%s\", offset %u\n",
where, (p - p_start)+startoffset);
where, (unsigned int)((p - p_start)+startoffset));
if (bad_pass)
{
/* Note, that the following string might be used by other programs
@ -1133,7 +1133,7 @@ parse_bag_data (const unsigned char *buffer, size_t length, int startoffset,
}
gcry_free (cram_buffer);
log_error ( "data error at \"%s\", offset %u\n",
where, (p - buffer) + startoffset);
where, (unsigned int)((p - buffer) + startoffset));
if (r_consumed)
*r_consumed = consumed;
return NULL;
@ -1309,7 +1309,8 @@ p12_parse (const unsigned char *buffer, size_t length, const char *pw,
gcry_free (cram_buffer);
return result;
bailout:
log_error ("error at \"%s\", offset %u\n", where, (p - p_start));
log_error ("error at \"%s\", offset %u\n",
where, (unsigned int)(p - p_start));
if (result)
{
int i;

View File

@ -861,7 +861,7 @@ make_shadow_info (const char *serialno, const char *idstring)
p = stpcpy (p, numbuf);
for (s=serialno; *s && s[1]; s += 2)
*(unsigned char *)p++ = xtoi_2 (s);
sprintf (numbuf, "%d:", strlen (idstring));
sprintf (numbuf, "%u:", (unsigned int)strlen (idstring));
p = stpcpy (p, numbuf);
p = stpcpy (p, idstring);
*p++ = ')';

View File

@ -93,7 +93,55 @@ if test "$1" = "--build-w32"; then
fi
# ***** end W32 build script *******
# ***** AMD64 cross build script *******
# Used to cross-compile for AMD64 (for testing)
if test "$1" = "--build-amd64"; then
tmp=`dirname $0`
tsdir=`cd "$tmp"; pwd`
shift
if [ ! -f $tsdir/scripts/config.guess ]; then
echo "$tsdir/scripts/config.guess not found" >&2
exit 1
fi
build=`$tsdir/scripts/config.guess`
[ -z "$amd64root" ] && amd64root="$HOME/amd64root"
echo "Using $amd64root as standard install directory" >&2
# Locate the cross compiler
crossbindir=
for host in x86_64-linux-gnu amd64-linux-gnu; do
if ${host}-gcc --version >/dev/null 2>&1 ; then
crossbindir=/usr/${host}/bin
conf_CC="CC=${host}-gcc"
break;
fi
done
if [ -z "$crossbindir" ]; then
echo "Cross compiler kit not installed" >&2
echo "Stop." >&2
exit 1
fi
if [ -f "$tsdir/config.log" ]; then
if ! head $tsdir/config.log | grep "$host" >/dev/null; then
echo "Please run a 'make distclean' first" >&2
exit 1
fi
fi
$tsdir/configure --enable-maintainer-mode --prefix=${amd64root} \
--host=${host} --build=${build} \
--with-gpg-error-prefix=${amd64root} \
--with-ksba-prefix=${amd64root} \
--with-libgcrypt-prefix=${amd64root} \
--with-libassuan-prefix=${amd64root} \
--with-zlib=/usr/x86_64-linux-gnu/usr \
--with-pth-prefix=/usr/x86_64-linux-gnu/usr
rc=$?
exit $rc
fi
# ***** end AMD64 cross build script *******
# Grep the required versions from configure.ac

View File

@ -1,3 +1,11 @@
2006-11-15 Werner Koch <wk@g10code.com>
* estream.c: Disabled Pth soft mapping.
(my_funopen_hook_ret_t): New.
(print_fun_writer): Use it here.
* iobuf.c (fd_cache_close): Use %d instead of %p for debug output.
2006-11-03 Werner Koch <wk@g10code.com>
* Makefile.am (t_convert_DEPENDENCIES): Add libcommon. From

View File

@ -46,6 +46,9 @@
#endif
#ifdef HAVE_PTH
/* We explicitly need to disable soft mapping as Debian currently
enables it for no reason. */
# define PTH_SYSCALL_SOFT 0
# include <pth.h>
#endif
@ -59,6 +62,7 @@ void *memrchr (const void *block, int c, size_t size);
#include <estream.h>
/* Generally used types. */
@ -66,6 +70,13 @@ void *memrchr (const void *block, int c, size_t size);
typedef void *(*func_realloc_t) (void *mem, size_t size);
typedef void (*func_free_t) (void *mem);
#ifdef HAVE_FOPENCOOKIE
typedef ssize_t my_funopen_hook_ret_t;
#else
typedef int my_funopen_hook_ret_t;
#endif
/* Buffer management layer. */
@ -1651,7 +1662,7 @@ doreadline (estream_t ES__RESTRICT stream, size_t max_length,
/* Helper for esprint. */
#if defined(HAVE_FOPENCOOKIE) || defined(HAVE_FUNOPEN)
static int
static my_funopen_hook_ret_t
print_fun_writer (void *cookie_arg, const char *buffer, size_t size)
{
estream_t stream = cookie_arg;

View File

@ -271,7 +271,7 @@ fd_cache_close (const char *fname, FILEP_OR_FD fp)
close (fp);
#endif
if (DBG_IOBUF)
log_debug ("fd_cache_close (%p) real\n", (void *) fp);
log_debug ("fd_cache_close (%d) real\n", fp);
return;
}
/* try to reuse a slot */

View File

@ -1,3 +1,8 @@
2006-11-15 Werner Koch <wk@g10code.com>
* logging.c (my_funopen_hook_ret_t): New.
(fun_writer): Use it.
2006-10-19 Werner Koch <wk@g10code.com>
* stringhelp.c (memrchr) [!HAVE_MEMRCHR]: Provide a replacement.
@ -5,7 +10,7 @@
2006-09-27 Werner Koch <wk@g10code.com>
* mischelp.c: New.
(timegm): Copied from gnupg 1.4, Changed from LGPL to GPL. Fixed
(timegm): Copied from gnupg 1.4, changed from GPL to LGPL. Fixed
a memory leak.
* stringhelp.h (isascii): New.

View File

@ -48,6 +48,13 @@
#define USE_FUNWRITER 1
#endif
#ifdef HAVE_FOPENCOOKIE
typedef ssize_t my_funopen_hook_ret_t;
#else
typedef int my_funopen_hook_ret_t;
#endif
static FILE *logstream;
static int log_socket = -1;
static char prefix_buffer[80];
@ -111,7 +118,7 @@ writen (int fd, const void *buffer, size_t nbytes)
}
static int
static my_funopen_hook_ret_t
fun_writer (void *cookie_arg, const char *buffer, size_t size)
{
struct fun_cookie_s *cookie = cookie_arg;

View File

@ -1,3 +1,7 @@
2006-11-15 Werner Koch <wk@g10code.com>
* kbxutil.c (dump_openpgp_key): Cast printf argument.
2006-10-20 Werner Koch <wk@g10code.com>
* keybox-search.c (blob_x509_has_grip, has_keygrip): New.

View File

@ -367,7 +367,7 @@ dump_openpgp_key (keybox_openpgp_info_t info, const unsigned char *image)
u = &info->uids;
do
{
printf ("uid\t\t%.*s\n", u->len, image + u->off);
printf ("uid\t\t%.*s\n", (int)u->len, image + u->off);
u = u->next;
}
while (u);

View File

@ -1,3 +1,8 @@
2006-11-17 Werner Koch <wk@g10code.com>
* gnupg-pth.m4: Make sure that have_w32_system is set to no by
default.
2006-11-14 Werner Koch <wk@g10code.com>
* libassuan.m4: Updated from libassuan SVN.

View File

@ -91,6 +91,7 @@ AC_DEFUN([GNUPG_PATH_PTH],
fi
AC_PATH_PROG(PTH_CONFIG, pth-config, no)
tmp=ifelse([$1], ,1.3.7,$1)
test -z "$have_w32_system" && have_w32_system="no"
if test "$have_w32_system" = no; then
if test "$PTH_CONFIG" != "no"; then
GNUPG_PTH_VERSION_CHECK($tmp)

View File

@ -1,3 +1,37 @@
2006-11-20 Werner Koch <wk@g10code.com>
* app-openpgp.c (verify_chv2): Support for keypads (only CHV2).
* ccid-driver.c (ccid_transceive_secure): Made it work for Kaan
and SCM.
2006-11-17 Werner Koch <wk@g10code.com>
* ccid-driver.c (scan_or_find_devices): Use DEBUGOUT_2 instead of
log_debug. Removed few other log_debug.
* iso7816.c (iso7816_check_keypad): Allow for a SW of 0.
* command.c (pin_cb): New mode to prompt for a keypad entry.
* scdaemon.c (main) <gpgconf-list>: Add disable-keypad.
2006-11-15 Werner Koch <wk@g10code.com>
* app-p15.c (read_ef_odf): Cast one printf arg.
* scdaemon.h (struct server_control_s): Add field THREAD_STARTUP.
* command.c (scd_command_handler): Add new arg CTRL.
* scdaemon.c (scd_init_default_ctrl): Made static.
(scd_deinit_default_ctrl): New.
(start_connection_thread): Call init/deinit of ctrl.
(handle_connections): Allocate CTRL.
* apdu.c (PCSC_ERR_MASK): New.
(reset_pcsc_reader, pcsc_get_status, pcsc_send_apdu)
(close_pcsc_reader, open_pcsc_reader): Use it after shifting error
values. Reported by Henrik Nordstrom. Fixes bug #724.
2006-10-24 Werner Koch <wk@g10code.com>
* scdaemon.h (GCRY_MD_USER_TLS_MD5SHA1): New.

View File

@ -222,6 +222,11 @@ static char (* DLSTDCALL CT_close) (unsigned short ctn);
#define PCSC_E_READER_UNAVAILABLE 0x80100017
#define PCSC_W_REMOVED_CARD 0x80100069
/* The PC/SC error is defined as a long as per specs. Due to left
shifts bit 31 will get sign extended. We use this mask to fix
it. */
#define PCSC_ERR_MASK(a) ((a) & 0xffffffff)
struct pcsc_io_request_s
{
@ -739,7 +744,7 @@ pcsc_error_to_sw (long ec)
{
int rc;
switch (ec)
switch ( PCSC_ERR_MASK (ec) )
{
case 0: rc = 0; break;
@ -834,7 +839,8 @@ reset_pcsc_reader (int slot)
sw = SW_HOST_GENERAL_ERROR;
goto command_failed;
}
err = (msgbuf[5] << 24) | (msgbuf[6] << 16) | (msgbuf[7] << 8 ) | msgbuf[8];
err = PCSC_ERR_MASK ((msgbuf[5] << 24) | (msgbuf[6] << 16)
| (msgbuf[7] << 8 ) | msgbuf[8]);
if (err)
{
log_error ("PC/SC RESET failed: %s (0x%lx)\n",
@ -981,7 +987,8 @@ pcsc_get_status (int slot, unsigned int *status)
goto command_failed;
}
len -= 4; /* Already read the error code. */
err = (msgbuf[5] << 24) | (msgbuf[6] << 16) | (msgbuf[7] << 8 ) | msgbuf[8];
err = PCSC_ERR_MASK ((msgbuf[5] << 24) | (msgbuf[6] << 16)
| (msgbuf[7] << 8 ) | msgbuf[8]);
if (err)
{
log_error ("pcsc_status failed: %s (0x%lx)\n",
@ -1151,7 +1158,8 @@ pcsc_send_apdu (int slot, unsigned char *apdu, size_t apdulen,
goto command_failed;
}
len -= 4; /* Already read the error code. */
err = (msgbuf[5] << 24) | (msgbuf[6] << 16) | (msgbuf[7] << 8 ) | msgbuf[8];
err = PCSC_ERR_MASK ((msgbuf[5] << 24) | (msgbuf[6] << 16)
| (msgbuf[7] << 8 ) | msgbuf[8]);
if (err)
{
log_error ("pcsc_transmit failed: %s (0x%lx)\n",
@ -1283,7 +1291,8 @@ close_pcsc_reader (int slot)
goto command_failed;
}
len -= 4; /* Already read the error code. */
err = (msgbuf[5] << 24) | (msgbuf[6] << 16) | (msgbuf[7] << 8 ) | msgbuf[8];
err = PCSC_ERR_MASK ((msgbuf[5] << 24) | (msgbuf[6] << 16)
| (msgbuf[7] << 8 ) | msgbuf[8]);
if (err)
log_error ("pcsc_close failed: %s (0x%lx)\n",
pcsc_error_string (err), err);
@ -1470,7 +1479,8 @@ open_pcsc_reader (const char *portstr)
(unsigned long)len);
goto command_failed;
}
err = (msgbuf[5] << 24) | (msgbuf[6] << 16) | (msgbuf[7] << 8 ) | msgbuf[8];
err = PCSC_ERR_MASK ((msgbuf[5] << 24) | (msgbuf[6] << 16)
| (msgbuf[7] << 8 ) | msgbuf[8]);
if (err)
{
log_error ("PC/SC OPEN failed: %s\n", pcsc_error_string (err));

View File

@ -1291,27 +1291,52 @@ verify_chv2 (app_t app,
{
char *pinvalue;
iso7816_pininfo_t pininfo;
int did_keypad = 0;
memset (&pininfo, 0, sizeof pininfo);
pininfo.mode = 1;
pininfo.minlen = 6;
rc = pincb (pincb_arg, "PIN", &pinvalue);
if (rc)
if (!opt.disable_keypad
&& !iso7816_check_keypad (app->slot, ISO7816_VERIFY, &pininfo) )
{
log_info (_("PIN callback returned error: %s\n"), gpg_strerror (rc));
return rc;
/* The reader supports the verify command through the keypad. */
did_keypad = 1;
rc = pincb (pincb_arg,
_("||Please enter your PIN at the reader's keypad"),
NULL);
if (rc)
{
log_info (_("PIN callback returned error: %s\n"),
gpg_strerror (rc));
return rc;
}
rc = iso7816_verify_kp (app->slot, 0x82, "", 0, &pininfo);
/* Dismiss the prompt. */
pincb (pincb_arg, NULL, NULL);
}
else
{
/* The reader has no keypad or we don't want to use it. */
rc = pincb (pincb_arg, "PIN", &pinvalue);
if (rc)
{
log_info (_("PIN callback returned error: %s\n"),
gpg_strerror (rc));
return rc;
}
if (strlen (pinvalue) < 6)
{
log_error (_("PIN for CHV%d is too short;"
" minimum length is %d\n"), 2, 6);
xfree (pinvalue);
return gpg_error (GPG_ERR_BAD_PIN);
}
rc = iso7816_verify (app->slot, 0x82, pinvalue, strlen (pinvalue));
}
if (strlen (pinvalue) < 6)
{
log_error (_("PIN for CHV%d is too short;"
" minimum length is %d\n"), 2, 6);
xfree (pinvalue);
return gpg_error (GPG_ERR_BAD_PIN);
}
rc = iso7816_verify (app->slot, 0x82, pinvalue, strlen (pinvalue));
if (rc)
{
log_error (_("verify CHV%d failed: %s\n"), 2, gpg_strerror (rc));
@ -1321,7 +1346,7 @@ verify_chv2 (app_t app,
}
app->did_chv2 = 1;
if (!app->did_chv1 && !app->force_chv1)
if (!app->did_chv1 && !app->force_chv1 && !did_keypad)
{
rc = iso7816_verify (app->slot, 0x81, pinvalue, strlen (pinvalue));
if (gpg_err_code (rc) == GPG_ERR_BAD_PIN)

View File

@ -692,7 +692,8 @@ read_ef_odf (app_t app, unsigned short odf_fid)
}
if (buflen)
log_info ("warning: %u bytes of garbage detected at end of ODF\n", buflen);
log_info ("warning: %u bytes of garbage detected at end of ODF\n",
(unsigned int)buflen);
xfree (buffer);
return 0;

View File

@ -200,7 +200,8 @@ enum {
VENDOR_CHERRY = 0x046a,
VENDOR_SCM = 0x04e6,
VENDOR_OMNIKEY= 0x076b,
VENDOR_GEMPC = 0x08e6
VENDOR_GEMPC = 0x08e6,
VENDOR_KAAN = 0x0d46
};
/* A list and a table with special transport descriptions. */
@ -990,11 +991,10 @@ scan_or_find_devices (int readerno, const char *readerid,
fd = open (transports[i].name, O_RDWR);
if (fd == -1)
{
log_debug ("failed to open `%s': %s\n",
DEBUGOUT_2 ("failed to open `%s': %s\n",
transports[i].name, strerror (errno));
continue;
}
log_debug ("opened `%s': fd=%d\n", transports[i].name, fd);
rid = malloc (strlen (transports[i].name) + 30 + 10);
if (!rid)
@ -1047,7 +1047,6 @@ scan_or_find_devices (int readerno, const char *readerid,
}
free (rid);
close (fd);
log_debug ("closed fd %d\n", fd);
}
if (scan_mode)
@ -1208,10 +1207,7 @@ ccid_open_reader (ccid_driver_t *handle, const char *readerid)
if (idev)
usb_close (idev);
if (dev_fd != -1)
{
close (dev_fd);
log_debug ("closed fd %d\n", dev_fd);
}
close (dev_fd);
free (*handle);
*handle = NULL;
}
@ -1254,7 +1250,6 @@ do_close_reader (ccid_driver_t handle)
if (handle->dev_fd != -1)
{
close (handle->dev_fd);
log_debug ("closed fd %d\n", handle->dev_fd);
handle->dev_fd = -1;
}
}
@ -1324,10 +1319,7 @@ ccid_shutdown_reader (ccid_driver_t handle)
usb_close (handle->idev);
handle->idev = NULL;
if (handle->dev_fd != -1)
{
close (handle->dev_fd);
log_debug ("closed fd %d\n", handle->dev_fd);
}
close (handle->dev_fd);
handle->dev_fd = -1;
}
@ -2369,10 +2361,24 @@ ccid_transceive_secure (ccid_driver_t handle,
|| pinlen_min > pinlen_max)
return CCID_DRIVER_ERR_INV_VALUE;
/* We have only tested this with an SCM reader so better don't risk
anything and do not allow the use with other readers. */
if (handle->id_vendor != VENDOR_SCM)
return CCID_DRIVER_ERR_NOT_SUPPORTED;
/* We have only tested a few readers so better don't risk anything
and do not allow the use with other readers. */
switch (handle->id_vendor)
{
case VENDOR_SCM: /* Tested with SPR 532. */
case VENDOR_KAAN: /* Tested with KAAN Advanced (1.02). */
break;
/* The CHERRY XX44 does not yet work. I have not investigated it
closer because there is another problem: It echos a "*" for
each entered character and we somehow need to arrange that it
doesn't get to the tty at all. Given thate are running
without a control terminal there is not much we can do about.
A weird hack using pinentry comes in mind but I doubnt that
this is a clean solution. Need to contact Cherry.
*/
default:
return CCID_DRIVER_ERR_NOT_SUPPORTED;
}
if (testmode)
return 0; /* Success */
@ -2390,7 +2396,7 @@ ccid_transceive_secure (ccid_driver_t handle,
msg[0] = PC_to_RDR_Secure;
msg[5] = 0; /* slot */
msg[6] = seqno = handle->seqno++;
msg[7] = 4; /* bBWI */
msg[7] = 0; /* bBWI */
msg[8] = 0; /* RFU */
msg[9] = 0; /* RFU */
msg[10] = 0; /* Perform PIN verification. */
@ -2411,8 +2417,8 @@ ccid_transceive_secure (ccid_driver_t handle,
msg[14] = 0x00; /* bmPINLengthFormat:
Units are bytes, position is 0. */
}
msg[15] = pinlen_min; /* wPINMaxExtraDigit-Minimum. */
msg[16] = pinlen_max; /* wPINMaxExtraDigit-Maximum. */
msg[15] = pinlen_max; /* wPINMaxExtraDigit-Maximum. */
msg[16] = pinlen_min; /* wPINMaxExtraDigit-Minimum. */
msg[17] = 0x02; /* bEntryValidationCondition:
Validation key pressed */
if (pinlen_min && pinlen_max && pinlen_min == pinlen_max)
@ -2424,13 +2430,14 @@ ccid_transceive_secure (ccid_driver_t handle,
/* bTeoProlog follows: */
msg[22] = handle->nonnull_nad? ((1 << 4) | 0): 0;
msg[23] = ((handle->t1_ns & 1) << 6); /* I-block */
msg[24] = 4; /* apdulen. */
msg[24] = 0; /* The apdulen will be filled in by the reader. */
/* APDU follows: */
msg[25] = apdu_buf[0]; /* CLA */
msg[26] = apdu_buf[1]; /* INS */
msg[27] = apdu_buf[2]; /* P1 */
msg[28] = apdu_buf[3]; /* P2 */
msglen = 29;
/* An EDC is not required. */
set_msg_len (msg, msglen - 10);
DEBUGOUT ("sending");
@ -2444,12 +2451,30 @@ ccid_transceive_secure (ccid_driver_t handle,
msg = recv_buffer;
rc = bulk_in (handle, msg, sizeof recv_buffer, &msglen,
RDR_to_PC_DataBlock, seqno, 5000, 0);
RDR_to_PC_DataBlock, seqno, 30000, 0);
if (rc)
return rc;
tpdu = msg + 10;
tpdulen = msglen - 10;
if (handle->apdu_level)
{
if (resp)
{
if (tpdulen > maxresplen)
{
DEBUGOUT_2 ("provided buffer too short for received data "
"(%u/%u)\n",
(unsigned int)tpdulen, (unsigned int)maxresplen);
return CCID_DRIVER_ERR_INV_VALUE;
}
memcpy (resp, tpdu, tpdulen);
*nresp = tpdulen;
}
return 0;
}
if (tpdulen < 4)
{
@ -2595,7 +2620,7 @@ main (int argc, char **argv)
{
int rc;
ccid_driver_t ccid;
unsigned int slotstat;
int slotstat;
unsigned char result[512];
size_t resultlen;
int no_pinpad = 0;
@ -2623,7 +2648,7 @@ main (int argc, char **argv)
}
else if ( !strcmp (*argv, "--debug"))
{
ccid_set_debug_level (1);
ccid_set_debug_level (ccid_set_debug_level (-1)+1);
argc--; argv++;
}
else if ( !strcmp (*argv, "--no-poll"))

View File

@ -721,6 +721,31 @@ pin_cb (void *opaque, const char *info, char **retstr)
unsigned char *value;
size_t valuelen;
if (!retstr)
{
/* We prompt for keypad entry. To make sure that the popup has
been show we use an inquire and not just a status message.
We ignore any value returned. */
if (info)
{
log_debug ("prompting for keypad entry '%s'\n", info);
rc = asprintf (&command, "POPUPKEYPADPROMPT %s", info);
if (rc < 0)
return gpg_error (gpg_err_code_from_errno (errno));
rc = assuan_inquire (ctx, command, &value, &valuelen, MAXLEN_PIN);
free (command);
}
else
{
log_debug ("dismiss keypad entry prompt\n");
rc = assuan_inquire (ctx, "DISMISSKEYPADPROMPT",
&value, &valuelen, MAXLEN_PIN);
}
if (!rc)
xfree (value);
return rc;
}
*retstr = NULL;
log_debug ("asking for PIN '%s'\n", info);
@ -1584,14 +1609,10 @@ register_commands (assuan_context_t ctx)
/* Startup the server. If FD is given as -1 this is simple pipe
server, otherwise it is a regular server. */
void
scd_command_handler (int fd)
scd_command_handler (ctrl_t ctrl, int fd)
{
int rc;
assuan_context_t ctx;
struct server_control_s ctrl;
memset (&ctrl, 0, sizeof ctrl);
scd_init_default_ctrl (&ctrl);
if (fd == -1)
{
@ -1622,20 +1643,20 @@ scd_command_handler (int fd)
/* Allocate and initialize the server object. Put it into the list
of active sessions. */
ctrl.server_local = xcalloc (1, sizeof *ctrl.server_local);
ctrl.server_local->next_session = session_list;
session_list = ctrl.server_local;
ctrl.server_local->ctrl_backlink = &ctrl;
ctrl.server_local->assuan_ctx = ctx;
ctrl->server_local = xcalloc (1, sizeof *ctrl->server_local);
ctrl->server_local->next_session = session_list;
session_list = ctrl->server_local;
ctrl->server_local->ctrl_backlink = ctrl;
ctrl->server_local->assuan_ctx = ctx;
if (DBG_ASSUAN)
assuan_set_log_stream (ctx, log_get_stream ());
/* We open the reader right at startup so that the ticker is able to
update the status file. */
if (ctrl.reader_slot == -1)
if (ctrl->reader_slot == -1)
{
ctrl.reader_slot = get_reader_slot ();
ctrl->reader_slot = get_reader_slot ();
}
/* Command processing loop. */
@ -1661,23 +1682,24 @@ scd_command_handler (int fd)
}
/* Cleanup. */
do_reset (&ctrl, 0);
do_reset (ctrl, 0);
/* Release the server object. */
if (session_list == ctrl.server_local)
session_list = ctrl.server_local->next_session;
if (session_list == ctrl->server_local)
session_list = ctrl->server_local->next_session;
else
{
struct server_local_s *sl;
for (sl=session_list; sl->next_session; sl = sl->next_session)
if (sl->next_session == ctrl.server_local)
if (sl->next_session == ctrl->server_local)
break;
if (!sl->next_session)
BUG ();
sl->next_session = ctrl.server_local->next_session;
sl->next_session = ctrl->server_local->next_session;
}
xfree (ctrl.server_local);
xfree (ctrl->server_local);
ctrl->server_local = NULL;
/* Release the Assuan context. */
assuan_deinit_server (ctx);

View File

@ -235,7 +235,7 @@ iso7816_check_keypad (int slot, int command, iso7816_pininfo_t *pininfo)
sw = apdu_check_keypad (slot, command,
pininfo->mode, pininfo->minlen, pininfo->maxlen,
pininfo->padlen);
return map_sw (sw);
return iso7816_map_sw (sw);
}

View File

@ -600,7 +600,7 @@ main (int argc, char **argv )
printf ("disable-ccid:%lu:\n", GC_OPT_FLAG_NONE );
#endif
printf ("allow-admin:%lu:\n", GC_OPT_FLAG_NONE );
printf ("disable-keypad:%lu:\n", GC_OPT_FLAG_NONE );
scd_exit (0);
}
@ -615,6 +615,7 @@ main (int argc, char **argv )
if (pipe_server)
{
/* This is the simple pipe based server */
ctrl_t ctrl;
pth_attr_t tattr;
int fd = -1;
@ -656,10 +657,19 @@ main (int argc, char **argv )
pth_attr_set (tattr, PTH_ATTR_STACK_SIZE, 512*1024);
pth_attr_set (tattr, PTH_ATTR_NAME, "pipe-connection");
if (!pth_spawn (tattr, start_connection_thread, (void*)(-1)))
ctrl = xtrycalloc (1, sizeof *ctrl);
if ( !ctrl )
{
log_error ("error allocating connection control data: %s\n",
strerror (errno) );
scd_exit (2);
}
ctrl->thread_startup.fd = -1;
if ( !pth_spawn (tattr, start_connection_thread, ctrl) )
{
log_error ("error spawning pipe connection handler: %s\n",
strerror (errno) );
xfree (ctrl);
scd_exit (2);
}
@ -810,12 +820,18 @@ scd_exit (int rc)
}
void
static void
scd_init_default_ctrl (ctrl_t ctrl)
{
ctrl->reader_slot = -1;
}
static void
scd_deinit_default_ctrl (ctrl_t ctrl)
{
}
/* Return the name of the socket to be used to connect to this
process. If no socket is available, return NULL. */
@ -1007,23 +1023,26 @@ create_server_socket (int is_standard_name, const char *name)
static void *
start_connection_thread (void *arg)
{
int fd = (int)arg;
ctrl_t ctrl = arg;
scd_init_default_ctrl (ctrl);
if (opt.verbose)
log_info (_("handler for fd %d started\n"), ctrl->thread_startup.fd);
scd_command_handler (ctrl, ctrl->thread_startup.fd);
if (opt.verbose)
log_info (_("handler for fd %d started\n"), fd);
scd_command_handler (fd);
if (opt.verbose)
log_info (_("handler for fd %d terminated\n"), fd);
log_info (_("handler for fd %d terminated\n"), ctrl->thread_startup.fd);
/* If this thread is the pipe connection thread, flag that a
shutdown is required. With the next ticker event and given that
no other connections are running the shutdown will then
happen. */
if (fd == -1)
if (ctrl->thread_startup.fd == -1)
shutdown_pending = 1;
scd_deinit_default_ctrl (ctrl);
xfree (ctrl);
return NULL;
}
@ -1137,23 +1156,33 @@ handle_connections (int listen_fd)
if (listen_fd != -1 && FD_ISSET (listen_fd, &read_fdset))
{
ctrl_t ctrl;
plen = sizeof paddr;
fd = pth_accept (listen_fd, (struct sockaddr *)&paddr, &plen);
if (fd == -1)
{
log_error ("accept failed: %s\n", strerror (errno));
}
else if ( !(ctrl = xtrycalloc (1, sizeof *ctrl)) )
{
log_error ("error allocating connection control data: %s\n",
strerror (errno) );
close (fd);
}
else
{
char threadname[50];
snprintf (threadname, sizeof threadname-1, "conn fd=%d", fd);
threadname[sizeof threadname -1] = 0;
pth_attr_set (tattr, PTH_ATTR_NAME, threadname);
if (!pth_spawn (tattr, start_connection_thread, (void*)fd))
ctrl->thread_startup.fd = fd;
if (!pth_spawn (tattr, start_connection_thread, ctrl))
{
log_error ("error spawning connection handler: %s\n",
strerror (errno) );
xfree (ctrl);
close (fd);
}
}

View File

@ -90,6 +90,12 @@ struct app_ctx_s;
struct server_control_s
{
/* Private data used to fire up the connection thread. We use this
structure do avoid an extra allocation for just a few bytes. */
struct {
int fd;
} thread_startup;
/* Local data of the server; used only in command.c. */
struct server_local_s *server_local;
@ -115,11 +121,10 @@ typedef struct app_ctx_s *app_t;
/*-- scdaemon.c --*/
void scd_exit (int rc);
void scd_init_default_ctrl (ctrl_t ctrl);
const char *scd_get_socket_name (void);
/*-- command.c --*/
void scd_command_handler (int);
void scd_command_handler (ctrl_t, int);
void send_status_info (ctrl_t ctrl, const char *keyword, ...)
GNUPG_GCC_A_SENTINEL(1);
void scd_update_reader_status_file (void);

View File

@ -1,3 +1,8 @@
2006-11-16 Werner Koch <wk@g10code.com>
* Makefile.am (plain-large): Use gpg.texi instead of FAQ which
won't be found as it is not a source file. Pointed out by Moritz.
2006-10-04 Werner Koch <wk@g10code.com>
* signencrypt.test: Need to prepend srcdir to the file name

View File

@ -100,7 +100,7 @@ data-80000:
plain-large:
cat $(srcdir)/../../doc/HACKING \
$(srcdir)/../../doc/DETAILS \
$(srcdir)/../../doc/FAQ >plain-large
$(srcdir)/../../doc/gpg.texi >plain-large
# To speed up key generation we create a dummy random seed file
random_seed:

View File

@ -1,3 +1,7 @@
2006-11-17 Werner Koch <wk@g10code.com>
* gpgconf-comp.c: Made disable-keypad a basic option.
2006-11-03 Werner Koch <wk@g10code.com>
* symcryptrun.c: Include signal.h and include pth.h only if test

View File

@ -530,7 +530,7 @@ static gc_option_t gc_options_scdaemon[] =
{ "disable-ccid", GC_OPT_FLAG_NONE, GC_LEVEL_EXPERT,
"gnupg", "do not use the internal CCID driver",
GC_ARG_TYPE_NONE, GC_BACKEND_SCDAEMON },
{ "disable-keypad", GC_OPT_FLAG_NONE, GC_LEVEL_ADVANCED,
{ "disable-keypad", GC_OPT_FLAG_NONE, GC_LEVEL_BASIC,
"gnupg", "do not use a reader's keypad",
GC_ARG_TYPE_NONE, GC_BACKEND_SCDAEMON },