New PIN Callback attributes in gpg-agent.

Common prompts for keypad and simple card reader.
More support for Netkey cards;  PIN management works now.
This commit is contained in:
Werner Koch 2009-03-05 19:19:37 +00:00
parent 2749c6bcd9
commit 59d7a54e72
13 changed files with 687 additions and 163 deletions

4
NEWS
View File

@ -1,3 +1,7 @@
Noteworthy changes in version 2.0.12
-------------------------------------------------
Noteworthy changes in version 2.0.11 (2009-03-03) Noteworthy changes in version 2.0.11 (2009-03-03)
------------------------------------------------- -------------------------------------------------

View File

@ -1,3 +1,9 @@
2009-03-05 Werner Koch <wk@g10code.com>
* divert-scd.c (getpin_cb): Support flag 'P'. Change max_digits
from 8 to 16. Append a message about keypads.
* findkey.c (unprotect): Change max digits to 16.
2009-03-02 Werner Koch <wk@g10code.com> 2009-03-02 Werner Koch <wk@g10code.com>
* command.c (cmd_getinfo): Add subcommand "scd_running". * command.c (cmd_getinfo): Add subcommand "scd_running".

View File

@ -1061,7 +1061,7 @@ cmd_learn (assuan_context_t ctx, char *line)
/* PASSWD <hexstring_with_keygrip> /* PASSWD <hexstring_with_keygrip>
Change the passphrase/PID for the key identified by keygrip in LINE. */ Change the passphrase/PIN for the key identified by keygrip in LINE. */
static int static int
cmd_passwd (assuan_context_t ctx, char *line) cmd_passwd (assuan_context_t ctx, char *line)
{ {

View File

@ -1,5 +1,5 @@
/* divert-scd.c - divert operations to the scdaemon /* divert-scd.c - divert operations to the scdaemon
* Copyright (C) 2002, 2003 Free Software Foundation, Inc. * Copyright (C) 2002, 2003, 2009 Free Software Foundation, Inc.
* *
* This file is part of GnuPG. * This file is part of GnuPG.
* *
@ -181,10 +181,11 @@ encode_md_for_card (const unsigned char *digest, size_t digestlen, int algo,
Flags: Flags:
'N' = New PIN, this requests a second prompt to repeat the the 'N' = New PIN, this requests a second prompt to repeat the
PIN. If the PIN is not correctly repeated it starts from PIN. If the PIN is not correctly repeated it starts from
all over. all over.
'A' = The PIN is an Admin PIN, SO-PIN, PUK or alike. 'A' = The PIN is an Admin PIN, SO-PIN or alike.
'P' = The PIN is a PUK (Personal Unblocking Key).
'R' = The PIN is a Reset Code. 'R' = The PIN is a Reset Code.
Example: Example:
@ -204,6 +205,7 @@ getpin_cb (void *opaque, const char *info, char *buf, size_t maxbuf)
int any_flags = 0; int any_flags = 0;
int newpin = 0; int newpin = 0;
int resetcode = 0; int resetcode = 0;
int is_puk = 0;
const char *again_text = NULL; const char *again_text = NULL;
const char *prompt = "PIN"; const char *prompt = "PIN";
@ -217,6 +219,13 @@ getpin_cb (void *opaque, const char *info, char *buf, size_t maxbuf)
{ {
if (*s == 'A') if (*s == 'A')
prompt = _("Admin PIN"); prompt = _("Admin PIN");
else if (*s == 'P')
{
/* TRANSLATORS: A PUK is the Personal Unblocking Code
used to unblock a PIN. */
prompt = _("PUK");
is_puk = 1;
}
else if (*s == 'N') else if (*s == 'N')
newpin = 1; newpin = 1;
else if (*s == 'R') else if (*s == 'R')
@ -242,7 +251,22 @@ getpin_cb (void *opaque, const char *info, char *buf, size_t maxbuf)
} }
else if (maxbuf == 1) /* Open the pinentry. */ else if (maxbuf == 1) /* Open the pinentry. */
{ {
rc = agent_popup_message_start (ctrl, info, NULL); if (info)
{
char *desc;
if ( asprintf (&desc,
_("%s%%0A%%0AUse the reader's keypad for input."),
info) < 0 )
rc = gpg_error_from_syserror ();
else
{
rc = agent_popup_message_start (ctrl, desc, NULL);
xfree (desc);
}
}
else
rc = agent_popup_message_start (ctrl, NULL, NULL);
} }
else else
rc = gpg_error (GPG_ERR_INV_VALUE); rc = gpg_error (GPG_ERR_INV_VALUE);
@ -258,7 +282,7 @@ getpin_cb (void *opaque, const char *info, char *buf, size_t maxbuf)
return gpg_error_from_syserror (); return gpg_error_from_syserror ();
pi->max_length = maxbuf-1; pi->max_length = maxbuf-1;
pi->min_digits = 0; /* we want a real passphrase */ pi->min_digits = 0; /* we want a real passphrase */
pi->max_digits = 8; pi->max_digits = 16;
pi->max_tries = 3; pi->max_tries = 3;
if (any_flags) if (any_flags)
@ -277,17 +301,21 @@ getpin_cb (void *opaque, const char *info, char *buf, size_t maxbuf)
} }
pi2->max_length = maxbuf-1; pi2->max_length = maxbuf-1;
pi2->min_digits = 0; pi2->min_digits = 0;
pi2->max_digits = 8; pi2->max_digits = 16;
pi2->max_tries = 1; pi2->max_tries = 1;
rc = agent_askpin (ctrl, rc = agent_askpin (ctrl,
(resetcode? (resetcode?
_("Repeat this Reset Code"): _("Repeat this Reset Code"):
is_puk?
_("Repeat this PUK"):
_("Repeat this PIN")), _("Repeat this PIN")),
prompt, NULL, pi2); prompt, NULL, pi2);
if (!rc && strcmp (pi->pin, pi2->pin)) if (!rc && strcmp (pi->pin, pi2->pin))
{ {
again_text = (resetcode? again_text = (resetcode?
N_("Reset Code not correctly repeated; try again"): N_("Reset Code not correctly repeated; try again"):
is_puk?
N_("PUK not correctly repeated; try again"):
N_("PIN not correctly repeated; try again")); N_("PIN not correctly repeated; try again"));
xfree (pi2); xfree (pi2);
xfree (pi); xfree (pi);

View File

@ -367,7 +367,7 @@ unprotect (ctrl_t ctrl, const char *desc_text,
return gpg_error_from_syserror (); return gpg_error_from_syserror ();
pi->max_length = 100; pi->max_length = 100;
pi->min_digits = 0; /* we want a real passphrase */ pi->min_digits = 0; /* we want a real passphrase */
pi->max_digits = 8; pi->max_digits = 16;
pi->max_tries = 3; pi->max_tries = 3;
pi->check_cb = try_unprotect_cb; pi->check_cb = try_unprotect_cb;
arg.ctrl = ctrl; arg.ctrl = ctrl;

View File

@ -24,8 +24,8 @@ min_automake_version="1.10"
# Remember to change the version number immediately *after* a release. # Remember to change the version number immediately *after* a release.
# Set my_issvn to "yes" for non-released code. Remember to run an # Set my_issvn to "yes" for non-released code. Remember to run an
# "svn up" and "autogen.sh" right before creating a distribution. # "svn up" and "autogen.sh" right before creating a distribution.
m4_define([my_version], [2.0.11]) m4_define([my_version], [2.0.12])
m4_define([my_issvn], [no]) m4_define([my_issvn], [yes])
m4_define([svn_revision], m4_esyscmd([printf "%d" $(svn info 2>/dev/null \ m4_define([svn_revision], m4_esyscmd([printf "%d" $(svn info 2>/dev/null \
| sed -n '/^Revision:/ s/[^0-9]//gp'|head -1)])) | sed -n '/^Revision:/ s/[^0-9]//gp'|head -1)]))

View File

@ -1,3 +1,12 @@
2009-03-04 Werner Koch <wk@g10code.com>
* help.txt (gpg.keygen.size): Add a link to web page.
2009-03-03 Werner Koch <wk@g10code.com>
* gpg.texi (Operational GPG Commands): "merge-only" is an
import-option. Reported by Joseph Oreste Bruni.
2009-03-02 Werner Koch <wk@g10code.com> 2009-03-02 Werner Koch <wk@g10code.com>
* gpg-agent.texi (Invoking GPG-AGENT): Modernized instructions. * gpg-agent.texi (Invoking GPG-AGENT): Modernized instructions.

View File

@ -425,7 +425,7 @@ Import/merge keys. This adds the given keys to the
keyring. The fast version is currently just a synonym. keyring. The fast version is currently just a synonym.
There are a few other options which control how this command works. There are a few other options which control how this command works.
Most notable here is the @option{--keyserver-options merge-only} option Most notable here is the @option{--import-options merge-only} option
which does not insert new keys but does only the merging of new which does not insert new keys but does only the merging of new
signatures, user-IDs and subkeys. signatures, user-IDs and subkeys.

View File

@ -135,7 +135,13 @@ Please consult your security expert first.
.gpg.keygen.size .gpg.keygen.size
Enter the size of the key. Enter the size of the key.
The suggested default is usually a good choice.
If you want to use a large key size, for example 4096 bit, please
think again whether it really makes sense for you. You may want
to view the web page http://www.xkcd.com/538/ .
. .
.gpg.keygen.size.huge.okay .gpg.keygen.size.huge.okay

View File

@ -1,3 +1,30 @@
2009-03-05 Werner Koch <wk@g10code.com>
* app-openpgp.c (verify_a_chv): Remove special case for keypads.
(verify_chv3): Ditto.
* app-nks.c (get_chv_status): New.
(parse_pwidstr): New.
(verify_pin): Add args PWID and DESC and use them. Remove the
CHV1 caching.
(do_change_pin): Allow PIN selection and add reset mode.
(do_learn_status): Use NKS-NKS3 tag for TCOS 3 cards.
(do_readcert, do_sign): Allow NKS-NKS3 tag.
2009-03-04 Werner Koch <wk@g10code.com>
* app-nks.c (do_getattr): New.
(app_select_nks): Register it.
(verify_pin): Factor some code out to...
(basic_pin_checks): New.
(do_change_pin): Call the basic check.
(app_select_nks): Move AID to ..
(aid_nks): .. new.
(aid_sigg): New.
(switch_application): New.
(do_getattr, do_learn_status, do_readcert, do_sign, do_decipher)
(do_change_pin, do_check_pin): Make sure we are in NKS mode.
2009-03-03 Werner Koch <wk@g10code.com> 2009-03-03 Werner Koch <wk@g10code.com>
* command.c (scd_command_handler): Remove dereference of STOPME * command.c (scd_command_handler): Remove dereference of STOPME

View File

@ -17,6 +17,27 @@
* along with this program; if not, see <http://www.gnu.org/licenses/>. * along with this program; if not, see <http://www.gnu.org/licenses/>.
*/ */
/* Notes:
- This is still work in progress. We are now targeting TCOS 3 cards
but try to keep compatibility to TCOS 2. Both are not fully
working as of now. TCOS 3 PIN management seems to work. Use GPA
from SVN trunk to test it.
- If required, we automagically switch between the NKS application
and the SigG application. This avoids to use the DINSIG
application which is somewhat limited, has no support for Secure
Messaging as required by TCOS 3 and has no way to change the PIN
or even set the NullPIN.
- We use the prefix NKS-DF01 for TCOS 2 cards and NKS-NKS3 for newer
cards. This is because the NKS application has moved to DF02 with
TCOS 3 and thus we better use a DF independent tag.
- We use only the global PINs for the NKS application.
*/
#include <config.h> #include <config.h>
#include <errno.h> #include <errno.h>
#include <stdio.h> #include <stdio.h>
@ -30,9 +51,15 @@
#include "iso7816.h" #include "iso7816.h"
#include "app-common.h" #include "app-common.h"
#include "tlv.h" #include "tlv.h"
#include "apdu.h"
static char const aid_nks[] = { 0xD2, 0x76, 0x00, 0x00, 0x03, 0x01, 0x02 };
static char const aid_sigg[] = { 0xD2, 0x76, 0x00, 0x00, 0x66, 0x01 };
static struct static struct
{ {
int is_sigg; /* Valid for SigG application. */
int fid; /* File ID. */ int fid; /* File ID. */
int nks_ver; /* 0 for NKS version 2, 3 for version 3. */ int nks_ver; /* 0 for NKS version 2, 3 for version 3. */
int certtype; /* Type of certificate or 0 if it is not a certificate. */ int certtype; /* Type of certificate or 0 if it is not a certificate. */
@ -40,20 +67,23 @@ static struct
int issignkey; /* True if file is a key usable for signing. */ int issignkey; /* True if file is a key usable for signing. */
int isenckey; /* True if file is a key usable for decryption. */ int isenckey; /* True if file is a key usable for decryption. */
} filelist[] = { } filelist[] = {
{ 0x4531, 0, 0, 0xC000, 1, 0 }, /* EF_PK.NKS.SIG */ { 0, 0x4531, 0, 0, 0xC000, 1, 0 }, /* EF_PK.NKS.SIG */
{ 0xC000, 0, 101 }, /* EF_C.NKS.SIG */ { 1, 0x4531, 3, 0, 0x0000, 1, 1 }, /* EF_PK.CH.SIG */
{ 0x4331, 0, 100 }, { 0, 0xC000, 0, 101 }, /* EF_C.NKS.SIG */
{ 0x4332, 0, 100 }, { 1, 0xC000, 0, 101 }, /* EF_C.CH.SIG */
{ 0xB000, 0, 110 }, /* EF_PK.RCA.NKS */ { 0, 0x4331, 0, 100 },
{ 0x45B1, 0, 0, 0xC200, 0, 1 }, /* EF_PK.NKS.ENC */ { 0, 0x4332, 0, 100 },
{ 0xC200, 0, 101 }, /* EF_C.NKS.ENC */ { 0, 0xB000, 0, 110 }, /* EF_PK.RCA.NKS */
{ 0x43B1, 0, 100 }, { 0, 0x45B1, 0, 0, 0xC200, 0, 1 }, /* EF_PK.NKS.ENC */
{ 0x43B2, 0, 100 }, { 0, 0xC200, 0, 101 }, /* EF_C.NKS.ENC */
{ 0x4571, 3, 0, 0xc500, 0, 0 }, /* EF_PK.NKS.AUT */ { 0, 0x43B1, 0, 100 },
{ 0xC500, 3, 101 }, /* EF_C.NKS.AUT */ { 0, 0x43B2, 0, 100 },
{ 0x45B2, 3, 0, 0xC201, 0, 1 }, /* EF_PK.NKS.ENC1024 */ { 0, 0x4571, 3, 0, 0xc500, 0, 0 }, /* EF_PK.NKS.AUT */
{ 0xC201, 3, 101 }, /* EF_C.NKS.ENC1024 */ { 0, 0xC500, 3, 101 }, /* EF_C.NKS.AUT */
{ 0 } { 0, 0x45B2, 3, 0, 0xC201, 0, 1 }, /* EF_PK.NKS.ENC1024 */
{ 0, 0xC201, 3, 101 }, /* EF_C.NKS.ENC1024 */
/* { 1, 0xB000, 3, ... */
{ 0, 0 }
}; };
@ -62,9 +92,13 @@ static struct
struct app_local_s { struct app_local_s {
int nks_version; /* NKS version. */ int nks_version; /* NKS version. */
int sigg_active; /* True if switched to the SigG application. */
}; };
static gpg_error_t switch_application (app_t app, int enable_sigg);
/* Release local data. */ /* Release local data. */
@ -146,6 +180,134 @@ keygripstr_from_pk_file (app_t app, int fid, char *r_gripstr)
} }
/* TCOS responds to a verify with empty data (i.e. without the Lc
byte) with the status of the PIN. PWID is the PIN ID, If SIGG is
true, the application is switched into SigG mode.
Returns:
-1 = Error retrieving the data,
-2 = No such PIN,
-3 = PIN blocked,
-4 = NullPIN activ,
n >= 0 = Number of verification attempts left. */
static int
get_chv_status (app_t app, int sigg, int pwid)
{
unsigned char *result = NULL;
size_t resultlen;
char command[4];
int rc;
if (switch_application (app, sigg))
return sigg? -2 : -1; /* No such PIN / General error. */
command[0] = 0x00;
command[1] = 0x20;
command[2] = 0x00;
command[3] = pwid;
if (apdu_send_direct (app->slot, command, 4, 0, &result, &resultlen))
rc = -1; /* Error. */
else if (resultlen < 2)
rc = -1; /* Error. */
else
{
unsigned int sw = ((result[resultlen-2] << 8) | result[resultlen-1]);
if (sw == 0x6a88)
rc = -2; /* No such PIN. */
else if (sw == 0x6983)
rc = -3; /* PIN is blocked. */
else if (sw == 0x6985)
rc = -4; /* NullPIN is activ. */
else if ((sw & 0xfff0) == 0x63C0)
rc = (sw & 0x000f); /* PIN has N tries left. */
else
rc = -1; /* Other error. */
}
xfree (result);
return rc;
}
/* Implement the GETATTR command. This is similar to the LEARN
command but returns just one value via the status interface. */
static gpg_error_t
do_getattr (app_t app, ctrl_t ctrl, const char *name)
{
static struct {
const char *name;
int special;
} table[] = {
{ "$AUTHKEYID", 1 },
{ "NKS-VERSION", 2 },
{ "CHV-STATUS", 3 },
{ NULL, 0 }
};
gpg_error_t err = 0;
int idx;
char buffer[100];
err = switch_application (app, 0);
if (err)
return err;
for (idx=0; table[idx].name && strcmp (table[idx].name, name); idx++)
;
if (!table[idx].name)
return gpg_error (GPG_ERR_INV_NAME);
switch (table[idx].special)
{
case 1: /* $AUTHKEYID */
{
/* NetKey 3.0 cards define this key for authentication.
FIXME: We don't have the readkey command, so this
information is pretty useless. */
char const tmp[] = "NKS-NKS3.4571";
send_status_info (ctrl, table[idx].name, tmp, strlen (tmp), NULL, 0);
}
break;
case 2: /* NKS-VERSION */
snprintf (buffer, sizeof buffer, "%d", app->app_local->nks_version);
send_status_info (ctrl, table[idx].name,
buffer, strlen (buffer), NULL, 0);
break;
case 3: /* CHV-STATUS */
{
/* Returns: PW1.CH PW2.CH PW1.CH.SIG PW2.CH.SIG That are the
two global passwords followed by the two SigG passwords.
For the values, see the function get_chv_status. */
int tmp[4];
/* We use a helper array so that we can control that there is
no superfluous application switch. Note that PW2.CH.SIG
really has the identifier 0x83 and not 0x82 as one would
expect. */
tmp[0] = get_chv_status (app, 0, 0x00);
tmp[1] = get_chv_status (app, 0, 0x01);
tmp[2] = get_chv_status (app, 1, 0x81);
tmp[3] = get_chv_status (app, 1, 0x83);
snprintf (buffer, sizeof buffer,
"%d %d %d %d", tmp[0], tmp[1], tmp[2], tmp[3]);
send_status_info (ctrl, table[idx].name,
buffer, strlen (buffer), NULL, 0);
}
break;
default:
err = gpg_error (GPG_ERR_NOT_IMPLEMENTED);
break;
}
return err;
}
static gpg_error_t static gpg_error_t
do_learn_status (app_t app, ctrl_t ctrl) do_learn_status (app_t app, ctrl_t ctrl)
@ -154,12 +316,19 @@ do_learn_status (app_t app, ctrl_t ctrl)
char ct_buf[100], id_buf[100]; char ct_buf[100], id_buf[100];
int i; int i;
/* Output information about all useful objects. */ err = switch_application (app, 0);
if (err)
return err;
/* Output information about all useful objects in the NKS application. */
for (i=0; filelist[i].fid; i++) for (i=0; filelist[i].fid; i++)
{ {
if (filelist[i].nks_ver > app->app_local->nks_version) if (filelist[i].nks_ver > app->app_local->nks_version)
continue; continue;
if (filelist[i].is_sigg)
continue;
if (filelist[i].certtype) if (filelist[i].certtype)
{ {
size_t len; size_t len;
@ -171,8 +340,10 @@ do_learn_status (app_t app, ctrl_t ctrl)
/* FIXME: We should store the length in the application's /* FIXME: We should store the length in the application's
context so that a following readcert does only need to context so that a following readcert does only need to
read that many bytes. */ read that many bytes. */
sprintf (ct_buf, "%d", filelist[i].certtype); snprintf (ct_buf, sizeof ct_buf, "%d", filelist[i].certtype);
sprintf (id_buf, "NKS-DF01.%04X", filelist[i].fid); snprintf (id_buf, sizeof id_buf, "NKS-%s.%04X",
app->app_local->nks_version < 3? "DF01":"NKS3",
filelist[i].fid);
send_status_info (ctrl, "CERTINFO", send_status_info (ctrl, "CERTINFO",
ct_buf, strlen (ct_buf), ct_buf, strlen (ct_buf),
id_buf, strlen (id_buf), id_buf, strlen (id_buf),
@ -189,7 +360,9 @@ do_learn_status (app_t app, ctrl_t ctrl)
filelist[i].fid, gpg_strerror (err)); filelist[i].fid, gpg_strerror (err));
else else
{ {
sprintf (id_buf, "NKS-DF01.%04X", filelist[i].fid); snprintf (id_buf, sizeof id_buf, "NKS-%s.%04X",
app->app_local->nks_version < 3? "DF01":"NKS3",
filelist[i].fid);
send_status_info (ctrl, "KEYPAIRINFO", send_status_info (ctrl, "KEYPAIRINFO",
gripstr, 40, gripstr, 40,
id_buf, strlen (id_buf), id_buf, strlen (id_buf),
@ -198,6 +371,59 @@ do_learn_status (app_t app, ctrl_t ctrl)
} }
} }
err = switch_application (app, 1);
if (err)
return 0; /* Silently ignore if we can't swicth to SigG. */
for (i=0; filelist[i].fid; i++)
{
if (filelist[i].nks_ver > app->app_local->nks_version)
continue;
if (!filelist[i].is_sigg)
continue;
if (filelist[i].certtype)
{
size_t len;
len = app_help_read_length_of_cert (app->slot,
filelist[i].fid, NULL);
if (len)
{
/* FIXME: We should store the length in the application's
context so that a following readcert does only need to
read that many bytes. */
snprintf (ct_buf, sizeof ct_buf, "%d", filelist[i].certtype);
snprintf (id_buf, sizeof id_buf, "NKS-SIGG.%04X",
filelist[i].fid);
send_status_info (ctrl, "CERTINFO",
ct_buf, strlen (ct_buf),
id_buf, strlen (id_buf),
NULL, (size_t)0);
}
}
else if (filelist[i].iskeypair)
{
char gripstr[40+1];
err = keygripstr_from_pk_file (app, filelist[i].fid, gripstr);
if (err)
log_error ("can't get keygrip from FID 0x%04X: %s\n",
filelist[i].fid, gpg_strerror (err));
else
{
snprintf (id_buf, sizeof id_buf, "NKS-SIGG.%04X",
filelist[i].fid);
send_status_info (ctrl, "KEYPAIRINFO",
gripstr, 40,
id_buf, strlen (id_buf),
NULL, (size_t)0);
}
}
}
return 0; return 0;
} }
@ -223,7 +449,16 @@ do_readcert (app_t app, const char *certid,
*cert = NULL; *cert = NULL;
*certlen = 0; *certlen = 0;
if (strncmp (certid, "NKS-DF01.", 9) )
err = switch_application (app, 0);
if (err)
return err;
if (!strncmp (certid, "NKS-NKS3.", 9))
;
else if (!strncmp (certid, "NKS-DF01.", 9))
;
else
return gpg_error (GPG_ERR_INV_ID); return gpg_error (GPG_ERR_INV_ID);
certid += 9; certid += 9;
if (!hexdigitp (certid) || !hexdigitp (certid+1) if (!hexdigitp (certid) || !hexdigitp (certid+1)
@ -331,21 +566,34 @@ do_readcert (app_t app, const char *certid,
} }
static gpg_error_t
basic_pin_checks (const char *pinvalue, int minlen, int maxlen)
{
if (strlen (pinvalue) < minlen)
{
log_error ("PIN is too short; minimum length is %d\n", minlen);
return gpg_error (GPG_ERR_BAD_PIN);
}
if (strlen (pinvalue) > maxlen)
{
log_error ("PIN is too large; maximum length is %d\n", maxlen);
return gpg_error (GPG_ERR_BAD_PIN);
}
return 0;
}
/* Verify the PIN if required. */ /* Verify the PIN if required. */
static gpg_error_t static gpg_error_t
verify_pin (app_t app, verify_pin (app_t app, int pwid, const char *desc,
gpg_error_t (*pincb)(void*, const char *, char **), gpg_error_t (*pincb)(void*, const char *, char **),
void *pincb_arg) void *pincb_arg)
{ {
iso7816_pininfo_t pininfo; iso7816_pininfo_t pininfo;
int rc; int rc;
/* Note that force_chv1 is never set but we do it here anyway so if (!desc)
that other applications may reuse this function. For example it desc = "PIN";
makes sense to set force_chv1 for German signature law cards.
NKS is very similar to the DINSIG draft standard. */
if ( app->did_chv1 && !app->force_chv1 )
return 0; /* No need to verify it again. */
memset (&pininfo, 0, sizeof pininfo); memset (&pininfo, 0, sizeof pininfo);
pininfo.mode = 1; pininfo.mode = 1;
@ -375,7 +623,7 @@ verify_pin (app_t app,
{ {
char *pinvalue; char *pinvalue;
rc = pincb (pincb_arg, "PIN", &pinvalue); rc = pincb (pincb_arg, desc, &pinvalue);
if (rc) if (rc)
{ {
log_info ("PIN callback returned error: %s\n", gpg_strerror (rc)); log_info ("PIN callback returned error: %s\n", gpg_strerror (rc));
@ -384,24 +632,14 @@ verify_pin (app_t app,
/* The following limits are due to TCOS but also defined in the /* The following limits are due to TCOS but also defined in the
NKS specs. */ NKS specs. */
if (strlen (pinvalue) < pininfo.minlen) rc = basic_pin_checks (pinvalue, pininfo.minlen, pininfo.maxlen);
if (rc)
{ {
log_error ("PIN is too short; minimum length is %d\n",
pininfo.minlen);
xfree (pinvalue); xfree (pinvalue);
return gpg_error (GPG_ERR_BAD_PIN); return rc;
}
else if (strlen (pinvalue) > pininfo.maxlen)
{
log_error ("PIN is too large; maximum length is %d\n",
pininfo.maxlen);
xfree (pinvalue);
return gpg_error (GPG_ERR_BAD_PIN);
} }
/* Although it is possible to use a local PIN, we use the global rc = iso7816_verify (app->slot, pwid, pinvalue, strlen (pinvalue));
PIN for this application. */
rc = iso7816_verify (app->slot, 0, pinvalue, strlen (pinvalue));
xfree (pinvalue); xfree (pinvalue);
} }
@ -413,7 +651,6 @@ verify_pin (app_t app,
log_error ("verify PIN failed\n"); log_error ("verify PIN failed\n");
return rc; return rc;
} }
app->did_chv1 = 1;
return 0; return 0;
} }
@ -447,9 +684,17 @@ do_sign (app_t app, const char *keyidstr, int hashalgo,
if (indatalen != 20 && indatalen != 16 && indatalen != 35) if (indatalen != 20 && indatalen != 16 && indatalen != 35)
return gpg_error (GPG_ERR_INV_VALUE); return gpg_error (GPG_ERR_INV_VALUE);
rc = switch_application (app, 0);
if (rc)
return rc;
/* Check that the provided ID is valid. This is not really needed /* Check that the provided ID is valid. This is not really needed
but we do it to enforce correct usage by the caller. */ but we do it to enforce correct usage by the caller. */
if (strncmp (keyidstr, "NKS-DF01.", 9) ) if (!strncmp (keyidstr, "NKS-NKS3.", 9) )
;
else if (!strncmp (keyidstr, "NKS-DF01.", 9) )
;
else
return gpg_error (GPG_ERR_INV_ID); return gpg_error (GPG_ERR_INV_ID);
keyidstr += 9; keyidstr += 9;
if (!hexdigitp (keyidstr) || !hexdigitp (keyidstr+1) if (!hexdigitp (keyidstr) || !hexdigitp (keyidstr+1)
@ -490,7 +735,7 @@ do_sign (app_t app, const char *keyidstr, int hashalgo,
memcpy (data+15, indata, indatalen); memcpy (data+15, indata, indatalen);
} }
rc = verify_pin (app, pincb, pincb_arg); rc = verify_pin (app, 0, NULL, pincb, pincb_arg);
if (!rc) if (!rc)
rc = iso7816_compute_ds (app->slot, data, 35, outdata, outdatalen); rc = iso7816_compute_ds (app->slot, data, 35, outdata, outdatalen);
return rc; return rc;
@ -519,9 +764,17 @@ do_decipher (app_t app, const char *keyidstr,
if (!keyidstr || !*keyidstr || !indatalen) if (!keyidstr || !*keyidstr || !indatalen)
return gpg_error (GPG_ERR_INV_VALUE); return gpg_error (GPG_ERR_INV_VALUE);
rc = switch_application (app, 0);
if (rc)
return rc;
/* Check that the provided ID is valid. This is not really needed /* Check that the provided ID is valid. This is not really needed
but we do it to to enforce correct usage by the caller. */ but we do it to to enforce correct usage by the caller. */
if (strncmp (keyidstr, "NKS-DF01.", 9) ) if (!strncmp (keyidstr, "NKS-NKS3.", 9) )
;
else if (!strncmp (keyidstr, "NKS-DF01.", 9) )
;
else
return gpg_error (GPG_ERR_INV_ID); return gpg_error (GPG_ERR_INV_ID);
keyidstr += 9; keyidstr += 9;
if (!hexdigitp (keyidstr) || !hexdigitp (keyidstr+1) if (!hexdigitp (keyidstr) || !hexdigitp (keyidstr+1)
@ -542,7 +795,7 @@ do_decipher (app_t app, const char *keyidstr,
0xC1, 0xB8, 0xC1, 0xB8,
mse_parm, sizeof mse_parm); mse_parm, sizeof mse_parm);
if (!rc) if (!rc)
rc = verify_pin (app, pincb, pincb_arg); rc = verify_pin (app, 0, NULL, pincb, pincb_arg);
if (!rc) if (!rc)
rc = iso7816_decipher (app->slot, indata, indatalen, 0x81, rc = iso7816_decipher (app->slot, indata, indatalen, 0x81,
outdata, outdatalen); outdata, outdatalen);
@ -550,67 +803,221 @@ do_decipher (app_t app, const char *keyidstr,
} }
/* Handle the PASSWD command. CHVNOSTR is currently ignored; we
always use VHV0. RESET_MODE is not yet implemented. */ /* Parse a password ID string. Returns NULL on error or a string
suitable as passpahrse prompt on success. On success stores the
reference value for the password at R_PWID and a flag indicating
that the SigG application is to be used at R_SIGG. If NEW_MODE is
true, the returned description is suitable for a new Password.
Supported values for PWIDSTR are:
PW1.CH - Global password 1
PW2.CH - Global password 2
PW1.CH.SIG - SigG password 1
PW2.CH.SIG - SigG password 2
*/
static const char *
parse_pwidstr (const char *pwidstr, int new_mode, int *r_sigg, int *r_pwid)
{
const char *desc;
if (!pwidstr)
desc = NULL;
else if (!strcmp (pwidstr, "PW1.CH"))
{
*r_sigg = 0;
*r_pwid = 0x00;
/* TRANSLATORS: Do not translate the "|*|" prefixes but keep
them verbatim at the start of the string. */
desc = (new_mode
? _("|N|Please enter a new PIN for the standard keys.")
: _("||Please enter the PIN for the standard keys."));
}
else if (!strcmp (pwidstr, "PW2.CH"))
{
*r_pwid = 0x01;
desc = (new_mode
? _("|NP|Please enter a new PIN Unblocking Code (PUK) "
"for the standard keys.")
: _("|P|Please enter the PIN Unblocking Code (PUK) "
"for the standard keys."));
}
else if (!strcmp (pwidstr, "PW1.CH.SIG"))
{
*r_pwid = 0x81;
*r_sigg = 1;
desc = (new_mode
? _("|N|Please enter a new PIN for the key to create "
"qualified signatures.")
: _("||Please enter the PIN for the key to create "
"qualified signatures."));
}
else if (!strcmp (pwidstr, "PW2.CH.SIG"))
{
*r_pwid = 0x83; /* Yes, that is 83 and not 82. */
*r_sigg = 1;
desc = (new_mode
? _("|NP|Please enter a new PIN Unblocking Code (PUK) "
"for the key to create qualified signatures.")
: _("|P|Please enter the PIN Unblocking Code (PUK) "
"for the key to create qualified signatures."));
}
else
desc = NULL;
return desc;
}
/* Handle the PASSWD command. See parse_pwidstr() for allowed values
for CHVNOSTR. */
static gpg_error_t static gpg_error_t
do_change_pin (app_t app, ctrl_t ctrl, const char *chvnostr, do_change_pin (app_t app, ctrl_t ctrl, const char *pwidstr,
unsigned int flags, unsigned int flags,
gpg_error_t (*pincb)(void*, const char *, char **), gpg_error_t (*pincb)(void*, const char *, char **),
void *pincb_arg) void *pincb_arg)
{ {
gpg_error_t err; gpg_error_t err;
char *pinvalue; char *newpin = NULL;
const char *oldpin; char *oldpin = NULL;
size_t newpinlen;
size_t oldpinlen; size_t oldpinlen;
int is_sigg;
const char *newdesc;
int pwid;
iso7816_pininfo_t pininfo;
(void)ctrl; (void)ctrl;
(void)chvnostr;
if ((flags & APP_CHANGE_FLAG_RESET)) /* The minimum length is enforced by TCOS, the maximum length is
return gpg_error (GPG_ERR_NOT_IMPLEMENTED); just a reasonable value. */
memset (&pininfo, 0, sizeof pininfo);
pininfo.minlen = 6;
pininfo.maxlen = 16;
newdesc = parse_pwidstr (pwidstr, 1, &is_sigg, &pwid);
if (!newdesc)
return gpg_error (GPG_ERR_INV_ID);
err = switch_application (app, is_sigg);
if (err)
return err;
if ((flags & APP_CHANGE_FLAG_NULLPIN)) if ((flags & APP_CHANGE_FLAG_NULLPIN))
{ {
/* With the nullpin flag, we do not verify the PIN - it would fail /* With the nullpin flag, we do not verify the PIN - it would
if the Nullpin is still set. */ fail if the Nullpin is still set. */
oldpin = "\0\0\0\0\0"; oldpin = xtrycalloc (1, 6);
if (!oldpin)
{
err = gpg_error_from_syserror ();
goto leave;
}
oldpinlen = 6; oldpinlen = 6;
} }
else else
{ {
err = verify_pin (app, pincb, pincb_arg); const char *desc;
int dummy1, dummy2;
if ((flags & APP_CHANGE_FLAG_RESET))
{
/* Reset mode: Ask for the alternate PIN. */
const char *altpwidstr;
if (!strcmp (pwidstr, "PW1.CH"))
altpwidstr = "PW2.CH";
else if (!strcmp (pwidstr, "PW2.CH"))
altpwidstr = "PW1.CH";
else if (!strcmp (pwidstr, "PW1.CH.SIG"))
altpwidstr = "PW2.CH.SIG";
else if (!strcmp (pwidstr, "PW2.CH.SIG"))
altpwidstr = "PW1.CH.SIG";
else
{
err = gpg_error (GPG_ERR_BUG);
goto leave;
}
desc = parse_pwidstr (altpwidstr, 0, &dummy1, &dummy2);
}
else
{
/* Regular change mode: Ask for the old PIN. */
desc = parse_pwidstr (pwidstr, 0, &dummy1, &dummy2);
}
err = pincb (pincb_arg, desc, &oldpin);
if (err) if (err)
return err; {
oldpin = NULL; log_error ("error getting old PIN: %s\n", gpg_strerror (err));
oldpinlen = 0; goto leave;
}
oldpinlen = strlen (oldpin);
err = basic_pin_checks (oldpin, pininfo.minlen, pininfo.maxlen);
if (err)
goto leave;
} }
/* TRANSLATORS: Do not translate the "|*|" prefixes but err = pincb (pincb_arg, newdesc, &newpin);
keep it at the start of the string. We need this elsewhere
to get some infos on the string. */
err = pincb (pincb_arg, _("|N|New PIN"), &pinvalue);
if (err) if (err)
{ {
log_error (_("error getting new PIN: %s\n"), gpg_strerror (err)); log_error (_("error getting new PIN: %s\n"), gpg_strerror (err));
return err; goto leave;
} }
newpinlen = strlen (newpin);
err = basic_pin_checks (newpin, pininfo.minlen, pininfo.maxlen);
if (err)
goto leave;
err = iso7816_change_reference_data (app->slot, 0x00, if ((flags & APP_CHANGE_FLAG_RESET))
oldpin, oldpinlen, {
pinvalue, strlen (pinvalue)); char *data;
xfree (pinvalue); size_t datalen = oldpinlen + newpinlen;
data = xtrymalloc (datalen);
if (!data)
{
err = gpg_error_from_syserror ();
goto leave;
}
memcpy (data, oldpin, oldpinlen);
memcpy (data+oldpinlen, newpin, newpinlen);
err = iso7816_reset_retry_counter_with_rc (app->slot, pwid,
data, datalen);
wipememory (data, datalen);
xfree (data);
}
else
err = iso7816_change_reference_data (app->slot, pwid,
oldpin, oldpinlen,
newpin, newpinlen);
leave:
xfree (oldpin);
xfree (newpin);
return err; return err;
} }
/* Perform a simple verify operation. KEYIDSTR should be NULL or empty. */ /* Perform a simple verify operation. KEYIDSTR should be NULL or empty. */
static gpg_error_t static gpg_error_t
do_check_pin (app_t app, const char *keyidstr, do_check_pin (app_t app, const char *pwidstr,
gpg_error_t (*pincb)(void*, const char *, char **), gpg_error_t (*pincb)(void*, const char *, char **),
void *pincb_arg) void *pincb_arg)
{ {
(void)keyidstr; gpg_error_t err;
return verify_pin (app, pincb, pincb_arg); int pwid;
int is_sigg;
const char *desc;
desc = parse_pwidstr (pwidstr, 0, &is_sigg, &pwid);
if (!desc)
return gpg_error (GPG_ERR_INV_ID);
err = switch_application (app, is_sigg);
if (err)
return err;
return verify_pin (app, pwid, desc, pincb, pincb_arg);
} }
@ -647,15 +1054,42 @@ get_nks_version (int slot)
} }
/* If ENABLE_SIGG is true switch to the SigG application if not yet
active. If false switch to the NKS application if not yet active.
Returns 0 on success. */
static gpg_error_t
switch_application (app_t app, int enable_sigg)
{
gpg_error_t err;
if ((app->app_local->sigg_active && enable_sigg)
|| (!app->app_local->sigg_active && !enable_sigg) )
return 0; /* Already switched. */
log_info ("app-nks: switching to %s\n", enable_sigg? "SigG":"NKS");
if (enable_sigg)
err = iso7816_select_application (app->slot, aid_sigg, sizeof aid_sigg, 0);
else
err = iso7816_select_application (app->slot, aid_nks, sizeof aid_nks, 0);
if (!err)
app->app_local->sigg_active = enable_sigg;
else
log_error ("app-nks: error switching to %s: %s\n",
enable_sigg? "SigG":"NKS", gpg_strerror (err));
return err;
}
/* Select the NKS application. */ /* Select the NKS application. */
gpg_error_t gpg_error_t
app_select_nks (app_t app) app_select_nks (app_t app)
{ {
static char const aid[] = { 0xD2, 0x76, 0x00, 0x00, 0x03, 0x01, 0x02 };
int slot = app->slot; int slot = app->slot;
int rc; int rc;
rc = iso7816_select_application (slot, aid, sizeof aid, 0); rc = iso7816_select_application (slot, aid_nks, sizeof aid_nks, 0);
if (!rc) if (!rc)
{ {
app->apptype = "NKS"; app->apptype = "NKS";
@ -674,7 +1108,7 @@ app_select_nks (app_t app)
app->fnc.deinit = do_deinit; app->fnc.deinit = do_deinit;
app->fnc.learn_status = do_learn_status; app->fnc.learn_status = do_learn_status;
app->fnc.readcert = do_readcert; app->fnc.readcert = do_readcert;
app->fnc.getattr = NULL; app->fnc.getattr = do_getattr;
app->fnc.setattr = NULL; app->fnc.setattr = NULL;
app->fnc.genkey = NULL; app->fnc.genkey = NULL;
app->fnc.sign = do_sign; app->fnc.sign = do_sign;

View File

@ -1,5 +1,6 @@
/* app-openpgp.c - The OpenPGP card application. /* app-openpgp.c - The OpenPGP card application.
* Copyright (C) 2003, 2004, 2005, 2007, 2008 Free Software Foundation, Inc. * Copyright (C) 2003, 2004, 2005, 2007, 2008,
* 2009 Free Software Foundation, Inc.
* *
* This file is part of GnuPG. * This file is part of GnuPG.
* *
@ -1402,7 +1403,8 @@ verify_a_chv (app_t app,
int chvno, unsigned long sigcount, char **pinvalue) int chvno, unsigned long sigcount, char **pinvalue)
{ {
int rc = 0; int rc = 0;
char *prompt; char *prompt_buffer = NULL;
const char *prompt;
iso7816_pininfo_t pininfo; iso7816_pininfo_t pininfo;
int minlen = 6; int minlen = 6;
@ -1432,30 +1434,34 @@ verify_a_chv (app_t app,
memset (&pininfo, 0, sizeof pininfo); memset (&pininfo, 0, sizeof pininfo);
pininfo.mode = 1; pininfo.mode = 1;
pininfo.minlen = minlen; pininfo.minlen = minlen;
if (chvno == 1)
{
#define PROMPTSTRING _("||Please enter the PIN%%0A[sigs done: %lu]")
size_t promptsize = strlen (PROMPTSTRING) + 50;
prompt_buffer = xtrymalloc (promptsize);
if (!prompt_buffer)
return gpg_error_from_syserror ();
snprintf (prompt_buffer, promptsize-1, PROMPTSTRING, sigcount);
prompt = prompt_buffer;
#undef PROMPTSTRING
}
else
prompt = _("||Please enter the PIN");
if (!opt.disable_keypad if (!opt.disable_keypad
&& !iso7816_check_keypad (app->slot, ISO7816_VERIFY, &pininfo) ) && !iso7816_check_keypad (app->slot, ISO7816_VERIFY, &pininfo) )
{ {
/* The reader supports the verify command through the keypad. */ /* The reader supports the verify command through the keypad.
Note that the pincb appends a text to the prompt telling the
if (chvno == 1) user to use the keypad. */
{ rc = pincb (pincb_arg, prompt, NULL);
#define PROMPTSTRING _("||Please enter your PIN at the reader's keypad%%0A" \ prompt = NULL;
"[sigs done: %lu]") xfree (prompt_buffer);
size_t promptsize = strlen (PROMPTSTRING) + 50; prompt_buffer = NULL;
prompt = xmalloc (promptsize);
if (!prompt)
return gpg_error_from_syserror ();
snprintf (prompt, promptsize-1, PROMPTSTRING, sigcount);
rc = pincb (pincb_arg, prompt, NULL);
xfree (prompt);
#undef PROMPTSTRING
}
else
rc = pincb (pincb_arg,
_("||Please enter your PIN at the reader's keypad"),
NULL);
if (rc) if (rc)
{ {
log_info (_("PIN callback returned error: %s\n"), log_info (_("PIN callback returned error: %s\n"),
@ -1471,23 +1477,10 @@ verify_a_chv (app_t app,
else else
{ {
/* The reader has no keypad or we don't want to use it. */ /* The reader has no keypad or we don't want to use it. */
rc = pincb (pincb_arg, prompt, pinvalue);
if (chvno == 1) prompt = NULL;
{ xfree (prompt_buffer);
#define PROMPTSTRING _("||Please enter the PIN%%0A[sigs done: %lu]") prompt_buffer = NULL;
size_t promptsize = strlen (PROMPTSTRING) + 50;
prompt = xtrymalloc (promptsize);
if (!prompt)
return gpg_error_from_syserror ();
snprintf (prompt, promptsize-1, PROMPTSTRING, sigcount);
rc = pincb (pincb_arg, prompt, pinvalue);
xfree (prompt);
#undef PROMPTSTRING
}
else
rc = pincb (pincb_arg, _("||Please enter the PIN"), pinvalue);
if (rc) if (rc)
{ {
log_info (_("PIN callback returned error: %s\n"), log_info (_("PIN callback returned error: %s\n"),
@ -1586,6 +1579,8 @@ verify_chv3 (app_t app,
iso7816_pininfo_t pininfo; iso7816_pininfo_t pininfo;
int minlen = 8; int minlen = 8;
int remaining; int remaining;
char *prompt_buffer = NULL;
const char *prompt;
memset (&pininfo, 0, sizeof pininfo); memset (&pininfo, 0, sizeof pininfo);
pininfo.mode = 1; pininfo.mode = 1;
@ -1610,31 +1605,33 @@ verify_chv3 (app_t app,
log_info(_("%d Admin PIN attempts remaining before card" log_info(_("%d Admin PIN attempts remaining before card"
" is permanently locked\n"), remaining); " is permanently locked\n"), remaining);
if (remaining < 3)
{
/* TRANSLATORS: Do not translate the "|A|" prefix but keep
it at the start of the string. Use %%0A to force a
lienfeed. */
#define PROMPTSTRING _("|A|Please enter the Admin PIN%%0A" \
"[remaining attempts: %d]")
size_t promptsize = strlen (PROMPTSTRING) + 50;
prompt_buffer = xtrymalloc (promptsize);
if (!prompt_buffer)
return gpg_error_from_syserror ();
snprintf (prompt_buffer, promptsize-1, PROMPTSTRING, remaining);
prompt = prompt_buffer;
#undef PROMPTSTRING
}
else
prompt = _("|A|Please enter the Admin PIN");
if (!opt.disable_keypad if (!opt.disable_keypad
&& !iso7816_check_keypad (app->slot, ISO7816_VERIFY, &pininfo) ) && !iso7816_check_keypad (app->slot, ISO7816_VERIFY, &pininfo) )
{ {
/* The reader supports the verify command through the keypad. */ /* The reader supports the verify command through the keypad. */
rc = pincb (pincb_arg, prompt, NULL);
if (remaining < 3) prompt = NULL;
{ xfree (prompt_buffer);
#define PROMPTSTRING _("|A|Please enter the Admin PIN" \ prompt_buffer = NULL;
" at the reader's keypad%%0A" \
"[remaining attempts: %d]")
size_t promptsize = strlen (PROMPTSTRING) + 50;
char *prompt;
prompt = xmalloc (promptsize);
if (!prompt)
return gpg_error_from_syserror ();
snprintf (prompt, promptsize-1, PROMPTSTRING, remaining);
rc = pincb (pincb_arg, prompt, NULL);
xfree (prompt);
#undef PROMPTSTRING
}
else
rc = pincb (pincb_arg, _("|A|Please enter the Admin PIN"
" at the reader's keypad"), NULL);
if (rc) if (rc)
{ {
log_info (_("PIN callback returned error: %s\n"), log_info (_("PIN callback returned error: %s\n"),
@ -1649,10 +1646,10 @@ verify_chv3 (app_t app,
{ {
char *pinvalue; char *pinvalue;
/* TRANSLATORS: Do not translate the "|A|" prefix but keep rc = pincb (pincb_arg, prompt, &pinvalue);
it at the start of the string. We need this elsewhere to prompt = NULL;
get some infos on the string. */ xfree (prompt_buffer);
rc = pincb (pincb_arg, _("|A|Admin PIN"), &pinvalue); prompt_buffer = NULL;
if (rc) if (rc)
{ {
log_info (_("PIN callback returned error: %s\n"), log_info (_("PIN callback returned error: %s\n"),

View File

@ -1370,9 +1370,10 @@ cmd_random (assuan_context_t ctx, char *line)
/* PASSWD [--reset] [--nullpin] <chvno> /* PASSWD [--reset] [--nullpin] <chvno>
Change the PIN or reset the retry counter of the card holder Change the PIN or, if --reset is given, reset the retry counter of
verfication vector CHVNO. The option --nullpin is used for TCOS the card holder verfication vector CHVNO. The option --nullpin is
cards to set the initial PIN. */ used for TCOS cards to set the initial PIN. The format of CHVNO
depends on the card application. */
static int static int
cmd_passwd (assuan_context_t ctx, char *line) cmd_passwd (assuan_context_t ctx, char *line)
{ {
@ -1435,13 +1436,27 @@ cmd_passwd (assuan_context_t ctx, char *line)
literal string "[CHV3]": In this case the Admin PIN is checked literal string "[CHV3]": In this case the Admin PIN is checked
if and only if the retry counter is still at 3. if and only if the retry counter is still at 3.
For Netkey:
Any of the valid PIN Ids may be used. These are the strings:
PW1.CH - Global password 1
PW2.CH - Global password 2
PW1.CH.SIG - SigG password 1
PW2.CH.SIG - SigG password 2
For a definitive list, see the implementation in app-nks.c.
Note that we call a PW2.* PIN a "PUK" despite that since TCOS
3.0 they are technically alternative PINs used to mutally
unblock each other.
*/ */
static int static int
cmd_checkpin (assuan_context_t ctx, char *line) cmd_checkpin (assuan_context_t ctx, char *line)
{ {
ctrl_t ctrl = assuan_get_pointer (ctx); ctrl_t ctrl = assuan_get_pointer (ctx);
int rc; int rc;
char *keyidstr; char *idstr;
if ( IS_LOCKED (ctrl) ) if ( IS_LOCKED (ctrl) )
return gpg_error (GPG_ERR_LOCKED); return gpg_error (GPG_ERR_LOCKED);
@ -1455,14 +1470,12 @@ cmd_checkpin (assuan_context_t ctx, char *line)
/* We have to use a copy of the key ID because the function may use /* We have to use a copy of the key ID because the function may use
the pin_cb which in turn uses the assuan line buffer and thus the pin_cb which in turn uses the assuan line buffer and thus
overwriting the original line with the keyid. */ overwriting the original line with the keyid. */
keyidstr = xtrystrdup (line); idstr = xtrystrdup (line);
if (!keyidstr) if (!idstr)
return out_of_core (); return out_of_core ();
rc = app_check_pin (ctrl->app_ctx, rc = app_check_pin (ctrl->app_ctx, idstr, pin_cb, ctx);
keyidstr, xfree (idstr);
pin_cb, ctx);
xfree (keyidstr);
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));
@ -1566,7 +1579,7 @@ cmd_unlock (assuan_context_t ctx, char *line)
deny_admin - Returns OK if admin commands are not allowed or deny_admin - Returns OK if admin commands are not allowed or
GPG_ERR_GENERAL if admin commands are allowed. GPG_ERR_GENERAL if admin commands are allowed.
app_list - Return a list of supported applciations. One app_list - Return a list of supported applications. One
application per line, fields delimited by colons, application per line, fields delimited by colons,
first field is the name. first field is the name.
*/ */