mirror of
git://git.gnupg.org/gnupg.git
synced 2024-12-22 10:19:57 +01:00
* protect-tool.c: New option --canonical.
(show_file): Implement it. * keyformat.txt: Define the created-at attribute for keys. * ccid-driver.c: Replaced macro DEBUG_T1 by a new debug level. (parse_ccid_descriptor): Mark SCR335 firmware version 5.18 good. (ccid_transceive): Arghhh. The seqno is another bit in the R-block than in the I block, this was wrong at one place. * scdaemon.c: New options --debug-ccid-driver and --debug-disable-ticker. * app-openpgp.c (do_genkey, do_writekey): Factored code to check for existing key out into .. (does_key_exist): .. New function. * gpg-connect-agent.c (add_definq, show_definq, clear_definq) (handle_inquire): New. (read_and_print_response): Handle INQUIRE command. (main): Implement control commands.
This commit is contained in:
parent
4237a9cc7f
commit
41862f5f13
@ -1,3 +1,10 @@
|
|||||||
|
2005-05-20 Werner Koch <wk@g10code.com>
|
||||||
|
|
||||||
|
* protect-tool.c: New option --canonical.
|
||||||
|
(show_file): Implement it.
|
||||||
|
|
||||||
|
* keyformat.txt: Define the created-at attribute for keys.
|
||||||
|
|
||||||
2005-05-18 Werner Koch <wk@g10code.com>
|
2005-05-18 Werner Koch <wk@g10code.com>
|
||||||
|
|
||||||
* divert-scd.c (ask_for_card): Removed the card reset kludge.
|
* divert-scd.c (ask_for_card): Removed the card reset kludge.
|
||||||
|
@ -158,7 +158,7 @@ start_scd (ctrl_t ctrl)
|
|||||||
gpg_error_t err = 0;
|
gpg_error_t err = 0;
|
||||||
const char *pgmname;
|
const char *pgmname;
|
||||||
assuan_context_t ctx;
|
assuan_context_t ctx;
|
||||||
const char *argv[4];
|
const char *argv[3];
|
||||||
int no_close_list[3];
|
int no_close_list[3];
|
||||||
int i;
|
int i;
|
||||||
int rc;
|
int rc;
|
||||||
@ -266,9 +266,8 @@ start_scd (ctrl_t ctrl)
|
|||||||
pgmname++;
|
pgmname++;
|
||||||
|
|
||||||
argv[0] = pgmname;
|
argv[0] = pgmname;
|
||||||
argv[1] = "--server";
|
argv[1] = "--multi-server";
|
||||||
argv[2] = "--multi-server";
|
argv[2] = NULL;
|
||||||
argv[3] = NULL;
|
|
||||||
|
|
||||||
i=0;
|
i=0;
|
||||||
if (!opt.running_detached)
|
if (!opt.running_detached)
|
||||||
|
@ -30,12 +30,17 @@ Libgcrypt. Here is an example of an unprotected file:
|
|||||||
(q #00f7a7c..[some bytes not shown]..61#)
|
(q #00f7a7c..[some bytes not shown]..61#)
|
||||||
(u #304559a..[some bytes not shown]..9b#)
|
(u #304559a..[some bytes not shown]..9b#)
|
||||||
)
|
)
|
||||||
|
(created-at timestamp)
|
||||||
(uri http://foo.bar x-foo:whatever_you_want)
|
(uri http://foo.bar x-foo:whatever_you_want)
|
||||||
(comment whatever)
|
(comment whatever)
|
||||||
)
|
)
|
||||||
|
|
||||||
"comment" and "uri" are optional. "comment" is currently used to keep
|
"comment", "created-at" and "uri" are optional. "comment" is
|
||||||
track of ssh key comments.
|
currently used to keep track of ssh key comments. "created-at" is used
|
||||||
|
to keep track of the creation time stamp used with OpenPGP keys; it is
|
||||||
|
optional but required for some operations to calculate the fingerprint
|
||||||
|
of the key. This timestamp should be a string with the number of
|
||||||
|
seconds since Epoch or an ISO time string (yyyymmddThhmmss).
|
||||||
|
|
||||||
Actually this form should not be used for regular purposes and only
|
Actually this form should not be used for regular purposes and only
|
||||||
accepted by gpg-agent with the configuration option:
|
accepted by gpg-agent with the configuration option:
|
||||||
|
@ -60,6 +60,7 @@ enum cmd_and_opt_values
|
|||||||
oShadow,
|
oShadow,
|
||||||
oShowShadowInfo,
|
oShowShadowInfo,
|
||||||
oShowKeygrip,
|
oShowKeygrip,
|
||||||
|
oCanonical,
|
||||||
|
|
||||||
oP12Import,
|
oP12Import,
|
||||||
oP12Export,
|
oP12Export,
|
||||||
@ -86,6 +87,7 @@ struct rsa_secret_key_s
|
|||||||
|
|
||||||
static const char *opt_homedir;
|
static const char *opt_homedir;
|
||||||
static int opt_armor;
|
static int opt_armor;
|
||||||
|
static int opt_canonical;
|
||||||
static int opt_store;
|
static int opt_store;
|
||||||
static int opt_force;
|
static int opt_force;
|
||||||
static int opt_no_fail_on_exist;
|
static int opt_no_fail_on_exist;
|
||||||
@ -107,6 +109,7 @@ static ARGPARSE_OPTS opts[] = {
|
|||||||
|
|
||||||
{ oVerbose, "verbose", 0, "verbose" },
|
{ oVerbose, "verbose", 0, "verbose" },
|
||||||
{ oArmor, "armor", 0, "write output in advanced format" },
|
{ oArmor, "armor", 0, "write output in advanced format" },
|
||||||
|
{ oCanonical, "canonical", 0, "write output in canonical format" },
|
||||||
{ oPassphrase, "passphrase", 2, "|STRING|use passphrase STRING" },
|
{ oPassphrase, "passphrase", 2, "|STRING|use passphrase STRING" },
|
||||||
{ oProtect, "protect", 256, "protect a private key"},
|
{ oProtect, "protect", 256, "protect a private key"},
|
||||||
{ oUnprotect, "unprotect", 256, "unprotect a private key"},
|
{ oUnprotect, "unprotect", 256, "unprotect a private key"},
|
||||||
@ -509,13 +512,20 @@ show_file (const char *fname)
|
|||||||
keylen = gcry_sexp_canon_len (key, 0, NULL,NULL);
|
keylen = gcry_sexp_canon_len (key, 0, NULL,NULL);
|
||||||
assert (keylen);
|
assert (keylen);
|
||||||
|
|
||||||
|
if (opt_canonical)
|
||||||
|
{
|
||||||
|
fwrite (key, keylen, 1, stdout);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
p = make_advanced (key, keylen);
|
p = make_advanced (key, keylen);
|
||||||
xfree (key);
|
|
||||||
if (p)
|
if (p)
|
||||||
{
|
{
|
||||||
fwrite (p, strlen (p), 1, stdout);
|
fwrite (p, strlen (p), 1, stdout);
|
||||||
xfree (p);
|
xfree (p);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
xfree (key);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
@ -1079,6 +1089,7 @@ main (int argc, char **argv )
|
|||||||
{
|
{
|
||||||
case oVerbose: opt.verbose++; break;
|
case oVerbose: opt.verbose++; break;
|
||||||
case oArmor: opt_armor=1; break;
|
case oArmor: opt_armor=1; break;
|
||||||
|
case oCanonical: opt_canonical=1; break;
|
||||||
case oHomedir: opt_homedir = pargs.r.ret_str; break;
|
case oHomedir: opt_homedir = pargs.r.ret_str; break;
|
||||||
|
|
||||||
case oProtect: cmd = oProtect; break;
|
case oProtect: cmd = oProtect; break;
|
||||||
|
@ -60,7 +60,7 @@ hash_passphrase (const char *passphrase, int hashalgo,
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* Calculate the MIC for a private key S-Exp. SHA1HASH should pint to
|
/* Calculate the MIC for a private key S-Exp. SHA1HASH should point to
|
||||||
a 20 byte buffer. This function is suitable for any algorithms. */
|
a 20 byte buffer. This function is suitable for any algorithms. */
|
||||||
static int
|
static int
|
||||||
calculate_mic (const unsigned char *plainkey, unsigned char *sha1hash)
|
calculate_mic (const unsigned char *plainkey, unsigned char *sha1hash)
|
||||||
|
@ -54,6 +54,12 @@ abbreviate this command.
|
|||||||
Run in server mode and wait for commands on the @code{stdin}. This is
|
Run in server mode and wait for commands on the @code{stdin}. This is
|
||||||
default mode is to create a socket and listen for commands there.
|
default mode is to create a socket and listen for commands there.
|
||||||
|
|
||||||
|
@item --multi-server
|
||||||
|
@opindex multi-server
|
||||||
|
Run in server mode and wait for commands on the @code{stdin} as well as
|
||||||
|
on an additional Unix Domain socket. The server command @code{GETINFO}
|
||||||
|
may be used to get the name of that extra socket.
|
||||||
|
|
||||||
@item --daemon
|
@item --daemon
|
||||||
@opindex daemon
|
@opindex daemon
|
||||||
Run the program in the background. This option is required to prevent
|
Run the program in the background. This option is required to prevent
|
||||||
@ -120,6 +126,13 @@ How these messages are mapped to the actual debugging flags is not
|
|||||||
specified and may change with newer releaes of this program. They are
|
specified and may change with newer releaes of this program. They are
|
||||||
however carefully selected to best aid in debugging.
|
however carefully selected to best aid in debugging.
|
||||||
|
|
||||||
|
@quotation Note
|
||||||
|
All debugging options are subject to change and thus should not be used
|
||||||
|
by any application program. As the name says, they are only used as
|
||||||
|
helpers to debug problems.
|
||||||
|
@end quotation
|
||||||
|
|
||||||
|
|
||||||
@item --debug @var{flags}
|
@item --debug @var{flags}
|
||||||
@opindex debug
|
@opindex debug
|
||||||
This option is only useful for debugging and the behaviour may change at
|
This option is only useful for debugging and the behaviour may change at
|
||||||
@ -128,7 +141,7 @@ usual C-Syntax. The currently defined bits are:
|
|||||||
|
|
||||||
@table @code
|
@table @code
|
||||||
@item 0 (1)
|
@item 0 (1)
|
||||||
X.509 or OpenPGP protocol related data
|
command I/O
|
||||||
@item 1 (2)
|
@item 1 (2)
|
||||||
values of big number integers
|
values of big number integers
|
||||||
@item 2 (4)
|
@item 2 (4)
|
||||||
@ -143,8 +156,8 @@ usual C-Syntax. The currently defined bits are:
|
|||||||
write hashed data to files named @code{dbgmd-000*}
|
write hashed data to files named @code{dbgmd-000*}
|
||||||
@item 10 (1024)
|
@item 10 (1024)
|
||||||
trace Assuan protocol
|
trace Assuan protocol
|
||||||
@item 12 (4096)
|
@item 11 (2048)
|
||||||
bypass all certificate validation
|
trace APDU I/O to the card. This may reveal sensitive data.
|
||||||
@end table
|
@end table
|
||||||
|
|
||||||
@item --debug-all
|
@item --debug-all
|
||||||
@ -157,6 +170,17 @@ When running in server mode, wait @var{n} seconds before entering the
|
|||||||
actual processing loop and print the pid. This gives time to attach a
|
actual processing loop and print the pid. This gives time to attach a
|
||||||
debugger.
|
debugger.
|
||||||
|
|
||||||
|
@item --debug-ccid-driver
|
||||||
|
@opindex debug-wait
|
||||||
|
Enable debug output from the included CCID driver for smartcards.
|
||||||
|
Using this option twice will also enable some tracing of the T=1
|
||||||
|
protocol. Note that this option may reveal sensitive data.
|
||||||
|
|
||||||
|
@item --debug-disable-ticker
|
||||||
|
@opindex debug-disable-ticker
|
||||||
|
This option disables all ticker functions like checking for card
|
||||||
|
insertions.
|
||||||
|
|
||||||
@item --no-detach
|
@item --no-detach
|
||||||
@opindex no-detach
|
@opindex no-detach
|
||||||
Don't detach the process from the console. This is manly usefule for
|
Don't detach the process from the console. This is manly usefule for
|
||||||
@ -286,6 +310,7 @@ syncronizing access to a token between sessions.
|
|||||||
* Scdaemon PKDECRYPT:: Decrypting data with a Smartcard.
|
* Scdaemon PKDECRYPT:: Decrypting data with a Smartcard.
|
||||||
* Scdaemon GETATTR:: Read an attribute's value.
|
* Scdaemon GETATTR:: Read an attribute's value.
|
||||||
* Scdaemon SETATTR:: Update an attribute's value.
|
* Scdaemon SETATTR:: Update an attribute's value.
|
||||||
|
* Scdaemon WRITEKEY:: Write a key to a card.
|
||||||
* Scdaemon GENKEY:: Generate a new key on-card.
|
* Scdaemon GENKEY:: Generate a new key on-card.
|
||||||
* Scdaemon RANDOM:: Return random bytes generate on-card.
|
* Scdaemon RANDOM:: Return random bytes generate on-card.
|
||||||
* Scdaemon PASSWD:: Change PINs.
|
* Scdaemon PASSWD:: Change PINs.
|
||||||
@ -420,6 +445,25 @@ TO BE WRITTEN.
|
|||||||
|
|
||||||
TO BE WRITTEN.
|
TO BE WRITTEN.
|
||||||
|
|
||||||
|
@node Scdaemon WRITEKEY
|
||||||
|
@subsection Write a key to a card.
|
||||||
|
|
||||||
|
@example
|
||||||
|
WRITEKEY [--force] @var{keyid}
|
||||||
|
@end example
|
||||||
|
|
||||||
|
This command is used to store a secret key on a a smartcard. The
|
||||||
|
allowed keyids depend on the currently selected smartcard
|
||||||
|
application. The actual keydata is requested using the inquiry
|
||||||
|
@code{KEYDATA} and need to be provided without any protection. With
|
||||||
|
@option{--force} set an existing key under this @var{keyid} will get
|
||||||
|
overwritten. The key data is expected to be the usual canonical encoded
|
||||||
|
S-expression.
|
||||||
|
|
||||||
|
A PIN will be requested in most saes. This however depends on the
|
||||||
|
actual card application.
|
||||||
|
|
||||||
|
|
||||||
@node Scdaemon GENKEY
|
@node Scdaemon GENKEY
|
||||||
@subsection Generate a new key on-card.
|
@subsection Generate a new key on-card.
|
||||||
|
|
||||||
|
@ -1,3 +1,30 @@
|
|||||||
|
2005-05-20 Werner Koch <wk@g10code.com>
|
||||||
|
|
||||||
|
* ccid-driver.c: Replaced macro DEBUG_T1 by a new debug level.
|
||||||
|
(parse_ccid_descriptor): Mark SCR335 firmware version 5.18 good.
|
||||||
|
(ccid_transceive): Arghhh. The seqno is another bit in the
|
||||||
|
R-block than in the I block, this was wrong at one place.
|
||||||
|
|
||||||
|
* scdaemon.c: New options --debug-ccid-driver and
|
||||||
|
--debug-disable-ticker.
|
||||||
|
|
||||||
|
* app-openpgp.c (do_genkey, do_writekey): Factored code to check
|
||||||
|
for existing key out into ..
|
||||||
|
(does_key_exist): .. New function.
|
||||||
|
|
||||||
|
2005-05-19 Werner Koch <wk@g10code.com>
|
||||||
|
|
||||||
|
* tlv.c (parse_sexp): New.
|
||||||
|
|
||||||
|
* command.c (cmd_writekey): New.
|
||||||
|
* app.c (app_writekey): New.
|
||||||
|
* app-common.c (app_t): Add function ptr WRITEKEY.
|
||||||
|
* app-openpgp.c (do_writekey): New.
|
||||||
|
|
||||||
|
* app-openpgp.c (do_readkey) [GNUPG_MAJOR_VERSION==1]: Return error.
|
||||||
|
* app-common.h (app_t) [GNUPG_MAJOR_VERSION==1]: Add a field to
|
||||||
|
store the Assuan context.
|
||||||
|
|
||||||
2005-05-17 Werner Koch <wk@g10code.com>
|
2005-05-17 Werner Koch <wk@g10code.com>
|
||||||
|
|
||||||
* scdaemon.c: Removed non-pth code paths.
|
* scdaemon.c: Removed non-pth code paths.
|
||||||
|
@ -23,10 +23,15 @@
|
|||||||
#ifndef GNUPG_SCD_APP_COMMON_H
|
#ifndef GNUPG_SCD_APP_COMMON_H
|
||||||
#define GNUPG_SCD_APP_COMMON_H
|
#define GNUPG_SCD_APP_COMMON_H
|
||||||
|
|
||||||
#if GNUPG_MAJOR_VERSION != 1
|
#if GNUPG_MAJOR_VERSION == 1
|
||||||
#include <ksba.h>
|
# ifdef ENABLE_AGENT_SUPPORT
|
||||||
|
# include "assuan.h"
|
||||||
|
# endif
|
||||||
|
#else
|
||||||
|
# include <ksba.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
struct app_local_s; /* Defined by all app-*.c. */
|
struct app_local_s; /* Defined by all app-*.c. */
|
||||||
|
|
||||||
struct app_ctx_s {
|
struct app_ctx_s {
|
||||||
@ -35,6 +40,15 @@ struct app_ctx_s {
|
|||||||
unsupported operations the particular
|
unsupported operations the particular
|
||||||
function pointer is set to NULL */
|
function pointer is set to NULL */
|
||||||
int slot; /* Used reader. */
|
int slot; /* Used reader. */
|
||||||
|
|
||||||
|
/* If this is used by GnuPG 1.4 we need to know the assuan context
|
||||||
|
in case we need to divert the operation to an already running
|
||||||
|
agent. This if ASSUAN_CTX is not NULL we take this as indication
|
||||||
|
that all operations are diverted to gpg-agent. */
|
||||||
|
#if GNUPG_MAJOR_VERSION == 1 && defined(ENABLE_AGENT_SUPPORT)
|
||||||
|
assuan_context_t assuan_ctx;
|
||||||
|
#endif /*GNUPG_MAJOR_VERSION == 1*/
|
||||||
|
|
||||||
unsigned char *serialno; /* Serialnumber in raw form, allocated. */
|
unsigned char *serialno; /* Serialnumber in raw form, allocated. */
|
||||||
size_t serialnolen; /* Length in octets of serialnumber. */
|
size_t serialnolen; /* Length in octets of serialnumber. */
|
||||||
const char *apptype;
|
const char *apptype;
|
||||||
@ -72,6 +86,11 @@ struct app_ctx_s {
|
|||||||
void *pincb_arg,
|
void *pincb_arg,
|
||||||
const void *indata, size_t indatalen,
|
const void *indata, size_t indatalen,
|
||||||
unsigned char **outdata, size_t *outdatalen);
|
unsigned char **outdata, size_t *outdatalen);
|
||||||
|
gpg_error_t (*writekey) (app_t app, ctrl_t ctrl,
|
||||||
|
const char *certid, unsigned int flags,
|
||||||
|
gpg_error_t (*pincb)(void*,const char *,char **),
|
||||||
|
void *pincb_arg,
|
||||||
|
const unsigned char *pk, size_t pklen);
|
||||||
gpg_error_t (*genkey) (app_t app, ctrl_t ctrl,
|
gpg_error_t (*genkey) (app_t app, ctrl_t ctrl,
|
||||||
const char *keynostr, unsigned int flags,
|
const char *keynostr, unsigned int flags,
|
||||||
gpg_error_t (*pincb)(void*, const char *, char **),
|
gpg_error_t (*pincb)(void*, const char *, char **),
|
||||||
@ -134,6 +153,11 @@ gpg_error_t app_decipher (app_t app, const char *keyidstr,
|
|||||||
void *pincb_arg,
|
void *pincb_arg,
|
||||||
const void *indata, size_t indatalen,
|
const void *indata, size_t indatalen,
|
||||||
unsigned char **outdata, size_t *outdatalen );
|
unsigned char **outdata, size_t *outdatalen );
|
||||||
|
gpg_error_t app_writekey (app_t app, ctrl_t ctrl,
|
||||||
|
const char *keyidstr, unsigned int flags,
|
||||||
|
gpg_error_t (*pincb)(void*, const char *, char **),
|
||||||
|
void *pincb_arg,
|
||||||
|
const unsigned char *keydata, size_t keydatalen);
|
||||||
gpg_error_t app_genkey (app_t app, ctrl_t ctrl,
|
gpg_error_t app_genkey (app_t app, ctrl_t ctrl,
|
||||||
const char *keynostr, unsigned int flags,
|
const char *keynostr, unsigned int flags,
|
||||||
gpg_error_t (*pincb)(void*, const char *, char **),
|
gpg_error_t (*pincb)(void*, const char *, char **),
|
||||||
|
@ -565,7 +565,7 @@ store_fpr (int slot, int keynumber, u32 timestamp,
|
|||||||
n = 6 + 2 + mlen + 2 + elen;
|
n = 6 + 2 + mlen + 2 + elen;
|
||||||
p = buffer = xtrymalloc (3 + n);
|
p = buffer = xtrymalloc (3 + n);
|
||||||
if (!buffer)
|
if (!buffer)
|
||||||
return gpg_error (gpg_err_code_from_errno (errno));
|
return gpg_error_from_errno (errno);
|
||||||
|
|
||||||
*p++ = 0x99; /* ctb */
|
*p++ = 0x99; /* ctb */
|
||||||
*p++ = n >> 8; /* 2 byte length header */
|
*p++ = n >> 8; /* 2 byte length header */
|
||||||
@ -1207,6 +1207,7 @@ do_learn_status (app_t app, ctrl_t ctrl)
|
|||||||
static gpg_error_t
|
static gpg_error_t
|
||||||
do_readkey (app_t app, const char *keyid, unsigned char **pk, size_t *pklen)
|
do_readkey (app_t app, const char *keyid, unsigned char **pk, size_t *pklen)
|
||||||
{
|
{
|
||||||
|
#if GNUPG_MAJOR_VERSION > 1
|
||||||
gpg_error_t err;
|
gpg_error_t err;
|
||||||
int keyno;
|
int keyno;
|
||||||
unsigned char *buf;
|
unsigned char *buf;
|
||||||
@ -1230,6 +1231,9 @@ do_readkey (app_t app, const char *keyid, unsigned char **pk, size_t *pklen)
|
|||||||
*pk = buf;
|
*pk = buf;
|
||||||
*pklen = app->app_local->pk[keyno-1].keylen;;
|
*pklen = app->app_local->pk[keyno-1].keylen;;
|
||||||
return 0;
|
return 0;
|
||||||
|
#else
|
||||||
|
return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -1523,6 +1527,318 @@ do_change_pin (app_t app, ctrl_t ctrl, const char *chvnostr, int reset_mode,
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Check whether a key already exists. KEYIDX is the index of the key
|
||||||
|
(0..2). If FORCE is TRUE a diagnositivc will be printed but no
|
||||||
|
error returned if the key already exists. */
|
||||||
|
static gpg_error_t
|
||||||
|
does_key_exist (app_t app, int keyidx, int force)
|
||||||
|
{
|
||||||
|
const unsigned char *fpr;
|
||||||
|
unsigned char *buffer;
|
||||||
|
size_t buflen, n;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
assert (keyidx >=0 && keyidx <= 2);
|
||||||
|
|
||||||
|
if (iso7816_get_data (app->slot, 0x006E, &buffer, &buflen))
|
||||||
|
{
|
||||||
|
log_error (_("error reading application data\n"));
|
||||||
|
return gpg_error (GPG_ERR_GENERAL);
|
||||||
|
}
|
||||||
|
fpr = find_tlv (buffer, buflen, 0x00C5, &n);
|
||||||
|
if (!fpr || n < 60)
|
||||||
|
{
|
||||||
|
log_error (_("error reading fingerprint DO\n"));
|
||||||
|
xfree (buffer);
|
||||||
|
return gpg_error (GPG_ERR_GENERAL);
|
||||||
|
}
|
||||||
|
fpr += 20*keyidx;
|
||||||
|
for (i=0; i < 20 && !fpr[i]; i++)
|
||||||
|
;
|
||||||
|
xfree (buffer);
|
||||||
|
if (i!=20 && !force)
|
||||||
|
{
|
||||||
|
log_error (_("key already exists\n"));
|
||||||
|
return gpg_error (GPG_ERR_EEXIST);
|
||||||
|
}
|
||||||
|
else if (i!=20)
|
||||||
|
log_info (_("existing key will be replaced\n"));
|
||||||
|
else
|
||||||
|
log_info (_("generating new key\n"));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* Handle the WRITEKEY command for OpenPGP. This function expects a
|
||||||
|
canonical encoded S-expression with the secret key in KEYDATA and
|
||||||
|
its length (for assertions) in KEYDATALEN. KEYID needs to be the
|
||||||
|
usual keyid which for OpenPGP is the string "OPENPGP.n" with
|
||||||
|
n=1,2,3. Bit 0 of FLAGS indicates whether an existing key shall
|
||||||
|
get overwritten. PINCB and PINCB_ARG are the usual arguments for
|
||||||
|
the pinentry callback. */
|
||||||
|
static gpg_error_t
|
||||||
|
do_writekey (app_t app, ctrl_t ctrl,
|
||||||
|
const char *keyid, unsigned int flags,
|
||||||
|
gpg_error_t (*pincb)(void*, const char *, char **),
|
||||||
|
void *pincb_arg,
|
||||||
|
const unsigned char *keydata, size_t keydatalen)
|
||||||
|
{
|
||||||
|
gpg_error_t err;
|
||||||
|
int force = (flags & 1);
|
||||||
|
int keyno;
|
||||||
|
const unsigned char *buf, *tok;
|
||||||
|
size_t buflen, toklen;
|
||||||
|
int depth, last_depth1, last_depth2;
|
||||||
|
const unsigned char *rsa_n = NULL;
|
||||||
|
const unsigned char *rsa_e = NULL;
|
||||||
|
const unsigned char *rsa_p = NULL;
|
||||||
|
const unsigned char *rsa_q = NULL;
|
||||||
|
size_t rsa_n_len, rsa_e_len, rsa_p_len, rsa_q_len;
|
||||||
|
unsigned int nbits;
|
||||||
|
unsigned char *template = NULL;
|
||||||
|
unsigned char *tp;
|
||||||
|
size_t template_len;
|
||||||
|
unsigned char fprbuf[20];
|
||||||
|
u32 created_at = 0;
|
||||||
|
|
||||||
|
if (!strcmp (keyid, "OPENPGP.1"))
|
||||||
|
keyno = 0;
|
||||||
|
else if (!strcmp (keyid, "OPENPGP.2"))
|
||||||
|
keyno = 1;
|
||||||
|
else if (!strcmp (keyid, "OPENPGP.3"))
|
||||||
|
keyno = 2;
|
||||||
|
else
|
||||||
|
return gpg_error (GPG_ERR_INV_ID);
|
||||||
|
|
||||||
|
err = does_key_exist (app, keyno, force);
|
||||||
|
if (err)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
Parse the S-expression
|
||||||
|
*/
|
||||||
|
buf = keydata;
|
||||||
|
buflen = keydatalen;
|
||||||
|
depth = 0;
|
||||||
|
if ((err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen)))
|
||||||
|
goto leave;
|
||||||
|
if ((err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen)))
|
||||||
|
goto leave;
|
||||||
|
if (!tok || toklen != 11 || memcmp ("private-key", tok, toklen))
|
||||||
|
{
|
||||||
|
if (!tok)
|
||||||
|
;
|
||||||
|
else if (toklen == 21 && !memcmp ("protected-private-key", tok, toklen))
|
||||||
|
log_info ("protected-private-key passed to writekey\n");
|
||||||
|
else if (toklen == 20 && !memcmp ("shadowed-private-key", tok, toklen))
|
||||||
|
log_info ("shadowed-private-key passed to writekey\n");
|
||||||
|
err = gpg_error (GPG_ERR_BAD_KEY);
|
||||||
|
goto leave;
|
||||||
|
}
|
||||||
|
if ((err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen)))
|
||||||
|
goto leave;
|
||||||
|
if ((err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen)))
|
||||||
|
goto leave;
|
||||||
|
if (!tok || toklen != 3 || memcmp ("rsa", tok, toklen))
|
||||||
|
{
|
||||||
|
err = gpg_error (GPG_ERR_WRONG_PUBKEY_ALGO);
|
||||||
|
goto leave;
|
||||||
|
}
|
||||||
|
last_depth1 = depth;
|
||||||
|
while (!(err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen))
|
||||||
|
&& depth && depth >= last_depth1)
|
||||||
|
{
|
||||||
|
if (tok)
|
||||||
|
{
|
||||||
|
err = gpg_error (GPG_ERR_UNKNOWN_SEXP);
|
||||||
|
goto leave;
|
||||||
|
}
|
||||||
|
if ((err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen)))
|
||||||
|
goto leave;
|
||||||
|
if (tok && toklen == 1)
|
||||||
|
{
|
||||||
|
const unsigned char **mpi;
|
||||||
|
size_t *mpi_len;
|
||||||
|
|
||||||
|
switch (*tok)
|
||||||
|
{
|
||||||
|
case 'n': mpi = &rsa_n; mpi_len = &rsa_n_len; break;
|
||||||
|
case 'e': mpi = &rsa_e; mpi_len = &rsa_e_len; break;
|
||||||
|
case 'p': mpi = &rsa_p; mpi_len = &rsa_p_len; break;
|
||||||
|
case 'q': mpi = &rsa_q; mpi_len = &rsa_q_len;break;
|
||||||
|
default: mpi = NULL; mpi_len = NULL; break;
|
||||||
|
}
|
||||||
|
if (mpi && *mpi)
|
||||||
|
{
|
||||||
|
err = gpg_error (GPG_ERR_DUP_VALUE);
|
||||||
|
goto leave;
|
||||||
|
}
|
||||||
|
if ((err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen)))
|
||||||
|
goto leave;
|
||||||
|
if (tok && mpi)
|
||||||
|
{
|
||||||
|
/* Strip off leading zero bytes and save. */
|
||||||
|
for (;toklen && !*tok; toklen--, tok++)
|
||||||
|
;
|
||||||
|
*mpi = tok;
|
||||||
|
*mpi_len = toklen;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* Skip until end of list. */
|
||||||
|
last_depth2 = depth;
|
||||||
|
while (!(err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen))
|
||||||
|
&& depth && depth >= last_depth2)
|
||||||
|
;
|
||||||
|
if (err)
|
||||||
|
goto leave;
|
||||||
|
}
|
||||||
|
/* Parse other attributes. */
|
||||||
|
last_depth1 = depth;
|
||||||
|
while (!(err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen))
|
||||||
|
&& depth && depth >= last_depth1)
|
||||||
|
{
|
||||||
|
if (tok)
|
||||||
|
{
|
||||||
|
err = gpg_error (GPG_ERR_UNKNOWN_SEXP);
|
||||||
|
goto leave;
|
||||||
|
}
|
||||||
|
if ((err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen)))
|
||||||
|
goto leave;
|
||||||
|
if (tok && toklen == 10 && !memcmp ("created-at", tok, toklen))
|
||||||
|
{
|
||||||
|
if ((err = parse_sexp (&buf,&buflen,&depth,&tok,&toklen)))
|
||||||
|
goto leave;
|
||||||
|
if (tok)
|
||||||
|
{
|
||||||
|
for (created_at=0; toklen && *tok && *tok >= '0' && *tok <= '9';
|
||||||
|
tok++, toklen--)
|
||||||
|
created_at = created_at*10 + (*tok - '0');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* Skip until end of list. */
|
||||||
|
last_depth2 = depth;
|
||||||
|
while (!(err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen))
|
||||||
|
&& depth && depth >= last_depth2)
|
||||||
|
;
|
||||||
|
if (err)
|
||||||
|
goto leave;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Check that we have all parameters and that they match the card
|
||||||
|
description. */
|
||||||
|
if (!created_at)
|
||||||
|
{
|
||||||
|
log_error (_("creation timestamp missing\n"));
|
||||||
|
err = gpg_error (GPG_ERR_INV_VALUE);
|
||||||
|
goto leave;
|
||||||
|
}
|
||||||
|
nbits = rsa_n? count_bits (rsa_n, rsa_n_len) : 0;
|
||||||
|
if (nbits != 1024)
|
||||||
|
{
|
||||||
|
log_error (_("RSA modulus missing or not of size %d bits\n"), 1024);
|
||||||
|
err = gpg_error (GPG_ERR_BAD_SECKEY);
|
||||||
|
goto leave;
|
||||||
|
}
|
||||||
|
nbits = rsa_e? count_bits (rsa_e, rsa_e_len) : 0;
|
||||||
|
if (nbits < 2 || nbits > 32)
|
||||||
|
{
|
||||||
|
log_error (_("RSA public exponent missing or largerr than %d bits\n"),
|
||||||
|
32);
|
||||||
|
err = gpg_error (GPG_ERR_BAD_SECKEY);
|
||||||
|
goto leave;
|
||||||
|
}
|
||||||
|
nbits = rsa_p? count_bits (rsa_p, rsa_p_len) : 0;
|
||||||
|
if (nbits != 512)
|
||||||
|
{
|
||||||
|
log_error (_("RSA prime %s missing or not of size %d bits\n"), "P", 512);
|
||||||
|
err = gpg_error (GPG_ERR_BAD_SECKEY);
|
||||||
|
goto leave;
|
||||||
|
}
|
||||||
|
nbits = rsa_q? count_bits (rsa_q, rsa_q_len) : 0;
|
||||||
|
if (nbits != 512)
|
||||||
|
{
|
||||||
|
log_error (_("RSA prime %s missing or not of size %d bits\n"), "Q", 512);
|
||||||
|
err = gpg_error (GPG_ERR_BAD_SECKEY);
|
||||||
|
goto leave;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Build the private key template as described in section 4.3.3.6 of
|
||||||
|
the OpenPGP card specs:
|
||||||
|
0xC0 <length> public exponent
|
||||||
|
0xC1 <length> prime p
|
||||||
|
0xC2 <length> prime q
|
||||||
|
*/
|
||||||
|
assert (rsa_e_len <= 4);
|
||||||
|
template_len = (1 + 1 + 4
|
||||||
|
+ 1 + 1 + rsa_p_len
|
||||||
|
+ 1 + 1 + rsa_q_len);
|
||||||
|
template = tp = xtrymalloc_secure (template_len);
|
||||||
|
if (!template)
|
||||||
|
{
|
||||||
|
err = gpg_error_from_errno (errno);
|
||||||
|
goto leave;
|
||||||
|
}
|
||||||
|
*tp++ = 0xC0;
|
||||||
|
*tp++ = 4;
|
||||||
|
memcpy (tp, rsa_e, rsa_e_len);
|
||||||
|
if (rsa_e_len < 4)
|
||||||
|
{
|
||||||
|
/* Right justify E. */
|
||||||
|
memmove (tp+4-rsa_e_len, tp, 4-rsa_e_len);
|
||||||
|
memset (tp, 0, 4-rsa_e_len);
|
||||||
|
}
|
||||||
|
tp += 4;
|
||||||
|
|
||||||
|
*tp++ = 0xC1;
|
||||||
|
*tp++ = rsa_p_len;
|
||||||
|
memcpy (tp, rsa_p, rsa_p_len);
|
||||||
|
tp += rsa_p_len;
|
||||||
|
|
||||||
|
*tp++ = 0xC2;
|
||||||
|
*tp++ = rsa_q_len;
|
||||||
|
memcpy (tp, rsa_q, rsa_q_len);
|
||||||
|
tp += rsa_q_len;
|
||||||
|
|
||||||
|
assert (tp - template == template_len);
|
||||||
|
|
||||||
|
|
||||||
|
/* Obviously we need to remove the cached public key. */
|
||||||
|
xfree (app->app_local->pk[keyno].key);
|
||||||
|
app->app_local->pk[keyno].key = NULL;
|
||||||
|
app->app_local->pk[keyno].keylen = 0;
|
||||||
|
app->app_local->pk[keyno].read_done = 0;
|
||||||
|
|
||||||
|
/* Prepare for storing the key. */
|
||||||
|
err = verify_chv3 (app, pincb, pincb_arg);
|
||||||
|
if (err)
|
||||||
|
goto leave;
|
||||||
|
|
||||||
|
/* Store the key. */
|
||||||
|
err = iso7816_put_data (app->slot,
|
||||||
|
(app->card_version > 0x0007? 0xE0 : 0xE9) + keyno,
|
||||||
|
template, template_len);
|
||||||
|
if (err)
|
||||||
|
{
|
||||||
|
log_error (_("failed to store the key: %s\n"), gpg_strerror (err));
|
||||||
|
goto leave;
|
||||||
|
}
|
||||||
|
|
||||||
|
err = store_fpr (app->slot, keyno, created_at,
|
||||||
|
rsa_n, rsa_n_len, rsa_e, rsa_e_len,
|
||||||
|
fprbuf, app->card_version);
|
||||||
|
if (err)
|
||||||
|
goto leave;
|
||||||
|
|
||||||
|
|
||||||
|
leave:
|
||||||
|
xfree (template);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Handle the GENKEY command. */
|
/* Handle the GENKEY command. */
|
||||||
static gpg_error_t
|
static gpg_error_t
|
||||||
@ -1531,13 +1847,11 @@ do_genkey (app_t app, ctrl_t ctrl, const char *keynostr, unsigned int flags,
|
|||||||
void *pincb_arg)
|
void *pincb_arg)
|
||||||
{
|
{
|
||||||
int rc;
|
int rc;
|
||||||
int i;
|
|
||||||
char numbuf[30];
|
char numbuf[30];
|
||||||
unsigned char fprbuf[20];
|
unsigned char fprbuf[20];
|
||||||
const unsigned char *fpr;
|
|
||||||
const unsigned char *keydata, *m, *e;
|
const unsigned char *keydata, *m, *e;
|
||||||
unsigned char *buffer;
|
unsigned char *buffer = NULL;
|
||||||
size_t buflen, keydatalen, n, mlen, elen;
|
size_t buflen, keydatalen, mlen, elen;
|
||||||
time_t created_at;
|
time_t created_at;
|
||||||
int keyno = atoi (keynostr);
|
int keyno = atoi (keynostr);
|
||||||
int force = (flags & 1);
|
int force = (flags & 1);
|
||||||
@ -1558,41 +1872,15 @@ do_genkey (app_t app, ctrl_t ctrl, const char *keynostr, unsigned int flags,
|
|||||||
app->app_local->pk[keyno].read_done = 0;
|
app->app_local->pk[keyno].read_done = 0;
|
||||||
|
|
||||||
/* Check whether a key already exists. */
|
/* Check whether a key already exists. */
|
||||||
rc = iso7816_get_data (app->slot, 0x006E, &buffer, &buflen);
|
rc = does_key_exist (app, keyno, force);
|
||||||
if (rc)
|
if (rc)
|
||||||
{
|
return rc;
|
||||||
log_error (_("error reading application data\n"));
|
|
||||||
return gpg_error (GPG_ERR_GENERAL);
|
|
||||||
}
|
|
||||||
fpr = find_tlv (buffer, buflen, 0x00C5, &n);
|
|
||||||
if (!fpr || n != 60)
|
|
||||||
{
|
|
||||||
rc = gpg_error (GPG_ERR_GENERAL);
|
|
||||||
log_error (_("error reading fingerprint DO\n"));
|
|
||||||
goto leave;
|
|
||||||
}
|
|
||||||
fpr += 20*keyno;
|
|
||||||
for (i=0; i < 20 && !fpr[i]; i++)
|
|
||||||
;
|
|
||||||
if (i!=20 && !force)
|
|
||||||
{
|
|
||||||
rc = gpg_error (GPG_ERR_EEXIST);
|
|
||||||
log_error (_("key already exists\n"));
|
|
||||||
goto leave;
|
|
||||||
}
|
|
||||||
else if (i!=20)
|
|
||||||
log_info (_("existing key will be replaced\n"));
|
|
||||||
else
|
|
||||||
log_info (_("generating new key\n"));
|
|
||||||
|
|
||||||
|
|
||||||
/* Prepare for key generation by verifying the ADmin PIN. */
|
/* Prepare for key generation by verifying the ADmin PIN. */
|
||||||
rc = verify_chv3 (app, pincb, pincb_arg);
|
rc = verify_chv3 (app, pincb, pincb_arg);
|
||||||
if (rc)
|
if (rc)
|
||||||
goto leave;
|
goto leave;
|
||||||
|
|
||||||
xfree (buffer); buffer = NULL;
|
|
||||||
|
|
||||||
#if 1
|
#if 1
|
||||||
log_info (_("please wait while key is being generated ...\n"));
|
log_info (_("please wait while key is being generated ...\n"));
|
||||||
start_at = time (NULL);
|
start_at = time (NULL);
|
||||||
@ -2216,6 +2504,7 @@ app_select_openpgp (app_t app)
|
|||||||
app->fnc.readkey = do_readkey;
|
app->fnc.readkey = do_readkey;
|
||||||
app->fnc.getattr = do_getattr;
|
app->fnc.getattr = do_getattr;
|
||||||
app->fnc.setattr = do_setattr;
|
app->fnc.setattr = do_setattr;
|
||||||
|
app->fnc.writekey = do_writekey;
|
||||||
app->fnc.genkey = do_genkey;
|
app->fnc.genkey = do_genkey;
|
||||||
app->fnc.sign = do_sign;
|
app->fnc.sign = do_sign;
|
||||||
app->fnc.auth = do_auth;
|
app->fnc.auth = do_auth;
|
||||||
|
29
scd/app.c
29
scd/app.c
@ -546,6 +546,35 @@ app_decipher (app_t app, const char *keyidstr,
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Perform the WRITEKEY operation. */
|
||||||
|
gpg_error_t
|
||||||
|
app_writekey (app_t app, ctrl_t ctrl,
|
||||||
|
const char *keyidstr, unsigned int flags,
|
||||||
|
gpg_error_t (*pincb)(void*, const char *, char **),
|
||||||
|
void *pincb_arg,
|
||||||
|
const unsigned char *keydata, size_t keydatalen)
|
||||||
|
{
|
||||||
|
gpg_error_t err;
|
||||||
|
|
||||||
|
if (!app || !keyidstr || !*keyidstr || !pincb)
|
||||||
|
return gpg_error (GPG_ERR_INV_VALUE);
|
||||||
|
if (!app->initialized)
|
||||||
|
return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED);
|
||||||
|
if (!app->fnc.writekey)
|
||||||
|
return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION);
|
||||||
|
err = lock_reader (app);
|
||||||
|
if (err)
|
||||||
|
return err;
|
||||||
|
err = app->fnc.writekey (app, ctrl, keyidstr, flags,
|
||||||
|
pincb, pincb_arg, keydata, keydatalen);
|
||||||
|
unlock_reader (app);
|
||||||
|
if (opt.verbose)
|
||||||
|
log_info ("operation writekey result: %s\n", gpg_strerror (err));
|
||||||
|
return err;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Perform a SETATTR operation. */
|
/* Perform a SETATTR operation. */
|
||||||
gpg_error_t
|
gpg_error_t
|
||||||
app_genkey (app_t app, CTRL ctrl, const char *keynostr, unsigned int flags,
|
app_genkey (app_t app, CTRL ctrl, const char *keynostr, unsigned int flags,
|
||||||
|
@ -108,9 +108,6 @@
|
|||||||
# include "scdaemon.h"
|
# include "scdaemon.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* Define to print information pertaining the T=1 protocol. */
|
|
||||||
#undef DEBUG_T1
|
|
||||||
|
|
||||||
|
|
||||||
# define DEBUGOUT(t) do { if (debug_level) \
|
# define DEBUGOUT(t) do { if (debug_level) \
|
||||||
log_debug (DRVNAME t); } while (0)
|
log_debug (DRVNAME t); } while (0)
|
||||||
@ -120,6 +117,8 @@
|
|||||||
log_debug (DRVNAME t,(a),(b)); } while (0)
|
log_debug (DRVNAME t,(a),(b)); } while (0)
|
||||||
# define DEBUGOUT_3(t,a,b,c) do { if (debug_level) \
|
# define DEBUGOUT_3(t,a,b,c) do { if (debug_level) \
|
||||||
log_debug (DRVNAME t,(a),(b),(c));} while (0)
|
log_debug (DRVNAME t,(a),(b),(c));} while (0)
|
||||||
|
# define DEBUGOUT_4(t,a,b,c,d) do { if (debug_level) \
|
||||||
|
log_debug (DRVNAME t,(a),(b),(c),(d));} while (0)
|
||||||
# define DEBUGOUT_CONT(t) do { if (debug_level) \
|
# define DEBUGOUT_CONT(t) do { if (debug_level) \
|
||||||
log_printf (t); } while (0)
|
log_printf (t); } while (0)
|
||||||
# define DEBUGOUT_CONT_1(t,a) do { if (debug_level) \
|
# define DEBUGOUT_CONT_1(t,a) do { if (debug_level) \
|
||||||
@ -141,6 +140,8 @@
|
|||||||
fprintf (stderr, DRVNAME t, (a), (b)); } while (0)
|
fprintf (stderr, DRVNAME t, (a), (b)); } while (0)
|
||||||
# define DEBUGOUT_3(t,a,b,c) do { if (debug_level) \
|
# define DEBUGOUT_3(t,a,b,c) do { if (debug_level) \
|
||||||
fprintf (stderr, DRVNAME t, (a), (b), (c)); } while (0)
|
fprintf (stderr, DRVNAME t, (a), (b), (c)); } while (0)
|
||||||
|
# define DEBUGOUT_4(t,a,b,c,d) do { if (debug_level) \
|
||||||
|
fprintf (stderr, DRVNAME t, (a), (b), (c), (d));} while(0)
|
||||||
# define DEBUGOUT_CONT(t) do { if (debug_level) \
|
# define DEBUGOUT_CONT(t) do { if (debug_level) \
|
||||||
fprintf (stderr, t); } while (0)
|
fprintf (stderr, t); } while (0)
|
||||||
# define DEBUGOUT_CONT_1(t,a) do { if (debug_level) \
|
# define DEBUGOUT_CONT_1(t,a) do { if (debug_level) \
|
||||||
@ -216,7 +217,11 @@ struct ccid_driver_s
|
|||||||
|
|
||||||
|
|
||||||
static int initialized_usb; /* Tracks whether USB has been initialized. */
|
static int initialized_usb; /* Tracks whether USB has been initialized. */
|
||||||
static int debug_level; /* Flag to control the debug output. */
|
static int debug_level; /* Flag to control the debug output.
|
||||||
|
0 = No debugging
|
||||||
|
1 = USB I/O info
|
||||||
|
2 = T=1 protocol tracing
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
static unsigned int compute_edc (const unsigned char *data, size_t datalen,
|
static unsigned int compute_edc (const unsigned char *data, size_t datalen,
|
||||||
@ -457,7 +462,7 @@ parse_ccid_descriptor (ccid_driver_t handle,
|
|||||||
&& handle->max_ifsd > 48
|
&& handle->max_ifsd > 48
|
||||||
&& ( (handle->id_product == 0xe001 && handle->bcd_device < 0x0516)
|
&& ( (handle->id_product == 0xe001 && handle->bcd_device < 0x0516)
|
||||||
||(handle->id_product == 0x5111 && handle->bcd_device < 0x0620)
|
||(handle->id_product == 0x5111 && handle->bcd_device < 0x0620)
|
||||||
||(handle->id_product == 0x5115 && handle->bcd_device < 0x0519)
|
||(handle->id_product == 0x5115 && handle->bcd_device < 0x0518)
|
||||||
||(handle->id_product == 0xe003 && handle->bcd_device < 0x0504)
|
||(handle->id_product == 0xe003 && handle->bcd_device < 0x0504)
|
||||||
))
|
))
|
||||||
{
|
{
|
||||||
@ -827,7 +832,8 @@ scan_or_find_devices (int readerno, const char *readerid,
|
|||||||
|
|
||||||
/* Set the level of debugging to to usea dn return the old level. -1
|
/* Set the level of debugging to to usea dn return the old level. -1
|
||||||
just returns the old level. A level of 0 disables debugging, 1
|
just returns the old level. A level of 0 disables debugging, 1
|
||||||
enables debugging, other values are not yet defined. */
|
enables debugging, 2 enables additional tracing of the T=1
|
||||||
|
protocol, other values are not yet defined. */
|
||||||
int
|
int
|
||||||
ccid_set_debug_level (int level)
|
ccid_set_debug_level (int level)
|
||||||
{
|
{
|
||||||
@ -1437,12 +1443,13 @@ ccid_get_atr (ccid_driver_t handle,
|
|||||||
DEBUGOUT_CONT_1 (" %02X", msg[i]);
|
DEBUGOUT_CONT_1 (" %02X", msg[i]);
|
||||||
DEBUGOUT_LF ();
|
DEBUGOUT_LF ();
|
||||||
|
|
||||||
#ifdef DEBUG_T1
|
if (debug_level > 1)
|
||||||
fprintf (stderr, "T1: put %c-block seq=%d\n",
|
DEBUGOUT_3 ("T=1: put %c-block seq=%d%s\n",
|
||||||
((msg[11] & 0xc0) == 0x80)? 'R' :
|
((msg[11] & 0xc0) == 0x80)? 'R' :
|
||||||
(msg[11] & 0x80)? 'S' : 'I',
|
(msg[11] & 0x80)? 'S' : 'I',
|
||||||
((msg[11] & 0x80)? !!(msg[11]& 0x10) : !!(msg[11] & 0x40)));
|
((msg[11] & 0x80)? !!(msg[11]& 0x10)
|
||||||
#endif
|
: !!(msg[11] & 0x40)),
|
||||||
|
(!(msg[11] & 0x80) && (msg[11] & 0x20)? " [more]":""));
|
||||||
|
|
||||||
rc = bulk_out (handle, msg, msglen);
|
rc = bulk_out (handle, msg, msglen);
|
||||||
if (rc)
|
if (rc)
|
||||||
@ -1460,14 +1467,15 @@ ccid_get_atr (ccid_driver_t handle,
|
|||||||
if (tpdulen < 4)
|
if (tpdulen < 4)
|
||||||
return CCID_DRIVER_ERR_ABORTED;
|
return CCID_DRIVER_ERR_ABORTED;
|
||||||
|
|
||||||
#ifdef DEBUG_T1
|
if (debug_level > 1)
|
||||||
fprintf (stderr, "T1: got %c-block seq=%d err=%d\n",
|
DEBUGOUT_4 ("T=1: got %c-block seq=%d err=%d%s\n",
|
||||||
((msg[11] & 0xc0) == 0x80)? 'R' :
|
((msg[11] & 0xc0) == 0x80)? 'R' :
|
||||||
(msg[11] & 0x80)? 'S' : 'I',
|
(msg[11] & 0x80)? 'S' : 'I',
|
||||||
((msg[11] & 0x80)? !!(msg[11]& 0x10) : !!(msg[11] & 0x40)),
|
((msg[11] & 0x80)? !!(msg[11]& 0x10)
|
||||||
((msg[11] & 0xc0) == 0x80)? (msg[11] & 0x0f) : 0
|
: !!(msg[11] & 0x40)),
|
||||||
);
|
((msg[11] & 0xc0) == 0x80)? (msg[11] & 0x0f) : 0,
|
||||||
#endif
|
(!(msg[11] & 0x80) && (msg[11] & 0x20)? " [more]":""));
|
||||||
|
|
||||||
if ((tpdu[1] & 0xe0) != 0xe0 || tpdu[2] != 1)
|
if ((tpdu[1] & 0xe0) != 0xe0 || tpdu[2] != 1)
|
||||||
{
|
{
|
||||||
DEBUGOUT ("invalid response for S-block (Change-IFSD)\n");
|
DEBUGOUT ("invalid response for S-block (Change-IFSD)\n");
|
||||||
@ -1706,12 +1714,13 @@ ccid_transceive (ccid_driver_t handle,
|
|||||||
DEBUGOUT_CONT_1 (" %02X", msg[i]);
|
DEBUGOUT_CONT_1 (" %02X", msg[i]);
|
||||||
DEBUGOUT_LF ();
|
DEBUGOUT_LF ();
|
||||||
|
|
||||||
#ifdef DEBUG_T1
|
if (debug_level > 1)
|
||||||
fprintf (stderr, "T1: put %c-block seq=%d\n",
|
DEBUGOUT_3 ("T=1: put %c-block seq=%d%s\n",
|
||||||
((msg[11] & 0xc0) == 0x80)? 'R' :
|
((msg[11] & 0xc0) == 0x80)? 'R' :
|
||||||
(msg[11] & 0x80)? 'S' : 'I',
|
(msg[11] & 0x80)? 'S' : 'I',
|
||||||
((msg[11] & 0x80)? !!(msg[11]& 0x10) : !!(msg[11] & 0x40)));
|
((msg[11] & 0x80)? !!(msg[11]& 0x10)
|
||||||
#endif
|
: !!(msg[11] & 0x40)),
|
||||||
|
(!(msg[11] & 0x80) && (msg[11] & 0x20)? " [more]":""));
|
||||||
|
|
||||||
rc = bulk_out (handle, msg, msglen);
|
rc = bulk_out (handle, msg, msglen);
|
||||||
if (rc)
|
if (rc)
|
||||||
@ -1731,14 +1740,14 @@ ccid_transceive (ccid_driver_t handle,
|
|||||||
usb_clear_halt (handle->idev, handle->ep_bulk_in);
|
usb_clear_halt (handle->idev, handle->ep_bulk_in);
|
||||||
return CCID_DRIVER_ERR_ABORTED;
|
return CCID_DRIVER_ERR_ABORTED;
|
||||||
}
|
}
|
||||||
#ifdef DEBUG_T1
|
|
||||||
fprintf (stderr, "T1: got %c-block seq=%d err=%d\n",
|
if (debug_level > 1)
|
||||||
|
DEBUGOUT_4 ("T=1: got %c-block seq=%d err=%d%s\n",
|
||||||
((msg[11] & 0xc0) == 0x80)? 'R' :
|
((msg[11] & 0xc0) == 0x80)? 'R' :
|
||||||
(msg[11] & 0x80)? 'S' : 'I',
|
(msg[11] & 0x80)? 'S' : 'I',
|
||||||
((msg[11] & 0x80)? !!(msg[11]& 0x10) : !!(msg[11] & 0x40)),
|
((msg[11] & 0x80)? !!(msg[11]& 0x10) : !!(msg[11] & 0x40)),
|
||||||
((msg[11] & 0xc0) == 0x80)? (msg[11] & 0x0f) : 0
|
((msg[11] & 0xc0) == 0x80)? (msg[11] & 0x0f) : 0,
|
||||||
);
|
(!(msg[11] & 0x80) && (msg[11] & 0x20)? " [more]":""));
|
||||||
#endif
|
|
||||||
|
|
||||||
if (!(tpdu[1] & 0x80))
|
if (!(tpdu[1] & 0x80))
|
||||||
{ /* This is an I-block. */
|
{ /* This is an I-block. */
|
||||||
@ -1814,8 +1823,8 @@ ccid_transceive (ccid_driver_t handle,
|
|||||||
msg = send_buffer;
|
msg = send_buffer;
|
||||||
tpdulen = last_tpdulen;
|
tpdulen = last_tpdulen;
|
||||||
}
|
}
|
||||||
else if (sending && !!(tpdu[1] & 0x40) == handle->t1_ns)
|
else if (sending && !!(tpdu[1] & 0x10) == handle->t1_ns)
|
||||||
{ /* Reponse does not match our sequence number. */
|
{ /* Response does not match our sequence number. */
|
||||||
DEBUGOUT ("R-block with wrong seqno received on more bit\n");
|
DEBUGOUT ("R-block with wrong seqno received on more bit\n");
|
||||||
return CCID_DRIVER_ERR_CARD_IO_ERROR;
|
return CCID_DRIVER_ERR_CARD_IO_ERROR;
|
||||||
}
|
}
|
||||||
@ -1835,7 +1844,7 @@ ccid_transceive (ccid_driver_t handle,
|
|||||||
else
|
else
|
||||||
{ /* This is a S-block. */
|
{ /* This is a S-block. */
|
||||||
retries = 0;
|
retries = 0;
|
||||||
DEBUGOUT_2 ("T1 S-block %s received cmd=%d\n",
|
DEBUGOUT_2 ("T=1 S-block %s received cmd=%d\n",
|
||||||
(tpdu[1] & 0x20)? "response": "request",
|
(tpdu[1] & 0x20)? "response": "request",
|
||||||
(tpdu[1] & 0x1f));
|
(tpdu[1] & 0x1f));
|
||||||
if ( !(tpdu[1] & 0x20) && (tpdu[1] & 0x1f) == 3 && tpdu[2])
|
if ( !(tpdu[1] & 0x20) && (tpdu[1] & 0x1f) == 3 && tpdu[2])
|
||||||
@ -1853,7 +1862,7 @@ ccid_transceive (ccid_driver_t handle,
|
|||||||
if (use_crc)
|
if (use_crc)
|
||||||
tpdu[tpdulen++] = (edc >> 8);
|
tpdu[tpdulen++] = (edc >> 8);
|
||||||
tpdu[tpdulen++] = edc;
|
tpdu[tpdulen++] = edc;
|
||||||
DEBUGOUT_1 ("T1 waittime extension of bwi=%d\n", bwi);
|
DEBUGOUT_1 ("T=1 waittime extension of bwi=%d\n", bwi);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
return CCID_DRIVER_ERR_CARD_IO_ERROR;
|
return CCID_DRIVER_ERR_CARD_IO_ERROR;
|
||||||
@ -2008,14 +2017,13 @@ ccid_transceive_secure (ccid_driver_t handle,
|
|||||||
usb_clear_halt (handle->idev, handle->ep_bulk_in);
|
usb_clear_halt (handle->idev, handle->ep_bulk_in);
|
||||||
return CCID_DRIVER_ERR_ABORTED;
|
return CCID_DRIVER_ERR_ABORTED;
|
||||||
}
|
}
|
||||||
#ifdef DEBUG_T1
|
if (debug_level > 1)
|
||||||
fprintf (stderr, "T1: got %c-block seq=%d err=%d\n",
|
DEBUGOUT_4 ("T=1: got %c-block seq=%d err=%d%s\n",
|
||||||
((msg[11] & 0xc0) == 0x80)? 'R' :
|
((msg[11] & 0xc0) == 0x80)? 'R' :
|
||||||
(msg[11] & 0x80)? 'S' : 'I',
|
(msg[11] & 0x80)? 'S' : 'I',
|
||||||
((msg[11] & 0x80)? !!(msg[11]& 0x10) : !!(msg[11] & 0x40)),
|
((msg[11] & 0x80)? !!(msg[11]& 0x10) : !!(msg[11] & 0x40)),
|
||||||
((msg[11] & 0xc0) == 0x80)? (msg[11] & 0x0f) : 0
|
((msg[11] & 0xc0) == 0x80)? (msg[11] & 0x0f) : 0,
|
||||||
);
|
(!(msg[11] & 0x80) && (msg[11] & 0x20)? " [more]":""));
|
||||||
#endif
|
|
||||||
|
|
||||||
if (!(tpdu[1] & 0x80))
|
if (!(tpdu[1] & 0x80))
|
||||||
{ /* This is an I-block. */
|
{ /* This is an I-block. */
|
||||||
@ -2062,7 +2070,7 @@ ccid_transceive_secure (ccid_driver_t handle,
|
|||||||
DEBUGOUT ("No retries supported for Secure operation\n");
|
DEBUGOUT ("No retries supported for Secure operation\n");
|
||||||
return CCID_DRIVER_ERR_CARD_IO_ERROR;
|
return CCID_DRIVER_ERR_CARD_IO_ERROR;
|
||||||
}
|
}
|
||||||
else if (!!(tpdu[1] & 0x40) == handle->t1_ns)
|
else if (!!(tpdu[1] & 0x10) == handle->t1_ns)
|
||||||
{ /* Reponse does not match our sequence number. */
|
{ /* Reponse does not match our sequence number. */
|
||||||
DEBUGOUT ("R-block with wrong seqno received on more bit\n");
|
DEBUGOUT ("R-block with wrong seqno received on more bit\n");
|
||||||
return CCID_DRIVER_ERR_CARD_IO_ERROR;
|
return CCID_DRIVER_ERR_CARD_IO_ERROR;
|
||||||
@ -2075,7 +2083,7 @@ ccid_transceive_secure (ccid_driver_t handle,
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{ /* This is a S-block. */
|
{ /* This is a S-block. */
|
||||||
DEBUGOUT_2 ("T1 S-block %s received cmd=%d for Secure operation\n",
|
DEBUGOUT_2 ("T=1 S-block %s received cmd=%d for Secure operation\n",
|
||||||
(tpdu[1] & 0x20)? "response": "request",
|
(tpdu[1] & 0x20)? "response": "request",
|
||||||
(tpdu[1] & 0x1f));
|
(tpdu[1] & 0x1f));
|
||||||
return CCID_DRIVER_ERR_CARD_IO_ERROR;
|
return CCID_DRIVER_ERR_CARD_IO_ERROR;
|
||||||
|
@ -40,6 +40,9 @@
|
|||||||
/* Maximum length allowed as a PIN; used for INQUIRE NEEDPIN */
|
/* Maximum length allowed as a PIN; used for INQUIRE NEEDPIN */
|
||||||
#define MAXLEN_PIN 100
|
#define MAXLEN_PIN 100
|
||||||
|
|
||||||
|
/* Maximum allowed size of key data as used in inquiries. */
|
||||||
|
#define MAXLEN_KEYDATA 4096
|
||||||
|
|
||||||
|
|
||||||
#define set_error(e,t) assuan_set_error (ctx, ASSUAN_ ## e, (t))
|
#define set_error(e,t) assuan_set_error (ctx, ASSUAN_ ## e, (t))
|
||||||
|
|
||||||
@ -523,7 +526,7 @@ cmd_readcert (assuan_context_t ctx, char *line)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* READKEY <hexified_certid>
|
/* READKEY <keyid>
|
||||||
|
|
||||||
Return the public key for the given cert or key ID as an standard
|
Return the public key for the given cert or key ID as an standard
|
||||||
S-Expression.
|
S-Expression.
|
||||||
@ -913,6 +916,79 @@ cmd_setattr (assuan_context_t ctx, char *orig_line)
|
|||||||
return map_to_assuan_status (rc);
|
return map_to_assuan_status (rc);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* WRITEKEY [--force] <keyid>
|
||||||
|
|
||||||
|
This command is used to store a secret key on a a smartcard. The
|
||||||
|
allowed keyids depend on the currently selected smartcard
|
||||||
|
application. The actual keydata is requested using the inquiry
|
||||||
|
"KETDATA" and need to be provided without any protection. With
|
||||||
|
--force set an existing key under this KEYID will get overwritten.
|
||||||
|
The keydata is expected to be the usual canonical encoded
|
||||||
|
S-expression.
|
||||||
|
|
||||||
|
A PIN will be requested for most NAMEs. See the corresponding
|
||||||
|
writekey function of the actually used application (app-*.c) for
|
||||||
|
details. */
|
||||||
|
static int
|
||||||
|
cmd_writekey (assuan_context_t ctx, char *line)
|
||||||
|
{
|
||||||
|
ctrl_t ctrl = assuan_get_pointer (ctx);
|
||||||
|
int rc;
|
||||||
|
char *keyid;
|
||||||
|
int force = has_option (line, "--force");
|
||||||
|
unsigned char *keydata;
|
||||||
|
size_t keydatalen;
|
||||||
|
|
||||||
|
if ( IS_LOCKED (ctrl) )
|
||||||
|
return gpg_error (GPG_ERR_LOCKED);
|
||||||
|
|
||||||
|
/* Skip over options. */
|
||||||
|
while ( *line == '-' && line[1] == '-' )
|
||||||
|
{
|
||||||
|
while (*line && !spacep (line))
|
||||||
|
line++;
|
||||||
|
while (spacep (line))
|
||||||
|
line++;
|
||||||
|
}
|
||||||
|
if (!*line)
|
||||||
|
return set_error (Parameter_Error, "no keyid given");
|
||||||
|
keyid = line;
|
||||||
|
while (*line && !spacep (line))
|
||||||
|
line++;
|
||||||
|
*line = 0;
|
||||||
|
|
||||||
|
if ((rc = open_card (ctrl, NULL)))
|
||||||
|
return rc;
|
||||||
|
|
||||||
|
if (!ctrl->app_ctx)
|
||||||
|
return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION);
|
||||||
|
|
||||||
|
keyid = xtrystrdup (keyid);
|
||||||
|
if (!keyid)
|
||||||
|
return ASSUAN_Out_Of_Core;
|
||||||
|
|
||||||
|
/* Now get the actual keydata. */
|
||||||
|
rc = assuan_inquire (ctx, "KEYDATA", &keydata, &keydatalen, MAXLEN_KEYDATA);
|
||||||
|
if (rc)
|
||||||
|
{
|
||||||
|
xfree (keyid);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Write the key to the card. */
|
||||||
|
rc = app_writekey (ctrl->app_ctx, ctrl, keyid, force? 1:0,
|
||||||
|
pin_cb, ctx, keydata, keydatalen);
|
||||||
|
xfree (keyid);
|
||||||
|
xfree (keydata);
|
||||||
|
|
||||||
|
TEST_CARD_REMOVAL (ctrl, rc);
|
||||||
|
return map_to_assuan_status (rc);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* GENKEY [--force] <no>
|
/* GENKEY [--force] <no>
|
||||||
|
|
||||||
Generate a key on-card identified by NO, which is application
|
Generate a key on-card identified by NO, which is application
|
||||||
@ -924,7 +1000,7 @@ cmd_setattr (assuan_context_t ctx, char *orig_line)
|
|||||||
S KEY-DATA [p|n] <hexdata>
|
S KEY-DATA [p|n] <hexdata>
|
||||||
|
|
||||||
|
|
||||||
--force is required to overwriet an already existing key. The
|
--force is required to overwrite an already existing key. The
|
||||||
KEY-CREATED-AT is required for further processing because it is
|
KEY-CREATED-AT is required for further processing because it is
|
||||||
part of the hashed key material for the fingerprint.
|
part of the hashed key material for the fingerprint.
|
||||||
|
|
||||||
@ -1222,6 +1298,7 @@ register_commands (assuan_context_t ctx)
|
|||||||
{ "OUTPUT", NULL },
|
{ "OUTPUT", NULL },
|
||||||
{ "GETATTR", cmd_getattr },
|
{ "GETATTR", cmd_getattr },
|
||||||
{ "SETATTR", cmd_setattr },
|
{ "SETATTR", cmd_setattr },
|
||||||
|
{ "WRITEKEY", cmd_writekey },
|
||||||
{ "GENKEY", cmd_genkey },
|
{ "GENKEY", cmd_genkey },
|
||||||
{ "RANDOM", cmd_random },
|
{ "RANDOM", cmd_random },
|
||||||
{ "PASSWD", cmd_passwd },
|
{ "PASSWD", cmd_passwd },
|
||||||
|
@ -50,6 +50,7 @@
|
|||||||
#ifdef HAVE_W32_SYSTEM
|
#ifdef HAVE_W32_SYSTEM
|
||||||
#include "../jnlib/w32-afunix.h"
|
#include "../jnlib/w32-afunix.h"
|
||||||
#endif
|
#endif
|
||||||
|
#include "ccid-driver.h"
|
||||||
|
|
||||||
|
|
||||||
enum cmd_and_opt_values
|
enum cmd_and_opt_values
|
||||||
@ -66,7 +67,7 @@ enum cmd_and_opt_values
|
|||||||
oDebugAll,
|
oDebugAll,
|
||||||
oDebugLevel,
|
oDebugLevel,
|
||||||
oDebugWait,
|
oDebugWait,
|
||||||
oDebugSC,
|
oDebugCCIDDriver,
|
||||||
oNoGreeting,
|
oNoGreeting,
|
||||||
oNoOptions,
|
oNoOptions,
|
||||||
oHomedir,
|
oHomedir,
|
||||||
@ -85,8 +86,8 @@ enum cmd_and_opt_values
|
|||||||
oAllowAdmin,
|
oAllowAdmin,
|
||||||
oDenyAdmin,
|
oDenyAdmin,
|
||||||
oDisableApplication,
|
oDisableApplication,
|
||||||
|
oDebugDisableTicker
|
||||||
aTest };
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -97,6 +98,8 @@ static ARGPARSE_OPTS opts[] = {
|
|||||||
{ 301, NULL, 0, N_("@Options:\n ") },
|
{ 301, NULL, 0, N_("@Options:\n ") },
|
||||||
|
|
||||||
{ oServer, "server", 0, N_("run in server mode (foreground)") },
|
{ oServer, "server", 0, N_("run in server mode (foreground)") },
|
||||||
|
{ oMultiServer, "multi-server", 0,
|
||||||
|
N_("run in multi server mode (foreground)") },
|
||||||
{ oDaemon, "daemon", 0, N_("run in daemon mode (background)") },
|
{ oDaemon, "daemon", 0, N_("run in daemon mode (background)") },
|
||||||
{ oVerbose, "verbose", 0, N_("verbose") },
|
{ oVerbose, "verbose", 0, N_("verbose") },
|
||||||
{ oQuiet, "quiet", 0, N_("be somewhat more quiet") },
|
{ oQuiet, "quiet", 0, N_("be somewhat more quiet") },
|
||||||
@ -107,10 +110,10 @@ static ARGPARSE_OPTS opts[] = {
|
|||||||
{ oDebugAll, "debug-all" ,0, "@"},
|
{ oDebugAll, "debug-all" ,0, "@"},
|
||||||
{ oDebugLevel, "debug-level" ,2, "@"},
|
{ oDebugLevel, "debug-level" ,2, "@"},
|
||||||
{ oDebugWait,"debug-wait",1, "@"},
|
{ oDebugWait,"debug-wait",1, "@"},
|
||||||
|
{ oDebugCCIDDriver, "debug-ccid-driver", 0, "@"},
|
||||||
|
{ oDebugDisableTicker, "debug-disable-ticker", 0, "@"},
|
||||||
{ oNoDetach, "no-detach" ,0, N_("do not detach from the console")},
|
{ oNoDetach, "no-detach" ,0, N_("do not detach from the console")},
|
||||||
{ oLogFile, "log-file" ,2, N_("use a log file for the server")},
|
{ oLogFile, "log-file" ,2, N_("use a log file for the server")},
|
||||||
{ oMultiServer, "multi-server", 0,
|
|
||||||
N_("allow additional connections in server mode")},
|
|
||||||
{ oReaderPort, "reader-port", 2, N_("|N|connect to reader at port N")},
|
{ oReaderPort, "reader-port", 2, N_("|N|connect to reader at port N")},
|
||||||
{ octapiDriver, "ctapi-driver", 2, N_("|NAME|use NAME as ct-API driver")},
|
{ octapiDriver, "ctapi-driver", 2, N_("|NAME|use NAME as ct-API driver")},
|
||||||
{ opcscDriver, "pcsc-driver", 2, N_("|NAME|use NAME as PC/SC driver")},
|
{ opcscDriver, "pcsc-driver", 2, N_("|NAME|use NAME as PC/SC driver")},
|
||||||
@ -125,10 +128,6 @@ static ARGPARSE_OPTS opts[] = {
|
|||||||
{ oDenyAdmin, "deny-admin", 0, "@" },
|
{ oDenyAdmin, "deny-admin", 0, "@" },
|
||||||
{ oDisableApplication, "disable-application", 2, "@"},
|
{ oDisableApplication, "disable-application", 2, "@"},
|
||||||
|
|
||||||
/* Dummy options to be removed at some point. */
|
|
||||||
{ oDebugSC, "debug-sc", 1, "@" },
|
|
||||||
{ oDisableOpenSC, "disable-opensc", 0, "@" },
|
|
||||||
|
|
||||||
{0}
|
{0}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -150,6 +149,12 @@ static int maybe_setuid = 1;
|
|||||||
/* Name of the communication socket */
|
/* Name of the communication socket */
|
||||||
static char *socket_name;
|
static char *socket_name;
|
||||||
|
|
||||||
|
|
||||||
|
/* Debug flag to disable the ticker. The ticker is in fact not
|
||||||
|
disabled but it won't perform any ticker specific actions. */
|
||||||
|
static int ticker_disabled;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
static char *create_socket_name (int use_standard_socket,
|
static char *create_socket_name (int use_standard_socket,
|
||||||
char *standard_name, char *template);
|
char *standard_name, char *template);
|
||||||
@ -443,7 +448,10 @@ main (int argc, char **argv )
|
|||||||
case oDebugAll: opt.debug = ~0; break;
|
case oDebugAll: opt.debug = ~0; break;
|
||||||
case oDebugLevel: debug_level = pargs.r.ret_str; break;
|
case oDebugLevel: debug_level = pargs.r.ret_str; break;
|
||||||
case oDebugWait: debug_wait = pargs.r.ret_int; break;
|
case oDebugWait: debug_wait = pargs.r.ret_int; break;
|
||||||
case oDebugSC: break;
|
case oDebugCCIDDriver:
|
||||||
|
ccid_set_debug_level (ccid_set_debug_level (-1)+1);
|
||||||
|
break;
|
||||||
|
case oDebugDisableTicker: ticker_disabled = 1; break;
|
||||||
|
|
||||||
case oOptions:
|
case oOptions:
|
||||||
/* config files may not be nested (silently ignore them) */
|
/* config files may not be nested (silently ignore them) */
|
||||||
@ -463,7 +471,7 @@ main (int argc, char **argv )
|
|||||||
case oCsh: csh_style = 1; break;
|
case oCsh: csh_style = 1; break;
|
||||||
case oSh: csh_style = 0; break;
|
case oSh: csh_style = 0; break;
|
||||||
case oServer: pipe_server = 1; break;
|
case oServer: pipe_server = 1; break;
|
||||||
case oMultiServer: multi_server = 1; break;
|
case oMultiServer: pipe_server = 1; multi_server = 1; break;
|
||||||
case oDaemon: is_daemon = 1; break;
|
case oDaemon: is_daemon = 1; break;
|
||||||
|
|
||||||
case oReaderPort: opt.reader_port = pargs.r.ret_str; break;
|
case oReaderPort: opt.reader_port = pargs.r.ret_str; break;
|
||||||
@ -839,6 +847,7 @@ handle_signal (int signo)
|
|||||||
static void
|
static void
|
||||||
handle_tick (void)
|
handle_tick (void)
|
||||||
{
|
{
|
||||||
|
if (!ticker_disabled)
|
||||||
scd_update_reader_status_file ();
|
scd_update_reader_status_file ();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
73
scd/tlv.c
73
scd/tlv.c
@ -221,3 +221,76 @@ parse_ber_header (unsigned char const **buffer, size_t *size,
|
|||||||
*size = length;
|
*size = length;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* FIXME: The following function should not go into this file but for
|
||||||
|
now it is easier to keep it here. */
|
||||||
|
|
||||||
|
/* Return the next token of an canconical encoded S-expression. BUF
|
||||||
|
is the pointer to the S-expression and BUFLEN is a pointer to the
|
||||||
|
length of this S-expression (used to validate the syntax). Both
|
||||||
|
are updated to reflect the new position. The token itself is
|
||||||
|
returned as a pointer into the orginal buffer at TOK and TOKLEN.
|
||||||
|
If a parentheses is the next token, TOK will be set to NULL.
|
||||||
|
TOKLEN is checked to be within the bounds. On error a error code
|
||||||
|
is returned and all pointers should are not guaranteed to point to
|
||||||
|
a meanigful value. DEPTH should be initialized to 0 and will
|
||||||
|
reflect on return the actual depth of the tree. To detect the end
|
||||||
|
of the S-expression it is advisable to check DEPTH after a
|
||||||
|
successful return:
|
||||||
|
|
||||||
|
depth = 0;
|
||||||
|
while (!(err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen))
|
||||||
|
&& depth)
|
||||||
|
process_token (tok, toklen);
|
||||||
|
if (err)
|
||||||
|
handle_error ();
|
||||||
|
*/
|
||||||
|
gpg_error_t
|
||||||
|
parse_sexp (unsigned char const **buf, size_t *buflen,
|
||||||
|
int *depth, unsigned char const **tok, size_t *toklen)
|
||||||
|
{
|
||||||
|
const unsigned char *s;
|
||||||
|
size_t n, vlen;
|
||||||
|
|
||||||
|
s = *buf;
|
||||||
|
n = *buflen;
|
||||||
|
*tok = NULL;
|
||||||
|
*toklen = 0;
|
||||||
|
if (!n)
|
||||||
|
return *depth ? gpg_error (GPG_ERR_INV_SEXP) : 0;
|
||||||
|
if (*s == '(')
|
||||||
|
{
|
||||||
|
s++; n--;
|
||||||
|
(*depth)++;
|
||||||
|
*buf = s;
|
||||||
|
*buflen = n;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (*s == ')')
|
||||||
|
{
|
||||||
|
if (!*depth)
|
||||||
|
return gpg_error (GPG_ERR_INV_SEXP);
|
||||||
|
*toklen = 1;
|
||||||
|
s++; n--;
|
||||||
|
(*depth)--;
|
||||||
|
*buf = s;
|
||||||
|
*buflen = n;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
for (vlen=0; n && *s && *s != ':' && (*s >= '0' && *s <= '9'); s++, n--)
|
||||||
|
vlen = vlen*10 + (*s - '0');
|
||||||
|
if (!n || *s != ':')
|
||||||
|
return gpg_error (GPG_ERR_INV_SEXP);
|
||||||
|
s++; n--;
|
||||||
|
if (vlen > n)
|
||||||
|
return gpg_error (GPG_ERR_INV_SEXP);
|
||||||
|
*tok = s;
|
||||||
|
*toklen = vlen;
|
||||||
|
s += vlen;
|
||||||
|
n -= vlen;
|
||||||
|
*buf = s;
|
||||||
|
*buflen = n;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
17
scd/tlv.h
17
scd/tlv.h
@ -88,4 +88,21 @@ gpg_error_t parse_ber_header (unsigned char const **buffer, size_t *size,
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* Return the next token of an canconical encoded S-expression. BUF
|
||||||
|
is the pointer to the S-expression and BUFLEN is a pointer to the
|
||||||
|
length of this S-expression (used to validate the syntax). Both
|
||||||
|
are updated to reflect the new position. The token itself is
|
||||||
|
returned as a pointer into the orginal buffer at TOK and TOKLEN.
|
||||||
|
If a parentheses is the next token, TOK will be set to NULL.
|
||||||
|
TOKLEN is checked to be within the bounds. On error a error code
|
||||||
|
is returned and all pointers should are not guaranteed to point to
|
||||||
|
a meanigful value. DEPTH should be initialized to 0 and will
|
||||||
|
reflect on return the actual depth of the tree. To detect the end
|
||||||
|
of the S-expression it is advisable to check DEPTH after a
|
||||||
|
successful return. */
|
||||||
|
gpg_error_t parse_sexp (unsigned char const **buf, size_t *buflen,
|
||||||
|
int *depth, unsigned char const **tok, size_t *toklen);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#endif /* SCD_TLV_H */
|
#endif /* SCD_TLV_H */
|
||||||
|
@ -1,3 +1,10 @@
|
|||||||
|
2005-05-20 Werner Koch <wk@g10code.com>
|
||||||
|
|
||||||
|
* gpg-connect-agent.c (add_definq, show_definq, clear_definq)
|
||||||
|
(handle_inquire): New.
|
||||||
|
(read_and_print_response): Handle INQUIRE command.
|
||||||
|
(main): Implement control commands.
|
||||||
|
|
||||||
2005-04-21 Werner Koch <wk@g10code.com>
|
2005-04-21 Werner Koch <wk@g10code.com>
|
||||||
|
|
||||||
* symcryptrun.c (main): Optionally allow the input file as command
|
* symcryptrun.c (main): Optionally allow the input file as command
|
||||||
@ -368,7 +375,7 @@
|
|||||||
* watchgnupg.c: New.
|
* watchgnupg.c: New.
|
||||||
|
|
||||||
|
|
||||||
Copyright 2003, 2004 Free Software Foundation, Inc.
|
Copyright 2003, 2004, 2005 Free Software Foundation, Inc.
|
||||||
|
|
||||||
This file is free software; as a special exception the author gives
|
This file is free software; as a special exception the author gives
|
||||||
unlimited permission to copy and/or distribute it, with or without
|
unlimited permission to copy and/or distribute it, with or without
|
||||||
|
@ -76,6 +76,23 @@ struct
|
|||||||
} opt;
|
} opt;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* Definitions for /definq commands and a global linked list with all
|
||||||
|
the definitions. */
|
||||||
|
struct definq_s
|
||||||
|
{
|
||||||
|
struct definq_s *next;
|
||||||
|
char *name; /* Name of inquiry or NULL for any name. */
|
||||||
|
int is_prog; /* True if this is a program to run. */
|
||||||
|
char file[1]; /* Name of file or program. */
|
||||||
|
};
|
||||||
|
typedef struct definq_s *definq_t;
|
||||||
|
|
||||||
|
static definq_t definq_list;
|
||||||
|
static definq_t *definq_list_tail = &definq_list;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*-- local prototypes --*/
|
/*-- local prototypes --*/
|
||||||
static int read_and_print_response (assuan_context_t ctx);
|
static int read_and_print_response (assuan_context_t ctx);
|
||||||
static assuan_context_t start_agent (void);
|
static assuan_context_t start_agent (void);
|
||||||
@ -129,6 +146,68 @@ i18n_init(void)
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Store an inquire response pattern. Note, that this function may
|
||||||
|
change the content of LINE. We assume that leading white spaces
|
||||||
|
are already removed. */
|
||||||
|
static void
|
||||||
|
add_definq (char *line, int is_prog)
|
||||||
|
{
|
||||||
|
definq_t d;
|
||||||
|
char *name, *p;
|
||||||
|
|
||||||
|
/* Get name. */
|
||||||
|
name = line;
|
||||||
|
for (p=name; *p && !spacep (p); p++)
|
||||||
|
;
|
||||||
|
if (*p)
|
||||||
|
*p++ = 0;
|
||||||
|
while (spacep (p))
|
||||||
|
p++;
|
||||||
|
|
||||||
|
d = xmalloc (sizeof *d + strlen (p) );
|
||||||
|
strcpy (d->file, p);
|
||||||
|
d->is_prog = is_prog;
|
||||||
|
if ( !strcmp (name, "*"))
|
||||||
|
d->name = NULL;
|
||||||
|
else
|
||||||
|
d->name = xstrdup (name);
|
||||||
|
|
||||||
|
d->next = NULL;
|
||||||
|
*definq_list_tail = d;
|
||||||
|
definq_list_tail = &d->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Show all inquiry defintions. */
|
||||||
|
static void
|
||||||
|
show_definq (void)
|
||||||
|
{
|
||||||
|
definq_t d;
|
||||||
|
|
||||||
|
for (d=definq_list; d; d = d->next)
|
||||||
|
if (d->name)
|
||||||
|
printf ("%-20s %c %s\n", d->name, d->is_prog? 'p':'f', d->file);
|
||||||
|
for (d=definq_list; d; d = d->next)
|
||||||
|
if (!d->name)
|
||||||
|
printf ("%-20s %c %s\n", "*", d->is_prog? 'p':'f', d->file);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Clear all inquiry definitions. */
|
||||||
|
static void
|
||||||
|
clear_definq (void)
|
||||||
|
{
|
||||||
|
while (definq_list)
|
||||||
|
{
|
||||||
|
definq_t tmp = definq_list->next;
|
||||||
|
xfree (definq_list->name);
|
||||||
|
xfree (definq_list);
|
||||||
|
definq_list = tmp;
|
||||||
|
}
|
||||||
|
definq_list_tail = &definq_list;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* gpg-connect-agent's entry point. */
|
/* gpg-connect-agent's entry point. */
|
||||||
int
|
int
|
||||||
@ -138,7 +217,7 @@ main (int argc, char **argv)
|
|||||||
const char *fname;
|
const char *fname;
|
||||||
int no_more_options = 0;
|
int no_more_options = 0;
|
||||||
assuan_context_t ctx;
|
assuan_context_t ctx;
|
||||||
char *line;
|
char *line, *p;
|
||||||
size_t linesize;
|
size_t linesize;
|
||||||
int rc;
|
int rc;
|
||||||
|
|
||||||
@ -213,6 +292,57 @@ main (int argc, char **argv)
|
|||||||
log_info (_("line shortened due to embedded Nul character\n"));
|
log_info (_("line shortened due to embedded Nul character\n"));
|
||||||
if (line[n-1] == '\n')
|
if (line[n-1] == '\n')
|
||||||
line[n-1] = 0;
|
line[n-1] = 0;
|
||||||
|
if (*line == '/')
|
||||||
|
{
|
||||||
|
/* Handle control commands. */
|
||||||
|
char *cmd = line+1;
|
||||||
|
|
||||||
|
for (p=cmd; *p && !spacep (p); p++)
|
||||||
|
;
|
||||||
|
if (*p)
|
||||||
|
*p++ = 0;
|
||||||
|
while (spacep (p))
|
||||||
|
p++;
|
||||||
|
if (!strcmp (cmd, "definqfile"))
|
||||||
|
{
|
||||||
|
add_definq (p, 0);
|
||||||
|
}
|
||||||
|
else if (!strcmp (cmd, "definqprog"))
|
||||||
|
{
|
||||||
|
add_definq (p, 1);
|
||||||
|
}
|
||||||
|
else if (!strcmp (cmd, "showdef"))
|
||||||
|
{
|
||||||
|
show_definq ();
|
||||||
|
}
|
||||||
|
else if (!strcmp (cmd, "cleardef"))
|
||||||
|
{
|
||||||
|
clear_definq ();
|
||||||
|
}
|
||||||
|
else if (!strcmp (cmd, "echo"))
|
||||||
|
{
|
||||||
|
puts (p);
|
||||||
|
}
|
||||||
|
else if (!strcmp (cmd, "help"))
|
||||||
|
{
|
||||||
|
puts ("Available commands:\n"
|
||||||
|
"/echo ARGS Echo ARGS.\n"
|
||||||
|
"/definqfile NAME FILE\n"
|
||||||
|
" Use content of FILE for inquiries with NAME.\n"
|
||||||
|
" NAME may be \"*\" to match any inquiry.\n"
|
||||||
|
"/definqprog NAME PGM\n"
|
||||||
|
" Run PGM for inquiries matching NAME and pass the\n"
|
||||||
|
" entire line to it as arguments.\n"
|
||||||
|
"/showdef Print all definitions.\n"
|
||||||
|
"/cleardef Delete all definitions.\n"
|
||||||
|
"/help Print this help.");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
log_error (_("unknown command `%s'\n"), cmd );
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
rc = assuan_write_line (ctx, line);
|
rc = assuan_write_line (ctx, line);
|
||||||
if (rc)
|
if (rc)
|
||||||
{
|
{
|
||||||
@ -234,6 +364,94 @@ main (int argc, char **argv)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Handle an Inquire from the server. Return False if it could not be
|
||||||
|
handled; in this case the caller shll complete the operation. LINE
|
||||||
|
is the complete line as received from the server. This function
|
||||||
|
may change the content of LINE. */
|
||||||
|
static int
|
||||||
|
handle_inquire (assuan_context_t ctx, char *line)
|
||||||
|
{
|
||||||
|
const char *name;
|
||||||
|
definq_t d;
|
||||||
|
FILE *fp;
|
||||||
|
char buffer[1024];
|
||||||
|
int rc, n;
|
||||||
|
|
||||||
|
/* Skip the command and trailing spaces. */
|
||||||
|
for (; *line && !spacep (line); line++)
|
||||||
|
;
|
||||||
|
while (spacep (line))
|
||||||
|
line++;
|
||||||
|
/* Get the name. */
|
||||||
|
name = line;
|
||||||
|
for (; *line && !spacep (line); line++)
|
||||||
|
;
|
||||||
|
if (*line)
|
||||||
|
*line++ = 0;
|
||||||
|
|
||||||
|
/* Now match it against our list. he second loop is todetect the
|
||||||
|
match all entry. **/
|
||||||
|
for (d=definq_list; d; d = d->next)
|
||||||
|
if (d->name && !strcmp (d->name, name))
|
||||||
|
break;
|
||||||
|
if (!d)
|
||||||
|
for (d=definq_list; d; d = d->next)
|
||||||
|
if (!d->name)
|
||||||
|
break;
|
||||||
|
if (!d)
|
||||||
|
{
|
||||||
|
if (opt.verbose)
|
||||||
|
log_info ("no handler for inquiry `%s' found\n", name);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (d->is_prog)
|
||||||
|
{
|
||||||
|
fp = popen (d->file, "r");
|
||||||
|
if (!fp)
|
||||||
|
log_error ("error executing `%s': %s\n", d->file, strerror (errno));
|
||||||
|
else if (opt.verbose)
|
||||||
|
log_error ("handling inquiry `%s' by running `%s'\n", name, d->file);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
fp = fopen (d->file, "rb");
|
||||||
|
if (!fp)
|
||||||
|
log_error ("error opening `%s': %s\n", d->file, strerror (errno));
|
||||||
|
else if (opt.verbose)
|
||||||
|
log_error ("handling inquiry `%s' by returning content of `%s'\n",
|
||||||
|
name, d->file);
|
||||||
|
}
|
||||||
|
if (!fp)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
while ( (n = fread (buffer, 1, sizeof buffer, fp)) )
|
||||||
|
{
|
||||||
|
rc = assuan_send_data (ctx, buffer, n);
|
||||||
|
if (rc)
|
||||||
|
{
|
||||||
|
log_error ("sending data back failed: %s\n", assuan_strerror (rc) );
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (ferror (fp))
|
||||||
|
log_error ("error reading from `%s': %s\n", d->file, strerror (errno));
|
||||||
|
|
||||||
|
rc = assuan_send_data (ctx, NULL, 0);
|
||||||
|
if (rc)
|
||||||
|
log_error ("sending data back failed: %s\n", assuan_strerror (rc) );
|
||||||
|
|
||||||
|
if (d->is_prog)
|
||||||
|
{
|
||||||
|
if (pclose (fp))
|
||||||
|
log_error ("error running `%s': %s\n", d->file, strerror (errno));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
fclose (fp);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Read all response lines from server and print them. Returns 0 on
|
/* Read all response lines from server and print them. Returns 0 on
|
||||||
success or an assuan error code. */
|
success or an assuan error code. */
|
||||||
static int
|
static int
|
||||||
@ -325,7 +543,8 @@ read_and_print_response (assuan_context_t ctx)
|
|||||||
{
|
{
|
||||||
fwrite (line, linelen, 1, stdout);
|
fwrite (line, linelen, 1, stdout);
|
||||||
putchar ('\n');
|
putchar ('\n');
|
||||||
return 0;
|
if (!handle_inquire (ctx, line))
|
||||||
|
assuan_write_line (ctx, "CANCEL");
|
||||||
}
|
}
|
||||||
else if (linelen >= 3
|
else if (linelen >= 3
|
||||||
&& line[0] == 'E' && line[1] == 'N' && line[2] == 'D'
|
&& line[0] == 'E' && line[1] == 'N' && line[2] == 'D'
|
||||||
|
Loading…
x
Reference in New Issue
Block a user