* gpg-agent.c (handle_connections): Need to check for events if

select returns with -1.

* tools.texi (gpg-connect-agent): New.

* app-openpgp.c (get_one_do): Never try to get a non cacheable
object from the cache.
(get_one_do): Add new arg to return an error code.  Changed all
callers.
(do_getattr): Let it return a proper error code.

* app.c (select_application): Return an error code and the
application context in an new arg.
* command.c (open_card): Adjusted for that.  Don't use the
fallback if no card is present.  Return an error if the card has
been removed without a reset.
(do_reset, cmd_serialno): Clear that error flag.
(TEST_CARD_REMOVAL): New. Use it with all command handlers.

* scdaemon.c (ticker_thread): Termintate if a shutdown is pending.

* apdu.c: Added some PCSC error codes.
(pcsc_error_to_sw): New.
(reset_pcsc_reader, pcsc_get_status, pcsc_send_apdu)
(open_pcsc_reader): Do proper error code mapping.

* gpg-connect-agent.c: New.
* Makefile.am: Add it.
This commit is contained in:
Werner Koch 2005-02-24 17:36:11 +00:00
parent 4e5bf2fd93
commit 3af261572b
18 changed files with 839 additions and 239 deletions

View File

@ -1,3 +1,8 @@
2005-02-24 Werner Koch <wk@g10code.com>
* gpg-agent.c (handle_connections): Need to check for events if
select returns with -1.
2005-02-23 Werner Koch <wk@g10code.com> 2005-02-23 Werner Koch <wk@g10code.com>
* command-ssh.c (get_passphrase): Removed. * command-ssh.c (get_passphrase): Removed.

View File

@ -1463,6 +1463,11 @@ handle_connections (int listen_fd, int listen_fd_ssh)
ret = pth_select_ev (FD_SETSIZE, &read_fdset, NULL, NULL, NULL, ev); ret = pth_select_ev (FD_SETSIZE, &read_fdset, NULL, NULL, NULL, ev);
if (ret == -1) if (ret == -1)
{ {
if (pth_event_occurred (ev))
{
handle_signal (signo);
continue;
}
log_error (_("pth_select failed: %s - waiting 1s\n"), log_error (_("pth_select failed: %s - waiting 1s\n"),
strerror (errno)); strerror (errno));
pth_sleep (1); pth_sleep (1);

View File

@ -1,4 +1,4 @@
/* simple-pwquery.c - A simple password query cleint for gpg-agent /* simple-pwquery.c - A simple password query client for gpg-agent
* Copyright (C) 2002, 2004 Free Software Foundation, Inc. * Copyright (C) 2002, 2004 Free Software Foundation, Inc.
* *
* This file is part of GnuPG. * This file is part of GnuPG.

View File

@ -39,7 +39,7 @@
memory was enable and ERRNO is set accordingly. memory was enable and ERRNO is set accordingly.
If a line has been truncated, the file pointer is moved forward to If a line has been truncated, the file pointer is moved forward to
the end of the line so that the next read start with tghe next the end of the line so that the next read start with the next
line. Note that MAX_LENGTH must be re-initialzied in this case.. line. Note that MAX_LENGTH must be re-initialzied in this case..
Note: The returned buffer is allocated with enough extra space to Note: The returned buffer is allocated with enough extra space to

View File

@ -1,3 +1,7 @@
2005-02-24 Werner Koch <wk@g10code.com>
* tools.texi (gpg-connect-agent): New.
2005-02-14 Werner Koch <wk@g10code.com> 2005-02-14 Werner Koch <wk@g10code.com>
* gpgsm.texi (Certificate Management): Document --import. * gpgsm.texi (Certificate Management): Document --import.

View File

@ -326,10 +326,8 @@ Ignore requests to change change the current @sc{tty} respective the X
window system's @code{DISPLAY} variable. This is useful to lock the window system's @code{DISPLAY} variable. This is useful to lock the
pinentry to pop up at the @sc{tty} or display you started the agent. pinentry to pop up at the @sc{tty} or display you started the agent.
@item --ssh-support @item --enable-ssh-support
@itemx --ssh-support @opindex enable-ssh-support
@opindex ssh-support
@opindex ssh
Enable emulation of the OpenSSH Agent protocol. Enable emulation of the OpenSSH Agent protocol.
@ -350,13 +348,11 @@ Once, a key has been added to the gpg-agent this way, the gpg-agent
will be ready to use the key. will be ready to use the key.
Note: in case the gpg-agent receives a signature request, the user Note: in case the gpg-agent receives a signature request, the user
might need to be prompted for a passphrased, which is necessary for might need to be prompted for a passphrase, which is necessary for
decrypting the stored key. Since the ssh-agent protocol does not decrypting the stored key. Since the ssh-agent protocol does not
contain a mechanism for telling the agent on which display/terminal it contain a mechanism for telling the agent on which display/terminal it
is running, gpg-agent's --ssh-support switch implies --keep-display is running, gpg-agent's ssh-support will use the TTY or X display where
and --keep-tty. This strategy causes the gpg-agent to open a pinentry gpg-agent has been started.
on the display or on the terminal, on which it (the gpg-agent) was
started.
@end table @end table

View File

@ -13,6 +13,7 @@ GnuPG comes with a couple of smaller tools:
* gpgconf:: Modify .gnupg home directories. * gpgconf:: Modify .gnupg home directories.
* gpgsm-gencert.sh:: Generate an X.509 certificate request. * gpgsm-gencert.sh:: Generate an X.509 certificate request.
* gpg-preset-passphrase:: Put a passphrase into the cache. * gpg-preset-passphrase:: Put a passphrase into the cache.
* gpg-connect-agent:: Communicate with a running agent.
@end menu @end menu
@c @c
@ -665,3 +666,64 @@ for other users.
@c
@c GPG-CONNECT-AGENT
@c
@node gpg-connect-agent
@section Communicate with a runnig agent.
The @command{gpg-connect-agent} is a utility to communicate with a
running @command{gpg-agent}. It is useful to check out the commands
gpg-agent provides using the Assuan interface. It might also be useful
for scripting simple applications. Inputis expected at stdin and out
put gets printed to stdout.
It is very similar to running @command{gpg-agent} in server mode; but
here we connect to a running instance.
@menu
* Invoking gpg-connect-agent:: List of all commands and options.
@end menu
@node Invoking gpg-connect-agent
@subsection List of all commands and options.
@noindent
@command{gpg-connect-agent} is invoked this way:
@example
gpg-connect-agent [options]
@end example
@noindent
The following options may be used:
@table @gnupgtabopt
@item -v
@itemx --verbose
@opindex verbose
Output additional information while running.
@item -q
@item --quiet
@opindex q
@opindex quiet
Try to be as quiet as possible.
@item --homedir @var{dir}
@opindex homedir
Set the name of the home directory to @var{dir}. If his option is not
used, the home directory defaults to @file{~/.gnupg}. It is only
recognized when given on the command line. It also overrides any home
directory stated through the environment variable @env{GNUPGHOME} or
(on W32 systems) by means on the Registry entry
@var{HKCU\Software\GNU\GnuPG:HomeDir}.
@end table

View File

@ -1,3 +1,26 @@
2005-02-24 Werner Koch <wk@g10code.com>
* app-openpgp.c (get_one_do): Never try to get a non cacheable
object from the cache.
(get_one_do): Add new arg to return an error code. Changed all
callers.
(do_getattr): Let it return a proper error code.
* app.c (select_application): Return an error code and the
application context in an new arg.
* command.c (open_card): Adjusted for that. Don't use the
fallback if no card is present. Return an error if the card has
been removed without a reset.
(do_reset, cmd_serialno): Clear that error flag.
(TEST_CARD_REMOVAL): New. Use it with all command handlers.
* scdaemon.c (ticker_thread): Termintate if a shutdown is pending.
* apdu.c: Added some PCSC error codes.
(pcsc_error_to_sw): New.
(reset_pcsc_reader, pcsc_get_status, pcsc_send_apdu)
(open_pcsc_reader): Do proper error code mapping.
2005-02-22 Werner Koch <wk@g10code.com> 2005-02-22 Werner Koch <wk@g10code.com>
* app-openpgp.c (app_local_s): New field PK. * app-openpgp.c (app_local_s): New field PK.

File diff suppressed because it is too large Load Diff

View File

@ -104,7 +104,8 @@ size_t app_help_read_length_of_cert (int slot, int fid, size_t *r_certoff);
/*-- app.c --*/ /*-- app.c --*/
app_t select_application (ctrl_t ctrl, int slot, const char *name); gpg_error_t select_application (ctrl_t ctrl, int slot, const char *name,
app_t *r_app);
void release_application (app_t app); void release_application (app_t app);
int app_munge_serialno (app_t app); int app_munge_serialno (app_t app);
int app_get_serial_and_stamp (app_t app, char **serial, time_t *stamp); int app_get_serial_and_stamp (app_t app, char **serial, time_t *stamp);

View File

@ -306,23 +306,33 @@ flush_cache (app_t app)
NULL if not found or a pointer which must be used to release the NULL if not found or a pointer which must be used to release the
buffer holding value. */ buffer holding value. */
static void * static void *
get_one_do (app_t app, int tag, unsigned char **result, size_t *nbytes) get_one_do (app_t app, int tag, unsigned char **result, size_t *nbytes,
int *r_rc)
{ {
int rc, i; int rc, i;
unsigned char *buffer; unsigned char *buffer;
size_t buflen; size_t buflen;
unsigned char *value; unsigned char *value;
size_t valuelen; size_t valuelen;
int dummyrc;
if (!r_rc)
r_rc = &dummyrc;
*result = NULL; *result = NULL;
*nbytes = 0; *nbytes = 0;
*r_rc = 0;
for (i=0; data_objects[i].tag && data_objects[i].tag != tag; i++) for (i=0; data_objects[i].tag && data_objects[i].tag != tag; i++)
; ;
if (app->card_version > 0x0100 && data_objects[i].get_immediate_in_v11) if (app->card_version > 0x0100 && data_objects[i].get_immediate_in_v11)
{ {
if( iso7816_get_data (app->slot, tag, &buffer, &buflen)) rc = iso7816_get_data (app->slot, tag, &buffer, &buflen);
return NULL; if (rc)
{
*r_rc = rc;
return NULL;
}
*result = buffer; *result = buffer;
*nbytes = buflen; *nbytes = buflen;
return buffer; return buffer;
@ -334,7 +344,8 @@ get_one_do (app_t app, int tag, unsigned char **result, size_t *nbytes)
{ {
rc = get_cached_data (app, data_objects[i].get_from, rc = get_cached_data (app, data_objects[i].get_from,
&buffer, &buflen, &buffer, &buflen,
data_objects[i].get_immediate_in_v11); (data_objects[i].dont_cache
|| data_objects[i].get_immediate_in_v11));
if (!rc) if (!rc)
{ {
const unsigned char *s; const unsigned char *s;
@ -356,7 +367,8 @@ get_one_do (app_t app, int tag, unsigned char **result, size_t *nbytes)
if (!value) /* Not in a constructed DO, try simple. */ if (!value) /* Not in a constructed DO, try simple. */
{ {
rc = get_cached_data (app, tag, &buffer, &buflen, rc = get_cached_data (app, tag, &buffer, &buflen,
data_objects[i].get_immediate_in_v11); (data_objects[i].dont_cache
|| data_objects[i].get_immediate_in_v11));
if (!rc) if (!rc)
{ {
value = buffer; value = buffer;
@ -370,6 +382,7 @@ get_one_do (app_t app, int tag, unsigned char **result, size_t *nbytes)
*result = value; *result = value;
return buffer; return buffer;
} }
*r_rc = rc;
return NULL; return NULL;
} }
@ -488,7 +501,7 @@ parse_login_data (app_t app)
app->app_local->flags.def_chv2 = 0; app->app_local->flags.def_chv2 = 0;
/* Read the DO. */ /* Read the DO. */
relptr = get_one_do (app, 0x005E, &buffer, &buflen); relptr = get_one_do (app, 0x005E, &buffer, &buflen, NULL);
if (!relptr) if (!relptr)
return; /* Ooops. */ return; /* Ooops. */
for (; buflen; buflen--, buffer++) for (; buflen; buflen--, buffer++)
@ -678,7 +691,7 @@ do_getattr (app_t app, ctrl_t ctrl, const char *name)
{ "PRIVATE-DO-4", 0x0104 }, { "PRIVATE-DO-4", 0x0104 },
{ NULL, 0 } { NULL, 0 }
}; };
int idx, i; int idx, i, rc;
void *relptr; void *relptr;
unsigned char *value; unsigned char *value;
size_t valuelen; size_t valuelen;
@ -723,7 +736,7 @@ do_getattr (app_t app, ctrl_t ctrl, const char *name)
return 0; return 0;
} }
relptr = get_one_do (app, table[idx].tag, &value, &valuelen); relptr = get_one_do (app, table[idx].tag, &value, &valuelen, &rc);
if (relptr) if (relptr)
{ {
if (table[idx].special == 1) if (table[idx].special == 1)
@ -760,7 +773,7 @@ do_getattr (app_t app, ctrl_t ctrl, const char *name)
xfree (relptr); xfree (relptr);
} }
return 0; return rc;
} }
@ -1075,7 +1088,7 @@ verify_chv3 (app_t app,
unsigned char *value; unsigned char *value;
size_t valuelen; size_t valuelen;
relptr = get_one_do (app, 0x00C4, &value, &valuelen); relptr = get_one_do (app, 0x00C4, &value, &valuelen, NULL);
if (!relptr || valuelen < 7) if (!relptr || valuelen < 7)
{ {
log_error (_("error retrieving CHV status from card\n")); log_error (_("error retrieving CHV status from card\n"));
@ -1442,7 +1455,7 @@ get_sig_counter (app_t app)
size_t valuelen; size_t valuelen;
unsigned long ul; unsigned long ul;
relptr = get_one_do (app, 0x0093, &value, &valuelen); relptr = get_one_do (app, 0x0093, &value, &valuelen, NULL);
if (!relptr) if (!relptr)
return 0; return 0;
ul = convert_sig_counter_value (value, valuelen); ul = convert_sig_counter_value (value, valuelen);
@ -1880,7 +1893,7 @@ app_select_openpgp (app_t app)
goto leave; goto leave;
} }
relptr = get_one_do (app, 0x00C4, &buffer, &buflen); relptr = get_one_do (app, 0x00C4, &buffer, &buflen, NULL);
if (!relptr) if (!relptr)
{ {
log_error (_("can't access %s - invalid OpenPGP card?\n"), log_error (_("can't access %s - invalid OpenPGP card?\n"),
@ -1890,7 +1903,7 @@ app_select_openpgp (app_t app)
app->force_chv1 = (buflen && *buffer == 0); app->force_chv1 = (buflen && *buffer == 0);
xfree (relptr); xfree (relptr);
relptr = get_one_do (app, 0x00C0, &buffer, &buflen); relptr = get_one_do (app, 0x00C0, &buffer, &buflen, NULL);
if (!relptr) if (!relptr)
{ {
log_error (_("can't access %s - invalid OpenPGP card?\n"), log_error (_("can't access %s - invalid OpenPGP card?\n"),
@ -1973,7 +1986,7 @@ app_openpgp_cardinfo (app_t app,
if (disp_name) if (disp_name)
{ {
*disp_name = NULL; *disp_name = NULL;
relptr = get_one_do (app, 0x005B, &value, &valuelen); relptr = get_one_do (app, 0x005B, &value, &valuelen, NULL);
if (relptr) if (relptr)
{ {
*disp_name = make_printable_string (value, valuelen, 0); *disp_name = make_printable_string (value, valuelen, 0);
@ -1984,7 +1997,7 @@ app_openpgp_cardinfo (app_t app,
if (pubkey_url) if (pubkey_url)
{ {
*pubkey_url = NULL; *pubkey_url = NULL;
relptr = get_one_do (app, 0x5F50, &value, &valuelen); relptr = get_one_do (app, 0x5F50, &value, &valuelen, NULL);
if (relptr) if (relptr)
{ {
*pubkey_url = make_printable_string (value, valuelen, 0); *pubkey_url = make_printable_string (value, valuelen, 0);
@ -1998,7 +2011,7 @@ app_openpgp_cardinfo (app_t app,
*fpr2 = NULL; *fpr2 = NULL;
if (fpr3) if (fpr3)
*fpr3 = NULL; *fpr3 = NULL;
relptr = get_one_do (app, 0x00C5, &value, &valuelen); relptr = get_one_do (app, 0x00C5, &value, &valuelen, NULL);
if (relptr && valuelen >= 60) if (relptr && valuelen >= 60)
{ {
if (fpr1) if (fpr1)

View File

@ -1,5 +1,5 @@
/* app.c - Application selection. /* app.c - Application selection.
* Copyright (C) 2003, 2004 Free Software Foundation, Inc. * Copyright (C) 2003, 2004, 2005 Free Software Foundation, Inc.
* *
* This file is part of GnuPG. * This file is part of GnuPG.
* *
@ -49,21 +49,23 @@ is_app_allowed (const char *name)
/* If called with NAME as NULL, select the best fitting application /* If called with NAME as NULL, select the best fitting application
and return a context; otherwise select the application with NAME and return a context; otherwise select the application with NAME
and return a context. SLOT identifies the reader device. Returns and return a context. SLOT identifies the reader device. Returns
NULL if no application was found or no card is present. */ an error code and stores NULL at R_APP if no application was found
APP or no card is present. */
select_application (ctrl_t ctrl, int slot, const char *name) gpg_error_t
select_application (ctrl_t ctrl, int slot, const char *name, app_t *r_app)
{ {
int rc; int rc;
APP app; app_t app;
unsigned char *result = NULL; unsigned char *result = NULL;
size_t resultlen; size_t resultlen;
*r_app = NULL;
app = xtrycalloc (1, sizeof *app); app = xtrycalloc (1, sizeof *app);
if (!app) if (!app)
{ {
rc = gpg_error (gpg_err_code_from_errno (errno)); rc = gpg_error_from_errno (errno);
log_info ("error allocating context: %s\n", gpg_strerror (rc)); log_info ("error allocating context: %s\n", gpg_strerror (rc));
return NULL; return rc;
} }
app->slot = slot; app->slot = slot;
@ -75,7 +77,7 @@ select_application (ctrl_t ctrl, int slot, const char *name)
if (!rc) if (!rc)
rc = iso7816_select_file (slot, 0x2F02, 0, NULL, NULL); rc = iso7816_select_file (slot, 0x2F02, 0, NULL, NULL);
if (!rc) if (!rc)
rc = iso7816_read_binary (slot, 0, 0, &result, &resultlen); rc = iso7816_read_binary (slot, 0, 0, &result, &resultlen);
if (!rc) if (!rc)
{ {
size_t n; size_t n;
@ -111,7 +113,12 @@ select_application (ctrl_t ctrl, int slot, const char *name)
result = NULL; result = NULL;
} }
/* For certain error codes, there is no need to try more. */
if (gpg_err_code (rc) == GPG_ERR_CARD_NOT_PRESENT)
goto leave;
/* Figure out the application to use. */
rc = gpg_error (GPG_ERR_NOT_FOUND); rc = gpg_error (GPG_ERR_NOT_FOUND);
if (rc && is_app_allowed ("openpgp") && (!name || !strcmp (name, "openpgp"))) if (rc && is_app_allowed ("openpgp") && (!name || !strcmp (name, "openpgp")))
@ -135,11 +142,12 @@ select_application (ctrl_t ctrl, int slot, const char *name)
log_info ("no supported card application found: %s\n", log_info ("no supported card application found: %s\n",
gpg_strerror (rc)); gpg_strerror (rc));
xfree (app); xfree (app);
return NULL; return rc;
} }
app->initialized = 1; app->initialized = 1;
return app; *r_app = app;
return 0;
} }

View File

@ -45,10 +45,24 @@ static ctrl_t primary_connection;
#define set_error(e,t) assuan_set_error (ctx, ASSUAN_ ## e, (t)) #define set_error(e,t) assuan_set_error (ctx, ASSUAN_ ## e, (t))
/* Macro to flag a a removed card. */
#define TEST_CARD_REMOVAL(c,r) \
do { \
int _r = (r); \
if (gpg_err_code (_r) == GPG_ERR_CARD_NOT_PRESENT \
|| gpg_err_code (_r) == GPG_ERR_CARD_REMOVED) \
(c)->server_local->card_removed = 1; \
} while (0)
/* Data used to associate an Assuan context with local server data */ /* Data used to associate an Assuan context with local server data */
struct server_local_s { struct server_local_s {
ASSUAN_CONTEXT assuan_ctx; assuan_context_t assuan_ctx;
int event_signal; /* Or 0 if not used. */ int event_signal; /* Or 0 if not used. */
int card_removed; /* True if the card has been removed and a
reset is required to continue
operation. */
}; };
@ -89,6 +103,7 @@ do_reset (ctrl_t ctrl, int do_close)
ctrl->reader_slot = -1; ctrl->reader_slot = -1;
} }
} }
ctrl->server_local->card_removed = 0;
} }
@ -122,11 +137,18 @@ option_handler (ASSUAN_CONTEXT ctx, const char *key, const char *value)
/* If the card has not yet been opened, do it. Note that this /* If the card has not yet been opened, do it. Note that this
function returns an Assuan error, so don't map the error a second function returns an Assuan error, so don't map the error a second
time */ time */
static AssuanError static assuan_error_t
open_card (ctrl_t ctrl, const char *apptype) open_card (ctrl_t ctrl, const char *apptype)
{ {
gpg_error_t err;
int slot; int slot;
/* If we ever got a card not present error code, return that. Only
the SERIALNO command and a reset are able to clear from that
state. */
if (ctrl->server_local->card_removed)
return map_to_assuan_status (gpg_error (GPG_ERR_CARD_REMOVED));
if (ctrl->app_ctx) if (ctrl->app_ctx)
return 0; /* Already initialized for one specific application. */ return 0; /* Already initialized for one specific application. */
if (ctrl->card_ctx) if (ctrl->card_ctx)
@ -137,24 +159,28 @@ open_card (ctrl_t ctrl, const char *apptype)
else else
slot = apdu_open_reader (opt.reader_port); slot = apdu_open_reader (opt.reader_port);
ctrl->reader_slot = slot; ctrl->reader_slot = slot;
if (slot != -1) if (slot == -1)
ctrl->app_ctx = select_application (ctrl, slot, apptype); err = gpg_error (GPG_ERR_CARD);
if (!ctrl->app_ctx) else
{ /* No application found - fall back to old mode. */ err = select_application (ctrl, slot, apptype, &ctrl->app_ctx);
if (!ctrl->app_ctx
&& gpg_err_code (err) != GPG_ERR_CARD_NOT_PRESENT)
{
/* No application found - fall back to old mode. */
/* Note that we should rework the old code to use the /* Note that we should rework the old code to use the
application paradigma too. */ application paradigma too. */
int rc;
/* If an APPTYPE was requested and it is not pkcs#15, we return /* If an APPTYPE was requested and it is not pkcs#15, we return
an error here. */ an error here. */
if (apptype && !(!strcmp (apptype, "P15") || !strcmp (apptype, "p15"))) if (apptype && !(!strcmp (apptype, "P15") || !strcmp (apptype, "p15")))
rc = gpg_error (GPG_ERR_NOT_SUPPORTED); err = gpg_error (GPG_ERR_NOT_SUPPORTED);
else else
rc = card_open (&ctrl->card_ctx); err = card_open (&ctrl->card_ctx);
if (rc)
return map_to_assuan_status (rc);
} }
return 0;
if (gpg_err_code (err) == GPG_ERR_CARD_NOT_PRESENT)
ctrl->server_local->card_removed = 1;
return map_to_assuan_status (err);
} }
@ -215,12 +241,15 @@ percent_plus_unescape (unsigned char *string)
static int static int
cmd_serialno (ASSUAN_CONTEXT ctx, char *line) cmd_serialno (ASSUAN_CONTEXT ctx, char *line)
{ {
CTRL ctrl = assuan_get_pointer (ctx); ctrl_t ctrl = assuan_get_pointer (ctx);
int rc = 0; int rc = 0;
char *serial_and_stamp; char *serial_and_stamp;
char *serial; char *serial;
time_t stamp; time_t stamp;
/* Clear the remove flag so that the open_card is able to reread it. */
ctrl->server_local->card_removed = 0;
if ((rc = open_card (ctrl, *line? line:NULL))) if ((rc = open_card (ctrl, *line? line:NULL)))
return rc; return rc;
@ -443,6 +472,7 @@ cmd_learn (ASSUAN_CONTEXT ctx, char *line)
if (rc == -1) if (rc == -1)
rc = 0; rc = 0;
TEST_CARD_REMOVAL (ctrl, rc);
return map_to_assuan_status (rc); return map_to_assuan_status (rc);
} }
@ -485,6 +515,7 @@ cmd_readcert (ASSUAN_CONTEXT ctx, char *line)
return rc; return rc;
} }
TEST_CARD_REMOVAL (ctrl, rc);
return map_to_assuan_status (rc); return map_to_assuan_status (rc);
} }
@ -575,6 +606,7 @@ cmd_readkey (assuan_context_t ctx, char *line)
leave: leave:
ksba_cert_release (kc); ksba_cert_release (kc);
xfree (cert); xfree (cert);
TEST_CARD_REMOVAL (ctrl, rc);
return map_to_assuan_status (rc); return map_to_assuan_status (rc);
} }
@ -697,6 +729,7 @@ cmd_pksign (ASSUAN_CONTEXT ctx, char *line)
return rc; /* that is already an assuan error code */ return rc; /* that is already an assuan error code */
} }
TEST_CARD_REMOVAL (ctrl, rc);
return map_to_assuan_status (rc); return map_to_assuan_status (rc);
} }
@ -743,6 +776,7 @@ cmd_pkauth (ASSUAN_CONTEXT ctx, char *line)
return rc; /* that is already an assuan error code */ return rc; /* that is already an assuan error code */
} }
TEST_CARD_REMOVAL (ctrl, rc);
return map_to_assuan_status (rc); return map_to_assuan_status (rc);
} }
@ -789,6 +823,7 @@ cmd_pkdecrypt (ASSUAN_CONTEXT ctx, char *line)
return rc; /* that is already an assuan error code */ return rc; /* that is already an assuan error code */
} }
TEST_CARD_REMOVAL (ctrl, rc);
return map_to_assuan_status (rc); return map_to_assuan_status (rc);
} }
@ -824,6 +859,7 @@ cmd_getattr (ASSUAN_CONTEXT ctx, char *line)
rc = app_getattr (ctrl->app_ctx, ctrl, keyword); rc = app_getattr (ctrl->app_ctx, ctrl, keyword);
TEST_CARD_REMOVAL (ctrl, rc);
return map_to_assuan_status (rc); return map_to_assuan_status (rc);
} }
@ -871,6 +907,7 @@ cmd_setattr (ASSUAN_CONTEXT ctx, char *orig_line)
rc = app_setattr (ctrl->app_ctx, keyword, pin_cb, ctx, line, nbytes); rc = app_setattr (ctrl->app_ctx, keyword, pin_cb, ctx, line, nbytes);
xfree (linebuf); xfree (linebuf);
TEST_CARD_REMOVAL (ctrl, rc);
return map_to_assuan_status (rc); return map_to_assuan_status (rc);
} }
@ -927,6 +964,8 @@ cmd_genkey (ASSUAN_CONTEXT ctx, char *line)
return ASSUAN_Out_Of_Core; return ASSUAN_Out_Of_Core;
rc = app_genkey (ctrl->app_ctx, ctrl, keyno, force? 1:0, pin_cb, ctx); rc = app_genkey (ctrl->app_ctx, ctrl, keyno, force? 1:0, pin_cb, ctx);
xfree (keyno); xfree (keyno);
TEST_CARD_REMOVAL (ctrl, rc);
return map_to_assuan_status (rc); return map_to_assuan_status (rc);
} }
@ -966,6 +1005,7 @@ cmd_random (ASSUAN_CONTEXT ctx, char *line)
} }
xfree (buffer); xfree (buffer);
TEST_CARD_REMOVAL (ctrl, rc);
return map_to_assuan_status (rc); return map_to_assuan_status (rc);
} }
@ -1010,6 +1050,8 @@ cmd_passwd (ASSUAN_CONTEXT ctx, char *line)
if (rc) if (rc)
log_error ("command passwd failed: %s\n", gpg_strerror (rc)); log_error ("command passwd failed: %s\n", gpg_strerror (rc));
xfree (chvnostr); xfree (chvnostr);
TEST_CARD_REMOVAL (ctrl, rc);
return map_to_assuan_status (rc); return map_to_assuan_status (rc);
} }
@ -1044,6 +1086,7 @@ cmd_checkpin (ASSUAN_CONTEXT ctx, char *line)
if (rc) if (rc)
log_error ("app_check_pin failed: %s\n", gpg_strerror (rc)); log_error ("app_check_pin failed: %s\n", gpg_strerror (rc));
TEST_CARD_REMOVAL (ctrl, rc);
return map_to_assuan_status (rc); return map_to_assuan_status (rc);
} }
@ -1226,7 +1269,9 @@ send_status_info (CTRL ctrl, const char *keyword, ...)
} }
/* This fucntion is called by the ticker thread to check for changes
of the reader stati. It updates the reader status files and if
requested by the caller also send a signal to the caller. */
void void
scd_update_reader_status_file (void) scd_update_reader_status_file (void)
{ {
@ -1239,10 +1284,10 @@ scd_update_reader_status_file (void)
int used; int used;
unsigned int status, changed; unsigned int status, changed;
/* Note, that we only try to get the status, becuase it does not /* Note, that we only try to get the status, because it does not
make sense to wait here for a operation to complete. If we are make sense to wait here for a operation to complete. If we are
so busy working with the card, delays in the status file updated busy working with a card, delays in the status file update should
are should be acceptable. */ be acceptable. */
for (slot=0; (slot < DIM(last) for (slot=0; (slot < DIM(last)
&&!apdu_enum_reader (slot, &used)); slot++) &&!apdu_enum_reader (slot, &used)); slot++)
if (used && !apdu_get_status (slot, 0, &status, &changed)) if (used && !apdu_get_status (slot, 0, &status, &changed))

View File

@ -936,7 +936,7 @@ ticker_thread (void *dummy_arg)
sigs_ev = NULL; sigs_ev = NULL;
#endif #endif
for (;;) while (!shutdown_pending)
{ {
if (!time_ev) if (!time_ev)
{ {
@ -968,6 +968,7 @@ ticker_thread (void *dummy_arg)
} }
pth_event_free (sigs_ev, PTH_FREE_ALL); pth_event_free (sigs_ev, PTH_FREE_ALL);
return NULL;
} }
#endif /*USE_GNU_PTH*/ #endif /*USE_GNU_PTH*/
#endif /*!HAVE_OPENSC*/ #endif /*!HAVE_OPENSC*/

View File

@ -59,10 +59,10 @@
Print VALUE. Print VALUE.
openfile <filename> openfile <filename>
Open file FILENAME for read access and retrun the file descriptor. Open file FILENAME for read access and return the file descriptor.
createfile <filename> createfile <filename>
Create file FILENAME, open for write access and retrun the file Create file FILENAME, open for write access and return the file
descriptor. descriptor.
pipeserver <program> pipeserver <program>

View File

@ -1,3 +1,8 @@
2005-02-24 Werner Koch <wk@g10code.com>
* gpg-connect-agent.c: New.
* Makefile.am: Add it.
2004-12-21 Werner Koch <wk@g10code.com> 2004-12-21 Werner Koch <wk@g10code.com>
* gpgconf-comp.c (get_config_pathname) [DOSISH]: Detect absolute * gpgconf-comp.c (get_config_pathname) [DOSISH]: Detect absolute

View File

@ -24,15 +24,13 @@ EXTRA_DIST = Manifest watchgnupg.c \
AM_CPPFLAGS = -I$(top_srcdir)/intl -I$(top_srcdir)/common AM_CPPFLAGS = -I$(top_srcdir)/intl -I$(top_srcdir)/common
include $(top_srcdir)/am/cmacros.am include $(top_srcdir)/am/cmacros.am
# Note, that we require GPG_ERROR_CFLAGS only because some share header files AM_CFLAGS = $(GPG_ERROR_CFLAGS) $(LIBASSUAN_CFLAGS)
# require that file. It is not actually used in gpgconf.
AM_CFLAGS = @GPG_ERROR_CFLAGS@
sbin_SCRIPTS = addgnupghome sbin_SCRIPTS = addgnupghome
bin_SCRIPTS = gpgsm-gencert.sh bin_SCRIPTS = gpgsm-gencert.sh
bin_PROGRAMS = gpgconf bin_PROGRAMS = gpgconf gpg-connect-agent
if !HAVE_W32_SYSTEM if !HAVE_W32_SYSTEM
bin_PROGRAMS += watchgnupg bin_PROGRAMS += watchgnupg
endif endif
@ -41,4 +39,9 @@ gpgconf_SOURCES = gpgconf.c gpgconf.h gpgconf-comp.c no-libgcrypt.c
gpgconf_LDADD = ../jnlib/libjnlib.a ../common/libcommon.a @LIBINTL@ gpgconf_LDADD = ../jnlib/libjnlib.a ../common/libcommon.a @LIBINTL@
watchgnupg_SOURCES = watchgnupg.c watchgnupg_SOURCES = watchgnupg.c
gpg_connect_agent_SOURCES = gpg-connect-agent.c no-libgcrypt.c
gpg_connect_agent_LDADD = ../jnlib/libjnlib.a ../common/libcommon.a \
$(LIBASSUAN_LIBS) $(GPG_ERROR_LIBS) $(LIBINTL)

362
tools/gpg-connect-agent.c Normal file
View File

@ -0,0 +1,362 @@
/* gpg-connect-agent.c - Tool to connect to the agent.
* Copyright (C) 2005 Free Software Foundation, Inc.
*
* This file is part of GnuPG.
*
* GnuPG is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* GnuPG is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
*/
#include <config.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <assuan.h>
#include "i18n.h"
#include "../common/util.h"
#include "../common/asshelp.h"
/* Constants to identify the commands and options. */
enum cmd_and_opt_values
{
aNull = 0,
oQuiet = 'q',
oVerbose = 'v',
oNoVerbose = 500,
oHomedir
};
/* The list of commands and options. */
static ARGPARSE_OPTS opts[] =
{
{ 301, NULL, 0, N_("@\nOptions:\n ") },
{ oVerbose, "verbose", 0, N_("verbose") },
{ oQuiet, "quiet", 0, N_("quiet") },
/* hidden options */
{ oNoVerbose, "no-verbose", 0, "@"},
{ oHomedir, "homedir", 2, "@" },
{0}
};
/* We keep all global options in the structure OPT. */
struct
{
int verbose; /* Verbosity level. */
int quiet; /* Be extra quiet. */
const char *homedir; /* Configuration directory name */
} opt;
/*-- local prototypes --*/
static int read_and_print_response (assuan_context_t ctx);
static assuan_context_t start_agent (void);
/* Print usage information and and provide strings for help. */
static const char *
my_strusage( int level )
{
const char *p;
switch (level)
{
case 11: p = "gpg-connect-agent (GnuPG)";
break;
case 13: p = VERSION; break;
case 17: p = PRINTABLE_OS_NAME; break;
case 19: p = _("Please report bugs to <" PACKAGE_BUGREPORT ">.\n");
break;
case 1:
case 40: p = _("Usage: gpg-connect-agent [options] (-h for help)");
break;
case 41:
p = _("Syntax: gpg-connect-agent [options]\n"
"Connect to a running agent and send commands\n");
break;
case 31: p = "\nHome: "; break;
case 32: p = opt.homedir; break;
case 33: p = "\n"; break;
default: p = NULL; break;
}
return p;
}
/* Initialize the gettext system. */
static void
i18n_init(void)
{
#ifdef USE_SIMPLE_GETTEXT
set_gettext_file (PACKAGE_GT);
#else
# ifdef ENABLE_NLS
setlocale (LC_ALL, "" );
bindtextdomain (PACKAGE_GT, LOCALEDIR);
textdomain (PACKAGE_GT);
# endif
#endif
}
/* gpg-connect-agent's entry point. */
int
main (int argc, char **argv)
{
ARGPARSE_ARGS pargs;
const char *fname;
int no_more_options = 0;
assuan_context_t ctx;
char *line;
size_t linesize;
int rc;
set_strusage (my_strusage);
log_set_prefix ("gpg-connect-agent", 1);
i18n_init();
opt.homedir = default_homedir ();
/* Parse the command line. */
pargs.argc = &argc;
pargs.argv = &argv;
pargs.flags = 1; /* Do not remove the args. */
while (!no_more_options && optfile_parse (NULL, NULL, NULL, &pargs, opts))
{
switch (pargs.r_opt)
{
case oQuiet: opt.quiet = 1; break;
case oVerbose: opt.verbose++; break;
case oNoVerbose: opt.verbose = 0; break;
case oHomedir: opt.homedir = pargs.r.ret_str; break;
default: pargs.err = 2; break;
}
}
if (log_get_errorcount (0))
exit (2);
fname = argc ? *argv : NULL;
ctx = start_agent ();
line = NULL;
linesize = 0;
for (;;)
{
int n;
size_t maxlength;
maxlength = 2048;
n = read_line (stdin, &line, &linesize, &maxlength);
if (n < 0)
{
log_error (_("error reading input: %s\n"), strerror (errno));
exit (1);
}
if (!n)
break; /* EOF */
if (!maxlength)
{
log_error (_("line too long - skipped\n"));
continue;
}
if (memchr (line, 0, n))
log_info (_("line shortened due to embedded Nul character\n"));
if (line[n-1] == '\n')
line[n-1] = 0;
rc = assuan_write_line (ctx, line);
if (rc)
{
log_info (_("sending line failed: %s\n"), assuan_strerror (rc) );
continue;
}
if (*line == '#' || !*line)
continue; /* Don't expect a response for a coment line. */
rc = read_and_print_response (ctx);
if (rc)
log_info (_("receiving line failed: %s\n"), assuan_strerror (rc) );
}
if (opt.verbose)
log_info ("closing connection to agent\n");
return 0;
}
/* Read all response lines from server and print them. Returns 0 on
success or an assuan error code. */
static int
read_and_print_response (assuan_context_t ctx)
{
char *line;
int linelen;
assuan_error_t rc;
for (;;)
{
do
{
rc = assuan_read_line (ctx, &line, &linelen);
if (rc)
return rc;
}
while (*line == '#' || !linelen);
if (linelen >= 1
&& line[0] == 'D' && line[1] == ' ')
{
fwrite (line, linelen, 1, stdout);
putchar ('\n');
}
else if (linelen >= 1
&& line[0] == 'S'
&& (line[1] == '\0' || line[1] == ' '))
{
fwrite (line, linelen, 1, stdout);
putchar ('\n');
}
else if (linelen >= 2
&& line[0] == 'O' && line[1] == 'K'
&& (line[2] == '\0' || line[2] == ' '))
{
fwrite (line, linelen, 1, stdout);
putchar ('\n');
return 0;
}
else if (linelen >= 3
&& line[0] == 'E' && line[1] == 'R' && line[2] == 'R'
&& (line[3] == '\0' || line[3] == ' '))
{
fwrite (line, linelen, 1, stdout);
putchar ('\n');
return 0;
}
else if (linelen >= 7
&& line[0] == 'I' && line[1] == 'N' && line[2] == 'Q'
&& line[3] == 'U' && line[4] == 'I' && line[5] == 'R'
&& line[6] == 'E'
&& (line[7] == '\0' || line[7] == ' '))
{
fwrite (line, linelen, 1, stdout);
putchar ('\n');
return 0;
}
else if (linelen >= 3
&& line[0] == 'E' && line[1] == 'N' && line[2] == 'D'
&& (line[3] == '\0' || line[3] == ' '))
{
fwrite (line, linelen, 1, stdout);
putchar ('\n');
/* Received from server, thus more responses are expected. */
}
else
return ASSUAN_Invalid_Response;
}
}
/* Connect to teh agebnt and send the standard options. */
static assuan_context_t
start_agent (void)
{
int rc = 0;
char *infostr, *p;
assuan_context_t ctx;
infostr = getenv ("GPG_AGENT_INFO");
if (!infostr || !*infostr)
{
char *sockname;
/* Check whether we can connect at the standard socket. */
sockname = make_filename (opt.homedir, "S.gpg-agent", NULL);
rc = assuan_socket_connect (&ctx, sockname, 0);
xfree (sockname);
}
else
{
int prot;
int pid;
infostr = xstrdup (infostr);
if ( !(p = strchr (infostr, PATHSEP_C)) || p == infostr)
{
log_error (_("malformed GPG_AGENT_INFO environment variable\n"));
xfree (infostr);
exit (1);
}
*p++ = 0;
pid = atoi (p);
while (*p && *p != ':')
p++;
prot = *p? atoi (p+1) : 0;
if (prot != 1)
{
log_error (_("gpg-agent protocol version %d is not supported\n"),
prot);
xfree (infostr);
exit (1);
}
rc = assuan_socket_connect (&ctx, infostr, pid);
xfree (infostr);
}
if (rc)
{
log_error ("can't connect to the agent: %s\n", assuan_strerror (rc));
exit (1);
}
if (opt.verbose)
log_info ("connection to agent established\n");
rc = assuan_transact (ctx, "RESET", NULL, NULL, NULL, NULL, NULL, NULL);
if (rc)
{
log_error (_("error sending %s command: %s\n"), "RESET",
assuan_strerror (rc));
exit (1);
}
rc = send_pinentry_environment (ctx, GPG_ERR_SOURCE_DEFAULT,
NULL, NULL, NULL, NULL, NULL);
if (rc)
{
log_error (_("error sending standard options: %s\n"), gpg_strerror (rc));
exit (1);
}
return ctx;
}