1
0
mirror of git://git.gnupg.org/gnupg.git synced 2024-12-22 10:19:57 +01:00

Changes needed to support smartcards. Well, only _support_. There is

no real code yet.
This commit is contained in:
Werner Koch 2002-02-28 11:07:59 +00:00
parent f8c8ca26d4
commit 56341c289c
23 changed files with 808 additions and 104 deletions

View File

@ -1,3 +1,22 @@
2002-02-28 Werner Koch <wk@gnupg.org>
* pksign.c (agent_pksign): Detect whether a Smartcard is to be
used and divert the operation in this case.
* pkdecrypt.c (agent_pkdecrypt): Likewise
* findkey.c (agent_key_from_file): Add optional arg shadow_info
and have it return information about a shadowed key.
* protect.c (agent_get_shadow_info): New.
* divert-scd.c: New.
2002-02-27 Werner Koch <wk@gnupg.org>
* protect.c (agent_shadow_key): New.
* command.c (cmd_learn): New command LEARN.
* gpg-agent.c: New option --scdaemon-program.
* call-scd.c (start_scd): New. Based on query.c
* query.c: Add 2 more arguments to all uses of assuan_transact.
2002-02-18 Werner Koch <wk@gnupg.org>
* findkey.c (unprotect): Show an error message for a bad passphrase.

View File

@ -35,7 +35,9 @@ gpg_agent_SOURCES = \
pkdecrypt.c \
genkey.c \
protect.c \
trustlist.c
trustlist.c \
divert-scd.c \
call-scd.c
gpg_agent_LDADD = ../jnlib/libjnlib.a ../assuan/libassuan.a \
../common/libcommon.a $(LIBGCRYPT_LIBS)

View File

@ -36,6 +36,7 @@ struct {
int batch; /* batch mode */
const char *homedir; /* configuration directory name */
const char *pinentry_program;
const char *scdaemon_program;
int no_grab; /* don't let the pinentry grab the keyboard */
unsigned long def_cache_ttl;
@ -101,7 +102,8 @@ const char *trans (const char *text);
void start_command_handler (int);
/*-- findkey.c --*/
GCRY_SEXP agent_key_from_file (const unsigned char *grip);
GCRY_SEXP agent_key_from_file (const unsigned char *grip,
unsigned char **shadow_info);
int agent_key_available (const unsigned char *grip);
/*-- query.c --*/
@ -135,6 +137,11 @@ int agent_protect (const unsigned char *plainkey, const char *passphrase,
int agent_unprotect (const unsigned char *protectedkey, const char *passphrase,
unsigned char **result, size_t *resultlen);
int agent_private_key_type (const unsigned char *privatekey);
int agent_shadow_key (const unsigned char *pubkey,
const unsigned char *shadow_info,
unsigned char **result);
int agent_get_shadow_info (const unsigned char *shadowkey,
unsigned char const **shadow_info);
/*-- trustlist.c --*/
@ -143,4 +150,14 @@ int agent_listtrusted (void *assuan_context);
int agent_marktrusted (const char *name, const char *fpr, int flag);
/*-- divert-scd.c --*/
int divert_pksign (GCRY_SEXP *s_sig, GCRY_SEXP s_hash,
const char *shadow_info);
int divert_pkdecrypt (GCRY_SEXP *s_plain, GCRY_SEXP s_cipher,
const char *shadow_info);
/*-- call-scd.c --*/
int agent_learn_card (void);
#endif /*AGENT_H*/

View File

@ -462,6 +462,22 @@ cmd_clear_passphrase (ASSUAN_CONTEXT ctx, char *line)
return 0;
}
/* LEARN
Learn something about the currently inserted smartcard
*/
static int
cmd_learn (ASSUAN_CONTEXT ctx, char *line)
{
int rc;
rc = agent_learn_card ();
if (rc)
log_error ("agent_learn_card failed: %s\n", gnupg_strerror (rc));
return map_to_assuan_status (rc);
}
/* Tell the assuan library about our commands */
@ -485,6 +501,7 @@ register_commands (ASSUAN_CONTEXT ctx)
{ "CLEAR_PASSPHRASE",0, cmd_clear_passphrase },
{ "LISTTRUSTED", 0, cmd_listtrusted },
{ "MARKTRUSTED", 0, cmd_marktrusted },
{ "LEARN", 0, cmd_learn },
{ "", ASSUAN_CMD_INPUT, NULL },
{ "", ASSUAN_CMD_OUTPUT, NULL },
{ NULL }

View File

@ -95,9 +95,12 @@ unprotect (unsigned char **keybuf, const unsigned char *grip)
/* Return the secret key as an S-Exp after locating it using the grip.
Returns NULL if key is not available. */
Returns NULL if key is not available or the operation should be
diverted to a token. In the latter case shadow_info will point to
an allocated S-Expression with the shadow_info part from the
file. */
GCRY_SEXP
agent_key_from_file (const unsigned char *grip)
agent_key_from_file (const unsigned char *grip, unsigned char **shadow_info)
{
int i, rc;
char *fname;
@ -108,6 +111,9 @@ agent_key_from_file (const unsigned char *grip)
GCRY_SEXP s_skey;
char hexgrip[40+4+1];
if (shadow_info)
*shadow_info = NULL;
for (i=0; i < 20; i++)
sprintf (hexgrip+2*i, "%02X", grip[i]);
strcpy (hexgrip+40, ".key");
@ -173,8 +179,30 @@ agent_key_from_file (const unsigned char *grip)
gnupg_strerror (rc));
break;
case PRIVATE_KEY_SHADOWED:
log_error ("shadowed private keys are not yet supported\n");
rc = GNUPG_Not_Implemented;
if (shadow_info)
{
const unsigned char *s;
size_t n;
rc = agent_get_shadow_info (buf, &s);
if (!rc)
{
n = gcry_sexp_canon_len (s, 0, NULL,NULL);
assert (n);
*shadow_info = xtrymalloc (n);
if (!*shadow_info)
rc = GNUPG_Out_Of_Core;
else
{
memcpy (*shadow_info, s, n);
rc = 0;
}
}
if (rc)
log_error ("get_shadow_info failed: %s\n", gnupg_strerror (rc));
}
rc = -1; /* ugly interface: we return an error but keep a value
in shadow_info. */
break;
default:
log_error ("invalid private key format\n");

View File

@ -69,6 +69,7 @@ enum cmd_and_opt_values
oBatch,
oPinentryProgram,
oScdaemonProgram,
oDefCacheTTL,
aTest };
@ -92,7 +93,8 @@ static ARGPARSE_OPTS opts[] = {
{ oNoGrab, "no-grab" ,0, N_("do not grab keyboard and mouse")},
{ oLogFile, "log-file" ,2, N_("use a log file for the server")},
{ oPinentryProgram, "pinentry-program", 2 , "path of PIN Entry program" },
{ oPinentryProgram, "pinentry-program", 2 , "path to PIN Entry program" },
{ oScdaemonProgram, "scdaemon-program", 2 , "path to SCdaemon program" },
{ oDefCacheTTL, "default-cache-ttl", 4,
"|N|expire cached PINs after N seconds"},
@ -372,6 +374,7 @@ main (int argc, char **argv )
case oServer: pipe_server = 1; break;
case oPinentryProgram: opt.pinentry_program = pargs.r.ret_str; break;
case oScdaemonProgram: opt.scdaemon_program = pargs.r.ret_str; break;
case oDefCacheTTL: opt.def_cache_ttl = pargs.r.ret_ulong; break;
default : pargs.err = configfp? 1:2; break;

View File

@ -4,7 +4,8 @@ keyformat.txt (wk 2001-12-18)
Some notes on the format of the secret keys used with gpg-agent.
Location of keys
================
The secret keys[1] are stored on a per file basis in a directory below
the ~/.gnupg home directory. This directory is named
@ -13,9 +14,12 @@ the ~/.gnupg home directory. This directory is named
and should have permissions 700.
The secret keys are stored in files with a name matching the
hexadecimal representation of the keygrip[2]. The content of the file
is an S-Expression like the ones used with Libgcrypt. Here is an
example of an unprotected file:
hexadecimal representation of the keygrip[2].
Unprotected Private Key Format
==============================
The content of the file is an S-Expression like the ones used with
Libgcrypt. Here is an example of an unprotected file:
(private-key
(rsa
@ -47,8 +51,9 @@ keys is in canonical representation[3]:
)
This describes an unprotected key; a protected key is like this:
Protected Private Key Format
==============================
A protected key is like this:
(protected-private-key
(rsa
@ -116,12 +121,35 @@ the stored one - If they don't match the integrity of the key is not
given.
Shadowed Private Key Format
============================
To keep track of keys stored on IC cards we use a third format for
private kyes which are called shadow keys as they are only a reference
to keys stored on a token:
(shadowed-private-key
(rsa
(n #00e0ce9..[some bytes not shown]..51#)
(e #010001#)
(shadowed protocol (info))
)
(uri http://foo.bar x-foo:whatever_you_want)
)
The currently used protocol is "ti-v1" (token info version 1). The
second list with the information has this layout:
(card_serial_number id_string_of_key)
More items may be added to the list.
Notes:
======
[1] I usually use the terms private and secret key exchangeable but prefer the
term secret key because it can be visually be better distinguished
from the term public key.

View File

@ -39,6 +39,7 @@ agent_pkdecrypt (CTRL ctrl, const char *ciphertext, size_t ciphertextlen,
FILE *outfp)
{
GCRY_SEXP s_skey = NULL, s_cipher = NULL, s_plain = NULL;
unsigned char *shadow_info = NULL;
int rc;
char *buf = NULL;
size_t len;
@ -63,27 +64,38 @@ agent_pkdecrypt (CTRL ctrl, const char *ciphertext, size_t ciphertextlen,
log_printhex ("keygrip:", ctrl->keygrip, 20);
log_printhex ("cipher: ", ciphertext, ciphertextlen);
}
s_skey = agent_key_from_file (ctrl->keygrip);
if (!s_skey)
s_skey = agent_key_from_file (ctrl->keygrip, &shadow_info);
if (!s_skey && !shadow_info)
{
log_error ("failed to read the secret key\n");
rc = seterr (No_Secret_Key);
goto leave;
}
if (DBG_CRYPTO)
{
log_debug ("skey: ");
gcry_sexp_dump (s_skey);
if (!s_skey)
{ /* divert operation to the smartcard */
rc = divert_pkdecrypt (&s_plain, s_cipher, shadow_info);
if (rc)
{
log_error ("smartcard decryption failed: %s\n", gnupg_strerror (rc));
goto leave;
}
}
else
{ /* no smartcard, but a private key */
if (DBG_CRYPTO)
{
log_debug ("skey: ");
gcry_sexp_dump (s_skey);
}
rc = gcry_pk_decrypt (&s_plain, s_cipher, s_skey);
if (rc)
{
log_error ("decryption failed: %s\n", gcry_strerror (rc));
rc = map_gcry_err (rc);
goto leave;
}
rc = gcry_pk_decrypt (&s_plain, s_cipher, s_skey);
if (rc)
{
log_error ("decryption failed: %s\n", gcry_strerror (rc));
rc = map_gcry_err (rc);
goto leave;
}
}
if (DBG_CRYPTO)
{
@ -106,6 +118,7 @@ agent_pkdecrypt (CTRL ctrl, const char *ciphertext, size_t ciphertextlen,
gcry_sexp_release (s_plain);
gcry_sexp_release (s_cipher);
xfree (buf);
xfree (shadow_info);
return rc;
}

View File

@ -90,6 +90,7 @@ agent_pksign (CTRL ctrl, FILE *outfp)
{
GCRY_SEXP s_skey = NULL, s_hash = NULL, s_sig = NULL;
GCRY_MPI frame = NULL;
unsigned char *shadow_info = NULL;
int rc;
char *buf = NULL;
size_t len;
@ -97,39 +98,50 @@ agent_pksign (CTRL ctrl, FILE *outfp)
if (!ctrl->have_keygrip)
return seterr (No_Secret_Key);
s_skey = agent_key_from_file (ctrl->keygrip);
if (!s_skey)
s_skey = agent_key_from_file (ctrl->keygrip, &shadow_info);
if (!s_skey && !shadow_info)
{
log_error ("failed to read the secret key\n");
rc = seterr (No_Secret_Key);
goto leave;
}
/* put the hash into a sexp */
/* put the hash into a sexp FIXME: this belongs into libgcrypt/divert-scd.c*/
rc = do_encode_md (ctrl->digest.value,
ctrl->digest.valuelen,
ctrl->digest.algo,
gcry_pk_get_nbits (s_skey),
&frame);
if (rc)
goto leave;
goto leave;
if ( gcry_sexp_build (&s_hash, NULL, "%m", frame) )
BUG ();
if (DBG_CRYPTO)
{
log_debug ("skey: ");
gcry_sexp_dump (s_skey);
if (!s_skey)
{ /* divert operation to the smartcard */
rc = divert_pksign (&s_sig, s_hash, shadow_info);
if (rc)
{
log_error ("smartcard signing failed: %s\n", gnupg_strerror (rc));
goto leave;
}
}
else
{ /* no smartcard, but a private key */
if (DBG_CRYPTO)
{
log_debug ("skey: ");
gcry_sexp_dump (s_skey);
}
/* sign */
rc = gcry_pk_sign (&s_sig, s_hash, s_skey);
if (rc)
{
log_error ("signing failed: %s\n", gcry_strerror (rc));
rc = map_gcry_err (rc);
goto leave;
/* sign */
rc = gcry_pk_sign (&s_sig, s_hash, s_skey);
if (rc)
{
log_error ("signing failed: %s\n", gcry_strerror (rc));
rc = map_gcry_err (rc);
goto leave;
}
}
if (DBG_CRYPTO)
@ -138,7 +150,6 @@ agent_pksign (CTRL ctrl, FILE *outfp)
gcry_sexp_dump (s_sig);
}
len = gcry_sexp_sprint (s_sig, GCRYSEXP_FMT_CANON, NULL, 0);
assert (len);
buf = xmalloc (len);
@ -156,6 +167,7 @@ agent_pksign (CTRL ctrl, FILE *outfp)
gcry_sexp_release (s_sig);
gcry_mpi_release (frame);
xfree (buf);
xfree (shadow_info);
return rc;
}

View File

@ -49,6 +49,8 @@ enum cmd_and_opt_values
oUnprotect = 'u',
oNoVerbose = 500,
oShadow,
oShowShadowInfo,
aTest };
@ -65,6 +67,8 @@ static ARGPARSE_OPTS opts[] = {
{ oPassphrase, "passphrase", 2, "|STRING| Use passphrase STRING" },
{ oProtect, "protect", 256, "protect a private key"},
{ oUnprotect, "unprotect", 256, "unprotect a private key"},
{ oShadow, "shadow", 256, "create a shadow entry for a priblic key"},
{ oShowShadowInfo, "show-shadow-info", 256, "return the shadow info"},
{0}
};
@ -125,8 +129,7 @@ my_gcry_logger (void *dummy, int level, const char *fmt, va_list arg_ptr)
case GCRY_LOG_FATAL:level = JNLIB_LOG_FATAL; break;
case GCRY_LOG_BUG: level = JNLIB_LOG_BUG; break;
case GCRY_LOG_DEBUG:level = JNLIB_LOG_DEBUG; break;
default: level = JNLIB_LOG_ERROR; break;
}
default: level = JNLIB_LOG_ERROR; break; }
log_logv (level, fmt, arg_ptr);
}
@ -291,6 +294,79 @@ read_and_unprotect (const char *fname)
}
static void
read_and_shadow (const char *fname)
{
int rc;
unsigned char *key;
unsigned char *result;
size_t resultlen;
key = read_key (fname);
if (!key)
return;
rc = agent_shadow_key (key, "(8:313233342:43)", &result);
xfree (key);
if (rc)
{
log_error ("shadowing the key failed: %s\n", gnupg_strerror (rc));
return;
}
resultlen = gcry_sexp_canon_len (result, 0, NULL,NULL);
assert (resultlen);
if (opt_armor)
{
char *p = make_advanced (result, resultlen);
xfree (result);
if (!p)
return;
result = p;
resultlen = strlen (p);
}
fwrite (result, resultlen, 1, stdout);
xfree (result);
}
static void
show_shadow_info (const char *fname)
{
int rc;
unsigned char *key;
const unsigned char *info;
size_t infolen;
key = read_key (fname);
if (!key)
return;
rc = agent_get_shadow_info (key, &info);
xfree (key);
if (rc)
{
log_error ("get_shadow_info failed: %s\n", gnupg_strerror (rc));
return;
}
infolen = gcry_sexp_canon_len (info, 0, NULL,NULL);
assert (infolen);
if (opt_armor)
{
char *p = make_advanced (info, infolen);
if (!p)
return;
fwrite (p, strlen (p), 1, stdout);
xfree (p);
}
else
fwrite (info, infolen, 1, stdout);
}
int
main (int argc, char **argv )
@ -325,6 +401,8 @@ main (int argc, char **argv )
case oProtect: cmd = oProtect; break;
case oUnprotect: cmd = oUnprotect; break;
case oShadow: cmd = oShadow; break;
case oShowShadowInfo: cmd = oShowShadowInfo; break;
case oPassphrase: passphrase = pargs.r.ret_str; break;
@ -341,6 +419,10 @@ main (int argc, char **argv )
read_and_protect (*argv);
else if (cmd == oUnprotect)
read_and_unprotect (*argv);
else if (cmd == oShadow)
read_and_shadow (*argv);
else if (cmd == oShowShadowInfo)
show_shadow_info (*argv);
else
log_info ("no action requested\n");

View File

@ -780,7 +780,7 @@ agent_unprotect (const unsigned char *protectedkey, const char *passphrase,
PRIVATE_KEY_UNKNOWN if we can't figure out the type (this is the
value 0), PRIVATE_KEY_CLEAR for an unprotected private key.
PRIVATE_KEY_PROTECTED for an protected private key or
PRIVATE_KEY_SHADOWED for a sub key where the secret parts are store
PRIVATE_KEY_SHADOWED for a sub key where the secret parts are stored
elsewhere. */
int
agent_private_key_type (const unsigned char *privatekey)
@ -886,3 +886,154 @@ hash_passphrase (const char *passphrase, int hashalgo,
}
/* Create a shadow key from a public key. We use the shadow protocol
"ti-v1" and insert the S-expressionn SHADOW_INFO. The resulting
S-expression is returned in an allocated buffer RESULT will point
to. The input parameters are expected to be valid canonilized
S-expressions */
int
agent_shadow_key (const unsigned char *pubkey,
const unsigned char *shadow_info,
unsigned char **result)
{
const unsigned char *s;
const unsigned char *point;
size_t n;
int depth = 0;
unsigned char *p;
size_t pubkey_len = gcry_sexp_canon_len (pubkey, 0, NULL,NULL);
size_t shadow_info_len = gcry_sexp_canon_len (shadow_info, 0, NULL,NULL);
if (!pubkey_len || !shadow_info_len)
return GNUPG_Invalid_Value;
s = pubkey;
if (*s != '(')
return GNUPG_Invalid_Sexp;
depth++;
s++;
n = snext (&s);
if (!n)
return GNUPG_Invalid_Sexp;
if (!smatch (&s, n, "public-key"))
return GNUPG_Unknown_Sexp;
if (*s != '(')
return GNUPG_Unknown_Sexp;
depth++;
s++;
n = snext (&s);
if (!n)
return GNUPG_Invalid_Sexp;
s += n; /* skip over the algorithm name */
while (*s != ')')
{
if (*s != '(')
return GNUPG_Invalid_Sexp;
depth++;
s++;
n = snext (&s);
if (!n)
return GNUPG_Invalid_Sexp;
s += n;
n = snext (&s);
if (!n)
return GNUPG_Invalid_Sexp;
s +=n; /* skip value */
if (*s != ')')
return GNUPG_Invalid_Sexp;
depth--;
s++;
}
point = s; /* insert right before the point */
depth--;
s++;
assert (depth == 1);
/* calculate required length by taking in account: the "shadowed-"
prefix, the "shadowed", "t1-v1" as well as some parenthesis */
n = 12 + pubkey_len + 1 + 3+8 + 2+5 + shadow_info_len + 1;
*result = p = xtrymalloc (n);
if (!p)
return GNUPG_Out_Of_Core;
p = stpcpy (p, "(20:shadowed-private-key");
/* (10:public-key ...)*/
memcpy (p, pubkey+14, point - (pubkey+14));
p += point - (pubkey+14);
p = stpcpy (p, "(8:shadowed5:t1-v1");
memcpy (p, shadow_info, shadow_info_len);
p += shadow_info_len;
*p++ = ')';
memcpy (p, point, pubkey_len - (point - pubkey));
p += pubkey_len - (point - pubkey);
return 0;
}
/* Parse a canonical encoded shadowed key and return a pointer to the
inner list with the shadow_info */
int
agent_get_shadow_info (const unsigned char *shadowkey,
unsigned char const **shadow_info)
{
const unsigned char *s;
size_t n;
int depth = 0;
s = shadowkey;
if (*s != '(')
return GNUPG_Invalid_Sexp;
depth++;
s++;
n = snext (&s);
if (!n)
return GNUPG_Invalid_Sexp;
if (!smatch (&s, n, "shadowed-private-key"))
return GNUPG_Unknown_Sexp;
if (*s != '(')
return GNUPG_Unknown_Sexp;
depth++;
s++;
n = snext (&s);
if (!n)
return GNUPG_Invalid_Sexp;
s += n; /* skip over the algorithm name */
for (;;)
{
if (*s == ')')
return GNUPG_Unknown_Sexp;
if (*s != '(')
return GNUPG_Invalid_Sexp;
depth++;
s++;
n = snext (&s);
if (!n)
return GNUPG_Invalid_Sexp;
if (smatch (&s, n, "shadowed"))
break;
s += n;
n = snext (&s);
if (!n)
return GNUPG_Invalid_Sexp;
s +=n; /* skip value */
if (*s != ')')
return GNUPG_Invalid_Sexp;
depth--;
s++;
}
/* found the shadowed list, s points to the protocol */
n = snext (&s);
if (!n)
return GNUPG_Invalid_Sexp;
if (smatch (&s, n, "t1-v1"))
{
if (*s != '(')
return GNUPG_Invalid_Sexp;
*shadow_info = s;
}
else
return GNUPG_Unsupported_Protocol;
return 0;
}

View File

@ -97,7 +97,7 @@ start_pinentry (void)
rc = assuan_transact (entry_ctx,
opt.no_grab? "OPTION no-grab":"OPTION grab",
NULL, NULL, NULL, NULL);
NULL, NULL, NULL, NULL, NULL, NULL);
if (rc)
return map_assuan_err (rc);
@ -160,14 +160,14 @@ agent_askpin (const char *desc_text, const char *start_err_text,
snprintf (line, DIM(line)-1, "SETDESC %s", desc_text);
line[DIM(line)-1] = 0;
rc = assuan_transact (entry_ctx, line, NULL, NULL, NULL, NULL);
rc = assuan_transact (entry_ctx, line, NULL, NULL, NULL, NULL, NULL, NULL);
if (rc)
return map_assuan_err (rc);
rc = assuan_transact (entry_ctx,
pininfo->min_digits? "SETPROMPT PIN:"
: "SETPROMPT Passphrase:",
NULL, NULL, NULL, NULL);
NULL, NULL, NULL, NULL, NULL, NULL);
if (rc)
return map_assuan_err (rc);
@ -189,13 +189,13 @@ agent_askpin (const char *desc_text, const char *start_err_text,
snprintf (line, DIM(line)-1, "SETERROR %s (try %d of %d)",
errtext, pininfo->failed_tries+1, pininfo->max_tries);
line[DIM(line)-1] = 0;
rc = assuan_transact (entry_ctx, line, NULL, NULL, NULL, NULL);
rc = assuan_transact (entry_ctx, line, NULL, NULL, NULL, NULL, NULL, NULL);
if (rc)
return map_assuan_err (rc);
errtext = NULL;
}
rc = assuan_transact (entry_ctx, "GETPIN", getpin_cb, &parm, NULL, NULL);
rc = assuan_transact (entry_ctx, "GETPIN", getpin_cb, &parm, NULL, NULL, NULL, NULL);
if (rc == ASSUAN_Too_Much_Data)
errtext = pininfo->min_digits? trans ("PIN too long")
: trans ("Passphrase too long");
@ -248,13 +248,13 @@ agent_get_passphrase (char **retpass, const char *desc, const char *prompt,
else
snprintf (line, DIM(line)-1, "RESET");
line[DIM(line)-1] = 0;
rc = assuan_transact (entry_ctx, line, NULL, NULL, NULL, NULL);
rc = assuan_transact (entry_ctx, line, NULL, NULL, NULL, NULL, NULL, NULL);
if (rc)
return map_assuan_err (rc);
snprintf (line, DIM(line)-1, "SETPROMPT %s", prompt? prompt : "Passphrase");
line[DIM(line)-1] = 0;
rc = assuan_transact (entry_ctx, line, NULL, NULL, NULL, NULL);
rc = assuan_transact (entry_ctx, line, NULL, NULL, NULL, NULL, NULL, NULL);
if (rc)
return map_assuan_err (rc);
@ -262,7 +262,7 @@ agent_get_passphrase (char **retpass, const char *desc, const char *prompt,
{
snprintf (line, DIM(line)-1, "SETERROR %s", errtext);
line[DIM(line)-1] = 0;
rc = assuan_transact (entry_ctx, line, NULL, NULL, NULL, NULL);
rc = assuan_transact (entry_ctx, line, NULL, NULL, NULL, NULL, NULL, NULL);
if (rc)
return map_assuan_err (rc);
}
@ -274,7 +274,7 @@ agent_get_passphrase (char **retpass, const char *desc, const char *prompt,
return seterr (Out_Of_Core);
assuan_begin_confidential (entry_ctx);
rc = assuan_transact (entry_ctx, "GETPIN", getpin_cb, &parm, NULL, NULL);
rc = assuan_transact (entry_ctx, "GETPIN", getpin_cb, &parm, NULL, NULL, NULL, NULL);
if (rc)
{
xfree (parm.buffer);
@ -317,7 +317,7 @@ agent_get_confirmation (const char *desc, const char *prompt)
else
snprintf (line, DIM(line)-1, "RESET");
line[DIM(line)-1] = 0;
rc = assuan_transact (entry_ctx, line, NULL, NULL, NULL, NULL);
rc = assuan_transact (entry_ctx, line, NULL, NULL, NULL, NULL, NULL, NULL);
if (rc)
return map_assuan_err (rc);
@ -325,12 +325,12 @@ agent_get_confirmation (const char *desc, const char *prompt)
{
snprintf (line, DIM(line)-1, "SETPROMPT %s", prompt);
line[DIM(line)-1] = 0;
rc = assuan_transact (entry_ctx, line, NULL, NULL, NULL, NULL);
rc = assuan_transact (entry_ctx, line, NULL, NULL, NULL, NULL, NULL, NULL);
if (rc)
return map_assuan_err (rc);
}
rc = assuan_transact (entry_ctx, "CONFIRM", NULL, NULL, NULL, NULL);
rc = assuan_transact (entry_ctx, "CONFIRM", NULL, NULL, NULL, NULL, NULL, NULL);
return map_assuan_err (rc);
}

View File

@ -94,6 +94,11 @@ enum {
GNUPG_Not_Confirmed = 65,
GNUPG_Configuration_Error = 66,
GNUPG_No_Policy_Match = 67,
GNUPG_Invalid_Index = 68,
GNUPG_Invalid_Id = 69,
GNUPG_No_Scdaemon = 70,
GNUPG_Scdaemon_Error = 71,
GNUPG_Unsupported_Protocol = 72,
};
/* Status codes - fixme: should go into another file */

View File

@ -44,6 +44,7 @@ map_ksba_err (int err)
break;
case KSBA_Unsupported_Algorithm: err = GNUPG_Unsupported_Algorithm; break;
case KSBA_Invalid_Index: err = GNUPG_Invalid_Index; break;
default:
err = seterr (General_Error);
@ -136,6 +137,7 @@ map_assuan_err (int err)
break;
case ASSUAN_Canceled: err = GNUPG_Canceled; break;
case ASSUAN_Invalid_Index: err = GNUPG_Invalid_Index; break;
case ASSUAN_Not_Implemented: err = GNUPG_Not_Implemented; break;
case ASSUAN_Server_Fault: err = GNUPG_Assuan_Server_Fault; break;
@ -153,6 +155,7 @@ map_assuan_err (int err)
case ASSUAN_No_PKCS15_App: err = GNUPG_No_PKCS15_App; break;
case ASSUAN_Card_Not_Present: err= GNUPG_Card_Not_Present; break;
case ASSUAN_Not_Confirmed: err = GNUPG_Not_Confirmed; break;
case ASSUAN_Invalid_Id: err = GNUPG_Invalid_Id; break;
default:
err = err < 100? GNUPG_Assuan_Server_Fault : GNUPG_Assuan_Error;
@ -182,6 +185,7 @@ map_to_assuan_status (int rc)
case GNUPG_Invalid_Name: rc = ASSUAN_Invalid_Name; break;
case GNUPG_Not_Trusted: rc = ASSUAN_Not_Trusted; break;
case GNUPG_Canceled: rc = ASSUAN_Canceled; break;
case GNUPG_Invalid_Index: rc = ASSUAN_Invalid_Index; break;
case GNUPG_Card_Error:
case GNUPG_Card_Reset:
@ -194,7 +198,7 @@ map_to_assuan_status (int rc)
case GNUPG_Invalid_Card: rc = ASSUAN_Invalid_Card; break;
case GNUPG_No_PKCS15_App: rc = ASSUAN_No_PKCS15_App; break;
case GNUPG_Not_Confirmed: rc = ASSUAN_Not_Confirmed; break;
case GNUPG_Invalid_Id: rc = ASSUAN_Invalid_Id; break;
case GNUPG_Bad_PIN:
case GNUPG_Bad_Passphrase:

View File

@ -20,7 +20,8 @@
bin_PROGRAMS = scdaemon
AM_CPPFLAGS = -I$(top_srcdir)/common $(LIBOPENSC_CFLAGS) $(LIBGCRYPT_CFLAGS)
AM_CPPFLAGS = -I$(top_srcdir)/common $(LIBOPENSC_CFLAGS) $(LIBGCRYPT_CFLAGS) \
$(LIBKSBA_CFLAGS)
LDFLAGS = @LDFLAGS@
scdaemon_SOURCES = \
@ -29,7 +30,8 @@ scdaemon_SOURCES = \
scdaemon_LDADD = ../jnlib/libjnlib.a ../assuan/libassuan.a \
../common/libcommon.a $(LIBOPENSC_LIBS) $(LIBGCRYPT_LIBS)
../common/libcommon.a \
$(LIBOPENSC_LIBS) $(LIBGCRYPT_LIBS) $(LIBKSBA_LIBS)

View File

@ -26,6 +26,7 @@
#include <time.h>
#include <opensc-pkcs15.h>
#include <ksba.h>
#include "scdaemon.h"
@ -114,13 +115,13 @@ card_open (CARD *rcard)
}
card->ctx->error_file = log_get_stream ();
card->ctx->debug_file = log_get_stream ();
if (sc_detect_card (card->ctx, card->reader) != 1)
if (sc_detect_card_presence (card->ctx->reader[card->reader], 0) != 1)
{
rc = GNUPG_Card_Not_Present;
goto leave;
}
rc = sc_connect_card (card->ctx, card->reader, &card->scard);
rc = sc_connect_card (card->ctx->reader[card->reader], 0, &card->scard);
if (rc)
{
log_error ("failed to connect card in reader %d: %s\n",
@ -175,7 +176,7 @@ card_close (CARD card)
if (card->scard)
{
sc_unlock (card->scard);
sc_disconnect_card (card->scard);
sc_disconnect_card (card->scard, 0);
card->scard = NULL;
}
if (card->ctx)
@ -219,3 +220,204 @@ card_get_serial_and_stamp (CARD card, char **serial, time_t *stamp)
return GNUPG_Out_Of_Core;
return 0;
}
/* Get the keygrip from CERT, return 0 on success */
static int
get_keygrip (KsbaCert cert, unsigned char *array)
{
GCRY_SEXP s_pkey;
int rc;
KsbaSexp p;
size_t n;
p = ksba_cert_get_public_key (cert);
if (!p)
return -1; /* oops */
n = gcry_sexp_canon_len (p, 0, NULL, NULL);
if (!n)
return -1; /* libksba did not return a proper S-expression */
rc = gcry_sexp_sscan ( &s_pkey, NULL, p, n);
xfree (p);
if (rc)
return -1; /* can't parse that S-expression */
array = gcry_pk_get_keygrip (s_pkey, array);
gcry_sexp_release (s_pkey);
if (!array)
return -1; /* failed to calculate the keygrip */
return 0;
}
/* Enumerate all keypairs on the card and return the Keygrip as well
as the internal identification of the key. KEYGRIP must be a
caller provided buffer with a size of 20 bytes which will receive
the KEYGRIP of the keypair. If KEYID is not NULL, it returns the
ID field of the key in allocated memory, NKEYID will then receive
the length of it. The function returns -1 when all keys have been
enumerated. Note that the error GNUPG_Missing_Certificate may be
returned if there is just the private key but no public key (ie.e a
certificate) available. Applications might want to continue
enumerating after this error.*/
int
card_enum_keypairs (CARD card, int idx,
unsigned char *keygrip,
unsigned char **keyid, size_t *nkeyid)
{
int rc;
KsbaError krc;
struct sc_pkcs15_prkey_info *pinfo;
struct sc_pkcs15_cert_info *certinfo;
struct sc_pkcs15_cert *certder;
KsbaCert cert;
if (keyid)
*keyid = NULL;
if (nkeyid)
*nkeyid = 0;
if (!card || !keygrip || !card->p15card)
return GNUPG_Invalid_Value;
if (idx < 0)
return GNUPG_Invalid_Index;
rc = sc_pkcs15_enum_private_keys (card->p15card);
if (rc < 0)
{
log_error ("sc_pkcs15_enum_private_keys failed: %s\n", sc_strerror (rc));
return GNUPG_Card_Error;
}
if ( idx >= card->p15card->prkey_count)
return -1;
pinfo = card->p15card->prkey_info + idx;
/* now we need to read the certificate so that we can calculate the
keygrip */
rc = sc_pkcs15_find_cert_by_id (card->p15card, &pinfo->id, &certinfo);
if (rc)
{
log_info ("certificate for private key %d not found: %s\n",
idx, sc_strerror (rc));
/* but we should return the ID anyway */
if (keyid)
{
*keyid = xtrymalloc (pinfo->id.len);
if (!*keyid)
return GNUPG_Out_Of_Core;
memcpy (*keyid, pinfo->id.value, pinfo->id.len);
}
if (nkeyid)
*nkeyid = pinfo->id.len;
return GNUPG_Missing_Certificate;
}
rc = sc_pkcs15_read_certificate (card->p15card, certinfo, &certder);
if (rc)
{
log_info ("failed to read certificate for private key %d: %s\n",
idx, sc_strerror (rc));
return GNUPG_Card_Error;
}
cert = ksba_cert_new ();
if (!cert)
{
sc_pkcs15_free_certificate (certder);
return GNUPG_Out_Of_Core;
}
krc = ksba_cert_init_from_mem (cert, certder->data, certder->data_len);
sc_pkcs15_free_certificate (certder);
if (krc)
{
log_error ("failed to parse the certificate for private key %d: %s\n",
idx, ksba_strerror (krc));
ksba_cert_release (cert);
return GNUPG_Card_Error;
}
if (get_keygrip (cert, keygrip))
{
log_error ("failed to calculate the keygrip of private key %d\n", idx);
ksba_cert_release (cert);
return GNUPG_Card_Error;
}
ksba_cert_release (cert);
/* return the iD */
if (keyid)
{
*keyid = xtrymalloc (pinfo->id.len);
if (!*keyid)
return GNUPG_Out_Of_Core;
memcpy (*keyid, pinfo->id.value, pinfo->id.len);
}
if (nkeyid)
*nkeyid = pinfo->id.len;
return 0;
}
/* Read the certificate identified by CERTIDSTR which is the
hexadecimal encoded ID of the certificate, prefixed with the string
"3F005015.". The certificate is return in DER encoded form in CERT
and NCERT. */
int
card_read_cert (CARD card, const char *certidstr,
unsigned char **cert, size_t *ncert)
{
struct sc_pkcs15_id certid;
struct sc_pkcs15_cert_info *certinfo;
struct sc_pkcs15_cert *certder;
const char *s;
int rc, n;
if (!card || !certidstr || !card->p15card || !cert || !ncert)
return GNUPG_Invalid_Value;
/* For now we only support the standard DF */
if (strncmp (certidstr, "3F005015.", 9) )
return GNUPG_Invalid_Id;
for (s=certidstr+9, n=0; hexdigitp (s); s++, n++)
;
if (*s || (n&1))
return GNUPG_Invalid_Id; /* invalid or odd number of digits */
n /= 2;
if (!n || n > SC_PKCS15_MAX_ID_SIZE)
return GNUPG_Invalid_Id; /* empty or too large */
for (s=certidstr+9, n=0; *s; s += 2, n++)
certid.value[n] = xtoi_2 (s);
certid.len = n;
rc = sc_pkcs15_find_cert_by_id (card->p15card, &certid, &certinfo);
if (rc)
{
log_info ("certificate '%s' not found: %s\n",
certidstr, sc_strerror (rc));
return -1;
}
rc = sc_pkcs15_read_certificate (card->p15card, certinfo, &certder);
if (rc)
{
log_info ("failed to read certificate '%s': %s\n",
certidstr, sc_strerror (rc));
return GNUPG_Card_Error;
}
*cert = xtrymalloc (certder->data_len);
if (!*cert)
{
sc_pkcs15_free_certificate (certder);
return GNUPG_Out_Of_Core;
}
memcpy (*cert, certder->data, certder->data_len);
*ncert = certder->data_len;
sc_pkcs15_free_certificate (certder);
return 0;
}

View File

@ -75,14 +75,20 @@ option_handler (ASSUAN_CONTEXT ctx, const char *key, const char *value)
/* LEARN [--force]
Learn all useful information of the currently inserted card. When
used without the force options, the command might to an INQUIRE
used without the force options, the command might do an INQUIRE
like this:
INQUIRE KNOWNCARDP <hexstring_with_serialNumber> <timestamp>
The client should just send an "END" if the processing should go on
or a "CANCEL" to force the function to terminate with a Cancel
error message.
error message. The response of this command is a list of status
lines formatted as this:
S KEYPAIRINFO <hexstring_with_keygrip> <hexstring_with_id>
If there is no certificate yet stored on the card a single "X" is
returned as the keygrip.
*/
static int
@ -90,6 +96,7 @@ cmd_learn (ASSUAN_CONTEXT ctx, char *line)
{
CTRL ctrl = assuan_get_pointer (ctx);
int rc = 0;
int idx;
/* if this is the first command issued for a new card, open the card and
and create a context */
@ -104,40 +111,138 @@ cmd_learn (ASSUAN_CONTEXT ctx, char *line)
the card using a serial number and inquiring the client with
that. The client may choose to cancel the operation if he already
knows about this card */
if (!has_option (line, "--force"))
{
char *serial_and_stamp;
char *serial;
time_t stamp;
rc = card_get_serial_and_stamp (ctrl->card_ctx, &serial, &stamp);
if (rc)
return map_to_assuan_status (rc);
rc = asprintf (&serial_and_stamp, "%s %lu", serial, (unsigned long)stamp);
xfree (serial);
if (rc < 0)
return ASSUAN_Out_Of_Core;
rc = 0;
assuan_write_status (ctx, "SERIALNO", serial_and_stamp);
if (!has_option (line, "--force"))
{
char *command;
rc = asprintf (&command, "KNOWNCARDP %s", serial_and_stamp);
if (rc < 0)
{
free (serial_and_stamp);
return ASSUAN_Out_Of_Core;
}
rc = 0;
rc = assuan_inquire (ctx, command, NULL, NULL, 0);
free (command); /* (must use standard free here) */
if (rc)
{
if (rc != ASSUAN_Canceled)
log_error ("inquire KNOWNCARDP failed: %s\n",
assuan_strerror (rc));
free (serial_and_stamp);
return rc;
}
/* not canceled, so we have to proceeed */
}
free (serial_and_stamp);
}
for (idx=0; !rc; idx++)
{
char *serial;
time_t stamp;
char *command;
unsigned char keygrip[20];
unsigned char *keyid;
size_t nkeyid;
int no_cert = 0;
rc = card_get_serial_and_stamp (ctrl->card_ctx, &serial, &stamp);
if (rc)
return map_to_assuan_status (rc);
rc = asprintf (&command, "KNOWNCARDP %s %lu",
serial, (unsigned long)stamp);
xfree (serial);
if (rc < 0)
return ASSUAN_Out_Of_Core;
rc = 0;
rc = assuan_inquire (ctx, command, NULL, NULL, 0);
free (command); /* (must use standard free here) */
if (rc)
rc = card_enum_keypairs (ctrl->card_ctx, idx,
keygrip, &keyid, &nkeyid);
if (rc == GNUPG_Missing_Certificate && keyid)
{
if (rc != ASSUAN_Canceled)
log_error ("inquire KNOWNCARDP failed: %s\n",
assuan_strerror (rc));
return rc;
/* this does happen with an incomplete personalized
card; i.e. during the time we have stored the key on the
card but not stored the certificate; probably becuase it
has not yet been received back from the CA. Note that we
must release KEYID in this case. */
rc = 0;
no_cert = 1;
}
/* not canceled, so we have to proceeed */
if (!rc)
{
char *buf, *p;
buf = p = xtrymalloc (40+1+9+2*nkeyid+1);
if (!buf)
rc = GNUPG_Out_Of_Core;
else
{
int i;
if (no_cert)
*p++ = 'X';
else
{
for (i=0; i < 20; i++, p += 2)
sprintf (p, "%02X", keygrip[i]);
}
*p++ = ' ';
/* fixme: we need to get the pkcs-15 DF from the card function */
p = stpcpy (p, "3F005015.");
for (i=0; i < nkeyid; i++, p += 2)
sprintf (p, "%02X", keyid[i]);
*p = 0;
assuan_write_status (ctx, "KEYPAIRINFO", buf);
xfree (buf);
}
}
xfree (keyid);
}
if (rc == -1)
rc = 0;
return map_to_assuan_status (rc);
}
/* READCERT <hexified_certid>
*/
static int
cmd_readcert (ASSUAN_CONTEXT ctx, char *line)
{
CTRL ctrl = assuan_get_pointer (ctx);
int rc;
unsigned char *cert;
size_t ncert;
if (!ctrl->card_ctx)
{
rc = card_open (&ctrl->card_ctx);
if (rc)
return map_to_assuan_status (rc);
}
rc = card_read_cert (ctrl->card_ctx, line, &cert, &ncert);
if (rc)
{
log_error ("card_read_cert failed: %s\n", gnupg_strerror (rc));
}
if (!rc)
{
rc = assuan_send_data (ctx, cert, ncert);
xfree (cert);
if (rc)
return rc;
}
return map_to_assuan_status (rc);
}
/* Tell the assuan library about our commands */
@ -150,6 +255,7 @@ register_commands (ASSUAN_CONTEXT ctx)
int (*handler)(ASSUAN_CONTEXT, char *line);
} table[] = {
{ "LEARN", 0, cmd_learn },
{ "READCERT", 0, cmd_readcert },
{ "", ASSUAN_CMD_INPUT, NULL },
{ "", ASSUAN_CMD_OUTPUT, NULL },
{ NULL }

View File

@ -34,6 +34,7 @@
#include <unistd.h>
#include <signal.h>
#include <ksba.h>
#include <gcrypt.h>
#define JNLIB_NEED_LOG_LOGV
@ -242,6 +243,7 @@ main (int argc, char **argv )
"1.1.5", gcry_check_version (NULL) );
}
ksba_set_malloc_hooks (gcry_malloc, gcry_realloc, gcry_free);
assuan_set_malloc_hooks (gcry_malloc, gcry_realloc, gcry_free);
gcry_set_log_handler (my_gcry_logger, NULL);
gcry_control (GCRYCTL_USE_SECURE_RNDPOOL);

View File

@ -77,6 +77,11 @@ void scd_command_handler (int);
int card_open (CARD *rcard);
void card_close (CARD card);
int card_get_serial_and_stamp (CARD card, char **serial, time_t *stamp);
int card_enum_keypairs (CARD card, int idx,
unsigned char *keygrip,
unsigned char **keyid, size_t *nkeyid);
int card_read_cert (CARD card, const char *certidstr,
unsigned char **cert, size_t *ncert);

View File

@ -1,3 +1,8 @@
2002-02-27 Werner Koch <wk@gnupg.org>
* call-dirmngr.c, call-agent.c: Add 2 more arguments to all uses
of assuan_transact.
2002-02-25 Werner Koch <wk@gnupg.org>
* server.c (option_handler): Allow to use -2 for "send all certs

View File

@ -245,13 +245,13 @@ gpgsm_agent_pksign (const char *keygrip,
if (digestlen*2 + 50 > DIM(line))
return seterr (General_Error);
rc = assuan_transact (agent_ctx, "RESET", NULL, NULL, NULL, NULL);
rc = assuan_transact (agent_ctx, "RESET", NULL, NULL, NULL, NULL, NULL, NULL);
if (rc)
return map_assuan_err (rc);
snprintf (line, DIM(line)-1, "SIGKEY %s", keygrip);
line[DIM(line)-1] = 0;
rc = assuan_transact (agent_ctx, line, NULL, NULL, NULL, NULL);
rc = assuan_transact (agent_ctx, line, NULL, NULL, NULL, NULL, NULL, NULL);
if (rc)
return map_assuan_err (rc);
@ -259,13 +259,13 @@ gpgsm_agent_pksign (const char *keygrip,
p = line + strlen (line);
for (i=0; i < digestlen ; i++, p += 2 )
sprintf (p, "%02X", digest[i]);
rc = assuan_transact (agent_ctx, line, NULL, NULL, NULL, NULL);
rc = assuan_transact (agent_ctx, line, NULL, NULL, NULL, NULL, NULL, NULL);
if (rc)
return map_assuan_err (rc);
init_membuf (&data, 1024);
rc = assuan_transact (agent_ctx, "PKSIGN",
membuf_data_cb, &data, NULL, NULL);
membuf_data_cb, &data, NULL, NULL, NULL, NULL);
if (rc)
{
xfree (get_membuf (&data, &len));
@ -327,14 +327,14 @@ gpgsm_agent_pkdecrypt (const char *keygrip,
if (rc)
return rc;
rc = assuan_transact (agent_ctx, "RESET", NULL, NULL, NULL, NULL);
rc = assuan_transact (agent_ctx, "RESET", NULL, NULL, NULL, NULL, NULL, NULL);
if (rc)
return map_assuan_err (rc);
assert ( DIM(line) >= 50 );
snprintf (line, DIM(line)-1, "SETKEY %s", keygrip);
line[DIM(line)-1] = 0;
rc = assuan_transact (agent_ctx, line, NULL, NULL, NULL, NULL);
rc = assuan_transact (agent_ctx, line, NULL, NULL, NULL, NULL, NULL, NULL);
if (rc)
return map_assuan_err (rc);
@ -344,7 +344,7 @@ gpgsm_agent_pkdecrypt (const char *keygrip,
cipher_parm.ciphertextlen = ciphertextlen;
rc = assuan_transact (agent_ctx, "PKDECRYPT",
membuf_data_cb, &data,
inq_ciphertext_cb, &cipher_parm);
inq_ciphertext_cb, &cipher_parm, NULL, NULL);
if (rc)
{
xfree (get_membuf (&data, &len));
@ -403,7 +403,7 @@ gpgsm_agent_genkey (KsbaConstSexp keyparms, KsbaSexp *r_pubkey)
if (rc)
return rc;
rc = assuan_transact (agent_ctx, "RESET", NULL, NULL, NULL, NULL);
rc = assuan_transact (agent_ctx, "RESET", NULL, NULL, NULL, NULL, NULL, NULL);
if (rc)
return map_assuan_err (rc);
@ -415,7 +415,7 @@ gpgsm_agent_genkey (KsbaConstSexp keyparms, KsbaSexp *r_pubkey)
return GNUPG_Invalid_Value;
rc = assuan_transact (agent_ctx, "GENKEY",
membuf_data_cb, &data,
inq_genkey_parms, &gk_parm);
inq_genkey_parms, &gk_parm, NULL, NULL);
if (rc)
{
xfree (get_membuf (&data, &len));
@ -458,7 +458,7 @@ gpgsm_agent_istrusted (KsbaCert cert)
line[DIM(line)-1] = 0;
xfree (fpr);
rc = assuan_transact (agent_ctx, line, NULL, NULL, NULL, NULL);
rc = assuan_transact (agent_ctx, line, NULL, NULL, NULL, NULL, NULL, NULL);
return map_assuan_err (rc);
}
@ -492,7 +492,7 @@ gpgsm_agent_marktrusted (KsbaCert cert)
ksba_free (dn);
xfree (fpr);
rc = assuan_transact (agent_ctx, line, NULL, NULL, NULL, NULL);
rc = assuan_transact (agent_ctx, line, NULL, NULL, NULL, NULL, NULL, NULL);
return map_assuan_err (rc);
}
@ -516,7 +516,7 @@ gpgsm_agent_havekey (const char *hexkeygrip)
snprintf (line, DIM(line)-1, "HAVEKEY %s", hexkeygrip);
line[DIM(line)-1] = 0;
rc = assuan_transact (agent_ctx, line, NULL, NULL, NULL, NULL);
rc = assuan_transact (agent_ctx, line, NULL, NULL, NULL, NULL, NULL, NULL);
return map_assuan_err (rc);
}

View File

@ -227,7 +227,8 @@ gpgsm_dirmngr_isvalid (KsbaCert cert)
line[DIM(line)-1] = 0;
xfree (certid);
rc = assuan_transact (dirmngr_ctx, line, NULL, NULL, inq_certificate, &parm);
rc = assuan_transact (dirmngr_ctx, line, NULL, NULL,
inq_certificate, &parm, NULL, NULL);
return map_assuan_err (rc);
}

View File

@ -371,7 +371,7 @@ cmd_message (ASSUAN_CONTEXT ctx, char *line)
/* Note that the line contains a space separated list of pappern where
each pappern is percent escaped and spacesmay be replaced by
each pappern is percent escaped and spaces may be replaced by
'+'. */
static int
cmd_listkeys (ASSUAN_CONTEXT ctx, char *line)