diff --git a/agent/cvt-openpgp.c b/agent/cvt-openpgp.c
index bf05174fa..06cd1c840 100644
--- a/agent/cvt-openpgp.c
+++ b/agent/cvt-openpgp.c
@@ -1,1410 +1,1410 @@
/* cvt-openpgp.c - Convert an OpenPGP key to our internal format.
* Copyright (C) 1998-2002, 2006, 2009, 2010 Free Software Foundation, Inc.
* Copyright (C) 2013, 2014 Werner Koch
*
* This file is part of GnuPG.
*
* GnuPG is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* GnuPG is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see .
*/
#include
#include
#include
#include
#include
#include "agent.h"
#include "../common/i18n.h"
#include "cvt-openpgp.h"
#include "../common/host2net.h"
/* Helper to pass data via the callback to do_unprotect. */
struct try_do_unprotect_arg_s
{
int is_v4;
int is_protected;
int pubkey_algo;
const char *curve;
int protect_algo;
char *iv;
int ivlen;
int s2k_mode;
int s2k_algo;
byte *s2k_salt;
u32 s2k_count;
u16 desired_csum;
gcry_mpi_t *skey;
size_t skeysize;
int skeyidx;
gcry_sexp_t *r_key;
};
/* Compute the keygrip from the public key and store it at GRIP. */
static gpg_error_t
get_keygrip (int pubkey_algo, const char *curve, gcry_mpi_t *pkey,
unsigned char *grip)
{
gpg_error_t err;
gcry_sexp_t s_pkey = NULL;
switch (pubkey_algo)
{
case GCRY_PK_DSA:
err = gcry_sexp_build (&s_pkey, NULL,
"(public-key(dsa(p%m)(q%m)(g%m)(y%m)))",
pkey[0], pkey[1], pkey[2], pkey[3]);
break;
case GCRY_PK_ELG:
err = gcry_sexp_build (&s_pkey, NULL,
"(public-key(elg(p%m)(g%m)(y%m)))",
pkey[0], pkey[1], pkey[2]);
break;
case GCRY_PK_RSA:
err = gcry_sexp_build (&s_pkey, NULL,
"(public-key(rsa(n%m)(e%m)))", pkey[0], pkey[1]);
break;
case GCRY_PK_ECC:
if (!curve)
err = gpg_error (GPG_ERR_BAD_SECKEY);
else
{
const char *format;
if (!strcmp (curve, "Ed25519"))
format = "(public-key(ecc(curve %s)(flags eddsa)(q%m)))";
else if (!strcmp (curve, "Curve25519"))
format = "(public-key(ecc(curve %s)(flags djb-tweak)(q%m)))";
else
format = "(public-key(ecc(curve %s)(q%m)))";
err = gcry_sexp_build (&s_pkey, NULL, format, curve, pkey[0]);
}
break;
default:
err = gpg_error (GPG_ERR_PUBKEY_ALGO);
break;
}
if (!err && !gcry_pk_get_keygrip (s_pkey, grip))
err = gpg_error (GPG_ERR_INTERNAL);
gcry_sexp_release (s_pkey);
return err;
}
/* Convert a secret key given as algorithm id and an array of key
parameters into our s-expression based format. Note that
PUBKEY_ALGO has an gcrypt algorithm number. */
static gpg_error_t
convert_secret_key (gcry_sexp_t *r_key, int pubkey_algo, gcry_mpi_t *skey,
const char *curve)
{
gpg_error_t err;
gcry_sexp_t s_skey = NULL;
*r_key = NULL;
switch (pubkey_algo)
{
case GCRY_PK_DSA:
err = gcry_sexp_build (&s_skey, NULL,
"(private-key(dsa(p%m)(q%m)(g%m)(y%m)(x%m)))",
skey[0], skey[1], skey[2], skey[3], skey[4]);
break;
case GCRY_PK_ELG:
case GCRY_PK_ELG_E:
err = gcry_sexp_build (&s_skey, NULL,
"(private-key(elg(p%m)(g%m)(y%m)(x%m)))",
skey[0], skey[1], skey[2], skey[3]);
break;
case GCRY_PK_RSA:
case GCRY_PK_RSA_E:
case GCRY_PK_RSA_S:
err = gcry_sexp_build (&s_skey, NULL,
"(private-key(rsa(n%m)(e%m)(d%m)(p%m)(q%m)(u%m)))",
skey[0], skey[1], skey[2], skey[3], skey[4],
skey[5]);
break;
case GCRY_PK_ECC:
if (!curve)
err = gpg_error (GPG_ERR_BAD_SECKEY);
else
{
const char *format;
if (!strcmp (curve, "Ed25519"))
/* Do not store the OID as name but the real name and the
EdDSA flag. */
format = "(private-key(ecc(curve %s)(flags eddsa)(q%m)(d%m)))";
else if (!strcmp (curve, "Curve25519"))
format = "(private-key(ecc(curve %s)(flags djb-tweak)(q%m)(d%m)))";
else
format = "(private-key(ecc(curve %s)(q%m)(d%m)))";
err = gcry_sexp_build (&s_skey, NULL, format, curve, skey[0], skey[1]);
}
break;
default:
err = gpg_error (GPG_ERR_PUBKEY_ALGO);
break;
}
if (!err)
*r_key = s_skey;
return err;
}
/* Convert a secret key given as algorithm id, an array of key
parameters, and an S-expression of the original OpenPGP transfer
key into our s-expression based format. This is a variant of
convert_secret_key which is used for the openpgp-native protection
mode. Note that PUBKEY_ALGO has an gcrypt algorithm number. */
static gpg_error_t
convert_transfer_key (gcry_sexp_t *r_key, int pubkey_algo, gcry_mpi_t *skey,
const char *curve, gcry_sexp_t transfer_key)
{
gpg_error_t err;
gcry_sexp_t s_skey = NULL;
*r_key = NULL;
switch (pubkey_algo)
{
case GCRY_PK_DSA:
err = gcry_sexp_build
(&s_skey, NULL,
"(protected-private-key(dsa(p%m)(q%m)(g%m)(y%m)"
"(protected openpgp-native%S)))",
skey[0], skey[1], skey[2], skey[3], transfer_key);
break;
case GCRY_PK_ELG:
err = gcry_sexp_build
(&s_skey, NULL,
"(protected-private-key(elg(p%m)(g%m)(y%m)"
"(protected openpgp-native%S)))",
skey[0], skey[1], skey[2], transfer_key);
break;
case GCRY_PK_RSA:
err = gcry_sexp_build
(&s_skey, NULL,
"(protected-private-key(rsa(n%m)(e%m)"
"(protected openpgp-native%S)))",
skey[0], skey[1], transfer_key );
break;
case GCRY_PK_ECC:
if (!curve)
err = gpg_error (GPG_ERR_BAD_SECKEY);
else
{
const char *format;
if (!strcmp (curve, "Ed25519"))
/* Do not store the OID as name but the real name and the
EdDSA flag. */
format = "(protected-private-key(ecc(curve %s)(flags eddsa)(q%m)"
"(protected openpgp-native%S)))";
else if (!strcmp (curve, "Curve25519"))
format = "(protected-private-key(ecc(curve %s)(flags djb-tweak)(q%m)"
"(protected openpgp-native%S)))";
else
format = "(protected-private-key(ecc(curve %s)(q%m)"
"(protected openpgp-native%S)))";
err = gcry_sexp_build (&s_skey, NULL, format, curve, skey[0], transfer_key);
}
break;
default:
err = gpg_error (GPG_ERR_PUBKEY_ALGO);
break;
}
if (!err)
*r_key = s_skey;
return err;
}
/* Hash the passphrase and set the key. */
static gpg_error_t
hash_passphrase_and_set_key (const char *passphrase,
gcry_cipher_hd_t hd, int protect_algo,
int s2k_mode, int s2k_algo,
byte *s2k_salt, u32 s2k_count)
{
gpg_error_t err;
unsigned char *key;
size_t keylen;
keylen = gcry_cipher_get_algo_keylen (protect_algo);
if (!keylen)
return gpg_error (GPG_ERR_INTERNAL);
key = xtrymalloc_secure (keylen);
if (!key)
return gpg_error_from_syserror ();
err = s2k_hash_passphrase (passphrase,
s2k_algo, s2k_mode, s2k_salt, s2k_count,
key, keylen);
if (!err)
err = gcry_cipher_setkey (hd, key, keylen);
xfree (key);
return err;
}
static u16
checksum (const unsigned char *p, unsigned int n)
{
u16 a;
for (a=0; n; n-- )
a += *p++;
return a;
}
/* Return the number of expected key parameters. */
static void
get_npkey_nskey (int pubkey_algo, size_t *npkey, size_t *nskey)
{
switch (pubkey_algo)
{
case GCRY_PK_RSA: *npkey = 2; *nskey = 6; break;
case GCRY_PK_ELG: *npkey = 3; *nskey = 4; break;
case GCRY_PK_ELG_E: *npkey = 3; *nskey = 4; break;
case GCRY_PK_DSA: *npkey = 4; *nskey = 5; break;
case GCRY_PK_ECC: *npkey = 1; *nskey = 2; break;
default: *npkey = 0; *nskey = 0; break;
}
}
/* Helper for do_unprotect. PUBKEY_ALOGO is the gcrypt algo number.
On success R_NPKEY and R_NSKEY receive the number or parameters for
the algorithm PUBKEY_ALGO and R_SKEYLEN the used length of
SKEY. */
static int
prepare_unprotect (int pubkey_algo, gcry_mpi_t *skey, size_t skeysize,
int s2k_mode,
unsigned int *r_npkey, unsigned int *r_nskey,
unsigned int *r_skeylen)
{
size_t npkey, nskey, skeylen;
int i;
/* Count the actual number of MPIs is in the array and set the
remainder to NULL for easier processing later on. */
for (skeylen = 0; skey[skeylen]; skeylen++)
;
for (i=skeylen; i < skeysize; i++)
skey[i] = NULL;
/* Check some args. */
if (s2k_mode == 1001)
{
/* Stub key. */
log_info (_("secret key parts are not available\n"));
return gpg_error (GPG_ERR_UNUSABLE_SECKEY);
}
if (gcry_pk_test_algo (pubkey_algo))
{
log_info (_("public key algorithm %d (%s) is not supported\n"),
pubkey_algo, gcry_pk_algo_name (pubkey_algo));
return gpg_error (GPG_ERR_PUBKEY_ALGO);
}
/* Get properties of the public key algorithm and do some
consistency checks. Note that we need at least NPKEY+1 elements
in the SKEY array. */
get_npkey_nskey (pubkey_algo, &npkey, &nskey);
if (!npkey || !nskey || npkey >= nskey)
return gpg_error (GPG_ERR_INTERNAL);
if (skeylen <= npkey)
return gpg_error (GPG_ERR_MISSING_VALUE);
if (nskey+1 >= skeysize)
return gpg_error (GPG_ERR_BUFFER_TOO_SHORT);
/* Check that the public key parameters are all available and not
encrypted. */
for (i=0; i < npkey; i++)
{
if (!skey[i] || gcry_mpi_get_flag (skey[i], GCRYMPI_FLAG_USER1))
return gpg_error (GPG_ERR_BAD_SECKEY);
}
if (r_npkey)
*r_npkey = npkey;
if (r_nskey)
*r_nskey = nskey;
if (r_skeylen)
*r_skeylen = skeylen;
return 0;
}
/* Note that this function modifies SKEY. SKEYSIZE is the allocated
size of the array including the NULL item; this is used for a
bounds check. On success a converted key is stored at R_KEY. */
static int
do_unprotect (const char *passphrase,
int pkt_version, int pubkey_algo, int is_protected,
const char *curve, gcry_mpi_t *skey, size_t skeysize,
int protect_algo, void *protect_iv, size_t protect_ivlen,
int s2k_mode, int s2k_algo, byte *s2k_salt, u32 s2k_count,
u16 desired_csum, gcry_sexp_t *r_key)
{
gpg_error_t err;
unsigned int npkey, nskey, skeylen;
gcry_cipher_hd_t cipher_hd = NULL;
u16 actual_csum;
size_t nbytes;
int i;
gcry_mpi_t tmpmpi;
*r_key = NULL;
err = prepare_unprotect (pubkey_algo, skey, skeysize, s2k_mode,
&npkey, &nskey, &skeylen);
if (err)
return err;
/* Check whether SKEY is at all protected. If it is not protected
merely verify the checksum. */
if (!is_protected)
{
actual_csum = 0;
for (i=npkey; i < nskey; i++)
{
if (!skey[i] || gcry_mpi_get_flag (skey[i], GCRYMPI_FLAG_USER1))
return gpg_error (GPG_ERR_BAD_SECKEY);
if (gcry_mpi_get_flag (skey[i], GCRYMPI_FLAG_OPAQUE))
{
unsigned int nbits;
const unsigned char *buffer;
buffer = gcry_mpi_get_opaque (skey[i], &nbits);
nbytes = (nbits+7)/8;
actual_csum += checksum (buffer, nbytes);
}
else
{
unsigned char *buffer;
err = gcry_mpi_aprint (GCRYMPI_FMT_PGP, &buffer, &nbytes,
skey[i]);
if (!err)
actual_csum += checksum (buffer, nbytes);
xfree (buffer);
}
if (err)
return err;
}
if (actual_csum != desired_csum)
return gpg_error (GPG_ERR_CHECKSUM);
goto do_convert;
}
if (gcry_cipher_test_algo (protect_algo))
{
/* The algorithm numbers are Libgcrypt numbers but fortunately
the OpenPGP algorithm numbers map one-to-one to the Libgcrypt
numbers. */
log_info (_("protection algorithm %d (%s) is not supported\n"),
protect_algo, gnupg_cipher_algo_name (protect_algo));
return gpg_error (GPG_ERR_CIPHER_ALGO);
}
if (gcry_md_test_algo (s2k_algo))
{
log_info (_("protection hash algorithm %d (%s) is not supported\n"),
s2k_algo, gcry_md_algo_name (s2k_algo));
return gpg_error (GPG_ERR_DIGEST_ALGO);
}
err = gcry_cipher_open (&cipher_hd, protect_algo,
GCRY_CIPHER_MODE_CFB,
(GCRY_CIPHER_SECURE
| (protect_algo >= 100 ?
0 : GCRY_CIPHER_ENABLE_SYNC)));
if (err)
{
log_error ("failed to open cipher_algo %d: %s\n",
protect_algo, gpg_strerror (err));
return err;
}
err = hash_passphrase_and_set_key (passphrase, cipher_hd, protect_algo,
s2k_mode, s2k_algo, s2k_salt, s2k_count);
if (err)
{
gcry_cipher_close (cipher_hd);
return err;
}
gcry_cipher_setiv (cipher_hd, protect_iv, protect_ivlen);
actual_csum = 0;
if (pkt_version >= 4)
{
int ndata;
unsigned int ndatabits;
const unsigned char *p;
unsigned char *data;
u16 csum_pgp7 = 0;
if (!gcry_mpi_get_flag (skey[npkey], GCRYMPI_FLAG_OPAQUE ))
{
gcry_cipher_close (cipher_hd);
return gpg_error (GPG_ERR_BAD_SECKEY);
}
p = gcry_mpi_get_opaque (skey[npkey], &ndatabits);
ndata = (ndatabits+7)/8;
if (ndata > 1)
csum_pgp7 = buf16_to_u16 (p+ndata-2);
data = xtrymalloc_secure (ndata);
if (!data)
{
err = gpg_error_from_syserror ();
gcry_cipher_close (cipher_hd);
return err;
}
gcry_cipher_decrypt (cipher_hd, data, ndata, p, ndata);
p = data;
if (is_protected == 2)
{
/* This is the new SHA1 checksum method to detect tampering
with the key as used by the Klima/Rosa attack. */
desired_csum = 0;
actual_csum = 1; /* Default to bad checksum. */
if (ndata < 20)
log_error ("not enough bytes for SHA-1 checksum\n");
else
{
gcry_md_hd_t h;
if (gcry_md_open (&h, GCRY_MD_SHA1, 1))
BUG(); /* Algo not available. */
gcry_md_write (h, data, ndata - 20);
gcry_md_final (h);
if (!memcmp (gcry_md_read (h, GCRY_MD_SHA1), data+ndata-20, 20))
actual_csum = 0; /* Digest does match. */
gcry_md_close (h);
}
}
else
{
/* Old 16 bit checksum method. */
if (ndata < 2)
{
log_error ("not enough bytes for checksum\n");
desired_csum = 0;
actual_csum = 1; /* Mark checksum bad. */
}
else
{
desired_csum = buf16_to_u16 (data+ndata-2);
actual_csum = checksum (data, ndata-2);
if (desired_csum != actual_csum)
{
/* This is a PGP 7.0.0 workaround */
desired_csum = csum_pgp7; /* Take the encrypted one. */
}
}
}
/* Better check it here. Otherwise the gcry_mpi_scan would fail
because the length may have an arbitrary value. */
if (desired_csum == actual_csum)
{
for (i=npkey; i < nskey; i++ )
{
if (gcry_mpi_scan (&tmpmpi, GCRYMPI_FMT_PGP, p, ndata, &nbytes))
{
/* Checksum was okay, but not correctly decrypted. */
desired_csum = 0;
actual_csum = 1; /* Mark checksum bad. */
break;
}
gcry_mpi_release (skey[i]);
skey[i] = tmpmpi;
ndata -= nbytes;
p += nbytes;
}
skey[i] = NULL;
skeylen = i;
assert (skeylen <= skeysize);
/* Note: at this point NDATA should be 2 for a simple
checksum or 20 for the sha1 digest. */
}
xfree(data);
}
else /* Packet version <= 3. */
{
unsigned char *buffer;
for (i = npkey; i < nskey; i++)
{
const unsigned char *p;
size_t ndata;
unsigned int ndatabits;
if (!skey[i] || !gcry_mpi_get_flag (skey[i], GCRYMPI_FLAG_OPAQUE))
{
gcry_cipher_close (cipher_hd);
return gpg_error (GPG_ERR_BAD_SECKEY);
}
p = gcry_mpi_get_opaque (skey[i], &ndatabits);
ndata = (ndatabits+7)/8;
if (!(ndata >= 2) || !(ndata == (buf16_to_ushort (p) + 7)/8 + 2))
{
gcry_cipher_close (cipher_hd);
return gpg_error (GPG_ERR_BAD_SECKEY);
}
buffer = xtrymalloc_secure (ndata);
if (!buffer)
{
err = gpg_error_from_syserror ();
gcry_cipher_close (cipher_hd);
return err;
}
gcry_cipher_sync (cipher_hd);
buffer[0] = p[0];
buffer[1] = p[1];
gcry_cipher_decrypt (cipher_hd, buffer+2, ndata-2, p+2, ndata-2);
actual_csum += checksum (buffer, ndata);
err = gcry_mpi_scan (&tmpmpi, GCRYMPI_FMT_PGP, buffer, ndata, &ndata);
xfree (buffer);
if (err)
{
/* Checksum was okay, but not correctly decrypted. */
desired_csum = 0;
actual_csum = 1; /* Mark checksum bad. */
break;
}
gcry_mpi_release (skey[i]);
skey[i] = tmpmpi;
}
}
gcry_cipher_close (cipher_hd);
/* Now let's see whether we have used the correct passphrase. */
if (actual_csum != desired_csum)
return gpg_error (GPG_ERR_BAD_PASSPHRASE);
do_convert:
if (nskey != skeylen)
err = gpg_error (GPG_ERR_BAD_SECKEY);
else
err = convert_secret_key (r_key, pubkey_algo, skey, curve);
if (err)
return err;
/* The checksum may fail, thus we also check the key itself. */
err = gcry_pk_testkey (*r_key);
if (err)
{
gcry_sexp_release (*r_key);
*r_key = NULL;
return gpg_error (GPG_ERR_BAD_PASSPHRASE);
}
return 0;
}
/* Callback function to try the unprotection from the passphrase query
code. */
static gpg_error_t
try_do_unprotect_cb (struct pin_entry_info_s *pi)
{
gpg_error_t err;
struct try_do_unprotect_arg_s *arg = pi->check_cb_arg;
err = do_unprotect (pi->pin,
arg->is_v4? 4:3,
arg->pubkey_algo, arg->is_protected,
arg->curve,
arg->skey, arg->skeysize,
arg->protect_algo, arg->iv, arg->ivlen,
arg->s2k_mode, arg->s2k_algo,
arg->s2k_salt, arg->s2k_count,
arg->desired_csum, arg->r_key);
/* SKEY may be modified now, thus we need to re-compute SKEYIDX. */
for (arg->skeyidx = 0; (arg->skeyidx < arg->skeysize
&& arg->skey[arg->skeyidx]); arg->skeyidx++)
;
return err;
}
/* See convert_from_openpgp for the core of the description. This
function adds an optional PASSPHRASE argument and uses this to
silently decrypt the key; CACHE_NONCE and R_PASSPHRASE must both be
NULL in this mode. */
static gpg_error_t
convert_from_openpgp_main (ctrl_t ctrl, gcry_sexp_t s_pgp, int dontcare_exist,
unsigned char *grip, const char *prompt,
const char *cache_nonce, const char *passphrase,
unsigned char **r_key, char **r_passphrase)
{
gpg_error_t err;
int unattended;
int from_native;
gcry_sexp_t top_list;
gcry_sexp_t list = NULL;
const char *value;
size_t valuelen;
char *string;
int idx;
int is_v4, is_protected;
int pubkey_algo;
int protect_algo = 0;
char iv[16];
int ivlen = 0;
int s2k_mode = 0;
int s2k_algo = 0;
byte s2k_salt[8];
u32 s2k_count = 0;
size_t npkey, nskey;
gcry_mpi_t skey[10]; /* We support up to 9 parameters. */
char *curve = NULL;
u16 desired_csum;
int skeyidx = 0;
gcry_sexp_t s_skey = NULL;
*r_key = NULL;
if (r_passphrase)
*r_passphrase = NULL;
unattended = !r_passphrase;
from_native = (!cache_nonce && passphrase && !r_passphrase);
top_list = gcry_sexp_find_token (s_pgp, "openpgp-private-key", 0);
if (!top_list)
goto bad_seckey;
list = gcry_sexp_find_token (top_list, "version", 0);
if (!list)
goto bad_seckey;
value = gcry_sexp_nth_data (list, 1, &valuelen);
if (!value || valuelen != 1 || !(value[0] == '3' || value[0] == '4'))
goto bad_seckey;
is_v4 = (value[0] == '4');
gcry_sexp_release (list);
list = gcry_sexp_find_token (top_list, "protection", 0);
if (!list)
goto bad_seckey;
value = gcry_sexp_nth_data (list, 1, &valuelen);
if (!value)
goto bad_seckey;
if (valuelen == 4 && !memcmp (value, "sha1", 4))
is_protected = 2;
else if (valuelen == 3 && !memcmp (value, "sum", 3))
is_protected = 1;
else if (valuelen == 4 && !memcmp (value, "none", 4))
is_protected = 0;
else
goto bad_seckey;
if (is_protected)
{
string = gcry_sexp_nth_string (list, 2);
if (!string)
goto bad_seckey;
protect_algo = gcry_cipher_map_name (string);
xfree (string);
value = gcry_sexp_nth_data (list, 3, &valuelen);
if (!value || !valuelen || valuelen > sizeof iv)
goto bad_seckey;
memcpy (iv, value, valuelen);
ivlen = valuelen;
string = gcry_sexp_nth_string (list, 4);
if (!string)
goto bad_seckey;
s2k_mode = strtol (string, NULL, 10);
xfree (string);
string = gcry_sexp_nth_string (list, 5);
if (!string)
goto bad_seckey;
s2k_algo = gcry_md_map_name (string);
xfree (string);
value = gcry_sexp_nth_data (list, 6, &valuelen);
if (!value || !valuelen || valuelen > sizeof s2k_salt)
goto bad_seckey;
memcpy (s2k_salt, value, valuelen);
string = gcry_sexp_nth_string (list, 7);
if (!string)
goto bad_seckey;
s2k_count = strtoul (string, NULL, 10);
xfree (string);
}
gcry_sexp_release (list);
list = gcry_sexp_find_token (top_list, "algo", 0);
if (!list)
goto bad_seckey;
string = gcry_sexp_nth_string (list, 1);
if (!string)
goto bad_seckey;
pubkey_algo = gcry_pk_map_name (string);
xfree (string);
get_npkey_nskey (pubkey_algo, &npkey, &nskey);
if (!npkey || !nskey || npkey >= nskey)
goto bad_seckey;
if (npkey == 1) /* This is ECC */
{
gcry_sexp_release (list);
list = gcry_sexp_find_token (top_list, "curve", 0);
if (!list)
goto bad_seckey;
curve = gcry_sexp_nth_string (list, 1);
if (!curve)
goto bad_seckey;
}
gcry_sexp_release (list);
list = gcry_sexp_find_token (top_list, "skey", 0);
if (!list)
goto bad_seckey;
for (idx=0;;)
{
int is_enc;
value = gcry_sexp_nth_data (list, ++idx, &valuelen);
if (!value && skeyidx >= npkey)
break; /* Ready. */
/* Check for too many parameters. Note that depending on the
protection mode and version number we may see less than NSKEY
(but at least NPKEY+1) parameters. */
if (idx >= 2*nskey)
goto bad_seckey;
if (skeyidx >= DIM (skey)-1)
goto bad_seckey;
if (!value || valuelen != 1 || !(value[0] == '_' || value[0] == 'e'))
goto bad_seckey;
is_enc = (value[0] == 'e');
value = gcry_sexp_nth_data (list, ++idx, &valuelen);
if (!value || !valuelen)
goto bad_seckey;
if (is_enc)
{
/* Encrypted parameters need to be stored as opaque. */
skey[skeyidx] = gcry_mpi_set_opaque_copy (NULL, value, valuelen*8);
if (!skey[skeyidx])
goto outofmem;
gcry_mpi_set_flag (skey[skeyidx], GCRYMPI_FLAG_USER1);
}
else
{
if (gcry_mpi_scan (skey + skeyidx, GCRYMPI_FMT_STD,
value, valuelen, NULL))
goto bad_seckey;
}
skeyidx++;
}
skey[skeyidx++] = NULL;
gcry_sexp_release (list);
list = gcry_sexp_find_token (top_list, "csum", 0);
if (list)
{
string = gcry_sexp_nth_string (list, 1);
if (!string)
goto bad_seckey;
desired_csum = strtoul (string, NULL, 10);
xfree (string);
}
else
desired_csum = 0;
gcry_sexp_release (list); list = NULL;
gcry_sexp_release (top_list); top_list = NULL;
#if 0
log_debug ("XXX is_v4=%d\n", is_v4);
log_debug ("XXX pubkey_algo=%d\n", pubkey_algo);
log_debug ("XXX is_protected=%d\n", is_protected);
log_debug ("XXX protect_algo=%d\n", protect_algo);
- log_printhex ("XXX iv", iv, ivlen);
+ log_printhex (iv, ivlen, "XXX iv");
log_debug ("XXX ivlen=%d\n", ivlen);
log_debug ("XXX s2k_mode=%d\n", s2k_mode);
log_debug ("XXX s2k_algo=%d\n", s2k_algo);
- log_printhex ("XXX s2k_salt", s2k_salt, sizeof s2k_salt);
+ log_printhex (s2k_salt, sizeof s2k_salt, "XXX s2k_salt");
log_debug ("XXX s2k_count=%lu\n", (unsigned long)s2k_count);
log_debug ("XXX curve='%s'\n", curve);
for (idx=0; skey[idx]; idx++)
gcry_log_debugmpi (gcry_mpi_get_flag (skey[idx], GCRYMPI_FLAG_USER1)
? "skey(e)" : "skey(_)", skey[idx]);
#endif /*0*/
err = get_keygrip (pubkey_algo, curve, skey, grip);
if (err)
goto leave;
if (!dontcare_exist && !from_native && !agent_key_available (grip))
{
err = gpg_error (GPG_ERR_EEXIST);
goto leave;
}
if (unattended && !from_native)
{
err = prepare_unprotect (pubkey_algo, skey, DIM(skey), s2k_mode,
NULL, NULL, NULL);
if (err)
goto leave;
err = convert_transfer_key (&s_skey, pubkey_algo, skey, curve, s_pgp);
if (err)
goto leave;
}
else
{
struct pin_entry_info_s *pi;
struct try_do_unprotect_arg_s pi_arg;
pi = xtrycalloc_secure (1, sizeof (*pi) + MAX_PASSPHRASE_LEN + 1);
if (!pi)
return gpg_error_from_syserror ();
pi->max_length = MAX_PASSPHRASE_LEN + 1;
pi->min_digits = 0; /* We want a real passphrase. */
pi->max_digits = 16;
pi->max_tries = 3;
pi->check_cb = try_do_unprotect_cb;
pi->check_cb_arg = &pi_arg;
pi_arg.is_v4 = is_v4;
pi_arg.is_protected = is_protected;
pi_arg.pubkey_algo = pubkey_algo;
pi_arg.curve = curve;
pi_arg.protect_algo = protect_algo;
pi_arg.iv = iv;
pi_arg.ivlen = ivlen;
pi_arg.s2k_mode = s2k_mode;
pi_arg.s2k_algo = s2k_algo;
pi_arg.s2k_salt = s2k_salt;
pi_arg.s2k_count = s2k_count;
pi_arg.desired_csum = desired_csum;
pi_arg.skey = skey;
pi_arg.skeysize = DIM (skey);
pi_arg.skeyidx = skeyidx;
pi_arg.r_key = &s_skey;
err = gpg_error (GPG_ERR_BAD_PASSPHRASE);
if (!is_protected)
{
err = try_do_unprotect_cb (pi);
}
else if (cache_nonce)
{
char *cache_value;
cache_value = agent_get_cache (ctrl, cache_nonce, CACHE_MODE_NONCE);
if (cache_value)
{
if (strlen (cache_value) < pi->max_length)
strcpy (pi->pin, cache_value);
xfree (cache_value);
}
if (*pi->pin)
err = try_do_unprotect_cb (pi);
}
else if (from_native)
{
if (strlen (passphrase) < pi->max_length)
strcpy (pi->pin, passphrase);
err = try_do_unprotect_cb (pi);
}
if (gpg_err_code (err) == GPG_ERR_BAD_PASSPHRASE && !from_native)
err = agent_askpin (ctrl, prompt, NULL, NULL, pi, NULL, 0);
skeyidx = pi_arg.skeyidx;
if (!err && r_passphrase && is_protected)
{
*r_passphrase = xtrystrdup (pi->pin);
if (!*r_passphrase)
err = gpg_error_from_syserror ();
}
xfree (pi);
if (err)
goto leave;
}
/* Save some memory and get rid of the SKEY array now. */
for (idx=0; idx < skeyidx; idx++)
gcry_mpi_release (skey[idx]);
skeyidx = 0;
/* Note that the padding is not required - we use it only because
that function allows us to create the result in secure memory. */
err = make_canon_sexp_pad (s_skey, 1, r_key, NULL);
leave:
xfree (curve);
gcry_sexp_release (s_skey);
gcry_sexp_release (list);
gcry_sexp_release (top_list);
for (idx=0; idx < skeyidx; idx++)
gcry_mpi_release (skey[idx]);
if (err && r_passphrase)
{
xfree (*r_passphrase);
*r_passphrase = NULL;
}
return err;
bad_seckey:
err = gpg_error (GPG_ERR_BAD_SECKEY);
goto leave;
outofmem:
err = gpg_error (GPG_ERR_ENOMEM);
goto leave;
}
/* Convert an OpenPGP transfer key into our internal format. Before
asking for a passphrase we check whether the key already exists in
our key storage. S_PGP is the OpenPGP key in transfer format. If
CACHE_NONCE is given the passphrase will be looked up in the cache.
On success R_KEY will receive a canonical encoded S-expression with
the unprotected key in our internal format; the caller needs to
release that memory. The passphrase used to decrypt the OpenPGP
key will be returned at R_PASSPHRASE; the caller must release this
passphrase. If R_PASSPHRASE is NULL the unattended conversion mode
will be used which uses the openpgp-native protection format for
the key. The keygrip will be stored at the 20 byte buffer pointed
to by GRIP. On error NULL is stored at all return arguments. */
gpg_error_t
convert_from_openpgp (ctrl_t ctrl, gcry_sexp_t s_pgp, int dontcare_exist,
unsigned char *grip, const char *prompt,
const char *cache_nonce,
unsigned char **r_key, char **r_passphrase)
{
return convert_from_openpgp_main (ctrl, s_pgp, dontcare_exist, grip, prompt,
cache_nonce, NULL,
r_key, r_passphrase);
}
/* This function is called by agent_unprotect to re-protect an
openpgp-native protected private-key into the standard private-key
protection format. */
gpg_error_t
convert_from_openpgp_native (ctrl_t ctrl,
gcry_sexp_t s_pgp, const char *passphrase,
unsigned char **r_key)
{
gpg_error_t err;
unsigned char grip[20];
if (!passphrase)
return gpg_error (GPG_ERR_INTERNAL);
err = convert_from_openpgp_main (ctrl, s_pgp, 0, grip, NULL,
NULL, passphrase,
r_key, NULL);
/* On success try to re-write the key. */
if (!err)
{
if (*passphrase)
{
unsigned char *protectedkey = NULL;
size_t protectedkeylen;
if (!agent_protect (*r_key, passphrase,
&protectedkey, &protectedkeylen,
ctrl->s2k_count, -1))
agent_write_private_key (grip, protectedkey, protectedkeylen, 1);
xfree (protectedkey);
}
else
{
/* Empty passphrase: write key without protection. */
agent_write_private_key (grip,
*r_key,
gcry_sexp_canon_len (*r_key, 0, NULL,NULL),
1);
}
}
return err;
}
/* Given an ARRAY of mpis with the key parameters, protect the secret
parameters in that array and replace them by one opaque encoded
mpi. NPKEY is the number of public key parameters and NSKEY is
the number of secret key parameters (including the public ones).
On success the array will have NPKEY+1 elements. */
static gpg_error_t
apply_protection (gcry_mpi_t *array, int npkey, int nskey,
const char *passphrase,
int protect_algo, void *protect_iv, size_t protect_ivlen,
int s2k_mode, int s2k_algo, byte *s2k_salt, u32 s2k_count)
{
gpg_error_t err;
int i, j;
gcry_cipher_hd_t cipherhd;
unsigned char *bufarr[10];
size_t narr[10];
unsigned int nbits[10];
int ndata;
unsigned char *p, *data;
assert (npkey < nskey);
assert (nskey < DIM (bufarr));
/* Collect only the secret key parameters into BUFARR et al and
compute the required size of the data buffer. */
ndata = 20; /* Space for the SHA-1 checksum. */
for (i = npkey, j = 0; i < nskey; i++, j++ )
{
err = gcry_mpi_aprint (GCRYMPI_FMT_USG, bufarr+j, narr+j, array[i]);
if (err)
{
for (i = 0; i < j; i++)
xfree (bufarr[i]);
return err;
}
nbits[j] = gcry_mpi_get_nbits (array[i]);
ndata += 2 + narr[j];
}
/* Allocate data buffer and stuff it with the secret key parameters. */
data = xtrymalloc_secure (ndata);
if (!data)
{
err = gpg_error_from_syserror ();
for (i = 0; i < (nskey-npkey); i++ )
xfree (bufarr[i]);
return err;
}
p = data;
for (i = 0; i < (nskey-npkey); i++ )
{
*p++ = nbits[i] >> 8 ;
*p++ = nbits[i];
memcpy (p, bufarr[i], narr[i]);
p += narr[i];
xfree (bufarr[i]);
bufarr[i] = NULL;
}
assert (p == data + ndata - 20);
/* Append a hash of the secret key parameters. */
gcry_md_hash_buffer (GCRY_MD_SHA1, p, data, ndata - 20);
/* Encrypt it. */
err = gcry_cipher_open (&cipherhd, protect_algo,
GCRY_CIPHER_MODE_CFB, GCRY_CIPHER_SECURE);
if (!err)
err = hash_passphrase_and_set_key (passphrase, cipherhd, protect_algo,
s2k_mode, s2k_algo, s2k_salt, s2k_count);
if (!err)
err = gcry_cipher_setiv (cipherhd, protect_iv, protect_ivlen);
if (!err)
err = gcry_cipher_encrypt (cipherhd, data, ndata, NULL, 0);
gcry_cipher_close (cipherhd);
if (err)
{
xfree (data);
return err;
}
/* Replace the secret key parameters in the array by one opaque value. */
for (i = npkey; i < nskey; i++ )
{
gcry_mpi_release (array[i]);
array[i] = NULL;
}
array[npkey] = gcry_mpi_set_opaque (NULL, data, ndata*8);
return 0;
}
/*
* Examining S_KEY in S-Expression and extract data.
* When REQ_PRIVATE_KEY_DATA == 1, S_KEY's CAR should be 'private-key',
* but it also allows shadowed or protected versions.
* On success, it returns 0, otherwise error number.
* R_ALGONAME is static string which is no need to free by caller.
* R_NPKEY is pointer to number of public key data.
* R_NSKEY is pointer to number of private key data.
* R_ELEMS is static string which is no need to free by caller.
* ARRAY contains public and private key data.
* ARRAYSIZE is the allocated size of the array for cross-checking.
* R_CURVE is pointer to S-Expression of the curve (can be NULL).
* R_FLAGS is pointer to S-Expression of the flags (can be NULL).
*/
gpg_error_t
extract_private_key (gcry_sexp_t s_key, int req_private_key_data,
const char **r_algoname, int *r_npkey, int *r_nskey,
const char **r_elems,
gcry_mpi_t *array, int arraysize,
gcry_sexp_t *r_curve, gcry_sexp_t *r_flags)
{
gpg_error_t err;
gcry_sexp_t list, l2;
char *name;
const char *algoname, *format;
int npkey, nskey;
gcry_sexp_t curve = NULL;
gcry_sexp_t flags = NULL;
*r_curve = NULL;
*r_flags = NULL;
if (!req_private_key_data)
{
list = gcry_sexp_find_token (s_key, "shadowed-private-key", 0 );
if (!list)
list = gcry_sexp_find_token (s_key, "protected-private-key", 0 );
if (!list)
list = gcry_sexp_find_token (s_key, "private-key", 0 );
}
else
list = gcry_sexp_find_token (s_key, "private-key", 0);
if (!list)
{
log_error ("invalid private key format\n");
return gpg_error (GPG_ERR_BAD_SECKEY);
}
l2 = gcry_sexp_cadr (list);
gcry_sexp_release (list);
list = l2;
name = gcry_sexp_nth_string (list, 0);
if (!name)
{
gcry_sexp_release (list);
return gpg_error (GPG_ERR_INV_OBJ); /* Invalid structure of object. */
}
if (arraysize < 7)
BUG ();
/* Map NAME to a name as used by Libgcrypt. We do not use the
Libgcrypt function here because we need a lowercase name and
require special treatment for some algorithms. */
strlwr (name);
if (!strcmp (name, "rsa"))
{
algoname = "rsa";
format = "ned?p?q?u?";
npkey = 2;
nskey = 6;
err = gcry_sexp_extract_param (list, NULL, format,
array+0, array+1, array+2, array+3,
array+4, array+5, NULL);
}
else if (!strcmp (name, "elg"))
{
algoname = "elg";
format = "pgyx?";
npkey = 3;
nskey = 4;
err = gcry_sexp_extract_param (list, NULL, format,
array+0, array+1, array+2, array+3,
NULL);
}
else if (!strcmp (name, "dsa"))
{
algoname = "dsa";
format = "pqgyx?";
npkey = 4;
nskey = 5;
err = gcry_sexp_extract_param (list, NULL, format,
array+0, array+1, array+2, array+3,
array+4, NULL);
}
else if (!strcmp (name, "ecc") || !strcmp (name, "ecdsa"))
{
algoname = "ecc";
format = "qd?";
npkey = 1;
nskey = 2;
curve = gcry_sexp_find_token (list, "curve", 0);
flags = gcry_sexp_find_token (list, "flags", 0);
err = gcry_sexp_extract_param (list, NULL, format,
array+0, array+1, NULL);
}
else
{
err = gpg_error (GPG_ERR_PUBKEY_ALGO);
}
xfree (name);
gcry_sexp_release (list);
if (err)
{
gcry_sexp_release (curve);
gcry_sexp_release (flags);
return err;
}
else
{
*r_algoname = algoname;
if (r_elems)
*r_elems = format;
*r_npkey = npkey;
if (r_nskey)
*r_nskey = nskey;
*r_curve = curve;
*r_flags = flags;
return 0;
}
}
/* Convert our key S_KEY into an OpenPGP key transfer format. On
success a canonical encoded S-expression is stored at R_TRANSFERKEY
and its length at R_TRANSFERKEYLEN; this S-expression is also
padded to a multiple of 64 bits. */
gpg_error_t
convert_to_openpgp (ctrl_t ctrl, gcry_sexp_t s_key, const char *passphrase,
unsigned char **r_transferkey, size_t *r_transferkeylen)
{
gpg_error_t err;
const char *algoname;
int npkey, nskey;
gcry_mpi_t array[10];
gcry_sexp_t curve = NULL;
gcry_sexp_t flags = NULL;
char protect_iv[16];
char salt[8];
unsigned long s2k_count;
int i, j;
(void)ctrl;
*r_transferkey = NULL;
for (i=0; i < DIM (array); i++)
array[i] = NULL;
err = extract_private_key (s_key, 1, &algoname, &npkey, &nskey, NULL,
array, DIM (array), &curve, &flags);
if (err)
return err;
gcry_create_nonce (protect_iv, sizeof protect_iv);
gcry_create_nonce (salt, sizeof salt);
/* We need to use the encoded S2k count. It is not possible to
encode it after it has been used because the encoding procedure
may round the value up. */
s2k_count = get_standard_s2k_count_rfc4880 ();
err = apply_protection (array, npkey, nskey, passphrase,
GCRY_CIPHER_AES, protect_iv, sizeof protect_iv,
3, GCRY_MD_SHA1, salt, s2k_count);
/* Turn it into the transfer key S-expression. Note that we always
return a protected key. */
if (!err)
{
char countbuf[35];
membuf_t mbuf;
void *format_args[10+2];
gcry_sexp_t tmpkey;
gcry_sexp_t tmpsexp = NULL;
snprintf (countbuf, sizeof countbuf, "%lu", s2k_count);
init_membuf (&mbuf, 50);
put_membuf_str (&mbuf, "(skey");
for (i=j=0; i < npkey; i++)
{
put_membuf_str (&mbuf, " _ %m");
format_args[j++] = array + i;
}
put_membuf_str (&mbuf, " e %m");
format_args[j++] = array + npkey;
put_membuf_str (&mbuf, ")\n");
put_membuf (&mbuf, "", 1);
tmpkey = NULL;
{
char *format = get_membuf (&mbuf, NULL);
if (!format)
err = gpg_error_from_syserror ();
else
err = gcry_sexp_build_array (&tmpkey, NULL, format, format_args);
xfree (format);
}
if (!err)
err = gcry_sexp_build (&tmpsexp, NULL,
"(openpgp-private-key\n"
" (version 1:4)\n"
" (algo %s)\n"
" %S%S\n"
" (protection sha1 aes %b 1:3 sha1 %b %s))\n",
algoname,
curve,
tmpkey,
(int)sizeof protect_iv, protect_iv,
(int)sizeof salt, salt,
countbuf);
gcry_sexp_release (tmpkey);
if (!err)
err = make_canon_sexp_pad (tmpsexp, 0, r_transferkey, r_transferkeylen);
gcry_sexp_release (tmpsexp);
}
for (i=0; i < DIM (array); i++)
gcry_mpi_release (array[i]);
gcry_sexp_release (curve);
gcry_sexp_release (flags);
return err;
}
diff --git a/agent/divert-scd.c b/agent/divert-scd.c
index 191ed7f2e..b79f7a880 100644
--- a/agent/divert-scd.c
+++ b/agent/divert-scd.c
@@ -1,612 +1,612 @@
/* divert-scd.c - divert operations to the scdaemon
* Copyright (C) 2002, 2003, 2009 Free Software Foundation, Inc.
*
* This file is part of GnuPG.
*
* GnuPG is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* GnuPG is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see .
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "agent.h"
#include "../common/i18n.h"
#include "../common/sexp-parse.h"
static int
ask_for_card (ctrl_t ctrl, const unsigned char *shadow_info, char **r_kid)
{
int rc, i;
char *serialno;
int no_card = 0;
char *desc;
char *want_sn, *want_kid, *want_sn_disp;
int len;
*r_kid = NULL;
rc = parse_shadow_info (shadow_info, &want_sn, &want_kid, NULL);
if (rc)
return rc;
want_sn_disp = xtrystrdup (want_sn);
if (!want_sn_disp)
{
rc = gpg_error_from_syserror ();
xfree (want_sn);
xfree (want_kid);
return rc;
}
len = strlen (want_sn_disp);
if (len == 32 && !strncmp (want_sn_disp, "D27600012401", 12))
{
/* This is an OpenPGP card - reformat */
memmove (want_sn_disp, want_sn_disp+16, 4);
want_sn_disp[4] = ' ';
memmove (want_sn_disp+5, want_sn_disp+20, 8);
want_sn_disp[13] = 0;
}
else if (len == 20 && want_sn_disp[19] == '0')
{
/* We assume that a 20 byte serial number is a standard one
* which has the property to have a zero in the last nibble (Due
* to BCD representation). We don't display this '0' because it
* may confuse the user. */
want_sn_disp[19] = 0;
}
for (;;)
{
rc = agent_card_serialno (ctrl, &serialno, want_sn);
if (!rc)
{
log_debug ("detected card with S/N %s\n", serialno);
i = strcmp (serialno, want_sn);
xfree (serialno);
serialno = NULL;
if (!i)
{
xfree (want_sn_disp);
xfree (want_sn);
*r_kid = want_kid;
return 0; /* yes, we have the correct card */
}
}
else if (gpg_err_code (rc) == GPG_ERR_ENODEV)
{
log_debug ("no device present\n");
rc = 0;
no_card = 1;
}
else if (gpg_err_code (rc) == GPG_ERR_CARD_NOT_PRESENT)
{
log_debug ("no card present\n");
rc = 0;
no_card = 2;
}
else
{
log_error ("error accessing card: %s\n", gpg_strerror (rc));
}
if (!rc)
{
if (asprintf (&desc,
"%s:%%0A%%0A"
" %s",
no_card
? L_("Please insert the card with serial number")
: L_("Please remove the current card and "
"insert the one with serial number"),
want_sn_disp) < 0)
{
rc = out_of_core ();
}
else
{
rc = agent_get_confirmation (ctrl, desc, NULL, NULL, 0);
if (ctrl->pinentry_mode == PINENTRY_MODE_LOOPBACK &&
gpg_err_code (rc) == GPG_ERR_NO_PIN_ENTRY)
rc = gpg_error (GPG_ERR_CARD_NOT_PRESENT);
xfree (desc);
}
}
if (rc)
{
xfree (want_sn_disp);
xfree (want_sn);
xfree (want_kid);
return rc;
}
}
}
/* Put the DIGEST into an DER encoded container and return it in R_VAL. */
static int
encode_md_for_card (const unsigned char *digest, size_t digestlen, int algo,
unsigned char **r_val, size_t *r_len)
{
unsigned char *frame;
unsigned char asn[100];
size_t asnlen;
*r_val = NULL;
*r_len = 0;
asnlen = DIM(asn);
if (!algo || gcry_md_test_algo (algo))
return gpg_error (GPG_ERR_DIGEST_ALGO);
if (gcry_md_algo_info (algo, GCRYCTL_GET_ASNOID, asn, &asnlen))
{
log_error ("no object identifier for algo %d\n", algo);
return gpg_error (GPG_ERR_INTERNAL);
}
frame = xtrymalloc (asnlen + digestlen);
if (!frame)
return out_of_core ();
memcpy (frame, asn, asnlen);
memcpy (frame+asnlen, digest, digestlen);
if (DBG_CRYPTO)
- log_printhex ("encoded hash:", frame, asnlen+digestlen);
+ log_printhex (frame, asnlen+digestlen, "encoded hash:");
*r_val = frame;
*r_len = asnlen+digestlen;
return 0;
}
/* Return true if STRING ends in "%0A". */
static int
has_percent0A_suffix (const char *string)
{
size_t n;
return (string
&& (n = strlen (string)) >= 3
&& !strcmp (string + n - 3, "%0A"));
}
/* Callback used to ask for the PIN which should be set into BUF. The
buf has been allocated by the caller and is of size MAXBUF which
includes the terminating null. The function should return an UTF-8
string with the passphrase, the buffer may optionally be padded
with arbitrary characters.
If DESC_TEXT is not NULL it can be used as further informtion shown
atop of the INFO message.
INFO gets displayed as part of a generic string. However if the
first character of INFO is a vertical bar all up to the next
verical bar are considered flags and only everything after the
second vertical bar gets displayed as the full prompt.
Flags:
'N' = New PIN, this requests a second prompt to repeat the
PIN. If the PIN is not correctly repeated it starts from
all over.
'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.
Example:
"|AN|Please enter the new security officer's PIN"
The text "Please ..." will get displayed and the flags 'A' and 'N'
are considered.
*/
static int
getpin_cb (void *opaque, const char *desc_text, const char *info,
char *buf, size_t maxbuf)
{
struct pin_entry_info_s *pi;
int rc;
ctrl_t ctrl = opaque;
const char *ends, *s;
int any_flags = 0;
int newpin = 0;
int resetcode = 0;
int is_puk = 0;
const char *again_text = NULL;
const char *prompt = "PIN";
if (buf && maxbuf < 2)
return gpg_error (GPG_ERR_INV_VALUE);
/* Parse the flags. */
if (info && *info =='|' && (ends=strchr (info+1, '|')))
{
for (s=info+1; s < ends; s++)
{
if (*s == 'A')
prompt = L_("Admin PIN");
else if (*s == 'P')
{
/* TRANSLATORS: A PUK is the Personal Unblocking Code
used to unblock a PIN. */
prompt = L_("PUK");
is_puk = 1;
}
else if (*s == 'N')
newpin = 1;
else if (*s == 'R')
{
prompt = L_("Reset Code");
resetcode = 1;
}
}
info = ends+1;
any_flags = 1;
}
else if (info && *info == '|')
log_debug ("pin_cb called without proper PIN info hack\n");
/* If BUF has been passed as NULL, we are in pinpad mode: The
callback opens the popup and immediately returns. */
if (!buf)
{
if (maxbuf == 0) /* Close the pinentry. */
{
agent_popup_message_stop (ctrl);
rc = 0;
}
else if (maxbuf == 1) /* Open the pinentry. */
{
if (info)
{
char *desc;
const char *desc2;
if (!strcmp (info, "--ack"))
{
desc2 = L_("Push ACK button on card/token.");
if (desc_text)
{
desc = strconcat (desc_text,
has_percent0A_suffix (desc_text)
? "%0A" : "%0A%0A",
desc2, NULL);
desc2 = NULL;
}
else
desc = NULL;
}
else
{
desc2 = NULL;
if (desc_text)
desc = strconcat (desc_text,
has_percent0A_suffix (desc_text)
? "%0A" : "%0A%0A",
info, "%0A%0A",
L_("Use the reader's pinpad for input."),
NULL);
else
desc = strconcat (info, "%0A%0A",
L_("Use the reader's pinpad for input."),
NULL);
}
if (!desc2 && !desc)
rc = gpg_error_from_syserror ();
else
{
rc = agent_popup_message_start (ctrl,
desc2? desc2:desc, NULL);
xfree (desc);
}
}
else
rc = agent_popup_message_start (ctrl, desc_text, NULL);
}
else
rc = gpg_error (GPG_ERR_INV_VALUE);
return rc;
}
/* FIXME: keep PI and TRIES in OPAQUE. Frankly this is a whole
mess because we should call the card's verify function from the
pinentry check pin CB. */
again:
pi = gcry_calloc_secure (1, sizeof (*pi) + maxbuf + 10);
if (!pi)
return gpg_error_from_syserror ();
pi->max_length = maxbuf-1;
pi->min_digits = 0; /* we want a real passphrase */
pi->max_digits = 16;
pi->max_tries = 3;
if (any_flags)
{
{
char *desc2;
if (desc_text)
desc2 = strconcat (desc_text,
has_percent0A_suffix (desc_text)
? "%0A" : "%0A%0A",
info, NULL);
else
desc2 = NULL;
rc = agent_askpin (ctrl, desc2? desc2 : info,
prompt, again_text, pi, NULL, 0);
xfree (desc2);
}
again_text = NULL;
if (!rc && newpin)
{
struct pin_entry_info_s *pi2;
pi2 = gcry_calloc_secure (1, sizeof (*pi) + maxbuf + 10);
if (!pi2)
{
rc = gpg_error_from_syserror ();
xfree (pi);
return rc;
}
pi2->max_length = maxbuf-1;
pi2->min_digits = 0;
pi2->max_digits = 16;
pi2->max_tries = 1;
rc = agent_askpin (ctrl,
(resetcode?
L_("Repeat this Reset Code"):
is_puk?
L_("Repeat this PUK"):
L_("Repeat this PIN")),
prompt, NULL, pi2, NULL, 0);
if (!rc && strcmp (pi->pin, pi2->pin))
{
again_text = (resetcode?
L_("Reset Code not correctly repeated; try again"):
is_puk?
L_("PUK not correctly repeated; try again"):
L_("PIN not correctly repeated; try again"));
xfree (pi2);
xfree (pi);
goto again;
}
xfree (pi2);
}
}
else
{
char *desc, *desc2;
if ( asprintf (&desc,
L_("Please enter the PIN%s%s%s to unlock the card"),
info? " (":"",
info? info:"",
info? ")":"") < 0)
desc = NULL;
if (desc_text)
desc2 = strconcat (desc_text,
has_percent0A_suffix (desc_text)
? "%0A" : "%0A%0A",
desc, NULL);
else
desc2 = NULL;
rc = agent_askpin (ctrl, desc2? desc2 : desc? desc : info,
prompt, NULL, pi, NULL, 0);
xfree (desc2);
xfree (desc);
}
if (!rc)
{
strncpy (buf, pi->pin, maxbuf-1);
buf[maxbuf-1] = 0;
}
xfree (pi);
return rc;
}
/* This function is used when a sign operation has been diverted to a
* smartcard. DESC_TEXT is the original text for a prompt has send by
* gpg to gpg-agent.
*
* FIXME: Explain the other args. */
int
divert_pksign (ctrl_t ctrl, const char *desc_text,
const unsigned char *digest, size_t digestlen, int algo,
const unsigned char *shadow_info, unsigned char **r_sig,
size_t *r_siglen)
{
int rc;
char *kid;
size_t siglen;
unsigned char *sigval = NULL;
(void)desc_text;
rc = ask_for_card (ctrl, shadow_info, &kid);
if (rc)
return rc;
if (algo == MD_USER_TLS_MD5SHA1)
{
int save = ctrl->use_auth_call;
ctrl->use_auth_call = 1;
rc = agent_card_pksign (ctrl, kid, getpin_cb, ctrl, NULL,
algo, digest, digestlen, &sigval, &siglen);
ctrl->use_auth_call = save;
}
else
{
unsigned char *data;
size_t ndata;
rc = encode_md_for_card (digest, digestlen, algo, &data, &ndata);
if (!rc)
{
rc = agent_card_pksign (ctrl, kid, getpin_cb, ctrl, NULL,
algo, data, ndata, &sigval, &siglen);
xfree (data);
}
}
if (!rc)
{
*r_sig = sigval;
*r_siglen = siglen;
}
xfree (kid);
return rc;
}
/* Decrypt the value given asn an S-expression in CIPHER using the
key identified by SHADOW_INFO and return the plaintext in an
allocated buffer in R_BUF. The padding information is stored at
R_PADDING with -1 for not known. */
int
divert_pkdecrypt (ctrl_t ctrl, const char *desc_text,
const unsigned char *cipher,
const unsigned char *shadow_info,
char **r_buf, size_t *r_len, int *r_padding)
{
int rc;
char *kid;
const unsigned char *s;
size_t n;
int depth;
const unsigned char *ciphertext;
size_t ciphertextlen;
char *plaintext;
size_t plaintextlen;
(void)desc_text;
*r_padding = -1;
s = cipher;
if (*s != '(')
return gpg_error (GPG_ERR_INV_SEXP);
s++;
n = snext (&s);
if (!n)
return gpg_error (GPG_ERR_INV_SEXP);
if (!smatch (&s, n, "enc-val"))
return gpg_error (GPG_ERR_UNKNOWN_SEXP);
if (*s != '(')
return gpg_error (GPG_ERR_UNKNOWN_SEXP);
s++;
n = snext (&s);
if (!n)
return gpg_error (GPG_ERR_INV_SEXP);
/* First check whether we have a flags parameter and skip it. */
if (smatch (&s, n, "flags"))
{
depth = 1;
if (sskip (&s, &depth) || depth)
return gpg_error (GPG_ERR_INV_SEXP);
if (*s != '(')
return gpg_error (GPG_ERR_INV_SEXP);
s++;
n = snext (&s);
if (!n)
return gpg_error (GPG_ERR_INV_SEXP);
}
if (smatch (&s, n, "rsa"))
{
if (*s != '(')
return gpg_error (GPG_ERR_UNKNOWN_SEXP);
s++;
n = snext (&s);
if (!n)
return gpg_error (GPG_ERR_INV_SEXP);
if (!smatch (&s, n, "a"))
return gpg_error (GPG_ERR_UNKNOWN_SEXP);
n = snext (&s);
}
else if (smatch (&s, n, "ecdh"))
{
if (*s != '(')
return gpg_error (GPG_ERR_UNKNOWN_SEXP);
s++;
n = snext (&s);
if (!n)
return gpg_error (GPG_ERR_INV_SEXP);
if (smatch (&s, n, "s"))
{
n = snext (&s);
s += n;
if (*s++ != ')')
return gpg_error (GPG_ERR_INV_SEXP);
if (*s++ != '(')
return gpg_error (GPG_ERR_UNKNOWN_SEXP);
n = snext (&s);
if (!n)
return gpg_error (GPG_ERR_INV_SEXP);
}
if (!smatch (&s, n, "e"))
return gpg_error (GPG_ERR_UNKNOWN_SEXP);
n = snext (&s);
}
else
return gpg_error (GPG_ERR_UNSUPPORTED_ALGORITHM);
if (!n)
return gpg_error (GPG_ERR_UNKNOWN_SEXP);
ciphertext = s;
ciphertextlen = n;
rc = ask_for_card (ctrl, shadow_info, &kid);
if (rc)
return rc;
rc = agent_card_pkdecrypt (ctrl, kid, getpin_cb, ctrl, NULL,
ciphertext, ciphertextlen,
&plaintext, &plaintextlen, r_padding);
if (!rc)
{
*r_buf = plaintext;
*r_len = plaintextlen;
}
xfree (kid);
return rc;
}
int
divert_writekey (ctrl_t ctrl, int force, const char *serialno,
const char *id, const char *keydata, size_t keydatalen)
{
return agent_card_writekey (ctrl, force, serialno, id, keydata, keydatalen,
getpin_cb, ctrl);
}
int
divert_generic_cmd (ctrl_t ctrl, const char *cmdline, void *assuan_context)
{
return agent_card_scd (ctrl, cmdline, getpin_cb, ctrl, assuan_context);
}
diff --git a/agent/gpg-agent.c b/agent/gpg-agent.c
index 27cb70cf3..3dcbbf802 100644
--- a/agent/gpg-agent.c
+++ b/agent/gpg-agent.c
@@ -1,3269 +1,3269 @@
/* gpg-agent.c - The GnuPG Agent
* Copyright (C) 2000-2007, 2009-2010 Free Software Foundation, Inc.
* Copyright (C) 2000-2016 Werner Koch
*
* This file is part of GnuPG.
*
* GnuPG is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* GnuPG is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see .
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#ifdef HAVE_W32_SYSTEM
# ifndef WINVER
# define WINVER 0x0500 /* Same as in common/sysutils.c */
# endif
# ifdef HAVE_WINSOCK2_H
# include
# endif
# include
# include
#else /*!HAVE_W32_SYSTEM*/
# include
# include
#endif /*!HAVE_W32_SYSTEM*/
#include
#ifdef HAVE_SIGNAL_H
# include
#endif
#include
#define INCLUDED_BY_MAIN_MODULE 1
#define GNUPG_COMMON_NEED_AFLOCAL
#include "agent.h"
#include /* Malloc hooks and socket wrappers. */
#include "../common/i18n.h"
#include "../common/sysutils.h"
#include "../common/gc-opt-flags.h"
#include "../common/exechelp.h"
#include "../common/asshelp.h"
#include "../common/init.h"
enum cmd_and_opt_values
{ aNull = 0,
oCsh = 'c',
oQuiet = 'q',
oSh = 's',
oVerbose = 'v',
oNoVerbose = 500,
aGPGConfList,
aGPGConfTest,
aUseStandardSocketP,
oOptions,
oDebug,
oDebugAll,
oDebugLevel,
oDebugWait,
oDebugQuickRandom,
oDebugPinentry,
oNoGreeting,
oNoOptions,
oHomedir,
oNoDetach,
oGrab,
oNoGrab,
oLogFile,
oServer,
oDaemon,
oSupervised,
oBatch,
oPinentryProgram,
oPinentryTouchFile,
oPinentryInvisibleChar,
oPinentryTimeout,
oDisplay,
oTTYname,
oTTYtype,
oLCctype,
oLCmessages,
oXauthority,
oScdaemonProgram,
oDefCacheTTL,
oDefCacheTTLSSH,
oMaxCacheTTL,
oMaxCacheTTLSSH,
oEnforcePassphraseConstraints,
oMinPassphraseLen,
oMinPassphraseNonalpha,
oCheckPassphrasePattern,
oMaxPassphraseDays,
oEnablePassphraseHistory,
oEnableExtendedKeyFormat,
oUseStandardSocket,
oNoUseStandardSocket,
oExtraSocket,
oBrowserSocket,
oFakedSystemTime,
oIgnoreCacheForSigning,
oAllowMarkTrusted,
oNoAllowMarkTrusted,
oAllowPresetPassphrase,
oAllowLoopbackPinentry,
oNoAllowLoopbackPinentry,
oNoAllowExternalCache,
oAllowEmacsPinentry,
oKeepTTY,
oKeepDISPLAY,
oSSHSupport,
oSSHFingerprintDigest,
oPuttySupport,
oDisableScdaemon,
oDisableCheckOwnSocket,
oS2KCount,
oS2KCalibration,
oAutoExpandSecmem,
oListenBacklog,
oWriteEnvFile
};
#ifndef ENAMETOOLONG
# define ENAMETOOLONG EINVAL
#endif
static ARGPARSE_OPTS opts[] = {
ARGPARSE_c (aGPGConfList, "gpgconf-list", "@"),
ARGPARSE_c (aGPGConfTest, "gpgconf-test", "@"),
ARGPARSE_c (aUseStandardSocketP, "use-standard-socket-p", "@"),
ARGPARSE_group (301, N_("@Options:\n ")),
ARGPARSE_s_n (oDaemon, "daemon", N_("run in daemon mode (background)")),
ARGPARSE_s_n (oServer, "server", N_("run in server mode (foreground)")),
#ifndef HAVE_W32_SYSTEM
ARGPARSE_s_n (oSupervised, "supervised", N_("run in supervised mode")),
#endif
ARGPARSE_s_n (oVerbose, "verbose", N_("verbose")),
ARGPARSE_s_n (oQuiet, "quiet", N_("be somewhat more quiet")),
ARGPARSE_s_n (oSh, "sh", N_("sh-style command output")),
ARGPARSE_s_n (oCsh, "csh", N_("csh-style command output")),
ARGPARSE_s_s (oOptions, "options", N_("|FILE|read options from FILE")),
ARGPARSE_s_s (oDebug, "debug", "@"),
ARGPARSE_s_n (oDebugAll, "debug-all", "@"),
ARGPARSE_s_s (oDebugLevel, "debug-level", "@"),
ARGPARSE_s_i (oDebugWait, "debug-wait", "@"),
ARGPARSE_s_n (oDebugQuickRandom, "debug-quick-random", "@"),
ARGPARSE_s_n (oDebugPinentry, "debug-pinentry", "@"),
ARGPARSE_s_n (oNoDetach, "no-detach", N_("do not detach from the console")),
ARGPARSE_s_n (oGrab, "grab", "@"),
/* FIXME: Add the below string for 2.3 */
/* N_("let PIN-Entry grab keyboard and mouse")), */
ARGPARSE_s_n (oNoGrab, "no-grab", "@"),
ARGPARSE_s_s (oLogFile, "log-file", N_("use a log file for the server")),
ARGPARSE_s_s (oPinentryProgram, "pinentry-program",
/* */ N_("|PGM|use PGM as the PIN-Entry program")),
ARGPARSE_s_s (oPinentryTouchFile, "pinentry-touch-file", "@"),
ARGPARSE_s_s (oPinentryInvisibleChar, "pinentry-invisible-char", "@"),
ARGPARSE_s_u (oPinentryTimeout, "pinentry-timeout", "@"),
ARGPARSE_s_s (oScdaemonProgram, "scdaemon-program",
/* */ N_("|PGM|use PGM as the SCdaemon program") ),
ARGPARSE_s_n (oDisableScdaemon, "disable-scdaemon",
/* */ N_("do not use the SCdaemon") ),
ARGPARSE_s_n (oDisableCheckOwnSocket, "disable-check-own-socket", "@"),
ARGPARSE_s_s (oExtraSocket, "extra-socket",
/* */ N_("|NAME|accept some commands via NAME")),
ARGPARSE_s_s (oBrowserSocket, "browser-socket", "@"),
ARGPARSE_s_s (oFakedSystemTime, "faked-system-time", "@"),
ARGPARSE_s_n (oBatch, "batch", "@"),
ARGPARSE_s_s (oHomedir, "homedir", "@"),
ARGPARSE_s_s (oDisplay, "display", "@"),
ARGPARSE_s_s (oTTYname, "ttyname", "@"),
ARGPARSE_s_s (oTTYtype, "ttytype", "@"),
ARGPARSE_s_s (oLCctype, "lc-ctype", "@"),
ARGPARSE_s_s (oLCmessages, "lc-messages", "@"),
ARGPARSE_s_s (oXauthority, "xauthority", "@"),
ARGPARSE_s_n (oKeepTTY, "keep-tty",
/* */ N_("ignore requests to change the TTY")),
ARGPARSE_s_n (oKeepDISPLAY, "keep-display",
/* */ N_("ignore requests to change the X display")),
ARGPARSE_s_u (oDefCacheTTL, "default-cache-ttl",
N_("|N|expire cached PINs after N seconds")),
ARGPARSE_s_u (oDefCacheTTLSSH, "default-cache-ttl-ssh", "@" ),
ARGPARSE_s_u (oMaxCacheTTL, "max-cache-ttl", "@" ),
ARGPARSE_s_u (oMaxCacheTTLSSH, "max-cache-ttl-ssh", "@" ),
ARGPARSE_s_n (oEnforcePassphraseConstraints, "enforce-passphrase-constraints",
/* */ "@"),
ARGPARSE_s_u (oMinPassphraseLen, "min-passphrase-len", "@"),
ARGPARSE_s_u (oMinPassphraseNonalpha, "min-passphrase-nonalpha", "@"),
ARGPARSE_s_s (oCheckPassphrasePattern, "check-passphrase-pattern", "@"),
ARGPARSE_s_u (oMaxPassphraseDays, "max-passphrase-days", "@"),
ARGPARSE_s_n (oEnablePassphraseHistory, "enable-passphrase-history", "@"),
ARGPARSE_s_n (oIgnoreCacheForSigning, "ignore-cache-for-signing",
/* */ N_("do not use the PIN cache when signing")),
ARGPARSE_s_n (oNoAllowExternalCache, "no-allow-external-cache",
/* */ N_("disallow the use of an external password cache")),
ARGPARSE_s_n (oNoAllowMarkTrusted, "no-allow-mark-trusted",
/* */ N_("disallow clients to mark keys as \"trusted\"")),
ARGPARSE_s_n (oAllowMarkTrusted, "allow-mark-trusted", "@"),
ARGPARSE_s_n (oAllowPresetPassphrase, "allow-preset-passphrase",
/* */ N_("allow presetting passphrase")),
ARGPARSE_s_n (oNoAllowLoopbackPinentry, "no-allow-loopback-pinentry",
N_("disallow caller to override the pinentry")),
ARGPARSE_s_n (oAllowLoopbackPinentry, "allow-loopback-pinentry", "@"),
ARGPARSE_s_n (oAllowEmacsPinentry, "allow-emacs-pinentry",
/* */ N_("allow passphrase to be prompted through Emacs")),
ARGPARSE_s_n (oSSHSupport, "enable-ssh-support", N_("enable ssh support")),
ARGPARSE_s_s (oSSHFingerprintDigest, "ssh-fingerprint-digest",
N_("|ALGO|use ALGO to show ssh fingerprints")),
ARGPARSE_s_n (oPuttySupport, "enable-putty-support",
#ifdef HAVE_W32_SYSTEM
/* */ N_("enable putty support")
#else
/* */ "@"
#endif
),
ARGPARSE_s_n (oEnableExtendedKeyFormat, "enable-extended-key-format", "@"),
ARGPARSE_s_u (oS2KCount, "s2k-count", "@"),
ARGPARSE_s_u (oS2KCalibration, "s2k-calibration", "@"),
ARGPARSE_op_u (oAutoExpandSecmem, "auto-expand-secmem", "@"),
ARGPARSE_s_i (oListenBacklog, "listen-backlog", "@"),
/* Dummy options for backward compatibility. */
ARGPARSE_o_s (oWriteEnvFile, "write-env-file", "@"),
ARGPARSE_s_n (oUseStandardSocket, "use-standard-socket", "@"),
ARGPARSE_s_n (oNoUseStandardSocket, "no-use-standard-socket", "@"),
ARGPARSE_end () /* End of list */
};
/* The list of supported debug flags. */
static struct debug_flags_s debug_flags [] =
{
{ DBG_MPI_VALUE , "mpi" },
{ DBG_CRYPTO_VALUE , "crypto" },
{ DBG_MEMORY_VALUE , "memory" },
{ DBG_CACHE_VALUE , "cache" },
{ DBG_MEMSTAT_VALUE, "memstat" },
{ DBG_HASHING_VALUE, "hashing" },
{ DBG_IPC_VALUE , "ipc" },
{ 77, NULL } /* 77 := Do not exit on "help" or "?". */
};
#define DEFAULT_CACHE_TTL (10*60) /* 10 minutes */
#define DEFAULT_CACHE_TTL_SSH (30*60) /* 30 minutes */
#define MAX_CACHE_TTL (120*60) /* 2 hours */
#define MAX_CACHE_TTL_SSH (120*60) /* 2 hours */
#define MIN_PASSPHRASE_LEN (8)
#define MIN_PASSPHRASE_NONALPHA (1)
#define MAX_PASSPHRASE_DAYS (0)
/* The timer tick used for housekeeping stuff. Note that on Windows
* we use a SetWaitableTimer seems to signal earlier than about 2
* seconds. Thus we use 4 seconds on all platforms except for
* Windowsce. CHECK_OWN_SOCKET_INTERVAL defines how often we check
* our own socket in standard socket mode. If that value is 0 we
* don't check at all. All values are in seconds. */
#if defined(HAVE_W32CE_SYSTEM)
# define TIMERTICK_INTERVAL (60)
# define CHECK_OWN_SOCKET_INTERVAL (0) /* Never */
#else
# define TIMERTICK_INTERVAL (4)
# define CHECK_OWN_SOCKET_INTERVAL (60)
#endif
/* Flag indicating that the ssh-agent subsystem has been enabled. */
static int ssh_support;
#ifdef HAVE_W32_SYSTEM
/* Flag indicating that support for Putty has been enabled. */
static int putty_support;
/* A magic value used with WM_COPYDATA. */
#define PUTTY_IPC_MAGIC 0x804e50ba
/* To avoid surprises we limit the size of the mapped IPC file to this
value. Putty currently (0.62) uses 8k, thus 16k should be enough
for the foreseeable future. */
#define PUTTY_IPC_MAXLEN 16384
#endif /*HAVE_W32_SYSTEM*/
/* The list of open file descriptors at startup. Note that this list
* has been allocated using the standard malloc. */
#ifndef HAVE_W32_SYSTEM
static int *startup_fd_list;
#endif
/* The signal mask at startup and a flag telling whether it is valid. */
#ifdef HAVE_SIGPROCMASK
static sigset_t startup_signal_mask;
static int startup_signal_mask_valid;
#endif
/* Flag to indicate that a shutdown was requested. */
static int shutdown_pending;
/* Counter for the currently running own socket checks. */
static int check_own_socket_running;
/* Flags to indicate that check_own_socket shall not be called. */
static int disable_check_own_socket;
/* Flag indicating that we are in supervised mode. */
static int is_supervised;
/* Flag to inhibit socket removal in cleanup. */
static int inhibit_socket_removal;
/* It is possible that we are currently running under setuid permissions */
static int maybe_setuid = 1;
/* Name of the communication socket used for native gpg-agent
requests. The second variable is either NULL or a malloced string
with the real socket name in case it has been redirected. */
static char *socket_name;
static char *redir_socket_name;
/* Name of the optional extra socket used for native gpg-agent requests. */
static char *socket_name_extra;
static char *redir_socket_name_extra;
/* Name of the optional browser socket used for native gpg-agent requests. */
static char *socket_name_browser;
static char *redir_socket_name_browser;
/* Name of the communication socket used for ssh-agent protocol. */
static char *socket_name_ssh;
static char *redir_socket_name_ssh;
/* We need to keep track of the server's nonces (these are dummies for
POSIX systems). */
static assuan_sock_nonce_t socket_nonce;
static assuan_sock_nonce_t socket_nonce_extra;
static assuan_sock_nonce_t socket_nonce_browser;
static assuan_sock_nonce_t socket_nonce_ssh;
/* Value for the listen() backlog argument. We use the same value for
* all sockets - 64 is on current Linux half of the default maximum.
* Let's try this as default. Change at runtime with --listen-backlog. */
static int listen_backlog = 64;
/* Default values for options passed to the pinentry. */
static char *default_display;
static char *default_ttyname;
static char *default_ttytype;
static char *default_lc_ctype;
static char *default_lc_messages;
static char *default_xauthority;
/* Name of a config file, which will be reread on a HUP if it is not NULL. */
static char *config_filename;
/* Helper to implement --debug-level */
static const char *debug_level;
/* Keep track of the current log file so that we can avoid updating
the log file after a SIGHUP if it didn't changed. Malloced. */
static char *current_logfile;
/* The handle_tick() function may test whether a parent is still
* running. We record the PID of the parent here or -1 if it should
* be watched. */
static pid_t parent_pid = (pid_t)(-1);
/* This flag is true if the inotify mechanism for detecting the
* removal of the homedir is active. This flag is used to disable the
* alternative but portable stat based check. */
static int have_homedir_inotify;
/* Depending on how gpg-agent was started, the homedir inotify watch
* may not be reliable. This flag is set if we assume that inotify
* works reliable. */
static int reliable_homedir_inotify;
/* Number of active connections. */
static int active_connections;
/* This object is used to dispatch progress messages from Libgcrypt to
* the right thread. Given that we will have at max only a few dozen
* connections at a time, using a linked list is the easiest way to
* handle this. */
struct progress_dispatch_s
{
struct progress_dispatch_s *next;
/* The control object of the connection. If this is NULL no
* connection is associated with this item and it is free for reuse
* by new connections. */
ctrl_t ctrl;
/* The thread id of (npth_self) of the connection. */
npth_t tid;
/* The callback set by the connection. This is similar to the
* Libgcrypt callback but with the control object passed as the
* first argument. */
void (*cb)(ctrl_t ctrl,
const char *what, int printchar,
int current, int total);
};
struct progress_dispatch_s *progress_dispatch_list;
/*
Local prototypes.
*/
static char *create_socket_name (char *standard_name, int with_homedir);
static gnupg_fd_t create_server_socket (char *name, int primary, int cygwin,
char **r_redir_name,
assuan_sock_nonce_t *nonce);
static void create_directories (void);
static void agent_libgcrypt_progress_cb (void *data, const char *what,
int printchar,
int current, int total);
static void agent_init_default_ctrl (ctrl_t ctrl);
static void agent_deinit_default_ctrl (ctrl_t ctrl);
static void handle_connections (gnupg_fd_t listen_fd,
gnupg_fd_t listen_fd_extra,
gnupg_fd_t listen_fd_browser,
gnupg_fd_t listen_fd_ssh);
static void check_own_socket (void);
static int check_for_running_agent (int silent);
/* Pth wrapper function definitions. */
ASSUAN_SYSTEM_NPTH_IMPL;
/*
Functions.
*/
/* Allocate a string describing a library version by calling a GETFNC.
This function is expected to be called only once. GETFNC is
expected to have a semantic like gcry_check_version (). */
static char *
make_libversion (const char *libname, const char *(*getfnc)(const char*))
{
const char *s;
char *result;
if (maybe_setuid)
{
gcry_control (GCRYCTL_INIT_SECMEM, 0, 0); /* Drop setuid. */
maybe_setuid = 0;
}
s = getfnc (NULL);
result = xmalloc (strlen (libname) + 1 + strlen (s) + 1);
strcpy (stpcpy (stpcpy (result, libname), " "), s);
return result;
}
/* Return strings describing this program. The case values are
described in common/argparse.c:strusage. The values here override
the default values given by strusage. */
static const char *
my_strusage (int level)
{
static char *ver_gcry;
const char *p;
switch (level)
{
case 11: p = "@GPG_AGENT@ (@GNUPG@)";
break;
case 13: p = VERSION; break;
case 17: p = PRINTABLE_OS_NAME; break;
/* TRANSLATORS: @EMAIL@ will get replaced by the actual bug
reporting address. This is so that we can change the
reporting address without breaking the translations. */
case 19: p = _("Please report bugs to <@EMAIL@>.\n"); break;
case 20:
if (!ver_gcry)
ver_gcry = make_libversion ("libgcrypt", gcry_check_version);
p = ver_gcry;
break;
case 1:
case 40: p = _("Usage: @GPG_AGENT@ [options] (-h for help)");
break;
case 41: p = _("Syntax: @GPG_AGENT@ [options] [command [args]]\n"
"Secret key management for @GNUPG@\n");
break;
default: p = NULL;
}
return p;
}
/* Setup the debugging. With the global variable DEBUG_LEVEL set to NULL
only the active debug flags are propagated to the subsystems. With
DEBUG_LEVEL set, a specific set of debug flags is set; thus overriding
all flags already set. Note that we don't fail here, because it is
important to keep gpg-agent running even after re-reading the
options due to a SIGHUP. */
static void
set_debug (void)
{
int numok = (debug_level && digitp (debug_level));
int numlvl = numok? atoi (debug_level) : 0;
if (!debug_level)
;
else if (!strcmp (debug_level, "none") || (numok && numlvl < 1))
opt.debug = 0;
else if (!strcmp (debug_level, "basic") || (numok && numlvl <= 2))
opt.debug = DBG_IPC_VALUE;
else if (!strcmp (debug_level, "advanced") || (numok && numlvl <= 5))
opt.debug = DBG_IPC_VALUE;
else if (!strcmp (debug_level, "expert") || (numok && numlvl <= 8))
opt.debug = (DBG_IPC_VALUE | DBG_CACHE_VALUE);
else if (!strcmp (debug_level, "guru") || numok)
{
opt.debug = ~0;
/* Unless the "guru" string has been used we don't want to allow
hashing debugging. The rationale is that people tend to
select the highest debug value and would then clutter their
disk with debug files which may reveal confidential data. */
if (numok)
opt.debug &= ~(DBG_HASHING_VALUE);
}
else
{
log_error (_("invalid debug-level '%s' given\n"), debug_level);
opt.debug = 0; /* Reset debugging, so that prior debug
statements won't have an undesired effect. */
}
if (opt.debug && !opt.verbose)
opt.verbose = 1;
if (opt.debug && opt.quiet)
opt.quiet = 0;
if (opt.debug & DBG_MPI_VALUE)
gcry_control (GCRYCTL_SET_DEBUG_FLAGS, 2);
if (opt.debug & DBG_CRYPTO_VALUE )
gcry_control (GCRYCTL_SET_DEBUG_FLAGS, 1);
gcry_control (GCRYCTL_SET_VERBOSITY, (int)opt.verbose);
if (opt.debug)
parse_debug_flag (NULL, &opt.debug, debug_flags);
}
/* Helper for cleanup to remove one socket with NAME. REDIR_NAME is
the corresponding real name if the socket has been redirected. */
static void
remove_socket (char *name, char *redir_name)
{
if (name && *name)
{
if (redir_name)
name = redir_name;
gnupg_remove (name);
*name = 0;
}
}
/* Discover which inherited file descriptors correspond to which
* services/sockets offered by gpg-agent, using the LISTEN_FDS and
* LISTEN_FDNAMES convention. The understood labels are "ssh",
* "extra", and "browser". "std" or other labels will be interpreted
* as the standard socket.
*
* This function is designed to log errors when the expected file
* descriptors don't make sense, but to do its best to continue to
* work even in the face of minor misconfigurations.
*
* For more information on the LISTEN_FDS convention, see
* sd_listen_fds(3) on certain Linux distributions.
*/
#ifndef HAVE_W32_SYSTEM
static void
map_supervised_sockets (gnupg_fd_t *r_fd,
gnupg_fd_t *r_fd_extra,
gnupg_fd_t *r_fd_browser,
gnupg_fd_t *r_fd_ssh)
{
struct {
const char *label;
int **fdaddr;
char **nameaddr;
} tbl[] = {
{ "ssh", &r_fd_ssh, &socket_name_ssh },
{ "browser", &r_fd_browser, &socket_name_browser },
{ "extra", &r_fd_extra, &socket_name_extra },
{ "std", &r_fd, &socket_name } /* (Must be the last item.) */
};
const char *envvar;
char **fdnames;
int nfdnames;
int fd_count;
*r_fd = *r_fd_extra = *r_fd_browser = *r_fd_ssh = -1;
/* Print a warning if LISTEN_PID does not match outr pid. */
envvar = getenv ("LISTEN_PID");
if (!envvar)
log_error ("no LISTEN_PID environment variable found in "
"--supervised mode (ignoring)\n");
else if (strtoul (envvar, NULL, 10) != (unsigned long)getpid ())
log_error ("environment variable LISTEN_PID (%lu) does not match"
" our pid (%lu) in --supervised mode (ignoring)\n",
(unsigned long)strtoul (envvar, NULL, 10),
(unsigned long)getpid ());
/* Parse LISTEN_FDNAMES into the array FDNAMES. */
envvar = getenv ("LISTEN_FDNAMES");
if (envvar)
{
fdnames = strtokenize (envvar, ":");
if (!fdnames)
{
log_error ("strtokenize failed: %s\n",
gpg_strerror (gpg_error_from_syserror ()));
agent_exit (1);
}
for (nfdnames=0; fdnames[nfdnames]; nfdnames++)
;
}
else
{
fdnames = NULL;
nfdnames = 0;
}
/* Parse LISTEN_FDS into fd_count or provide a replacement. */
envvar = getenv ("LISTEN_FDS");
if (envvar)
fd_count = atoi (envvar);
else if (fdnames)
{
log_error ("no LISTEN_FDS environment variable found in --supervised"
" mode (relying on LISTEN_FDNAMES instead)\n");
fd_count = nfdnames;
}
else
{
log_error ("no LISTEN_FDS or LISTEN_FDNAMES environment variables "
"found in --supervised mode"
" (assuming 1 active descriptor)\n");
fd_count = 1;
}
if (fd_count < 1)
{
log_error ("--supervised mode expects at least one file descriptor"
" (was told %d, carrying on as though it were 1)\n",
fd_count);
fd_count = 1;
}
/* Assign the descriptors to the return values. */
if (!fdnames)
{
struct stat statbuf;
if (fd_count != 1)
log_error ("no LISTEN_FDNAMES and LISTEN_FDS (%d) != 1"
" in --supervised mode."
" (ignoring all sockets but the first one)\n",
fd_count);
if (fstat (3, &statbuf) == -1 && errno ==EBADF)
log_fatal ("file descriptor 3 must be valid in --supervised mode"
" if LISTEN_FDNAMES is not set\n");
*r_fd = 3;
socket_name = gnupg_get_socket_name (3);
}
else if (fd_count != nfdnames)
{
log_fatal ("number of items in LISTEN_FDNAMES (%d) does not match "
"LISTEN_FDS (%d) in --supervised mode\n",
nfdnames, fd_count);
}
else
{
int i, j, fd;
char *name;
for (i = 0; i < nfdnames; i++)
{
for (j = 0; j < DIM (tbl); j++)
{
if (!strcmp (fdnames[i], tbl[j].label) || j == DIM(tbl)-1)
{
fd = 3 + i;
if (**tbl[j].fdaddr == -1)
{
name = gnupg_get_socket_name (fd);
if (name)
{
**tbl[j].fdaddr = fd;
*tbl[j].nameaddr = name;
log_info ("using fd %d for %s socket (%s)\n",
fd, tbl[j].label, name);
}
else
{
log_error ("cannot listen on fd %d for %s socket\n",
fd, tbl[j].label);
close (fd);
}
}
else
{
log_error ("cannot listen on more than one %s socket\n",
tbl[j].label);
close (fd);
}
break;
}
}
}
}
xfree (fdnames);
}
#endif /*!HAVE_W32_SYSTEM*/
/* Cleanup code for this program. This is either called has an atexit
handler or directly. */
static void
cleanup (void)
{
static int done;
if (done)
return;
done = 1;
deinitialize_module_cache ();
if (!is_supervised && !inhibit_socket_removal)
{
remove_socket (socket_name, redir_socket_name);
if (opt.extra_socket > 1)
remove_socket (socket_name_extra, redir_socket_name_extra);
if (opt.browser_socket > 1)
remove_socket (socket_name_browser, redir_socket_name_browser);
remove_socket (socket_name_ssh, redir_socket_name_ssh);
}
}
/* Handle options which are allowed to be reset after program start.
Return true when the current option in PARGS could be handled and
false if not. As a special feature, passing a value of NULL for
PARGS, resets the options to the default. REREAD should be set
true if it is not the initial option parsing. */
static int
parse_rereadable_options (ARGPARSE_ARGS *pargs, int reread)
{
int i;
if (!pargs)
{ /* reset mode */
opt.quiet = 0;
opt.verbose = 0;
opt.debug = 0;
opt.no_grab = 1;
opt.debug_pinentry = 0;
opt.pinentry_program = NULL;
opt.pinentry_touch_file = NULL;
xfree (opt.pinentry_invisible_char);
opt.pinentry_invisible_char = NULL;
opt.pinentry_timeout = 0;
opt.scdaemon_program = NULL;
opt.def_cache_ttl = DEFAULT_CACHE_TTL;
opt.def_cache_ttl_ssh = DEFAULT_CACHE_TTL_SSH;
opt.max_cache_ttl = MAX_CACHE_TTL;
opt.max_cache_ttl_ssh = MAX_CACHE_TTL_SSH;
opt.enforce_passphrase_constraints = 0;
opt.min_passphrase_len = MIN_PASSPHRASE_LEN;
opt.min_passphrase_nonalpha = MIN_PASSPHRASE_NONALPHA;
opt.check_passphrase_pattern = NULL;
opt.max_passphrase_days = MAX_PASSPHRASE_DAYS;
opt.enable_passphrase_history = 0;
opt.enable_extended_key_format = 0;
opt.ignore_cache_for_signing = 0;
opt.allow_mark_trusted = 1;
opt.allow_external_cache = 1;
opt.allow_loopback_pinentry = 1;
opt.allow_emacs_pinentry = 0;
opt.disable_scdaemon = 0;
disable_check_own_socket = 0;
/* Note: When changing the next line, change also gpgconf_list. */
opt.ssh_fingerprint_digest = GCRY_MD_MD5;
opt.s2k_count = 0;
set_s2k_calibration_time (0); /* Set to default. */
return 1;
}
switch (pargs->r_opt)
{
case oQuiet: opt.quiet = 1; break;
case oVerbose: opt.verbose++; break;
case oDebug:
parse_debug_flag (pargs->r.ret_str, &opt.debug, debug_flags);
break;
case oDebugAll: opt.debug = ~0; break;
case oDebugLevel: debug_level = pargs->r.ret_str; break;
case oDebugPinentry: opt.debug_pinentry = 1; break;
case oLogFile:
if (!reread)
return 0; /* not handeld */
if (!current_logfile || !pargs->r.ret_str
|| strcmp (current_logfile, pargs->r.ret_str))
{
log_set_file (pargs->r.ret_str);
xfree (current_logfile);
current_logfile = xtrystrdup (pargs->r.ret_str);
}
break;
case oNoGrab: opt.no_grab |= 1; break;
case oGrab: opt.no_grab |= 2; break;
case oPinentryProgram: opt.pinentry_program = pargs->r.ret_str; break;
case oPinentryTouchFile: opt.pinentry_touch_file = pargs->r.ret_str; break;
case oPinentryInvisibleChar:
xfree (opt.pinentry_invisible_char);
opt.pinentry_invisible_char = xtrystrdup (pargs->r.ret_str); break;
break;
case oPinentryTimeout: opt.pinentry_timeout = pargs->r.ret_ulong; break;
case oScdaemonProgram: opt.scdaemon_program = pargs->r.ret_str; break;
case oDisableScdaemon: opt.disable_scdaemon = 1; break;
case oDisableCheckOwnSocket: disable_check_own_socket = 1; break;
case oDefCacheTTL: opt.def_cache_ttl = pargs->r.ret_ulong; break;
case oDefCacheTTLSSH: opt.def_cache_ttl_ssh = pargs->r.ret_ulong; break;
case oMaxCacheTTL: opt.max_cache_ttl = pargs->r.ret_ulong; break;
case oMaxCacheTTLSSH: opt.max_cache_ttl_ssh = pargs->r.ret_ulong; break;
case oEnforcePassphraseConstraints:
opt.enforce_passphrase_constraints=1;
break;
case oMinPassphraseLen: opt.min_passphrase_len = pargs->r.ret_ulong; break;
case oMinPassphraseNonalpha:
opt.min_passphrase_nonalpha = pargs->r.ret_ulong;
break;
case oCheckPassphrasePattern:
opt.check_passphrase_pattern = pargs->r.ret_str;
break;
case oMaxPassphraseDays:
opt.max_passphrase_days = pargs->r.ret_ulong;
break;
case oEnablePassphraseHistory:
opt.enable_passphrase_history = 1;
break;
case oEnableExtendedKeyFormat:
opt.enable_extended_key_format = 1;
break;
case oIgnoreCacheForSigning: opt.ignore_cache_for_signing = 1; break;
case oAllowMarkTrusted: opt.allow_mark_trusted = 1; break;
case oNoAllowMarkTrusted: opt.allow_mark_trusted = 0; break;
case oAllowPresetPassphrase: opt.allow_preset_passphrase = 1; break;
case oAllowLoopbackPinentry: opt.allow_loopback_pinentry = 1; break;
case oNoAllowLoopbackPinentry: opt.allow_loopback_pinentry = 0; break;
case oNoAllowExternalCache: opt.allow_external_cache = 0;
break;
case oAllowEmacsPinentry: opt.allow_emacs_pinentry = 1;
break;
case oSSHFingerprintDigest:
i = gcry_md_map_name (pargs->r.ret_str);
if (!i)
log_error (_("selected digest algorithm is invalid\n"));
else
opt.ssh_fingerprint_digest = i;
break;
case oS2KCount:
opt.s2k_count = pargs->r.ret_ulong;
break;
case oS2KCalibration:
set_s2k_calibration_time (pargs->r.ret_ulong);
break;
default:
return 0; /* not handled */
}
return 1; /* handled */
}
/* Fixup some options after all have been processed. */
static void
finalize_rereadable_options (void)
{
/* Hack to allow --grab to override --no-grab. */
if ((opt.no_grab & 2))
opt.no_grab = 0;
}
static void
thread_init_once (void)
{
static int npth_initialized = 0;
if (!npth_initialized)
{
npth_initialized++;
npth_init ();
}
gpgrt_set_syscall_clamp (npth_unprotect, npth_protect);
/* Now that we have set the syscall clamp we need to tell Libgcrypt
* that it should get them from libgpg-error. Note that Libgcrypt
* has already been initialized but at that point nPth was not
* initialized and thus Libgcrypt could not set its system call
* clamp. */
#if GCRYPT_VERSION_NUMBER >= 0x010800 /* 1.8.0 */
gcry_control (GCRYCTL_REINIT_SYSCALL_CLAMP, 0, 0);
#endif
}
static void
initialize_modules (void)
{
thread_init_once ();
assuan_set_system_hooks (ASSUAN_SYSTEM_NPTH);
initialize_module_cache ();
initialize_module_call_pinentry ();
initialize_module_call_scd ();
initialize_module_trustlist ();
}
/* The main entry point. */
int
main (int argc, char **argv )
{
ARGPARSE_ARGS pargs;
int orig_argc;
char **orig_argv;
FILE *configfp = NULL;
char *configname = NULL;
const char *shell;
unsigned configlineno;
int parse_debug = 0;
int default_config =1;
int pipe_server = 0;
int is_daemon = 0;
int nodetach = 0;
int csh_style = 0;
char *logfile = NULL;
int debug_wait = 0;
int gpgconf_list = 0;
gpg_error_t err;
struct assuan_malloc_hooks malloc_hooks;
early_system_init ();
/* Before we do anything else we save the list of currently open
file descriptors and the signal mask. This info is required to
do the exec call properly. We don't need it on Windows. */
#ifndef HAVE_W32_SYSTEM
startup_fd_list = get_all_open_fds ();
#endif /*!HAVE_W32_SYSTEM*/
#ifdef HAVE_SIGPROCMASK
if (!sigprocmask (SIG_UNBLOCK, NULL, &startup_signal_mask))
startup_signal_mask_valid = 1;
#endif /*HAVE_SIGPROCMASK*/
/* Set program name etc. */
set_strusage (my_strusage);
gcry_control (GCRYCTL_SUSPEND_SECMEM_WARN);
/* Please note that we may running SUID(ROOT), so be very CAREFUL
when adding any stuff between here and the call to INIT_SECMEM()
somewhere after the option parsing */
log_set_prefix (GPG_AGENT_NAME, GPGRT_LOG_WITH_PREFIX|GPGRT_LOG_WITH_PID);
/* Make sure that our subsystems are ready. */
i18n_init ();
init_common_subsystems (&argc, &argv);
malloc_hooks.malloc = gcry_malloc;
malloc_hooks.realloc = gcry_realloc;
malloc_hooks.free = gcry_free;
assuan_set_malloc_hooks (&malloc_hooks);
assuan_set_gpg_err_source (GPG_ERR_SOURCE_DEFAULT);
assuan_sock_init ();
assuan_sock_set_system_hooks (ASSUAN_SYSTEM_NPTH);
setup_libassuan_logging (&opt.debug, NULL);
setup_libgcrypt_logging ();
gcry_control (GCRYCTL_USE_SECURE_RNDPOOL);
gcry_set_progress_handler (agent_libgcrypt_progress_cb, NULL);
disable_core_dumps ();
/* Set default options. */
parse_rereadable_options (NULL, 0); /* Reset them to default values. */
shell = getenv ("SHELL");
if (shell && strlen (shell) >= 3 && !strcmp (shell+strlen (shell)-3, "csh") )
csh_style = 1;
/* Record some of the original environment strings. */
{
const char *s;
int idx;
static const char *names[] =
{ "DISPLAY", "TERM", "XAUTHORITY", "PINENTRY_USER_DATA", NULL };
err = 0;
opt.startup_env = session_env_new ();
if (!opt.startup_env)
err = gpg_error_from_syserror ();
for (idx=0; !err && names[idx]; idx++)
{
s = getenv (names[idx]);
if (s)
err = session_env_setenv (opt.startup_env, names[idx], s);
}
if (!err)
{
s = gnupg_ttyname (0);
if (s)
err = session_env_setenv (opt.startup_env, "GPG_TTY", s);
}
if (err)
log_fatal ("error recording startup environment: %s\n",
gpg_strerror (err));
/* Fixme: Better use the locale function here. */
opt.startup_lc_ctype = getenv ("LC_CTYPE");
if (opt.startup_lc_ctype)
opt.startup_lc_ctype = xstrdup (opt.startup_lc_ctype);
opt.startup_lc_messages = getenv ("LC_MESSAGES");
if (opt.startup_lc_messages)
opt.startup_lc_messages = xstrdup (opt.startup_lc_messages);
}
/* Check whether we have a config file on the commandline */
orig_argc = argc;
orig_argv = argv;
pargs.argc = &argc;
pargs.argv = &argv;
pargs.flags= 1|(1<<6); /* do not remove the args, ignore version */
while (arg_parse( &pargs, opts))
{
if (pargs.r_opt == oDebug || pargs.r_opt == oDebugAll)
parse_debug++;
else if (pargs.r_opt == oOptions)
{ /* yes there is one, so we do not try the default one, but
read the option file when it is encountered at the
commandline */
default_config = 0;
}
else if (pargs.r_opt == oNoOptions)
default_config = 0; /* --no-options */
else if (pargs.r_opt == oHomedir)
gnupg_set_homedir (pargs.r.ret_str);
else if (pargs.r_opt == oDebugQuickRandom)
{
gcry_control (GCRYCTL_ENABLE_QUICK_RANDOM, 0);
}
}
/* Initialize the secure memory. */
gcry_control (GCRYCTL_INIT_SECMEM, SECMEM_BUFFER_SIZE, 0);
maybe_setuid = 0;
/*
Now we are now working under our real uid
*/
if (default_config)
configname = make_filename (gnupg_homedir (),
GPG_AGENT_NAME EXTSEP_S "conf", NULL);
argc = orig_argc;
argv = orig_argv;
pargs.argc = &argc;
pargs.argv = &argv;
pargs.flags= 1; /* do not remove the args */
next_pass:
if (configname)
{
configlineno = 0;
configfp = fopen (configname, "r");
if (!configfp)
{
if (default_config)
{
if( parse_debug )
log_info (_("Note: no default option file '%s'\n"),
configname );
/* Save the default conf file name so that
reread_configuration is able to test whether the
config file has been created in the meantime. */
xfree (config_filename);
config_filename = configname;
configname = NULL;
}
else
{
log_error (_("option file '%s': %s\n"),
configname, strerror(errno) );
exit(2);
}
xfree (configname);
configname = NULL;
}
if (parse_debug && configname )
log_info (_("reading options from '%s'\n"), configname );
default_config = 0;
}
while (optfile_parse( configfp, configname, &configlineno, &pargs, opts) )
{
if (parse_rereadable_options (&pargs, 0))
continue; /* Already handled */
switch (pargs.r_opt)
{
case aGPGConfList: gpgconf_list = 1; break;
case aGPGConfTest: gpgconf_list = 2; break;
case aUseStandardSocketP: gpgconf_list = 3; break;
case oBatch: opt.batch=1; break;
case oDebugWait: debug_wait = pargs.r.ret_int; break;
case oOptions:
/* config files may not be nested (silently ignore them) */
if (!configfp)
{
xfree(configname);
configname = xstrdup(pargs.r.ret_str);
goto next_pass;
}
break;
case oNoGreeting: /* Dummy option. */ break;
case oNoVerbose: opt.verbose = 0; break;
case oNoOptions: break; /* no-options */
case oHomedir: gnupg_set_homedir (pargs.r.ret_str); break;
case oNoDetach: nodetach = 1; break;
case oLogFile: logfile = pargs.r.ret_str; break;
case oCsh: csh_style = 1; break;
case oSh: csh_style = 0; break;
case oServer: pipe_server = 1; break;
case oDaemon: is_daemon = 1; break;
case oSupervised: is_supervised = 1; break;
case oDisplay: default_display = xstrdup (pargs.r.ret_str); break;
case oTTYname: default_ttyname = xstrdup (pargs.r.ret_str); break;
case oTTYtype: default_ttytype = xstrdup (pargs.r.ret_str); break;
case oLCctype: default_lc_ctype = xstrdup (pargs.r.ret_str); break;
case oLCmessages: default_lc_messages = xstrdup (pargs.r.ret_str);
break;
case oXauthority: default_xauthority = xstrdup (pargs.r.ret_str);
break;
case oUseStandardSocket:
case oNoUseStandardSocket:
obsolete_option (configname, configlineno, "use-standard-socket");
break;
case oFakedSystemTime:
{
time_t faked_time = isotime2epoch (pargs.r.ret_str);
if (faked_time == (time_t)(-1))
faked_time = (time_t)strtoul (pargs.r.ret_str, NULL, 10);
gnupg_set_time (faked_time, 0);
}
break;
case oKeepTTY: opt.keep_tty = 1; break;
case oKeepDISPLAY: opt.keep_display = 1; break;
case oSSHSupport:
ssh_support = 1;
break;
case oPuttySupport:
# ifdef HAVE_W32_SYSTEM
putty_support = 1;
# endif
break;
case oExtraSocket:
opt.extra_socket = 1; /* (1 = points into argv) */
socket_name_extra = pargs.r.ret_str;
break;
case oBrowserSocket:
opt.browser_socket = 1; /* (1 = points into argv) */
socket_name_browser = pargs.r.ret_str;
break;
case oAutoExpandSecmem:
/* Try to enable this option. It will officially only be
* supported by Libgcrypt 1.9 but 1.8.2 already supports it
* on the quiet and thus we use the numeric value value. */
gcry_control (78 /*GCRYCTL_AUTO_EXPAND_SECMEM*/,
(unsigned int)pargs.r.ret_ulong, 0);
break;
case oListenBacklog:
listen_backlog = pargs.r.ret_int;
break;
case oDebugQuickRandom:
/* Only used by the first stage command line parser. */
break;
case oWriteEnvFile:
obsolete_option (configname, configlineno, "write-env-file");
break;
default : pargs.err = configfp? 1:2; break;
}
}
if (configfp)
{
fclose( configfp );
configfp = NULL;
/* Keep a copy of the name so that it can be read on SIGHUP. */
if (config_filename != configname)
{
xfree (config_filename);
config_filename = configname;
}
configname = NULL;
goto next_pass;
}
xfree (configname);
configname = NULL;
if (log_get_errorcount(0))
exit(2);
finalize_rereadable_options ();
/* Print a warning if an argument looks like an option. */
if (!opt.quiet && !(pargs.flags & ARGPARSE_FLAG_STOP_SEEN))
{
int i;
for (i=0; i < argc; i++)
if (argv[i][0] == '-' && argv[i][1] == '-')
log_info (_("Note: '%s' is not considered an option\n"), argv[i]);
}
#ifdef ENABLE_NLS
/* gpg-agent usually does not output any messages because it runs in
the background. For log files it is acceptable to have messages
always encoded in utf-8. We switch here to utf-8, so that
commands like --help still give native messages. It is far
easier to switch only once instead of for every message and it
actually helps when more then one thread is active (avoids an
extra copy step). */
bind_textdomain_codeset (PACKAGE_GT, "UTF-8");
#endif
if (!pipe_server && !is_daemon && !gpgconf_list && !is_supervised)
{
/* We have been called without any command and thus we merely
check whether an agent is already running. We do this right
here so that we don't clobber a logfile with this check but
print the status directly to stderr. */
opt.debug = 0;
set_debug ();
check_for_running_agent (0);
agent_exit (0);
}
if (is_supervised)
;
else if (!opt.extra_socket)
opt.extra_socket = 1;
else if (socket_name_extra
&& (!strcmp (socket_name_extra, "none")
|| !strcmp (socket_name_extra, "/dev/null")))
{
/* User requested not to create this socket. */
opt.extra_socket = 0;
socket_name_extra = NULL;
}
if (is_supervised)
;
else if (!opt.browser_socket)
opt.browser_socket = 1;
else if (socket_name_browser
&& (!strcmp (socket_name_browser, "none")
|| !strcmp (socket_name_browser, "/dev/null")))
{
/* User requested not to create this socket. */
opt.browser_socket = 0;
socket_name_browser = NULL;
}
set_debug ();
if (atexit (cleanup))
{
log_error ("atexit failed\n");
cleanup ();
exit (1);
}
/* Try to create missing directories. */
create_directories ();
if (debug_wait && pipe_server)
{
thread_init_once ();
log_debug ("waiting for debugger - my pid is %u .....\n",
(unsigned int)getpid());
gnupg_sleep (debug_wait);
log_debug ("... okay\n");
}
if (gpgconf_list == 3)
{
/* We now use the standard socket always - return true for
backward compatibility. */
agent_exit (0);
}
else if (gpgconf_list == 2)
agent_exit (0);
else if (gpgconf_list)
{
char *filename;
char *filename_esc;
/* List options and default values in the GPG Conf format. */
filename = make_filename (gnupg_homedir (),
GPG_AGENT_NAME EXTSEP_S "conf", NULL);
filename_esc = percent_escape (filename, NULL);
es_printf ("%s-%s.conf:%lu:\"%s\n",
GPGCONF_NAME, GPG_AGENT_NAME,
GC_OPT_FLAG_DEFAULT, filename_esc);
xfree (filename);
xfree (filename_esc);
es_printf ("verbose:%lu:\n"
"quiet:%lu:\n"
"debug-level:%lu:\"none:\n"
"log-file:%lu:\n",
GC_OPT_FLAG_NONE|GC_OPT_FLAG_RUNTIME,
GC_OPT_FLAG_NONE|GC_OPT_FLAG_RUNTIME,
GC_OPT_FLAG_DEFAULT|GC_OPT_FLAG_RUNTIME,
GC_OPT_FLAG_NONE|GC_OPT_FLAG_RUNTIME );
es_printf ("default-cache-ttl:%lu:%d:\n",
GC_OPT_FLAG_DEFAULT|GC_OPT_FLAG_RUNTIME, DEFAULT_CACHE_TTL );
es_printf ("default-cache-ttl-ssh:%lu:%d:\n",
GC_OPT_FLAG_DEFAULT|GC_OPT_FLAG_RUNTIME, DEFAULT_CACHE_TTL_SSH );
es_printf ("max-cache-ttl:%lu:%d:\n",
GC_OPT_FLAG_DEFAULT|GC_OPT_FLAG_RUNTIME, MAX_CACHE_TTL );
es_printf ("max-cache-ttl-ssh:%lu:%d:\n",
GC_OPT_FLAG_DEFAULT|GC_OPT_FLAG_RUNTIME, MAX_CACHE_TTL_SSH );
es_printf ("enforce-passphrase-constraints:%lu:\n",
GC_OPT_FLAG_NONE|GC_OPT_FLAG_RUNTIME);
es_printf ("min-passphrase-len:%lu:%d:\n",
GC_OPT_FLAG_DEFAULT|GC_OPT_FLAG_RUNTIME, MIN_PASSPHRASE_LEN );
es_printf ("min-passphrase-nonalpha:%lu:%d:\n",
GC_OPT_FLAG_DEFAULT|GC_OPT_FLAG_RUNTIME,
MIN_PASSPHRASE_NONALPHA);
es_printf ("check-passphrase-pattern:%lu:\n",
GC_OPT_FLAG_DEFAULT|GC_OPT_FLAG_RUNTIME);
es_printf ("max-passphrase-days:%lu:%d:\n",
GC_OPT_FLAG_DEFAULT|GC_OPT_FLAG_RUNTIME,
MAX_PASSPHRASE_DAYS);
es_printf ("enable-passphrase-history:%lu:\n",
GC_OPT_FLAG_NONE|GC_OPT_FLAG_RUNTIME);
es_printf ("no-grab:%lu:\n",
GC_OPT_FLAG_NONE|GC_OPT_FLAG_RUNTIME);
es_printf ("ignore-cache-for-signing:%lu:\n",
GC_OPT_FLAG_NONE|GC_OPT_FLAG_RUNTIME);
es_printf ("no-allow-external-cache:%lu:\n",
GC_OPT_FLAG_NONE|GC_OPT_FLAG_RUNTIME);
es_printf ("no-allow-mark-trusted:%lu:\n",
GC_OPT_FLAG_NONE|GC_OPT_FLAG_RUNTIME);
es_printf ("disable-scdaemon:%lu:\n",
GC_OPT_FLAG_NONE|GC_OPT_FLAG_RUNTIME);
es_printf ("enable-ssh-support:%lu:\n", GC_OPT_FLAG_NONE);
es_printf ("ssh-fingerprint-digest:%lu:\"%s:\n",
GC_OPT_FLAG_DEFAULT|GC_OPT_FLAG_RUNTIME, "md5");
#ifdef HAVE_W32_SYSTEM
es_printf ("enable-putty-support:%lu:\n", GC_OPT_FLAG_NONE);
#endif
es_printf ("no-allow-loopback-pinentry:%lu:\n",
GC_OPT_FLAG_NONE|GC_OPT_FLAG_RUNTIME);
es_printf ("allow-emacs-pinentry:%lu:\n",
GC_OPT_FLAG_NONE|GC_OPT_FLAG_RUNTIME);
es_printf ("pinentry-timeout:%lu:0:\n",
GC_OPT_FLAG_DEFAULT|GC_OPT_FLAG_RUNTIME);
es_printf ("enable-extended-key-format:%lu:\n",
GC_OPT_FLAG_NONE|GC_OPT_FLAG_RUNTIME);
es_printf ("grab:%lu:\n",
GC_OPT_FLAG_NONE|GC_OPT_FLAG_RUNTIME);
agent_exit (0);
}
/* Now start with logging to a file if this is desired. */
if (logfile)
{
log_set_file (logfile);
log_set_prefix (NULL, (GPGRT_LOG_WITH_PREFIX
| GPGRT_LOG_WITH_TIME
| GPGRT_LOG_WITH_PID));
current_logfile = xstrdup (logfile);
}
/* Make sure that we have a default ttyname. */
if (!default_ttyname && gnupg_ttyname (1))
default_ttyname = xstrdup (gnupg_ttyname (1));
if (!default_ttytype && getenv ("TERM"))
default_ttytype = xstrdup (getenv ("TERM"));
if (pipe_server)
{
/* This is the simple pipe based server */
ctrl_t ctrl;
initialize_modules ();
ctrl = xtrycalloc (1, sizeof *ctrl);
if (!ctrl)
{
log_error ("error allocating connection control data: %s\n",
strerror (errno) );
agent_exit (1);
}
ctrl->session_env = session_env_new ();
if (!ctrl->session_env)
{
log_error ("error allocating session environment block: %s\n",
strerror (errno) );
xfree (ctrl);
agent_exit (1);
}
agent_init_default_ctrl (ctrl);
start_command_handler (ctrl, GNUPG_INVALID_FD, GNUPG_INVALID_FD);
agent_deinit_default_ctrl (ctrl);
xfree (ctrl);
}
else if (is_supervised)
{
#ifndef HAVE_W32_SYSTEM
gnupg_fd_t fd, fd_extra, fd_browser, fd_ssh;
initialize_modules ();
/* when supervised and sending logs to stderr, the process
supervisor should handle log entry metadata (pid, name,
timestamp) */
if (!logfile)
log_set_prefix (NULL, 0);
log_info ("%s %s starting in supervised mode.\n",
strusage(11), strusage(13) );
/* See below in "regular server mode" on why we remove certain
* envvars. */
if (!opt.keep_display)
gnupg_unsetenv ("DISPLAY");
gnupg_unsetenv ("INSIDE_EMACS");
/* Virtually create the sockets. Note that we use -1 here
* because the whole thing works only on Unix. */
map_supervised_sockets (&fd, &fd_extra, &fd_browser, &fd_ssh);
if (fd == -1)
log_fatal ("no standard socket provided\n");
#ifdef HAVE_SIGPROCMASK
if (startup_signal_mask_valid)
{
if (sigprocmask (SIG_SETMASK, &startup_signal_mask, NULL))
log_error ("error restoring signal mask: %s\n",
strerror (errno));
}
else
log_info ("no saved signal mask\n");
#endif /*HAVE_SIGPROCMASK*/
log_info ("listening on: std=%d extra=%d browser=%d ssh=%d\n",
fd, fd_extra, fd_browser, fd_ssh);
handle_connections (fd, fd_extra, fd_browser, fd_ssh);
#endif /*!HAVE_W32_SYSTEM*/
}
else if (!is_daemon)
; /* NOTREACHED */
else
{ /* Regular server mode */
gnupg_fd_t fd;
gnupg_fd_t fd_extra = GNUPG_INVALID_FD;
gnupg_fd_t fd_browser = GNUPG_INVALID_FD;
gnupg_fd_t fd_ssh = GNUPG_INVALID_FD;
#ifndef HAVE_W32_SYSTEM
pid_t pid;
#endif
/* Remove the DISPLAY variable so that a pinentry does not
default to a specific display. There is still a default
display when gpg-agent was started using --display or a
client requested this using an OPTION command. Note, that we
don't do this when running in reverse daemon mode (i.e. when
exec the program given as arguments). */
#ifndef HAVE_W32_SYSTEM
if (!opt.keep_display && !argc)
gnupg_unsetenv ("DISPLAY");
#endif
/* Remove the INSIDE_EMACS variable so that a pinentry does not
always try to interact with Emacs. The variable is set when
a client requested this using an OPTION command. */
gnupg_unsetenv ("INSIDE_EMACS");
/* Create the sockets. */
socket_name = create_socket_name (GPG_AGENT_SOCK_NAME, 1);
fd = create_server_socket (socket_name, 1, 0,
&redir_socket_name, &socket_nonce);
if (opt.extra_socket)
{
if (socket_name_extra)
socket_name_extra = create_socket_name (socket_name_extra, 0);
else
socket_name_extra = create_socket_name
/**/ (GPG_AGENT_EXTRA_SOCK_NAME, 1);
opt.extra_socket = 2; /* Indicate that it has been malloced. */
fd_extra = create_server_socket (socket_name_extra, 0, 0,
&redir_socket_name_extra,
&socket_nonce_extra);
}
if (opt.browser_socket)
{
if (socket_name_browser)
socket_name_browser = create_socket_name (socket_name_browser, 0);
else
socket_name_browser= create_socket_name
/**/ (GPG_AGENT_BROWSER_SOCK_NAME, 1);
opt.browser_socket = 2; /* Indicate that it has been malloced. */
fd_browser = create_server_socket (socket_name_browser, 0, 0,
&redir_socket_name_browser,
&socket_nonce_browser);
}
socket_name_ssh = create_socket_name (GPG_AGENT_SSH_SOCK_NAME, 1);
fd_ssh = create_server_socket (socket_name_ssh, 0, 1,
&redir_socket_name_ssh,
&socket_nonce_ssh);
/* If we are going to exec a program in the parent, we record
the PID, so that the child may check whether the program is
still alive. */
if (argc)
parent_pid = getpid ();
fflush (NULL);
#ifdef HAVE_W32_SYSTEM
(void)csh_style;
(void)nodetach;
initialize_modules ();
#else /*!HAVE_W32_SYSTEM*/
pid = fork ();
if (pid == (pid_t)-1)
{
log_fatal ("fork failed: %s\n", strerror (errno) );
exit (1);
}
else if (pid)
{ /* We are the parent */
char *infostr_ssh_sock, *infostr_ssh_valid;
/* Close the socket FD. */
close (fd);
/* The signal mask might not be correct right now and thus
we restore it. That is not strictly necessary but some
programs falsely assume a cleared signal mask. */
#ifdef HAVE_SIGPROCMASK
if (startup_signal_mask_valid)
{
if (sigprocmask (SIG_SETMASK, &startup_signal_mask, NULL))
log_error ("error restoring signal mask: %s\n",
strerror (errno));
}
else
log_info ("no saved signal mask\n");
#endif /*HAVE_SIGPROCMASK*/
/* Create the SSH info string if enabled. */
if (ssh_support)
{
if (asprintf (&infostr_ssh_sock, "SSH_AUTH_SOCK=%s",
socket_name_ssh) < 0)
{
log_error ("out of core\n");
kill (pid, SIGTERM);
exit (1);
}
if (asprintf (&infostr_ssh_valid, "gnupg_SSH_AUTH_SOCK_by=%lu",
(unsigned long)getpid()) < 0)
{
log_error ("out of core\n");
kill (pid, SIGTERM);
exit (1);
}
}
*socket_name = 0; /* Don't let cleanup() remove the socket -
the child should do this from now on */
if (opt.extra_socket)
*socket_name_extra = 0;
if (opt.browser_socket)
*socket_name_browser = 0;
*socket_name_ssh = 0;
if (argc)
{ /* Run the program given on the commandline. */
if (ssh_support && (putenv (infostr_ssh_sock)
|| putenv (infostr_ssh_valid)))
{
log_error ("failed to set environment: %s\n",
strerror (errno) );
kill (pid, SIGTERM );
exit (1);
}
/* Close all the file descriptors except the standard
ones and those open at startup. We explicitly don't
close 0,1,2 in case something went wrong collecting
them at startup. */
close_all_fds (3, startup_fd_list);
/* Run the command. */
execvp (argv[0], argv);
log_error ("failed to run the command: %s\n", strerror (errno));
kill (pid, SIGTERM);
exit (1);
}
else
{
/* Print the environment string, so that the caller can use
shell's eval to set it */
if (csh_style)
{
if (ssh_support)
{
*strchr (infostr_ssh_sock, '=') = ' ';
es_printf ("setenv %s;\n", infostr_ssh_sock);
}
}
else
{
if (ssh_support)
{
es_printf ("%s; export SSH_AUTH_SOCK;\n",
infostr_ssh_sock);
}
}
if (ssh_support)
{
xfree (infostr_ssh_sock);
xfree (infostr_ssh_valid);
}
exit (0);
}
/*NOTREACHED*/
} /* End parent */
/*
This is the child
*/
initialize_modules ();
/* Detach from tty and put process into a new session */
if (!nodetach )
{
int i;
unsigned int oldflags;
/* Close stdin, stdout and stderr unless it is the log stream */
for (i=0; i <= 2; i++)
{
if (!log_test_fd (i) && i != fd )
{
if ( ! close (i)
&& open ("/dev/null", i? O_WRONLY : O_RDONLY) == -1)
{
log_error ("failed to open '%s': %s\n",
"/dev/null", strerror (errno));
cleanup ();
exit (1);
}
}
}
if (setsid() == -1)
{
log_error ("setsid() failed: %s\n", strerror(errno) );
cleanup ();
exit (1);
}
log_get_prefix (&oldflags);
log_set_prefix (NULL, oldflags | GPGRT_LOG_RUN_DETACHED);
opt.running_detached = 1;
/* Unless we are running with a program given on the command
* line we can assume that the inotify things works and thus
* we can avoid tye regular stat calls. */
if (!argc)
reliable_homedir_inotify = 1;
}
{
struct sigaction sa;
sa.sa_handler = SIG_IGN;
sigemptyset (&sa.sa_mask);
sa.sa_flags = 0;
sigaction (SIGPIPE, &sa, NULL);
}
#endif /*!HAVE_W32_SYSTEM*/
if (gnupg_chdir (gnupg_daemon_rootdir ()))
{
log_error ("chdir to '%s' failed: %s\n",
gnupg_daemon_rootdir (), strerror (errno));
exit (1);
}
log_info ("%s %s started\n", strusage(11), strusage(13) );
handle_connections (fd, fd_extra, fd_browser, fd_ssh);
assuan_sock_close (fd);
}
return 0;
}
/* Exit entry point. This function should be called instead of a
plain exit. */
void
agent_exit (int rc)
{
/*FIXME: update_random_seed_file();*/
/* We run our cleanup handler because that may close cipher contexts
stored in secure memory and thus this needs to be done before we
explicitly terminate secure memory. */
cleanup ();
#if 1
/* at this time a bit annoying */
if (opt.debug & DBG_MEMSTAT_VALUE)
{
gcry_control( GCRYCTL_DUMP_MEMORY_STATS );
gcry_control( GCRYCTL_DUMP_RANDOM_STATS );
}
if (opt.debug)
gcry_control (GCRYCTL_DUMP_SECMEM_STATS );
#endif
gcry_control (GCRYCTL_TERM_SECMEM );
rc = rc? rc : log_get_errorcount(0)? 2 : 0;
exit (rc);
}
/* This is our callback function for gcrypt progress messages. It is
set once at startup and dispatches progress messages to the
corresponding threads of the agent. */
static void
agent_libgcrypt_progress_cb (void *data, const char *what, int printchar,
int current, int total)
{
struct progress_dispatch_s *dispatch;
npth_t mytid = npth_self ();
(void)data;
for (dispatch = progress_dispatch_list; dispatch; dispatch = dispatch->next)
if (dispatch->ctrl && dispatch->tid == mytid)
break;
if (dispatch && dispatch->cb)
dispatch->cb (dispatch->ctrl, what, printchar, current, total);
/* Libgcrypt < 1.8 does not know about nPth and thus when it reads
* from /dev/random this will block the process. To mitigate this
* problem we yield the thread when Libgcrypt tells us that it needs
* more entropy. This way other threads have chance to run. */
#if GCRYPT_VERSION_NUMBER < 0x010800 /* 1.8.0 */
if (what && !strcmp (what, "need_entropy"))
{
#if GPGRT_VERSION_NUMBER < 0x011900 /* 1.25 */
/* In older gpg-error versions gpgrt_yield is buggy for use with
* nPth and thus we need to resort to a sleep call. */
npth_usleep (1000); /* 1ms */
#else
gpgrt_yield ();
#endif
}
#endif
}
/* If a progress dispatcher callback has been associated with the
* current connection unregister it. */
static void
unregister_progress_cb (void)
{
struct progress_dispatch_s *dispatch;
npth_t mytid = npth_self ();
for (dispatch = progress_dispatch_list; dispatch; dispatch = dispatch->next)
if (dispatch->ctrl && dispatch->tid == mytid)
break;
if (dispatch)
{
dispatch->ctrl = NULL;
dispatch->cb = NULL;
}
}
/* Setup a progress callback CB for the current connection. Using a
* CB of NULL disables the callback. */
void
agent_set_progress_cb (void (*cb)(ctrl_t ctrl, const char *what,
int printchar, int current, int total),
ctrl_t ctrl)
{
struct progress_dispatch_s *dispatch, *firstfree;
npth_t mytid = npth_self ();
firstfree = NULL;
for (dispatch = progress_dispatch_list; dispatch; dispatch = dispatch->next)
{
if (dispatch->ctrl && dispatch->tid == mytid)
break;
if (!dispatch->ctrl && !firstfree)
firstfree = dispatch;
}
if (!dispatch) /* None allocated: Reuse or allocate a new one. */
{
if (firstfree)
{
dispatch = firstfree;
}
else if ((dispatch = xtrycalloc (1, sizeof *dispatch)))
{
dispatch->next = progress_dispatch_list;
progress_dispatch_list = dispatch;
}
else
{
log_error ("error allocating new progress dispatcher slot: %s\n",
gpg_strerror (gpg_error_from_syserror ()));
return;
}
dispatch->ctrl = ctrl;
dispatch->tid = mytid;
}
dispatch->cb = cb;
}
/* Each thread has its own local variables conveyed by a control
structure usually identified by an argument named CTRL. This
function is called immediately after allocating the control
structure. Its purpose is to setup the default values for that
structure. Note that some values may have already been set. */
static void
agent_init_default_ctrl (ctrl_t ctrl)
{
assert (ctrl->session_env);
/* Note we ignore malloc errors because we can't do much about it
and the request will fail anyway shortly after this
initialization. */
session_env_setenv (ctrl->session_env, "DISPLAY", default_display);
session_env_setenv (ctrl->session_env, "GPG_TTY", default_ttyname);
session_env_setenv (ctrl->session_env, "TERM", default_ttytype);
session_env_setenv (ctrl->session_env, "XAUTHORITY", default_xauthority);
session_env_setenv (ctrl->session_env, "PINENTRY_USER_DATA", NULL);
if (ctrl->lc_ctype)
xfree (ctrl->lc_ctype);
ctrl->lc_ctype = default_lc_ctype? xtrystrdup (default_lc_ctype) : NULL;
if (ctrl->lc_messages)
xfree (ctrl->lc_messages);
ctrl->lc_messages = default_lc_messages? xtrystrdup (default_lc_messages)
/**/ : NULL;
ctrl->cache_ttl_opt_preset = CACHE_TTL_OPT_PRESET;
}
/* Release all resources allocated by default in the control
structure. This is the counterpart to agent_init_default_ctrl. */
static void
agent_deinit_default_ctrl (ctrl_t ctrl)
{
unregister_progress_cb ();
session_env_release (ctrl->session_env);
if (ctrl->lc_ctype)
xfree (ctrl->lc_ctype);
if (ctrl->lc_messages)
xfree (ctrl->lc_messages);
}
/* Because the ssh protocol does not send us information about the
current TTY setting, we use this function to use those from startup
or those explicitly set. This is also used for the restricted mode
where we ignore requests to change the environment. */
gpg_error_t
agent_copy_startup_env (ctrl_t ctrl)
{
gpg_error_t err = 0;
int iterator = 0;
const char *name, *value;
while (!err && (name = session_env_list_stdenvnames (&iterator, NULL)))
{
if ((value = session_env_getenv (opt.startup_env, name)))
err = session_env_setenv (ctrl->session_env, name, value);
}
if (!err && !ctrl->lc_ctype && opt.startup_lc_ctype)
if (!(ctrl->lc_ctype = xtrystrdup (opt.startup_lc_ctype)))
err = gpg_error_from_syserror ();
if (!err && !ctrl->lc_messages && opt.startup_lc_messages)
if (!(ctrl->lc_messages = xtrystrdup (opt.startup_lc_messages)))
err = gpg_error_from_syserror ();
if (err)
log_error ("error setting default session environment: %s\n",
gpg_strerror (err));
return err;
}
/* Reread parts of the configuration. Note, that this function is
obviously not thread-safe and should only be called from the PTH
signal handler.
Fixme: Due to the way the argument parsing works, we create a
memory leak here for all string type arguments. There is currently
no clean way to tell whether the memory for the argument has been
allocated or points into the process' original arguments. Unless
we have a mechanism to tell this, we need to live on with this. */
static void
reread_configuration (void)
{
ARGPARSE_ARGS pargs;
FILE *fp;
unsigned int configlineno = 0;
int dummy;
if (!config_filename)
return; /* No config file. */
fp = fopen (config_filename, "r");
if (!fp)
{
log_info (_("option file '%s': %s\n"),
config_filename, strerror(errno) );
return;
}
parse_rereadable_options (NULL, 1); /* Start from the default values. */
memset (&pargs, 0, sizeof pargs);
dummy = 0;
pargs.argc = &dummy;
pargs.flags = 1; /* do not remove the args */
while (optfile_parse (fp, config_filename, &configlineno, &pargs, opts) )
{
if (pargs.r_opt < -1)
pargs.err = 1; /* Print a warning. */
else /* Try to parse this option - ignore unchangeable ones. */
parse_rereadable_options (&pargs, 1);
}
fclose (fp);
finalize_rereadable_options ();
set_debug ();
}
/* Return the file name of the socket we are using for native
requests. */
const char *
get_agent_socket_name (void)
{
const char *s = socket_name;
return (s && *s)? s : NULL;
}
/* Return the file name of the socket we are using for SSH
requests. */
const char *
get_agent_ssh_socket_name (void)
{
const char *s = socket_name_ssh;
return (s && *s)? s : NULL;
}
/* Return the number of active connections. */
int
get_agent_active_connection_count (void)
{
return active_connections;
}
/* Under W32, this function returns the handle of the scdaemon
notification event. Calling it the first time creates that
event. */
#if defined(HAVE_W32_SYSTEM) && !defined(HAVE_W32CE_SYSTEM)
void *
get_agent_scd_notify_event (void)
{
static HANDLE the_event = INVALID_HANDLE_VALUE;
if (the_event == INVALID_HANDLE_VALUE)
{
HANDLE h, h2;
SECURITY_ATTRIBUTES sa = { sizeof (SECURITY_ATTRIBUTES), NULL, TRUE};
/* We need to use a manual reset event object due to the way our
w32-pth wait function works: If we would use an automatic
reset event we are not able to figure out which handle has
been signaled because at the time we single out the signaled
handles using WFSO the event has already been reset due to
the WFMO. */
h = CreateEvent (&sa, TRUE, FALSE, NULL);
if (!h)
log_error ("can't create scd notify event: %s\n", w32_strerror (-1) );
else if (!DuplicateHandle (GetCurrentProcess(), h,
GetCurrentProcess(), &h2,
EVENT_MODIFY_STATE|SYNCHRONIZE, TRUE, 0))
{
log_error ("setting synchronize for scd notify event failed: %s\n",
w32_strerror (-1) );
CloseHandle (h);
}
else
{
CloseHandle (h);
the_event = h2;
}
}
return the_event;
}
#endif /*HAVE_W32_SYSTEM && !HAVE_W32CE_SYSTEM*/
/* Create a name for the socket in the home directory as using
STANDARD_NAME. We also check for valid characters as well as
against a maximum allowed length for a unix domain socket is done.
The function terminates the process in case of an error. Returns:
Pointer to an allocated string with the absolute name of the socket
used. */
static char *
create_socket_name (char *standard_name, int with_homedir)
{
char *name;
if (with_homedir)
name = make_filename (gnupg_socketdir (), standard_name, NULL);
else
name = make_filename (standard_name, NULL);
if (strchr (name, PATHSEP_C))
{
log_error (("'%s' are not allowed in the socket name\n"), PATHSEP_S);
agent_exit (2);
}
return name;
}
/* Create a Unix domain socket with NAME. Returns the file descriptor
or terminates the process in case of an error. Note that this
function needs to be used for the regular socket first (indicated
by PRIMARY) and only then for the extra and the ssh sockets. If
the socket has been redirected the name of the real socket is
stored as a malloced string at R_REDIR_NAME. If CYGWIN is set a
Cygwin compatible socket is created (Windows only). */
static gnupg_fd_t
create_server_socket (char *name, int primary, int cygwin,
char **r_redir_name, assuan_sock_nonce_t *nonce)
{
struct sockaddr *addr;
struct sockaddr_un *unaddr;
socklen_t len;
gnupg_fd_t fd;
int rc;
xfree (*r_redir_name);
*r_redir_name = NULL;
fd = assuan_sock_new (AF_UNIX, SOCK_STREAM, 0);
if (fd == ASSUAN_INVALID_FD)
{
log_error (_("can't create socket: %s\n"), strerror (errno));
*name = 0; /* Inhibit removal of the socket by cleanup(). */
agent_exit (2);
}
if (cygwin)
assuan_sock_set_flag (fd, "cygwin", 1);
unaddr = xmalloc (sizeof *unaddr);
addr = (struct sockaddr*)unaddr;
{
int redirected;
if (assuan_sock_set_sockaddr_un (name, addr, &redirected))
{
if (errno == ENAMETOOLONG)
log_error (_("socket name '%s' is too long\n"), name);
else
log_error ("error preparing socket '%s': %s\n",
name, gpg_strerror (gpg_error_from_syserror ()));
*name = 0; /* Inhibit removal of the socket by cleanup(). */
xfree (unaddr);
agent_exit (2);
}
if (redirected)
{
*r_redir_name = xstrdup (unaddr->sun_path);
if (opt.verbose)
log_info ("redirecting socket '%s' to '%s'\n", name, *r_redir_name);
}
}
len = SUN_LEN (unaddr);
rc = assuan_sock_bind (fd, addr, len);
/* Our error code mapping on W32CE returns EEXIST thus we also test
for this. */
if (rc == -1
&& (errno == EADDRINUSE
#ifdef HAVE_W32_SYSTEM
|| errno == EEXIST
#endif
))
{
/* Check whether a gpg-agent is already running. We do this
test only if this is the primary socket. For secondary
sockets we assume that a test for gpg-agent has already been
done and reuse the requested socket. Testing the ssh-socket
is not possible because at this point, though we know the new
Assuan socket, the Assuan server and thus the ssh-agent
server is not yet operational; this would lead to a hang. */
if (primary && !check_for_running_agent (1))
{
log_set_prefix (NULL, GPGRT_LOG_WITH_PREFIX);
log_set_file (NULL);
log_error (_("a gpg-agent is already running - "
"not starting a new one\n"));
*name = 0; /* Inhibit removal of the socket by cleanup(). */
assuan_sock_close (fd);
xfree (unaddr);
agent_exit (2);
}
gnupg_remove (unaddr->sun_path);
rc = assuan_sock_bind (fd, addr, len);
}
if (rc != -1 && (rc=assuan_sock_get_nonce (addr, len, nonce)))
log_error (_("error getting nonce for the socket\n"));
if (rc == -1)
{
/* We use gpg_strerror here because it allows us to get strings
for some W32 socket error codes. */
log_error (_("error binding socket to '%s': %s\n"),
unaddr->sun_path,
gpg_strerror (gpg_error_from_syserror ()));
assuan_sock_close (fd);
*name = 0; /* Inhibit removal of the socket by cleanup(). */
xfree (unaddr);
agent_exit (2);
}
if (gnupg_chmod (unaddr->sun_path, "-rwx"))
log_error (_("can't set permissions of '%s': %s\n"),
unaddr->sun_path, strerror (errno));
if (listen (FD2INT(fd), listen_backlog ) == -1)
{
log_error ("listen(fd,%d) failed: %s\n",
listen_backlog, strerror (errno));
*name = 0; /* Inhibit removal of the socket by cleanup(). */
assuan_sock_close (fd);
xfree (unaddr);
agent_exit (2);
}
if (opt.verbose)
log_info (_("listening on socket '%s'\n"), unaddr->sun_path);
xfree (unaddr);
return fd;
}
/* Check that the directory for storing the private keys exists and
create it if not. This function won't fail as it is only a
convenience function and not strictly necessary. */
static void
create_private_keys_directory (const char *home)
{
char *fname;
struct stat statbuf;
fname = make_filename (home, GNUPG_PRIVATE_KEYS_DIR, NULL);
if (stat (fname, &statbuf) && errno == ENOENT)
{
if (gnupg_mkdir (fname, "-rwx"))
log_error (_("can't create directory '%s': %s\n"),
fname, strerror (errno) );
else if (!opt.quiet)
log_info (_("directory '%s' created\n"), fname);
}
if (gnupg_chmod (fname, "-rwx"))
log_error (_("can't set permissions of '%s': %s\n"),
fname, strerror (errno));
xfree (fname);
}
/* Create the directory only if the supplied directory name is the
same as the default one. This way we avoid to create arbitrary
directories when a non-default home directory is used. To cope
with HOME, we compare only the suffix if we see that the default
homedir does start with a tilde. We don't stop here in case of
problems because other functions will throw an error anyway.*/
static void
create_directories (void)
{
struct stat statbuf;
const char *defhome = standard_homedir ();
char *home;
home = make_filename (gnupg_homedir (), NULL);
if ( stat (home, &statbuf) )
{
if (errno == ENOENT)
{
if (
#ifdef HAVE_W32_SYSTEM
( !compare_filenames (home, defhome) )
#else
(*defhome == '~'
&& (strlen (home) >= strlen (defhome+1)
&& !strcmp (home + strlen(home)
- strlen (defhome+1), defhome+1)))
|| (*defhome != '~' && !strcmp (home, defhome) )
#endif
)
{
if (gnupg_mkdir (home, "-rwx"))
log_error (_("can't create directory '%s': %s\n"),
home, strerror (errno) );
else
{
if (!opt.quiet)
log_info (_("directory '%s' created\n"), home);
create_private_keys_directory (home);
}
}
}
else
log_error (_("stat() failed for '%s': %s\n"), home, strerror (errno));
}
else if ( !S_ISDIR(statbuf.st_mode))
{
log_error (_("can't use '%s' as home directory\n"), home);
}
else /* exists and is a directory. */
{
create_private_keys_directory (home);
}
xfree (home);
}
/* This is the worker for the ticker. It is called every few seconds
and may only do fast operations. */
static void
handle_tick (void)
{
static time_t last_minute;
struct stat statbuf;
if (!last_minute)
last_minute = time (NULL);
/* Check whether the scdaemon has died and cleanup in this case. */
agent_scd_check_aliveness ();
/* If we are running as a child of another process, check whether
the parent is still alive and shutdown if not. */
#ifndef HAVE_W32_SYSTEM
if (parent_pid != (pid_t)(-1))
{
if (kill (parent_pid, 0))
{
shutdown_pending = 2;
log_info ("parent process died - shutting down\n");
log_info ("%s %s stopped\n", strusage(11), strusage(13) );
cleanup ();
agent_exit (0);
}
}
#endif /*HAVE_W32_SYSTEM*/
/* Code to be run from time to time. */
#if CHECK_OWN_SOCKET_INTERVAL > 0
if (last_minute + CHECK_OWN_SOCKET_INTERVAL <= time (NULL))
{
check_own_socket ();
last_minute = time (NULL);
}
#endif
/* Need to check for expired cache entries. */
agent_cache_housekeeping ();
/* Check whether the homedir is still available. */
if (!shutdown_pending
&& (!have_homedir_inotify || !reliable_homedir_inotify)
&& stat (gnupg_homedir (), &statbuf) && errno == ENOENT)
{
shutdown_pending = 1;
log_info ("homedir has been removed - shutting down\n");
}
}
/* A global function which allows us to call the reload stuff from
other places too. This is only used when build for W32. */
void
agent_sighup_action (void)
{
log_info ("SIGHUP received - "
"re-reading configuration and flushing cache\n");
agent_flush_cache ();
reread_configuration ();
agent_reload_trustlist ();
/* We flush the module name cache so that after installing a
"pinentry" binary that one can be used in case the
"pinentry-basic" fallback was in use. */
gnupg_module_name_flush_some ();
if (opt.disable_scdaemon)
agent_card_killscd ();
}
/* A helper function to handle SIGUSR2. */
static void
agent_sigusr2_action (void)
{
if (opt.verbose)
log_info ("SIGUSR2 received - updating card event counter\n");
/* Nothing to check right now. We only increment a counter. */
bump_card_eventcounter ();
}
#ifndef HAVE_W32_SYSTEM
/* The signal handler for this program. It is expected to be run in
its own thread and not in the context of a signal handler. */
static void
handle_signal (int signo)
{
switch (signo)
{
#ifndef HAVE_W32_SYSTEM
case SIGHUP:
agent_sighup_action ();
break;
case SIGUSR1:
log_info ("SIGUSR1 received - printing internal information:\n");
/* Fixme: We need to see how to integrate pth dumping into our
logging system. */
/* pth_ctrl (PTH_CTRL_DUMPSTATE, log_get_stream ()); */
agent_query_dump_state ();
agent_scd_dump_state ();
break;
case SIGUSR2:
agent_sigusr2_action ();
break;
case SIGTERM:
if (!shutdown_pending)
log_info ("SIGTERM received - shutting down ...\n");
else
log_info ("SIGTERM received - still %i open connections\n",
active_connections);
shutdown_pending++;
if (shutdown_pending > 2)
{
log_info ("shutdown forced\n");
log_info ("%s %s stopped\n", strusage(11), strusage(13) );
cleanup ();
agent_exit (0);
}
break;
case SIGINT:
log_info ("SIGINT received - immediate shutdown\n");
log_info( "%s %s stopped\n", strusage(11), strusage(13));
cleanup ();
agent_exit (0);
break;
#endif
default:
log_info ("signal %d received - no action defined\n", signo);
}
}
#endif
/* Check the nonce on a new connection. This is a NOP unless we
are using our Unix domain socket emulation under Windows. */
static int
check_nonce (ctrl_t ctrl, assuan_sock_nonce_t *nonce)
{
if (assuan_sock_check_nonce (ctrl->thread_startup.fd, nonce))
{
log_info (_("error reading nonce on fd %d: %s\n"),
FD2INT(ctrl->thread_startup.fd), strerror (errno));
assuan_sock_close (ctrl->thread_startup.fd);
xfree (ctrl);
return -1;
}
else
return 0;
}
#ifdef HAVE_W32_SYSTEM
/* The window message processing function for Putty. Warning: This
code runs as a native Windows thread. Use of our own functions
needs to be bracket with pth_leave/pth_enter. */
static LRESULT CALLBACK
putty_message_proc (HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
{
int ret = 0;
int w32rc;
COPYDATASTRUCT *cds;
const char *mapfile;
HANDLE maphd;
PSID mysid = NULL;
PSID mapsid = NULL;
void *data = NULL;
PSECURITY_DESCRIPTOR psd = NULL;
ctrl_t ctrl = NULL;
if (msg != WM_COPYDATA)
{
return DefWindowProc (hwnd, msg, wparam, lparam);
}
cds = (COPYDATASTRUCT*)lparam;
if (cds->dwData != PUTTY_IPC_MAGIC)
return 0; /* Ignore data with the wrong magic. */
mapfile = cds->lpData;
if (!cds->cbData || mapfile[cds->cbData - 1])
return 0; /* Ignore empty and non-properly terminated strings. */
if (DBG_IPC)
{
npth_protect ();
log_debug ("ssh map file '%s'", mapfile);
npth_unprotect ();
}
maphd = OpenFileMapping (FILE_MAP_ALL_ACCESS, FALSE, mapfile);
if (DBG_IPC)
{
npth_protect ();
log_debug ("ssh map handle %p\n", maphd);
npth_unprotect ();
}
if (!maphd || maphd == INVALID_HANDLE_VALUE)
return 0;
npth_protect ();
mysid = w32_get_user_sid ();
if (!mysid)
{
log_error ("error getting my sid\n");
goto leave;
}
w32rc = GetSecurityInfo (maphd, SE_KERNEL_OBJECT,
OWNER_SECURITY_INFORMATION,
&mapsid, NULL, NULL, NULL,
&psd);
if (w32rc)
{
log_error ("error getting sid of ssh map file: rc=%d", w32rc);
goto leave;
}
if (DBG_IPC)
{
char *sidstr;
if (!ConvertSidToStringSid (mysid, &sidstr))
sidstr = NULL;
log_debug (" my sid: '%s'", sidstr? sidstr: "[error]");
LocalFree (sidstr);
if (!ConvertSidToStringSid (mapsid, &sidstr))
sidstr = NULL;
log_debug ("ssh map file sid: '%s'", sidstr? sidstr: "[error]");
LocalFree (sidstr);
}
if (!EqualSid (mysid, mapsid))
{
log_error ("ssh map file has a non-matching sid\n");
goto leave;
}
data = MapViewOfFile (maphd, FILE_MAP_ALL_ACCESS, 0, 0, 0);
if (DBG_IPC)
log_debug ("ssh IPC buffer at %p\n", data);
if (!data)
goto leave;
- /* log_printhex ("request:", data, 20); */
+ /* log_printhex (data, 20, "request:"); */
ctrl = xtrycalloc (1, sizeof *ctrl);
if (!ctrl)
{
log_error ("error allocating connection control data: %s\n",
strerror (errno) );
goto leave;
}
ctrl->session_env = session_env_new ();
if (!ctrl->session_env)
{
log_error ("error allocating session environment block: %s\n",
strerror (errno) );
goto leave;
}
agent_init_default_ctrl (ctrl);
if (!serve_mmapped_ssh_request (ctrl, data, PUTTY_IPC_MAXLEN))
ret = 1; /* Valid ssh message has been constructed. */
agent_deinit_default_ctrl (ctrl);
- /* log_printhex (" reply:", data, 20); */
+ /* log_printhex (data, 20, " reply:"); */
leave:
xfree (ctrl);
if (data)
UnmapViewOfFile (data);
xfree (mapsid);
if (psd)
LocalFree (psd);
xfree (mysid);
CloseHandle (maphd);
npth_unprotect ();
return ret;
}
#endif /*HAVE_W32_SYSTEM*/
#ifdef HAVE_W32_SYSTEM
/* The thread handling Putty's IPC requests. */
static void *
putty_message_thread (void *arg)
{
WNDCLASS wndwclass = {0, putty_message_proc, 0, 0,
NULL, NULL, NULL, NULL, NULL, "Pageant"};
HWND hwnd;
MSG msg;
(void)arg;
if (opt.verbose)
log_info ("putty message loop thread started\n");
/* The message loop runs as thread independent from our nPth system.
This also means that we need to make sure that we switch back to
our system before calling any no-windows function. */
npth_unprotect ();
/* First create a window to make sure that a message queue exists
for this thread. */
if (!RegisterClass (&wndwclass))
{
npth_protect ();
log_error ("error registering Pageant window class");
return NULL;
}
hwnd = CreateWindowEx (0, "Pageant", "Pageant", 0,
0, 0, 0, 0,
HWND_MESSAGE, /* hWndParent */
NULL, /* hWndMenu */
NULL, /* hInstance */
NULL); /* lpParm */
if (!hwnd)
{
npth_protect ();
log_error ("error creating Pageant window");
return NULL;
}
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
/* Back to nPth. */
npth_protect ();
if (opt.verbose)
log_info ("putty message loop thread stopped\n");
return NULL;
}
#endif /*HAVE_W32_SYSTEM*/
static void *
do_start_connection_thread (ctrl_t ctrl)
{
active_connections++;
agent_init_default_ctrl (ctrl);
if (opt.verbose && !DBG_IPC)
log_info (_("handler 0x%lx for fd %d started\n"),
(unsigned long) npth_self(), FD2INT(ctrl->thread_startup.fd));
start_command_handler (ctrl, GNUPG_INVALID_FD, ctrl->thread_startup.fd);
if (opt.verbose && !DBG_IPC)
log_info (_("handler 0x%lx for fd %d terminated\n"),
(unsigned long) npth_self(), FD2INT(ctrl->thread_startup.fd));
agent_deinit_default_ctrl (ctrl);
xfree (ctrl);
active_connections--;
return NULL;
}
/* This is the standard connection thread's main function. */
static void *
start_connection_thread_std (void *arg)
{
ctrl_t ctrl = arg;
if (check_nonce (ctrl, &socket_nonce))
{
log_error ("handler 0x%lx nonce check FAILED\n",
(unsigned long) npth_self());
return NULL;
}
return do_start_connection_thread (ctrl);
}
/* This is the extra socket connection thread's main function. */
static void *
start_connection_thread_extra (void *arg)
{
ctrl_t ctrl = arg;
if (check_nonce (ctrl, &socket_nonce_extra))
{
log_error ("handler 0x%lx nonce check FAILED\n",
(unsigned long) npth_self());
return NULL;
}
ctrl->restricted = 1;
return do_start_connection_thread (ctrl);
}
/* This is the browser socket connection thread's main function. */
static void *
start_connection_thread_browser (void *arg)
{
ctrl_t ctrl = arg;
if (check_nonce (ctrl, &socket_nonce_browser))
{
log_error ("handler 0x%lx nonce check FAILED\n",
(unsigned long) npth_self());
return NULL;
}
ctrl->restricted = 2;
return do_start_connection_thread (ctrl);
}
/* This is the ssh connection thread's main function. */
static void *
start_connection_thread_ssh (void *arg)
{
ctrl_t ctrl = arg;
if (check_nonce (ctrl, &socket_nonce_ssh))
return NULL;
active_connections++;
agent_init_default_ctrl (ctrl);
if (opt.verbose)
log_info (_("ssh handler 0x%lx for fd %d started\n"),
(unsigned long) npth_self(), FD2INT(ctrl->thread_startup.fd));
start_command_handler_ssh (ctrl, ctrl->thread_startup.fd);
if (opt.verbose)
log_info (_("ssh handler 0x%lx for fd %d terminated\n"),
(unsigned long) npth_self(), FD2INT(ctrl->thread_startup.fd));
agent_deinit_default_ctrl (ctrl);
xfree (ctrl);
active_connections--;
return NULL;
}
/* Connection handler loop. Wait for connection requests and spawn a
thread after accepting a connection. */
static void
handle_connections (gnupg_fd_t listen_fd,
gnupg_fd_t listen_fd_extra,
gnupg_fd_t listen_fd_browser,
gnupg_fd_t listen_fd_ssh)
{
gpg_error_t err;
npth_attr_t tattr;
struct sockaddr_un paddr;
socklen_t plen;
fd_set fdset, read_fdset;
int ret;
gnupg_fd_t fd;
int nfd;
int saved_errno;
struct timespec abstime;
struct timespec curtime;
struct timespec timeout;
#ifdef HAVE_W32_SYSTEM
HANDLE events[2];
unsigned int events_set;
#endif
int sock_inotify_fd = -1;
int home_inotify_fd = -1;
struct {
const char *name;
void *(*func) (void *arg);
gnupg_fd_t l_fd;
} listentbl[] = {
{ "std", start_connection_thread_std },
{ "extra", start_connection_thread_extra },
{ "browser", start_connection_thread_browser },
{ "ssh", start_connection_thread_ssh }
};
ret = npth_attr_init(&tattr);
if (ret)
log_fatal ("error allocating thread attributes: %s\n",
strerror (ret));
npth_attr_setdetachstate (&tattr, NPTH_CREATE_DETACHED);
#ifndef HAVE_W32_SYSTEM
npth_sigev_init ();
npth_sigev_add (SIGHUP);
npth_sigev_add (SIGUSR1);
npth_sigev_add (SIGUSR2);
npth_sigev_add (SIGINT);
npth_sigev_add (SIGTERM);
npth_sigev_fini ();
#else
# ifdef HAVE_W32CE_SYSTEM
/* Use a dummy event. */
sigs = 0;
ev = pth_event (PTH_EVENT_SIGS, &sigs, &signo);
# else
events[0] = get_agent_scd_notify_event ();
events[1] = INVALID_HANDLE_VALUE;
# endif
#endif
if (disable_check_own_socket)
sock_inotify_fd = -1;
else if ((err = gnupg_inotify_watch_socket (&sock_inotify_fd, socket_name)))
{
if (gpg_err_code (err) != GPG_ERR_NOT_SUPPORTED)
log_info ("error enabling daemon termination by socket removal: %s\n",
gpg_strerror (err));
}
if (disable_check_own_socket)
home_inotify_fd = -1;
else if ((err = gnupg_inotify_watch_delete_self (&home_inotify_fd,
gnupg_homedir ())))
{
if (gpg_err_code (err) != GPG_ERR_NOT_SUPPORTED)
log_info ("error enabling daemon termination by homedir removal: %s\n",
gpg_strerror (err));
}
else
have_homedir_inotify = 1;
/* On Windows we need to fire up a separate thread to listen for
requests from Putty (an SSH client), so we can replace Putty's
Pageant (its ssh-agent implementation). */
#ifdef HAVE_W32_SYSTEM
if (putty_support)
{
npth_t thread;
ret = npth_create (&thread, &tattr, putty_message_thread, NULL);
if (ret)
{
log_error ("error spawning putty message loop: %s\n", strerror (ret));
}
}
#endif /*HAVE_W32_SYSTEM*/
/* Set a flag to tell call-scd.c that it may enable event
notifications. */
opt.sigusr2_enabled = 1;
FD_ZERO (&fdset);
FD_SET (FD2INT (listen_fd), &fdset);
nfd = FD2INT (listen_fd);
if (listen_fd_extra != GNUPG_INVALID_FD)
{
FD_SET ( FD2INT(listen_fd_extra), &fdset);
if (FD2INT (listen_fd_extra) > nfd)
nfd = FD2INT (listen_fd_extra);
}
if (listen_fd_browser != GNUPG_INVALID_FD)
{
FD_SET ( FD2INT(listen_fd_browser), &fdset);
if (FD2INT (listen_fd_browser) > nfd)
nfd = FD2INT (listen_fd_browser);
}
if (listen_fd_ssh != GNUPG_INVALID_FD)
{
FD_SET ( FD2INT(listen_fd_ssh), &fdset);
if (FD2INT (listen_fd_ssh) > nfd)
nfd = FD2INT (listen_fd_ssh);
}
if (sock_inotify_fd != -1)
{
FD_SET (sock_inotify_fd, &fdset);
if (sock_inotify_fd > nfd)
nfd = sock_inotify_fd;
}
if (home_inotify_fd != -1)
{
FD_SET (home_inotify_fd, &fdset);
if (home_inotify_fd > nfd)
nfd = home_inotify_fd;
}
listentbl[0].l_fd = listen_fd;
listentbl[1].l_fd = listen_fd_extra;
listentbl[2].l_fd = listen_fd_browser;
listentbl[3].l_fd = listen_fd_ssh;
npth_clock_gettime (&abstime);
abstime.tv_sec += TIMERTICK_INTERVAL;
for (;;)
{
/* Shutdown test. */
if (shutdown_pending)
{
if (active_connections == 0)
break; /* ready */
/* Do not accept new connections but keep on running the
* loop to cope with the timer events.
*
* Note that we do not close the listening socket because a
* client trying to connect to that socket would instead
* restart a new dirmngr instance - which is unlikely the
* intention of a shutdown. */
FD_ZERO (&fdset);
nfd = -1;
if (sock_inotify_fd != -1)
{
FD_SET (sock_inotify_fd, &fdset);
nfd = sock_inotify_fd;
}
if (home_inotify_fd != -1)
{
FD_SET (home_inotify_fd, &fdset);
if (home_inotify_fd > nfd)
nfd = home_inotify_fd;
}
}
/* POSIX says that fd_set should be implemented as a structure,
thus a simple assignment is fine to copy the entire set. */
read_fdset = fdset;
npth_clock_gettime (&curtime);
if (!(npth_timercmp (&curtime, &abstime, <)))
{
/* Timeout. */
handle_tick ();
npth_clock_gettime (&abstime);
abstime.tv_sec += TIMERTICK_INTERVAL;
}
npth_timersub (&abstime, &curtime, &timeout);
#ifndef HAVE_W32_SYSTEM
ret = npth_pselect (nfd+1, &read_fdset, NULL, NULL, &timeout,
npth_sigev_sigmask ());
saved_errno = errno;
{
int signo;
while (npth_sigev_get_pending (&signo))
handle_signal (signo);
}
#else
ret = npth_eselect (nfd+1, &read_fdset, NULL, NULL, &timeout,
events, &events_set);
saved_errno = errno;
/* This is valid even if npth_eselect returns an error. */
if (events_set & 1)
agent_sigusr2_action ();
#endif
if (ret == -1 && saved_errno != EINTR)
{
log_error (_("npth_pselect failed: %s - waiting 1s\n"),
strerror (saved_errno));
npth_sleep (1);
continue;
}
if (ret <= 0)
/* Interrupt or timeout. Will be handled when calculating the
next timeout. */
continue;
/* The inotify fds are set even when a shutdown is pending (see
* above). So we must handle them in any case. To avoid that
* they trigger a second time we close them immediately. */
if (sock_inotify_fd != -1
&& FD_ISSET (sock_inotify_fd, &read_fdset)
&& gnupg_inotify_has_name (sock_inotify_fd, GPG_AGENT_SOCK_NAME))
{
shutdown_pending = 1;
close (sock_inotify_fd);
sock_inotify_fd = -1;
log_info ("socket file has been removed - shutting down\n");
}
if (home_inotify_fd != -1
&& FD_ISSET (home_inotify_fd, &read_fdset))
{
shutdown_pending = 1;
close (home_inotify_fd);
home_inotify_fd = -1;
log_info ("homedir has been removed - shutting down\n");
}
if (!shutdown_pending)
{
int idx;
ctrl_t ctrl;
npth_t thread;
for (idx=0; idx < DIM(listentbl); idx++)
{
if (listentbl[idx].l_fd == GNUPG_INVALID_FD)
continue;
if (!FD_ISSET (FD2INT (listentbl[idx].l_fd), &read_fdset))
continue;
plen = sizeof paddr;
fd = INT2FD (npth_accept (FD2INT(listentbl[idx].l_fd),
(struct sockaddr *)&paddr, &plen));
if (fd == GNUPG_INVALID_FD)
{
log_error ("accept failed for %s: %s\n",
listentbl[idx].name, strerror (errno));
}
else if ( !(ctrl = xtrycalloc (1, sizeof *ctrl)))
{
log_error ("error allocating connection data for %s: %s\n",
listentbl[idx].name, strerror (errno) );
assuan_sock_close (fd);
}
else if ( !(ctrl->session_env = session_env_new ()))
{
log_error ("error allocating session env block for %s: %s\n",
listentbl[idx].name, strerror (errno) );
xfree (ctrl);
assuan_sock_close (fd);
}
else
{
ctrl->thread_startup.fd = fd;
ret = npth_create (&thread, &tattr,
listentbl[idx].func, ctrl);
if (ret)
{
log_error ("error spawning connection handler for %s:"
" %s\n", listentbl[idx].name, strerror (ret));
assuan_sock_close (fd);
xfree (ctrl);
}
}
}
}
}
if (sock_inotify_fd != -1)
close (sock_inotify_fd);
if (home_inotify_fd != -1)
close (home_inotify_fd);
cleanup ();
log_info (_("%s %s stopped\n"), strusage(11), strusage(13));
npth_attr_destroy (&tattr);
}
/* Helper for check_own_socket. */
static gpg_error_t
check_own_socket_pid_cb (void *opaque, const void *buffer, size_t length)
{
membuf_t *mb = opaque;
put_membuf (mb, buffer, length);
return 0;
}
/* The thread running the actual check. We need to run this in a
separate thread so that check_own_thread can be called from the
timer tick. */
static void *
check_own_socket_thread (void *arg)
{
int rc;
char *sockname = arg;
assuan_context_t ctx = NULL;
membuf_t mb;
char *buffer;
check_own_socket_running++;
rc = assuan_new (&ctx);
if (rc)
{
log_error ("can't allocate assuan context: %s\n", gpg_strerror (rc));
goto leave;
}
assuan_set_flag (ctx, ASSUAN_NO_LOGGING, 1);
rc = assuan_socket_connect (ctx, sockname, (pid_t)(-1), 0);
if (rc)
{
log_error ("can't connect my own socket: %s\n", gpg_strerror (rc));
goto leave;
}
init_membuf (&mb, 100);
rc = assuan_transact (ctx, "GETINFO pid", check_own_socket_pid_cb, &mb,
NULL, NULL, NULL, NULL);
put_membuf (&mb, "", 1);
buffer = get_membuf (&mb, NULL);
if (rc || !buffer)
{
log_error ("sending command \"%s\" to my own socket failed: %s\n",
"GETINFO pid", gpg_strerror (rc));
rc = 1;
}
else if ( (pid_t)strtoul (buffer, NULL, 10) != getpid ())
{
log_error ("socket is now serviced by another server\n");
rc = 1;
}
else if (opt.verbose > 1)
log_error ("socket is still served by this server\n");
xfree (buffer);
leave:
xfree (sockname);
if (ctx)
assuan_release (ctx);
if (rc)
{
/* We may not remove the socket as it is now in use by another
server. */
inhibit_socket_removal = 1;
shutdown_pending = 2;
log_info ("this process is useless - shutting down\n");
}
check_own_socket_running--;
return NULL;
}
/* Check whether we are still listening on our own socket. In case
another gpg-agent process started after us has taken ownership of
our socket, we would linger around without any real task. Thus we
better check once in a while whether we are really needed. */
static void
check_own_socket (void)
{
char *sockname;
npth_t thread;
npth_attr_t tattr;
int err;
if (disable_check_own_socket)
return;
if (check_own_socket_running || shutdown_pending)
return; /* Still running or already shutting down. */
sockname = make_filename_try (gnupg_socketdir (), GPG_AGENT_SOCK_NAME, NULL);
if (!sockname)
return; /* Out of memory. */
err = npth_attr_init (&tattr);
if (err)
return;
npth_attr_setdetachstate (&tattr, NPTH_CREATE_DETACHED);
err = npth_create (&thread, &tattr, check_own_socket_thread, sockname);
if (err)
log_error ("error spawning check_own_socket_thread: %s\n", strerror (err));
npth_attr_destroy (&tattr);
}
/* Figure out whether an agent is available and running. Prints an
error if not. If SILENT is true, no messages are printed.
Returns 0 if the agent is running. */
static int
check_for_running_agent (int silent)
{
gpg_error_t err;
char *sockname;
assuan_context_t ctx = NULL;
sockname = make_filename_try (gnupg_socketdir (), GPG_AGENT_SOCK_NAME, NULL);
if (!sockname)
return gpg_error_from_syserror ();
err = assuan_new (&ctx);
if (!err)
err = assuan_socket_connect (ctx, sockname, (pid_t)(-1), 0);
xfree (sockname);
if (err)
{
if (!silent)
log_error (_("no gpg-agent running in this session\n"));
if (ctx)
assuan_release (ctx);
return -1;
}
if (!opt.quiet && !silent)
log_info ("gpg-agent running and available\n");
assuan_release (ctx);
return 0;
}
diff --git a/agent/pkdecrypt.c b/agent/pkdecrypt.c
index 46697bae1..06a8e0b6f 100644
--- a/agent/pkdecrypt.c
+++ b/agent/pkdecrypt.c
@@ -1,146 +1,146 @@
/* pkdecrypt.c - public key decryption (well, actually using a secret key)
* Copyright (C) 2001, 2003 Free Software Foundation, Inc.
*
* This file is part of GnuPG.
*
* GnuPG is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* GnuPG is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see .
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "agent.h"
/* DECRYPT the stuff in ciphertext which is expected to be a S-Exp.
Try to get the key from CTRL and write the decoded stuff back to
OUTFP. The padding information is stored at R_PADDING with -1
for not known. */
int
agent_pkdecrypt (ctrl_t ctrl, const char *desc_text,
const unsigned char *ciphertext, size_t ciphertextlen,
membuf_t *outbuf, int *r_padding)
{
gcry_sexp_t s_skey = NULL, s_cipher = NULL, s_plain = NULL;
unsigned char *shadow_info = NULL;
int rc;
char *buf = NULL;
size_t len;
*r_padding = -1;
if (!ctrl->have_keygrip)
{
log_error ("speculative decryption not yet supported\n");
rc = gpg_error (GPG_ERR_NO_SECKEY);
goto leave;
}
rc = gcry_sexp_sscan (&s_cipher, NULL, (char*)ciphertext, ciphertextlen);
if (rc)
{
log_error ("failed to convert ciphertext: %s\n", gpg_strerror (rc));
rc = gpg_error (GPG_ERR_INV_DATA);
goto leave;
}
if (DBG_CRYPTO)
{
- log_printhex ("keygrip:", ctrl->keygrip, 20);
- log_printhex ("cipher: ", ciphertext, ciphertextlen);
+ log_printhex (ctrl->keygrip, 20, "keygrip:");
+ log_printhex (ciphertext, ciphertextlen, "cipher: ");
}
rc = agent_key_from_file (ctrl, NULL, desc_text,
ctrl->keygrip, &shadow_info,
CACHE_MODE_NORMAL, NULL, &s_skey, NULL);
if (rc)
{
if (gpg_err_code (rc) != GPG_ERR_NO_SECKEY)
log_error ("failed to read the secret key\n");
goto leave;
}
if (shadow_info)
{ /* divert operation to the smartcard */
if (!gcry_sexp_canon_len (ciphertext, ciphertextlen, NULL, NULL))
{
rc = gpg_error (GPG_ERR_INV_SEXP);
goto leave;
}
rc = divert_pkdecrypt (ctrl, desc_text, ciphertext, shadow_info,
&buf, &len, r_padding);
if (rc)
{
log_error ("smartcard decryption failed: %s\n", gpg_strerror (rc));
goto leave;
}
put_membuf_printf (outbuf, "(5:value%u:", (unsigned int)len);
put_membuf (outbuf, buf, len);
put_membuf (outbuf, ")", 2);
}
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", gpg_strerror (rc));
goto leave;
}
if (DBG_CRYPTO)
{
log_debug ("plain: ");
gcry_sexp_dump (s_plain);
}
len = gcry_sexp_sprint (s_plain, GCRYSEXP_FMT_CANON, NULL, 0);
assert (len);
buf = xmalloc (len);
len = gcry_sexp_sprint (s_plain, GCRYSEXP_FMT_CANON, buf, len);
assert (len);
if (*buf == '(')
put_membuf (outbuf, buf, len);
else
{
/* Old style libgcrypt: This is only an S-expression
part. Turn it into a complete S-expression. */
put_membuf (outbuf, "(5:value", 8);
put_membuf (outbuf, buf, len);
put_membuf (outbuf, ")", 2);
}
}
leave:
gcry_sexp_release (s_skey);
gcry_sexp_release (s_plain);
gcry_sexp_release (s_cipher);
xfree (buf);
xfree (shadow_info);
return rc;
}
diff --git a/common/logging.c b/common/logging.c
index df55e6827..7fe8b7419 100644
--- a/common/logging.c
+++ b/common/logging.c
@@ -1,1103 +1,1110 @@
/* logging.c - Useful logging functions
* Copyright (C) 1998, 1999, 2000, 2001, 2003, 2004, 2005, 2006,
* 2009, 2010 Free Software Foundation, Inc.
*
* This file is part of GnuPG.
*
* GnuPG is free software; you can redistribute and/or modify this
* part of GnuPG under the terms of either
*
* - the GNU Lesser General Public License as published by the Free
* Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* or
*
* - the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at
* your option) any later version.
*
* or both in parallel, as here.
*
* GnuPG is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copies of the GNU General Public License
* and the GNU Lesser General Public License along with this program;
* if not, see .
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#ifdef HAVE_W32_SYSTEM
# ifdef HAVE_WINSOCK2_H
# include
# endif
# include
#else /*!HAVE_W32_SYSTEM*/
# include
# include
# include
# include
#endif /*!HAVE_W32_SYSTEM*/
#include
#include
#include
/* #include */
#define GNUPG_COMMON_NEED_AFLOCAL 1
#include "util.h"
#include "i18n.h"
#include "common-defs.h"
#include "logging.h"
#include "sysutils.h"
#ifdef HAVE_W32_SYSTEM
# ifndef S_IRWXG
# define S_IRGRP S_IRUSR
# define S_IWGRP S_IWUSR
# endif
# ifndef S_IRWXO
# define S_IROTH S_IRUSR
# define S_IWOTH S_IWUSR
# endif
#endif
#ifdef HAVE_W32CE_SYSTEM
# define isatty(a) (0)
#endif
#undef WITH_IPV6
#if defined (AF_INET6) && defined(PF_INET) \
&& defined (INET6_ADDRSTRLEN) && defined(HAVE_INET_PTON)
# define WITH_IPV6 1
#endif
#ifndef EAFNOSUPPORT
# define EAFNOSUPPORT EINVAL
#endif
#ifndef INADDR_NONE /* Slowaris is missing that. */
#define INADDR_NONE ((unsigned long)(-1))
#endif /*INADDR_NONE*/
#ifdef HAVE_W32_SYSTEM
#define sock_close(a) closesocket(a)
#else
#define sock_close(a) close(a)
#endif
static estream_t logstream;
static int log_socket = -1;
static char prefix_buffer[80];
static int with_time;
static int with_prefix;
static int with_pid;
#ifdef HAVE_W32_SYSTEM
static int no_registry;
#endif
static int (*get_pid_suffix_cb)(unsigned long *r_value);
static const char * (*socket_dir_cb)(void);
static int running_detached;
static int force_prefixes;
static int missing_lf;
static int errorcount;
int
log_get_errorcount (int clear)
{
int n = errorcount;
if( clear )
errorcount = 0;
return n;
}
void
log_inc_errorcount (void)
{
/* Protect against counter overflow. */
if (errorcount < 30000)
errorcount++;
}
/* The following 3 functions are used by es_fopencookie to write logs
to a socket. */
struct fun_cookie_s
{
int fd;
int quiet;
int want_socket;
int is_socket;
#ifdef HAVE_W32CE_SYSTEM
int use_writefile;
#endif
char name[1];
};
/* Write NBYTES of BUFFER to file descriptor FD. */
static int
writen (int fd, const void *buffer, size_t nbytes, int is_socket)
{
const char *buf = buffer;
size_t nleft = nbytes;
int nwritten;
#ifndef HAVE_W32_SYSTEM
(void)is_socket; /* Not required. */
#endif
while (nleft > 0)
{
#ifdef HAVE_W32_SYSTEM
if (is_socket)
nwritten = send (fd, buf, nleft, 0);
else
#endif
nwritten = write (fd, buf, nleft);
if (nwritten < 0 && errno == EINTR)
continue;
if (nwritten < 0)
return -1;
nleft -= nwritten;
buf = buf + nwritten;
}
return 0;
}
/* Returns true if STR represents a valid port number in decimal
notation and no garbage is following. */
static int
parse_portno (const char *str, unsigned short *r_port)
{
unsigned int value;
for (value=0; *str && (*str >= '0' && *str <= '9'); str++)
{
value = value * 10 + (*str - '0');
if (value > 65535)
return 0;
}
if (*str || !value)
return 0;
*r_port = value;
return 1;
}
static gpgrt_ssize_t
fun_writer (void *cookie_arg, const void *buffer, size_t size)
{
struct fun_cookie_s *cookie = cookie_arg;
/* FIXME: Use only estream with a callback for socket writing. This
avoids the ugly mix of fd and estream code. */
/* Note that we always try to reconnect to the socket but print
error messages only the first time an error occurred. If
RUNNING_DETACHED is set we don't fall back to stderr and even do
not print any error messages. This is needed because detached
processes often close stderr and by writing to file descriptor 2
we might send the log message to a file not intended for logging
(e.g. a pipe or network connection). */
if (cookie->want_socket && cookie->fd == -1)
{
#ifdef WITH_IPV6
struct sockaddr_in6 srvr_addr_in6;
#endif
struct sockaddr_in srvr_addr_in;
#ifndef HAVE_W32_SYSTEM
struct sockaddr_un srvr_addr_un;
#endif
const char *name_for_err = "";
size_t addrlen;
struct sockaddr *srvr_addr = NULL;
unsigned short port = 0;
int af = AF_LOCAL;
int pf = PF_LOCAL;
const char *name = cookie->name;
/* Not yet open or meanwhile closed due to an error. */
cookie->is_socket = 0;
/* Check whether this is a TCP socket or a local socket. */
if (!strncmp (name, "tcp://", 6) && name[6])
{
name += 6;
af = AF_INET;
pf = PF_INET;
}
#ifndef HAVE_W32_SYSTEM
else if (!strncmp (name, "socket://", 9))
name += 9;
#endif
if (af == AF_LOCAL)
{
addrlen = 0;
#ifndef HAVE_W32_SYSTEM
memset (&srvr_addr, 0, sizeof srvr_addr);
srvr_addr_un.sun_family = af;
if (!*name && (name = socket_dir_cb ()) && *name)
{
if (strlen (name) + 7 < sizeof (srvr_addr_un.sun_path)-1)
{
strncpy (srvr_addr_un.sun_path,
name, sizeof (srvr_addr_un.sun_path)-1);
strcat (srvr_addr_un.sun_path, "/S.log");
srvr_addr_un.sun_path[sizeof (srvr_addr_un.sun_path)-1] = 0;
srvr_addr = (struct sockaddr *)&srvr_addr_un;
addrlen = SUN_LEN (&srvr_addr_un);
name_for_err = srvr_addr_un.sun_path;
}
}
else
{
if (*name && strlen (name) < sizeof (srvr_addr_un.sun_path)-1)
{
strncpy (srvr_addr_un.sun_path,
name, sizeof (srvr_addr_un.sun_path)-1);
srvr_addr_un.sun_path[sizeof (srvr_addr_un.sun_path)-1] = 0;
srvr_addr = (struct sockaddr *)&srvr_addr_un;
addrlen = SUN_LEN (&srvr_addr_un);
}
}
#endif /*!HAVE_W32SYSTEM*/
}
else
{
char *addrstr, *p;
#ifdef HAVE_INET_PTON
void *addrbuf = NULL;
#endif /*HAVE_INET_PTON*/
addrstr = xtrymalloc (strlen (name) + 1);
if (!addrstr)
addrlen = 0; /* This indicates an error. */
else if (*name == '[')
{
/* Check for IPv6 literal address. */
strcpy (addrstr, name+1);
p = strchr (addrstr, ']');
if (!p || p[1] != ':' || !parse_portno (p+2, &port))
{
gpg_err_set_errno (EINVAL);
addrlen = 0;
}
else
{
*p = 0;
#ifdef WITH_IPV6
af = AF_INET6;
pf = PF_INET6;
memset (&srvr_addr_in6, 0, sizeof srvr_addr_in6);
srvr_addr_in6.sin6_family = af;
srvr_addr_in6.sin6_port = htons (port);
#ifdef HAVE_INET_PTON
addrbuf = &srvr_addr_in6.sin6_addr;
#endif /*HAVE_INET_PTON*/
srvr_addr = (struct sockaddr *)&srvr_addr_in6;
addrlen = sizeof srvr_addr_in6;
#else
gpg_err_set_errno (EAFNOSUPPORT);
addrlen = 0;
#endif
}
}
else
{
/* Check for IPv4 literal address. */
strcpy (addrstr, name);
p = strchr (addrstr, ':');
if (!p || !parse_portno (p+1, &port))
{
gpg_err_set_errno (EINVAL);
addrlen = 0;
}
else
{
*p = 0;
memset (&srvr_addr_in, 0, sizeof srvr_addr_in);
srvr_addr_in.sin_family = af;
srvr_addr_in.sin_port = htons (port);
#ifdef HAVE_INET_PTON
addrbuf = &srvr_addr_in.sin_addr;
#endif /*HAVE_INET_PTON*/
srvr_addr = (struct sockaddr *)&srvr_addr_in;
addrlen = sizeof srvr_addr_in;
}
}
if (addrlen)
{
#ifdef HAVE_INET_PTON
if (inet_pton (af, addrstr, addrbuf) != 1)
addrlen = 0;
#else /*!HAVE_INET_PTON*/
/* We need to use the old function. If we are here v6
support isn't enabled anyway and thus we can do fine
without. Note that Windows has a compatible inet_pton
function named inetPton, but only since Vista. */
srvr_addr_in.sin_addr.s_addr = inet_addr (addrstr);
if (srvr_addr_in.sin_addr.s_addr == INADDR_NONE)
addrlen = 0;
#endif /*!HAVE_INET_PTON*/
}
xfree (addrstr);
}
cookie->fd = addrlen? socket (pf, SOCK_STREAM, 0) : -1;
if (cookie->fd == -1)
{
if (!cookie->quiet && !running_detached
&& isatty (es_fileno (es_stderr)))
es_fprintf (es_stderr, "failed to create socket for logging: %s\n",
strerror(errno));
}
else
{
if (connect (cookie->fd, srvr_addr, addrlen) == -1)
{
if (!cookie->quiet && !running_detached
&& isatty (es_fileno (es_stderr)))
es_fprintf (es_stderr, "can't connect to '%s%s': %s\n",
cookie->name, name_for_err, strerror(errno));
sock_close (cookie->fd);
cookie->fd = -1;
}
}
if (cookie->fd == -1)
{
if (!running_detached)
{
/* Due to all the problems with apps not running
detached but being called with stderr closed or used
for a different purposes, it does not make sense to
switch to stderr. We therefore disable it. */
if (!cookie->quiet)
{
/* fputs ("switching logging to stderr\n", stderr);*/
cookie->quiet = 1;
}
cookie->fd = -1; /*fileno (stderr);*/
}
}
else /* Connection has been established. */
{
cookie->quiet = 0;
cookie->is_socket = 1;
}
}
log_socket = cookie->fd;
if (cookie->fd != -1)
{
#ifdef HAVE_W32CE_SYSTEM
if (cookie->use_writefile)
{
DWORD nwritten;
WriteFile ((HANDLE)cookie->fd, buffer, size, &nwritten, NULL);
return (gpgrt_ssize_t)size; /* Okay. */
}
#endif
if (!writen (cookie->fd, buffer, size, cookie->is_socket))
return (gpgrt_ssize_t)size; /* Okay. */
}
if (!running_detached && cookie->fd != -1
&& isatty (es_fileno (es_stderr)))
{
if (*cookie->name)
es_fprintf (es_stderr, "error writing to '%s': %s\n",
cookie->name, strerror(errno));
else
es_fprintf (es_stderr, "error writing to file descriptor %d: %s\n",
cookie->fd, strerror(errno));
}
if (cookie->is_socket && cookie->fd != -1)
{
sock_close (cookie->fd);
cookie->fd = -1;
log_socket = -1;
}
return (gpgrt_ssize_t)size;
}
static int
fun_closer (void *cookie_arg)
{
struct fun_cookie_s *cookie = cookie_arg;
if (cookie->fd != -1 && cookie->fd != 2)
sock_close (cookie->fd);
xfree (cookie);
log_socket = -1;
return 0;
}
/* Common function to either set the logging to a file or a file
descriptor. */
static void
set_file_fd (const char *name, int fd)
{
estream_t fp;
int want_socket;
#ifdef HAVE_W32CE_SYSTEM
int use_writefile = 0;
#endif
struct fun_cookie_s *cookie;
/* Close an open log stream. */
if (logstream)
{
if (logstream != es_stderr)
es_fclose (logstream);
logstream = NULL;
}
/* Figure out what kind of logging we want. */
if (name && !strcmp (name, "-"))
{
name = NULL;
fd = es_fileno (es_stderr);
}
want_socket = 0;
if (name && !strncmp (name, "tcp://", 6) && name[6])
want_socket = 1;
#ifndef HAVE_W32_SYSTEM
else if (name && !strncmp (name, "socket://", 9))
want_socket = 2;
#endif /*HAVE_W32_SYSTEM*/
#ifdef HAVE_W32CE_SYSTEM
else if (name && !strcmp (name, "GPG2:"))
{
HANDLE hd;
ActivateDevice (L"Drivers\\"GNUPG_NAME"_Log", 0);
/* Ignore a filename and write the debug output to the GPG2:
device. */
hd = CreateFile (L"GPG2:", GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
fd = (hd == INVALID_HANDLE_VALUE)? -1 : (int)hd;
name = NULL;
force_prefixes = 1;
use_writefile = 1;
}
#endif /*HAVE_W32CE_SYSTEM*/
/* Setup a new stream. */
/* The xmalloc below is justified because we can expect that this
function is called only during initialization and there is no
easy way out of this error condition. */
cookie = xmalloc (sizeof *cookie + (name? strlen (name):0));
strcpy (cookie->name, name? name:"");
cookie->quiet = 0;
cookie->is_socket = 0;
cookie->want_socket = want_socket;
#ifdef HAVE_W32CE_SYSTEM
cookie->use_writefile = use_writefile;
#endif
if (!name)
cookie->fd = fd;
else if (want_socket)
cookie->fd = -1;
else
{
do
cookie->fd = open (name, O_WRONLY|O_APPEND|O_CREAT,
(S_IRUSR|S_IRGRP|S_IROTH|S_IWUSR|S_IWGRP|S_IWOTH));
while (cookie->fd == -1 && errno == EINTR);
}
log_socket = cookie->fd;
{
es_cookie_io_functions_t io = { NULL };
io.func_write = fun_writer;
io.func_close = fun_closer;
fp = es_fopencookie (cookie, "w", io);
}
/* On error default to a stderr based estream. */
if (!fp)
fp = es_stderr;
es_setvbuf (fp, NULL, _IOLBF, 0);
logstream = fp;
/* We always need to print the prefix and the pid for socket mode,
so that the server reading the socket can do something
meaningful. */
force_prefixes = want_socket;
missing_lf = 0;
}
/* Set the file to write log to. The special names NULL and "-" may
be used to select stderr and names formatted like
"socket:///home/foo/mylogs" may be used to write the logging to the
socket "/home/foo/mylogs". If the connection to the socket fails
or a write error is detected, the function writes to stderr and
tries the next time again to connect the socket.
*/
void
log_set_file (const char *name)
{
set_file_fd (name? name: "-", -1);
}
void
log_set_fd (int fd)
{
if (! gnupg_fd_valid (fd))
log_fatal ("logger-fd is invalid: %s\n", strerror (errno));
set_file_fd (NULL, fd);
}
/* Set a function to retrieve the directory name of a socket if
* only "socket://" has been given to log_set_file. */
void
log_set_socket_dir_cb (const char *(*fnc)(void))
{
socket_dir_cb = fnc;
}
void
log_set_pid_suffix_cb (int (*cb)(unsigned long *r_value))
{
get_pid_suffix_cb = cb;
}
void
log_set_prefix (const char *text, unsigned int flags)
{
if (text)
{
strncpy (prefix_buffer, text, sizeof (prefix_buffer)-1);
prefix_buffer[sizeof (prefix_buffer)-1] = 0;
}
with_prefix = (flags & GPGRT_LOG_WITH_PREFIX);
with_time = (flags & GPGRT_LOG_WITH_TIME);
with_pid = (flags & GPGRT_LOG_WITH_PID);
running_detached = (flags & GPGRT_LOG_RUN_DETACHED);
#ifdef HAVE_W32_SYSTEM
no_registry = (flags & GPGRT_LOG_NO_REGISTRY);
#endif
}
const char *
log_get_prefix (unsigned int *flags)
{
if (flags)
{
*flags = 0;
if (with_prefix)
*flags |= GPGRT_LOG_WITH_PREFIX;
if (with_time)
*flags |= GPGRT_LOG_WITH_TIME;
if (with_pid)
*flags |= GPGRT_LOG_WITH_PID;
if (running_detached)
*flags |= GPGRT_LOG_RUN_DETACHED;
#ifdef HAVE_W32_SYSTEM
if (no_registry)
*flags |= GPGRT_LOG_NO_REGISTRY;
#endif
}
return prefix_buffer;
}
/* This function returns true if the file descriptor FD is in use for
logging. This is preferable over a test using log_get_fd in that
it allows the logging code to use more then one file descriptor. */
int
log_test_fd (int fd)
{
if (logstream)
{
int tmp = es_fileno (logstream);
if ( tmp != -1 && tmp == fd)
return 1;
}
if (log_socket != -1 && log_socket == fd)
return 1;
return 0;
}
int
log_get_fd ()
{
return logstream? es_fileno(logstream) : -1;
}
estream_t
log_get_stream ()
{
if (!logstream)
{
log_set_file (NULL); /* Make sure a log stream has been set. */
assert (logstream);
}
return logstream;
}
static void
print_prefix (int level, int leading_backspace)
{
if (level != GPGRT_LOG_CONT)
{ /* Note this does not work for multiple line logging as we would
* need to print to a buffer first */
if (with_time && !force_prefixes)
{
struct tm *tp;
time_t atime = time (NULL);
tp = localtime (&atime);
es_fprintf_unlocked (logstream, "%04d-%02d-%02d %02d:%02d:%02d ",
1900+tp->tm_year, tp->tm_mon+1, tp->tm_mday,
tp->tm_hour, tp->tm_min, tp->tm_sec );
}
if (with_prefix || force_prefixes)
es_fputs_unlocked (prefix_buffer, logstream);
if (with_pid || force_prefixes)
{
unsigned long pidsuf;
int pidfmt;
if (get_pid_suffix_cb && (pidfmt=get_pid_suffix_cb (&pidsuf)))
es_fprintf_unlocked (logstream, pidfmt == 1? "[%u.%lu]":"[%u.%lx]",
(unsigned int)getpid (), pidsuf);
else
es_fprintf_unlocked (logstream, "[%u]", (unsigned int)getpid ());
}
if ((!with_time && (with_prefix || with_pid)) || force_prefixes)
es_putc_unlocked (':', logstream);
/* A leading backspace suppresses the extra space so that we can
correctly output, programname, filename and linenumber. */
if (!leading_backspace
&& (with_time || with_prefix || with_pid || force_prefixes))
es_putc_unlocked (' ', logstream);
}
switch (level)
{
case GPGRT_LOG_BEGIN: break;
case GPGRT_LOG_CONT: break;
case GPGRT_LOG_INFO: break;
case GPGRT_LOG_WARN: break;
case GPGRT_LOG_ERROR: break;
case GPGRT_LOG_FATAL: es_fputs_unlocked ("Fatal: ",logstream ); break;
case GPGRT_LOG_BUG: es_fputs_unlocked ("Ohhhh jeeee: ", logstream); break;
case GPGRT_LOG_DEBUG: es_fputs_unlocked ("DBG: ", logstream ); break;
default:
es_fprintf_unlocked (logstream,"[Unknown log level %d]: ", level);
break;
}
}
static void
do_logv (int level, int ignore_arg_ptr, const char *extrastring,
const char *prefmt, const char *fmt, va_list arg_ptr)
{
int leading_backspace = (fmt && *fmt == '\b');
if (!logstream)
{
#ifdef HAVE_W32_SYSTEM
char *tmp;
tmp = (no_registry
? NULL
: read_w32_registry_string (NULL, GNUPG_REGISTRY_DIR,
"DefaultLogFile"));
log_set_file (tmp && *tmp? tmp : NULL);
xfree (tmp);
#else
log_set_file (NULL); /* Make sure a log stream has been set. */
#endif
assert (logstream);
}
es_flockfile (logstream);
if (missing_lf && level != GPGRT_LOG_CONT)
es_putc_unlocked ('\n', logstream );
missing_lf = 0;
print_prefix (level, leading_backspace);
if (leading_backspace)
fmt++;
if (fmt)
{
if (prefmt)
es_fputs_unlocked (prefmt, logstream);
if (ignore_arg_ptr)
{ /* This is used by log_string and comes with the extra
* feature that after a LF the next line is indent at the
* length of the prefix. Note that we do not yet include
* the length of the timestamp and pid in the indent
* computation. */
const char *p, *pend;
for (p = fmt; (pend = strchr (p, '\n')); p = pend+1)
es_fprintf_unlocked (logstream, "%*s%.*s",
(int)((p != fmt
&& (with_prefix || force_prefixes))
?strlen (prefix_buffer)+2:0), "",
(int)(pend - p)+1, p);
es_fputs_unlocked (p, logstream);
}
else
es_vfprintf_unlocked (logstream, fmt, arg_ptr);
if (*fmt && fmt[strlen(fmt)-1] != '\n')
missing_lf = 1;
}
/* If we have an EXTRASTRING print it now while we still hold the
* lock on the logstream. */
if (extrastring)
{
int c;
if (missing_lf)
{
es_putc_unlocked ('\n', logstream);
missing_lf = 0;
}
print_prefix (level, leading_backspace);
es_fputs_unlocked (">> ", logstream);
missing_lf = 1;
while ((c = *extrastring++))
{
missing_lf = 1;
if (c == '\\')
es_fputs_unlocked ("\\\\", logstream);
else if (c == '\r')
es_fputs_unlocked ("\\r", logstream);
else if (c == '\n')
{
es_fputs_unlocked ("\\n\n", logstream);
if (*extrastring)
{
print_prefix (level, leading_backspace);
es_fputs_unlocked (">> ", logstream);
}
else
missing_lf = 0;
}
else
es_putc_unlocked (c, logstream);
}
if (missing_lf)
{
es_putc_unlocked ('\n', logstream);
missing_lf = 0;
}
}
if (level == GPGRT_LOG_FATAL)
{
if (missing_lf)
es_putc_unlocked ('\n', logstream);
es_funlockfile (logstream);
exit (2);
}
else if (level == GPGRT_LOG_BUG)
{
if (missing_lf)
es_putc_unlocked ('\n', logstream );
es_funlockfile (logstream);
/* Using backtrace requires a configure test and to pass
* -rdynamic to gcc. Thus we do not enable it now. */
/* { */
/* void *btbuf[20]; */
/* int btidx, btlen; */
/* char **btstr; */
/* btlen = backtrace (btbuf, DIM (btbuf)); */
/* btstr = backtrace_symbols (btbuf, btlen); */
/* if (btstr) */
/* for (btidx=0; btidx < btlen; btidx++) */
/* log_debug ("[%d] %s\n", btidx, btstr[btidx]); */
/* } */
abort ();
}
else
es_funlockfile (logstream);
}
void
log_log (int level, const char *fmt, ...)
{
va_list arg_ptr ;
va_start (arg_ptr, fmt) ;
do_logv (level, 0, NULL, NULL, fmt, arg_ptr);
va_end (arg_ptr);
}
void
log_logv (int level, const char *fmt, va_list arg_ptr)
{
do_logv (level, 0, NULL, NULL, fmt, arg_ptr);
}
/* Same as log_logv but PREFIX is printed immediately before FMT.
* Note that PREFIX is an additional string and independent of the
* prefix set by log_set_prefix. */
void
log_logv_with_prefix (int level, const char *prefix,
const char *fmt, va_list arg_ptr)
{
do_logv (level, 0, NULL, prefix, fmt, arg_ptr);
}
static void
do_log_ignore_arg (int level, const char *str, ...)
{
va_list arg_ptr;
va_start (arg_ptr, str);
do_logv (level, 1, NULL, NULL, str, arg_ptr);
va_end (arg_ptr);
}
/* Log STRING at LEVEL but indent from the second line on by the
* length of the prefix. */
void
log_string (int level, const char *string)
{
/* We need a dummy arg_ptr, but there is no portable way to create
* one. So we call the do_logv function through a variadic wrapper. */
do_log_ignore_arg (level, string);
}
void
log_info (const char *fmt, ...)
{
va_list arg_ptr ;
va_start (arg_ptr, fmt);
do_logv (GPGRT_LOG_INFO, 0, NULL, NULL, fmt, arg_ptr);
va_end (arg_ptr);
}
void
log_error (const char *fmt, ...)
{
va_list arg_ptr ;
va_start (arg_ptr, fmt);
do_logv (GPGRT_LOG_ERROR, 0, NULL, NULL, fmt, arg_ptr);
va_end (arg_ptr);
log_inc_errorcount ();
}
void
log_fatal (const char *fmt, ...)
{
va_list arg_ptr ;
va_start (arg_ptr, fmt);
do_logv (GPGRT_LOG_FATAL, 0, NULL, NULL, fmt, arg_ptr);
va_end (arg_ptr);
abort (); /* Never called; just to make the compiler happy. */
}
void
log_bug (const char *fmt, ...)
{
va_list arg_ptr ;
va_start (arg_ptr, fmt);
do_logv (GPGRT_LOG_BUG, 0, NULL, NULL, fmt, arg_ptr);
va_end (arg_ptr);
abort (); /* Never called; just to make the compiler happy. */
}
void
log_debug (const char *fmt, ...)
{
va_list arg_ptr ;
va_start (arg_ptr, fmt);
do_logv (GPGRT_LOG_DEBUG, 0, NULL, NULL, fmt, arg_ptr);
va_end (arg_ptr);
}
/* The same as log_debug but at the end of the output STRING is
* printed with LFs expanded to include the prefix and a final --end--
* marker. */
void
log_debug_with_string (const char *string, const char *fmt, ...)
{
va_list arg_ptr ;
va_start (arg_ptr, fmt);
do_logv (GPGRT_LOG_DEBUG, 0, string, NULL, fmt, arg_ptr);
va_end (arg_ptr);
}
void
log_printf (const char *fmt, ...)
{
va_list arg_ptr;
va_start (arg_ptr, fmt);
do_logv (fmt ? GPGRT_LOG_CONT : GPGRT_LOG_BEGIN, 0, NULL, NULL, fmt, arg_ptr);
va_end (arg_ptr);
}
/* Flush the log - this is useful to make sure that the trailing
linefeed has been printed. */
void
log_flush (void)
{
do_log_ignore_arg (GPGRT_LOG_CONT, NULL);
}
/* Print a hexdump of BUFFER. With TEXT of NULL print just the raw
dump, with TEXT just an empty string, print a trailing linefeed,
otherwise print an entire debug line. */
void
-log_printhex (const char *text, const void *buffer, size_t length)
+log_printhex (const void *buffer, size_t length, const char *fmt, ...)
{
- if (text && *text)
- log_debug ("%s ", text);
+ if (fmt && *fmt)
+ {
+ va_list arg_ptr ;
+
+ va_start (arg_ptr, fmt);
+ do_logv (GPGRT_LOG_DEBUG, 0, NULL, NULL, fmt, arg_ptr);
+ va_end (arg_ptr);
+ log_printf (" ");
+ }
if (length)
{
const unsigned char *p = buffer;
log_printf ("%02X", *p);
for (length--, p++; length--; p++)
log_printf (" %02X", *p);
}
- if (text)
+ if (fmt)
log_printf ("\n");
}
/*
void
log_printcanon () {}
is found in sexputils.c
*/
/*
void
log_printsexp () {}
is found in sexputils.c
*/
void
log_clock (const char *string)
{
#if 0
static unsigned long long initial;
struct timespec tv;
unsigned long long now;
if (clock_gettime (CLOCK_REALTIME, &tv))
{
log_debug ("error getting the realtime clock value\n");
return;
}
now = tv.tv_sec * 1000000000ull;
now += tv.tv_nsec;
if (!initial)
initial = now;
log_debug ("[%6llu] %s", (now - initial)/1000, string);
#else
/* You need to link with -ltr to enable the above code. */
log_debug ("[not enabled in the source] %s", string);
#endif
}
#ifdef GPGRT_HAVE_MACRO_FUNCTION
void
bug_at( const char *file, int line, const char *func )
{
log_log (GPGRT_LOG_BUG, "... this is a bug (%s:%d:%s)\n", file, line, func);
abort (); /* Never called; just to make the compiler happy. */
}
#else /*!GPGRT_HAVE_MACRO_FUNCTION*/
void
bug_at( const char *file, int line )
{
log_log (GPGRT_LOG_BUG, "you found a bug ... (%s:%d)\n", file, line);
abort (); /* Never called; just to make the compiler happy. */
}
#endif /*!GPGRT_HAVE_MACRO_FUNCTION*/
#ifdef GPGRT_HAVE_MACRO_FUNCTION
void
_log_assert (const char *expr, const char *file, int line, const char *func)
{
log_log (GPGRT_LOG_BUG, "Assertion \"%s\" in %s failed (%s:%d)\n",
expr, func, file, line);
abort (); /* Never called; just to make the compiler happy. */
}
#else /*!GPGRT_HAVE_MACRO_FUNCTION*/
void
_log_assert (const char *expr, const char *file, int line)
{
log_log (GPGRT_LOG_BUG, "Assertion \"%s\" failed (%s:%d)\n",
expr, file, line);
abort (); /* Never called; just to make the compiler happy. */
}
#endif /*!GPGRT_HAVE_MACRO_FUNCTION*/
diff --git a/common/logging.h b/common/logging.h
index 2225100cb..cb1ec1160 100644
--- a/common/logging.h
+++ b/common/logging.h
@@ -1,141 +1,142 @@
/* logging.h
* Copyright (C) 1999, 2000, 2001, 2004, 2006,
* 2010 Free Software Foundation, Inc.
*
* This file is part of GnuPG.
*
* GnuPG is free software; you can redistribute and/or modify this
* part of GnuPG under the terms of either
*
* - the GNU Lesser General Public License as published by the Free
* Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* or
*
* - the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at
* your option) any later version.
*
* or both in parallel, as here.
*
* GnuPG is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copies of the GNU General Public License
* and the GNU Lesser General Public License along with this program;
* if not, see .
*/
#ifndef GNUPG_COMMON_LOGGING_H
#define GNUPG_COMMON_LOGGING_H
#include
#include
#include
#include "mischelp.h"
#include "w32help.h"
int log_get_errorcount (int clear);
void log_inc_errorcount (void);
void log_set_file( const char *name );
void log_set_fd (int fd);
void log_set_socket_dir_cb (const char *(*fnc)(void));
void log_set_pid_suffix_cb (int (*cb)(unsigned long *r_value));
void log_set_prefix (const char *text, unsigned int flags);
const char *log_get_prefix (unsigned int *flags);
int log_test_fd (int fd);
int log_get_fd(void);
estream_t log_get_stream (void);
#ifdef GPGRT_HAVE_MACRO_FUNCTION
void bug_at (const char *file, int line, const char *func)
GPGRT_ATTR_NORETURN;
void _log_assert (const char *expr, const char *file, int line,
const char *func) GPGRT_ATTR_NORETURN;
# define BUG() bug_at( __FILE__ , __LINE__, __FUNCTION__)
# define log_assert(expr) \
((expr) \
? (void) 0 \
: _log_assert (#expr, __FILE__, __LINE__, __FUNCTION__))
#else /*!GPGRT_HAVE_MACRO_FUNCTION*/
void bug_at (const char *file, int line);
void _log_assert (const char *expr, const char *file, int line);
# define BUG() bug_at( __FILE__ , __LINE__ )
# define log_assert(expr) \
((expr) \
? (void) 0 \
: _log_assert (#expr, __FILE__, __LINE__))
#endif /*!GPGRT_HAVE_MACRO_FUNCTION*/
/* Flag values for log_set_prefix. */
#define GPGRT_LOG_WITH_PREFIX 1
#define GPGRT_LOG_WITH_TIME 2
#define GPGRT_LOG_WITH_PID 4
#define GPGRT_LOG_RUN_DETACHED 256
#define GPGRT_LOG_NO_REGISTRY 512
/* Log levels as used by log_log. */
enum jnlib_log_levels {
GPGRT_LOG_BEGIN,
GPGRT_LOG_CONT,
GPGRT_LOG_INFO,
GPGRT_LOG_WARN,
GPGRT_LOG_ERROR,
GPGRT_LOG_FATAL,
GPGRT_LOG_BUG,
GPGRT_LOG_DEBUG
};
void log_log (int level, const char *fmt, ...) GPGRT_ATTR_PRINTF(2,3);
void log_logv (int level, const char *fmt, va_list arg_ptr);
void log_logv_with_prefix (int level, const char *prefix,
const char *fmt, va_list arg_ptr);
void log_string (int level, const char *string);
void log_bug (const char *fmt, ...) GPGRT_ATTR_NR_PRINTF(1,2);
void log_fatal (const char *fmt, ...) GPGRT_ATTR_NR_PRINTF(1,2);
void log_error (const char *fmt, ...) GPGRT_ATTR_PRINTF(1,2);
void log_info (const char *fmt, ...) GPGRT_ATTR_PRINTF(1,2);
void log_debug (const char *fmt, ...) GPGRT_ATTR_PRINTF(1,2);
void log_debug_with_string (const char *string, const char *fmt,
...) GPGRT_ATTR_PRINTF(2,3);
void log_printf (const char *fmt, ...) GPGRT_ATTR_PRINTF(1,2);
void log_flush (void);
-/* Print a hexdump of BUFFER. With TEXT passes as NULL print just the
- raw dump, with TEXT being an empty string, print a trailing
- linefeed, otherwise print an entire debug line with TEXT followed
- by the hexdump and a final LF. */
-void log_printhex (const char *text, const void *buffer, size_t length);
+/* Print a hexdump of BUFFER. With FMT passed as NULL print just the
+ * raw dump, with FMT being an empty string, print a trailing
+ * linefeed, otherwise print an entire debug line with expanded FMT
+ * followed by the hexdump and a final LF. */
+void log_printhex (const void *buffer, size_t length,
+ const char *fmt, ...) GPGRT_ATTR_PRINTF(3,4);
void log_clock (const char *string);
/* Some handy assertion macros which don't abort. */
#define return_if_fail(expr) do { \
if (!(expr)) { \
log_debug ("%s:%d: assertion '%s' failed\n", \
__FILE__, __LINE__, #expr ); \
return; \
} } while (0)
#define return_null_if_fail(expr) do { \
if (!(expr)) { \
log_debug ("%s:%d: assertion '%s' failed\n", \
__FILE__, __LINE__, #expr ); \
return NULL; \
} } while (0)
#define return_val_if_fail(expr,val) do { \
if (!(expr)) { \
log_debug ("%s:%d: assertion '%s' failed\n", \
__FILE__, __LINE__, #expr ); \
return (val); \
} } while (0)
#define never_reached() do { \
log_debug ("%s:%d: oops - should never get here\n", \
__FILE__, __LINE__ ); \
} while (0)
#endif /*GNUPG_COMMON_LOGGING_H*/
diff --git a/configure.ac b/configure.ac
index 25125a5f4..a2f4bd730 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1,2095 +1,2095 @@
# configure.ac - for GnuPG 2.2
# Copyright (C) 1998-2019 Free Software Foundation, Inc.
# Copyright (C) 1998-2019 Werner Koch
# Copyright (C) 2003-2020 g10 Code GmbH
#
# This file is part of GnuPG.
#
# GnuPG is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 3 of the License, or
# (at your option) any later version.
#
# GnuPG is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, see .
# Process this file with autoconf to produce a configure script.
AC_PREREQ(2.61)
min_automake_version="1.14"
# To build a release you need to create a tag with the version number
# (git tag -s gnupg-2.n.m) and run "./autogen.sh --force". Please
# bump the version number immediately *after* the release and do
# another commit and push so that the git magic is able to work.
m4_define([mym4_package],[gnupg])
m4_define([mym4_major], [2])
m4_define([mym4_minor], [2])
m4_define([mym4_micro], [21])
# To start a new development series, i.e a new major or minor number
# you need to mark an arbitrary commit before the first beta release
# with an annotated tag. For example the 2.1 branch starts off with
# the tag "gnupg-2.1-base". This is used as the base for counting
# beta numbers before the first release of a series.
# Below is m4 magic to extract and compute the git revision number,
# the decimalized short revision number, a beta version string and a
# flag indicating a development version (mym4_isbeta). Note that the
# m4 processing is done by autoconf and not during the configure run.
m4_define([mym4_verslist], m4_split(m4_esyscmd([./autogen.sh --find-version] \
mym4_package mym4_major mym4_minor mym4_micro),[:]))
m4_define([mym4_isbeta], m4_argn(2, mym4_verslist))
m4_define([mym4_version], m4_argn(4, mym4_verslist))
m4_define([mym4_revision], m4_argn(7, mym4_verslist))
m4_define([mym4_revision_dec], m4_argn(8, mym4_verslist))
m4_esyscmd([echo ]mym4_version[>VERSION])
AC_INIT([mym4_package],[mym4_version], [https://bugs.gnupg.org])
# When changing the SWDB tag please also adjust the hard coded tags in
# build-aux/speedo.mk and Makefile.am
AC_DEFINE_UNQUOTED(GNUPG_SWDB_TAG, "gnupg22", [swdb tag for this branch])
NEED_GPG_ERROR_VERSION=1.24
NEED_LIBGCRYPT_API=1
NEED_LIBGCRYPT_VERSION=1.7.0
NEED_LIBASSUAN_API=2
NEED_LIBASSUAN_VERSION=2.5.0
NEED_KSBA_API=1
NEED_KSBA_VERSION=1.3.4
NEED_NTBTLS_API=1
NEED_NTBTLS_VERSION=0.1.0
NEED_NPTH_API=1
NEED_NPTH_VERSION=1.2
NEED_GNUTLS_VERSION=3.0
NEED_SQLITE_VERSION=3.7
development_version=mym4_isbeta
PACKAGE=$PACKAGE_NAME
PACKAGE_GT=${PACKAGE_NAME}2
VERSION=$PACKAGE_VERSION
AC_CONFIG_AUX_DIR([build-aux])
AC_CONFIG_SRCDIR([sm/gpgsm.c])
AC_CONFIG_HEADER([config.h])
AM_INIT_AUTOMAKE([serial-tests dist-bzip2 no-dist-gzip])
AC_CANONICAL_HOST
AB_INIT
AC_GNU_SOURCE
# Some status variables.
have_gpg_error=no
have_libgcrypt=no
have_libassuan=no
have_ksba=no
have_ntbtls=no
have_gnutls=no
have_sqlite=no
have_npth=no
have_libusb=no
have_system_resolver=no
gnupg_have_ldap="n/a"
use_zip=yes
use_bzip2=yes
use_exec=yes
use_trust_models=yes
use_tofu=yes
use_libdns=yes
card_support=yes
use_ccid_driver=auto
dirmngr_auto_start=yes
use_tls_library=no
large_secmem=no
show_tor_support=no
GNUPG_BUILD_PROGRAM(gpg, yes)
GNUPG_BUILD_PROGRAM(gpgsm, yes)
# The agent is a required part and can't be disabled anymore.
build_agent=yes
GNUPG_BUILD_PROGRAM(scdaemon, yes)
GNUPG_BUILD_PROGRAM(g13, no)
GNUPG_BUILD_PROGRAM(dirmngr, yes)
GNUPG_BUILD_PROGRAM(doc, yes)
GNUPG_BUILD_PROGRAM(symcryptrun, no)
# We use gpgtar to unpack test data, hence we always build it. If the
# user opts out, we simply don't install it.
GNUPG_BUILD_PROGRAM(gpgtar, yes)
# We also install the gpg-wks-server tool by default but disable it
# later for platforms where it can't be build.
GNUPG_BUILD_PROGRAM(wks-tools, yes)
AC_SUBST(PACKAGE)
AC_SUBST(PACKAGE_GT)
AC_SUBST(VERSION)
AC_DEFINE_UNQUOTED(PACKAGE, "$PACKAGE", [Name of this package])
AC_DEFINE_UNQUOTED(PACKAGE_GT, "$PACKAGE_GT",
[Name of this package for gettext])
AC_DEFINE_UNQUOTED(VERSION, "$VERSION", [Version of this package])
AC_DEFINE_UNQUOTED(PACKAGE_BUGREPORT, "$PACKAGE_BUGREPORT",
[Bug report address])
AC_DEFINE_UNQUOTED(NEED_LIBGCRYPT_VERSION, "$NEED_LIBGCRYPT_VERSION",
[Required version of Libgcrypt])
AC_DEFINE_UNQUOTED(NEED_KSBA_VERSION, "$NEED_KSBA_VERSION",
[Required version of Libksba])
AC_DEFINE_UNQUOTED(NEED_NTBTLS_VERSION, "$NEED_NTBTLS_VERSION",
[Required version of NTBTLS])
# The default is to use the modules from this package and the few
# other packages in a standard place; i.e where this package gets
# installed. With these options it is possible to override these
# ${prefix} depended values with fixed paths, which can't be replaced
# at make time. See also am/cmacros.am and the defaults in AH_BOTTOM.
AC_ARG_WITH(agent-pgm,
[ --with-agent-pgm=PATH Use PATH as the default for the agent)],
GNUPG_AGENT_PGM="$withval", GNUPG_AGENT_PGM="" )
AC_SUBST(GNUPG_AGENT_PGM)
AM_CONDITIONAL(GNUPG_AGENT_PGM, test -n "$GNUPG_AGENT_PGM")
show_gnupg_agent_pgm="(default)"
test -n "$GNUPG_AGENT_PGM" && show_gnupg_agent_pgm="$GNUPG_AGENT_PGM"
AC_ARG_WITH(pinentry-pgm,
[ --with-pinentry-pgm=PATH Use PATH as the default for the pinentry)],
GNUPG_PINENTRY_PGM="$withval", GNUPG_PINENTRY_PGM="" )
AC_SUBST(GNUPG_PINENTRY_PGM)
AM_CONDITIONAL(GNUPG_PINENTRY_PGM, test -n "$GNUPG_PINENTRY_PGM")
show_gnupg_pinentry_pgm="(default)"
test -n "$GNUPG_PINENTRY_PGM" && show_gnupg_pinentry_pgm="$GNUPG_PINENTRY_PGM"
AC_ARG_WITH(scdaemon-pgm,
[ --with-scdaemon-pgm=PATH Use PATH as the default for the scdaemon)],
GNUPG_SCDAEMON_PGM="$withval", GNUPG_SCDAEMON_PGM="" )
AC_SUBST(GNUPG_SCDAEMON_PGM)
AM_CONDITIONAL(GNUPG_SCDAEMON_PGM, test -n "$GNUPG_SCDAEMON_PGM")
show_gnupg_scdaemon_pgm="(default)"
test -n "$GNUPG_SCDAEMON_PGM" && show_gnupg_scdaemon_pgm="$GNUPG_SCDAEMON_PGM"
AC_ARG_WITH(dirmngr-pgm,
[ --with-dirmngr-pgm=PATH Use PATH as the default for the dirmngr)],
GNUPG_DIRMNGR_PGM="$withval", GNUPG_DIRMNGR_PGM="" )
AC_SUBST(GNUPG_DIRMNGR_PGM)
AM_CONDITIONAL(GNUPG_DIRMNGR_PGM, test -n "$GNUPG_DIRMNGR_PGM")
show_gnupg_dirmngr_pgm="(default)"
test -n "$GNUPG_DIRMNGR_PGM" && show_gnupg_dirmngr_pgm="$GNUPG_DIRMNGR_PGM"
AC_ARG_WITH(protect-tool-pgm,
[ --with-protect-tool-pgm=PATH Use PATH as the default for the protect-tool)],
GNUPG_PROTECT_TOOL_PGM="$withval", GNUPG_PROTECT_TOOL_PGM="" )
AC_SUBST(GNUPG_PROTECT_TOOL_PGM)
AM_CONDITIONAL(GNUPG_PROTECT_TOOL_PGM, test -n "$GNUPG_PROTECT_TOOL_PGM")
show_gnupg_protect_tool_pgm="(default)"
test -n "$GNUPG_PROTECT_TOOL_PGM" \
&& show_gnupg_protect_tool_pgm="$GNUPG_PROTECT_TOOL_PGM"
AC_ARG_WITH(dirmngr-ldap-pgm,
[ --with-dirmngr-ldap-pgm=PATH Use PATH as the default for the dirmngr ldap wrapper)],
GNUPG_DIRMNGR_LDAP_PGM="$withval", GNUPG_DIRMNGR_LDAP_PGM="" )
AC_SUBST(GNUPG_DIRMNGR_LDAP_PGM)
AM_CONDITIONAL(GNUPG_DIRMNGR_LDAP_PGM, test -n "$GNUPG_DIRMNGR_LDAP_PGM")
show_gnupg_dirmngr_ldap_pgm="(default)"
test -n "$GNUPG_DIRMNGR_LDAP_PGM" \
&& show_gnupg_dirmngr_ldap_pgm="$GNUPG_DIRMNGR_LDAP_PGM"
#
# For a long time gpg 2.x was installed as gpg2. This changed with
# 2.2. This option can be used to install gpg under the name gpg2.
#
AC_ARG_ENABLE(gpg-is-gpg2,
AC_HELP_STRING([--enable-gpg-is-gpg2],[Set installed name of gpg to gpg2]),
gpg_is_gpg2=$enableval)
if test "$gpg_is_gpg2" = "yes"; then
AC_DEFINE(USE_GPG2_HACK, 1, [Define to install gpg as gpg2])
fi
AM_CONDITIONAL(USE_GPG2_HACK, test "$gpg_is_gpg2" = "yes")
# SELinux support includes tracking of sensitive files to avoid
# leaking their contents through processing these files by gpg itself
AC_MSG_CHECKING([whether SELinux support is requested])
AC_ARG_ENABLE(selinux-support,
AC_HELP_STRING([--enable-selinux-support],
[enable SELinux support]),
selinux_support=$enableval, selinux_support=no)
AC_MSG_RESULT($selinux_support)
AC_MSG_CHECKING([whether to allocate extra secure memory])
AC_ARG_ENABLE(large-secmem,
AC_HELP_STRING([--enable-large-secmem],
[allocate extra secure memory]),
large_secmem=$enableval, large_secmem=no)
AC_MSG_RESULT($large_secmem)
if test "$large_secmem" = yes ; then
SECMEM_BUFFER_SIZE=65536
else
SECMEM_BUFFER_SIZE=32768
fi
AC_DEFINE_UNQUOTED(SECMEM_BUFFER_SIZE,$SECMEM_BUFFER_SIZE,
[Size of secure memory buffer])
AC_MSG_CHECKING([calibrated passphrase-stretching (s2k) duration])
AC_ARG_WITH(agent-s2k-calibration,
AC_HELP_STRING([--with-agent-s2k-calibration=MSEC],
[calibrate passphrase stretching (s2k) to MSEC milliseconds]),
agent_s2k_calibration=$withval, agent_s2k_calibration=100)
AC_MSG_RESULT($agent_s2k_calibration milliseconds)
AC_DEFINE_UNQUOTED(AGENT_S2K_CALIBRATION, $agent_s2k_calibration,
[Agent s2k calibration time (ms)])
AC_MSG_CHECKING([whether to enable trust models])
AC_ARG_ENABLE(trust-models,
AC_HELP_STRING([--disable-trust-models],
[disable all trust models except "always"]),
use_trust_models=$enableval)
AC_MSG_RESULT($use_trust_models)
if test "$use_trust_models" = no ; then
AC_DEFINE(NO_TRUST_MODELS, 1,
[Define to include only trust-model always])
fi
AC_MSG_CHECKING([whether to enable TOFU])
AC_ARG_ENABLE(tofu,
AC_HELP_STRING([--disable-tofu],
[disable the TOFU trust model]),
use_tofu=$enableval, use_tofu=$use_trust_models)
AC_MSG_RESULT($use_tofu)
if test "$use_trust_models" = no && test "$use_tofu" = yes; then
AC_MSG_ERROR([both --disable-trust-models and --enable-tofu given])
fi
AC_MSG_CHECKING([whether to enable libdns])
AC_ARG_ENABLE(libdns,
AC_HELP_STRING([--disable-libdns],
[do not build with libdns support]),
use_libdns=$enableval, use_libdns=yes)
AC_MSG_RESULT($use_libdns)
if test x"$use_libdns" = xyes ; then
AC_DEFINE(USE_LIBDNS, 1, [Build with integrated libdns support])
fi
AM_CONDITIONAL(USE_LIBDNS, test "$use_libdns" = yes)
#
# Options to disable algorithm
#
GNUPG_GPG_DISABLE_ALGO([rsa],[RSA public key])
# Elgamal is a MUST algorithm
# DSA is a MUST algorithm
GNUPG_GPG_DISABLE_ALGO([ecdh],[ECDH public key])
GNUPG_GPG_DISABLE_ALGO([ecdsa],[ECDSA public key])
GNUPG_GPG_DISABLE_ALGO([eddsa],[EdDSA public key])
GNUPG_GPG_DISABLE_ALGO([idea],[IDEA cipher])
# 3DES is a MUST algorithm
GNUPG_GPG_DISABLE_ALGO([cast5],[CAST5 cipher])
GNUPG_GPG_DISABLE_ALGO([blowfish],[BLOWFISH cipher])
GNUPG_GPG_DISABLE_ALGO([aes128],[AES128 cipher])
GNUPG_GPG_DISABLE_ALGO([aes192],[AES192 cipher])
GNUPG_GPG_DISABLE_ALGO([aes256],[AES256 cipher])
GNUPG_GPG_DISABLE_ALGO([twofish],[TWOFISH cipher])
GNUPG_GPG_DISABLE_ALGO([camellia128],[CAMELLIA128 cipher])
GNUPG_GPG_DISABLE_ALGO([camellia192],[CAMELLIA192 cipher])
GNUPG_GPG_DISABLE_ALGO([camellia256],[CAMELLIA256 cipher])
GNUPG_GPG_DISABLE_ALGO([md5],[MD5 hash])
# SHA1 is a MUST algorithm
GNUPG_GPG_DISABLE_ALGO([rmd160],[RIPE-MD160 hash])
GNUPG_GPG_DISABLE_ALGO([sha224],[SHA-224 hash])
# SHA256 is a MUST algorithm for GnuPG.
GNUPG_GPG_DISABLE_ALGO([sha384],[SHA-384 hash])
GNUPG_GPG_DISABLE_ALGO([sha512],[SHA-512 hash])
# Allow disabling of zip support.
# This is in general not a good idea because according to rfc4880 OpenPGP
# implementations SHOULD support ZLIB.
AC_MSG_CHECKING([whether to enable the ZIP and ZLIB compression algorithm])
AC_ARG_ENABLE(zip,
AC_HELP_STRING([--disable-zip],
[disable the ZIP and ZLIB compression algorithm]),
use_zip=$enableval)
AC_MSG_RESULT($use_zip)
# Allow disabling of bzib2 support.
# It is defined only after we confirm the library is available later
AC_MSG_CHECKING([whether to enable the BZIP2 compression algorithm])
AC_ARG_ENABLE(bzip2,
AC_HELP_STRING([--disable-bzip2],[disable the BZIP2 compression algorithm]),
use_bzip2=$enableval)
AC_MSG_RESULT($use_bzip2)
# Configure option to allow or disallow execution of external
# programs, like a photo viewer.
AC_MSG_CHECKING([whether to enable external program execution])
AC_ARG_ENABLE(exec,
AC_HELP_STRING([--disable-exec],[disable all external program execution]),
use_exec=$enableval)
AC_MSG_RESULT($use_exec)
if test "$use_exec" = no ; then
AC_DEFINE(NO_EXEC,1,[Define to disable all external program execution])
fi
if test "$use_exec" = yes ; then
AC_MSG_CHECKING([whether to enable photo ID viewing])
AC_ARG_ENABLE(photo-viewers,
[ --disable-photo-viewers disable photo ID viewers],
[if test "$enableval" = no ; then
AC_DEFINE(DISABLE_PHOTO_VIEWER,1,[define to disable photo viewing])
fi],enableval=yes)
gnupg_cv_enable_photo_viewers=$enableval
AC_MSG_RESULT($enableval)
if test "$gnupg_cv_enable_photo_viewers" = yes ; then
AC_MSG_CHECKING([whether to use a fixed photo ID viewer])
AC_ARG_WITH(photo-viewer,
[ --with-photo-viewer=FIXED_VIEWER set a fixed photo ID viewer],
[if test "$withval" = yes ; then
withval=no
elif test "$withval" != no ; then
AC_DEFINE_UNQUOTED(FIXED_PHOTO_VIEWER,"$withval",
[if set, restrict photo-viewer to this])
fi],withval=no)
AC_MSG_RESULT($withval)
fi
fi
#
# Check for the key/uid cache size. This can't be zero, but can be
# pretty small on embedded systems. This is used for the gpg part.
#
AC_MSG_CHECKING([for the size of the key and uid cache])
AC_ARG_ENABLE(key-cache,
AC_HELP_STRING([--enable-key-cache=SIZE],
[Set key cache to SIZE (default 4096)]),,enableval=4096)
if test "$enableval" = "no"; then
enableval=5
elif test "$enableval" = "yes" || test "$enableval" = ""; then
enableval=4096
fi
changequote(,)dnl
key_cache_size=`echo "$enableval" | sed 's/[A-Za-z]//g'`
changequote([,])dnl
if test "$enableval" != "$key_cache_size" || test "$key_cache_size" -lt 5; then
AC_MSG_ERROR([invalid key-cache size])
fi
AC_MSG_RESULT($key_cache_size)
AC_DEFINE_UNQUOTED(PK_UID_CACHE_SIZE,$key_cache_size,
[Size of the key and UID caches])
#
# Check whether we want to use Linux capabilities
#
AC_MSG_CHECKING([whether use of capabilities is requested])
AC_ARG_WITH(capabilities,
[ --with-capabilities use linux capabilities [default=no]],
[use_capabilities="$withval"],[use_capabilities=no])
AC_MSG_RESULT($use_capabilities)
#
# Check whether to disable the card support
AC_MSG_CHECKING([whether smartcard support is requested])
AC_ARG_ENABLE(card-support,
AC_HELP_STRING([--disable-card-support],
[disable smartcard support]),
card_support=$enableval)
AC_MSG_RESULT($card_support)
if test "$card_support" = yes ; then
AC_DEFINE(ENABLE_CARD_SUPPORT,1,[Define to include smartcard support])
else
build_scdaemon=no
fi
#
# Allow disabling of internal CCID support.
# It is defined only after we confirm the library is available later
#
AC_MSG_CHECKING([whether to enable the internal CCID driver])
AC_ARG_ENABLE(ccid-driver,
AC_HELP_STRING([--disable-ccid-driver],
[disable the internal CCID driver]),
use_ccid_driver=$enableval)
AC_MSG_RESULT($use_ccid_driver)
AC_MSG_CHECKING([whether to auto start dirmngr])
AC_ARG_ENABLE(dirmngr-auto-start,
AC_HELP_STRING([--disable-dirmngr-auto-start],
[disable auto starting of the dirmngr]),
dirmngr_auto_start=$enableval)
AC_MSG_RESULT($dirmngr_auto_start)
if test "$dirmngr_auto_start" = yes ; then
AC_DEFINE(USE_DIRMNGR_AUTO_START,1,
[Define to enable auto starting of the dirmngr])
fi
#
# To avoid double inclusion of config.h which might happen at some
# places, we add the usual double inclusion protection at the top of
# config.h.
#
AH_TOP([
#ifndef GNUPG_CONFIG_H_INCLUDED
#define GNUPG_CONFIG_H_INCLUDED
])
#
# Stuff which goes at the bottom of config.h.
#
AH_BOTTOM([
/* This is the major version number of GnuPG so that
source included files can test for this. Note, that
we use 2 here even for GnuPG 1.9.x. */
#define GNUPG_MAJOR_VERSION 2
/* Now to separate file name parts.
Please note that the string version must not contain more
than one character because the code assumes strlen()==1 */
#ifdef HAVE_DOSISH_SYSTEM
#define DIRSEP_C '\\'
#define DIRSEP_S "\\"
#define EXTSEP_C '.'
#define EXTSEP_S "."
#define PATHSEP_C ';'
#define PATHSEP_S ";"
#define EXEEXT_S ".exe"
#else
#define DIRSEP_C '/'
#define DIRSEP_S "/"
#define EXTSEP_C '.'
#define EXTSEP_S "."
#define PATHSEP_C ':'
#define PATHSEP_S ":"
#define EXEEXT_S ""
#endif
/* This is the same as VERSION, but should be overridden if the
platform cannot handle things like dots '.' in filenames. Set
SAFE_VERSION_DOT and SAFE_VERSION_DASH to whatever SAFE_VERSION
uses for dots and dashes. */
#define SAFE_VERSION VERSION
#define SAFE_VERSION_DOT '.'
#define SAFE_VERSION_DASH '-'
/* Some global constants.
* Note that the homedir must not end in a slash. */
#ifdef HAVE_DOSISH_SYSTEM
# ifdef HAVE_DRIVE_LETTERS
# define GNUPG_DEFAULT_HOMEDIR "c:/gnupg"
# else
# define GNUPG_DEFAULT_HOMEDIR "/gnupg"
# endif
#elif defined(__VMS)
#define GNUPG_DEFAULT_HOMEDIR "/SYS$LOGIN/gnupg"
#else
#define GNUPG_DEFAULT_HOMEDIR "~/.gnupg"
#endif
#define GNUPG_PRIVATE_KEYS_DIR "private-keys-v1.d"
#define GNUPG_OPENPGP_REVOC_DIR "openpgp-revocs.d"
/* For some systems (DOS currently), we hardcode the path here. For
POSIX systems the values are constructed by the Makefiles, so that
the values may be overridden by the make invocations; this is to
comply with the GNU coding standards. Note that these values are
only defaults. */
#ifdef HAVE_DOSISH_SYSTEM
# ifdef HAVE_DRIVE_LETTERS
# define GNUPG_BINDIR "c:\\gnupg"
# define GNUPG_LIBEXECDIR "c:\\gnupg"
# define GNUPG_LIBDIR "c:\\gnupg"
# define GNUPG_DATADIR "c:\\gnupg"
# define GNUPG_SYSCONFDIR "c:\\gnupg"
# else
# define GNUPG_BINDIR "\\gnupg"
# define GNUPG_LIBEXECDIR "\\gnupg"
# define GNUPG_LIBDIR "\\gnupg"
# define GNUPG_DATADIR "\\gnupg"
# define GNUPG_SYSCONFDIR "\\gnupg"
# endif
#endif
/* Derive some other constants. */
#if !(defined(HAVE_FORK) && defined(HAVE_PIPE) && defined(HAVE_WAITPID))
#define EXEC_TEMPFILE_ONLY
#endif
/* We didn't define endianness above, so get it from OS macros. This
is intended for making fat binary builds on OS X. */
#if !defined(BIG_ENDIAN_HOST) && !defined(LITTLE_ENDIAN_HOST)
#if defined(__BIG_ENDIAN__)
#define BIG_ENDIAN_HOST 1
#elif defined(__LITTLE_ENDIAN__)
#define LITTLE_ENDIAN_HOST 1
#else
#error "No endianness found"
#endif
#endif
/* Hack used for W32: ldap.m4 also tests for the ASCII version of
ldap_start_tls_s because that is the actual symbol used in the
library. winldap.h redefines it to our commonly used value,
thus we define our usual macro here. */
#ifdef HAVE_LDAP_START_TLS_SA
# ifndef HAVE_LDAP_START_TLS_S
# define HAVE_LDAP_START_TLS_S 1
# endif
#endif
/* Provide the es_ macro for estream. */
#define GPGRT_ENABLE_ES_MACROS 1
/* Tell libgcrypt not to use its own libgpg-error implementation. */
#define USE_LIBGPG_ERROR 1
/* Tell Libgcrypt not to include deprecated definitions. */
#define GCRYPT_NO_DEPRECATED 1
/* Our HTTP code is used in estream mode. */
#define HTTP_USE_ESTREAM 1
/* Under W32 we do an explicit socket initialization, thus we need to
avoid the on-demand initialization which would also install an atexit
handler. */
#define HTTP_NO_WSASTARTUP
/* Under Windows we use the gettext code from libgpg-error. */
#define GPG_ERR_ENABLE_GETTEXT_MACROS
/* Under WindowsCE we use the strerror replacement from libgpg-error. */
#define GPG_ERR_ENABLE_ERRNO_MACROS
#endif /*GNUPG_CONFIG_H_INCLUDED*/
])
AM_MAINTAINER_MODE
AC_ARG_VAR(SYSROOT,[locate config scripts also below that directory])
# Checks for programs.
AC_MSG_NOTICE([checking for programs])
AC_PROG_MAKE_SET
AM_SANITY_CHECK
missing_dir=`cd $ac_aux_dir && pwd`
AM_MISSING_PROG(ACLOCAL, aclocal, $missing_dir)
AM_MISSING_PROG(AUTOCONF, autoconf, $missing_dir)
AM_MISSING_PROG(AUTOMAKE, automake, $missing_dir)
AM_MISSING_PROG(AUTOHEADER, autoheader, $missing_dir)
AM_MISSING_PROG(MAKEINFO, makeinfo, $missing_dir)
AM_SILENT_RULES
AC_PROG_AWK
AC_PROG_CC
AC_PROG_CPP
AM_PROG_CC_C_O
if test "x$ac_cv_prog_cc_c89" = "xno" ; then
AC_MSG_ERROR([[No C-89 compiler found]])
fi
AC_PROG_INSTALL
AC_PROG_LN_S
AC_PROG_RANLIB
AC_CHECK_TOOL(AR, ar, :)
AC_PATH_PROG(PERL,"perl")
AC_CHECK_TOOL(WINDRES, windres, :)
AC_PATH_PROG(YAT2M, "yat2m")
AC_ARG_VAR(YAT2M, [tool to convert texi to man pages])
AM_CONDITIONAL(HAVE_YAT2M, test -n "$ac_cv_path_YAT2M")
AC_ISC_POSIX
AC_SYS_LARGEFILE
GNUPG_CHECK_USTAR
# We need to compile and run a program on the build machine. A
# comment in libgpg-error says that the AC_PROG_CC_FOR_BUILD macro in
# the AC archive is broken for autoconf 2.57. Given that there is no
# newer version of that macro, we assume that it is also broken for
# autoconf 2.61 and thus we use a simple but usually sufficient
# approach.
AC_MSG_CHECKING(for cc for build)
if test "$cross_compiling" = "yes"; then
CC_FOR_BUILD="${CC_FOR_BUILD-cc}"
else
CC_FOR_BUILD="${CC_FOR_BUILD-$CC}"
fi
AC_MSG_RESULT($CC_FOR_BUILD)
AC_ARG_VAR(CC_FOR_BUILD,[build system C compiler])
# We need to call this macro because other pkg-config macros are
# not always used.
PKG_PROG_PKG_CONFIG
try_gettext=yes
require_iconv=yes
have_dosish_system=no
have_w32_system=no
have_w32ce_system=no
have_android_system=no
use_simple_gettext=no
use_ldapwrapper=yes
mmap_needed=yes
require_pipe_to_unblock_pselect=yes
case "${host}" in
*-mingw32*)
# special stuff for Windoze NT
ac_cv_have_dev_random=no
AC_DEFINE(USE_ONLY_8DOT3,1,
[Set this to limit filenames to the 8.3 format])
AC_DEFINE(USE_SIMPLE_GETTEXT,1,
[Because the Unix gettext has too much overhead on
MingW32 systems and these systems lack Posix functions,
we use a simplified version of gettext])
have_dosish_system=yes
have_w32_system=yes
require_iconv=no
require_pipe_to_unblock_pselect=no
case "${host}" in
*-mingw32ce*)
have_w32ce_system=yes
;;
*)
AC_DEFINE(HAVE_DRIVE_LETTERS,1,
[Defined if the OS supports drive letters.])
;;
esac
try_gettext="no"
use_simple_gettext=yes
mmap_needed=no
build_wks_tools=no
;;
i?86-emx-os2 | i?86-*-os2*emx )
# OS/2 with the EMX environment
ac_cv_have_dev_random=no
AC_DEFINE(HAVE_DRIVE_LETTERS)
have_dosish_system=yes
try_gettext="no"
build_wks_tools=no
;;
i?86-*-msdosdjgpp*)
# DOS with the DJGPP environment
ac_cv_have_dev_random=no
AC_DEFINE(HAVE_DRIVE_LETTERS)
have_dosish_system=yes
try_gettext="no"
build_wks_tools=no
;;
*-*-hpux*)
if test -z "$GCC" ; then
CFLAGS="-Ae -D_HPUX_SOURCE $CFLAGS"
fi
;;
*-dec-osf4*)
if test -z "$GCC" ; then
# Suppress all warnings
# to get rid of the unsigned/signed char mismatch warnings.
CFLAGS="-w $CFLAGS"
fi
;;
*-dec-osf5*)
if test -z "$GCC" ; then
# Use the newer compiler `-msg_disable ptrmismatch1' to
# get rid of the unsigned/signed char mismatch warnings.
# Using this may hide other pointer mismatch warnings, but
# it at least lets other warning classes through
CFLAGS="-msg_disable ptrmismatch1 $CFLAGS"
fi
;;
m68k-atari-mint)
;;
*-linux-android*)
have_android_system=yes
# Android is fully utf-8 and we do not want to use iconv to
# keeps things simple
require_iconv=no
build_wks_tools=no
;;
*-apple-darwin*)
AC_DEFINE(_DARWIN_C_SOURCE, 900000L,
Expose all libc features (__DARWIN_C_FULL).)
;;
*-*-netbsd*)
require_pipe_to_unblock_pselect=yes
;;
*)
;;
esac
if test "$require_pipe_to_unblock_pselect" = yes; then
AC_DEFINE(HAVE_PSELECT_NO_EINTR, 1,
[Defined if we run on systems like NetBSD, where
pselect cannot be unblocked by signal from a thread
within the same process. We use pipe in this case, instead.])
fi
if test "$have_dosish_system" = yes; then
AC_DEFINE(HAVE_DOSISH_SYSTEM,1,
[Defined if we run on some of the PCDOS like systems
(DOS, Windoze. OS/2) with special properties like
no file modes, case insensitive file names and preferred
use of backslashes as directory name separators.])
fi
AM_CONDITIONAL(HAVE_DOSISH_SYSTEM, test "$have_dosish_system" = yes)
AM_CONDITIONAL(USE_SIMPLE_GETTEXT, test x"$use_simple_gettext" = xyes)
if test "$have_w32_system" = yes; then
AC_DEFINE(HAVE_W32_SYSTEM,1, [Defined if we run on a W32 API based system])
if test "$have_w32ce_system" = yes; then
AC_DEFINE(HAVE_W32CE_SYSTEM,1,[Defined if we run on WindowsCE])
fi
fi
AM_CONDITIONAL(HAVE_W32_SYSTEM, test "$have_w32_system" = yes)
AM_CONDITIONAL(HAVE_W32CE_SYSTEM, test "$have_w32ce_system" = yes)
if test "$have_android_system" = yes; then
AC_DEFINE(HAVE_ANDROID_SYSTEM,1, [Defined if we build for an Android system])
fi
AM_CONDITIONAL(HAVE_ANDROID_SYSTEM, test "$have_android_system" = yes)
# (These need to go after AC_PROG_CC so that $EXEEXT is defined)
AC_DEFINE_UNQUOTED(EXEEXT,"$EXEEXT",[The executable file extension, if any])
#
# Checks for libraries.
#
AC_MSG_NOTICE([checking for libraries])
#
# libgpg-error is a library with error codes shared between GnuPG
# related projects.
#
AM_PATH_GPG_ERROR("$NEED_GPG_ERROR_VERSION",
have_gpg_error=yes,have_gpg_error=no)
#
# Libgcrypt is our generic crypto library
#
AM_PATH_LIBGCRYPT("$NEED_LIBGCRYPT_API:$NEED_LIBGCRYPT_VERSION",
have_libgcrypt=yes,have_libgcrypt=no)
#
# libassuan is used for IPC
#
AM_PATH_LIBASSUAN("$NEED_LIBASSUAN_API:$NEED_LIBASSUAN_VERSION",
have_libassuan=yes,have_libassuan=no)
if test "$have_libassuan" = "yes"; then
AC_DEFINE_UNQUOTED(GNUPG_LIBASSUAN_VERSION, "$libassuan_version",
[version of the libassuan library])
show_tor_support="only .onion"
fi
#
# libksba is our X.509 support library
#
AM_PATH_KSBA("$NEED_KSBA_API:$NEED_KSBA_VERSION",have_ksba=yes,have_ksba=no)
#
# libusb allows us to use the integrated CCID smartcard reader driver.
#
# FiXME: Use GNUPG_CHECK_LIBUSB and modify to use separate AC_SUBSTs.
if test "$use_ccid_driver" = auto || test "$use_ccid_driver" = yes; then
case "${host}" in
*-mingw32*)
LIBUSB_NAME=
LIBUSB_LIBS=
LIBUSB_CPPFLAGS=
;;
*-*-darwin*)
LIBUSB_NAME=usb-1.0
LIBUSB_LIBS="-Wl,-framework,CoreFoundation -Wl,-framework,IOKit"
;;
*-*-freebsd*)
# FreeBSD has a native 1.0 compatible library by -lusb.
LIBUSB_NAME=usb
LIBUSB_LIBS=
;;
*)
LIBUSB_NAME=usb-1.0
LIBUSB_LIBS=
;;
esac
fi
if test x"$LIBUSB_NAME" != x ; then
AC_CHECK_LIB($LIBUSB_NAME, libusb_init,
[ LIBUSB_LIBS="-l$LIBUSB_NAME $LIBUSB_LIBS"
have_libusb=yes ])
AC_MSG_CHECKING([libusb include dir])
usb_incdir_found="no"
for _incdir in "" "/usr/include/libusb-1.0" \
"/usr/local/include/libusb-1.0" "/usr/pkg/include/libusb-1.0"; do
_libusb_save_cppflags=$CPPFLAGS
if test -n "${_incdir}"; then
CPPFLAGS="-I${_incdir} ${CPPFLAGS}"
fi
AC_PREPROC_IFELSE([AC_LANG_SOURCE([[@%:@include ]])],
[usb_incdir=${_incdir}; usb_incdir_found="yes"], [])
CPPFLAGS=${_libusb_save_cppflags}
if test "$usb_incdir_found" = "yes"; then
break
fi
done
if test "$usb_incdir_found" = "yes"; then
AC_MSG_RESULT([${usb_incdir}])
else
AC_MSG_RESULT([not found])
usb_incdir=""
have_libusb=no
if test "$use_ccid_driver" != yes; then
use_ccid_driver=no
fi
LIBUSB_LIBS=""
fi
if test "$have_libusb" = yes; then
AC_DEFINE(HAVE_LIBUSB,1, [defined if libusb is available])
fi
if test x"$usb_incdir" = x; then
LIBUSB_CPPFLAGS=""
else
LIBUSB_CPPFLAGS="-I${usb_incdir}"
fi
fi
AC_SUBST(LIBUSB_LIBS)
AC_SUBST(LIBUSB_CPPFLAGS)
#
# Check whether it is necessary to link against libdl.
# (For example to load libpcsclite)
#
gnupg_dlopen_save_libs="$LIBS"
LIBS=""
AC_SEARCH_LIBS(dlopen, c dl,,,)
DL_LIBS=$LIBS
AC_SUBST(DL_LIBS)
LIBS="$gnupg_dlopen_save_libs"
# Checks for g10
AC_ARG_ENABLE(sqlite,
AC_HELP_STRING([--disable-sqlite],
[disable the use of SQLITE]),
try_sqlite=$enableval, try_sqlite=yes)
if test x"$use_tofu" = xyes ; then
if test x"$try_sqlite" = xyes ; then
PKG_CHECK_MODULES([SQLITE3], [sqlite3 >= $NEED_SQLITE_VERSION],
[have_sqlite=yes],
[have_sqlite=no])
fi
if test "$have_sqlite" = "yes"; then
:
AC_SUBST([SQLITE3_CFLAGS])
AC_SUBST([SQLITE3_LIBS])
else
use_tofu=no
tmp=$(echo "$SQLITE3_PKG_ERRORS" | tr '\n' '\v' | sed 's/\v/\n*** /g')
AC_MSG_WARN([[
***
*** Building without SQLite support - TOFU disabled
***
*** $tmp]])
fi
fi
AM_CONDITIONAL(SQLITE3, test "$have_sqlite" = "yes")
if test x"$use_tofu" = xyes ; then
AC_DEFINE(USE_TOFU, 1, [Enable to build the TOFU code])
fi
# Checks for g13
AC_PATH_PROG(ENCFS, encfs, /usr/bin/encfs)
AC_DEFINE_UNQUOTED(ENCFS,
"${ENCFS}", [defines the filename of the encfs program])
AC_PATH_PROG(FUSERMOUNT, fusermount, /usr/bin/fusermount)
AC_DEFINE_UNQUOTED(FUSERMOUNT,
"${FUSERMOUNT}", [defines the filename of the fusermount program])
# Checks for dirmngr
#
# Checks for symcryptrun:
#
# libutil has openpty() and login_tty().
AC_CHECK_LIB(util, openpty,
[ LIBUTIL_LIBS="$LIBUTIL_LIBS -lutil"
AC_DEFINE(HAVE_LIBUTIL,1,
[defined if libutil is available])
])
AC_SUBST(LIBUTIL_LIBS)
# shred is used to clean temporary plain text files.
AC_PATH_PROG(SHRED, shred, /usr/bin/shred)
AC_DEFINE_UNQUOTED(SHRED,
"${SHRED}", [defines the filename of the shred program])
#
# Check whether the nPth library is available
#
AM_PATH_NPTH("$NEED_NPTH_API:$NEED_NPTH_VERSION",have_npth=yes,have_npth=no)
if test "$have_npth" = "yes"; then
AC_DEFINE(HAVE_NPTH, 1,
[Defined if the New Portable Thread Library is available])
AC_DEFINE(USE_NPTH, 1,
[Defined if support for nPth is requested and nPth is available])
else
AC_MSG_WARN([[
***
*** To support concurrent access for example in gpg-agent and the SCdaemon
*** we need the support of the New Portable Threads Library.
***]])
fi
#
# Enable debugging of nPth
#
AC_ARG_ENABLE(npth-debug,
AC_HELP_STRING([--enable-npth-debug],
[build with debug version of npth]),
[if test $enableval = yes ; then
AC_DEFINE(NPTH_ENABLE_DEBUG,1,
[Build with debug version of nPth])
fi])
#
# NTBTLS is our TLS library. If it is not available fallback to
# GNUTLS.
#
AC_ARG_ENABLE(ntbtls,
AC_HELP_STRING([--disable-ntbtls],
[disable the use of NTBTLS as TLS library]),
try_ntbtls=$enableval, try_ntbtls=yes)
if test x"$try_ntbtls" = xyes ; then
AM_PATH_NTBTLS("$NEED_NTBTLS_API:$NEED_NTBTLS_VERSION",
[have_ntbtls=yes],[have_ntbtls=no])
fi
if test "$have_ntbtls" = yes ; then
use_tls_library=ntbtls
AC_DEFINE(HTTP_USE_NTBTLS, 1, [Enable NTBTLS support in http.c])
else
AC_ARG_ENABLE(gnutls,
AC_HELP_STRING([--disable-gnutls],
[disable GNUTLS as fallback TLS library]),
try_gnutls=$enableval, try_gnutls=yes)
if test x"$try_gnutls" = xyes ; then
PKG_CHECK_MODULES([LIBGNUTLS], [gnutls >= $NEED_GNUTLS_VERSION],
[have_gnutls=yes],
[have_gnutls=no])
fi
if test "$have_gnutls" = "yes"; then
AC_SUBST([LIBGNUTLS_CFLAGS])
AC_SUBST([LIBGNUTLS_LIBS])
use_tls_library=gnutls
AC_DEFINE(HTTP_USE_GNUTLS, 1, [Enable GNUTLS support in http.c])
else
tmp=$(echo "$LIBGNUTLS_PKG_ERRORS" | tr '\n' '\v' | sed 's/\v/\n*** /g')
AC_MSG_WARN([[
***
*** Building without NTBTLS and GNUTLS - no TLS access to keyservers.
***
*** $tmp]])
fi
fi
#
# Allow to set a fixed trust store file for system provided certificates.
#
AC_ARG_WITH([default-trust-store-file],
[AC_HELP_STRING([--with-default-trust-store-file=FILE],
[Use FILE as system trust store])],
default_trust_store_file="$withval",
default_trust_store_file="")
if test x"$default_trust_store_file" = xno;then
default_trust_store_file=""
fi
if test x"$default_trust_store_file" != x ; then
AC_DEFINE_UNQUOTED([DEFAULT_TRUST_STORE_FILE],
["$default_trust_store_file"], [Use as default system trust store file])
fi
AC_MSG_NOTICE([checking for networking options])
#
# Must check for network library requirements before doing link tests
# for ldap, for example. If ldap libs are static (or dynamic and without
# ELF runtime link paths), then link will fail and LDAP support won't
# be detected.
#
AC_CHECK_FUNC(gethostbyname, , AC_CHECK_LIB(nsl, gethostbyname,
[NETLIBS="-lnsl $NETLIBS"]))
AC_CHECK_FUNC(setsockopt, , AC_CHECK_LIB(socket, setsockopt,
[NETLIBS="-lsocket $NETLIBS"]))
#
# Check standard resolver functions.
#
if test "$build_dirmngr" = "yes"; then
_dns_save_libs=$LIBS
LIBS=""
# Find the system resolver which can always be enabled with
# the dirmngr option --standard-resolver.
# the double underscore thing is a glibc-ism?
AC_SEARCH_LIBS(res_query,resolv bind,,
AC_SEARCH_LIBS(__res_query,resolv bind,,have_resolver=no))
AC_SEARCH_LIBS(dn_expand,resolv bind,,
AC_SEARCH_LIBS(__dn_expand,resolv bind,,have_resolver=no))
# macOS renames dn_skipname into res_9_dn_skipname in ,
# and for some reason fools us into believing we don't need
# -lresolv even if we do. Since the test program checking for the
# symbol does not include , we need to check for the
# renamed symbol explicitly.
AC_SEARCH_LIBS(res_9_dn_skipname,resolv bind,,
AC_SEARCH_LIBS(dn_skipname,resolv bind,,
AC_SEARCH_LIBS(__dn_skipname,resolv bind,,have_resolver=no)))
if test x"$have_resolver" != xno ; then
# Make sure that the BIND 4 resolver interface is workable before
# enabling any code that calls it. At some point I'll rewrite the
# code to use the BIND 8 resolver API.
# We might also want to use libdns instead.
AC_MSG_CHECKING([whether the resolver is usable])
AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include
#include
#include
#include ]],
[[unsigned char answer[PACKETSZ];
res_query("foo.bar",C_IN,T_A,answer,PACKETSZ);
dn_skipname(0,0);
dn_expand(0,0,0,0,0);
]])],have_resolver=yes,have_resolver=no)
AC_MSG_RESULT($have_resolver)
# This is Apple-specific and somewhat bizarre as they changed the
# define in bind 8 for some reason.
if test x"$have_resolver" != xyes ; then
AC_MSG_CHECKING(
[whether I can make the resolver usable with BIND_8_COMPAT])
AC_LINK_IFELSE([AC_LANG_PROGRAM([[#define BIND_8_COMPAT
#include
#include
#include
#include ]],
[[unsigned char answer[PACKETSZ];
res_query("foo.bar",C_IN,T_A,answer,PACKETSZ);
dn_skipname(0,0); dn_expand(0,0,0,0,0);
]])],[have_resolver=yes ; need_compat=yes])
AC_MSG_RESULT($have_resolver)
fi
fi
if test x"$have_resolver" = xyes ; then
AC_DEFINE(HAVE_SYSTEM_RESOLVER,1,[The system's resolver is usable.])
DNSLIBS="$DNSLIBS $LIBS"
if test x"$need_compat" = xyes ; then
AC_DEFINE(BIND_8_COMPAT,1,[an Apple OSXism])
fi
if test "$use_libdns" = yes; then
show_tor_support=yes
fi
elif test "$use_libdns" = yes; then
show_tor_support=yes
else
AC_MSG_WARN([[
***
*** The system's DNS resolver is not usable.
*** Dirmngr functionality is limited.
***]])
show_tor_support="${show_tor_support} (no system resolver)"
fi
if test "$have_w32_system" = yes; then
if test "$use_libdns" = yes; then
DNSLIBS="$DNSLIBS -liphlpapi"
fi
fi
LIBS=$_dns_save_libs
fi
AC_SUBST(DNSLIBS)
#
# Check for LDAP
#
# Note that running the check changes the variable
# gnupg_have_ldap from "n/a" to "no" or "yes".
AC_ARG_ENABLE(ldap,
AC_HELP_STRING([--disable-ldap],[disable LDAP support]),
[if test "$enableval" = "no"; then gnupg_have_ldap=no; fi])
if test "$gnupg_have_ldap" != "no" ; then
if test "$build_dirmngr" = "yes" ; then
GNUPG_CHECK_LDAP($NETLIBS)
AC_CHECK_LIB(lber, ber_free,
[ LBER_LIBS="$LBER_LIBS -llber"
AC_DEFINE(HAVE_LBER,1,
[defined if liblber is available])
have_lber=yes
])
fi
fi
AC_SUBST(LBER_LIBS)
if test "$gnupg_have_ldap" = "no"; then
AC_MSG_WARN([[
***
*** Building without LDAP support.
*** No CRL access or X.509 certificate search available.
***]])
fi
AM_CONDITIONAL(USE_LDAP, [test "$gnupg_have_ldap" = yes])
if test "$gnupg_have_ldap" = yes ; then
AC_DEFINE(USE_LDAP,1,[Defined if LDAP is support])
else
use_ldapwrapper=no
fi
if test "$use_ldapwrapper" = yes; then
AC_DEFINE(USE_LDAPWRAPPER,1, [Build dirmngr with LDAP wrapper process])
fi
AM_CONDITIONAL(USE_LDAPWRAPPER, test "$use_ldapwrapper" = yes)
#
# Check for sendmail
#
# This isn't necessarily sendmail itself, but anything that gives a
# sendmail-ish interface to the outside world. That includes Exim,
# Postfix, etc. Basically, anything that can handle "sendmail -t".
AC_ARG_WITH(mailprog,
AC_HELP_STRING([--with-mailprog=NAME],
[use "NAME -t" for mail transport]),
,with_mailprog=yes)
if test x"$with_mailprog" = xyes ; then
AC_PATH_PROG(SENDMAIL,sendmail,,$PATH:/usr/sbin:/usr/libexec:/usr/lib)
elif test x"$with_mailprog" != xno ; then
AC_MSG_CHECKING([for a mail transport program])
AC_SUBST(SENDMAIL,$with_mailprog)
AC_MSG_RESULT($with_mailprog)
fi
AC_DEFINE_UNQUOTED(NAME_OF_SENDMAIL,"$SENDMAIL",
[Tool with sendmail -t interface])
#
# Construct a printable name of the OS
#
case "${host}" in
*-mingw32ce*)
PRINTABLE_OS_NAME="W32CE"
;;
*-mingw32*)
PRINTABLE_OS_NAME="MingW32"
;;
*-*-cygwin*)
PRINTABLE_OS_NAME="Cygwin"
;;
i?86-emx-os2 | i?86-*-os2*emx )
PRINTABLE_OS_NAME="OS/2"
;;
i?86-*-msdosdjgpp*)
PRINTABLE_OS_NAME="MSDOS/DJGPP"
try_dynload=no
;;
*-linux*)
PRINTABLE_OS_NAME="GNU/Linux"
;;
*)
PRINTABLE_OS_NAME=`uname -s || echo "Unknown"`
;;
esac
AC_DEFINE_UNQUOTED(PRINTABLE_OS_NAME, "$PRINTABLE_OS_NAME",
[A human readable text with the name of the OS])
#
# Checking for iconv
#
if test "$require_iconv" = yes; then
AM_ICONV
else
LIBICONV=
LTLIBICONV=
AC_SUBST(LIBICONV)
AC_SUBST(LTLIBICONV)
fi
#
# Check for gettext
#
# This is "GNU gnupg" - The project-id script from gettext
# needs this string
#
AC_MSG_NOTICE([checking for gettext])
AM_PO_SUBDIRS
AM_GNU_GETTEXT_VERSION([0.17])
if test "$try_gettext" = yes; then
AM_GNU_GETTEXT([external],[need-ngettext])
# gettext requires some extra checks. These really should be part of
# the basic AM_GNU_GETTEXT macro. TODO: move other gettext-specific
# function checks to here.
AC_CHECK_FUNCS(strchr)
else
USE_NLS=no
USE_INCLUDED_LIBINTL=no
BUILD_INCLUDED_LIBINTL=no
POSUB=po
AC_SUBST(USE_NLS)
AC_SUBST(USE_INCLUDED_LIBINTL)
AC_SUBST(BUILD_INCLUDED_LIBINTL)
AC_SUBST(POSUB)
fi
# We use HAVE_LANGINFO_CODESET in a couple of places.
AM_LANGINFO_CODESET
# Checks required for our use of locales
gt_LC_MESSAGES
#
# SELinux support
#
if test "$selinux_support" = yes ; then
AC_DEFINE(ENABLE_SELINUX_HACKS,1,[Define to enable SELinux support])
fi
#
# Checks for header files.
#
AC_MSG_NOTICE([checking for header files])
AC_HEADER_STDC
AC_CHECK_HEADERS([string.h unistd.h langinfo.h termio.h locale.h getopt.h \
pty.h utmp.h pwd.h inttypes.h signal.h sys/select.h \
stdint.h signal.h util.h libutil.h termios.h \
ucred.h sys/ucred.h sys/sysmacros.h sys/mkdev.h])
AC_HEADER_TIME
#
# Checks for typedefs, structures, and compiler characteristics.
#
AC_MSG_NOTICE([checking for system characteristics])
AC_C_CONST
AC_C_INLINE
AC_C_VOLATILE
AC_TYPE_SIZE_T
AC_TYPE_MODE_T
AC_TYPE_SIGNAL
AC_DECL_SYS_SIGLIST
gl_HEADER_SYS_SOCKET
gl_TYPE_SOCKLEN_T
AC_SEARCH_LIBS([inet_addr], [nsl])
AC_ARG_ENABLE(endian-check,
AC_HELP_STRING([--disable-endian-check],
[disable the endian check and trust the OS provided macros]),
endiancheck=$enableval,endiancheck=yes)
if test x"$endiancheck" = xyes ; then
GNUPG_CHECK_ENDIAN
fi
# fixme: we should get rid of the byte type
GNUPG_CHECK_TYPEDEF(byte, HAVE_BYTE_TYPEDEF)
GNUPG_CHECK_TYPEDEF(ushort, HAVE_USHORT_TYPEDEF)
GNUPG_CHECK_TYPEDEF(ulong, HAVE_ULONG_TYPEDEF)
GNUPG_CHECK_TYPEDEF(u16, HAVE_U16_TYPEDEF)
GNUPG_CHECK_TYPEDEF(u32, HAVE_U32_TYPEDEF)
AC_CHECK_SIZEOF(unsigned short)
AC_CHECK_SIZEOF(unsigned int)
AC_CHECK_SIZEOF(unsigned long)
AC_CHECK_SIZEOF(unsigned long long)
AC_HEADER_TIME
AC_CHECK_SIZEOF(time_t,,[[
#include
#if TIME_WITH_SYS_TIME
# include
# include
#else
# if HAVE_SYS_TIME_H
# include
# else
# include
# endif
#endif
]])
GNUPG_TIME_T_UNSIGNED
if test "$ac_cv_sizeof_unsigned_short" = "0" \
|| test "$ac_cv_sizeof_unsigned_int" = "0" \
|| test "$ac_cv_sizeof_unsigned_long" = "0"; then
AC_MSG_WARN([Hmmm, something is wrong with the sizes - using defaults]);
fi
#
# Checks for library functions.
#
AC_MSG_NOTICE([checking for library functions])
AC_CHECK_DECLS(getpagesize)
AC_FUNC_FSEEKO
AC_FUNC_VPRINTF
AC_FUNC_FORK
AC_CHECK_FUNCS([atexit canonicalize_file_name clock_gettime ctermid \
explicit_bzero fcntl flockfile fsync ftello \
ftruncate funlockfile getaddrinfo getenv getpagesize \
getpwnam getpwuid getrlimit getrusage gettimeofday \
gmtime_r inet_ntop inet_pton isascii lstat memicmp \
memmove memrchr mmap nl_langinfo pipe raise rand \
setenv setlocale setrlimit sigaction sigprocmask \
stat stpcpy strcasecmp strerror strftime stricmp \
strlwr strncasecmp strpbrk strsep strtol strtoul \
strtoull tcgetattr timegm times ttyname unsetenv \
wait4 waitpid ])
# On some systems (e.g. Solaris) nanosleep requires linking to librl.
# Given that we use nanosleep only as an optimization over a select
# based wait function we want it only if it is available in libc.
_save_libs="$LIBS"
AC_SEARCH_LIBS([nanosleep], [],
[AC_DEFINE(HAVE_NANOSLEEP,1,
[Define to 1 if you have the `nanosleep' function in libc.])])
LIBS="$_save_libs"
# See whether libc supports the Linux inotify interface
case "${host}" in
*-*-linux*)
AC_CHECK_FUNCS([inotify_init])
;;
esac
if test "$have_android_system" = yes; then
# On Android ttyname is a stub but prints an error message.
AC_DEFINE(HAVE_BROKEN_TTYNAME,1,
[Defined if ttyname does not work properly])
fi
AC_CHECK_TYPES([struct sigaction, sigset_t],,,[#include ])
# Dirmngr requires mmap on Unix systems.
if test $ac_cv_func_mmap != yes -a $mmap_needed = yes; then
AC_MSG_ERROR([[Sorry, the current implementation requires mmap.]])
fi
#
# Check for the getsockopt SO_PEERCRED, etc.
#
AC_CHECK_MEMBERS([struct ucred.pid, struct ucred.cr_pid, struct sockpeercred.pid], [], [], [#include
#include ])
# (Open)Solaris
AC_CHECK_FUNCS([getpeerucred])
#
# W32 specific test
#
GNUPG_FUNC_MKDIR_TAKES_ONE_ARG
#
# Sanity check regex. Tests adapted from mutt.
#
AC_MSG_CHECKING([whether regular expression support is requested])
AC_ARG_ENABLE(regex,
AC_HELP_STRING([--disable-regex],
[do not handle regular expressions in trust signatures]),
use_regex=$enableval, use_regex=yes)
AC_MSG_RESULT($use_regex)
if test "$use_regex" = yes ; then
_cppflags="${CPPFLAGS}"
_ldflags="${LDFLAGS}"
AC_ARG_WITH(regex,
AC_HELP_STRING([--with-regex=DIR],[look for regex in DIR]),
[
if test -d "$withval" ; then
CPPFLAGS="${CPPFLAGS} -I$withval/include"
LDFLAGS="${LDFLAGS} -L$withval/lib"
fi
],withval="")
# Does the system have regex functions at all?
AC_SEARCH_LIBS([regcomp], [regex])
AC_CHECK_FUNC(regcomp, gnupg_cv_have_regex=yes, gnupg_cv_have_regex=no)
if test $gnupg_cv_have_regex = no; then
use_regex=no
else
if test x"$cross_compiling" = xyes; then
AC_MSG_WARN([cross compiling; assuming regexp libray is not broken])
else
AC_CACHE_CHECK([whether your system's regexp library is broken],
[gnupg_cv_regex_broken],
AC_TRY_RUN([
#include
#include
main() { regex_t blah ; regmatch_t p; p.rm_eo = p.rm_eo; return regcomp(&blah, "foo.*bar", REG_NOSUB) || regexec (&blah, "foobar", 0, NULL, 0); }],
gnupg_cv_regex_broken=no, gnupg_cv_regex_broken=yes, gnupg_cv_regex_broken=yes))
if test $gnupg_cv_regex_broken = yes; then
AC_MSG_WARN([your regex is broken - disabling regex use])
use_regex=no
fi
fi
fi
CPPFLAGS="${_cppflags}"
LDFLAGS="${_ldflags}"
fi
if test "$use_regex" != yes ; then
AC_DEFINE(DISABLE_REGEX,1, [Define to disable regular expression support])
fi
AM_CONDITIONAL(DISABLE_REGEX, test x"$use_regex" != xyes)
#
# Do we have zlib? Must do it here because Solaris failed
# when compiling a conftest (due to the "-lz" from LIBS).
# Note that we combine zlib and bzlib2 in ZLIBS.
#
if test "$use_zip" = yes ; then
_cppflags="${CPPFLAGS}"
_ldflags="${LDFLAGS}"
AC_ARG_WITH(zlib,
[ --with-zlib=DIR use libz in DIR],[
if test -d "$withval"; then
CPPFLAGS="${CPPFLAGS} -I$withval/include"
LDFLAGS="${LDFLAGS} -L$withval/lib"
fi
])
AC_CHECK_HEADER(zlib.h,
AC_CHECK_LIB(z, deflateInit2_,
[
ZLIBS="-lz"
AC_DEFINE(HAVE_ZIP,1, [Defined if ZIP and ZLIB are supported])
],
CPPFLAGS=${_cppflags} LDFLAGS=${_ldflags}),
CPPFLAGS=${_cppflags} LDFLAGS=${_ldflags})
fi
#
# Check whether we can support bzip2
#
if test "$use_bzip2" = yes ; then
_cppflags="${CPPFLAGS}"
_ldflags="${LDFLAGS}"
AC_ARG_WITH(bzip2,
AC_HELP_STRING([--with-bzip2=DIR],[look for bzip2 in DIR]),
[
if test -d "$withval" ; then
CPPFLAGS="${CPPFLAGS} -I$withval/include"
LDFLAGS="${LDFLAGS} -L$withval/lib"
fi
],withval="")
# Checking alongside stdio.h as an early version of bzip2 (1.0)
# required stdio.h to be included before bzlib.h, and Solaris 9 is
# woefully out of date.
if test "$withval" != no ; then
AC_CHECK_HEADER(bzlib.h,
AC_CHECK_LIB(bz2,BZ2_bzCompressInit,
[
have_bz2=yes
ZLIBS="$ZLIBS -lbz2"
AC_DEFINE(HAVE_BZIP2,1,
[Defined if the bz2 compression library is available])
],
CPPFLAGS=${_cppflags} LDFLAGS=${_ldflags}),
CPPFLAGS=${_cppflags} LDFLAGS=${_ldflags},[#include ])
fi
fi
AM_CONDITIONAL(ENABLE_BZIP2_SUPPORT,test x"$have_bz2" = "xyes")
AC_SUBST(ZLIBS)
# Check for readline support
GNUPG_CHECK_READLINE
if test "$development_version" = yes; then
AC_DEFINE(IS_DEVELOPMENT_VERSION,1,
[Defined if this is not a regular release])
fi
if test "$USE_MAINTAINER_MODE" = "yes"; then
AC_DEFINE(MAINTAINER_MODE,1,
[Defined if this build is in maintainer mode])
fi
AM_CONDITIONAL(CROSS_COMPILING, test x$cross_compiling = xyes)
GNUPG_CHECK_GNUMAKE
# Add some extra libs here so that previous tests don't fail for
# mysterious reasons - the final link step should bail out.
# W32SOCKLIBS is also defined so that if can be used for tools not
# requiring any network stuff but linking to code in libcommon which
# tracks in winsock stuff (e.g. init_common_subsystems).
if test "$have_w32_system" = yes; then
if test "$have_w32ce_system" = yes; then
W32SOCKLIBS="-lws2"
else
W32SOCKLIBS="-lws2_32"
fi
NETLIBS="${NETLIBS} ${W32SOCKLIBS}"
fi
AC_SUBST(NETLIBS)
AC_SUBST(W32SOCKLIBS)
#
# Setup gcc specific options
#
USE_C99_CFLAGS=
AC_MSG_NOTICE([checking for cc features])
if test "$GCC" = yes; then
mycflags=
mycflags_save=$CFLAGS
# Check whether gcc does not emit a diagnositc for unknown -Wno-*
# options. This is the case for gcc >= 4.6
AC_MSG_CHECKING([if gcc ignores unknown -Wno-* options])
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
#if __GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 6 )
#kickerror
#endif]],[])],[_gcc_silent_wno=yes],[_gcc_silent_wno=no])
AC_MSG_RESULT($_gcc_silent_wno)
# Note that it is okay to use CFLAGS here because these are just
# warning options and the user should have a chance of overriding
# them.
if test "$USE_MAINTAINER_MODE" = "yes"; then
mycflags="$mycflags -O3 -Wall -Wcast-align -Wshadow -Wstrict-prototypes"
mycflags="$mycflags -Wformat -Wno-format-y2k -Wformat-security"
if test x"$_gcc_silent_wno" = xyes ; then
_gcc_wopt=yes
else
AC_MSG_CHECKING([if gcc supports -Wno-missing-field-initializers])
CFLAGS="-Wno-missing-field-initializers"
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([],[])],
[_gcc_wopt=yes],[_gcc_wopt=no])
AC_MSG_RESULT($_gcc_wopt)
fi
if test x"$_gcc_wopt" = xyes ; then
- mycflags="$mycflags -W -Wno-sign-compare"
+ mycflags="$mycflags -W -Wno-sign-compare -Wno-format-zero-length"
mycflags="$mycflags -Wno-missing-field-initializers"
fi
AC_MSG_CHECKING([if gcc supports -Wdeclaration-after-statement])
CFLAGS="-Wdeclaration-after-statement"
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([],[])],_gcc_wopt=yes,_gcc_wopt=no)
AC_MSG_RESULT($_gcc_wopt)
if test x"$_gcc_wopt" = xyes ; then
mycflags="$mycflags -Wdeclaration-after-statement"
fi
AC_MSG_CHECKING([if gcc supports -Wlogical-op])
CFLAGS="-Wlogical-op -Werror"
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([],[])],_gcc_wopt=yes,_gcc_wopt=no)
AC_MSG_RESULT($_gcc_wopt)
if test x"$_gcc_wopt" = xyes ; then
mycflags="$mycflags -Wlogical-op"
fi
AC_MSG_CHECKING([if gcc supports -Wvla])
CFLAGS="-Wvla"
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([],[])],_gcc_wopt=yes,_gcc_wopt=no)
AC_MSG_RESULT($_gcc_wopt)
if test x"$_gcc_wopt" = xyes ; then
mycflags="$mycflags -Wvla"
fi
else
mycflags="$mycflags -Wall"
fi
if test x"$_gcc_silent_wno" = xyes ; then
_gcc_psign=yes
else
AC_MSG_CHECKING([if gcc supports -Wno-pointer-sign])
CFLAGS="-Wno-pointer-sign"
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([],[])],
[_gcc_psign=yes],[_gcc_psign=no])
AC_MSG_RESULT($_gcc_psign)
fi
if test x"$_gcc_psign" = xyes ; then
mycflags="$mycflags -Wno-pointer-sign"
fi
AC_MSG_CHECKING([if gcc supports -Wpointer-arith])
CFLAGS="-Wpointer-arith"
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([],[])],_gcc_psign=yes,_gcc_psign=no)
AC_MSG_RESULT($_gcc_psign)
if test x"$_gcc_psign" = xyes ; then
mycflags="$mycflags -Wpointer-arith"
fi
CFLAGS="$mycflags $mycflags_save"
if test "$use_libdns" = yes; then
# dirmngr/dns.{c,h} require C99 and GNU extensions. */
USE_C99_CFLAGS="-std=gnu99"
fi
fi
AC_SUBST(USE_C99_CFLAGS)
#
# This is handy for debugging so the compiler doesn't rearrange
# things and eliminate variables.
#
AC_ARG_ENABLE(optimization,
AC_HELP_STRING([--disable-optimization],
[disable compiler optimization]),
[if test $enableval = no ; then
CFLAGS=`echo $CFLAGS | sed s/-O[[1-9]]\ /-O0\ /g`
fi])
#
# Add -Werror to CFLAGS. This hack can be used to avoid problems with
# misbehaving autoconf tests in case the user supplied -Werror.
#
AC_ARG_ENABLE(werror,
AC_HELP_STRING([--enable-werror],
[append -Werror to CFLAGS]),
[if test $enableval = yes ; then
CFLAGS="$CFLAGS -Werror"
fi])
#
# Configure option --enable-all-tests
#
AC_MSG_CHECKING([whether "make check" shall run all tests])
AC_ARG_ENABLE(all-tests,
AC_HELP_STRING([--enable-all-tests],
[let "make check" run all tests]),
run_all_tests=$enableval, run_all_tests=no)
AC_MSG_RESULT($run_all_tests)
if test "$run_all_tests" = "yes"; then
AC_DEFINE(RUN_ALL_TESTS,1,
[Defined if "make check" shall run all tests])
fi
#
# We do not want support for the GNUPG_BUILDDIR environment variable
# in a released version. However, our regression tests suite requires
# this and thus we build with support for it during "make distcheck".
# This configure option implements this along with the top Makefile's
# AM_DISTCHECK_CONFIGURE_FLAGS.
#
gnupg_builddir_envvar=no
AC_ARG_ENABLE(gnupg-builddir-envvar,,
gnupg_builddir_envvar=$enableval)
if test x"$gnupg_builddir_envvar" = x"yes"; then
AC_DEFINE(ENABLE_GNUPG_BUILDDIR_ENVVAR, 1,
[This is only used with "make distcheck"])
fi
#
# To avoid problems with systemd cleaning up the /run/user directory,
# this option will make GnuPG try to use /run/gnupg/user as socket dir
# before /run/user
#
AC_ARG_ENABLE(run-gnupg-user-socket,
AC_HELP_STRING([--enable-run-gnupg-user-socket],
[try /run/gnupg/user for sockets prior to /run/user]),
use_run_gnupg_user_socket=$enableval)
if test x"$use_run_gnupg_user_socket" = x"yes"; then
AC_DEFINE(USE_RUN_GNUPG_USER_SOCKET, 1,
[If defined try /run/gnupg/user before /run/user])
fi
#
# Decide what to build
#
build_scdaemon_extra=""
if test "$build_scdaemon" = "yes"; then
if test $have_libusb = no; then
build_scdaemon_extra="without internal CCID driver"
fi
if test -n "$build_scdaemon_extra"; then
build_scdaemon_extra="(${build_scdaemon_extra})"
fi
fi
#
# Set variables for use by automake makefiles.
#
AM_CONDITIONAL(BUILD_GPG, test "$build_gpg" = "yes")
AM_CONDITIONAL(BUILD_GPGSM, test "$build_gpgsm" = "yes")
AM_CONDITIONAL(BUILD_AGENT, test "$build_agent" = "yes")
AM_CONDITIONAL(BUILD_SCDAEMON, test "$build_scdaemon" = "yes")
AM_CONDITIONAL(BUILD_G13, test "$build_g13" = "yes")
AM_CONDITIONAL(BUILD_DIRMNGR, test "$build_dirmngr" = "yes")
AM_CONDITIONAL(BUILD_DOC, test "$build_doc" = "yes")
AM_CONDITIONAL(BUILD_SYMCRYPTRUN, test "$build_symcryptrun" = "yes")
AM_CONDITIONAL(BUILD_GPGTAR, test "$build_gpgtar" = "yes")
AM_CONDITIONAL(BUILD_WKS_TOOLS, test "$build_wks_tools" = "yes")
AM_CONDITIONAL(ENABLE_CARD_SUPPORT, test "$card_support" = yes)
AM_CONDITIONAL(NO_TRUST_MODELS, test "$use_trust_models" = no)
AM_CONDITIONAL(USE_TOFU, test "$use_tofu" = yes)
#
# Set some defines for use gpgconf.
#
if test "$build_gpg" = yes ; then
AC_DEFINE(BUILD_WITH_GPG,1,[Defined if GPG is to be build])
fi
if test "$build_gpgsm" = yes ; then
AC_DEFINE(BUILD_WITH_GPGSM,1,[Defined if GPGSM is to be build])
fi
if test "$build_agent" = yes ; then
AC_DEFINE(BUILD_WITH_AGENT,1,[Defined if GPG-AGENT is to be build])
fi
if test "$build_scdaemon" = yes ; then
AC_DEFINE(BUILD_WITH_SCDAEMON,1,[Defined if SCDAEMON is to be build])
fi
if test "$build_dirmngr" = yes ; then
AC_DEFINE(BUILD_WITH_DIRMNGR,1,[Defined if DIRMNGR is to be build])
fi
if test "$build_g13" = yes ; then
AC_DEFINE(BUILD_WITH_G13,1,[Defined if G13 is to be build])
fi
#
# Define Name strings
#
AC_DEFINE_UNQUOTED(GNUPG_NAME, "GnuPG", [The name of the project])
AC_DEFINE_UNQUOTED(GPG_NAME, "gpg", [The name of the OpenPGP tool])
AC_DEFINE_UNQUOTED(GPG_DISP_NAME, "GnuPG", [The displayed name of gpg])
AC_DEFINE_UNQUOTED(GPGSM_NAME, "gpgsm", [The name of the S/MIME tool])
AC_DEFINE_UNQUOTED(GPGSM_DISP_NAME, "GPGSM", [The displayed name of gpgsm])
AC_DEFINE_UNQUOTED(GPG_AGENT_NAME, "gpg-agent", [The name of the agent])
AC_DEFINE_UNQUOTED(GPG_AGENT_DISP_NAME, "GPG Agent",
[The displayed name of gpg-agent])
AC_DEFINE_UNQUOTED(SCDAEMON_NAME, "scdaemon", [The name of the scdaemon])
AC_DEFINE_UNQUOTED(SCDAEMON_DISP_NAME, "SCDaemon",
[The displayed name of scdaemon])
AC_DEFINE_UNQUOTED(DIRMNGR_NAME, "dirmngr", [The name of the dirmngr])
AC_DEFINE_UNQUOTED(DIRMNGR_DISP_NAME, "DirMngr",
[The displayed name of dirmngr])
AC_DEFINE_UNQUOTED(G13_NAME, "g13", [The name of the g13 tool])
AC_DEFINE_UNQUOTED(G13_DISP_NAME, "G13", [The displayed name of g13])
AC_DEFINE_UNQUOTED(GPGCONF_NAME, "gpgconf", [The name of the gpgconf tool])
AC_DEFINE_UNQUOTED(GPGCONF_DISP_NAME, "GPGConf",
[The displayed name of gpgconf])
AC_DEFINE_UNQUOTED(GPGTAR_NAME, "gpgtar", [The name of the gpgtar tool])
AC_DEFINE_UNQUOTED(GPG_AGENT_SOCK_NAME, "S.gpg-agent",
[The name of the agent socket])
AC_DEFINE_UNQUOTED(GPG_AGENT_EXTRA_SOCK_NAME, "S.gpg-agent.extra",
[The name of the agent socket for remote access])
AC_DEFINE_UNQUOTED(GPG_AGENT_BROWSER_SOCK_NAME, "S.gpg-agent.browser",
[The name of the agent socket for browsers])
AC_DEFINE_UNQUOTED(GPG_AGENT_SSH_SOCK_NAME, "S.gpg-agent.ssh",
[The name of the agent socket for ssh])
AC_DEFINE_UNQUOTED(DIRMNGR_INFO_NAME, "DIRMNGR_INFO",
[The name of the dirmngr info envvar])
AC_DEFINE_UNQUOTED(SCDAEMON_SOCK_NAME, "S.scdaemon",
[The name of the SCdaemon socket])
AC_DEFINE_UNQUOTED(DIRMNGR_SOCK_NAME, "S.dirmngr",
[The name of the dirmngr socket])
AC_DEFINE_UNQUOTED(DIRMNGR_DEFAULT_KEYSERVER,
"hkps://hkps.pool.sks-keyservers.net",
[The default keyserver for dirmngr to use, if none is explicitly given])
AC_DEFINE_UNQUOTED(GPGEXT_GPG, "gpg", [The standard binary file suffix])
if test "$have_w32_system" = yes; then
AC_DEFINE_UNQUOTED(GNUPG_REGISTRY_DIR, "Software\\\\GNU\\\\GnuPG",
[The directory part of the W32 registry keys])
fi
#
# Provide information about the build.
#
BUILD_REVISION="mym4_revision"
AC_SUBST(BUILD_REVISION)
AC_DEFINE_UNQUOTED(BUILD_REVISION, "$BUILD_REVISION",
[GIT commit id revision used to build this package])
changequote(,)dnl
BUILD_VERSION=`echo "$VERSION" | sed 's/\([0-9.]*\).*/\1./'`
changequote([,])dnl
BUILD_VERSION="${BUILD_VERSION}mym4_revision_dec"
BUILD_FILEVERSION=`echo "${BUILD_VERSION}" | tr . ,`
AC_SUBST(BUILD_VERSION)
AC_SUBST(BUILD_FILEVERSION)
AC_ARG_ENABLE([build-timestamp],
AC_HELP_STRING([--enable-build-timestamp],
[set an explicit build timestamp for reproducibility.
(default is the current time in ISO-8601 format)]),
[if test "$enableval" = "yes"; then
BUILD_TIMESTAMP=`date -u +%Y-%m-%dT%H:%M+0000 2>/dev/null || date`
else
BUILD_TIMESTAMP="$enableval"
fi
BUILD_HOSTNAME="$ac_hostname"],
[BUILD_TIMESTAMP=""
BUILD_HOSTNAME=""])
AC_SUBST(BUILD_TIMESTAMP)
AC_DEFINE_UNQUOTED(BUILD_TIMESTAMP, "$BUILD_TIMESTAMP",
[The time this package was configured for a build])
AC_SUBST(BUILD_HOSTNAME)
#
# Print errors here so that they are visible all
# together and the user can acquire them all together.
#
die=no
if test "$have_gpg_error" = "no"; then
die=yes
AC_MSG_NOTICE([[
***
*** You need libgpg-error to build this program.
** This library is for example available at
*** https://gnupg.org/ftp/gcrypt/libgpg-error
*** (at least version $NEED_GPG_ERROR_VERSION is required.)
***]])
fi
if test "$have_libgcrypt" = "no"; then
die=yes
AC_MSG_NOTICE([[
***
*** You need libgcrypt to build this program.
** This library is for example available at
*** https://gnupg.org/ftp/gcrypt/libgcrypt/
*** (at least version $NEED_LIBGCRYPT_VERSION (API $NEED_LIBGCRYPT_API) is required.)
***]])
fi
if test "$have_libassuan" = "no"; then
die=yes
AC_MSG_NOTICE([[
***
*** You need libassuan to build this program.
*** This library is for example available at
*** https://gnupg.org/ftp/gcrypt/libassuan/
*** (at least version $NEED_LIBASSUAN_VERSION (API $NEED_LIBASSUAN_API) is required).
***]])
fi
if test "$have_ksba" = "no"; then
die=yes
AC_MSG_NOTICE([[
***
*** You need libksba to build this program.
*** This library is for example available at
*** https://gnupg.org/ftp/gcrypt/libksba/
*** (at least version $NEED_KSBA_VERSION using API $NEED_KSBA_API is required).
***]])
fi
if test "$gnupg_have_ldap" = yes; then
if test "$have_w32ce_system" = yes; then
AC_MSG_NOTICE([[
*** Note that CeGCC might be broken, a package fixing this is:
*** http://files.kolab.org/local/windows-ce/
*** source/wldap32_0.1-mingw32ce.orig.tar.gz
*** binary/wldap32-ce-arm-dev_0.1-1_all.deb
***]])
fi
fi
if test "$have_npth" = "no"; then
die=yes
AC_MSG_NOTICE([[
***
*** It is now required to build with support for the
*** New Portable Threads Library (nPth). Please install this
*** library first. The library is for example available at
*** https://gnupg.org/ftp/gcrypt/npth/
*** (at least version $NEED_NPTH_VERSION (API $NEED_NPTH_API) is required).
***]])
fi
if test "$require_iconv" = yes; then
if test "$am_func_iconv" != yes; then
die=yes
AC_MSG_NOTICE([[
***
*** The system does not provide a working iconv function. Please
*** install a suitable library; for example GNU Libiconv which is
*** available at:
*** https://ftp.gnu.org/gnu/libiconv/
***]])
fi
fi
if test "$use_ccid_driver" = yes; then
if test "$have_libusb" != yes; then
die=yes
AC_MSG_NOTICE([[
***
*** You need libusb to build the internal ccid driver. Please
*** install a libusb suitable for your system.
***]])
fi
fi
if test "$die" = "yes"; then
AC_MSG_ERROR([[
***
*** Required libraries not found. Please consult the above messages
*** and install them before running configure again.
***]])
fi
AC_CONFIG_FILES([ m4/Makefile
Makefile
po/Makefile.in
common/Makefile
common/w32info-rc.h
kbx/Makefile
g10/Makefile
sm/Makefile
agent/Makefile
scd/Makefile
g13/Makefile
dirmngr/Makefile
tools/gpg-zip
tools/Makefile
doc/Makefile
tests/Makefile
tests/gpgscm/Makefile
tests/openpgp/Makefile
tests/migrations/Makefile
tests/gpgsm/Makefile
tests/gpgme/Makefile
tests/pkits/Makefile
g10/gpg.w32-manifest
])
AC_OUTPUT
echo "
GnuPG v${VERSION} has been configured as follows:
Revision: mym4_revision (mym4_revision_dec)
Platform: $PRINTABLE_OS_NAME ($host)
OpenPGP: $build_gpg
S/MIME: $build_gpgsm
Agent: $build_agent
Smartcard: $build_scdaemon $build_scdaemon_extra
G13: $build_g13
Dirmngr: $build_dirmngr
Gpgtar: $build_gpgtar
WKS tools: $build_wks_tools
Protect tool: $show_gnupg_protect_tool_pgm
LDAP wrapper: $show_gnupg_dirmngr_ldap_pgm
Default agent: $show_gnupg_agent_pgm
Default pinentry: $show_gnupg_pinentry_pgm
Default scdaemon: $show_gnupg_scdaemon_pgm
Default dirmngr: $show_gnupg_dirmngr_pgm
Dirmngr auto start: $dirmngr_auto_start
Readline support: $gnupg_cv_have_readline
LDAP support: $gnupg_have_ldap
TLS support: $use_tls_library
TOFU support: $use_tofu
Tor support: $show_tor_support
"
if test x"$use_regex" != xyes ; then
echo "
Warning: No regular expression support available.
OpenPGP trust signatures won't work.
gpg-check-pattern will not be built.
"
fi
if test "x${gpg_config_script_warn}" != x; then
cat <.
*/
/*
1. To keep track of the CRLs actually cached and to store the meta
information of the CRLs a simple record oriented text file is
used. Fields in the file are colon (':') separated and values
containing colons or linefeeds are percent escaped (e.g. a colon
itself is represented as "%3A").
The first field is a record type identifier, so that the file is
useful to keep track of other meta data too.
The name of the file is "DIR.txt".
1.1. Comment record
Field 1: Constant beginning with "#".
Other fields are not defined and such a record is simply
skipped during processing.
1.2. Version record
Field 1: Constant "v"
Field 2: Version number of this file. Must be 1.
This record must be the first non-comment record and
there shall only exist one record of this type.
1.3. CRL cache record
Field 1: Constant "c", "u" or "i".
A "c" or "u" indicate a valid cache entry, however
"u" requires that a user root certificate check needs
to be done.
An "i" indicates an invalid cache entry which should
not be used but still exists so that it can be
updated at NEXT_UPDATE.
Field 2: Hexadecimal encoded SHA-1 hash of the issuer DN using
uppercase letters.
Field 3: Issuer DN in RFC-2253 notation.
Field 4: URL used to retrieve the corresponding CRL.
Field 5: 15 character ISO timestamp with THIS_UPDATE.
Field 6: 15 character ISO timestamp with NEXT_UPDATE.
Field 7: Hexadecimal encoded MD-5 hash of the DB file to detect
accidental modified (i.e. deleted and created) cache files.
Field 8: optional CRL number as a hex string.
Field 9: AuthorityKeyID.issuer, each Name separated by 0x01
Field 10: AuthorityKeyID.serial
Field 11: Hex fingerprint of trust anchor if field 1 is 'u'.
2. Layout of the standard CRL Cache DB file:
We use records of variable length with this structure
n bytes Serialnumber (binary) used as key
thus there is no need to store the length explicitly with DB2.
1 byte Reason for revocation
(currently the KSBA reason flags are used)
15 bytes ISO date of revocation (e.g. 19980815T142000)
Note that there is no terminating 0 stored.
The filename used is the hexadecimal (using uppercase letters)
SHA-1 hash value of the issuer DN prefixed with a "crl-" and
suffixed with a ".db". Thus the length of the filename is 47.
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#ifndef HAVE_W32_SYSTEM
#include
#endif
#ifdef MKDIR_TAKES_ONE_ARG
#undef mkdir
#define mkdir(a,b) mkdir(a)
#endif
#include "dirmngr.h"
#include "validate.h"
#include "certcache.h"
#include "crlcache.h"
#include "crlfetch.h"
#include "misc.h"
#include "cdb.h"
/* Change this whenever the format changes */
#define DBDIR_D "crls.d"
#define DBDIRFILE "DIR.txt"
#define DBDIRVERSION 1
/* The number of DB files we may have open at one time. We need to
limit this because there is no guarantee that the number of issuers
has a upper limit. We are currently using mmap, so it is a good
idea anyway to limit the number of opened cache files. */
#define MAX_OPEN_DB_FILES 5
#ifndef O_BINARY
# define O_BINARY 0
#endif
static const char oidstr_crlNumber[] = "2.5.29.20";
/* static const char oidstr_issuingDistributionPoint[] = "2.5.29.28"; */
static const char oidstr_authorityKeyIdentifier[] = "2.5.29.35";
/* Definition of one cached item. */
struct crl_cache_entry_s
{
struct crl_cache_entry_s *next;
int deleted; /* True if marked for deletion. */
int mark; /* Internally used by update_dir. */
unsigned int lineno;/* A 0 indicates a new entry. */
char *release_ptr; /* The actual allocated memory. */
char *url; /* Points into RELEASE_PTR. */
char *issuer; /* Ditto. */
char *issuer_hash; /* Ditto. */
char *dbfile_hash; /* MD5 sum of the cache file, points into RELEASE_PTR.*/
int invalid; /* Can't use this CRL. */
int user_trust_req; /* User supplied root certificate required. */
char *check_trust_anchor; /* Malloced fingerprint. */
ksba_isotime_t this_update;
ksba_isotime_t next_update;
ksba_isotime_t last_refresh; /* Use for the force_crl_refresh feature. */
char *crl_number;
char *authority_issuer;
char *authority_serialno;
struct cdb *cdb; /* The cache file handle or NULL if not open. */
unsigned int cdb_use_count; /* Current use count. */
unsigned int cdb_lru_count; /* Used for LRU purposes. */
int dbfile_checked; /* Set to true if the dbfile_hash value has
been checked one. */
};
/* Definition of the entire cache object. */
struct crl_cache_s
{
crl_cache_entry_t entries;
};
typedef struct crl_cache_s *crl_cache_t;
/* Prototypes. */
static crl_cache_entry_t find_entry (crl_cache_entry_t first,
const char *issuer_hash);
/* The currently loaded cache object. This is usually initialized
right at startup. */
static crl_cache_t current_cache;
/* Return the current cache object or bail out if it is has not yet
been initialized. */
static crl_cache_t
get_current_cache (void)
{
if (!current_cache)
log_fatal ("CRL cache has not yet been initialized\n");
return current_cache;
}
/*
Create ae directory if it does not yet exists. Returns on
success, or -1 on error.
*/
static int
create_directory_if_needed (const char *name)
{
DIR *dir;
char *fname;
fname = make_filename (opt.homedir_cache, name, NULL);
dir = opendir (fname);
if (!dir)
{
log_info (_("creating directory '%s'\n"), fname);
if (mkdir (fname, S_IRUSR|S_IWUSR|S_IXUSR) )
{
int save_errno = errno;
log_error (_("error creating directory '%s': %s\n"),
fname, strerror (errno));
xfree (fname);
gpg_err_set_errno (save_errno);
return -1;
}
}
else
closedir (dir);
xfree (fname);
return 0;
}
/* Remove all files from the cache directory. If FORCE is not true,
some sanity checks on the filenames are done. Return 0 if
everything went fine. */
static int
cleanup_cache_dir (int force)
{
char *dname = make_filename (opt.homedir_cache, DBDIR_D, NULL);
DIR *dir;
struct dirent *de;
int problem = 0;
if (!force)
{ /* Very minor sanity checks. */
if (!strcmp (dname, "~/") || !strcmp (dname, "/" ))
{
log_error (_("ignoring database dir '%s'\n"), dname);
xfree (dname);
return -1;
}
}
dir = opendir (dname);
if (!dir)
{
log_error (_("error reading directory '%s': %s\n"),
dname, strerror (errno));
xfree (dname);
return -1;
}
while ((de = readdir (dir)))
{
if (strcmp (de->d_name, "." ) && strcmp (de->d_name, ".."))
{
char *cdbname = make_filename (dname, de->d_name, NULL);
int okay;
struct stat sbuf;
if (force)
okay = 1;
else
okay = (!stat (cdbname, &sbuf) && S_ISREG (sbuf.st_mode));
if (okay)
{
log_info (_("removing cache file '%s'\n"), cdbname);
if (gnupg_remove (cdbname))
{
log_error ("failed to remove '%s': %s\n",
cdbname, strerror (errno));
problem = -1;
}
}
else
log_info (_("not removing file '%s'\n"), cdbname);
xfree (cdbname);
}
}
xfree (dname);
closedir (dir);
return problem;
}
/* Read the next line from the file FP and return the line in an
malloced buffer. Return NULL on error or EOF. There is no
limitation os the line length. The trailing linefeed has been
removed, the function will read the last line of a file, even if
that is not terminated by a LF. */
static char *
next_line_from_file (estream_t fp, gpg_error_t *r_err)
{
char buf[300];
char *largebuf = NULL;
size_t buflen;
size_t len = 0;
unsigned char *p;
int c;
char *tmpbuf;
*r_err = 0;
p = buf;
buflen = sizeof buf - 1;
while ((c=es_getc (fp)) != EOF && c != '\n')
{
if (len >= buflen)
{
if (!largebuf)
{
buflen += 1024;
largebuf = xtrymalloc ( buflen + 1 );
if (!largebuf)
{
*r_err = gpg_error_from_syserror ();
return NULL;
}
memcpy (largebuf, buf, len);
}
else
{
buflen += 1024;
tmpbuf = xtryrealloc (largebuf, buflen + 1);
if (!tmpbuf)
{
*r_err = gpg_error_from_syserror ();
xfree (largebuf);
return NULL;
}
largebuf = tmpbuf;
}
p = largebuf;
}
p[len++] = c;
}
if (c == EOF && !len)
return NULL;
p[len] = 0;
if (largebuf)
tmpbuf = xtryrealloc (largebuf, len+1);
else
tmpbuf = xtrystrdup (buf);
if (!tmpbuf)
{
*r_err = gpg_error_from_syserror ();
xfree (largebuf);
}
return tmpbuf;
}
/* Release one cache entry. */
static void
release_one_cache_entry (crl_cache_entry_t entry)
{
if (entry)
{
if (entry->cdb)
{
int fd = cdb_fileno (entry->cdb);
cdb_free (entry->cdb);
xfree (entry->cdb);
if (close (fd))
log_error (_("error closing cache file: %s\n"), strerror(errno));
}
xfree (entry->release_ptr);
xfree (entry->check_trust_anchor);
xfree (entry);
}
}
/* Release the CACHE object. */
static void
release_cache (crl_cache_t cache)
{
crl_cache_entry_t entry, entry2;
if (!cache)
return;
for (entry = cache->entries; entry; entry = entry2)
{
entry2 = entry->next;
release_one_cache_entry (entry);
}
cache->entries = NULL;
xfree (cache);
}
/* Open the dir file FNAME or create a new one if it does not yet
exist. */
static estream_t
open_dir_file (const char *fname)
{
estream_t fp;
fp = es_fopen (fname, "r");
if (!fp)
{
log_error (_("failed to open cache dir file '%s': %s\n"),
fname, strerror (errno));
/* Make sure that the directory exists, try to create if otherwise. */
if (create_directory_if_needed (NULL)
|| create_directory_if_needed (DBDIR_D))
return NULL;
fp = es_fopen (fname, "w");
if (!fp)
{
log_error (_("error creating new cache dir file '%s': %s\n"),
fname, strerror (errno));
return NULL;
}
es_fprintf (fp, "v:%d:\n", DBDIRVERSION);
if (es_ferror (fp))
{
log_error (_("error writing new cache dir file '%s': %s\n"),
fname, strerror (errno));
es_fclose (fp);
return NULL;
}
if (es_fclose (fp))
{
log_error (_("error closing new cache dir file '%s': %s\n"),
fname, strerror (errno));
return NULL;
}
log_info (_("new cache dir file '%s' created\n"), fname);
fp = es_fopen (fname, "r");
if (!fp)
{
log_error (_("failed to re-open cache dir file '%s': %s\n"),
fname, strerror (errno));
return NULL;
}
}
return fp;
}
/* Helper for open_dir. */
static gpg_error_t
check_dir_version (estream_t *fpadr, const char *fname,
unsigned int *lineno,
int cleanup_on_mismatch)
{
char *line;
gpg_error_t lineerr = 0;
estream_t fp = *fpadr;
int created = 0;
retry:
while ((line = next_line_from_file (fp, &lineerr)))
{
++*lineno;
if (*line == 'v' && line[1] == ':')
break;
else if (*line != '#')
{
log_error (_("first record of '%s' is not the version\n"), fname);
xfree (line);
return gpg_error (GPG_ERR_CONFIGURATION);
}
xfree (line);
}
if (lineerr)
return lineerr;
/* The !line catches the case of an empty DIR file. We handle this
the same as a non-matching version. */
if (!line || strtol (line+2, NULL, 10) != DBDIRVERSION)
{
if (!created && cleanup_on_mismatch)
{
log_error (_("old version of cache directory - cleaning up\n"));
es_fclose (fp);
*fpadr = NULL;
if (!cleanup_cache_dir (1))
{
*lineno = 0;
fp = *fpadr = open_dir_file (fname);
if (!fp)
{
xfree (line);
return gpg_error (GPG_ERR_CONFIGURATION);
}
created = 1;
goto retry;
}
}
log_error (_("old version of cache directory - giving up\n"));
xfree (line);
return gpg_error (GPG_ERR_CONFIGURATION);
}
xfree (line);
return 0;
}
/* Open the dir file and read in all available information. Store
that in a newly allocated cache object and return that if
everything worked out fine. Create the cache directory and the dir
if it does not yet exist. Remove all files in that directory if
the version does not match. */
static gpg_error_t
open_dir (crl_cache_t *r_cache)
{
crl_cache_t cache;
char *fname;
char *line = NULL;
gpg_error_t lineerr = 0;
estream_t fp;
crl_cache_entry_t entry, *entrytail;
unsigned int lineno;
gpg_error_t err = 0;
int anyerr = 0;
cache = xtrycalloc (1, sizeof *cache);
if (!cache)
return gpg_error_from_syserror ();
fname = make_filename (opt.homedir_cache, DBDIR_D, DBDIRFILE, NULL);
lineno = 0;
fp = open_dir_file (fname);
if (!fp)
{
err = gpg_error (GPG_ERR_CONFIGURATION);
goto leave;
}
err = check_dir_version (&fp, fname, &lineno, 1);
if (err)
goto leave;
/* Read in all supported entries from the dir file. */
cache->entries = NULL;
entrytail = &cache->entries;
xfree (line);
while ((line = next_line_from_file (fp, &lineerr)))
{
int fieldno;
char *p, *endp;
lineno++;
if ( *line == 'c' || *line == 'u' || *line == 'i' )
{
entry = xtrycalloc (1, sizeof *entry);
if (!entry)
{
err = gpg_error_from_syserror ();
goto leave;
}
entry->lineno = lineno;
entry->release_ptr = line;
if (*line == 'i')
{
entry->invalid = atoi (line+1);
if (entry->invalid < 1)
entry->invalid = 1;
}
else if (*line == 'u')
entry->user_trust_req = 1;
for (fieldno=1, p = line; p; p = endp, fieldno++)
{
endp = strchr (p, ':');
if (endp)
*endp++ = '\0';
switch (fieldno)
{
case 1: /* record type */ break;
case 2: entry->issuer_hash = p; break;
case 3: entry->issuer = unpercent_string (p); break;
case 4: entry->url = unpercent_string (p); break;
case 5:
strncpy (entry->this_update, p, 15);
entry->this_update[15] = 0;
break;
case 6:
strncpy (entry->next_update, p, 15);
entry->next_update[15] = 0;
break;
case 7: entry->dbfile_hash = p; break;
case 8: if (*p) entry->crl_number = p; break;
case 9:
if (*p)
entry->authority_issuer = unpercent_string (p);
break;
case 10:
if (*p)
entry->authority_serialno = unpercent_string (p);
break;
case 11:
if (*p)
entry->check_trust_anchor = xtrystrdup (p);
break;
default:
if (*p)
log_info (_("extra field detected in crl record of "
"'%s' line %u\n"), fname, lineno);
break;
}
}
if (!entry->issuer_hash)
{
log_info (_("invalid line detected in '%s' line %u\n"),
fname, lineno);
xfree (entry);
entry = NULL;
}
else if (find_entry (cache->entries, entry->issuer_hash))
{
/* Fixme: The duplicate checking used is not very
effective for large numbers of issuers. */
log_info (_("duplicate entry detected in '%s' line %u\n"),
fname, lineno);
xfree (entry);
entry = NULL;
}
else
{
line = NULL;
*entrytail = entry;
entrytail = &entry->next;
}
}
else if (*line == '#')
;
else
log_info (_("unsupported record type in '%s' line %u skipped\n"),
fname, lineno);
if (line)
xfree (line);
}
if (lineerr)
{
err = lineerr;
log_error (_("error reading '%s': %s\n"), fname, gpg_strerror (err));
goto leave;
}
if (es_ferror (fp))
{
log_error (_("error reading '%s': %s\n"), fname, strerror (errno));
err = gpg_error (GPG_ERR_CONFIGURATION);
goto leave;
}
/* Now do some basic checks on the data. */
for (entry = cache->entries; entry; entry = entry->next)
{
assert (entry->lineno);
if (strlen (entry->issuer_hash) != 40)
{
anyerr++;
log_error (_("invalid issuer hash in '%s' line %u\n"),
fname, entry->lineno);
}
else if ( !*entry->issuer )
{
anyerr++;
log_error (_("no issuer DN in '%s' line %u\n"),
fname, entry->lineno);
}
else if ( check_isotime (entry->this_update)
|| check_isotime (entry->next_update))
{
anyerr++;
log_error (_("invalid timestamp in '%s' line %u\n"),
fname, entry->lineno);
}
/* Checks not leading to an immediate fail. */
if (strlen (entry->dbfile_hash) != 32)
log_info (_("WARNING: invalid cache file hash in '%s' line %u\n"),
fname, entry->lineno);
}
if (anyerr)
{
log_error (_("detected errors in cache dir file\n"));
log_info (_("please check the reason and manually delete that file\n"));
err = gpg_error (GPG_ERR_CONFIGURATION);
}
leave:
es_fclose (fp);
xfree (line);
xfree (fname);
if (err)
{
release_cache (cache);
cache = NULL;
}
*r_cache = cache;
return err;
}
static void
write_percented_string (const char *s, estream_t fp)
{
for (; *s; s++)
if (*s == ':')
es_fputs ("%3A", fp);
else if (*s == '\n')
es_fputs ("%0A", fp);
else if (*s == '\r')
es_fputs ("%0D", fp);
else
es_putc (*s, fp);
}
static void
write_dir_line_crl (estream_t fp, crl_cache_entry_t e)
{
if (e->invalid)
es_fprintf (fp, "i%d", e->invalid);
else if (e->user_trust_req)
es_putc ('u', fp);
else
es_putc ('c', fp);
es_putc (':', fp);
es_fputs (e->issuer_hash, fp);
es_putc (':', fp);
write_percented_string (e->issuer, fp);
es_putc (':', fp);
write_percented_string (e->url, fp);
es_putc (':', fp);
es_fwrite (e->this_update, 15, 1, fp);
es_putc (':', fp);
es_fwrite (e->next_update, 15, 1, fp);
es_putc (':', fp);
es_fputs (e->dbfile_hash, fp);
es_putc (':', fp);
if (e->crl_number)
es_fputs (e->crl_number, fp);
es_putc (':', fp);
if (e->authority_issuer)
write_percented_string (e->authority_issuer, fp);
es_putc (':', fp);
if (e->authority_serialno)
es_fputs (e->authority_serialno, fp);
es_putc (':', fp);
if (e->check_trust_anchor && e->user_trust_req)
es_fputs (e->check_trust_anchor, fp);
es_putc ('\n', fp);
}
/* Update the current dir file using the cache. */
static gpg_error_t
update_dir (crl_cache_t cache)
{
char *fname = NULL;
char *tmpfname = NULL;
char *line = NULL;
gpg_error_t lineerr = 0;
estream_t fp;
estream_t fpout = NULL;
crl_cache_entry_t e;
unsigned int lineno;
gpg_error_t err = 0;
fname = make_filename (opt.homedir_cache, DBDIR_D, DBDIRFILE, NULL);
/* Fixme: Take an update file lock here. */
for (e= cache->entries; e; e = e->next)
e->mark = 1;
lineno = 0;
fp = es_fopen (fname, "r");
if (!fp)
{
err = gpg_error_from_errno (errno);
log_error (_("failed to open cache dir file '%s': %s\n"),
fname, strerror (errno));
goto leave;
}
err = check_dir_version (&fp, fname, &lineno, 0);
if (err)
goto leave;
es_rewind (fp);
lineno = 0;
/* Create a temporary DIR file. */
{
char *tmpbuf, *p;
const char *nodename;
#ifndef HAVE_W32_SYSTEM
struct utsname utsbuf;
#endif
#ifdef HAVE_W32_SYSTEM
nodename = "unknown";
#else
if (uname (&utsbuf))
nodename = "unknown";
else
nodename = utsbuf.nodename;
#endif
gpgrt_asprintf (&tmpbuf, "DIR-tmp-%s-%u-%p.txt.tmp",
nodename, (unsigned int)getpid (), &tmpbuf);
if (!tmpbuf)
{
err = gpg_error_from_errno (errno);
log_error (_("failed to create temporary cache dir file '%s': %s\n"),
tmpfname, strerror (errno));
goto leave;
}
for (p=tmpbuf; *p; p++)
if (*p == '/')
*p = '.';
tmpfname = make_filename (opt.homedir_cache, DBDIR_D, tmpbuf, NULL);
xfree (tmpbuf);
}
fpout = es_fopen (tmpfname, "w");
if (!fpout)
{
err = gpg_error_from_errno (errno);
log_error (_("failed to create temporary cache dir file '%s': %s\n"),
tmpfname, strerror (errno));
goto leave;
}
while ((line = next_line_from_file (fp, &lineerr)))
{
lineno++;
if (*line == 'c' || *line == 'u' || *line == 'i')
{
/* Extract the issuer hash field. */
char *fieldp, *endp;
fieldp = strchr (line, ':');
endp = fieldp? strchr (++fieldp, ':') : NULL;
if (endp)
{
/* There should be no percent within the issuer hash
field, thus we can compare it pretty easily. */
*endp = 0;
e = find_entry ( cache->entries, fieldp);
*endp = ':'; /* Restore original line. */
if (e && e->deleted)
{
/* Marked for deletion, so don't write it. */
e->mark = 0;
}
else if (e)
{
/* Yep, this is valid entry we know about; write it out */
write_dir_line_crl (fpout, e);
e->mark = 0;
}
else
{ /* We ignore entries we don't have in our cache
because they may have been added in the meantime
by other instances of dirmngr. */
es_fprintf (fpout, "# Next line added by "
"another process; our pid is %lu\n",
(unsigned long)getpid ());
es_fputs (line, fpout);
es_putc ('\n', fpout);
}
}
else
{
es_fputs ("# Invalid line detected: ", fpout);
es_fputs (line, fpout);
es_putc ('\n', fpout);
}
}
else
{
/* Write out all non CRL lines as they are. */
es_fputs (line, fpout);
es_putc ('\n', fpout);
}
xfree (line);
}
if (!es_ferror (fp) && !es_ferror (fpout) && !lineerr)
{
/* Write out the remaining entries. */
for (e= cache->entries; e; e = e->next)
if (e->mark)
{
if (!e->deleted)
write_dir_line_crl (fpout, e);
e->mark = 0;
}
}
if (lineerr)
{
err = lineerr;
log_error (_("error reading '%s': %s\n"), fname, gpg_strerror (err));
goto leave;
}
if (es_ferror (fp))
{
err = gpg_error_from_errno (errno);
log_error (_("error reading '%s': %s\n"), fname, strerror (errno));
}
if (es_ferror (fpout))
{
err = gpg_error_from_errno (errno);
log_error (_("error writing '%s': %s\n"), tmpfname, strerror (errno));
}
if (err)
goto leave;
/* Rename the files. */
es_fclose (fp);
fp = NULL;
if (es_fclose (fpout))
{
err = gpg_error_from_errno (errno);
log_error (_("error closing '%s': %s\n"), tmpfname, strerror (errno));
goto leave;
}
fpout = NULL;
#ifdef HAVE_W32_SYSTEM
/* No atomic mv on W32 systems. */
gnupg_remove (fname);
#endif
if (rename (tmpfname, fname))
{
err = gpg_error_from_errno (errno);
log_error (_("error renaming '%s' to '%s': %s\n"),
tmpfname, fname, strerror (errno));
goto leave;
}
leave:
/* Fixme: Relinquish update lock. */
xfree (line);
es_fclose (fp);
xfree (fname);
if (fpout)
{
es_fclose (fpout);
if (err && tmpfname)
gnupg_remove (tmpfname);
}
xfree (tmpfname);
return err;
}
/* Create the filename for the cache file from the 40 byte ISSUER_HASH
string. Caller must release the return string. */
static char *
make_db_file_name (const char *issuer_hash)
{
char bname[50];
assert (strlen (issuer_hash) == 40);
memcpy (bname, "crl-", 4);
memcpy (bname + 4, issuer_hash, 40);
strcpy (bname + 44, ".db");
return make_filename (opt.homedir_cache, DBDIR_D, bname, NULL);
}
/* Hash the file FNAME and return the MD5 digest in MD5BUFFER. The
caller must allocate MD%buffer wityh at least 16 bytes. Returns 0
on success. */
static int
hash_dbfile (const char *fname, unsigned char *md5buffer)
{
estream_t fp;
char *buffer;
size_t n;
gcry_md_hd_t md5;
gpg_error_t err;
buffer = xtrymalloc (65536);
fp = buffer? es_fopen (fname, "rb") : NULL;
if (!fp)
{
log_error (_("can't hash '%s': %s\n"), fname, strerror (errno));
xfree (buffer);
return -1;
}
err = gcry_md_open (&md5, GCRY_MD_MD5, 0);
if (err)
{
log_error (_("error setting up MD5 hash context: %s\n"),
gpg_strerror (err));
xfree (buffer);
es_fclose (fp);
return -1;
}
/* We better hash some information about the cache file layout in. */
sprintf (buffer, "%.100s/%.100s:%d", DBDIR_D, DBDIRFILE, DBDIRVERSION);
gcry_md_write (md5, buffer, strlen (buffer));
for (;;)
{
n = es_fread (buffer, 1, 65536, fp);
if (n < 65536 && es_ferror (fp))
{
log_error (_("error hashing '%s': %s\n"), fname, strerror (errno));
xfree (buffer);
es_fclose (fp);
gcry_md_close (md5);
return -1;
}
if (!n)
break;
gcry_md_write (md5, buffer, n);
}
es_fclose (fp);
xfree (buffer);
gcry_md_final (md5);
memcpy (md5buffer, gcry_md_read (md5, GCRY_MD_MD5), 16);
gcry_md_close (md5);
return 0;
}
/* Compare the file FNAME against the dexified MD5 hash MD5HASH and
return 0 if they match. */
static int
check_dbfile (const char *fname, const char *md5hexvalue)
{
unsigned char buffer1[16], buffer2[16];
if (strlen (md5hexvalue) != 32)
{
log_error (_("invalid formatted checksum for '%s'\n"), fname);
return -1;
}
unhexify (buffer1, md5hexvalue);
if (hash_dbfile (fname, buffer2))
return -1;
return memcmp (buffer1, buffer2, 16);
}
/* Open the cache file for ENTRY. This function implements a caching
strategy and might close unused cache files. It is required to use
unlock_db_file after using the file. */
static struct cdb *
lock_db_file (crl_cache_t cache, crl_cache_entry_t entry)
{
char *fname;
int fd;
int open_count;
crl_cache_entry_t e;
if (entry->cdb)
{
entry->cdb_use_count++;
return entry->cdb;
}
for (open_count = 0, e = cache->entries; e; e = e->next)
{
if (e->cdb)
open_count++;
/* log_debug ("CACHE: cdb=%p use_count=%u lru_count=%u\n", */
/* e->cdb,e->cdb_use_count,e->cdb_lru_count); */
}
/* If there are too many file open, find the least recent used DB
file and close it. Note that for Pth thread safeness we need to
use a loop here. */
while (open_count >= MAX_OPEN_DB_FILES )
{
crl_cache_entry_t last_e = NULL;
unsigned int last_lru = (unsigned int)(-1);
for (e = cache->entries; e; e = e->next)
if (e->cdb && !e->cdb_use_count && e->cdb_lru_count < last_lru)
{
last_lru = e->cdb_lru_count;
last_e = e;
}
if (!last_e)
{
log_error (_("too many open cache files; can't open anymore\n"));
return NULL;
}
/* log_debug ("CACHE: closing file at cdb=%p\n", last_e->cdb); */
fd = cdb_fileno (last_e->cdb);
cdb_free (last_e->cdb);
xfree (last_e->cdb);
last_e->cdb = NULL;
if (close (fd))
log_error (_("error closing cache file: %s\n"), strerror(errno));
open_count--;
}
fname = make_db_file_name (entry->issuer_hash);
if (opt.verbose)
log_info (_("opening cache file '%s'\n"), fname );
if (!entry->dbfile_checked)
{
if (!check_dbfile (fname, entry->dbfile_hash))
entry->dbfile_checked = 1;
/* Note, in case of an error we don't print an error here but
let require the caller to do that check. */
}
entry->cdb = xtrycalloc (1, sizeof *entry->cdb);
if (!entry->cdb)
{
xfree (fname);
return NULL;
}
fd = open (fname, O_RDONLY | O_BINARY);
if (fd == -1)
{
log_error (_("error opening cache file '%s': %s\n"),
fname, strerror (errno));
xfree (entry->cdb);
entry->cdb = NULL;
xfree (fname);
return NULL;
}
if (cdb_init (entry->cdb, fd))
{
log_error (_("error initializing cache file '%s' for reading: %s\n"),
fname, strerror (errno));
xfree (entry->cdb);
entry->cdb = NULL;
close (fd);
xfree (fname);
return NULL;
}
xfree (fname);
entry->cdb_use_count = 1;
entry->cdb_lru_count = 0;
return entry->cdb;
}
/* Unlock a cache file, so that it can be reused. */
static void
unlock_db_file (crl_cache_t cache, crl_cache_entry_t entry)
{
if (!entry->cdb)
log_error (_("calling unlock_db_file on a closed file\n"));
else if (!entry->cdb_use_count)
log_error (_("calling unlock_db_file on an unlocked file\n"));
else
{
entry->cdb_use_count--;
entry->cdb_lru_count++;
}
/* If the entry was marked for deletion in the meantime do it now.
We do this for the sake of Pth thread safeness. */
if (!entry->cdb_use_count && entry->deleted)
{
crl_cache_entry_t eprev, enext;
enext = entry->next;
for (eprev = cache->entries;
eprev && eprev->next != entry; eprev = eprev->next)
;
assert (eprev);
if (eprev == cache->entries)
cache->entries = enext;
else
eprev->next = enext;
/* FIXME: Do we leak ENTRY? */
}
}
/* Find ISSUER_HASH in our cache FIRST. This may be used to enumerate
the linked list we use to keep the CRLs of an issuer. */
static crl_cache_entry_t
find_entry (crl_cache_entry_t first, const char *issuer_hash)
{
while (first && (first->deleted || strcmp (issuer_hash, first->issuer_hash)))
first = first->next;
return first;
}
/* Create a new CRL cache. This function is usually called only once.
never fail. */
void
crl_cache_init(void)
{
crl_cache_t cache = NULL;
gpg_error_t err;
if (current_cache)
{
log_error ("crl cache has already been initialized - not doing twice\n");
return;
}
err = open_dir (&cache);
if (err)
log_fatal (_("failed to create a new cache object: %s\n"),
gpg_strerror (err));
current_cache = cache;
}
/* Remove the cache information and all its resources. Note that we
still keep the cache on disk. */
void
crl_cache_deinit (void)
{
if (current_cache)
{
release_cache (current_cache);
current_cache = NULL;
}
}
/* Delete the cache from disk and memory. Return 0 on success.*/
int
crl_cache_flush (void)
{
int rc;
crl_cache_deinit ();
rc = cleanup_cache_dir (0)? -1 : 0;
crl_cache_init ();
return rc;
}
/* Check whether the certificate identified by ISSUER_HASH and
SN/SNLEN is valid; i.e. not listed in our cache. With
FORCE_REFRESH set to true, a new CRL will be retrieved even if the
cache has not yet expired. We use a 30 minutes threshold here so
that invoking this function several times won't load the CRL over
and over. */
static crl_cache_result_t
cache_isvalid (ctrl_t ctrl, const char *issuer_hash,
const unsigned char *sn, size_t snlen,
int force_refresh)
{
crl_cache_t cache = get_current_cache ();
crl_cache_result_t retval;
struct cdb *cdb;
int rc;
crl_cache_entry_t entry;
gnupg_isotime_t current_time;
size_t n;
(void)ctrl;
entry = find_entry (cache->entries, issuer_hash);
if (!entry)
{
log_info (_("no CRL available for issuer id %s\n"), issuer_hash );
return CRL_CACHE_DONTKNOW;
}
gnupg_get_isotime (current_time);
if (strcmp (entry->next_update, current_time) < 0 )
{
log_info (_("cached CRL for issuer id %s too old; update required\n"),
issuer_hash);
return CRL_CACHE_DONTKNOW;
}
if (force_refresh)
{
gnupg_isotime_t tmptime;
if (*entry->last_refresh)
{
gnupg_copy_time (tmptime, entry->last_refresh);
add_seconds_to_isotime (tmptime, 30 * 60);
if (strcmp (tmptime, current_time) < 0 )
{
log_info (_("force-crl-refresh active and %d minutes passed for"
" issuer id %s; update required\n"),
30, issuer_hash);
return CRL_CACHE_DONTKNOW;
}
}
else
{
log_info (_("force-crl-refresh active for"
" issuer id %s; update required\n"),
issuer_hash);
return CRL_CACHE_DONTKNOW;
}
}
if (entry->invalid)
{
log_info (_("available CRL for issuer ID %s can't be used\n"),
issuer_hash);
return CRL_CACHE_CANTUSE;
}
cdb = lock_db_file (cache, entry);
if (!cdb)
return CRL_CACHE_DONTKNOW; /* Hmmm, not the best error code. */
if (!entry->dbfile_checked)
{
log_error (_("cached CRL for issuer id %s tampered; we need to update\n")
, issuer_hash);
unlock_db_file (cache, entry);
return CRL_CACHE_DONTKNOW;
}
rc = cdb_find (cdb, sn, snlen);
if (rc == 1)
{
n = cdb_datalen (cdb);
if (n != 16)
{
log_error (_("WARNING: invalid cache record length for S/N "));
log_printf ("0x");
- log_printhex ("", sn, snlen);
+ log_printhex (sn, snlen, "");
}
else if (opt.verbose)
{
unsigned char record[16];
char *tmp = hexify_data (sn, snlen, 1);
if (cdb_read (cdb, record, n, cdb_datapos (cdb)))
log_error (_("problem reading cache record for S/N %s: %s\n"),
tmp, strerror (errno));
else
log_info (_("S/N %s is not valid; reason=%02X date=%.15s\n"),
tmp, *record, record+1);
xfree (tmp);
}
retval = CRL_CACHE_INVALID;
}
else if (!rc)
{
if (opt.verbose)
{
char *serialno = hexify_data (sn, snlen, 1);
log_info (_("S/N %s is valid, it is not listed in the CRL\n"),
serialno );
xfree (serialno);
}
retval = CRL_CACHE_VALID;
}
else
{
log_error (_("error getting data from cache file: %s\n"),
strerror (errno));
retval = CRL_CACHE_DONTKNOW;
}
if (entry->user_trust_req
&& (retval == CRL_CACHE_VALID || retval == CRL_CACHE_INVALID))
{
if (!entry->check_trust_anchor)
{
log_error ("inconsistent data on user trust check\n");
retval = CRL_CACHE_CANTUSE;
}
else if (get_istrusted_from_client (ctrl, entry->check_trust_anchor))
{
if (opt.verbose)
log_info ("no system trust and client does not trust either\n");
retval = CRL_CACHE_CANTUSE;
}
else
{
/* Okay, the CRL is considered valid by the client and thus
we can return the result as is. */
}
}
unlock_db_file (cache, entry);
return retval;
}
/* Check whether the certificate identified by ISSUER_HASH and
SERIALNO is valid; i.e. not listed in our cache. With
FORCE_REFRESH set to true, a new CRL will be retrieved even if the
cache has not yet expired. We use a 30 minutes threshold here so
that invoking this function several times won't load the CRL over
and over. */
crl_cache_result_t
crl_cache_isvalid (ctrl_t ctrl, const char *issuer_hash, const char *serialno,
int force_refresh)
{
crl_cache_result_t result;
unsigned char snbuf_buffer[50];
unsigned char *snbuf;
size_t n;
n = strlen (serialno)/2+1;
if (n < sizeof snbuf_buffer - 1)
snbuf = snbuf_buffer;
else
{
snbuf = xtrymalloc (n);
if (!snbuf)
return CRL_CACHE_DONTKNOW;
}
n = unhexify (snbuf, serialno);
result = cache_isvalid (ctrl, issuer_hash, snbuf, n, force_refresh);
if (snbuf != snbuf_buffer)
xfree (snbuf);
return result;
}
/* Check whether the certificate CERT is valid; i.e. not listed in our
cache. With FORCE_REFRESH set to true, a new CRL will be retrieved
even if the cache has not yet expired. We use a 30 minutes
threshold here so that invoking this function several times won't
load the CRL over and over. */
gpg_error_t
crl_cache_cert_isvalid (ctrl_t ctrl, ksba_cert_t cert,
int force_refresh)
{
gpg_error_t err;
crl_cache_result_t result;
unsigned char issuerhash[20];
char issuerhash_hex[41];
ksba_sexp_t serial;
unsigned char *sn;
size_t snlen;
char *endp, *tmp;
int i;
/* Compute the hash value of the issuer name. */
tmp = ksba_cert_get_issuer (cert, 0);
if (!tmp)
{
log_error ("oops: issuer missing in certificate\n");
return gpg_error (GPG_ERR_INV_CERT_OBJ);
}
gcry_md_hash_buffer (GCRY_MD_SHA1, issuerhash, tmp, strlen (tmp));
xfree (tmp);
for (i=0,tmp=issuerhash_hex; i < 20; i++, tmp += 2)
sprintf (tmp, "%02X", issuerhash[i]);
/* Get the serial number. */
serial = ksba_cert_get_serial (cert);
if (!serial)
{
log_error ("oops: S/N missing in certificate\n");
return gpg_error (GPG_ERR_INV_CERT_OBJ);
}
sn = serial;
if (*sn != '(')
{
log_error ("oops: invalid S/N\n");
xfree (serial);
return gpg_error (GPG_ERR_INV_CERT_OBJ);
}
sn++;
snlen = strtoul (sn, &endp, 10);
sn = endp;
if (*sn != ':')
{
log_error ("oops: invalid S/N\n");
xfree (serial);
return gpg_error (GPG_ERR_INV_CERT_OBJ);
}
sn++;
/* Check the cache. */
result = cache_isvalid (ctrl, issuerhash_hex, sn, snlen, force_refresh);
switch (result)
{
case CRL_CACHE_VALID:
err = 0;
break;
case CRL_CACHE_INVALID:
err = gpg_error (GPG_ERR_CERT_REVOKED);
break;
case CRL_CACHE_DONTKNOW:
err = gpg_error (GPG_ERR_NO_CRL_KNOWN);
break;
case CRL_CACHE_CANTUSE:
err = gpg_error (GPG_ERR_NO_CRL_KNOWN);
break;
default:
log_fatal ("cache_isvalid returned invalid status code %d\n", result);
}
xfree (serial);
return err;
}
/* Return the hash algorithm's algo id from its name given in the
* non-null termnated string in (buffer,buflen). Returns 0 on failure
* or if the algo is not known. */
static int
hash_algo_from_buffer (const void *buffer, size_t buflen)
{
char *string;
int algo;
string = xtrymalloc (buflen + 1);
if (!string)
{
log_error (_("out of core\n"));
return 0;
}
memcpy (string, buffer, buflen);
string[buflen] = 0;
algo = gcry_md_map_name (string);
if (!algo)
log_error ("unknown digest algorithm '%s' used in certificate\n", string);
xfree (string);
return algo;
}
/* Return an unsigned integer from the non-null termnated string
* (buffer,buflen). Returns 0 on failure. */
static unsigned int
uint_from_buffer (const void *buffer, size_t buflen)
{
char *string;
unsigned int val;
string = xtrymalloc (buflen + 1);
if (!string)
{
log_error (_("out of core\n"));
return 0;
}
memcpy (string, buffer, buflen);
string[buflen] = 0;
val = strtoul (string, NULL, 10);
xfree (string);
return val;
}
/* Prepare a hash context for the signature verification. Input is
the CRL and the output is the hash context MD as well as the uses
algorithm identifier ALGO. */
static gpg_error_t
start_sig_check (ksba_crl_t crl, gcry_md_hd_t *md, int *algo, int *use_pss)
{
gpg_error_t err;
const char *algoid;
*use_pss = 0;
algoid = ksba_crl_get_digest_algo (crl);
if (algoid && !strcmp (algoid, "1.2.840.113549.1.1.10"))
{
/* Parse rsaPSS parameter. */
gcry_buffer_t ioarray[1] = { {0} };
ksba_sexp_t pssparam;
size_t n;
gcry_sexp_t psssexp;
pssparam = ksba_crl_get_sig_val (crl);
n = gcry_sexp_canon_len (pssparam, 0, NULL, NULL);
if (!n)
{
ksba_free (pssparam);
log_error (_("got an invalid S-expression from libksba\n"));
return gpg_error (GPG_ERR_INV_SEXP);
}
err = gcry_sexp_sscan (&psssexp, NULL, pssparam, n);
ksba_free (pssparam);
if (err)
{
log_error (_("converting S-expression failed: %s\n"),
gcry_strerror (err));
return err;
}
err = gcry_sexp_extract_param (psssexp, "sig-val",
"&'hash-algo'", ioarray, NULL);
gcry_sexp_release (psssexp);
if (err)
{
log_error ("extracting params from PSS failed: %s\n",
gpg_strerror (err));
return err;
}
*algo = hash_algo_from_buffer (ioarray[0].data, ioarray[0].len);
xfree (ioarray[0].data);
*use_pss = 1;
}
else
*algo = gcry_md_map_name (algoid);
if (!*algo)
{
log_error (_("unknown hash algorithm '%s'\n"), algoid? algoid:"?");
return gpg_error (GPG_ERR_DIGEST_ALGO);
}
err = gcry_md_open (md, *algo, 0);
if (err)
{
log_error (_("gcry_md_open for algorithm %d failed: %s\n"),
*algo, gcry_strerror (err));
return err;
}
if (DBG_HASHING)
gcry_md_debug (*md, "hash.cert");
ksba_crl_set_hash_function (crl, HASH_FNC, *md);
return 0;
}
/* Finish a hash context and verify the signature. This function
should return 0 on a good signature, GPG_ERR_BAD_SIGNATURE if the
signature does not verify or any other error code. CRL is the CRL
object we are working on, MD the hash context and ISSUER_CERT the
certificate of the CRL issuer. This function takes ownership of MD. */
static gpg_error_t
finish_sig_check (ksba_crl_t crl, gcry_md_hd_t md, int algo,
ksba_cert_t issuer_cert, int use_pss)
{
gpg_error_t err;
ksba_sexp_t sigval = NULL, pubkey = NULL;
size_t n;
gcry_sexp_t s_sig = NULL, s_hash = NULL, s_pkey = NULL;
unsigned int saltlen = 0; /* (used only with use_pss) */
/* This also stops debugging on the MD. */
gcry_md_final (md);
/* Get and convert the signature value. */
sigval = ksba_crl_get_sig_val (crl);
n = gcry_sexp_canon_len (sigval, 0, NULL, NULL);
if (!n)
{
log_error (_("got an invalid S-expression from libksba\n"));
err = gpg_error (GPG_ERR_INV_SEXP);
goto leave;
}
err = gcry_sexp_sscan (&s_sig, NULL, sigval, n);
if (err)
{
log_error (_("converting S-expression failed: %s\n"),
gcry_strerror (err));
goto leave;
}
if (use_pss)
{
/* Parse rsaPSS parameter which we should find in S_SIG. */
gcry_buffer_t ioarray[2] = { {0}, {0} };
ksba_sexp_t pssparam;
gcry_sexp_t psssexp;
int hashalgo;
pssparam = ksba_crl_get_sig_val (crl);
n = gcry_sexp_canon_len (pssparam, 0, NULL, NULL);
if (!n)
{
ksba_free (pssparam);
log_error (_("got an invalid S-expression from libksba\n"));
err = gpg_error (GPG_ERR_INV_SEXP);
goto leave;
}
err = gcry_sexp_sscan (&psssexp, NULL, pssparam, n);
ksba_free (pssparam);
if (err)
{
log_error (_("converting S-expression failed: %s\n"),
gcry_strerror (err));
goto leave;
}
err = gcry_sexp_extract_param (psssexp, "sig-val",
"&'hash-algo''salt-length'",
ioarray+0, ioarray+1, NULL);
gcry_sexp_release (psssexp);
if (err)
{
log_error ("extracting params from PSS failed: %s\n",
gpg_strerror (err));
goto leave;
}
hashalgo = hash_algo_from_buffer (ioarray[0].data, ioarray[0].len);
saltlen = uint_from_buffer (ioarray[1].data, ioarray[1].len);
xfree (ioarray[0].data);
xfree (ioarray[1].data);
if (hashalgo != algo)
{
log_error ("hash algo mismatch: %d announced but %d used\n",
algo, hashalgo);
return gpg_error (GPG_ERR_INV_CRL);
}
/* Add some restrictions; see ../sm/certcheck.c for details. */
switch (algo)
{
case GCRY_MD_SHA1:
case GCRY_MD_SHA256:
case GCRY_MD_SHA384:
case GCRY_MD_SHA512:
case GCRY_MD_SHA3_256:
case GCRY_MD_SHA3_384:
case GCRY_MD_SHA3_512:
break;
default:
log_error ("PSS hash algorithm '%s' rejected\n",
gcry_md_algo_name (algo));
return gpg_error (GPG_ERR_DIGEST_ALGO);
}
if (gcry_md_get_algo_dlen (algo) != saltlen)
{
log_error ("PSS hash algorithm '%s' rejected due to salt length %u\n",
gcry_md_algo_name (algo), saltlen);
return gpg_error (GPG_ERR_DIGEST_ALGO);
}
}
/* Get and convert the public key for the issuer certificate. */
if (DBG_X509)
dump_cert ("crl_issuer_cert", issuer_cert);
pubkey = ksba_cert_get_public_key (issuer_cert);
n = gcry_sexp_canon_len (pubkey, 0, NULL, NULL);
if (!n)
{
log_error (_("got an invalid S-expression from libksba\n"));
err = gpg_error (GPG_ERR_INV_SEXP);
goto leave;
}
err = gcry_sexp_sscan (&s_pkey, NULL, pubkey, n);
if (err)
{
log_error (_("converting S-expression failed: %s\n"),
gcry_strerror (err));
goto leave;
}
/* Create an S-expression with the actual hash value. */
if (use_pss)
{
err = gcry_sexp_build (&s_hash, NULL,
"(data (flags pss)"
"(hash %s %b)"
"(salt-length %u))",
hash_algo_to_string (algo),
(int)gcry_md_get_algo_dlen (algo),
gcry_md_read (md, algo),
saltlen);
}
else
{
err = gcry_sexp_build (&s_hash, NULL,
"(data(flags pkcs1)(hash %s %b))",
hash_algo_to_string (algo),
(int)gcry_md_get_algo_dlen (algo),
gcry_md_read (md, algo));
}
if (err)
{
log_error (_("creating S-expression failed: %s\n"), gcry_strerror (err));
goto leave;
}
/* Pass this on to the signature verification. */
err = gcry_pk_verify (s_sig, s_hash, s_pkey);
if (DBG_X509)
log_debug ("gcry_pk_verify: %s\n", gpg_strerror (err));
leave:
xfree (sigval);
xfree (pubkey);
gcry_sexp_release (s_sig);
gcry_sexp_release (s_hash);
gcry_sexp_release (s_pkey);
gcry_md_close (md);
return err;
}
/* Call this to match a start_sig_check that can not be completed
normally. Takes ownership of MD if MD is not NULL. */
static void
abort_sig_check (ksba_crl_t crl, gcry_md_hd_t md)
{
(void)crl;
if (md)
gcry_md_close (md);
}
/* Workhorse of the CRL loading machinery. The CRL is read using the
CRL object and stored in the data base file DB with the name FNAME
(only used for printing error messages). That DB should be a
temporary one and not the actual one. If the function fails the
caller should delete this temporary database file. CTRL is
required to retrieve certificates using the general dirmngr
callback service. R_CRLISSUER returns an allocated string with the
crl-issuer DN, THIS_UPDATE and NEXT_UPDATE are filled with the
corresponding data from the CRL. Note that these values might get
set even if the CRL processing fails at a later step; thus the
caller should free *R_ISSUER even if the function returns with an
error. R_TRUST_ANCHOR is set on exit to NULL or a string with the
hexified fingerprint of the root certificate, if checking this
certificate for trustiness is required.
*/
static int
crl_parse_insert (ctrl_t ctrl, ksba_crl_t crl,
struct cdb_make *cdb, const char *fname,
char **r_crlissuer,
ksba_isotime_t thisupdate, ksba_isotime_t nextupdate,
char **r_trust_anchor)
{
gpg_error_t err;
ksba_stop_reason_t stopreason;
ksba_cert_t crlissuer_cert = NULL;
gcry_md_hd_t md = NULL;
int algo = 0;
int use_pss = 0;
size_t n;
(void)fname;
*r_crlissuer = NULL;
*thisupdate = *nextupdate = 0;
*r_trust_anchor = NULL;
/* Start of the KSBA parser loop. */
do
{
err = ksba_crl_parse (crl, &stopreason);
if (err)
{
log_error (_("ksba_crl_parse failed: %s\n"), gpg_strerror (err) );
goto failure;
}
switch (stopreason)
{
case KSBA_SR_BEGIN_ITEMS:
{
err = start_sig_check (crl, &md, &algo, &use_pss);
if (err)
goto failure;
err = ksba_crl_get_update_times (crl, thisupdate, nextupdate);
if (err)
{
log_error (_("error getting update times of CRL: %s\n"),
gpg_strerror (err));
err = gpg_error (GPG_ERR_INV_CRL);
goto failure;
}
if (opt.verbose || !*nextupdate)
log_info (_("update times of this CRL: this=%s next=%s\n"),
thisupdate, nextupdate);
if (!*nextupdate)
{
log_info (_("nextUpdate not given; "
"assuming a validity period of one day\n"));
gnupg_copy_time (nextupdate, thisupdate);
add_seconds_to_isotime (nextupdate, 86400);
}
}
break;
case KSBA_SR_GOT_ITEM:
{
ksba_sexp_t serial;
const unsigned char *p;
ksba_isotime_t rdate;
ksba_crl_reason_t reason;
int rc;
unsigned char record[1+15];
err = ksba_crl_get_item (crl, &serial, rdate, &reason);
if (err)
{
log_error (_("error getting CRL item: %s\n"),
gpg_strerror (err));
err = gpg_error (GPG_ERR_INV_CRL);
ksba_free (serial);
goto failure;
}
p = serial_to_buffer (serial, &n);
if (!p)
BUG ();
record[0] = (reason & 0xff);
memcpy (record+1, rdate, 15);
rc = cdb_make_add (cdb, p, n, record, 1+15);
if (rc)
{
err = gpg_error_from_errno (errno);
log_error (_("error inserting item into "
"temporary cache file: %s\n"),
strerror (errno));
goto failure;
}
ksba_free (serial);
}
break;
case KSBA_SR_END_ITEMS:
break;
case KSBA_SR_READY:
{
char *crlissuer;
ksba_name_t authid;
ksba_sexp_t authidsn;
ksba_sexp_t keyid;
/* We need to look for the issuer only after having read
all items. The issuer itselfs comes before the items
but the optional authorityKeyIdentifier comes after the
items. */
err = ksba_crl_get_issuer (crl, &crlissuer);
if( err )
{
log_error (_("no CRL issuer found in CRL: %s\n"),
gpg_strerror (err) );
err = gpg_error (GPG_ERR_INV_CRL);
goto failure;
}
/* Note: This should be released by ksba_free, not xfree.
May need a memory reallocation dance. */
*r_crlissuer = crlissuer; /* (Do it here so we don't need
to free it later) */
if (!ksba_crl_get_auth_key_id (crl, &keyid, &authid, &authidsn))
{
const char *s;
if (opt.verbose)
log_info (_("locating CRL issuer certificate by "
"authorityKeyIdentifier\n"));
s = ksba_name_enum (authid, 0);
if (s && *authidsn)
crlissuer_cert = find_cert_bysn (ctrl, s, authidsn);
if (!crlissuer_cert && keyid)
crlissuer_cert = find_cert_bysubject (ctrl,
crlissuer, keyid);
if (!crlissuer_cert)
{
log_info ("CRL issuer certificate ");
if (keyid)
{
log_printf ("{");
dump_serial (keyid);
log_printf ("} ");
}
if (authidsn)
{
log_printf ("(#");
dump_serial (authidsn);
log_printf ("/");
dump_string (s);
log_printf (") ");
}
log_printf ("not found\n");
}
ksba_name_release (authid);
xfree (authidsn);
xfree (keyid);
}
else
crlissuer_cert = find_cert_bysubject (ctrl, crlissuer, NULL);
err = 0;
if (!crlissuer_cert)
{
err = gpg_error (GPG_ERR_MISSING_CERT);
goto failure;
}
err = finish_sig_check (crl, md, algo, crlissuer_cert, use_pss);
md = NULL; /* Closed. */
if (err)
{
log_error (_("CRL signature verification failed: %s\n"),
gpg_strerror (err));
goto failure;
}
err = validate_cert_chain (ctrl, crlissuer_cert, NULL,
(VALIDATE_FLAG_TRUST_CONFIG
| VALIDATE_FLAG_CRL
| VALIDATE_FLAG_RECURSIVE),
r_trust_anchor);
if (err)
{
log_error (_("error checking validity of CRL "
"issuer certificate: %s\n"),
gpg_strerror (err));
goto failure;
}
}
break;
default:
log_debug ("crl_parse_insert: unknown stop reason\n");
err = gpg_error (GPG_ERR_BUG);
goto failure;
}
}
while (stopreason != KSBA_SR_READY);
assert (!err);
failure:
abort_sig_check (crl, md);
ksba_cert_release (crlissuer_cert);
return err;
}
/* Return the crlNumber extension as an allocated hex string or NULL
if there is none. */
static char *
get_crl_number (ksba_crl_t crl)
{
gpg_error_t err;
ksba_sexp_t number;
char *string;
err = ksba_crl_get_crl_number (crl, &number);
if (err)
return NULL;
string = serial_hex (number);
ksba_free (number);
return string;
}
/* Return the authorityKeyIdentifier or NULL if it is not available.
The issuer name may consists of several parts - they are delimted by
0x01. */
static char *
get_auth_key_id (ksba_crl_t crl, char **serialno)
{
gpg_error_t err;
ksba_name_t name;
ksba_sexp_t sn;
int idx;
const char *s;
char *string;
size_t length;
*serialno = NULL;
err = ksba_crl_get_auth_key_id (crl, NULL, &name, &sn);
if (err)
return NULL;
*serialno = serial_hex (sn);
ksba_free (sn);
if (!name)
return xstrdup ("");
length = 0;
for (idx=0; (s = ksba_name_enum (name, idx)); idx++)
{
char *p = ksba_name_get_uri (name, idx);
length += strlen (p?p:s) + 1;
xfree (p);
}
string = xtrymalloc (length+1);
if (string)
{
*string = 0;
for (idx=0; (s = ksba_name_enum (name, idx)); idx++)
{
char *p = ksba_name_get_uri (name, idx);
if (*string)
strcat (string, "\x01");
strcat (string, p?p:s);
xfree (p);
}
}
ksba_name_release (name);
return string;
}
/* Insert the CRL retrieved using URL into the cache specified by
CACHE. The CRL itself will be read from the stream FP and is
expected in binary format.
Called by:
crl_cache_load
cmd_loadcrl
--load-crl
crl_cache_reload_crl
cmd_isvalid
cmd_checkcrl
cmd_loadcrl
--fetch-crl
*/
gpg_error_t
crl_cache_insert (ctrl_t ctrl, const char *url, ksba_reader_t reader)
{
crl_cache_t cache = get_current_cache ();
gpg_error_t err, err2;
ksba_crl_t crl;
char *fname = NULL;
char *newfname = NULL;
struct cdb_make cdb;
int fd_cdb = -1;
char *issuer = NULL;
char *issuer_hash = NULL;
ksba_isotime_t thisupdate, nextupdate;
crl_cache_entry_t entry = NULL;
crl_cache_entry_t e;
gnupg_isotime_t current_time;
char *checksum = NULL;
int invalidate_crl = 0;
int idx;
const char *oid;
int critical;
char *trust_anchor = NULL;
/* FIXME: We should acquire a mutex for the URL, so that we don't
simultaneously enter the same CRL twice. However this needs to be
interweaved with the checking function.*/
err2 = 0;
err = ksba_crl_new (&crl);
if (err)
{
log_error (_("ksba_crl_new failed: %s\n"), gpg_strerror (err));
goto leave;
}
err = ksba_crl_set_reader (crl, reader);
if ( err )
{
log_error (_("ksba_crl_set_reader failed: %s\n"), gpg_strerror (err));
goto leave;
}
/* Create a temporary cache file to load the CRL into. */
{
char *tmpfname, *p;
const char *nodename;
#ifndef HAVE_W32_SYSTEM
struct utsname utsbuf;
#endif
#ifdef HAVE_W32_SYSTEM
nodename = "unknown";
#else
if (uname (&utsbuf))
nodename = "unknown";
else
nodename = utsbuf.nodename;
#endif
gpgrt_asprintf (&tmpfname, "crl-tmp-%s-%u-%p.db.tmp",
nodename, (unsigned int)getpid (), &tmpfname);
if (!tmpfname)
{
err = gpg_error_from_syserror ();
goto leave;
}
for (p=tmpfname; *p; p++)
if (*p == '/')
*p = '.';
fname = make_filename (opt.homedir_cache, DBDIR_D, tmpfname, NULL);
xfree (tmpfname);
if (!gnupg_remove (fname))
log_info (_("removed stale temporary cache file '%s'\n"), fname);
else if (errno != ENOENT)
{
err = gpg_error_from_syserror ();
log_error (_("problem removing stale temporary cache file '%s': %s\n"),
fname, gpg_strerror (err));
goto leave;
}
}
fd_cdb = open (fname, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0644);
if (fd_cdb == -1)
{
err = gpg_error_from_errno (errno);
log_error (_("error creating temporary cache file '%s': %s\n"),
fname, strerror (errno));
goto leave;
}
cdb_make_start(&cdb, fd_cdb);
err = crl_parse_insert (ctrl, crl, &cdb, fname,
&issuer, thisupdate, nextupdate, &trust_anchor);
if (err)
{
log_error (_("crl_parse_insert failed: %s\n"), gpg_strerror (err));
/* Error in cleanup ignored. */
cdb_make_finish (&cdb);
goto leave;
}
/* Finish the database. */
if (cdb_make_finish (&cdb))
{
err = gpg_error_from_errno (errno);
log_error (_("error finishing temporary cache file '%s': %s\n"),
fname, strerror (errno));
goto leave;
}
if (close (fd_cdb))
{
err = gpg_error_from_errno (errno);
log_error (_("error closing temporary cache file '%s': %s\n"),
fname, strerror (errno));
goto leave;
}
fd_cdb = -1;
/* Create a checksum. */
{
unsigned char md5buf[16];
if (hash_dbfile (fname, md5buf))
{
err = gpg_error (GPG_ERR_CHECKSUM);
goto leave;
}
checksum = hexify_data (md5buf, 16, 0);
}
/* Check whether that new CRL is still not expired. */
gnupg_get_isotime (current_time);
if (strcmp (nextupdate, current_time) < 0 )
{
if (opt.force)
log_info (_("WARNING: new CRL still too old; it expired on %s "
"- loading anyway\n"), nextupdate);
else
{
log_error (_("new CRL still too old; it expired on %s\n"),
nextupdate);
if (!err2)
err2 = gpg_error (GPG_ERR_CRL_TOO_OLD);
invalidate_crl |= 1;
}
}
/* Check for unknown critical extensions. */
for (idx=0; !(err=ksba_crl_get_extension (crl, idx, &oid, &critical,
NULL, NULL)); idx++)
{
if (!critical
|| !strcmp (oid, oidstr_authorityKeyIdentifier)
|| !strcmp (oid, oidstr_crlNumber) )
continue;
log_error (_("unknown critical CRL extension %s\n"), oid);
if (!err2)
err2 = gpg_error (GPG_ERR_INV_CRL);
invalidate_crl |= 2;
}
if (gpg_err_code (err) == GPG_ERR_EOF
|| gpg_err_code (err) == GPG_ERR_NO_DATA )
err = 0;
if (err)
{
log_error (_("error reading CRL extensions: %s\n"), gpg_strerror (err));
err = gpg_error (GPG_ERR_INV_CRL);
}
/* Create an hex encoded SHA-1 hash of the issuer DN to be
used as the key for the cache. */
issuer_hash = hashify_data (issuer, strlen (issuer));
/* Create an ENTRY. */
entry = xtrycalloc (1, sizeof *entry);
if (!entry)
{
err = gpg_error_from_syserror ();
goto leave;
}
entry->release_ptr = xtrymalloc (strlen (issuer_hash) + 1
+ strlen (issuer) + 1
+ strlen (url) + 1
+ strlen (checksum) + 1);
if (!entry->release_ptr)
{
err = gpg_error_from_syserror ();
xfree (entry);
entry = NULL;
goto leave;
}
entry->issuer_hash = entry->release_ptr;
entry->issuer = stpcpy (entry->issuer_hash, issuer_hash) + 1;
entry->url = stpcpy (entry->issuer, issuer) + 1;
entry->dbfile_hash = stpcpy (entry->url, url) + 1;
strcpy (entry->dbfile_hash, checksum);
gnupg_copy_time (entry->this_update, thisupdate);
gnupg_copy_time (entry->next_update, nextupdate);
gnupg_copy_time (entry->last_refresh, current_time);
entry->crl_number = get_crl_number (crl);
entry->authority_issuer = get_auth_key_id (crl, &entry->authority_serialno);
entry->invalid = invalidate_crl;
entry->user_trust_req = !!trust_anchor;
entry->check_trust_anchor = trust_anchor;
trust_anchor = NULL;
/* Check whether we already have an entry for this issuer and mark
it as deleted. We better use a loop, just in case duplicates got
somehow into the list. */
for (e = cache->entries; (e=find_entry (e, entry->issuer_hash)); e = e->next)
e->deleted = 1;
/* Rename the temporary DB to the real name. */
newfname = make_db_file_name (entry->issuer_hash);
if (opt.verbose)
log_info (_("creating cache file '%s'\n"), newfname);
/* Just in case close unused matching files. Actually we need this
only under Windows but saving file descriptors is never bad. */
{
int any;
do
{
any = 0;
for (e = cache->entries; e; e = e->next)
if (!e->cdb_use_count && e->cdb
&& !strcmp (e->issuer_hash, entry->issuer_hash))
{
int fd = cdb_fileno (e->cdb);
cdb_free (e->cdb);
xfree (e->cdb);
e->cdb = NULL;
if (close (fd))
log_error (_("error closing cache file: %s\n"),
strerror(errno));
any = 1;
break;
}
}
while (any);
}
#ifdef HAVE_W32_SYSTEM
gnupg_remove (newfname);
#endif
if (rename (fname, newfname))
{
err = gpg_error_from_syserror ();
log_error (_("problem renaming '%s' to '%s': %s\n"),
fname, newfname, gpg_strerror (err));
goto leave;
}
xfree (fname); fname = NULL; /*(let the cleanup code not try to remove it)*/
/* Link the new entry in. */
entry->next = cache->entries;
cache->entries = entry;
entry = NULL;
err = update_dir (cache);
if (err)
{
log_error (_("updating the DIR file failed - "
"cache entry will get lost with the next program start\n"));
err = 0; /* Keep on running. */
}
leave:
release_one_cache_entry (entry);
if (fd_cdb != -1)
close (fd_cdb);
if (fname)
{
gnupg_remove (fname);
xfree (fname);
}
xfree (newfname);
ksba_crl_release (crl);
xfree (issuer);
xfree (issuer_hash);
xfree (checksum);
xfree (trust_anchor);
return err ? err : err2;
}
/* Print one cached entry E in a human readable format to stream
FP. Return 0 on success. */
static gpg_error_t
list_one_crl_entry (crl_cache_t cache, crl_cache_entry_t e, estream_t fp)
{
struct cdb_find cdbfp;
struct cdb *cdb;
int rc;
int warn = 0;
const unsigned char *s;
es_fputs ("--------------------------------------------------------\n", fp );
es_fprintf (fp, _("Begin CRL dump (retrieved via %s)\n"), e->url );
es_fprintf (fp, " Issuer:\t%s\n", e->issuer );
es_fprintf (fp, " Issuer Hash:\t%s\n", e->issuer_hash );
es_fprintf (fp, " This Update:\t%s\n", e->this_update );
es_fprintf (fp, " Next Update:\t%s\n", e->next_update );
es_fprintf (fp, " CRL Number :\t%s\n", e->crl_number? e->crl_number: "none");
es_fprintf (fp, " AuthKeyId :\t%s\n",
e->authority_serialno? e->authority_serialno:"none");
if (e->authority_serialno && e->authority_issuer)
{
es_fputs (" \t", fp);
for (s=e->authority_issuer; *s; s++)
if (*s == '\x01')
es_fputs ("\n \t", fp);
else
es_putc (*s, fp);
es_putc ('\n', fp);
}
es_fprintf (fp, " Trust Check:\t%s\n",
!e->user_trust_req? "[system]" :
e->check_trust_anchor? e->check_trust_anchor:"[missing]");
if ((e->invalid & 1))
es_fprintf (fp, _(" ERROR: The CRL will not be used "
"because it was still too old after an update!\n"));
if ((e->invalid & 2))
es_fprintf (fp, _(" ERROR: The CRL will not be used "
"due to an unknown critical extension!\n"));
if ((e->invalid & ~3))
es_fprintf (fp, _(" ERROR: The CRL will not be used\n"));
cdb = lock_db_file (cache, e);
if (!cdb)
return gpg_error (GPG_ERR_GENERAL);
if (!e->dbfile_checked)
es_fprintf (fp, _(" ERROR: This cached CRL may have been tampered with!\n"));
es_putc ('\n', fp);
rc = cdb_findinit (&cdbfp, cdb, NULL, 0);
while (!rc && (rc=cdb_findnext (&cdbfp)) > 0 )
{
unsigned char keyrecord[256];
unsigned char record[16];
int reason;
int any = 0;
cdbi_t n;
cdbi_t i;
rc = 0;
n = cdb_datalen (cdb);
if (n != 16)
{
log_error (_(" WARNING: invalid cache record length\n"));
warn = 1;
continue;
}
if (cdb_read (cdb, record, n, cdb_datapos (cdb)))
{
log_error (_("problem reading cache record: %s\n"),
strerror (errno));
warn = 1;
continue;
}
n = cdb_keylen (cdb);
if (n > sizeof keyrecord)
n = sizeof keyrecord;
if (cdb_read (cdb, keyrecord, n, cdb_keypos (cdb)))
{
log_error (_("problem reading cache key: %s\n"), strerror (errno));
warn = 1;
continue;
}
reason = *record;
es_fputs (" ", fp);
for (i = 0; i < n; i++)
es_fprintf (fp, "%02X", keyrecord[i]);
es_fputs (":\t reasons( ", fp);
if (reason & KSBA_CRLREASON_UNSPECIFIED)
es_fputs( "unspecified ", fp ), any = 1;
if (reason & KSBA_CRLREASON_KEY_COMPROMISE )
es_fputs( "key_compromise ", fp ), any = 1;
if (reason & KSBA_CRLREASON_CA_COMPROMISE )
es_fputs( "ca_compromise ", fp ), any = 1;
if (reason & KSBA_CRLREASON_AFFILIATION_CHANGED )
es_fputs( "affiliation_changed ", fp ), any = 1;
if (reason & KSBA_CRLREASON_SUPERSEDED )
es_fputs( "superseded", fp ), any = 1;
if (reason & KSBA_CRLREASON_CESSATION_OF_OPERATION )
es_fputs( "cessation_of_operation", fp ), any = 1;
if (reason & KSBA_CRLREASON_CERTIFICATE_HOLD )
es_fputs( "certificate_hold", fp ), any = 1;
if (reason && !any)
es_fputs( "other", fp );
es_fprintf (fp, ") rdate: %.15s\n", record+1);
}
if (rc)
log_error (_("error reading cache entry from db: %s\n"), strerror (rc));
unlock_db_file (cache, e);
es_fprintf (fp, _("End CRL dump\n") );
es_putc ('\n', fp);
return (rc||warn)? gpg_error (GPG_ERR_GENERAL) : 0;
}
/* Print the contents of the CRL CACHE in a human readable format to
stream FP. */
gpg_error_t
crl_cache_list (estream_t fp)
{
crl_cache_t cache = get_current_cache ();
crl_cache_entry_t entry;
gpg_error_t err = 0;
for (entry = cache->entries;
entry && !entry->deleted && !err;
entry = entry->next )
err = list_one_crl_entry (cache, entry, fp);
return err;
}
/* Load the CRL containing the file named FILENAME into our CRL cache. */
gpg_error_t
crl_cache_load (ctrl_t ctrl, const char *filename)
{
gpg_error_t err;
estream_t fp;
ksba_reader_t reader;
fp = es_fopen (filename, "rb");
if (!fp)
{
err = gpg_error_from_errno (errno);
log_error (_("can't open '%s': %s\n"), filename, strerror (errno));
return err;
}
err = create_estream_ksba_reader (&reader, fp);
if (!err)
{
err = crl_cache_insert (ctrl, filename, reader);
ksba_reader_release (reader);
}
es_fclose (fp);
return err;
}
/* Locate the corresponding CRL for the certificate CERT, read and
verify the CRL and store it in the cache. */
gpg_error_t
crl_cache_reload_crl (ctrl_t ctrl, ksba_cert_t cert)
{
gpg_error_t err;
ksba_reader_t reader = NULL;
char *issuer = NULL;
ksba_name_t distpoint = NULL;
ksba_name_t issuername = NULL;
char *distpoint_uri = NULL;
char *issuername_uri = NULL;
int any_dist_point = 0;
int seq;
/* Loop over all distribution points, get the CRLs and put them into
the cache. */
if (opt.verbose)
log_info ("checking distribution points\n");
seq = 0;
while ( !(err = ksba_cert_get_crl_dist_point (cert, seq++,
&distpoint,
&issuername, NULL )))
{
int name_seq;
gpg_error_t last_err = 0;
if (!distpoint && !issuername)
{
if (opt.verbose)
log_info ("no issuer name and no distribution point\n");
break; /* Not allowed; i.e. an invalid certificate. We give
up here and hope that the default method returns a
suitable CRL. */
}
xfree (issuername_uri); issuername_uri = NULL;
/* Get the URIs. We do this in a loop to iterate over all names
in the crlDP. */
for (name_seq=0; ksba_name_enum (distpoint, name_seq); name_seq++)
{
xfree (distpoint_uri); distpoint_uri = NULL;
distpoint_uri = ksba_name_get_uri (distpoint, name_seq);
if (!distpoint_uri)
continue;
if (!strncmp (distpoint_uri, "ldap:", 5)
|| !strncmp (distpoint_uri, "ldaps:", 6))
{
if (opt.ignore_ldap_dp)
continue;
}
else if (!strncmp (distpoint_uri, "http:", 5)
|| !strncmp (distpoint_uri, "https:", 6))
{
if (opt.ignore_http_dp)
continue;
}
else
continue; /* Skip unknown schemes. */
any_dist_point = 1;
if (opt.verbose)
log_info ("fetching CRL from '%s'\n", distpoint_uri);
err = crl_fetch (ctrl, distpoint_uri, &reader);
if (err)
{
log_error (_("crl_fetch via DP failed: %s\n"),
gpg_strerror (err));
last_err = err;
continue; /* with the next name. */
}
if (opt.verbose)
log_info ("inserting CRL (reader %p)\n", reader);
err = crl_cache_insert (ctrl, distpoint_uri, reader);
if (err)
{
log_error (_("crl_cache_insert via DP failed: %s\n"),
gpg_strerror (err));
last_err = err;
continue; /* with the next name. */
}
last_err = 0;
break; /* Ready. */
}
if (last_err)
{
err = last_err;
goto leave;
}
ksba_name_release (distpoint); distpoint = NULL;
/* We don't do anything with issuername_uri yet but we keep the
code for documentation. */
issuername_uri = ksba_name_get_uri (issuername, 0);
ksba_name_release (issuername); issuername = NULL;
/* Close the reader. */
crl_close_reader (reader);
reader = NULL;
}
if (gpg_err_code (err) == GPG_ERR_EOF)
err = 0;
/* If we did not found any distpoint, try something reasonable. */
if (!any_dist_point )
{
if (opt.verbose)
log_info ("no distribution point - trying issuer name\n");
crl_close_reader (reader);
reader = NULL;
issuer = ksba_cert_get_issuer (cert, 0);
if (!issuer)
{
log_error ("oops: issuer missing in certificate\n");
err = gpg_error (GPG_ERR_INV_CERT_OBJ);
goto leave;
}
if (opt.verbose)
log_info ("fetching CRL from default location\n");
err = crl_fetch_default (ctrl, issuer, &reader);
if (err)
{
log_error ("crl_fetch via issuer failed: %s\n",
gpg_strerror (err));
goto leave;
}
if (opt.verbose)
log_info ("inserting CRL (reader %p)\n", reader);
err = crl_cache_insert (ctrl, "default location(s)", reader);
if (err)
{
log_error (_("crl_cache_insert via issuer failed: %s\n"),
gpg_strerror (err));
goto leave;
}
}
leave:
crl_close_reader (reader);
xfree (distpoint_uri);
xfree (issuername_uri);
ksba_name_release (distpoint);
ksba_name_release (issuername);
ksba_free (issuer);
return err;
}
diff --git a/dirmngr/misc.c b/dirmngr/misc.c
index eef04ed83..ba47c99b2 100644
--- a/dirmngr/misc.c
+++ b/dirmngr/misc.c
@@ -1,656 +1,656 @@
/* misc.c - miscellaneous
* Copyright (C) 2002 Klarälvdalens Datakonsult AB
* Copyright (C) 2002, 2003, 2004, 2010 Free Software Foundation, Inc.
*
* This file is part of DirMngr.
*
* DirMngr is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* DirMngr is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
*/
#include
#include
#include
#include
#include
#include
#include "dirmngr.h"
#include "../common/util.h"
#include "misc.h"
/* Convert the hex encoded STRING back into binary and store the
result into the provided buffer RESULT. The actual size of that
buffer will be returned. The caller should provide RESULT of at
least strlen(STRING)/2 bytes. There is no error detection, the
parsing stops at the first non hex character. With RESULT given as
NULL, the function does only return the size of the buffer which
would be needed. */
size_t
unhexify (unsigned char *result, const char *string)
{
const char *s;
size_t n;
for (s=string,n=0; hexdigitp (s) && hexdigitp(s+1); s += 2)
{
if (result)
result[n] = xtoi_2 (s);
n++;
}
return n;
}
char*
hashify_data( const char* data, size_t len )
{
unsigned char buf[20];
gcry_md_hash_buffer (GCRY_MD_SHA1, buf, data, len);
return hexify_data (buf, 20, 0);
}
/* FIXME: Replace this by hextobin. */
char*
hexify_data (const unsigned char* data, size_t len, int with_prefix)
{
int i;
char *result = xmalloc (2*len + (with_prefix?2:0) + 1);
char *p;
if (with_prefix)
p = stpcpy (result, "0x");
else
p = result;
for (i = 0; i < 2*len; i+=2 )
snprintf (p+i, 3, "%02X", *data++);
return result;
}
char *
serial_hex (ksba_sexp_t serial )
{
unsigned char* p = serial;
char *endp;
unsigned long n;
char *certid;
if (!p)
return NULL;
else {
p++; /* ignore initial '(' */
n = strtoul (p, (char**)&endp, 10);
p = endp;
if (*p!=':')
return NULL;
else {
int i = 0;
certid = xmalloc( sizeof( char )*(2*n + 1 ) );
for (p++; n; n--, p++) {
sprintf ( certid+i , "%02X", *p);
i += 2;
}
}
}
return certid;
}
/* Take an S-Expression encoded blob and return a pointer to the
actual data as well as its length. Return NULL for an invalid
S-Expression.*/
const unsigned char *
serial_to_buffer (const ksba_sexp_t serial, size_t *length)
{
unsigned char *p = serial;
char *endp;
unsigned long n;
if (!p || *p != '(')
return NULL;
p++;
n = strtoul (p, &endp, 10);
p = endp;
if (*p != ':')
return NULL;
p++;
*length = n;
return p;
}
/* Do an in-place percent unescaping of STRING. Returns STRING. Note
that this function does not do a '+'-to-space unescaping.*/
char *
unpercent_string (char *string)
{
char *s = string;
char *d = string;
while (*s)
{
if (*s == '%' && s[1] && s[2])
{
s++;
*d++ = xtoi_2 ( s);
s += 2;
}
else
*d++ = *s++;
}
*d = 0;
return string;
}
/* Convert a canonical encoded S-expression in CANON into the GCRY
type. */
gpg_error_t
canon_sexp_to_gcry (const unsigned char *canon, gcry_sexp_t *r_sexp)
{
gpg_error_t err;
size_t n;
gcry_sexp_t sexp;
*r_sexp = NULL;
n = gcry_sexp_canon_len (canon, 0, NULL, NULL);
if (!n)
{
log_error (_("invalid canonical S-expression found\n"));
err = gpg_error (GPG_ERR_INV_SEXP);
}
else if ((err = gcry_sexp_sscan (&sexp, NULL, canon, n)))
log_error (_("converting S-expression failed: %s\n"), gcry_strerror (err));
else
*r_sexp = sexp;
return err;
}
/* Return an allocated buffer with the formatted fingerprint as one
large hexnumber */
char *
get_fingerprint_hexstring (ksba_cert_t cert)
{
unsigned char digest[20];
gcry_md_hd_t md;
int rc;
char *buf;
int i;
rc = gcry_md_open (&md, GCRY_MD_SHA1, 0);
if (rc)
log_fatal (_("gcry_md_open failed: %s\n"), gpg_strerror (rc));
rc = ksba_cert_hash (cert, 0, HASH_FNC, md);
if (rc)
{
log_error (_("oops: ksba_cert_hash failed: %s\n"), gpg_strerror (rc));
memset (digest, 0xff, 20); /* Use a dummy value. */
}
else
{
gcry_md_final (md);
memcpy (digest, gcry_md_read (md, GCRY_MD_SHA1), 20);
}
gcry_md_close (md);
buf = xmalloc (41);
*buf = 0;
for (i=0; i < 20; i++ )
sprintf (buf+strlen(buf), "%02X", digest[i]);
return buf;
}
/* Return an allocated buffer with the formatted fingerprint as one
large hexnumber. This version inserts the usual colons. */
char *
get_fingerprint_hexstring_colon (ksba_cert_t cert)
{
unsigned char digest[20];
gcry_md_hd_t md;
int rc;
char *buf;
int i;
rc = gcry_md_open (&md, GCRY_MD_SHA1, 0);
if (rc)
log_fatal (_("gcry_md_open failed: %s\n"), gpg_strerror (rc));
rc = ksba_cert_hash (cert, 0, HASH_FNC, md);
if (rc)
{
log_error (_("oops: ksba_cert_hash failed: %s\n"), gpg_strerror (rc));
memset (digest, 0xff, 20); /* Use a dummy value. */
}
else
{
gcry_md_final (md);
memcpy (digest, gcry_md_read (md, GCRY_MD_SHA1), 20);
}
gcry_md_close (md);
buf = xmalloc (61);
*buf = 0;
for (i=0; i < 20; i++ )
sprintf (buf+strlen(buf), "%02X:", digest[i]);
buf[strlen(buf)-1] = 0; /* Remove railing colon. */
return buf;
}
/* Dump the serial number SERIALNO to the log stream. */
void
dump_serial (ksba_sexp_t serialno)
{
char *p;
p = serial_hex (serialno);
log_printf ("%s", p?p:"?");
xfree (p);
}
/* Dump STRING to the log file but choose the best readable
format. */
void
dump_string (const char *string)
{
if (!string)
log_printf ("[error]");
else
{
const unsigned char *s;
for (s=string; *s; s++)
{
if (*s < ' ' || (*s >= 0x7f && *s <= 0xa0))
break;
}
if (!*s && *string != '[')
log_printf ("%s", string);
else
{
log_printf ( "[ ");
- log_printhex (NULL, string, strlen (string));
+ log_printhex (string, strlen (string), NULL);
log_printf ( " ]");
}
}
}
/* Dump an KSBA cert object to the log stream. Prefix the output with
TEXT. This is used for debugging. */
void
dump_cert (const char *text, ksba_cert_t cert)
{
ksba_sexp_t sexp;
char *p;
ksba_isotime_t t;
int idx;
log_debug ("BEGIN Certificate '%s':\n", text? text:"");
if (cert)
{
sexp = ksba_cert_get_serial (cert);
p = serial_hex (sexp);
log_debug (" serial: %s\n", p?p:"?");
xfree (p);
ksba_free (sexp);
ksba_cert_get_validity (cert, 0, t);
log_debug (" notBefore: ");
dump_isotime (t);
log_printf ("\n");
ksba_cert_get_validity (cert, 1, t);
log_debug (" notAfter: ");
dump_isotime (t);
log_printf ("\n");
p = ksba_cert_get_issuer (cert, 0);
log_debug (" issuer: ");
dump_string (p);
ksba_free (p);
log_printf ("\n");
p = ksba_cert_get_subject (cert, 0);
log_debug (" subject: ");
dump_string (p);
ksba_free (p);
log_printf ("\n");
for (idx=1; (p = ksba_cert_get_subject (cert, idx)); idx++)
{
log_debug (" aka: ");
dump_string (p);
ksba_free (p);
log_printf ("\n");
}
log_debug (" hash algo: %s\n", ksba_cert_get_digest_algo (cert));
p = get_fingerprint_hexstring (cert);
log_debug (" SHA1 fingerprint: %s\n", p);
xfree (p);
}
log_debug ("END Certificate\n");
}
/* Log the certificate's name in "#SN/ISSUERDN" format along with
TEXT. */
void
cert_log_name (const char *text, ksba_cert_t cert)
{
log_info ("%s", text? text:"certificate" );
if (cert)
{
ksba_sexp_t sn;
char *p;
p = ksba_cert_get_issuer (cert, 0);
sn = ksba_cert_get_serial (cert);
if (p && sn)
{
log_printf (" #");
dump_serial (sn);
log_printf ("/");
dump_string (p);
}
else
log_printf (" [invalid]");
ksba_free (sn);
xfree (p);
}
log_printf ("\n");
}
/* Log the certificate's subject DN along with TEXT. */
void
cert_log_subject (const char *text, ksba_cert_t cert)
{
log_info ("%s", text? text:"subject" );
if (cert)
{
char *p;
p = ksba_cert_get_subject (cert, 0);
if (p)
{
log_printf (" /");
dump_string (p);
xfree (p);
}
else
log_printf (" [invalid]");
}
log_printf ("\n");
}
/* Callback to print infos about the TLS certificates. */
void
cert_log_cb (http_session_t sess, gpg_error_t err,
const char *hostname, const void **certs, size_t *certlens)
{
ksba_cert_t cert;
size_t n;
(void)sess;
if (!err)
return; /* No error - no need to log anything */
log_debug ("expected hostname: %s\n", hostname);
for (n=0; certs[n]; n++)
{
err = ksba_cert_new (&cert);
if (!err)
err = ksba_cert_init_from_mem (cert, certs[n], certlens[n]);
if (err)
log_error ("error parsing cert for logging: %s\n", gpg_strerror (err));
else
{
char textbuf[20];
snprintf (textbuf, sizeof textbuf, "server[%u]", (unsigned int)n);
dump_cert (textbuf, cert);
}
ksba_cert_release (cert);
}
}
/****************
* Remove all %xx escapes; this is done inplace.
* Returns: New length of the string.
*/
static int
remove_percent_escapes (unsigned char *string)
{
int n = 0;
unsigned char *p, *s;
for (p = s = string; *s; s++)
{
if (*s == '%')
{
if (s[1] && s[2] && hexdigitp (s+1) && hexdigitp (s+2))
{
s++;
*p = xtoi_2 (s);
s++;
p++;
n++;
}
else
{
*p++ = *s++;
if (*s)
*p++ = *s++;
if (*s)
*p++ = *s++;
if (*s)
*p = 0;
return -1; /* Bad URI. */
}
}
else
{
*p++ = *s;
n++;
}
}
*p = 0; /* Always keep a string terminator. */
return n;
}
/* Return the host name and the port (0 if none was given) from the
URL. Return NULL on error or if host is not included in the
URL. */
char *
host_and_port_from_url (const char *url, int *port)
{
const char *s, *s2;
char *buf, *p;
int n;
s = url;
*port = 0;
/* Find the scheme */
if ( !(s2 = strchr (s, ':')) || s2 == s )
return NULL; /* No scheme given. */
s = s2+1;
/* Find the hostname */
if (*s != '/')
return NULL; /* Does not start with a slash. */
s++;
if (*s != '/')
return NULL; /* No host name. */
s++;
buf = xtrystrdup (s);
if (!buf)
{
log_error (_("malloc failed: %s\n"), strerror (errno));
return NULL;
}
if ((p = strchr (buf, '/')))
*p++ = 0;
strlwr (buf);
if ((p = strchr (buf, ':')))
{
*p++ = 0;
*port = atoi (p);
}
/* Remove quotes and make sure that no Nul has been encoded. */
if ((n = remove_percent_escapes (buf)) < 0
|| n != strlen (buf) )
{
log_error (_("bad URL encoding detected\n"));
xfree (buf);
return NULL;
}
return buf;
}
/* A KSBA reader callback to read from an estream. */
static int
my_estream_ksba_reader_cb (void *cb_value, char *buffer, size_t count,
size_t *r_nread)
{
estream_t fp = cb_value;
if (!fp)
return gpg_error (GPG_ERR_INV_VALUE);
if (!buffer && !count && !r_nread)
{
es_rewind (fp);
return 0;
}
*r_nread = es_fread (buffer, 1, count, fp);
if (!*r_nread)
return -1; /* EOF or error. */
return 0; /* Success. */
}
/* Create a KSBA reader object and connect it to the estream FP. */
gpg_error_t
create_estream_ksba_reader (ksba_reader_t *r_reader, estream_t fp)
{
gpg_error_t err;
ksba_reader_t reader;
*r_reader = NULL;
err = ksba_reader_new (&reader);
if (!err)
err = ksba_reader_set_cb (reader, my_estream_ksba_reader_cb, fp);
if (err)
{
log_error (_("error initializing reader object: %s\n"),
gpg_strerror (err));
ksba_reader_release (reader);
return err;
}
*r_reader = reader;
return 0;
}
gpg_error_t
armor_data (char **r_string, const void *data, size_t datalen)
{
gpg_error_t err;
struct b64state b64state;
estream_t fp;
long length;
char *buffer;
size_t nread;
*r_string = NULL;
fp = es_fopenmem (0, "rw,samethread");
if (!fp)
return gpg_error_from_syserror ();
if ((err=b64enc_start_es (&b64state, fp, "PGP PUBLIC KEY BLOCK"))
|| (err=b64enc_write (&b64state, data, datalen))
|| (err = b64enc_finish (&b64state)))
{
es_fclose (fp);
return err;
}
/* FIXME: To avoid the extra buffer allocation estream should
provide a function to snatch the internal allocated memory from
such a memory stream. */
length = es_ftell (fp);
if (length < 0)
{
err = gpg_error_from_syserror ();
es_fclose (fp);
return err;
}
buffer = xtrymalloc (length+1);
if (!buffer)
{
err = gpg_error_from_syserror ();
es_fclose (fp);
return err;
}
es_rewind (fp);
if (es_read (fp, buffer, length, &nread))
{
err = gpg_error_from_syserror ();
es_fclose (fp);
return err;
}
buffer[nread] = 0;
es_fclose (fp);
*r_string = buffer;
return 0;
}
/* Copy all data from IN to OUT. OUT may be NULL to use this fucntion
* as a dummy reader. */
gpg_error_t
copy_stream (estream_t in, estream_t out)
{
char buffer[512];
size_t nread;
while (!es_read (in, buffer, sizeof buffer, &nread))
{
if (!nread)
return 0; /* EOF */
if (out && es_write (out, buffer, nread, NULL))
break;
}
return gpg_error_from_syserror ();
}
diff --git a/g10/decrypt-data.c b/g10/decrypt-data.c
index 7f63dffb5..279388509 100644
--- a/g10/decrypt-data.c
+++ b/g10/decrypt-data.c
@@ -1,1012 +1,1012 @@
/* decrypt-data.c - Decrypt an encrypted data packet
* Copyright (C) 1998-2001, 2005-2006, 2009 Free Software Foundation, Inc.
* Copyright (C) 1998-2001, 2005-2006, 2009, 2018 Werner Koch
* Copyright (C) 2020 g10 Code GmbH
*
* This file is part of GnuPG.
*
* GnuPG is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* GnuPG is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see .
* SPDX-License-Identifier: GPL-3.0-or-later
*/
#include
#include
#include
#include
#include "gpg.h"
#include "../common/util.h"
#include "packet.h"
#include "options.h"
#include "../common/i18n.h"
#include "../common/status.h"
#include "../common/compliance.h"
static int aead_decode_filter (void *opaque, int control, iobuf_t a,
byte *buf, size_t *ret_len);
static int mdc_decode_filter ( void *opaque, int control, IOBUF a,
byte *buf, size_t *ret_len);
static int decode_filter ( void *opaque, int control, IOBUF a,
byte *buf, size_t *ret_len);
/* Our context object. */
struct decode_filter_context_s
{
/* Recounter (max value is 2). We need it because we do not know
* whether the iobuf or the outer control code frees this object
* first. */
int refcount;
/* The cipher handle. */
gcry_cipher_hd_t cipher_hd;
/* The hash handle for use in MDC mode. */
gcry_md_hd_t mdc_hash;
/* The start IV for AEAD encryption. */
byte startiv[16];
/* The holdback buffer and its used length. For AEAD we need 32+1
* bytes but we use 48 byte. For MDC we need 22 bytes; here
* holdbacklen will either 0 or 22. */
char holdback[48];
unsigned int holdbacklen;
/* Working on a partial length packet. */
unsigned int partial : 1;
/* EOF indicator with these true values:
* 1 = normal EOF
* 2 = premature EOF (tag or hash incomplete)
* 3 = premature EOF (general) */
unsigned int eof_seen : 2;
/* The actually used cipher algo for AEAD. */
byte cipher_algo;
/* The AEAD algo. */
byte aead_algo;
/* The encoded chunk byte for AEAD. */
byte chunkbyte;
/* The decoded CHUNKBYTE. */
uint64_t chunksize;
/* The chunk index for AEAD. */
uint64_t chunkindex;
/* The number of bytes in the current chunk. */
uint64_t chunklen;
/* The total count of decrypted plaintext octets. */
uint64_t total;
/* Remaining bytes in the packet according to the packet header.
* Not used if PARTIAL is true. */
size_t length;
};
typedef struct decode_filter_context_s *decode_filter_ctx_t;
/* Helper to release the decode context. */
static void
release_dfx_context (decode_filter_ctx_t dfx)
{
if (!dfx)
return;
log_assert (dfx->refcount);
if ( !--dfx->refcount )
{
gcry_cipher_close (dfx->cipher_hd);
dfx->cipher_hd = NULL;
gcry_md_close (dfx->mdc_hash);
dfx->mdc_hash = NULL;
xfree (dfx);
}
}
/* Set the nonce and the additional data for the current chunk. This
* also reset the decryption machinery so that the handle can be
* used for a new chunk. If FINAL is set the final AEAD chunk is
* processed. */
static gpg_error_t
aead_set_nonce_and_ad (decode_filter_ctx_t dfx, int final)
{
gpg_error_t err;
unsigned char ad[21];
unsigned char nonce[16];
int i;
switch (dfx->aead_algo)
{
case AEAD_ALGO_OCB:
memcpy (nonce, dfx->startiv, 15);
i = 7;
break;
case AEAD_ALGO_EAX:
memcpy (nonce, dfx->startiv, 16);
i = 8;
break;
default:
BUG ();
}
nonce[i++] ^= dfx->chunkindex >> 56;
nonce[i++] ^= dfx->chunkindex >> 48;
nonce[i++] ^= dfx->chunkindex >> 40;
nonce[i++] ^= dfx->chunkindex >> 32;
nonce[i++] ^= dfx->chunkindex >> 24;
nonce[i++] ^= dfx->chunkindex >> 16;
nonce[i++] ^= dfx->chunkindex >> 8;
nonce[i++] ^= dfx->chunkindex;
if (DBG_CRYPTO)
- log_printhex ("nonce:", nonce, i);
+ log_printhex (nonce, i, "nonce:");
err = gcry_cipher_setiv (dfx->cipher_hd, nonce, i);
if (err)
return err;
ad[0] = (0xc0 | PKT_ENCRYPTED_AEAD);
ad[1] = 1;
ad[2] = dfx->cipher_algo;
ad[3] = dfx->aead_algo;
ad[4] = dfx->chunkbyte;
ad[5] = dfx->chunkindex >> 56;
ad[6] = dfx->chunkindex >> 48;
ad[7] = dfx->chunkindex >> 40;
ad[8] = dfx->chunkindex >> 32;
ad[9] = dfx->chunkindex >> 24;
ad[10]= dfx->chunkindex >> 16;
ad[11]= dfx->chunkindex >> 8;
ad[12]= dfx->chunkindex;
if (final)
{
ad[13] = dfx->total >> 56;
ad[14] = dfx->total >> 48;
ad[15] = dfx->total >> 40;
ad[16] = dfx->total >> 32;
ad[17] = dfx->total >> 24;
ad[18] = dfx->total >> 16;
ad[19] = dfx->total >> 8;
ad[20] = dfx->total;
}
if (DBG_CRYPTO)
- log_printhex ("authdata:", ad, final? 21 : 13);
+ log_printhex (ad, final? 21 : 13, "authdata:");
return gcry_cipher_authenticate (dfx->cipher_hd, ad, final? 21 : 13);
}
/* Helper to check the 16 byte tag in TAGBUF. The FINAL flag is only
* for debug messages. */
static gpg_error_t
aead_checktag (decode_filter_ctx_t dfx, int final, const void *tagbuf)
{
gpg_error_t err;
if (DBG_FILTER)
- log_printhex ("tag:", tagbuf, 16);
+ log_printhex (tagbuf, 16, "tag:");
err = gcry_cipher_checktag (dfx->cipher_hd, tagbuf, 16);
if (err)
{
log_error ("gcry_cipher_checktag%s failed: %s\n",
final? " (final)":"", gpg_strerror (err));
return err;
}
if (DBG_FILTER)
log_debug ("%stag is valid\n", final?"final ":"");
return 0;
}
/****************
* Decrypt the data, specified by ED with the key DEK.
*/
int
decrypt_data (ctrl_t ctrl, void *procctx, PKT_encrypted *ed, DEK *dek)
{
decode_filter_ctx_t dfx;
enum gcry_cipher_modes ciphermode;
unsigned int startivlen;
byte *p;
int rc=0, c, i;
byte temp[32];
unsigned blocksize;
unsigned nprefix;
dfx = xtrycalloc (1, sizeof *dfx);
if (!dfx)
return gpg_error_from_syserror ();
dfx->refcount = 1;
if ( opt.verbose && !dek->algo_info_printed )
{
if (!openpgp_cipher_test_algo (dek->algo))
log_info (_("%s encrypted data\n"),
openpgp_cipher_algo_name (dek->algo));
else
log_info (_("encrypted with unknown algorithm %d\n"), dek->algo );
dek->algo_info_printed = 1;
}
if (ed->aead_algo)
{
rc = openpgp_aead_algo_info (ed->aead_algo, &ciphermode, &startivlen);
if (rc)
goto leave;
log_assert (startivlen <= sizeof dfx->startiv);
}
else
ciphermode = GCRY_CIPHER_MODE_CFB;
/* Check compliance. */
if (!gnupg_cipher_is_allowed (opt.compliance, 0, dek->algo, ciphermode))
{
log_error (_("cipher algorithm '%s' may not be used in %s mode\n"),
openpgp_cipher_algo_name (dek->algo),
gnupg_compliance_option_string (opt.compliance));
rc = gpg_error (GPG_ERR_CIPHER_ALGO);
goto leave;
}
write_status_printf (STATUS_DECRYPTION_INFO, "%d %d %d",
ed->mdc_method, dek->algo, 0);
if (opt.show_session_key)
{
char numbuf[25];
char *hexbuf;
snprintf (numbuf, sizeof numbuf, "%d:", dek->algo);
hexbuf = bin2hex (dek->key, dek->keylen, NULL);
if (!hexbuf)
{
rc = gpg_error_from_syserror ();
goto leave;
}
log_info ("session key: '%s%s'\n", numbuf, hexbuf);
write_status_strings (STATUS_SESSION_KEY, numbuf, hexbuf, NULL);
xfree (hexbuf);
}
rc = openpgp_cipher_test_algo (dek->algo);
if (rc)
goto leave;
blocksize = openpgp_cipher_get_algo_blklen (dek->algo);
if ( !blocksize || blocksize > 16 )
log_fatal ("unsupported blocksize %u\n", blocksize );
if (ed->aead_algo)
{
if (blocksize != 16)
{
rc = gpg_error (GPG_ERR_CIPHER_ALGO);
goto leave;
}
if (ed->chunkbyte > 56)
{
log_error ("invalid AEAD chunkbyte %u\n", ed->chunkbyte);
rc = gpg_error (GPG_ERR_INV_PACKET);
goto leave;
}
/* Read the Start-IV. */
if (ed->len)
{
for (i=0; i < startivlen && ed->len; i++, ed->len--)
{
if ((c=iobuf_get (ed->buf)) == -1)
break;
dfx->startiv[i] = c;
}
}
else
{
for (i=0; i < startivlen; i++ )
if ( (c=iobuf_get (ed->buf)) == -1 )
break;
else
dfx->startiv[i] = c;
}
if (i != startivlen)
{
log_error ("Start-IV in AEAD packet too short (%d/%u)\n",
i, startivlen);
rc = gpg_error (GPG_ERR_TOO_SHORT);
goto leave;
}
dfx->cipher_algo = ed->cipher_algo;
dfx->aead_algo = ed->aead_algo;
dfx->chunkbyte = ed->chunkbyte;
dfx->chunksize = (uint64_t)1 << (dfx->chunkbyte + 6);
if (dek->algo != dfx->cipher_algo)
log_info ("Note: different cipher algorithms used (%s/%s)\n",
openpgp_cipher_algo_name (dek->algo),
openpgp_cipher_algo_name (dfx->cipher_algo));
rc = openpgp_cipher_open (&dfx->cipher_hd,
dfx->cipher_algo,
ciphermode,
GCRY_CIPHER_SECURE);
if (rc)
goto leave; /* Should never happen. */
if (DBG_CRYPTO)
- log_printhex ("thekey:", dek->key, dek->keylen);
+ log_printhex (dek->key, dek->keylen, "thekey:");
rc = gcry_cipher_setkey (dfx->cipher_hd, dek->key, dek->keylen);
if (gpg_err_code (rc) == GPG_ERR_WEAK_KEY)
{
log_info (_("WARNING: message was encrypted with"
" a weak key in the symmetric cipher.\n"));
rc = 0;
}
else if (rc)
{
log_error("key setup failed: %s\n", gpg_strerror (rc));
goto leave;
}
if (!ed->buf)
{
log_error(_("problem handling encrypted packet\n"));
goto leave;
}
}
else /* CFB encryption. */
{
nprefix = blocksize;
if ( ed->len && ed->len < (nprefix+2) )
{
/* An invalid message. We can't check that during parsing
* because we may not know the used cipher then. */
rc = gpg_error (GPG_ERR_INV_PACKET);
goto leave;
}
if ( ed->mdc_method )
{
if (gcry_md_open (&dfx->mdc_hash, ed->mdc_method, 0 ))
BUG ();
if ( DBG_HASHING )
gcry_md_debug (dfx->mdc_hash, "checkmdc");
}
rc = openpgp_cipher_open (&dfx->cipher_hd, dek->algo,
GCRY_CIPHER_MODE_CFB,
(GCRY_CIPHER_SECURE
| ((ed->mdc_method || dek->algo >= 100)?
0 : GCRY_CIPHER_ENABLE_SYNC)));
if (rc)
{
/* We should never get an error here cause we already checked
* that the algorithm is available. */
BUG();
}
/* log_hexdump( "thekey", dek->key, dek->keylen );*/
rc = gcry_cipher_setkey (dfx->cipher_hd, dek->key, dek->keylen);
if ( gpg_err_code (rc) == GPG_ERR_WEAK_KEY )
{
log_info (_("WARNING: message was encrypted with"
" a weak key in the symmetric cipher.\n"));
rc = 0;
}
else if (rc)
{
log_error("key setup failed: %s\n", gpg_strerror (rc) );
goto leave;
}
if (!ed->buf)
{
log_error (_("problem handling encrypted packet\n"));
goto leave;
}
gcry_cipher_setiv (dfx->cipher_hd, NULL, 0);
if ( ed->len )
{
for (i=0; i < (nprefix+2) && ed->len; i++, ed->len-- )
{
if ( (c=iobuf_get(ed->buf)) == -1 )
break;
else
temp[i] = c;
}
}
else
{
for (i=0; i < (nprefix+2); i++ )
if ( (c=iobuf_get(ed->buf)) == -1 )
break;
else
temp[i] = c;
}
gcry_cipher_decrypt (dfx->cipher_hd, temp, nprefix+2, NULL, 0);
gcry_cipher_sync (dfx->cipher_hd);
p = temp;
/* log_hexdump( "prefix", temp, nprefix+2 ); */
if (dek->symmetric
&& (p[nprefix-2] != p[nprefix] || p[nprefix-1] != p[nprefix+1]) )
{
rc = gpg_error (GPG_ERR_BAD_KEY);
goto leave;
}
if ( dfx->mdc_hash )
gcry_md_write (dfx->mdc_hash, temp, nprefix+2);
}
dfx->refcount++;
dfx->partial = !!ed->is_partial;
dfx->length = ed->len;
if (ed->aead_algo)
iobuf_push_filter ( ed->buf, aead_decode_filter, dfx );
else if (ed->mdc_method)
iobuf_push_filter ( ed->buf, mdc_decode_filter, dfx );
else
iobuf_push_filter ( ed->buf, decode_filter, dfx );
if (opt.unwrap_encryption)
{
char *filename = NULL;
estream_t fp;
rc = get_output_file ("", 0, ed->buf, &filename, &fp);
if (! rc)
{
iobuf_t output = iobuf_esopen (fp, "w", 0);
armor_filter_context_t *afx = NULL;
if (opt.armor)
{
afx = new_armor_context ();
push_armor_filter (afx, output);
}
iobuf_copy (output, ed->buf);
if ((rc = iobuf_error (ed->buf)))
log_error (_("error reading '%s': %s\n"),
filename, gpg_strerror (rc));
else if ((rc = iobuf_error (output)))
log_error (_("error writing '%s': %s\n"),
filename, gpg_strerror (rc));
iobuf_close (output);
if (afx)
release_armor_context (afx);
}
xfree (filename);
}
else
proc_packets (ctrl, procctx, ed->buf );
ed->buf = NULL;
if (dfx->eof_seen > 1 )
rc = gpg_error (GPG_ERR_INV_PACKET);
else if ( ed->mdc_method )
{
/* We used to let parse-packet.c handle the MDC packet but this
turned out to be a problem with compressed packets: With old
style packets there is no length information available and
the decompressor uses an implicit end. However we can't know
this implicit end beforehand (:-) and thus may feed the
decompressor with more bytes than actually needed. It would
be possible to unread the extra bytes but due to our weird
iobuf system any unread is non reliable due to filters
already popped off. The easy and sane solution is to care
about the MDC packet only here and never pass it to the
packet parser. Fortunatley the OpenPGP spec requires a
strict format for the MDC packet so that we know that 22
bytes are appended. */
int datalen = gcry_md_get_algo_dlen (ed->mdc_method);
log_assert (dfx->cipher_hd);
log_assert (dfx->mdc_hash);
gcry_cipher_decrypt (dfx->cipher_hd, dfx->holdback, 22, NULL, 0);
gcry_md_write (dfx->mdc_hash, dfx->holdback, 2);
gcry_md_final (dfx->mdc_hash);
if ( dfx->holdback[0] != '\xd3'
|| dfx->holdback[1] != '\x14'
|| datalen != 20
|| memcmp (gcry_md_read (dfx->mdc_hash, 0), dfx->holdback+2, datalen))
rc = gpg_error (GPG_ERR_BAD_SIGNATURE);
- /* log_printhex("MDC message:", dfx->holdback, 22); */
- /* log_printhex("MDC calc:", gcry_md_read (dfx->mdc_hash,0), datalen); */
+ /* log_printhex(dfx->holdback, 22, "MDC message:"); */
+ /* log_printhex(gcry_md_read (dfx->mdc_hash,0), datalen, "MDC calc:"); */
}
leave:
release_dfx_context (dfx);
return rc;
}
/* Fill BUFFER with up to NBYTES-OFFSET from STREAM utilizing
* information from the context DFX. Returns the new offset which is
* the number of bytes read plus the original offset. On EOF the
* respective flag in DFX is set. */
static size_t
fill_buffer (decode_filter_ctx_t dfx, iobuf_t stream,
byte *buffer, size_t nbytes, size_t offset)
{
size_t nread = offset;
size_t curr;
int ret;
if (dfx->partial)
{
while (nread < nbytes)
{
curr = nbytes - nread;
ret = iobuf_read (stream, &buffer[nread], curr);
if (ret == -1)
{
dfx->eof_seen = 1; /* Normal EOF. */
break;
}
nread += ret;
}
}
else
{
while (nread < nbytes && dfx->length)
{
curr = nbytes - nread;
if (curr > dfx->length)
curr = dfx->length;
ret = iobuf_read (stream, &buffer[nread], curr);
if (ret == -1)
{
dfx->eof_seen = 3; /* Premature EOF. */
break;
}
nread += ret;
dfx->length -= ret;
}
if (!dfx->length)
dfx->eof_seen = 1; /* Normal EOF. */
}
return nread;
}
/* The core of the AEAD decryption. This is the underflow function of
* the aead_decode_filter. */
static gpg_error_t
aead_underflow (decode_filter_ctx_t dfx, iobuf_t a, byte *buf, size_t *ret_len)
{
const size_t size = *ret_len; /* The allocated size of BUF. */
gpg_error_t err;
size_t totallen = 0; /* The number of bytes to return on success or EOF. */
size_t off = 0; /* The offset into the buffer. */
size_t len; /* The current number of bytes in BUF+OFF. */
log_assert (size > 48); /* Our code requires at least this size. */
/* Copy the rest from the last call of this function into BUF. */
len = dfx->holdbacklen;
dfx->holdbacklen = 0;
memcpy (buf, dfx->holdback, len);
if (DBG_FILTER)
log_debug ("aead_underflow: size=%zu len=%zu%s%s\n", size, len,
dfx->partial? " partial":"", dfx->eof_seen? " eof":"");
/* Read and fill up BUF. We need to watch out for an EOF so that we
* can detect the last chunk which is commonly shorter than the
* chunksize. After the last data byte from the last chunk 32 more
* bytes are expected for the last chunk's tag and the following
* final chunk's tag. To detect the EOF we need to try reading at least
* one further byte; however we try to read 16 extra bytes to avoid
* single byte reads in some lower layers. The outcome is that we
* have up to 48 extra extra octets which we will later put into the
* holdback buffer for the next invocation (which handles the EOF
* case). */
len = fill_buffer (dfx, a, buf, size, len);
if (len < 32)
{
/* Not enough data for the last two tags. */
err = gpg_error (GPG_ERR_TRUNCATED);
goto leave;
}
if (dfx->eof_seen)
{
/* If have seen an EOF we copy only the last two auth tags into
* the holdback buffer. */
dfx->holdbacklen = 32;
memcpy (dfx->holdback, buf+len-32, 32);
len -= 32;
}
else
{
/* If have not seen an EOF we copy the entire extra 48 bytes
* into the holdback buffer for processing at the next call of
* this function. */
dfx->holdbacklen = len > 48? 48 : len;
memcpy (dfx->holdback, buf+len-dfx->holdbacklen, dfx->holdbacklen);
len -= dfx->holdbacklen;
}
/* log_printhex (dfx->holdback, dfx->holdbacklen, "holdback:"); */
/* Decrypt the buffer. This first requires a loop to handle the
* case when a chunk ends within the buffer. */
if (DBG_FILTER)
log_debug ("decrypt: chunklen=%ju total=%ju size=%zu len=%zu%s\n",
dfx->chunklen, dfx->total, size, len,
dfx->eof_seen? " eof":"");
while (len && dfx->chunklen + len >= dfx->chunksize)
{
size_t n = dfx->chunksize - dfx->chunklen;
byte tagbuf[16];
if (DBG_FILTER)
log_debug ("chunksize will be reached: n=%zu\n", n);
if (!dfx->chunklen)
{
/* First data for this chunk - prepare. */
err = aead_set_nonce_and_ad (dfx, 0);
if (err)
goto leave;
}
/* log_printhex (buf, n, "ciph:"); */
gcry_cipher_final (dfx->cipher_hd);
err = gcry_cipher_decrypt (dfx->cipher_hd, buf+off, n, NULL, 0);
if (err)
{
log_error ("gcry_cipher_decrypt failed (1): %s\n",
gpg_strerror (err));
goto leave;
}
/* log_printhex (buf, n, "plai:"); */
totallen += n;
dfx->chunklen += n;
dfx->total += n;
off += n;
len -= n;
if (DBG_FILTER)
log_debug ("ndecrypted: %zu (nchunk=%ju) bytes left: %zu at off=%zu\n",
totallen, dfx->chunklen, len, off);
/* Check the tag. */
if (len < 16)
{
/* The tag is not entirely in the buffer. Read the rest of
* the tag from the holdback buffer. Then shift the holdback
* buffer and fill it up again. */
memcpy (tagbuf, buf+off, len);
memcpy (tagbuf + len, dfx->holdback, 16 - len);
dfx->holdbacklen -= 16-len;
memmove (dfx->holdback, dfx->holdback + (16-len), dfx->holdbacklen);
if (dfx->eof_seen)
{
/* We should have the last chunk's tag in TAGBUF and the
* final tag in HOLDBACKBUF. */
if (len || dfx->holdbacklen != 16)
{
/* Not enough data for the last two tags. */
err = gpg_error (GPG_ERR_TRUNCATED);
goto leave;
}
}
else
{
len = 0;
dfx->holdbacklen = fill_buffer (dfx, a, dfx->holdback, 48,
dfx->holdbacklen);
if (dfx->holdbacklen < 32)
{
/* Not enough data for the last two tags. */
err = gpg_error (GPG_ERR_TRUNCATED);
goto leave;
}
}
}
else /* We already have the full tag. */
{
memcpy (tagbuf, buf+off, 16);
/* Remove that tag from the output. */
memmove (buf + off, buf + off + 16, len - 16);
len -= 16;
}
err = aead_checktag (dfx, 0, tagbuf);
if (err)
goto leave;
dfx->chunklen = 0;
dfx->chunkindex++;
continue;
}
/* The bulk decryption of our buffer. */
if (len)
{
if (!dfx->chunklen)
{
/* First data for this chunk - prepare. */
err = aead_set_nonce_and_ad (dfx, 0);
if (err)
goto leave;
}
if (dfx->eof_seen)
{
/* This is the last block of the last chunk. Its length may
* not be a multiple of the block length. */
gcry_cipher_final (dfx->cipher_hd);
}
err = gcry_cipher_decrypt (dfx->cipher_hd, buf + off, len, NULL, 0);
if (err)
{
log_error ("gcry_cipher_decrypt failed (2): %s\n",
gpg_strerror (err));
goto leave;
}
totallen += len;
dfx->chunklen += len;
dfx->total += len;
if (DBG_FILTER)
log_debug ("ndecrypted: %zu (nchunk=%ju)\n", totallen, dfx->chunklen);
}
if (dfx->eof_seen)
{
if (dfx->chunklen)
{
if (DBG_FILTER)
log_debug ("eof seen: holdback has the last and final tag\n");
log_assert (dfx->holdbacklen >= 32);
err = aead_checktag (dfx, 0, dfx->holdback);
if (err)
goto leave;
dfx->chunklen = 0;
dfx->chunkindex++;
off = 16;
}
else
{
if (DBG_FILTER)
log_debug ("eof seen: holdback has the final tag\n");
log_assert (dfx->holdbacklen >= 16);
off = 0;
}
/* Check the final chunk. */
err = aead_set_nonce_and_ad (dfx, 1);
if (err)
goto leave;
gcry_cipher_final (dfx->cipher_hd);
/* Decrypt an empty string (using HOLDBACK as a dummy). */
err = gcry_cipher_decrypt (dfx->cipher_hd, dfx->holdback, 0, NULL, 0);
if (err)
{
log_error ("gcry_cipher_decrypt failed (final): %s\n",
gpg_strerror (err));
goto leave;
}
err = aead_checktag (dfx, 1, dfx->holdback+off);
if (err)
goto leave;
err = gpg_error (GPG_ERR_EOF);
}
leave:
if (DBG_FILTER)
log_debug ("aead_underflow: returning %zu (%s)\n",
totallen, gpg_strerror (err));
/* In case of an auth error we map the error code to the same as
* used by the MDC decryption. */
if (gpg_err_code (err) == GPG_ERR_CHECKSUM)
err = gpg_error (GPG_ERR_BAD_SIGNATURE);
/* In case of an error we better wipe out the buffer than to convey
* partly decrypted data. */
if (err && gpg_err_code (err) != GPG_ERR_EOF)
memset (buf, 0, size);
*ret_len = totallen;
return err;
}
/* The IOBUF filter used to decrypt AEAD encrypted data. */
static int
aead_decode_filter (void *opaque, int control, IOBUF a,
byte *buf, size_t *ret_len)
{
decode_filter_ctx_t dfx = opaque;
int rc = 0;
if ( control == IOBUFCTRL_UNDERFLOW && dfx->eof_seen )
{
*ret_len = 0;
rc = -1;
}
else if ( control == IOBUFCTRL_UNDERFLOW )
{
log_assert (a);
rc = aead_underflow (dfx, a, buf, ret_len);
if (gpg_err_code (rc) == GPG_ERR_EOF)
rc = -1; /* We need to use the old convention in the filter. */
}
else if ( control == IOBUFCTRL_FREE )
{
release_dfx_context (dfx);
}
else if ( control == IOBUFCTRL_DESC )
{
mem2str (buf, "aead_decode_filter", *ret_len);
}
return rc;
}
static int
mdc_decode_filter (void *opaque, int control, IOBUF a,
byte *buf, size_t *ret_len)
{
decode_filter_ctx_t dfx = opaque;
size_t n, size = *ret_len;
int rc = 0;
/* Note: We need to distinguish between a partial and a fixed length
packet. The first is the usual case as created by GPG. However
for short messages the format degrades to a fixed length packet
and other implementations might use fixed length as well. Only
looking for the EOF on fixed data works only if the encrypted
packet is not followed by other data. This used to be a long
standing bug which was fixed on 2009-10-02. */
if ( control == IOBUFCTRL_UNDERFLOW && dfx->eof_seen )
{
*ret_len = 0;
rc = -1;
}
else if( control == IOBUFCTRL_UNDERFLOW )
{
log_assert (a);
log_assert (size > 44); /* Our code requires at least this size. */
/* Get at least 22 bytes and put it ahead in the buffer. */
n = fill_buffer (dfx, a, buf, 44, 22);
if (n == 44)
{
/* We have enough stuff - flush the deferred stuff. */
if ( !dfx->holdbacklen ) /* First time. */
{
memcpy (buf, buf+22, 22);
n = 22;
}
else
{
memcpy (buf, dfx->holdback, 22);
}
/* Fill up the buffer. */
n = fill_buffer (dfx, a, buf, size, n);
/* Move the trailing 22 bytes back to the holdback buffer. We
have at least 44 bytes thus a memmove is not needed. */
n -= 22;
memcpy (dfx->holdback, buf+n, 22 );
dfx->holdbacklen = 22;
}
else if ( !dfx->holdbacklen ) /* EOF seen but empty holdback buffer. */
{
/* This is bad because it means an incomplete hash. */
n -= 22;
memcpy (buf, buf+22, n );
dfx->eof_seen = 2; /* EOF with incomplete hash. */
}
else /* EOF seen (i.e. read less than 22 bytes). */
{
memcpy (buf, dfx->holdback, 22 );
n -= 22;
memcpy (dfx->holdback, buf+n, 22 );
dfx->eof_seen = 1; /* Normal EOF. */
}
if ( n )
{
if ( dfx->cipher_hd )
gcry_cipher_decrypt (dfx->cipher_hd, buf, n, NULL, 0);
if ( dfx->mdc_hash )
gcry_md_write (dfx->mdc_hash, buf, n);
}
else
{
log_assert ( dfx->eof_seen );
rc = -1; /* Return EOF. */
}
*ret_len = n;
}
else if ( control == IOBUFCTRL_FREE )
{
release_dfx_context (dfx);
}
else if ( control == IOBUFCTRL_DESC )
{
mem2str (buf, "mdc_decode_filter", *ret_len);
}
return rc;
}
static int
decode_filter( void *opaque, int control, IOBUF a, byte *buf, size_t *ret_len)
{
decode_filter_ctx_t fc = opaque;
size_t size = *ret_len;
size_t n;
int rc = 0;
if ( control == IOBUFCTRL_UNDERFLOW && fc->eof_seen )
{
*ret_len = 0;
rc = -1;
}
else if ( control == IOBUFCTRL_UNDERFLOW )
{
log_assert (a);
n = fill_buffer (fc, a, buf, size, 0);
if (n)
{
if (fc->cipher_hd)
gcry_cipher_decrypt (fc->cipher_hd, buf, n, NULL, 0);
}
else
{
if (!fc->eof_seen)
fc->eof_seen = 1;
rc = -1; /* Return EOF. */
}
*ret_len = n;
}
else if ( control == IOBUFCTRL_FREE )
{
release_dfx_context (fc);
}
else if ( control == IOBUFCTRL_DESC )
{
mem2str (buf, "decode_filter", *ret_len);
}
return rc;
}
diff --git a/g10/ecdh.c b/g10/ecdh.c
index dcb3cdec9..5bbea96c0 100644
--- a/g10/ecdh.c
+++ b/g10/ecdh.c
@@ -1,495 +1,495 @@
/* ecdh.c - ECDH public key operations used in public key glue code
* Copyright (C) 2010, 2011 Free Software Foundation, Inc.
*
* This file is part of GnuPG.
*
* GnuPG is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* GnuPG is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see .
*/
#include
#include
#include
#include
#include
#include "gpg.h"
#include "../common/util.h"
#include "pkglue.h"
#include "main.h"
#include "options.h"
/* A table with the default KEK parameters used by GnuPG. */
static const struct
{
unsigned int qbits;
int openpgp_hash_id; /* KEK digest algorithm. */
int openpgp_cipher_id; /* KEK cipher algorithm. */
} kek_params_table[] =
/* Note: Must be sorted by ascending values for QBITS. */
{
{ 256, DIGEST_ALGO_SHA256, CIPHER_ALGO_AES },
{ 384, DIGEST_ALGO_SHA384, CIPHER_ALGO_AES192 },
/* Note: 528 is 521 rounded to the 8 bit boundary */
{ 528, DIGEST_ALGO_SHA512, CIPHER_ALGO_AES256 }
};
/* Return KEK parameters as an opaque MPI The caller must free the
returned value. Returns NULL and sets ERRNO on error. */
gcry_mpi_t
pk_ecdh_default_params (unsigned int qbits)
{
byte *kek_params;
int i;
kek_params = xtrymalloc (4);
if (!kek_params)
return NULL;
kek_params[0] = 3; /* Number of bytes to follow. */
kek_params[1] = 1; /* Version for KDF+AESWRAP. */
/* Search for matching KEK parameter. Defaults to the strongest
possible choices. Performance is not an issue here, only
interoperability. */
for (i=0; i < DIM (kek_params_table); i++)
{
if (kek_params_table[i].qbits >= qbits
|| i+1 == DIM (kek_params_table))
{
kek_params[2] = kek_params_table[i].openpgp_hash_id;
kek_params[3] = kek_params_table[i].openpgp_cipher_id;
break;
}
}
log_assert (i < DIM (kek_params_table));
if (DBG_CRYPTO)
- log_printhex ("ECDH KEK params are", kek_params, sizeof(kek_params) );
+ log_printhex (kek_params, sizeof(kek_params), "ECDH KEK params are");
return gcry_mpi_set_opaque (NULL, kek_params, 4 * 8);
}
/* Encrypts/decrypts DATA using a key derived from the ECC shared
point SHARED_MPI using the FIPS SP 800-56A compliant method
key_derivation+key_wrapping. If IS_ENCRYPT is true the function
encrypts; if false, it decrypts. PKEY is the public key and PK_FP
the fingerprint of this public key. On success the result is
stored at R_RESULT; on failure NULL is stored at R_RESULT and an
error code returned. */
gpg_error_t
pk_ecdh_encrypt_with_shared_point (int is_encrypt, gcry_mpi_t shared_mpi,
const byte pk_fp[MAX_FINGERPRINT_LEN],
gcry_mpi_t data, gcry_mpi_t *pkey,
gcry_mpi_t *r_result)
{
gpg_error_t err;
byte *secret_x;
int secret_x_size;
unsigned int nbits;
const unsigned char *kek_params;
size_t kek_params_size;
int kdf_hash_algo;
int kdf_encr_algo;
unsigned char message[256];
size_t message_size;
*r_result = NULL;
nbits = pubkey_nbits (PUBKEY_ALGO_ECDH, pkey);
if (!nbits)
return gpg_error (GPG_ERR_TOO_SHORT);
{
size_t nbytes;
/* Extract x component of the shared point: this is the actual
shared secret. */
nbytes = (mpi_get_nbits (pkey[1] /* public point */)+7)/8;
secret_x = xtrymalloc_secure (nbytes);
if (!secret_x)
return gpg_error_from_syserror ();
err = gcry_mpi_print (GCRYMPI_FMT_USG, secret_x, nbytes,
&nbytes, shared_mpi);
if (err)
{
xfree (secret_x);
log_error ("ECDH ephemeral export of shared point failed: %s\n",
gpg_strerror (err));
return err;
}
/* Expected size of the x component */
secret_x_size = (nbits+7)/8;
/* Extract X from the result. It must be in the format of:
04 || X || Y
40 || X
41 || X
Since it always comes with the prefix, it's larger than X. In
old experimental version of libgcrypt, there is a case where it
returns X with no prefix of 40, so, nbytes == secret_x_size
is allowed. */
if (nbytes < secret_x_size)
{
xfree (secret_x);
return gpg_error (GPG_ERR_BAD_DATA);
}
/* Remove the prefix. */
if ((nbytes & 1))
memmove (secret_x, secret_x+1, secret_x_size);
/* Clear the rest of data. */
if (nbytes - secret_x_size)
memset (secret_x+secret_x_size, 0, nbytes-secret_x_size);
if (DBG_CRYPTO)
- log_printhex ("ECDH shared secret X is:", secret_x, secret_x_size );
+ log_printhex (secret_x, secret_x_size, "ECDH shared secret X is:");
}
/*** We have now the shared secret bytes in secret_x. ***/
/* At this point we are done with PK encryption and the rest of the
* function uses symmetric key encryption techniques to protect the
* input DATA. The following two sections will simply replace
* current secret_x with a value derived from it. This will become
* a KEK.
*/
if (!gcry_mpi_get_flag (pkey[2], GCRYMPI_FLAG_OPAQUE))
{
xfree (secret_x);
return gpg_error (GPG_ERR_BUG);
}
kek_params = gcry_mpi_get_opaque (pkey[2], &nbits);
kek_params_size = (nbits+7)/8;
if (DBG_CRYPTO)
- log_printhex ("ecdh KDF params:", kek_params, kek_params_size);
+ log_printhex (kek_params, kek_params_size, "ecdh KDF params:");
/* Expect 4 bytes 03 01 hash_alg symm_alg. */
if (kek_params_size != 4 || kek_params[0] != 3 || kek_params[1] != 1)
{
xfree (secret_x);
return gpg_error (GPG_ERR_BAD_PUBKEY);
}
kdf_hash_algo = kek_params[2];
kdf_encr_algo = kek_params[3];
if (DBG_CRYPTO)
log_debug ("ecdh KDF algorithms %s+%s with aeswrap\n",
openpgp_md_algo_name (kdf_hash_algo),
openpgp_cipher_algo_name (kdf_encr_algo));
if (kdf_hash_algo != GCRY_MD_SHA256
&& kdf_hash_algo != GCRY_MD_SHA384
&& kdf_hash_algo != GCRY_MD_SHA512)
{
xfree (secret_x);
return gpg_error (GPG_ERR_BAD_PUBKEY);
}
if (kdf_encr_algo != CIPHER_ALGO_AES
&& kdf_encr_algo != CIPHER_ALGO_AES192
&& kdf_encr_algo != CIPHER_ALGO_AES256)
{
xfree (secret_x);
return gpg_error (GPG_ERR_BAD_PUBKEY);
}
/* Build kdf_params. */
{
IOBUF obuf;
obuf = iobuf_temp();
/* variable-length field 1, curve name OID */
err = gpg_mpi_write_nohdr (obuf, pkey[0]);
/* fixed-length field 2 */
iobuf_put (obuf, PUBKEY_ALGO_ECDH);
/* variable-length field 3, KDF params */
err = (err ? err : gpg_mpi_write_nohdr (obuf, pkey[2]));
/* fixed-length field 4 */
iobuf_write (obuf, "Anonymous Sender ", 20);
/* fixed-length field 5, recipient fp */
iobuf_write (obuf, pk_fp, 20);
message_size = iobuf_temp_to_buffer (obuf, message, sizeof message);
iobuf_close (obuf);
if (err)
{
xfree (secret_x);
return err;
}
if(DBG_CRYPTO)
- log_printhex ("ecdh KDF message params are:", message, message_size);
+ log_printhex (message, message_size, "ecdh KDF message params are:");
}
/* Derive a KEK (key wrapping key) using MESSAGE and SECRET_X. */
{
gcry_md_hd_t h;
int old_size;
err = gcry_md_open (&h, kdf_hash_algo, 0);
if (err)
{
log_error ("gcry_md_open failed for kdf_hash_algo %d: %s",
kdf_hash_algo, gpg_strerror (err));
xfree (secret_x);
return err;
}
gcry_md_write(h, "\x00\x00\x00\x01", 4); /* counter = 1 */
gcry_md_write(h, secret_x, secret_x_size); /* x of the point X */
gcry_md_write(h, message, message_size); /* KDF parameters */
gcry_md_final (h);
log_assert( gcry_md_get_algo_dlen (kdf_hash_algo) >= 32 );
memcpy (secret_x, gcry_md_read (h, kdf_hash_algo),
gcry_md_get_algo_dlen (kdf_hash_algo));
gcry_md_close (h);
old_size = secret_x_size;
log_assert( old_size >= gcry_cipher_get_algo_keylen( kdf_encr_algo ) );
secret_x_size = gcry_cipher_get_algo_keylen( kdf_encr_algo );
log_assert( secret_x_size <= gcry_md_get_algo_dlen (kdf_hash_algo) );
/* We could have allocated more, so clean the tail before returning. */
memset (secret_x+secret_x_size, 0, old_size - secret_x_size);
if (DBG_CRYPTO)
- log_printhex ("ecdh KEK is:", secret_x, secret_x_size );
+ log_printhex (secret_x, secret_x_size, "ecdh KEK is:");
}
/* And, finally, aeswrap with key secret_x. */
{
gcry_cipher_hd_t hd;
size_t nbytes;
byte *data_buf;
int data_buf_size;
gcry_mpi_t result;
err = gcry_cipher_open (&hd, kdf_encr_algo, GCRY_CIPHER_MODE_AESWRAP, 0);
if (err)
{
log_error ("ecdh failed to initialize AESWRAP: %s\n",
gpg_strerror (err));
xfree (secret_x);
return err;
}
err = gcry_cipher_setkey (hd, secret_x, secret_x_size);
xfree (secret_x);
secret_x = NULL;
if (err)
{
gcry_cipher_close (hd);
log_error ("ecdh failed in gcry_cipher_setkey: %s\n",
gpg_strerror (err));
return err;
}
data_buf_size = (gcry_mpi_get_nbits(data)+7)/8;
if ((data_buf_size & 7) != (is_encrypt ? 0 : 1))
{
log_error ("can't use a shared secret of %d bytes for ecdh\n",
data_buf_size);
return gpg_error (GPG_ERR_BAD_DATA);
}
data_buf = xtrymalloc_secure( 1 + 2*data_buf_size + 8);
if (!data_buf)
{
err = gpg_error_from_syserror ();
gcry_cipher_close (hd);
return err;
}
if (is_encrypt)
{
byte *in = data_buf+1+data_buf_size+8;
/* Write data MPI into the end of data_buf. data_buf is size
aeswrap data. */
err = gcry_mpi_print (GCRYMPI_FMT_USG, in,
data_buf_size, &nbytes, data/*in*/);
if (err)
{
log_error ("ecdh failed to export DEK: %s\n", gpg_strerror (err));
gcry_cipher_close (hd);
xfree (data_buf);
return err;
}
if (DBG_CRYPTO)
- log_printhex ("ecdh encrypting :", in, data_buf_size );
+ log_printhex (in, data_buf_size, "ecdh encrypting :");
err = gcry_cipher_encrypt (hd, data_buf+1, data_buf_size+8,
in, data_buf_size);
memset (in, 0, data_buf_size);
gcry_cipher_close (hd);
if (err)
{
log_error ("ecdh failed in gcry_cipher_encrypt: %s\n",
gpg_strerror (err));
xfree (data_buf);
return err;
}
data_buf[0] = data_buf_size+8;
if (DBG_CRYPTO)
- log_printhex ("ecdh encrypted to:", data_buf+1, data_buf[0] );
+ log_printhex (data_buf+1, data_buf[0], "ecdh encrypted to:");
result = gcry_mpi_set_opaque (NULL, data_buf, 8 * (1+data_buf[0]));
if (!result)
{
err = gpg_error_from_syserror ();
xfree (data_buf);
log_error ("ecdh failed to create an MPI: %s\n",
gpg_strerror (err));
return err;
}
*r_result = result;
}
else
{
byte *in;
const void *p;
p = gcry_mpi_get_opaque (data, &nbits);
nbytes = (nbits+7)/8;
if (!p || nbytes > data_buf_size || !nbytes)
{
xfree (data_buf);
return gpg_error (GPG_ERR_BAD_MPI);
}
memcpy (data_buf, p, nbytes);
if (data_buf[0] != nbytes-1)
{
log_error ("ecdh inconsistent size\n");
xfree (data_buf);
return gpg_error (GPG_ERR_BAD_MPI);
}
in = data_buf+data_buf_size;
data_buf_size = data_buf[0];
if (DBG_CRYPTO)
- log_printhex ("ecdh decrypting :", data_buf+1, data_buf_size);
+ log_printhex (data_buf+1, data_buf_size, "ecdh decrypting :");
err = gcry_cipher_decrypt (hd, in, data_buf_size, data_buf+1,
data_buf_size);
gcry_cipher_close (hd);
if (err)
{
log_error ("ecdh failed in gcry_cipher_decrypt: %s\n",
gpg_strerror (err));
xfree (data_buf);
return err;
}
data_buf_size -= 8;
if (DBG_CRYPTO)
- log_printhex ("ecdh decrypted to :", in, data_buf_size);
+ log_printhex (in, data_buf_size, "ecdh decrypted to :");
/* Padding is removed later. */
/* if (in[data_buf_size-1] > 8 ) */
/* { */
/* log_error ("ecdh failed at decryption: invalid padding." */
/* " 0x%02x > 8\n", in[data_buf_size-1] ); */
/* return gpg_error (GPG_ERR_BAD_KEY); */
/* } */
err = gcry_mpi_scan (&result, GCRYMPI_FMT_USG, in, data_buf_size, NULL);
xfree (data_buf);
if (err)
{
log_error ("ecdh failed to create a plain text MPI: %s\n",
gpg_strerror (err));
return err;
}
*r_result = result;
}
}
return err;
}
static gcry_mpi_t
gen_k (unsigned nbits)
{
gcry_mpi_t k;
k = gcry_mpi_snew (nbits);
if (DBG_CRYPTO)
log_debug ("choosing a random k of %u bits\n", nbits);
gcry_mpi_randomize (k, nbits-1, GCRY_STRONG_RANDOM);
if (DBG_CRYPTO)
{
unsigned char *buffer;
if (gcry_mpi_aprint (GCRYMPI_FMT_HEX, &buffer, NULL, k))
BUG ();
log_debug ("ephemeral scalar MPI #0: %s\n", buffer);
gcry_free (buffer);
}
return k;
}
/* Generate an ephemeral key for the public ECDH key in PKEY. On
success the generated key is stored at R_K; on failure NULL is
stored at R_K and an error code returned. */
gpg_error_t
pk_ecdh_generate_ephemeral_key (gcry_mpi_t *pkey, gcry_mpi_t *r_k)
{
unsigned int nbits;
gcry_mpi_t k;
*r_k = NULL;
nbits = pubkey_nbits (PUBKEY_ALGO_ECDH, pkey);
if (!nbits)
return gpg_error (GPG_ERR_TOO_SHORT);
k = gen_k (nbits);
if (!k)
BUG ();
*r_k = k;
return 0;
}
/* Perform ECDH decryption. */
int
pk_ecdh_decrypt (gcry_mpi_t * result, const byte sk_fp[MAX_FINGERPRINT_LEN],
gcry_mpi_t data, gcry_mpi_t shared, gcry_mpi_t * skey)
{
if (!data)
return gpg_error (GPG_ERR_BAD_MPI);
return pk_ecdh_encrypt_with_shared_point (0 /*=decryption*/, shared,
sk_fp, data/*encr data as an MPI*/,
skey, result);
}
diff --git a/g10/encrypt.c b/g10/encrypt.c
index 7ec239b37..55c67cac4 100644
--- a/g10/encrypt.c
+++ b/g10/encrypt.c
@@ -1,1023 +1,1023 @@
/* encrypt.c - Main encryption driver
* Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005,
* 2006, 2009 Free Software Foundation, Inc.
* Copyright (C) 2016 g10 Code GmbH
*
* This file is part of GnuPG.
*
* GnuPG is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* GnuPG is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see .
*/
#include
#include
#include
#include
#include
#include "gpg.h"
#include "options.h"
#include "packet.h"
#include "../common/status.h"
#include "../common/iobuf.h"
#include "keydb.h"
#include "../common/util.h"
#include "main.h"
#include "filter.h"
#include "trustdb.h"
#include "../common/i18n.h"
#include "../common/status.h"
#include "pkglue.h"
#include "../common/compliance.h"
static int encrypt_simple( const char *filename, int mode, int use_seskey );
static int write_pubkey_enc_from_list (ctrl_t ctrl,
PK_LIST pk_list, DEK *dek, iobuf_t out);
/****************
* Encrypt FILENAME with only the symmetric cipher. Take input from
* stdin if FILENAME is NULL.
*/
int
encrypt_symmetric (const char *filename)
{
return encrypt_simple( filename, 1, 0 );
}
/****************
* Encrypt FILENAME as a literal data packet only. Take input from
* stdin if FILENAME is NULL.
*/
int
encrypt_store (const char *filename)
{
return encrypt_simple( filename, 0, 0 );
}
/* *SESKEY contains the unencrypted session key ((*SESKEY)->KEY) and
the algorithm that will be used to encrypt the contents of the SED
packet ((*SESKEY)->ALGO). If *SESKEY is NULL, then a random
session key that is appropriate for DEK->ALGO is generated and
stored there.
Encrypt that session key using DEK and store the result in ENCKEY,
which must be large enough to hold (*SESKEY)->KEYLEN + 1 bytes. */
void
encrypt_seskey (DEK *dek, DEK **seskey, byte *enckey)
{
gcry_cipher_hd_t hd;
byte buf[33];
log_assert ( dek->keylen <= 32 );
if (!*seskey)
{
*seskey=xmalloc_clear(sizeof(DEK));
(*seskey)->algo=dek->algo;
make_session_key(*seskey);
/*log_hexdump( "thekey", c->key, c->keylen );*/
}
/* The encrypted session key is prefixed with a one-octet algorithm id. */
buf[0] = (*seskey)->algo;
memcpy( buf + 1, (*seskey)->key, (*seskey)->keylen );
/* We only pass already checked values to the following function,
thus we consider any failure as fatal. */
if (openpgp_cipher_open (&hd, dek->algo, GCRY_CIPHER_MODE_CFB, 1))
BUG ();
if (gcry_cipher_setkey (hd, dek->key, dek->keylen))
BUG ();
gcry_cipher_setiv (hd, NULL, 0);
gcry_cipher_encrypt (hd, buf, (*seskey)->keylen + 1, NULL, 0);
gcry_cipher_close (hd);
memcpy( enckey, buf, (*seskey)->keylen + 1 );
wipememory( buf, sizeof buf ); /* burn key */
}
/* Shall we use the MDC? Yes - unless rfc-2440 compatibility is
* requested. Must return 1 or 0. */
int
use_mdc (pk_list_t pk_list,int algo)
{
(void)pk_list;
(void)algo;
/* RFC-2440 don't has MDC - this is the only way to create a legacy
* non-MDC encryption packet. */
if (RFC2440)
return 0;
return 1; /* In all other cases we use the MDC */
}
/* We don't want to use use_seskey yet because older gnupg versions
can't handle it, and there isn't really any point unless we're
making a message that can be decrypted by a public key or
passphrase. */
static int
encrypt_simple (const char *filename, int mode, int use_seskey)
{
iobuf_t inp, out;
PACKET pkt;
PKT_plaintext *pt = NULL;
STRING2KEY *s2k = NULL;
byte enckey[33];
int rc = 0;
int seskeylen = 0;
u32 filesize;
cipher_filter_context_t cfx;
armor_filter_context_t *afx = NULL;
compress_filter_context_t zfx;
text_filter_context_t tfx;
progress_filter_context_t *pfx;
int do_compress = !!default_compress_algo();
if (!gnupg_rng_is_compliant (opt.compliance))
{
rc = gpg_error (GPG_ERR_FORBIDDEN);
log_error (_("%s is not compliant with %s mode\n"),
"RNG",
gnupg_compliance_option_string (opt.compliance));
write_status_error ("random-compliance", rc);
return rc;
}
pfx = new_progress_context ();
memset( &cfx, 0, sizeof cfx);
memset( &zfx, 0, sizeof zfx);
memset( &tfx, 0, sizeof tfx);
init_packet(&pkt);
/* Prepare iobufs. */
inp = iobuf_open(filename);
if (inp)
iobuf_ioctl (inp, IOBUF_IOCTL_NO_CACHE, 1, NULL);
if (inp && is_secured_file (iobuf_get_fd (inp)))
{
iobuf_close (inp);
inp = NULL;
gpg_err_set_errno (EPERM);
}
if (!inp)
{
rc = gpg_error_from_syserror ();
log_error(_("can't open '%s': %s\n"), filename? filename: "[stdin]",
strerror(errno) );
release_progress_context (pfx);
return rc;
}
handle_progress (pfx, inp, filename);
if (opt.textmode)
iobuf_push_filter( inp, text_filter, &tfx );
cfx.dek = NULL;
if ( mode )
{
int canceled;
s2k = xmalloc_clear( sizeof *s2k );
s2k->mode = opt.s2k_mode;
s2k->hash_algo = S2K_DIGEST_ALGO;
cfx.dek = passphrase_to_dek (default_cipher_algo (), s2k, 1, 0,
NULL, &canceled);
if ( !cfx.dek || !cfx.dek->keylen )
{
rc = gpg_error (canceled? GPG_ERR_CANCELED:GPG_ERR_INV_PASSPHRASE);
xfree (cfx.dek);
xfree (s2k);
iobuf_close (inp);
log_error (_("error creating passphrase: %s\n"), gpg_strerror (rc));
release_progress_context (pfx);
return rc;
}
if (use_seskey && s2k->mode != 1 && s2k->mode != 3)
{
use_seskey = 0;
log_info (_("can't use a symmetric ESK packet "
"due to the S2K mode\n"));
}
if ( use_seskey )
{
DEK *dek = NULL;
seskeylen = openpgp_cipher_get_algo_keylen (default_cipher_algo ());
encrypt_seskey( cfx.dek, &dek, enckey );
xfree( cfx.dek ); cfx.dek = dek;
}
if (opt.verbose)
log_info(_("using cipher %s\n"),
openpgp_cipher_algo_name (cfx.dek->algo));
cfx.dek->use_mdc=use_mdc(NULL,cfx.dek->algo);
}
if (do_compress && cfx.dek && cfx.dek->use_mdc
&& is_file_compressed(filename, &rc))
{
if (opt.verbose)
log_info(_("'%s' already compressed\n"), filename);
do_compress = 0;
}
if ( rc || (rc = open_outfile (-1, filename, opt.armor? 1:0, 0, &out )))
{
iobuf_cancel (inp);
xfree (cfx.dek);
xfree (s2k);
release_progress_context (pfx);
return rc;
}
if ( opt.armor )
{
afx = new_armor_context ();
push_armor_filter (afx, out);
}
if ( s2k )
{
PKT_symkey_enc *enc = xmalloc_clear( sizeof *enc + seskeylen + 1 );
enc->version = 4;
enc->cipher_algo = cfx.dek->algo;
enc->s2k = *s2k;
if ( use_seskey && seskeylen )
{
enc->seskeylen = seskeylen + 1; /* algo id */
memcpy (enc->seskey, enckey, seskeylen + 1 );
}
pkt.pkttype = PKT_SYMKEY_ENC;
pkt.pkt.symkey_enc = enc;
if ((rc = build_packet( out, &pkt )))
log_error("build symkey packet failed: %s\n", gpg_strerror (rc) );
xfree (enc);
}
if (!opt.no_literal)
pt = setup_plaintext_name (filename, inp);
/* Note that PGP 5 has problems decrypting symmetrically encrypted
data if the file length is in the inner packet. It works when
only partial length headers are use. In the past, we always used
partial body length here, but since PGP 2, PGP 6, and PGP 7 need
the file length, and nobody should be using PGP 5 nowadays
anyway, this is now set to the file length. Note also that this
only applies to the RFC-1991 style symmetric messages, and not
the RFC-2440 style. PGP 6 and 7 work with either partial length
or fixed length with the new style messages. */
if ( !iobuf_is_pipe_filename (filename) && *filename && !opt.textmode )
{
off_t tmpsize;
int overflow;
if ( !(tmpsize = iobuf_get_filelength(inp, &overflow))
&& !overflow && opt.verbose)
log_info(_("WARNING: '%s' is an empty file\n"), filename );
/* We can't encode the length of very large files because
OpenPGP uses only 32 bit for file sizes. So if the
size of a file is larger than 2^32 minus some bytes for
packet headers, we switch to partial length encoding. */
if ( tmpsize < (IOBUF_FILELENGTH_LIMIT - 65536) )
filesize = tmpsize;
else
filesize = 0;
}
else
filesize = opt.set_filesize ? opt.set_filesize : 0; /* stdin */
if (!opt.no_literal)
{
/* Note that PT has been initialized above in !no_literal mode. */
pt->timestamp = make_timestamp();
pt->mode = opt.mimemode? 'm' : opt.textmode? 't' : 'b';
pt->len = filesize;
pt->new_ctb = !pt->len;
pt->buf = inp;
pkt.pkttype = PKT_PLAINTEXT;
pkt.pkt.plaintext = pt;
cfx.datalen = filesize && !do_compress ? calc_packet_length( &pkt ) : 0;
}
else
{
cfx.datalen = filesize && !do_compress ? filesize : 0;
pkt.pkttype = 0;
pkt.pkt.generic = NULL;
}
/* Register the cipher filter. */
if (mode)
iobuf_push_filter ( out, cipher_filter, &cfx );
/* Register the compress filter. */
if ( do_compress )
{
if (cfx.dek && cfx.dek->use_mdc)
zfx.new_ctb = 1;
push_compress_filter (out, &zfx, default_compress_algo());
}
/* Do the work. */
if (!opt.no_literal)
{
if ( (rc = build_packet( out, &pkt )) )
log_error("build_packet failed: %s\n", gpg_strerror (rc) );
}
else
{
/* User requested not to create a literal packet, so we copy the
plain data. */
byte copy_buffer[4096];
int bytes_copied;
while ((bytes_copied = iobuf_read(inp, copy_buffer, 4096)) != -1)
if ( (rc=iobuf_write(out, copy_buffer, bytes_copied)) ) {
log_error ("copying input to output failed: %s\n",
gpg_strerror (rc) );
break;
}
wipememory (copy_buffer, 4096); /* burn buffer */
}
/* Finish the stuff. */
iobuf_close (inp);
if (rc)
iobuf_cancel(out);
else
{
iobuf_close (out); /* fixme: check returncode */
if (mode)
write_status ( STATUS_END_ENCRYPTION );
}
if (pt)
pt->buf = NULL;
free_packet (&pkt, NULL);
xfree (cfx.dek);
xfree (s2k);
release_armor_context (afx);
release_progress_context (pfx);
return rc;
}
int
setup_symkey (STRING2KEY **symkey_s2k,DEK **symkey_dek)
{
int canceled;
*symkey_s2k=xmalloc_clear(sizeof(STRING2KEY));
(*symkey_s2k)->mode = opt.s2k_mode;
(*symkey_s2k)->hash_algo = S2K_DIGEST_ALGO;
*symkey_dek = passphrase_to_dek (opt.s2k_cipher_algo,
*symkey_s2k, 1, 0, NULL, &canceled);
if(!*symkey_dek || !(*symkey_dek)->keylen)
{
xfree(*symkey_dek);
xfree(*symkey_s2k);
return gpg_error (canceled?GPG_ERR_CANCELED:GPG_ERR_BAD_PASSPHRASE);
}
return 0;
}
static int
write_symkey_enc (STRING2KEY *symkey_s2k, DEK *symkey_dek, DEK *dek,
iobuf_t out)
{
int rc, seskeylen = openpgp_cipher_get_algo_keylen (dek->algo);
PKT_symkey_enc *enc;
byte enckey[33];
PACKET pkt;
enc=xmalloc_clear(sizeof(PKT_symkey_enc)+seskeylen+1);
encrypt_seskey(symkey_dek,&dek,enckey);
enc->version = 4;
enc->cipher_algo = opt.s2k_cipher_algo;
enc->s2k = *symkey_s2k;
enc->seskeylen = seskeylen + 1; /* algo id */
memcpy( enc->seskey, enckey, seskeylen + 1 );
pkt.pkttype = PKT_SYMKEY_ENC;
pkt.pkt.symkey_enc = enc;
if ((rc=build_packet(out,&pkt)))
log_error("build symkey_enc packet failed: %s\n",gpg_strerror (rc));
xfree(enc);
return rc;
}
/*
* Encrypt the file with the given userids (or ask if none is
* supplied). Either FILENAME or FILEFD must be given, but not both.
* The caller may provide a checked list of public keys in
* PROVIDED_PKS; if not the function builds a list of keys on its own.
*
* Note that FILEFD is currently only used by cmd_encrypt in the
* not yet finished server.c.
*/
int
encrypt_crypt (ctrl_t ctrl, int filefd, const char *filename,
strlist_t remusr, int use_symkey, pk_list_t provided_keys,
int outputfd)
{
iobuf_t inp = NULL;
iobuf_t out = NULL;
PACKET pkt;
PKT_plaintext *pt = NULL;
DEK *symkey_dek = NULL;
STRING2KEY *symkey_s2k = NULL;
int rc = 0, rc2 = 0;
u32 filesize;
cipher_filter_context_t cfx;
armor_filter_context_t *afx = NULL;
compress_filter_context_t zfx;
text_filter_context_t tfx;
progress_filter_context_t *pfx;
PK_LIST pk_list;
int do_compress;
int compliant;
if (filefd != -1 && filename)
return gpg_error (GPG_ERR_INV_ARG); /* Both given. */
do_compress = !!opt.compress_algo;
pfx = new_progress_context ();
memset( &cfx, 0, sizeof cfx);
memset( &zfx, 0, sizeof zfx);
memset( &tfx, 0, sizeof tfx);
init_packet(&pkt);
if (use_symkey
&& (rc=setup_symkey(&symkey_s2k,&symkey_dek)))
{
release_progress_context (pfx);
return rc;
}
if (provided_keys)
pk_list = provided_keys;
else
{
if ((rc = build_pk_list (ctrl, remusr, &pk_list)))
{
release_progress_context (pfx);
return rc;
}
}
/* Prepare iobufs. */
#ifdef HAVE_W32_SYSTEM
if (filefd == -1)
inp = iobuf_open (filename);
else
{
inp = NULL;
gpg_err_set_errno (ENOSYS);
}
#else
if (filefd == GNUPG_INVALID_FD)
inp = iobuf_open (filename);
else
inp = iobuf_fdopen_nc (FD2INT(filefd), "rb");
#endif
if (inp)
iobuf_ioctl (inp, IOBUF_IOCTL_NO_CACHE, 1, NULL);
if (inp && is_secured_file (iobuf_get_fd (inp)))
{
iobuf_close (inp);
inp = NULL;
gpg_err_set_errno (EPERM);
}
if (!inp)
{
char xname[64];
rc = gpg_error_from_syserror ();
if (filefd != -1)
snprintf (xname, sizeof xname, "[fd %d]", filefd);
else if (!filename)
strcpy (xname, "[stdin]");
else
*xname = 0;
log_error (_("can't open '%s': %s\n"),
*xname? xname : filename, gpg_strerror (rc) );
goto leave;
}
if (opt.verbose)
log_info (_("reading from '%s'\n"), iobuf_get_fname_nonnull (inp));
handle_progress (pfx, inp, filename);
if (opt.textmode)
iobuf_push_filter (inp, text_filter, &tfx);
rc = open_outfile (outputfd, filename, opt.armor? 1:0, 0, &out);
if (rc)
goto leave;
if (opt.armor)
{
afx = new_armor_context ();
push_armor_filter (afx, out);
}
/* Create a session key. */
cfx.dek = xmalloc_secure_clear (sizeof *cfx.dek);
if (!opt.def_cipher_algo)
{
/* Try to get it from the prefs. */
cfx.dek->algo = select_algo_from_prefs (pk_list, PREFTYPE_SYM, -1, NULL);
/* The only way select_algo_from_prefs can fail here is when
mixing v3 and v4 keys, as v4 keys have an implicit preference
entry for 3DES, and the pk_list cannot be empty. In this
case, use 3DES anyway as it's the safest choice - perhaps the
v3 key is being used in an OpenPGP implementation and we know
that the implementation behind any v4 key can handle 3DES. */
if (cfx.dek->algo == -1)
{
cfx.dek->algo = CIPHER_ALGO_3DES;
}
/* In case 3DES has been selected, print a warning if any key
does not have a preference for AES. This should help to
indentify why encrypting to several recipients falls back to
3DES. */
if (opt.verbose && cfx.dek->algo == CIPHER_ALGO_3DES)
warn_missing_aes_from_pklist (pk_list);
}
else
{
if (!opt.expert
&& (select_algo_from_prefs (pk_list, PREFTYPE_SYM,
opt.def_cipher_algo, NULL)
!= opt.def_cipher_algo))
{
log_info(_("WARNING: forcing symmetric cipher %s (%d)"
" violates recipient preferences\n"),
openpgp_cipher_algo_name (opt.def_cipher_algo),
opt.def_cipher_algo);
}
cfx.dek->algo = opt.def_cipher_algo;
}
/* Check compliance. */
if (! gnupg_cipher_is_allowed (opt.compliance, 1, cfx.dek->algo,
GCRY_CIPHER_MODE_CFB))
{
log_error (_("cipher algorithm '%s' may not be used in %s mode\n"),
openpgp_cipher_algo_name (cfx.dek->algo),
gnupg_compliance_option_string (opt.compliance));
rc = gpg_error (GPG_ERR_CIPHER_ALGO);
goto leave;
}
if (!gnupg_rng_is_compliant (opt.compliance))
{
rc = gpg_error (GPG_ERR_FORBIDDEN);
log_error (_("%s is not compliant with %s mode\n"),
"RNG",
gnupg_compliance_option_string (opt.compliance));
write_status_error ("random-compliance", rc);
goto leave;
}
compliant = gnupg_cipher_is_compliant (CO_DE_VS, cfx.dek->algo,
GCRY_CIPHER_MODE_CFB);
{
pk_list_t pkr;
for (pkr = pk_list; pkr; pkr = pkr->next)
{
PKT_public_key *pk = pkr->pk;
unsigned int nbits = nbits_from_pk (pk);
if (!gnupg_pk_is_compliant (opt.compliance,
pk->pubkey_algo, pk->pkey, nbits, NULL))
log_info (_("WARNING: key %s is not suitable for encryption"
" in %s mode\n"),
keystr_from_pk (pk),
gnupg_compliance_option_string (opt.compliance));
if (compliant
&& !gnupg_pk_is_compliant (CO_DE_VS, pk->pubkey_algo, pk->pkey,
nbits, NULL))
compliant = 0;
}
}
if (compliant)
write_status_strings (STATUS_ENCRYPTION_COMPLIANCE_MODE,
gnupg_status_compliance_flag (CO_DE_VS),
NULL);
cfx.dek->use_mdc = use_mdc (pk_list,cfx.dek->algo);
/* Only do the is-file-already-compressed check if we are using a
MDC. This forces compressed files to be re-compressed if we do
not have a MDC to give some protection against chosen ciphertext
attacks. */
if (do_compress && cfx.dek->use_mdc && is_file_compressed(filename, &rc2))
{
if (opt.verbose)
log_info(_("'%s' already compressed\n"), filename);
do_compress = 0;
}
if (rc2)
{
rc = rc2;
goto leave;
}
make_session_key (cfx.dek);
if (DBG_CRYPTO)
- log_printhex ("DEK is: ", cfx.dek->key, cfx.dek->keylen );
+ log_printhex (cfx.dek->key, cfx.dek->keylen, "DEK is: ");
rc = write_pubkey_enc_from_list (ctrl, pk_list, cfx.dek, out);
if (rc)
goto leave;
/* We put the passphrase (if any) after any public keys as this
seems to be the most useful on the recipient side - there is no
point in prompting a user for a passphrase if they have the
secret key needed to decrypt. */
if(use_symkey && (rc = write_symkey_enc(symkey_s2k,symkey_dek,cfx.dek,out)))
goto leave;
if (!opt.no_literal)
pt = setup_plaintext_name (filename, inp);
/* Get the size of the file if possible, i.e., if it is a real file. */
if (filename && *filename
&& !iobuf_is_pipe_filename (filename) && !opt.textmode )
{
off_t tmpsize;
int overflow;
if ( !(tmpsize = iobuf_get_filelength(inp, &overflow))
&& !overflow && opt.verbose)
log_info(_("WARNING: '%s' is an empty file\n"), filename );
/* We can't encode the length of very large files because
OpenPGP uses only 32 bit for file sizes. So if the size
of a file is larger than 2^32 minus some bytes for packet
headers, we switch to partial length encoding. */
if (tmpsize < (IOBUF_FILELENGTH_LIMIT - 65536) )
filesize = tmpsize;
else
filesize = 0;
}
else
filesize = opt.set_filesize ? opt.set_filesize : 0; /* stdin */
if (!opt.no_literal)
{
pt->timestamp = make_timestamp();
pt->mode = opt.mimemode? 'm' : opt.textmode ? 't' : 'b';
pt->len = filesize;
pt->new_ctb = !pt->len;
pt->buf = inp;
pkt.pkttype = PKT_PLAINTEXT;
pkt.pkt.plaintext = pt;
cfx.datalen = filesize && !do_compress? calc_packet_length( &pkt ) : 0;
}
else
cfx.datalen = filesize && !do_compress ? filesize : 0;
/* Register the cipher filter. */
iobuf_push_filter (out, cipher_filter, &cfx);
/* Register the compress filter. */
if (do_compress)
{
int compr_algo = opt.compress_algo;
if (compr_algo == -1)
{
compr_algo = select_algo_from_prefs (pk_list, PREFTYPE_ZIP, -1, NULL);
if (compr_algo == -1)
compr_algo = DEFAULT_COMPRESS_ALGO;
/* Theoretically impossible to get here since uncompressed
is implicit. */
}
else if (!opt.expert
&& select_algo_from_prefs(pk_list, PREFTYPE_ZIP,
compr_algo, NULL) != compr_algo)
{
log_info (_("WARNING: forcing compression algorithm %s (%d)"
" violates recipient preferences\n"),
compress_algo_to_string(compr_algo), compr_algo);
}
/* Algo 0 means no compression. */
if (compr_algo)
{
if (cfx.dek && cfx.dek->use_mdc)
zfx.new_ctb = 1;
push_compress_filter (out,&zfx,compr_algo);
}
}
/* Do the work. */
if (!opt.no_literal)
{
if ((rc = build_packet( out, &pkt )))
log_error ("build_packet failed: %s\n", gpg_strerror (rc));
}
else
{
/* User requested not to create a literal packet, so we copy the
plain data. */
byte copy_buffer[4096];
int bytes_copied;
while ((bytes_copied = iobuf_read (inp, copy_buffer, 4096)) != -1)
{
rc = iobuf_write (out, copy_buffer, bytes_copied);
if (rc)
{
log_error ("copying input to output failed: %s\n",
gpg_strerror (rc));
break;
}
}
wipememory (copy_buffer, 4096); /* Burn the buffer. */
}
/* Finish the stuff. */
leave:
iobuf_close (inp);
if (rc)
iobuf_cancel (out);
else
{
iobuf_close (out); /* fixme: check returncode */
write_status (STATUS_END_ENCRYPTION);
}
if (pt)
pt->buf = NULL;
free_packet (&pkt, NULL);
xfree (cfx.dek);
xfree (symkey_dek);
xfree (symkey_s2k);
if (!provided_keys)
release_pk_list (pk_list);
release_armor_context (afx);
release_progress_context (pfx);
return rc;
}
/*
* Filter to do a complete public key encryption.
*/
int
encrypt_filter (void *opaque, int control,
iobuf_t a, byte *buf, size_t *ret_len)
{
size_t size = *ret_len;
encrypt_filter_context_t *efx = opaque;
int rc = 0;
if (control == IOBUFCTRL_UNDERFLOW) /* decrypt */
{
BUG(); /* not used */
}
else if ( control == IOBUFCTRL_FLUSH ) /* encrypt */
{
if ( !efx->header_okay )
{
efx->cfx.dek = xmalloc_secure_clear ( sizeof *efx->cfx.dek );
if ( !opt.def_cipher_algo )
{
/* Try to get it from the prefs. */
efx->cfx.dek->algo =
select_algo_from_prefs (efx->pk_list, PREFTYPE_SYM, -1, NULL);
if (efx->cfx.dek->algo == -1 )
{
/* Because 3DES is implicitly in the prefs, this can
only happen if we do not have any public keys in
the list. */
efx->cfx.dek->algo = DEFAULT_CIPHER_ALGO;
}
/* In case 3DES has been selected, print a warning if
any key does not have a preference for AES. This
should help to indentify why encrypting to several
recipients falls back to 3DES. */
if (opt.verbose
&& efx->cfx.dek->algo == CIPHER_ALGO_3DES)
warn_missing_aes_from_pklist (efx->pk_list);
}
else
{
if (!opt.expert
&& select_algo_from_prefs (efx->pk_list,PREFTYPE_SYM,
opt.def_cipher_algo,
NULL) != opt.def_cipher_algo)
log_info(_("forcing symmetric cipher %s (%d) "
"violates recipient preferences\n"),
openpgp_cipher_algo_name (opt.def_cipher_algo),
opt.def_cipher_algo);
efx->cfx.dek->algo = opt.def_cipher_algo;
}
efx->cfx.dek->use_mdc = use_mdc (efx->pk_list,efx->cfx.dek->algo);
make_session_key ( efx->cfx.dek );
if (DBG_CRYPTO)
- log_printhex ("DEK is: ", efx->cfx.dek->key, efx->cfx.dek->keylen);
+ log_printhex (efx->cfx.dek->key, efx->cfx.dek->keylen, "DEK is: ");
rc = write_pubkey_enc_from_list (efx->ctrl,
efx->pk_list, efx->cfx.dek, a);
if (rc)
return rc;
if(efx->symkey_s2k && efx->symkey_dek)
{
rc=write_symkey_enc(efx->symkey_s2k,efx->symkey_dek,
efx->cfx.dek,a);
if(rc)
return rc;
}
iobuf_push_filter (a, cipher_filter, &efx->cfx);
efx->header_okay = 1;
}
rc = iobuf_write (a, buf, size);
}
else if (control == IOBUFCTRL_FREE)
{
xfree (efx->symkey_dek);
xfree (efx->symkey_s2k);
}
else if ( control == IOBUFCTRL_DESC )
{
mem2str (buf, "encrypt_filter", *ret_len);
}
return rc;
}
/*
* Write a pubkey-enc packet for the public key PK to OUT.
*/
int
write_pubkey_enc (ctrl_t ctrl,
PKT_public_key *pk, int throw_keyid, DEK *dek, iobuf_t out)
{
PACKET pkt;
PKT_pubkey_enc *enc;
int rc;
gcry_mpi_t frame;
print_pubkey_algo_note ( pk->pubkey_algo );
enc = xmalloc_clear ( sizeof *enc );
enc->pubkey_algo = pk->pubkey_algo;
keyid_from_pk( pk, enc->keyid );
enc->throw_keyid = throw_keyid;
/* Okay, what's going on: We have the session key somewhere in
* the structure DEK and want to encode this session key in an
* integer value of n bits. pubkey_nbits gives us the number of
* bits we have to use. We then encode the session key in some
* way and we get it back in the big intger value FRAME. Then
* we use FRAME, the public key PK->PKEY and the algorithm
* number PK->PUBKEY_ALGO and pass it to pubkey_encrypt which
* returns the encrypted value in the array ENC->DATA. This
* array has a size which depends on the used algorithm (e.g. 2
* for Elgamal). We don't need frame anymore because we have
* everything now in enc->data which is the passed to
* build_packet(). */
frame = encode_session_key (pk->pubkey_algo, dek,
pubkey_nbits (pk->pubkey_algo, pk->pkey));
rc = pk_encrypt (pk->pubkey_algo, enc->data, frame, pk, pk->pkey);
gcry_mpi_release (frame);
if (rc)
log_error ("pubkey_encrypt failed: %s\n", gpg_strerror (rc) );
else
{
if ( opt.verbose )
{
char *ustr = get_user_id_string_native (ctrl, enc->keyid);
log_info (_("%s/%s encrypted for: \"%s\"\n"),
openpgp_pk_algo_name (enc->pubkey_algo),
openpgp_cipher_algo_name (dek->algo),
ustr );
xfree (ustr);
}
/* And write it. */
init_packet (&pkt);
pkt.pkttype = PKT_PUBKEY_ENC;
pkt.pkt.pubkey_enc = enc;
rc = build_packet (out, &pkt);
if (rc)
log_error ("build_packet(pubkey_enc) failed: %s\n",
gpg_strerror (rc));
}
free_pubkey_enc(enc);
return rc;
}
/*
* Write pubkey-enc packets from the list of PKs to OUT.
*/
static int
write_pubkey_enc_from_list (ctrl_t ctrl, PK_LIST pk_list, DEK *dek, iobuf_t out)
{
if (opt.throw_keyids && (PGP6 || PGP7 || PGP8))
{
log_info(_("option '%s' may not be used in %s mode\n"),
"--throw-keyids",
gnupg_compliance_option_string (opt.compliance));
compliance_failure();
}
for ( ; pk_list; pk_list = pk_list->next )
{
PKT_public_key *pk = pk_list->pk;
int throw_keyid = (opt.throw_keyids || (pk_list->flags&1));
int rc = write_pubkey_enc (ctrl, pk, throw_keyid, dek, out);
if (rc)
return rc;
}
return 0;
}
void
encrypt_crypt_files (ctrl_t ctrl, int nfiles, char **files, strlist_t remusr)
{
int rc = 0;
if (opt.outfile)
{
log_error(_("--output doesn't work for this command\n"));
return;
}
if (!nfiles)
{
char line[2048];
unsigned int lno = 0;
while ( fgets(line, DIM(line), stdin) )
{
lno++;
if (!*line || line[strlen(line)-1] != '\n')
{
log_error("input line %u too long or missing LF\n", lno);
return;
}
line[strlen(line)-1] = '\0';
print_file_status(STATUS_FILE_START, line, 2);
rc = encrypt_crypt (ctrl, -1, line, remusr, 0, NULL, -1);
if (rc)
log_error ("encryption of '%s' failed: %s\n",
print_fname_stdin(line), gpg_strerror (rc) );
write_status( STATUS_FILE_DONE );
}
}
else
{
while (nfiles--)
{
print_file_status(STATUS_FILE_START, *files, 2);
if ( (rc = encrypt_crypt (ctrl, -1, *files, remusr, 0, NULL, -1)) )
log_error("encryption of '%s' failed: %s\n",
print_fname_stdin(*files), gpg_strerror (rc) );
write_status( STATUS_FILE_DONE );
files++;
}
}
}
diff --git a/g10/export.c b/g10/export.c
index 3f4b5656c..51492773b 100644
--- a/g10/export.c
+++ b/g10/export.c
@@ -1,2474 +1,2474 @@
/* export.c - Export keys in the OpenPGP defined format.
* Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004,
* 2005, 2010 Free Software Foundation, Inc.
* Copyright (C) 1998-2016 Werner Koch
*
* This file is part of GnuPG.
*
* GnuPG is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* GnuPG is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see .
*/
#include
#include
#include
#include
#include
#include "gpg.h"
#include "options.h"
#include "packet.h"
#include "../common/status.h"
#include "keydb.h"
#include "../common/util.h"
#include "main.h"
#include "../common/i18n.h"
#include "../common/membuf.h"
#include "../common/host2net.h"
#include "../common/zb32.h"
#include "../common/recsel.h"
#include "../common/mbox-util.h"
#include "../common/init.h"
#include "trustdb.h"
#include "call-agent.h"
#include "key-clean.h"
/* An object to keep track of subkeys. */
struct subkey_list_s
{
struct subkey_list_s *next;
u32 kid[2];
};
typedef struct subkey_list_s *subkey_list_t;
/* An object to track statistics for export operations. */
struct export_stats_s
{
ulong count; /* Number of processed keys. */
ulong secret_count; /* Number of secret keys seen. */
ulong exported; /* Number of actual exported keys. */
};
/* A global variable to store the selector created from
* --export-filter keep-uid=EXPR.
* --export-filter drop-subkey=EXPR.
*
* FIXME: We should put this into the CTRL object but that requires a
* lot more changes right now.
*/
static recsel_expr_t export_keep_uid;
static recsel_expr_t export_drop_subkey;
/* An object used for a linked list to implement the
* push_export_filter/pop_export_filters functions. */
struct export_filter_attic_s
{
struct export_filter_attic_s *next;
recsel_expr_t export_keep_uid;
recsel_expr_t export_drop_subkey;
};
static struct export_filter_attic_s *export_filter_attic;
/* Local prototypes. */
static int do_export (ctrl_t ctrl, strlist_t users, int secret,
unsigned int options, export_stats_t stats);
static int do_export_stream (ctrl_t ctrl, iobuf_t out,
strlist_t users, int secret,
kbnode_t *keyblock_out, unsigned int options,
export_stats_t stats, int *any);
static gpg_error_t print_pka_or_dane_records
/**/ (iobuf_t out, kbnode_t keyblock, PKT_public_key *pk,
const void *data, size_t datalen,
int print_pka, int print_dane);
static void
cleanup_export_globals (void)
{
recsel_release (export_keep_uid);
export_keep_uid = NULL;
recsel_release (export_drop_subkey);
export_drop_subkey = NULL;
}
/* Option parser for export options. See parse_options fro
details. */
int
parse_export_options(char *str,unsigned int *options,int noisy)
{
struct parse_options export_opts[]=
{
{"export-local-sigs",EXPORT_LOCAL_SIGS,NULL,
N_("export signatures that are marked as local-only")},
{"export-attributes",EXPORT_ATTRIBUTES,NULL,
N_("export attribute user IDs (generally photo IDs)")},
{"export-sensitive-revkeys",EXPORT_SENSITIVE_REVKEYS,NULL,
N_("export revocation keys marked as \"sensitive\"")},
{"export-clean",EXPORT_CLEAN,NULL,
N_("remove unusable parts from key during export")},
{"export-minimal",EXPORT_MINIMAL|EXPORT_CLEAN,NULL,
N_("remove as much as possible from key during export")},
{"export-pka", EXPORT_PKA_FORMAT, NULL, NULL },
{"export-dane", EXPORT_DANE_FORMAT, NULL, NULL },
{"backup", EXPORT_BACKUP, NULL,
N_("use the GnuPG key backup format")},
{"export-backup", EXPORT_BACKUP, NULL, NULL },
/* Aliases for backward compatibility */
{"include-local-sigs",EXPORT_LOCAL_SIGS,NULL,NULL},
{"include-attributes",EXPORT_ATTRIBUTES,NULL,NULL},
{"include-sensitive-revkeys",EXPORT_SENSITIVE_REVKEYS,NULL,NULL},
/* dummy */
{"export-unusable-sigs",0,NULL,NULL},
{"export-clean-sigs",0,NULL,NULL},
{"export-clean-uids",0,NULL,NULL},
{NULL,0,NULL,NULL}
/* add tags for include revoked and disabled? */
};
int rc;
rc = parse_options (str, options, export_opts, noisy);
if (rc && (*options & EXPORT_BACKUP))
{
/* Alter other options we want or don't want for restore. */
*options |= (EXPORT_LOCAL_SIGS | EXPORT_ATTRIBUTES
| EXPORT_SENSITIVE_REVKEYS);
*options &= ~(EXPORT_CLEAN | EXPORT_MINIMAL
| EXPORT_PKA_FORMAT | EXPORT_DANE_FORMAT);
}
return rc;
}
/* Parse and set an export filter from string. STRING has the format
* "NAME=EXPR" with NAME being the name of the filter. Spaces before
* and after NAME are not allowed. If this function is called several
* times all expressions for the same NAME are concatenated.
* Supported filter names are:
*
* - keep-uid :: If the expression evaluates to true for a certain
* user ID packet, that packet and all it dependencies
* will be exported. The expression may use these
* variables:
*
* - uid :: The entire user ID.
* - mbox :: The mail box part of the user ID.
* - primary :: Evaluate to true for the primary user ID.
*
* - drop-subkey :: If the expression evaluates to true for a subkey
* packet that subkey and all it dependencies will be
* remove from the keyblock. The expression may use these
* variables:
*
* - secret :: 1 for a secret subkey, else 0.
* - key_algo :: Public key algorithm id
*/
gpg_error_t
parse_and_set_export_filter (const char *string)
{
gpg_error_t err;
/* Auto register the cleanup function. */
register_mem_cleanup_func (cleanup_export_globals);
if (!strncmp (string, "keep-uid=", 9))
err = recsel_parse_expr (&export_keep_uid, string+9);
else if (!strncmp (string, "drop-subkey=", 12))
err = recsel_parse_expr (&export_drop_subkey, string+12);
else
err = gpg_error (GPG_ERR_INV_NAME);
return err;
}
/* Push the current export filters onto a stack so that new export
* filters can be defined which will be active until the next
* pop_export_filters or another push_export_filters. */
void
push_export_filters (void)
{
struct export_filter_attic_s *item;
item = xcalloc (1, sizeof *item);
item->export_keep_uid = export_keep_uid;
export_keep_uid = NULL;
item->export_drop_subkey = export_drop_subkey;
export_drop_subkey = NULL;
item->next = export_filter_attic;
export_filter_attic = item;
}
/* Revert the last push_export_filters. */
void
pop_export_filters (void)
{
struct export_filter_attic_s *item;
item = export_filter_attic;
if (!item)
BUG (); /* No corresponding push. */
export_filter_attic = item->next;
cleanup_export_globals ();
export_keep_uid = item->export_keep_uid;
export_drop_subkey = item->export_drop_subkey;
}
/* Create a new export stats object initialized to zero. On error
returns NULL and sets ERRNO. */
export_stats_t
export_new_stats (void)
{
export_stats_t stats;
return xtrycalloc (1, sizeof *stats);
}
/* Release an export stats object. */
void
export_release_stats (export_stats_t stats)
{
xfree (stats);
}
/* Print export statistics using the status interface. */
void
export_print_stats (export_stats_t stats)
{
if (!stats)
return;
if (is_status_enabled ())
{
char buf[15*20];
snprintf (buf, sizeof buf, "%lu %lu %lu",
stats->count,
stats->secret_count,
stats->exported );
write_status_text (STATUS_EXPORT_RES, buf);
}
}
/*
* Export public keys (to stdout or to --output FILE).
*
* Depending on opt.armor the output is armored. OPTIONS are defined
* in main.h. If USERS is NULL, all keys will be exported. STATS is
* either an export stats object for update or NULL.
*
* This function is the core of "gpg --export".
*/
int
export_pubkeys (ctrl_t ctrl, strlist_t users, unsigned int options,
export_stats_t stats)
{
return do_export (ctrl, users, 0, options, stats);
}
/*
* Export secret keys (to stdout or to --output FILE).
*
* Depending on opt.armor the output is armored. OPTIONS are defined
* in main.h. If USERS is NULL, all secret keys will be exported.
* STATS is either an export stats object for update or NULL.
*
* This function is the core of "gpg --export-secret-keys".
*/
int
export_seckeys (ctrl_t ctrl, strlist_t users, unsigned int options,
export_stats_t stats)
{
return do_export (ctrl, users, 1, options, stats);
}
/*
* Export secret sub keys (to stdout or to --output FILE).
*
* This is the same as export_seckeys but replaces the primary key by
* a stub key. Depending on opt.armor the output is armored. OPTIONS
* are defined in main.h. If USERS is NULL, all secret subkeys will
* be exported. STATS is either an export stats object for update or
* NULL.
*
* This function is the core of "gpg --export-secret-subkeys".
*/
int
export_secsubkeys (ctrl_t ctrl, strlist_t users, unsigned int options,
export_stats_t stats)
{
return do_export (ctrl, users, 2, options, stats);
}
/*
* Export a single key into a memory buffer. STATS is either an
* export stats object for update or NULL. If PREFIX is not NULL
* PREFIXLEN bytes from PREFIX are prepended to the R_DATA.
*/
gpg_error_t
export_pubkey_buffer (ctrl_t ctrl, const char *keyspec, unsigned int options,
const void *prefix, size_t prefixlen,
export_stats_t stats,
kbnode_t *r_keyblock, void **r_data, size_t *r_datalen)
{
gpg_error_t err;
iobuf_t iobuf;
int any;
strlist_t helplist;
*r_keyblock = NULL;
*r_data = NULL;
*r_datalen = 0;
helplist = NULL;
if (!add_to_strlist_try (&helplist, keyspec))
return gpg_error_from_syserror ();
iobuf = iobuf_temp ();
if (prefix && prefixlen)
iobuf_write (iobuf, prefix, prefixlen);
err = do_export_stream (ctrl, iobuf, helplist, 0, r_keyblock, options,
stats, &any);
if (!err && !any)
err = gpg_error (GPG_ERR_NOT_FOUND);
if (!err)
{
const void *src;
size_t datalen;
iobuf_flush_temp (iobuf);
src = iobuf_get_temp_buffer (iobuf);
datalen = iobuf_get_temp_length (iobuf);
if (!datalen)
err = gpg_error (GPG_ERR_NO_PUBKEY);
else if (!(*r_data = xtrymalloc (datalen)))
err = gpg_error_from_syserror ();
else
{
memcpy (*r_data, src, datalen);
*r_datalen = datalen;
}
}
iobuf_close (iobuf);
free_strlist (helplist);
if (err && *r_keyblock)
{
release_kbnode (*r_keyblock);
*r_keyblock = NULL;
}
return err;
}
/* Export the keys identified by the list of strings in USERS. If
Secret is false public keys will be exported. With secret true
secret keys will be exported; in this case 1 means the entire
secret keyblock and 2 only the subkeys. OPTIONS are the export
options to apply. */
static int
do_export (ctrl_t ctrl, strlist_t users, int secret, unsigned int options,
export_stats_t stats)
{
IOBUF out = NULL;
int any, rc;
armor_filter_context_t *afx = NULL;
compress_filter_context_t zfx;
memset( &zfx, 0, sizeof zfx);
rc = open_outfile (-1, NULL, 0, !!secret, &out );
if (rc)
return rc;
if ( opt.armor && !(options & (EXPORT_PKA_FORMAT|EXPORT_DANE_FORMAT)) )
{
afx = new_armor_context ();
afx->what = secret? 5 : 1;
push_armor_filter (afx, out);
}
rc = do_export_stream (ctrl, out, users, secret, NULL, options, stats, &any);
if ( rc || !any )
iobuf_cancel (out);
else
iobuf_close (out);
release_armor_context (afx);
return rc;
}
/* Release an entire subkey list. */
static void
release_subkey_list (subkey_list_t list)
{
while (list)
{
subkey_list_t tmp = list->next;;
xfree (list);
list = tmp;
}
}
/* Returns true if NODE is a subkey and contained in LIST. */
static int
subkey_in_list_p (subkey_list_t list, KBNODE node)
{
if (node->pkt->pkttype == PKT_PUBLIC_SUBKEY
|| node->pkt->pkttype == PKT_SECRET_SUBKEY )
{
u32 kid[2];
keyid_from_pk (node->pkt->pkt.public_key, kid);
for (; list; list = list->next)
if (list->kid[0] == kid[0] && list->kid[1] == kid[1])
return 1;
}
return 0;
}
/* Allocate a new subkey list item from NODE. */
static subkey_list_t
new_subkey_list_item (KBNODE node)
{
subkey_list_t list = xcalloc (1, sizeof *list);
if (node->pkt->pkttype == PKT_PUBLIC_SUBKEY
|| node->pkt->pkttype == PKT_SECRET_SUBKEY)
keyid_from_pk (node->pkt->pkt.public_key, list->kid);
return list;
}
/* Helper function to check whether the subkey at NODE actually
matches the description at DESC. The function returns true if the
key under question has been specified by an exact specification
(keyID or fingerprint) and does match the one at NODE. It is
assumed that the packet at NODE is either a public or secret
subkey. */
int
exact_subkey_match_p (KEYDB_SEARCH_DESC *desc, kbnode_t node)
{
u32 kid[2];
byte fpr[MAX_FINGERPRINT_LEN];
size_t fprlen;
int result = 0;
switch(desc->mode)
{
case KEYDB_SEARCH_MODE_SHORT_KID:
case KEYDB_SEARCH_MODE_LONG_KID:
keyid_from_pk (node->pkt->pkt.public_key, kid);
break;
case KEYDB_SEARCH_MODE_FPR16:
case KEYDB_SEARCH_MODE_FPR20:
case KEYDB_SEARCH_MODE_FPR:
fingerprint_from_pk (node->pkt->pkt.public_key, fpr,&fprlen);
break;
default:
break;
}
switch(desc->mode)
{
case KEYDB_SEARCH_MODE_SHORT_KID:
if (desc->u.kid[1] == kid[1])
result = 1;
break;
case KEYDB_SEARCH_MODE_LONG_KID:
if (desc->u.kid[0] == kid[0] && desc->u.kid[1] == kid[1])
result = 1;
break;
case KEYDB_SEARCH_MODE_FPR16:
if (!memcmp (desc->u.fpr, fpr, 16))
result = 1;
break;
case KEYDB_SEARCH_MODE_FPR20:
case KEYDB_SEARCH_MODE_FPR:
if (!memcmp (desc->u.fpr, fpr, 20))
result = 1;
break;
default:
break;
}
return result;
}
/* Return an error if the key represented by the S-expression S_KEY
* and the OpenPGP key represented by PK do not use the same curve. */
static gpg_error_t
match_curve_skey_pk (gcry_sexp_t s_key, PKT_public_key *pk)
{
gcry_sexp_t curve = NULL;
gcry_sexp_t flags = NULL;
char *curve_str = NULL;
char *flag;
const char *oidstr = NULL;
gcry_mpi_t curve_as_mpi = NULL;
gpg_error_t err;
int is_eddsa = 0;
int idx = 0;
if (!(pk->pubkey_algo==PUBKEY_ALGO_ECDH
|| pk->pubkey_algo==PUBKEY_ALGO_ECDSA
|| pk->pubkey_algo==PUBKEY_ALGO_EDDSA))
return gpg_error (GPG_ERR_PUBKEY_ALGO);
curve = gcry_sexp_find_token (s_key, "curve", 0);
if (!curve)
{
log_error ("no reported curve\n");
return gpg_error (GPG_ERR_UNKNOWN_CURVE);
}
curve_str = gcry_sexp_nth_string (curve, 1);
gcry_sexp_release (curve); curve = NULL;
if (!curve_str)
{
log_error ("no curve name\n");
return gpg_error (GPG_ERR_UNKNOWN_CURVE);
}
oidstr = openpgp_curve_to_oid (curve_str, NULL);
if (!oidstr)
{
log_error ("no OID known for curve '%s'\n", curve_str);
xfree (curve_str);
return gpg_error (GPG_ERR_UNKNOWN_CURVE);
}
xfree (curve_str);
err = openpgp_oid_from_str (oidstr, &curve_as_mpi);
if (err)
return err;
if (gcry_mpi_cmp (pk->pkey[0], curve_as_mpi))
{
log_error ("curves do not match\n");
gcry_mpi_release (curve_as_mpi);
return gpg_error (GPG_ERR_INV_CURVE);
}
gcry_mpi_release (curve_as_mpi);
flags = gcry_sexp_find_token (s_key, "flags", 0);
if (flags)
{
for (idx = 1; idx < gcry_sexp_length (flags); idx++)
{
flag = gcry_sexp_nth_string (flags, idx);
if (flag && (strcmp ("eddsa", flag) == 0))
is_eddsa = 1;
gcry_free (flag);
}
}
if (is_eddsa != (pk->pubkey_algo == PUBKEY_ALGO_EDDSA))
{
log_error ("disagreement about EdDSA\n");
err = gpg_error (GPG_ERR_INV_CURVE);
}
return err;
}
/* Return a canonicalized public key algoithms. This is used to
compare different flavors of algorithms (e.g. ELG and ELG_E are
considered the same). */
static enum gcry_pk_algos
canon_pk_algo (enum gcry_pk_algos algo)
{
switch (algo)
{
case GCRY_PK_RSA:
case GCRY_PK_RSA_E:
case GCRY_PK_RSA_S: return GCRY_PK_RSA;
case GCRY_PK_ELG:
case GCRY_PK_ELG_E: return GCRY_PK_ELG;
case GCRY_PK_ECC:
case GCRY_PK_ECDSA:
case GCRY_PK_ECDH: return GCRY_PK_ECC;
default: return algo;
}
}
/* Take a cleartext dump of a secret key in PK and change the
* parameter array in PK to include the secret parameters. */
static gpg_error_t
cleartext_secret_key_to_openpgp (gcry_sexp_t s_key, PKT_public_key *pk)
{
gpg_error_t err;
gcry_sexp_t top_list;
gcry_sexp_t key = NULL;
char *key_type = NULL;
enum gcry_pk_algos pk_algo;
struct seckey_info *ski;
int idx, sec_start;
gcry_mpi_t pub_params[10] = { NULL };
/* we look for a private-key, then the first element in it tells us
the type */
top_list = gcry_sexp_find_token (s_key, "private-key", 0);
if (!top_list)
goto bad_seckey;
/* ignore all S-expression after the first sublist -- we assume that
they are comments or otherwise irrelevant to OpenPGP */
if (gcry_sexp_length(top_list) < 2)
goto bad_seckey;
key = gcry_sexp_nth (top_list, 1);
if (!key)
goto bad_seckey;
key_type = gcry_sexp_nth_string(key, 0);
pk_algo = gcry_pk_map_name (key_type);
log_assert (!pk->seckey_info);
pk->seckey_info = ski = xtrycalloc (1, sizeof *ski);
if (!ski)
{
err = gpg_error_from_syserror ();
goto leave;
}
switch (canon_pk_algo (pk_algo))
{
case GCRY_PK_RSA:
if (!is_RSA (pk->pubkey_algo))
goto bad_pubkey_algo;
err = gcry_sexp_extract_param (key, NULL, "ne",
&pub_params[0],
&pub_params[1],
NULL);
for (idx=0; idx < 2 && !err; idx++)
if (gcry_mpi_cmp(pk->pkey[idx], pub_params[idx]))
err = gpg_error (GPG_ERR_BAD_PUBKEY);
if (!err)
{
for (idx = 2; idx < 6 && !err; idx++)
{
gcry_mpi_release (pk->pkey[idx]);
pk->pkey[idx] = NULL;
}
err = gcry_sexp_extract_param (key, NULL, "dpqu",
&pk->pkey[2],
&pk->pkey[3],
&pk->pkey[4],
&pk->pkey[5],
NULL);
}
if (!err)
{
for (idx = 2; idx < 6; idx++)
ski->csum += checksum_mpi (pk->pkey[idx]);
}
break;
case GCRY_PK_DSA:
if (!is_DSA (pk->pubkey_algo))
goto bad_pubkey_algo;
err = gcry_sexp_extract_param (key, NULL, "pqgy",
&pub_params[0],
&pub_params[1],
&pub_params[2],
&pub_params[3],
NULL);
for (idx=0; idx < 4 && !err; idx++)
if (gcry_mpi_cmp(pk->pkey[idx], pub_params[idx]))
err = gpg_error (GPG_ERR_BAD_PUBKEY);
if (!err)
{
gcry_mpi_release (pk->pkey[4]);
pk->pkey[4] = NULL;
err = gcry_sexp_extract_param (key, NULL, "x",
&pk->pkey[4],
NULL);
}
if (!err)
ski->csum += checksum_mpi (pk->pkey[4]);
break;
case GCRY_PK_ELG:
if (!is_ELGAMAL (pk->pubkey_algo))
goto bad_pubkey_algo;
err = gcry_sexp_extract_param (key, NULL, "pgy",
&pub_params[0],
&pub_params[1],
&pub_params[2],
NULL);
for (idx=0; idx < 3 && !err; idx++)
if (gcry_mpi_cmp(pk->pkey[idx], pub_params[idx]))
err = gpg_error (GPG_ERR_BAD_PUBKEY);
if (!err)
{
gcry_mpi_release (pk->pkey[3]);
pk->pkey[3] = NULL;
err = gcry_sexp_extract_param (key, NULL, "x",
&pk->pkey[3],
NULL);
}
if (!err)
ski->csum += checksum_mpi (pk->pkey[3]);
break;
case GCRY_PK_ECC:
err = match_curve_skey_pk (key, pk);
if (err)
goto leave;
if (!err)
err = gcry_sexp_extract_param (key, NULL, "q",
&pub_params[0],
NULL);
if (!err && (gcry_mpi_cmp(pk->pkey[1], pub_params[0])))
err = gpg_error (GPG_ERR_BAD_PUBKEY);
sec_start = 2;
if (pk->pubkey_algo == PUBKEY_ALGO_ECDH)
sec_start += 1;
if (!err)
{
gcry_mpi_release (pk->pkey[sec_start]);
pk->pkey[sec_start] = NULL;
err = gcry_sexp_extract_param (key, NULL, "d",
&pk->pkey[sec_start],
NULL);
}
if (!err)
ski->csum += checksum_mpi (pk->pkey[sec_start]);
break;
default:
pk->seckey_info = NULL;
xfree (ski);
err = gpg_error (GPG_ERR_NOT_IMPLEMENTED);
break;
}
leave:
gcry_sexp_release (top_list);
gcry_sexp_release (key);
gcry_free (key_type);
for (idx=0; idx < DIM(pub_params); idx++)
gcry_mpi_release (pub_params[idx]);
return err;
bad_pubkey_algo:
err = gpg_error (GPG_ERR_PUBKEY_ALGO);
goto leave;
bad_seckey:
err = gpg_error (GPG_ERR_BAD_SECKEY);
goto leave;
}
/* Use the key transfer format given in S_PGP to create the secinfo
structure in PK and change the parameter array in PK to include the
secret parameters. */
static gpg_error_t
transfer_format_to_openpgp (gcry_sexp_t s_pgp, PKT_public_key *pk)
{
gpg_error_t err;
gcry_sexp_t top_list;
gcry_sexp_t list = NULL;
char *curve = NULL;
const char *value;
size_t valuelen;
char *string;
int idx;
int is_v4, is_protected;
enum gcry_pk_algos pk_algo;
int protect_algo = 0;
char iv[16];
int ivlen = 0;
int s2k_mode = 0;
int s2k_algo = 0;
byte s2k_salt[8];
u32 s2k_count = 0;
int is_ecdh = 0;
size_t npkey, nskey;
gcry_mpi_t skey[10]; /* We support up to 9 parameters. */
int skeyidx = 0;
struct seckey_info *ski;
/* gcry_log_debugsxp ("transferkey", s_pgp); */
top_list = gcry_sexp_find_token (s_pgp, "openpgp-private-key", 0);
if (!top_list)
goto bad_seckey;
list = gcry_sexp_find_token (top_list, "version", 0);
if (!list)
goto bad_seckey;
value = gcry_sexp_nth_data (list, 1, &valuelen);
if (!value || valuelen != 1 || !(value[0] == '3' || value[0] == '4'))
goto bad_seckey;
is_v4 = (value[0] == '4');
gcry_sexp_release (list);
list = gcry_sexp_find_token (top_list, "protection", 0);
if (!list)
goto bad_seckey;
value = gcry_sexp_nth_data (list, 1, &valuelen);
if (!value)
goto bad_seckey;
if (valuelen == 4 && !memcmp (value, "sha1", 4))
is_protected = 2;
else if (valuelen == 3 && !memcmp (value, "sum", 3))
is_protected = 1;
else if (valuelen == 4 && !memcmp (value, "none", 4))
is_protected = 0;
else
goto bad_seckey;
if (is_protected)
{
string = gcry_sexp_nth_string (list, 2);
if (!string)
goto bad_seckey;
protect_algo = gcry_cipher_map_name (string);
xfree (string);
value = gcry_sexp_nth_data (list, 3, &valuelen);
if (!value || !valuelen || valuelen > sizeof iv)
goto bad_seckey;
memcpy (iv, value, valuelen);
ivlen = valuelen;
string = gcry_sexp_nth_string (list, 4);
if (!string)
goto bad_seckey;
s2k_mode = strtol (string, NULL, 10);
xfree (string);
string = gcry_sexp_nth_string (list, 5);
if (!string)
goto bad_seckey;
s2k_algo = gcry_md_map_name (string);
xfree (string);
value = gcry_sexp_nth_data (list, 6, &valuelen);
if (!value || !valuelen || valuelen > sizeof s2k_salt)
goto bad_seckey;
memcpy (s2k_salt, value, valuelen);
string = gcry_sexp_nth_string (list, 7);
if (!string)
goto bad_seckey;
s2k_count = strtoul (string, NULL, 10);
xfree (string);
}
/* Parse the gcrypt PK algo and check that it is okay. */
gcry_sexp_release (list);
list = gcry_sexp_find_token (top_list, "algo", 0);
if (!list)
goto bad_seckey;
string = gcry_sexp_nth_string (list, 1);
if (!string)
goto bad_seckey;
pk_algo = gcry_pk_map_name (string);
xfree (string); string = NULL;
if (gcry_pk_algo_info (pk_algo, GCRYCTL_GET_ALGO_NPKEY, NULL, &npkey)
|| gcry_pk_algo_info (pk_algo, GCRYCTL_GET_ALGO_NSKEY, NULL, &nskey)
|| !npkey || npkey >= nskey)
goto bad_seckey;
/* Check that the pubkey algo matches the one from the public key. */
switch (canon_pk_algo (pk_algo))
{
case GCRY_PK_RSA:
if (!is_RSA (pk->pubkey_algo))
pk_algo = 0; /* Does not match. */
break;
case GCRY_PK_DSA:
if (!is_DSA (pk->pubkey_algo))
pk_algo = 0; /* Does not match. */
break;
case GCRY_PK_ELG:
if (!is_ELGAMAL (pk->pubkey_algo))
pk_algo = 0; /* Does not match. */
break;
case GCRY_PK_ECC:
if (pk->pubkey_algo == PUBKEY_ALGO_ECDSA)
;
else if (pk->pubkey_algo == PUBKEY_ALGO_ECDH)
is_ecdh = 1;
else if (pk->pubkey_algo == PUBKEY_ALGO_EDDSA)
;
else
pk_algo = 0; /* Does not match. */
/* For ECC we do not have the domain parameters thus fix our info. */
npkey = 1;
nskey = 2;
break;
default:
pk_algo = 0; /* Oops. */
break;
}
if (!pk_algo)
{
err = gpg_error (GPG_ERR_PUBKEY_ALGO);
goto leave;
}
/* This check has to go after the ecc adjustments. */
if (nskey > PUBKEY_MAX_NSKEY)
goto bad_seckey;
/* Parse the key parameters. */
gcry_sexp_release (list);
list = gcry_sexp_find_token (top_list, "skey", 0);
if (!list)
goto bad_seckey;
for (idx=0;;)
{
int is_enc;
value = gcry_sexp_nth_data (list, ++idx, &valuelen);
if (!value && skeyidx >= npkey)
break; /* Ready. */
/* Check for too many parameters. Note that depending on the
protection mode and version number we may see less than NSKEY
(but at least NPKEY+1) parameters. */
if (idx >= 2*nskey)
goto bad_seckey;
if (skeyidx >= DIM (skey)-1)
goto bad_seckey;
if (!value || valuelen != 1 || !(value[0] == '_' || value[0] == 'e'))
goto bad_seckey;
is_enc = (value[0] == 'e');
value = gcry_sexp_nth_data (list, ++idx, &valuelen);
if (!value || !valuelen)
goto bad_seckey;
if (is_enc)
{
void *p = xtrymalloc (valuelen);
if (!p)
goto outofmem;
memcpy (p, value, valuelen);
skey[skeyidx] = gcry_mpi_set_opaque (NULL, p, valuelen*8);
if (!skey[skeyidx])
goto outofmem;
}
else
{
if (gcry_mpi_scan (skey + skeyidx, GCRYMPI_FMT_STD,
value, valuelen, NULL))
goto bad_seckey;
}
skeyidx++;
}
skey[skeyidx++] = NULL;
gcry_sexp_release (list); list = NULL;
/* We have no need for the CSUM value thus we don't parse it. */
/* list = gcry_sexp_find_token (top_list, "csum", 0); */
/* if (list) */
/* { */
/* string = gcry_sexp_nth_string (list, 1); */
/* if (!string) */
/* goto bad_seckey; */
/* desired_csum = strtoul (string, NULL, 10); */
/* xfree (string); */
/* } */
/* else */
/* desired_csum = 0; */
/* gcry_sexp_release (list); list = NULL; */
/* Get the curve name if any, */
list = gcry_sexp_find_token (top_list, "curve", 0);
if (list)
{
curve = gcry_sexp_nth_string (list, 1);
gcry_sexp_release (list); list = NULL;
}
gcry_sexp_release (top_list); top_list = NULL;
/* log_debug ("XXX is_v4=%d\n", is_v4); */
/* log_debug ("XXX pubkey_algo=%d\n", pubkey_algo); */
/* log_debug ("XXX is_protected=%d\n", is_protected); */
/* log_debug ("XXX protect_algo=%d\n", protect_algo); */
- /* log_printhex ("XXX iv", iv, ivlen); */
+ /* log_printhex (iv, ivlen, "XXX iv"); */
/* log_debug ("XXX ivlen=%d\n", ivlen); */
/* log_debug ("XXX s2k_mode=%d\n", s2k_mode); */
/* log_debug ("XXX s2k_algo=%d\n", s2k_algo); */
- /* log_printhex ("XXX s2k_salt", s2k_salt, sizeof s2k_salt); */
+ /* log_printhex (s2k_salt, sizeof s2k_salt, "XXX s2k_salt"); */
/* log_debug ("XXX s2k_count=%lu\n", (unsigned long)s2k_count); */
/* for (idx=0; skey[idx]; idx++) */
/* { */
/* int is_enc = gcry_mpi_get_flag (skey[idx], GCRYMPI_FLAG_OPAQUE); */
/* log_info ("XXX skey[%d]%s:", idx, is_enc? " (enc)":""); */
/* if (is_enc) */
/* { */
/* void *p; */
/* unsigned int nbits; */
/* p = gcry_mpi_get_opaque (skey[idx], &nbits); */
- /* log_printhex (NULL, p, (nbits+7)/8); */
+ /* log_printhex ( p, (nbits+7)/8, NULL); */
/* } */
/* else */
/* gcry_mpi_dump (skey[idx]); */
/* log_printf ("\n"); */
/* } */
if (!is_v4 || is_protected != 2 )
{
/* We only support the v4 format and a SHA-1 checksum. */
err = gpg_error (GPG_ERR_NOT_IMPLEMENTED);
goto leave;
}
/* We need to change the received parameters for ECC algorithms.
The transfer format has the curve name and the parameters
separate. We put them all into the SKEY array. */
if (canon_pk_algo (pk_algo) == GCRY_PK_ECC)
{
const char *oidstr;
/* Assert that all required parameters are available. We also
check that the array does not contain more parameters than
needed (this was used by some beta versions of 2.1. */
if (!curve || !skey[0] || !skey[1] || skey[2])
{
err = gpg_error (GPG_ERR_INTERNAL);
goto leave;
}
oidstr = openpgp_curve_to_oid (curve, NULL);
if (!oidstr)
{
log_error ("no OID known for curve '%s'\n", curve);
err = gpg_error (GPG_ERR_UNKNOWN_CURVE);
goto leave;
}
/* Put the curve's OID into the MPI array. This requires
that we shift Q and D. For ECDH also insert the KDF parms. */
if (is_ecdh)
{
skey[4] = NULL;
skey[3] = skey[1];
skey[2] = gcry_mpi_copy (pk->pkey[2]);
}
else
{
skey[3] = NULL;
skey[2] = skey[1];
}
skey[1] = skey[0];
skey[0] = NULL;
err = openpgp_oid_from_str (oidstr, skey + 0);
if (err)
goto leave;
/* Fixup the NPKEY and NSKEY to match OpenPGP reality. */
npkey = 2 + is_ecdh;
nskey = 3 + is_ecdh;
/* for (idx=0; skey[idx]; idx++) */
/* { */
/* log_info ("YYY skey[%d]:", idx); */
/* if (gcry_mpi_get_flag (skey[idx], GCRYMPI_FLAG_OPAQUE)) */
/* { */
/* void *p; */
/* unsigned int nbits; */
/* p = gcry_mpi_get_opaque (skey[idx], &nbits); */
- /* log_printhex (NULL, p, (nbits+7)/8); */
+ /* log_printhex (p, (nbits+7)/8, NULL); */
/* } */
/* else */
/* gcry_mpi_dump (skey[idx]); */
/* log_printf ("\n"); */
/* } */
}
/* Do some sanity checks. */
if (s2k_count > 255)
{
/* We expect an already encoded S2K count. */
err = gpg_error (GPG_ERR_INV_DATA);
goto leave;
}
err = openpgp_cipher_test_algo (protect_algo);
if (err)
goto leave;
err = openpgp_md_test_algo (s2k_algo);
if (err)
goto leave;
/* Check that the public key parameters match. Note that since
Libgcrypt 1.5 gcry_mpi_cmp handles opaque MPI correctly. */
for (idx=0; idx < npkey; idx++)
if (gcry_mpi_cmp (pk->pkey[idx], skey[idx]))
{
err = gpg_error (GPG_ERR_BAD_PUBKEY);
goto leave;
}
/* Check that the first secret key parameter in SKEY is encrypted
and that there are no more secret key parameters. The latter is
guaranteed by the v4 packet format. */
if (!gcry_mpi_get_flag (skey[npkey], GCRYMPI_FLAG_OPAQUE))
goto bad_seckey;
if (npkey+1 < DIM (skey) && skey[npkey+1])
goto bad_seckey;
/* Check that the secret key parameters in PK are all set to NULL. */
for (idx=npkey; idx < nskey; idx++)
if (pk->pkey[idx])
goto bad_seckey;
/* Now build the protection info. */
pk->seckey_info = ski = xtrycalloc (1, sizeof *ski);
if (!ski)
{
err = gpg_error_from_syserror ();
goto leave;
}
ski->is_protected = 1;
ski->sha1chk = 1;
ski->algo = protect_algo;
ski->s2k.mode = s2k_mode;
ski->s2k.hash_algo = s2k_algo;
log_assert (sizeof ski->s2k.salt == sizeof s2k_salt);
memcpy (ski->s2k.salt, s2k_salt, sizeof s2k_salt);
ski->s2k.count = s2k_count;
log_assert (ivlen <= sizeof ski->iv);
memcpy (ski->iv, iv, ivlen);
ski->ivlen = ivlen;
/* Store the protected secret key parameter. */
pk->pkey[npkey] = skey[npkey];
skey[npkey] = NULL;
/* That's it. */
leave:
gcry_free (curve);
gcry_sexp_release (list);
gcry_sexp_release (top_list);
for (idx=0; idx < skeyidx; idx++)
gcry_mpi_release (skey[idx]);
return err;
bad_seckey:
err = gpg_error (GPG_ERR_BAD_SECKEY);
goto leave;
outofmem:
err = gpg_error (GPG_ERR_ENOMEM);
goto leave;
}
/* Print an "EXPORTED" status line. PK is the primary public key. */
static void
print_status_exported (PKT_public_key *pk)
{
char *hexfpr;
if (!is_status_enabled ())
return;
hexfpr = hexfingerprint (pk, NULL, 0);
write_status_text (STATUS_EXPORTED, hexfpr? hexfpr : "[?]");
xfree (hexfpr);
}
/*
* Receive a secret key from agent specified by HEXGRIP.
*
* Since the key data from the agent is encrypted, decrypt it using
* CIPHERHD context. Then, parse the decrypted key data into transfer
* format, and put secret parameters into PK.
*
* If CLEARTEXT is 0, store the secret key material
* passphrase-protected. Otherwise, store secret key material in the
* clear.
*
* CACHE_NONCE_ADDR is used to share nonce for multple key retrievals.
*/
gpg_error_t
receive_seckey_from_agent (ctrl_t ctrl, gcry_cipher_hd_t cipherhd,
int cleartext,
char **cache_nonce_addr, const char *hexgrip,
PKT_public_key *pk)
{
gpg_error_t err = 0;
unsigned char *wrappedkey = NULL;
size_t wrappedkeylen;
unsigned char *key = NULL;
size_t keylen, realkeylen;
gcry_sexp_t s_skey;
char *prompt;
if (opt.verbose)
log_info ("key %s: asking agent for the secret parts\n", hexgrip);
prompt = gpg_format_keydesc (ctrl, pk, FORMAT_KEYDESC_EXPORT,1);
err = agent_export_key (ctrl, hexgrip, prompt, !cleartext, cache_nonce_addr,
&wrappedkey, &wrappedkeylen,
pk->keyid, pk->main_keyid, pk->pubkey_algo);
xfree (prompt);
if (err)
goto unwraperror;
if (wrappedkeylen < 24)
{
err = gpg_error (GPG_ERR_INV_LENGTH);
goto unwraperror;
}
keylen = wrappedkeylen - 8;
key = xtrymalloc_secure (keylen);
if (!key)
{
err = gpg_error_from_syserror ();
goto unwraperror;
}
err = gcry_cipher_decrypt (cipherhd, key, keylen, wrappedkey, wrappedkeylen);
if (err)
goto unwraperror;
realkeylen = gcry_sexp_canon_len (key, keylen, NULL, &err);
if (!realkeylen)
goto unwraperror; /* Invalid csexp. */
err = gcry_sexp_sscan (&s_skey, NULL, key, realkeylen);
if (!err)
{
if (cleartext)
err = cleartext_secret_key_to_openpgp (s_skey, pk);
else
err = transfer_format_to_openpgp (s_skey, pk);
gcry_sexp_release (s_skey);
}
unwraperror:
xfree (key);
xfree (wrappedkey);
if (err)
{
log_error ("key %s: error receiving key from agent:"
" %s%s\n", hexgrip, gpg_strerror (err),
gpg_err_code (err) == GPG_ERR_FULLY_CANCELED?
"":_(" - skipped"));
}
return err;
}
/* Write KEYBLOCK either to stdout or to the file set with the
* --output option. This is a simplified version of do_export_stream
* which supports only a few export options. */
gpg_error_t
write_keyblock_to_output (kbnode_t keyblock, int with_armor,
unsigned int options)
{
gpg_error_t err;
const char *fname;
iobuf_t out;
kbnode_t node;
armor_filter_context_t *afx = NULL;
iobuf_t out_help = NULL;
PKT_public_key *pk = NULL;
fname = opt.outfile? opt.outfile : "-";
if (is_secured_filename (fname) )
return gpg_error (GPG_ERR_EPERM);
out = iobuf_create (fname, 0);
if (!out)
{
err = gpg_error_from_syserror ();
log_error(_("can't create '%s': %s\n"), fname, gpg_strerror (err));
return err;
}
if (opt.verbose)
log_info (_("writing to '%s'\n"), iobuf_get_fname_nonnull (out));
if ((options & (EXPORT_PKA_FORMAT|EXPORT_DANE_FORMAT)))
{
with_armor = 0;
out_help = iobuf_temp ();
}
if (with_armor)
{
afx = new_armor_context ();
afx->what = 1;
push_armor_filter (afx, out);
}
for (node = keyblock; node; node = node->next)
{
if (is_deleted_kbnode (node))
continue;
if (node->pkt->pkttype == PKT_RING_TRUST)
continue; /* Skip - they should not be here anyway. */
if (!pk && (node->pkt->pkttype == PKT_PUBLIC_KEY
|| node->pkt->pkttype == PKT_SECRET_KEY))
pk = node->pkt->pkt.public_key;
if ((options & EXPORT_BACKUP))
err = build_packet_and_meta (out_help? out_help : out, node->pkt);
else
err = build_packet (out_help? out_help : out, node->pkt);
if (err)
{
log_error ("build_packet(%d) failed: %s\n",
node->pkt->pkttype, gpg_strerror (err) );
goto leave;
}
}
err = 0;
if (out_help && pk)
{
const void *data;
size_t datalen;
iobuf_flush_temp (out_help);
data = iobuf_get_temp_buffer (out_help);
datalen = iobuf_get_temp_length (out_help);
err = print_pka_or_dane_records (out,
keyblock, pk, data, datalen,
(options & EXPORT_PKA_FORMAT),
(options & EXPORT_DANE_FORMAT));
}
leave:
if (err)
iobuf_cancel (out);
else
iobuf_close (out);
iobuf_cancel (out_help);
release_armor_context (afx);
return err;
}
/*
* Apply the keep-uid filter to the keyblock. The deleted nodes are
* marked and thus the caller should call commit_kbnode afterwards.
* KEYBLOCK must not have any blocks marked as deleted.
*/
static void
apply_keep_uid_filter (ctrl_t ctrl, kbnode_t keyblock, recsel_expr_t selector)
{
kbnode_t node;
struct impex_filter_parm_s parm;
parm.ctrl = ctrl;
for (node = keyblock->next; node; node = node->next )
{
if (node->pkt->pkttype == PKT_USER_ID)
{
parm.node = node;
if (!recsel_select (selector, impex_filter_getval, &parm))
{
/* log_debug ("keep-uid: deleting '%s'\n", */
/* node->pkt->pkt.user_id->name); */
/* The UID packet and all following packets up to the
* next UID or a subkey. */
delete_kbnode (node);
for (; node->next
&& node->next->pkt->pkttype != PKT_USER_ID
&& node->next->pkt->pkttype != PKT_PUBLIC_SUBKEY
&& node->next->pkt->pkttype != PKT_SECRET_SUBKEY ;
node = node->next)
delete_kbnode (node->next);
}
/* else */
/* log_debug ("keep-uid: keeping '%s'\n", */
/* node->pkt->pkt.user_id->name); */
}
}
}
/*
* Apply the drop-subkey filter to the keyblock. The deleted nodes are
* marked and thus the caller should call commit_kbnode afterwards.
* KEYBLOCK must not have any blocks marked as deleted.
*/
static void
apply_drop_subkey_filter (ctrl_t ctrl, kbnode_t keyblock,
recsel_expr_t selector)
{
kbnode_t node;
struct impex_filter_parm_s parm;
parm.ctrl = ctrl;
for (node = keyblock->next; node; node = node->next )
{
if (node->pkt->pkttype == PKT_PUBLIC_SUBKEY
|| node->pkt->pkttype == PKT_SECRET_SUBKEY)
{
parm.node = node;
if (recsel_select (selector, impex_filter_getval, &parm))
{
/*log_debug ("drop-subkey: deleting a key\n");*/
/* The subkey packet and all following packets up to the
* next subkey. */
delete_kbnode (node);
for (; node->next
&& node->next->pkt->pkttype != PKT_PUBLIC_SUBKEY
&& node->next->pkt->pkttype != PKT_SECRET_SUBKEY ;
node = node->next)
delete_kbnode (node->next);
}
}
}
}
/* Print DANE or PKA records for all user IDs in KEYBLOCK to OUT. The
* data for the record is taken from (DATA,DATELEN). PK is the public
* key packet with the primary key. */
static gpg_error_t
print_pka_or_dane_records (iobuf_t out, kbnode_t keyblock, PKT_public_key *pk,
const void *data, size_t datalen,
int print_pka, int print_dane)
{
gpg_error_t err = 0;
kbnode_t kbctx, node;
PKT_user_id *uid;
char *mbox = NULL;
char hashbuf[32];
char *hash = NULL;
char *domain;
const char *s;
unsigned int len;
estream_t fp = NULL;
char *hexdata = NULL;
char *hexfpr;
hexfpr = hexfingerprint (pk, NULL, 0);
if (!hexfpr)
{
err = gpg_error_from_syserror ();
goto leave;
}
hexdata = bin2hex (data, datalen, NULL);
if (!hexdata)
{
err = gpg_error_from_syserror ();
goto leave;
}
ascii_strlwr (hexdata);
fp = es_fopenmem (0, "rw,samethread");
if (!fp)
{
err = gpg_error_from_syserror ();
goto leave;
}
for (kbctx = NULL; (node = walk_kbnode (keyblock, &kbctx, 0));)
{
if (node->pkt->pkttype != PKT_USER_ID)
continue;
uid = node->pkt->pkt.user_id;
if (uid->flags.expired || uid->flags.revoked)
continue;
xfree (mbox);
mbox = mailbox_from_userid (uid->name);
if (!mbox)
continue;
domain = strchr (mbox, '@');
*domain++ = 0;
if (print_pka)
{
es_fprintf (fp, "$ORIGIN _pka.%s.\n; %s\n; ", domain, hexfpr);
print_utf8_buffer (fp, uid->name, uid->len);
es_putc ('\n', fp);
gcry_md_hash_buffer (GCRY_MD_SHA1, hashbuf, mbox, strlen (mbox));
xfree (hash);
hash = zb32_encode (hashbuf, 8*20);
if (!hash)
{
err = gpg_error_from_syserror ();
goto leave;
}
len = strlen (hexfpr)/2;
es_fprintf (fp, "%s TYPE37 \\# %u 0006 0000 00 %02X %s\n\n",
hash, 6 + len, len, hexfpr);
}
if (print_dane && hexdata)
{
es_fprintf (fp, "$ORIGIN _openpgpkey.%s.\n; %s\n; ", domain, hexfpr);
print_utf8_buffer (fp, uid->name, uid->len);
es_putc ('\n', fp);
gcry_md_hash_buffer (GCRY_MD_SHA256, hashbuf, mbox, strlen (mbox));
xfree (hash);
hash = bin2hex (hashbuf, 28, NULL);
if (!hash)
{
err = gpg_error_from_syserror ();
goto leave;
}
ascii_strlwr (hash);
len = strlen (hexdata)/2;
es_fprintf (fp, "%s TYPE61 \\# %u (\n", hash, len);
for (s = hexdata; ;)
{
es_fprintf (fp, "\t%.64s\n", s);
if (strlen (s) < 64)
break;
s += 64;
}
es_fputs ("\t)\n\n", fp);
}
}
/* Make sure it is a string and write it. */
es_fputc (0, fp);
{
void *vp;
if (es_fclose_snatch (fp, &vp, NULL))
{
err = gpg_error_from_syserror ();
goto leave;
}
fp = NULL;
iobuf_writestr (out, vp);
es_free (vp);
}
err = 0;
leave:
xfree (hash);
xfree (mbox);
es_fclose (fp);
xfree (hexdata);
xfree (hexfpr);
return err;
}
/* Helper for do_export_stream which writes one keyblock to OUT. */
static gpg_error_t
do_export_one_keyblock (ctrl_t ctrl, kbnode_t keyblock, u32 *keyid,
iobuf_t out, int secret, unsigned int options,
export_stats_t stats, int *any,
KEYDB_SEARCH_DESC *desc, size_t ndesc,
size_t descindex, gcry_cipher_hd_t cipherhd)
{
gpg_error_t err = gpg_error (GPG_ERR_NOT_FOUND);
char *cache_nonce = NULL;
subkey_list_t subkey_list = NULL; /* Track already processed subkeys. */
int skip_until_subkey = 0;
int cleartext = 0;
char *hexgrip = NULL;
char *serialno = NULL;
PKT_public_key *pk;
u32 subkidbuf[2], *subkid;
kbnode_t kbctx, node;
/* NB: walk_kbnode skips packets marked as deleted. */
for (kbctx=NULL; (node = walk_kbnode (keyblock, &kbctx, 0)); )
{
if (skip_until_subkey)
{
if (node->pkt->pkttype == PKT_PUBLIC_SUBKEY)
skip_until_subkey = 0;
else
continue;
}
/* We used to use comment packets, but not any longer. In
* case we still have comments on a key, strip them here
* before we call build_packet(). */
if (node->pkt->pkttype == PKT_COMMENT)
continue;
/* Skip ring trust packets - they should not ne here anyway. */
if (node->pkt->pkttype == PKT_RING_TRUST)
continue;
/* If exact is set, then we only export what was requested
* (plus the primary key, if the user didn't specifically
* request it). */
if (desc[descindex].exact && node->pkt->pkttype == PKT_PUBLIC_SUBKEY)
{
if (!exact_subkey_match_p (desc+descindex, node))
{
/* Before skipping this subkey, check whether any
* other description wants an exact match on a
* subkey and include that subkey into the output
* too. Need to add this subkey to a list so that
* it won't get processed a second time.
*
* So the first step here is to check that list and
* skip in any case if the key is in that list.
*
* We need this whole mess because the import
* function of GnuPG < 2.1 is not able to merge
* secret keys and thus it is useless to output them
* as two separate keys and have import merge them.
*/
if (subkey_in_list_p (subkey_list, node))
skip_until_subkey = 1; /* Already processed this one. */
else
{
size_t j;
for (j=0; j < ndesc; j++)
if (j != descindex && desc[j].exact
&& exact_subkey_match_p (desc+j, node))
break;
if (!(j < ndesc))
skip_until_subkey = 1; /* No other one matching. */
}
}
if (skip_until_subkey)
continue;
/* Mark this one as processed. */
{
subkey_list_t tmp = new_subkey_list_item (node);
tmp->next = subkey_list;
subkey_list = tmp;
}
}
if (node->pkt->pkttype == PKT_SIGNATURE)
{
/* Do not export packets which are marked as not
* exportable. */
if (!(options & EXPORT_LOCAL_SIGS)
&& !node->pkt->pkt.signature->flags.exportable)
continue; /* not exportable */
/* Do not export packets with a "sensitive" revocation key
* unless the user wants us to. Note that we do export
* these when issuing the actual revocation (see revoke.c). */
if (!(options & EXPORT_SENSITIVE_REVKEYS)
&& node->pkt->pkt.signature->revkey)
{
int i;
for (i = 0; i < node->pkt->pkt.signature->numrevkeys; i++)
if ((node->pkt->pkt.signature->revkey[i].class & 0x40))
break;
if (i < node->pkt->pkt.signature->numrevkeys)
continue;
}
}
/* Don't export attribs? */
if (!(options & EXPORT_ATTRIBUTES)
&& node->pkt->pkttype == PKT_USER_ID
&& node->pkt->pkt.user_id->attrib_data)
{
/* Skip until we get to something that is not an attrib or a
* signature on an attrib. */
while (kbctx->next && kbctx->next->pkt->pkttype == PKT_SIGNATURE)
kbctx = kbctx->next;
continue;
}
if (secret && (node->pkt->pkttype == PKT_PUBLIC_KEY
|| node->pkt->pkttype == PKT_PUBLIC_SUBKEY))
{
pk = node->pkt->pkt.public_key;
if (node->pkt->pkttype == PKT_PUBLIC_KEY)
subkid = NULL;
else
{
keyid_from_pk (pk, subkidbuf);
subkid = subkidbuf;
}
if (pk->seckey_info)
{
log_error ("key %s: oops: seckey_info already set"
" - skipped\n", keystr_with_sub (keyid, subkid));
skip_until_subkey = 1;
continue;
}
xfree (hexgrip);
err = hexkeygrip_from_pk (pk, &hexgrip);
if (err)
{
log_error ("key %s: error computing keygrip: %s"
" - skipped\n", keystr_with_sub (keyid, subkid),
gpg_strerror (err));
skip_until_subkey = 1;
err = 0;
continue;
}
xfree (serialno);
serialno = NULL;
if (secret == 2 && node->pkt->pkttype == PKT_PUBLIC_KEY)
{
/* We are asked not to export the secret parts of the
* primary key. Make up an error code to create the
* stub. */
err = GPG_ERR_NOT_FOUND;
}
else
err = agent_get_keyinfo (ctrl, hexgrip, &serialno, &cleartext);
if ((!err && serialno)
&& secret == 2 && node->pkt->pkttype == PKT_PUBLIC_KEY)
{
/* It does not make sense to export a key with its
* primary key on card using a non-key stub. Thus we
* skip those keys when used with --export-secret-subkeys. */
log_info (_("key %s: key material on-card - skipped\n"),
keystr_with_sub (keyid, subkid));
skip_until_subkey = 1;
}
else if (gpg_err_code (err) == GPG_ERR_NOT_FOUND
|| (!err && serialno))
{
/* Create a key stub. */
struct seckey_info *ski;
const char *s;
pk->seckey_info = ski = xtrycalloc (1, sizeof *ski);
if (!ski)
{
err = gpg_error_from_syserror ();
goto leave;
}
ski->is_protected = 1;
if (err)
ski->s2k.mode = 1001; /* GNU dummy (no secret key). */
else
{
ski->s2k.mode = 1002; /* GNU-divert-to-card. */
for (s=serialno; sizeof (ski->ivlen) && *s && s[1];
ski->ivlen++, s += 2)
ski->iv[ski->ivlen] = xtoi_2 (s);
}
if ((options & EXPORT_BACKUP))
err = build_packet_and_meta (out, node->pkt);
else
err = build_packet (out, node->pkt);
if (!err && node->pkt->pkttype == PKT_PUBLIC_KEY)
{
stats->exported++;
print_status_exported (node->pkt->pkt.public_key);
}
}
else if (!err)
{
err = receive_seckey_from_agent (ctrl, cipherhd,
cleartext, &cache_nonce,
hexgrip, pk);
if (err)
{
if (gpg_err_code (err) == GPG_ERR_FULLY_CANCELED)
goto leave;
skip_until_subkey = 1;
err = 0;
}
else
{
if ((options & EXPORT_BACKUP))
err = build_packet_and_meta (out, node->pkt);
else
err = build_packet (out, node->pkt);
if (node->pkt->pkttype == PKT_PUBLIC_KEY)
{
stats->exported++;
print_status_exported (node->pkt->pkt.public_key);
}
}
}
else
{
log_error ("key %s: error getting keyinfo from agent: %s"
" - skipped\n", keystr_with_sub (keyid, subkid),
gpg_strerror (err));
skip_until_subkey = 1;
err = 0;
}
xfree (pk->seckey_info);
pk->seckey_info = NULL;
{
int i;
for (i = pubkey_get_npkey (pk->pubkey_algo);
i < pubkey_get_nskey (pk->pubkey_algo); i++)
{
gcry_mpi_release (pk->pkey[i]);
pk->pkey[i] = NULL;
}
}
}
else /* Not secret or common packets. */
{
if ((options & EXPORT_BACKUP))
err = build_packet_and_meta (out, node->pkt);
else
err = build_packet (out, node->pkt);
if (!err && node->pkt->pkttype == PKT_PUBLIC_KEY)
{
stats->exported++;
print_status_exported (node->pkt->pkt.public_key);
}
}
if (err)
{
log_error ("build_packet(%d) failed: %s\n",
node->pkt->pkttype, gpg_strerror (err));
goto leave;
}
if (!skip_until_subkey)
*any = 1;
}
leave:
release_subkey_list (subkey_list);
xfree (serialno);
xfree (hexgrip);
xfree (cache_nonce);
return err;
}
/* Export the keys identified by the list of strings in USERS to the
stream OUT. If SECRET is false public keys will be exported. With
secret true secret keys will be exported; in this case 1 means the
entire secret keyblock and 2 only the subkeys. OPTIONS are the
export options to apply. If KEYBLOCK_OUT is not NULL, AND the exit
code is zero, a pointer to the first keyblock found and exported
will be stored at this address; no other keyblocks are exported in
this case. The caller must free the returned keyblock. If any
key has been exported true is stored at ANY. */
static int
do_export_stream (ctrl_t ctrl, iobuf_t out, strlist_t users, int secret,
kbnode_t *keyblock_out, unsigned int options,
export_stats_t stats, int *any)
{
gpg_error_t err = 0;
PACKET pkt;
kbnode_t keyblock = NULL;
kbnode_t node;
size_t ndesc, descindex;
KEYDB_SEARCH_DESC *desc = NULL;
KEYDB_HANDLE kdbhd;
strlist_t sl;
gcry_cipher_hd_t cipherhd = NULL;
struct export_stats_s dummystats;
iobuf_t out_help = NULL;
if (!stats)
stats = &dummystats;
*any = 0;
init_packet (&pkt);
kdbhd = keydb_new ();
if (!kdbhd)
return gpg_error_from_syserror ();
/* For the PKA and DANE format open a helper iobuf and for DANE
* enforce some options. */
if ((options & (EXPORT_PKA_FORMAT | EXPORT_DANE_FORMAT)))
{
out_help = iobuf_temp ();
if ((options & EXPORT_DANE_FORMAT))
options |= EXPORT_MINIMAL | EXPORT_CLEAN;
}
if (!users)
{
ndesc = 1;
desc = xcalloc (ndesc, sizeof *desc);
desc[0].mode = KEYDB_SEARCH_MODE_FIRST;
}
else
{
for (ndesc=0, sl=users; sl; sl = sl->next, ndesc++)
;
desc = xmalloc ( ndesc * sizeof *desc);
for (ndesc=0, sl=users; sl; sl = sl->next)
{
if (!(err=classify_user_id (sl->d, desc+ndesc, 1)))
ndesc++;
else
log_error (_("key \"%s\" not found: %s\n"),
sl->d, gpg_strerror (err));
}
keydb_disable_caching (kdbhd); /* We are looping the search. */
/* It would be nice to see which of the given users did actually
match one in the keyring. To implement this we need to have
a found flag for each entry in desc. To set this flag we
must check all those entries after a match to mark all
matched one - currently we stop at the first match. To do
this we need an extra flag to enable this feature. */
}
#ifdef ENABLE_SELINUX_HACKS
if (secret)
{
log_error (_("exporting secret keys not allowed\n"));
err = gpg_error (GPG_ERR_NOT_SUPPORTED);
goto leave;
}
#endif
/* For secret key export we need to setup a decryption context. */
if (secret)
{
void *kek = NULL;
size_t keklen;
err = agent_keywrap_key (ctrl, 1, &kek, &keklen);
if (err)
{
log_error ("error getting the KEK: %s\n", gpg_strerror (err));
goto leave;
}
/* Prepare a cipher context. */
err = gcry_cipher_open (&cipherhd, GCRY_CIPHER_AES128,
GCRY_CIPHER_MODE_AESWRAP, 0);
if (!err)
err = gcry_cipher_setkey (cipherhd, kek, keklen);
if (err)
{
log_error ("error setting up an encryption context: %s\n",
gpg_strerror (err));
goto leave;
}
xfree (kek);
kek = NULL;
}
for (;;)
{
u32 keyid[2];
PKT_public_key *pk;
err = keydb_search (kdbhd, desc, ndesc, &descindex);
if (!users)
desc[0].mode = KEYDB_SEARCH_MODE_NEXT;
if (err)
break;
/* Read the keyblock. */
release_kbnode (keyblock);
keyblock = NULL;
err = keydb_get_keyblock (kdbhd, &keyblock);
if (err)
{
log_error (_("error reading keyblock: %s\n"), gpg_strerror (err));
goto leave;
}
node = find_kbnode (keyblock, PKT_PUBLIC_KEY);
if (!node)
{
log_error ("public key packet not found in keyblock - skipped\n");
continue;
}
stats->count++;
setup_main_keyids (keyblock); /* gpg_format_keydesc needs it. */
pk = node->pkt->pkt.public_key;
keyid_from_pk (pk, keyid);
/* If a secret key export is required we need to check whether
we have a secret key at all and if so create the seckey_info
structure. */
if (secret)
{
if (agent_probe_any_secret_key (ctrl, keyblock))
continue; /* No secret key (neither primary nor subkey). */
/* No v3 keys with GNU mode 1001. */
if (secret == 2 && pk->version == 3)
{
log_info (_("key %s: PGP 2.x style key - skipped\n"),
keystr (keyid));
continue;
}
/* The agent does not yet allow export of v3 packets. It is
actually questionable whether we should allow them at
all. */
if (pk->version == 3)
{
log_info ("key %s: PGP 2.x style key (v3) export "
"not yet supported - skipped\n", keystr (keyid));
continue;
}
stats->secret_count++;
}
/* Always do the cleaning on the public key part if requested.
* A designated revocation is never stripped, even with
* export-minimal set. */
if ((options & EXPORT_CLEAN))
{
merge_keys_and_selfsig (ctrl, keyblock);
clean_all_uids (ctrl, keyblock, opt.verbose,
(options&EXPORT_MINIMAL), NULL, NULL);
clean_all_subkeys (ctrl, keyblock, opt.verbose,
(options&EXPORT_MINIMAL)? KEY_CLEAN_ALL
/**/ : KEY_CLEAN_AUTHENCR,
NULL, NULL);
commit_kbnode (&keyblock);
}
if (export_keep_uid)
{
commit_kbnode (&keyblock);
apply_keep_uid_filter (ctrl, keyblock, export_keep_uid);
commit_kbnode (&keyblock);
}
if (export_drop_subkey)
{
commit_kbnode (&keyblock);
apply_drop_subkey_filter (ctrl, keyblock, export_drop_subkey);
commit_kbnode (&keyblock);
}
/* And write it. */
err = do_export_one_keyblock (ctrl, keyblock, keyid,
out_help? out_help : out,
secret, options, stats, any,
desc, ndesc, descindex, cipherhd);
if (err)
break;
if (keyblock_out)
{
*keyblock_out = keyblock;
break;
}
if (out_help)
{
/* We want to write PKA or DANE records. OUT_HELP has the
* keyblock and we print a record for each uid to OUT. */
const void *data;
size_t datalen;
iobuf_flush_temp (out_help);
data = iobuf_get_temp_buffer (out_help);
datalen = iobuf_get_temp_length (out_help);
err = print_pka_or_dane_records (out,
keyblock, pk, data, datalen,
(options & EXPORT_PKA_FORMAT),
(options & EXPORT_DANE_FORMAT));
if (err)
goto leave;
iobuf_close (out_help);
out_help = iobuf_temp ();
}
}
if (gpg_err_code (err) == GPG_ERR_NOT_FOUND)
err = 0;
leave:
iobuf_cancel (out_help);
gcry_cipher_close (cipherhd);
xfree(desc);
keydb_release (kdbhd);
if (err || !keyblock_out)
release_kbnode( keyblock );
if( !*any )
log_info(_("WARNING: nothing exported\n"));
return err;
}
static gpg_error_t
key_to_sshblob (membuf_t *mb, const char *identifier, ...)
{
va_list arg_ptr;
gpg_error_t err = 0;
unsigned char nbuf[4];
unsigned char *buf;
size_t buflen;
gcry_mpi_t a;
ulongtobuf (nbuf, (ulong)strlen (identifier));
put_membuf (mb, nbuf, 4);
put_membuf_str (mb, identifier);
if (!strncmp (identifier, "ecdsa-sha2-", 11))
{
ulongtobuf (nbuf, (ulong)strlen (identifier+11));
put_membuf (mb, nbuf, 4);
put_membuf_str (mb, identifier+11);
}
va_start (arg_ptr, identifier);
while ((a = va_arg (arg_ptr, gcry_mpi_t)))
{
err = gcry_mpi_aprint (GCRYMPI_FMT_SSH, &buf, &buflen, a);
if (err)
break;
if (!strcmp (identifier, "ssh-ed25519")
&& buflen > 5 && buf[4] == 0x40)
{
/* We need to strip our 0x40 prefix. */
put_membuf (mb, "\x00\x00\x00\x20", 4);
put_membuf (mb, buf+5, buflen-5);
}
else
put_membuf (mb, buf, buflen);
gcry_free (buf);
}
va_end (arg_ptr);
return err;
}
/* Export the key identified by USERID in the SSH public key format.
The function exports the latest subkey with Authentication
capability unless the '!' suffix is used to export a specific
key. */
gpg_error_t
export_ssh_key (ctrl_t ctrl, const char *userid)
{
gpg_error_t err;
kbnode_t keyblock = NULL;
KEYDB_SEARCH_DESC desc;
u32 latest_date;
u32 curtime = make_timestamp ();
kbnode_t latest_key, node;
PKT_public_key *pk;
const char *identifier = NULL;
membuf_t mb;
estream_t fp = NULL;
struct b64state b64_state;
const char *fname = "-";
init_membuf (&mb, 4096);
/* We need to know whether the key has been specified using the
exact syntax ('!' suffix). Thus we need to run a
classify_user_id on our own. */
err = classify_user_id (userid, &desc, 1);
/* Get the public key. */
if (!err)
{
getkey_ctx_t getkeyctx;
err = get_pubkey_byname (ctrl, GET_PUBKEY_NO_AKL,
&getkeyctx, NULL, userid, &keyblock,
NULL,
0 /* Only usable keys or given exact. */);
if (!err)
{
err = getkey_next (ctrl, getkeyctx, NULL, NULL);
if (!err)
err = gpg_error (GPG_ERR_AMBIGUOUS_NAME);
else if (gpg_err_code (err) == GPG_ERR_NO_PUBKEY)
err = 0;
}
getkey_end (ctrl, getkeyctx);
}
if (err)
{
log_error (_("key \"%s\" not found: %s\n"), userid, gpg_strerror (err));
return err;
}
/* The finish_lookup code in getkey.c does not handle auth keys,
thus we have to duplicate the code here to find the latest
subkey. However, if the key has been found using an exact match
('!' notation) we use that key without any further checks and
even allow the use of the primary key. */
latest_date = 0;
latest_key = NULL;
for (node = keyblock; node; node = node->next)
{
if ((node->pkt->pkttype == PKT_PUBLIC_SUBKEY
|| node->pkt->pkttype == PKT_PUBLIC_KEY)
&& node->pkt->pkt.public_key->flags.exact)
{
latest_key = node;
break;
}
}
if (!latest_key)
{
for (node = keyblock; node; node = node->next)
{
if (node->pkt->pkttype != PKT_PUBLIC_SUBKEY)
continue;
pk = node->pkt->pkt.public_key;
if (DBG_LOOKUP)
log_debug ("\tchecking subkey %08lX\n",
(ulong) keyid_from_pk (pk, NULL));
if (!(pk->pubkey_usage & PUBKEY_USAGE_AUTH))
{
if (DBG_LOOKUP)
log_debug ("\tsubkey not usable for authentication\n");
continue;
}
if (!pk->flags.valid)
{
if (DBG_LOOKUP)
log_debug ("\tsubkey not valid\n");
continue;
}
if (pk->flags.revoked)
{
if (DBG_LOOKUP)
log_debug ("\tsubkey has been revoked\n");
continue;
}
if (pk->has_expired)
{
if (DBG_LOOKUP)
log_debug ("\tsubkey has expired\n");
continue;
}
if (pk->timestamp > curtime && !opt.ignore_valid_from)
{
if (DBG_LOOKUP)
log_debug ("\tsubkey not yet valid\n");
continue;
}
if (DBG_LOOKUP)
log_debug ("\tsubkey might be fine\n");
/* In case a key has a timestamp of 0 set, we make sure that it
is used. A better change would be to compare ">=" but that
might also change the selected keys and is as such a more
intrusive change. */
if (pk->timestamp > latest_date || (!pk->timestamp && !latest_date))
{
latest_date = pk->timestamp;
latest_key = node;
}
}
/* If no subkey was suitable check the primary key. */
if (!latest_key
&& (node = keyblock) && node->pkt->pkttype == PKT_PUBLIC_KEY)
{
pk = node->pkt->pkt.public_key;
if (DBG_LOOKUP)
log_debug ("\tchecking primary key %08lX\n",
(ulong) keyid_from_pk (pk, NULL));
if (!(pk->pubkey_usage & PUBKEY_USAGE_AUTH))
{
if (DBG_LOOKUP)
log_debug ("\tprimary key not usable for authentication\n");
}
else if (!pk->flags.valid)
{
if (DBG_LOOKUP)
log_debug ("\tprimary key not valid\n");
}
else if (pk->flags.revoked)
{
if (DBG_LOOKUP)
log_debug ("\tprimary key has been revoked\n");
}
else if (pk->has_expired)
{
if (DBG_LOOKUP)
log_debug ("\tprimary key has expired\n");
}
else if (pk->timestamp > curtime && !opt.ignore_valid_from)
{
if (DBG_LOOKUP)
log_debug ("\tprimary key not yet valid\n");
}
else
{
if (DBG_LOOKUP)
log_debug ("\tprimary key is fine\n");
latest_date = pk->timestamp;
latest_key = node;
}
}
}
if (!latest_key)
{
err = gpg_error (GPG_ERR_UNUSABLE_PUBKEY);
log_error (_("key \"%s\" not found: %s\n"), userid, gpg_strerror (err));
goto leave;
}
pk = latest_key->pkt->pkt.public_key;
if (DBG_LOOKUP)
log_debug ("\tusing key %08lX\n", (ulong) keyid_from_pk (pk, NULL));
switch (pk->pubkey_algo)
{
case PUBKEY_ALGO_DSA:
identifier = "ssh-dss";
err = key_to_sshblob (&mb, identifier,
pk->pkey[0], pk->pkey[1], pk->pkey[2], pk->pkey[3],
NULL);
break;
case PUBKEY_ALGO_RSA:
case PUBKEY_ALGO_RSA_S:
identifier = "ssh-rsa";
err = key_to_sshblob (&mb, identifier, pk->pkey[1], pk->pkey[0], NULL);
break;
case PUBKEY_ALGO_ECDSA:
{
char *curveoid;
const char *curve;
curveoid = openpgp_oid_to_str (pk->pkey[0]);
if (!curveoid)
err = gpg_error_from_syserror ();
else if (!(curve = openpgp_oid_to_curve (curveoid, 0)))
err = gpg_error (GPG_ERR_UNKNOWN_CURVE);
else
{
if (!strcmp (curve, "nistp256"))
identifier = "ecdsa-sha2-nistp256";
else if (!strcmp (curve, "nistp384"))
identifier = "ecdsa-sha2-nistp384";
else if (!strcmp (curve, "nistp521"))
identifier = "ecdsa-sha2-nistp521";
if (!identifier)
err = gpg_error (GPG_ERR_UNKNOWN_CURVE);
else
err = key_to_sshblob (&mb, identifier, pk->pkey[1], NULL);
}
xfree (curveoid);
}
break;
case PUBKEY_ALGO_EDDSA:
if (!openpgp_oid_is_ed25519 (pk->pkey[0]))
err = gpg_error (GPG_ERR_UNKNOWN_CURVE);
else
{
identifier = "ssh-ed25519";
err = key_to_sshblob (&mb, identifier, pk->pkey[1], NULL);
}
break;
case PUBKEY_ALGO_ELGAMAL_E:
case PUBKEY_ALGO_ELGAMAL:
err = gpg_error (GPG_ERR_UNUSABLE_PUBKEY);
break;
default:
err = GPG_ERR_PUBKEY_ALGO;
break;
}
if (!identifier)
goto leave;
if (opt.outfile && *opt.outfile && strcmp (opt.outfile, "-"))
fp = es_fopen ((fname = opt.outfile), "w");
else
fp = es_stdout;
if (!fp)
{
err = gpg_error_from_syserror ();
log_error (_("error creating '%s': %s\n"), fname, gpg_strerror (err));
goto leave;
}
es_fprintf (fp, "%s ", identifier);
err = b64enc_start_es (&b64_state, fp, "");
if (!err)
{
void *blob;
size_t bloblen;
blob = get_membuf (&mb, &bloblen);
if (blob)
{
err = b64enc_write (&b64_state, blob, bloblen);
xfree (blob);
if (err)
goto leave;
}
err = b64enc_finish (&b64_state);
}
if (err)
goto leave;
es_fprintf (fp, " openpgp:0x%08lX\n", (ulong)keyid_from_pk (pk, NULL));
if (es_ferror (fp))
err = gpg_error_from_syserror ();
else
{
if (es_fclose (fp))
err = gpg_error_from_syserror ();
fp = NULL;
}
if (err)
log_error (_("error writing '%s': %s\n"), fname, gpg_strerror (err));
leave:
es_fclose (fp);
xfree (get_membuf (&mb, NULL));
release_kbnode (keyblock);
return err;
}
diff --git a/g10/keyid.c b/g10/keyid.c
index 5b868cd9c..69d85da0f 100644
--- a/g10/keyid.c
+++ b/g10/keyid.c
@@ -1,987 +1,987 @@
/* keyid.c - key ID and fingerprint handling
* Copyright (C) 1998, 1999, 2000, 2001, 2003,
* 2004, 2006, 2010 Free Software Foundation, Inc.
* Copyright (C) 2014 Werner Koch
* Copyright (C) 2016 g10 Code GmbH
*
* This file is part of GnuPG.
*
* GnuPG is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* GnuPG is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see .
*/
#include
#include
#include
#include
#include
#include
#include "gpg.h"
#include "../common/util.h"
#include "main.h"
#include "packet.h"
#include "options.h"
#include "keydb.h"
#include "../common/i18n.h"
#include "rmd160.h"
#include "../common/host2net.h"
#define KEYID_STR_SIZE 19
#ifdef HAVE_UNSIGNED_TIME_T
# define IS_INVALID_TIME_T(a) ((a) == (time_t)(-1))
#else
/* Error or 32 bit time_t and value after 2038-01-19. */
# define IS_INVALID_TIME_T(a) ((a) < 0)
#endif
/* Return a letter describing the public key algorithms. */
int
pubkey_letter( int algo )
{
switch (algo)
{
case PUBKEY_ALGO_RSA: return 'R' ;
case PUBKEY_ALGO_RSA_E: return 'r' ;
case PUBKEY_ALGO_RSA_S: return 's' ;
case PUBKEY_ALGO_ELGAMAL_E: return 'g' ;
case PUBKEY_ALGO_ELGAMAL: return 'G' ;
case PUBKEY_ALGO_DSA: return 'D' ;
case PUBKEY_ALGO_ECDH: return 'e' ; /* ECC DH (encrypt only) */
case PUBKEY_ALGO_ECDSA: return 'E' ; /* ECC DSA (sign only) */
case PUBKEY_ALGO_EDDSA: return 'E' ; /* ECC EdDSA (sign only) */
default: return '?';
}
}
/* Return a string describing the public key algorithm and the
keysize. For elliptic curves the functions prints the name of the
curve because the keysize is a property of the curve. The string
is copied to the supplied buffer up a length of BUFSIZE-1.
Examples for the output are:
"rsa2048" - RSA with 2048 bit
"elg1024" - Elgamal with 1024 bit
"ed25519" - ECC using the curve Ed25519.
"E_1.2.3.4" - ECC using the unsupported curve with OID "1.2.3.4".
"E_1.3.6.1.4.1.11591.2.12242973" ECC with a bogus OID.
"unknown_N" - Unknown OpenPGP algorithm N.
If the option --legacy-list-mode is active, the output use the
legacy format:
"2048R" - RSA with 2048 bit
"1024g" - Elgamal with 1024 bit
"256E" - ECDSA using a curve with 256 bit
The macro PUBKEY_STRING_SIZE may be used to allocate a buffer with
a suitable size.*/
char *
pubkey_string (PKT_public_key *pk, char *buffer, size_t bufsize)
{
const char *prefix = NULL;
if (opt.legacy_list_mode)
{
snprintf (buffer, bufsize, "%4u%c",
nbits_from_pk (pk), pubkey_letter (pk->pubkey_algo));
return buffer;
}
switch (pk->pubkey_algo)
{
case PUBKEY_ALGO_RSA:
case PUBKEY_ALGO_RSA_E:
case PUBKEY_ALGO_RSA_S: prefix = "rsa"; break;
case PUBKEY_ALGO_ELGAMAL_E: prefix = "elg"; break;
case PUBKEY_ALGO_DSA: prefix = "dsa"; break;
case PUBKEY_ALGO_ELGAMAL: prefix = "xxx"; break;
case PUBKEY_ALGO_ECDH:
case PUBKEY_ALGO_ECDSA:
case PUBKEY_ALGO_EDDSA: prefix = ""; break;
}
if (prefix && *prefix)
snprintf (buffer, bufsize, "%s%u", prefix, nbits_from_pk (pk));
else if (prefix)
{
char *curve = openpgp_oid_to_str (pk->pkey[0]);
const char *name = openpgp_oid_to_curve (curve, 0);
if (name)
snprintf (buffer, bufsize, "%s", name);
else if (curve)
snprintf (buffer, bufsize, "E_%s", curve);
else
snprintf (buffer, bufsize, "E_error");
xfree (curve);
}
else
snprintf (buffer, bufsize, "unknown_%u", (unsigned int)pk->pubkey_algo);
return buffer;
}
/* Hash a public key. This function is useful for v4 fingerprints and
for v3 or v4 key signing. */
void
hash_public_key (gcry_md_hd_t md, PKT_public_key *pk)
{
unsigned int n = 6;
unsigned int nn[PUBKEY_MAX_NPKEY];
byte *pp[PUBKEY_MAX_NPKEY];
int i;
unsigned int nbits;
size_t nbytes;
int npkey = pubkey_get_npkey (pk->pubkey_algo);
/* FIXME: We can avoid the extra malloc by calling only the first
mpi_print here which computes the required length and calling the
real mpi_print only at the end. The speed advantage would only be
for ECC (opaque MPIs) or if we could implement an mpi_print
variant with a callback handler to do the hashing. */
if (npkey==0 && pk->pkey[0]
&& gcry_mpi_get_flag (pk->pkey[0], GCRYMPI_FLAG_OPAQUE))
{
pp[0] = gcry_mpi_get_opaque (pk->pkey[0], &nbits);
nn[0] = (nbits+7)/8;
n+=nn[0];
}
else
{
for (i=0; i < npkey; i++ )
{
if (!pk->pkey[i])
{
/* This case may only happen if the parsing of the MPI
failed but the key was anyway created. May happen
during "gpg KEYFILE". */
pp[i] = NULL;
nn[i] = 0;
}
else if (gcry_mpi_get_flag (pk->pkey[i], GCRYMPI_FLAG_OPAQUE))
{
const void *p;
p = gcry_mpi_get_opaque (pk->pkey[i], &nbits);
pp[i] = xmalloc ((nbits+7)/8);
if (p)
memcpy (pp[i], p, (nbits+7)/8);
else
pp[i] = NULL;
nn[i] = (nbits+7)/8;
n += nn[i];
}
else
{
if (gcry_mpi_print (GCRYMPI_FMT_PGP, NULL, 0,
&nbytes, pk->pkey[i]))
BUG ();
pp[i] = xmalloc (nbytes);
if (gcry_mpi_print (GCRYMPI_FMT_PGP, pp[i], nbytes,
&nbytes, pk->pkey[i]))
BUG ();
nn[i] = nbytes;
n += nn[i];
}
}
}
gcry_md_putc ( md, 0x99 ); /* ctb */
/* What does it mean if n is greater than 0xFFFF ? */
gcry_md_putc ( md, n >> 8 ); /* 2 byte length header */
gcry_md_putc ( md, n );
gcry_md_putc ( md, pk->version );
gcry_md_putc ( md, pk->timestamp >> 24 );
gcry_md_putc ( md, pk->timestamp >> 16 );
gcry_md_putc ( md, pk->timestamp >> 8 );
gcry_md_putc ( md, pk->timestamp );
gcry_md_putc ( md, pk->pubkey_algo );
if(npkey==0 && pk->pkey[0]
&& gcry_mpi_get_flag (pk->pkey[0], GCRYMPI_FLAG_OPAQUE))
{
if (pp[0])
gcry_md_write (md, pp[0], nn[0]);
}
else
{
for(i=0; i < npkey; i++ )
{
if (pp[i])
gcry_md_write ( md, pp[i], nn[i] );
xfree(pp[i]);
}
}
}
static gcry_md_hd_t
do_fingerprint_md( PKT_public_key *pk )
{
gcry_md_hd_t md;
if (gcry_md_open (&md, DIGEST_ALGO_SHA1, 0))
BUG ();
hash_public_key(md,pk);
gcry_md_final( md );
return md;
}
/* fixme: Check whether we can replace this function or if not
describe why we need it. */
u32
v3_keyid (gcry_mpi_t a, u32 *ki)
{
byte *buffer, *p;
size_t nbytes;
if (gcry_mpi_print (GCRYMPI_FMT_USG, NULL, 0, &nbytes, a ))
BUG ();
/* fixme: allocate it on the stack */
buffer = xmalloc (nbytes);
if (gcry_mpi_print( GCRYMPI_FMT_USG, buffer, nbytes, NULL, a ))
BUG ();
if (nbytes < 8) /* oops */
ki[0] = ki[1] = 0;
else
{
p = buffer + nbytes - 8;
ki[0] = buf32_to_u32 (p);
p += 4;
ki[1] = buf32_to_u32 (p);
}
xfree (buffer);
return ki[1];
}
/* Return PK's keyid. The memory is owned by PK. */
u32 *
pk_keyid (PKT_public_key *pk)
{
keyid_from_pk (pk, NULL);
/* Uncomment this for help tracking down bugs related to keyid or
main_keyid not being set correctly. */
#if 0
if (! (pk->main_keyid[0] || pk->main_keyid[1]))
log_bug ("pk->main_keyid not set!\n");
if (keyid_cmp (pk->keyid, pk->main_keyid) == 0
&& ! pk->flags.primary)
log_bug ("keyid and main_keyid are the same, but primary flag not set!\n");
if (keyid_cmp (pk->keyid, pk->main_keyid) != 0
&& pk->flags.primary)
log_bug ("keyid and main_keyid are different, but primary flag set!\n");
#endif
return pk->keyid;
}
/* Return the keyid of the primary key associated with PK. The memory
is owned by PK. */
u32 *
pk_main_keyid (PKT_public_key *pk)
{
/* Uncomment this for help tracking down bugs related to keyid or
main_keyid not being set correctly. */
#if 0
if (! (pk->main_keyid[0] || pk->main_keyid[1]))
log_bug ("pk->main_keyid not set!\n");
#endif
return pk->main_keyid;
}
/* Copy the keyid in SRC to DEST and return DEST. */
u32 *
keyid_copy (u32 *dest, const u32 *src)
{
dest[0] = src[0];
dest[1] = src[1];
return dest;
}
char *
format_keyid (u32 *keyid, int format, char *buffer, int len)
{
char tmp[KEYID_STR_SIZE];
if (! buffer)
{
buffer = tmp;
len = sizeof (tmp);
}
if (format == KF_DEFAULT)
format = opt.keyid_format;
if (format == KF_DEFAULT)
format = KF_NONE;
switch (format)
{
case KF_NONE:
if (len)
*buffer = 0;
break;
case KF_SHORT:
snprintf (buffer, len, "%08lX", (ulong)keyid[1]);
break;
case KF_LONG:
snprintf (buffer, len, "%08lX%08lX", (ulong)keyid[0], (ulong)keyid[1]);
break;
case KF_0xSHORT:
snprintf (buffer, len, "0x%08lX", (ulong)keyid[1]);
break;
case KF_0xLONG:
snprintf (buffer, len, "0x%08lX%08lX", (ulong)keyid[0],(ulong)keyid[1]);
break;
default:
BUG();
}
if (buffer == tmp)
return xstrdup (buffer);
return buffer;
}
size_t
keystrlen(void)
{
int format = opt.keyid_format;
if (format == KF_DEFAULT)
format = KF_NONE;
switch(format)
{
case KF_NONE:
return 0;
case KF_SHORT:
return 8;
case KF_LONG:
return 16;
case KF_0xSHORT:
return 10;
case KF_0xLONG:
return 18;
default:
BUG();
}
}
const char *
keystr (u32 *keyid)
{
static char keyid_str[KEYID_STR_SIZE];
int format = opt.keyid_format;
if (format == KF_DEFAULT)
format = KF_NONE;
if (format == KF_NONE)
format = KF_LONG;
return format_keyid (keyid, format, keyid_str, sizeof (keyid_str));
}
/* This function returns the key id of the main and possible the
* subkey as one string. It is used by error messages. */
const char *
keystr_with_sub (u32 *main_kid, u32 *sub_kid)
{
static char buffer[KEYID_STR_SIZE+1+KEYID_STR_SIZE];
char *p;
int format = opt.keyid_format;
if (format == KF_NONE)
format = KF_LONG;
format_keyid (main_kid, format, buffer, KEYID_STR_SIZE);
if (sub_kid)
{
p = buffer + strlen (buffer);
*p++ = '/';
format_keyid (sub_kid, format, p, KEYID_STR_SIZE);
}
return buffer;
}
const char *
keystr_from_pk(PKT_public_key *pk)
{
keyid_from_pk(pk,NULL);
return keystr(pk->keyid);
}
const char *
keystr_from_pk_with_sub (PKT_public_key *main_pk, PKT_public_key *sub_pk)
{
keyid_from_pk (main_pk, NULL);
if (sub_pk)
keyid_from_pk (sub_pk, NULL);
return keystr_with_sub (main_pk->keyid, sub_pk? sub_pk->keyid:NULL);
}
/* Return PK's key id as a string using the default format. PK owns
the storage. */
const char *
pk_keyid_str (PKT_public_key *pk)
{
return keystr (pk_keyid (pk));
}
const char *
keystr_from_desc(KEYDB_SEARCH_DESC *desc)
{
switch(desc->mode)
{
case KEYDB_SEARCH_MODE_LONG_KID:
case KEYDB_SEARCH_MODE_SHORT_KID:
return keystr(desc->u.kid);
case KEYDB_SEARCH_MODE_FPR20:
{
u32 keyid[2];
keyid[0] = buf32_to_u32 (desc->u.fpr+12);
keyid[1] = buf32_to_u32 (desc->u.fpr+16);
return keystr(keyid);
}
case KEYDB_SEARCH_MODE_FPR16:
return "?v3 fpr?";
default:
BUG();
}
}
/*
* Get the keyid from the public key and put it into keyid
* if this is not NULL. Return the 32 low bits of the keyid.
*/
u32
keyid_from_pk (PKT_public_key *pk, u32 *keyid)
{
u32 lowbits;
u32 dummy_keyid[2];
if (!keyid)
keyid = dummy_keyid;
if( pk->keyid[0] || pk->keyid[1] )
{
keyid[0] = pk->keyid[0];
keyid[1] = pk->keyid[1];
lowbits = keyid[1];
}
else
{
const byte *dp;
gcry_md_hd_t md;
md = do_fingerprint_md(pk);
if(md)
{
dp = gcry_md_read ( md, 0 );
keyid[0] = buf32_to_u32 (dp+12);
keyid[1] = buf32_to_u32 (dp+16);
lowbits = keyid[1];
gcry_md_close (md);
pk->keyid[0] = keyid[0];
pk->keyid[1] = keyid[1];
}
else
pk->keyid[0]=pk->keyid[1]=keyid[0]=keyid[1]=lowbits=0xFFFFFFFF;
}
return lowbits;
}
/*
* Get the keyid from the fingerprint. This function is simple for most
* keys, but has to do a keylookup for old stayle keys.
*/
u32
keyid_from_fingerprint (ctrl_t ctrl, const byte *fprint,
size_t fprint_len, u32 *keyid)
{
u32 dummy_keyid[2];
if( !keyid )
keyid = dummy_keyid;
if (fprint_len != 20)
{
/* This is special as we have to lookup the key first. */
PKT_public_key pk;
int rc;
memset (&pk, 0, sizeof pk);
rc = get_pubkey_byfprint (ctrl, &pk, NULL, fprint, fprint_len);
if( rc )
{
log_error("Oops: keyid_from_fingerprint: no pubkey\n");
keyid[0] = 0;
keyid[1] = 0;
}
else
keyid_from_pk (&pk, keyid);
}
else
{
const byte *dp = fprint;
keyid[0] = buf32_to_u32 (dp+12);
keyid[1] = buf32_to_u32 (dp+16);
}
return keyid[1];
}
u32
keyid_from_sig (PKT_signature *sig, u32 *keyid)
{
if( keyid )
{
keyid[0] = sig->keyid[0];
keyid[1] = sig->keyid[1];
}
return sig->keyid[1];
}
byte *
namehash_from_uid (PKT_user_id *uid)
{
if (!uid->namehash)
{
uid->namehash = xmalloc (20);
if (uid->attrib_data)
rmd160_hash_buffer (uid->namehash, uid->attrib_data, uid->attrib_len);
else
rmd160_hash_buffer (uid->namehash, uid->name, uid->len);
}
return uid->namehash;
}
/*
* Return the number of bits used in PK.
*/
unsigned int
nbits_from_pk (PKT_public_key *pk)
{
return pubkey_nbits (pk->pubkey_algo, pk->pkey);
}
/* Convert an UTC TIMESTAMP into an UTC yyyy-mm-dd string. Return
* that string. The caller should pass a buffer with at least a size
* of MK_DATESTR_SIZE. */
char *
mk_datestr (char *buffer, size_t bufsize, u32 timestamp)
{
time_t atime = timestamp;
struct tm *tp;
if (IS_INVALID_TIME_T (atime))
strcpy (buffer, "????" "-??" "-??"); /* Mark this as invalid. */
else
{
tp = gmtime (&atime);
snprintf (buffer, bufsize, "%04d-%02d-%02d",
1900+tp->tm_year, tp->tm_mon+1, tp->tm_mday );
}
return buffer;
}
/*
* return a string with the creation date of the pk
* Note: this is alloced in a static buffer.
* Format is: yyyy-mm-dd
*/
const char *
datestr_from_pk (PKT_public_key *pk)
{
static char buffer[MK_DATESTR_SIZE];
return mk_datestr (buffer, sizeof buffer, pk->timestamp);
}
const char *
datestr_from_sig (PKT_signature *sig )
{
static char buffer[MK_DATESTR_SIZE];
return mk_datestr (buffer, sizeof buffer, sig->timestamp);
}
const char *
expirestr_from_pk (PKT_public_key *pk)
{
static char buffer[MK_DATESTR_SIZE];
if (!pk->expiredate)
return _("never ");
return mk_datestr (buffer, sizeof buffer, pk->expiredate);
}
const char *
expirestr_from_sig (PKT_signature *sig)
{
static char buffer[MK_DATESTR_SIZE];
if (!sig->expiredate)
return _("never ");
return mk_datestr (buffer, sizeof buffer, sig->expiredate);
}
const char *
revokestr_from_pk( PKT_public_key *pk )
{
static char buffer[MK_DATESTR_SIZE];
if(!pk->revoked.date)
return _("never ");
return mk_datestr (buffer, sizeof buffer, pk->revoked.date);
}
const char *
usagestr_from_pk (PKT_public_key *pk, int fill)
{
static char buffer[10];
int i = 0;
unsigned int use = pk->pubkey_usage;
if ( use & PUBKEY_USAGE_SIG )
buffer[i++] = 'S';
if ( use & PUBKEY_USAGE_CERT )
buffer[i++] = 'C';
if ( use & PUBKEY_USAGE_ENC )
buffer[i++] = 'E';
if ( (use & PUBKEY_USAGE_AUTH) )
buffer[i++] = 'A';
while (fill && i < 4)
buffer[i++] = ' ';
buffer[i] = 0;
return buffer;
}
const char *
colon_strtime (u32 t)
{
static char buf[20];
if (!t)
return "";
snprintf (buf, sizeof buf, "%lu", (ulong)t);
return buf;
}
const char *
colon_datestr_from_pk (PKT_public_key *pk)
{
static char buf[20];
snprintf (buf, sizeof buf, "%lu", (ulong)pk->timestamp);
return buf;
}
const char *
colon_datestr_from_sig (PKT_signature *sig)
{
static char buf[20];
snprintf (buf, sizeof buf, "%lu", (ulong)sig->timestamp);
return buf;
}
const char *
colon_expirestr_from_sig (PKT_signature *sig)
{
static char buf[20];
if (!sig->expiredate)
return "";
snprintf (buf, sizeof buf,"%lu", (ulong)sig->expiredate);
return buf;
}
/*
* Return a byte array with the fingerprint for the given PK/SK
* The length of the array is returned in ret_len. Caller must free
* the array or provide an array of length MAX_FINGERPRINT_LEN.
*/
byte *
fingerprint_from_pk (PKT_public_key *pk, byte *array, size_t *ret_len)
{
const byte *dp;
size_t len;
gcry_md_hd_t md;
md = do_fingerprint_md(pk);
dp = gcry_md_read( md, 0 );
len = gcry_md_get_algo_dlen (gcry_md_get_algo (md));
log_assert( len <= MAX_FINGERPRINT_LEN );
if (!array)
array = xmalloc ( len );
memcpy (array, dp, len );
pk->keyid[0] = buf32_to_u32 (dp+12);
pk->keyid[1] = buf32_to_u32 (dp+16);
gcry_md_close( md);
if (ret_len)
*ret_len = len;
return array;
}
/* Return an allocated buffer with the fingerprint of PK formatted as
* a plain hexstring. If BUFFER is NULL the result is a malloc'd
* string. If BUFFER is not NULL the result will be copied into this
* buffer. In the latter case BUFLEN describes the length of the
* buffer; if this is too short the function terminates the process.
* Returns a malloc'ed string or BUFFER. A suitable length for BUFFER
* is (2*MAX_FINGERPRINT_LEN + 1). */
char *
hexfingerprint (PKT_public_key *pk, char *buffer, size_t buflen)
{
unsigned char fpr[MAX_FINGERPRINT_LEN];
size_t len;
fingerprint_from_pk (pk, fpr, &len);
if (!buffer)
{
buffer = xtrymalloc (2 * len + 1);
if (!buffer)
return NULL;
}
else if (buflen < 2*len+1)
log_fatal ("%s: buffer too short (%zu)\n", __func__, buflen);
bin2hex (fpr, len, buffer);
return buffer;
}
/* Pretty print a hex fingerprint. If BUFFER is NULL the result is a
malloc'd string. If BUFFER is not NULL the result will be copied
into this buffer. In the latter case BUFLEN describes the length
of the buffer; if this is too short the function terminates the
process. Returns a malloc'ed string or BUFFER. A suitable length
for BUFFER is (MAX_FORMATTED_FINGERPRINT_LEN + 1). */
char *
format_hexfingerprint (const char *fingerprint, char *buffer, size_t buflen)
{
int hexlen = strlen (fingerprint);
int space;
int i, j;
if (hexlen == 40) /* v4 fingerprint */
{
space = (/* The characters and the NUL. */
40 + 1
/* After every fourth character, we add a space (except
the last). */
+ 40 / 4 - 1
/* Half way through we add a second space. */
+ 1);
}
else /* Other fingerprint versions - print as is. */
{
space = hexlen + 1;
}
if (!buffer)
buffer = xmalloc (space);
else if (buflen < space)
log_fatal ("%s: buffer too short (%zu)\n", __func__, buflen);
if (hexlen == 40) /* v4 fingerprint */
{
for (i = 0, j = 0; i < 40; i ++)
{
if (i && i % 4 == 0)
buffer[j ++] = ' ';
if (i == 40 / 2)
buffer[j ++] = ' ';
buffer[j ++] = fingerprint[i];
}
buffer[j ++] = 0;
log_assert (j == space);
}
else
{
strcpy (buffer, fingerprint);
}
return buffer;
}
/* Return the so called KEYGRIP which is the SHA-1 hash of the public
key parameters expressed as an canoncial encoded S-Exp. ARRAY must
be 20 bytes long. Returns 0 on success or an error code. */
gpg_error_t
keygrip_from_pk (PKT_public_key *pk, unsigned char *array)
{
gpg_error_t err;
gcry_sexp_t s_pkey;
if (DBG_PACKET)
log_debug ("get_keygrip for public key\n");
switch (pk->pubkey_algo)
{
case GCRY_PK_DSA:
err = gcry_sexp_build (&s_pkey, NULL,
"(public-key(dsa(p%m)(q%m)(g%m)(y%m)))",
pk->pkey[0], pk->pkey[1],
pk->pkey[2], pk->pkey[3]);
break;
case GCRY_PK_ELG:
case GCRY_PK_ELG_E:
err = gcry_sexp_build (&s_pkey, NULL,
"(public-key(elg(p%m)(g%m)(y%m)))",
pk->pkey[0], pk->pkey[1], pk->pkey[2]);
break;
case GCRY_PK_RSA:
case GCRY_PK_RSA_S:
case GCRY_PK_RSA_E:
err = gcry_sexp_build (&s_pkey, NULL,
"(public-key(rsa(n%m)(e%m)))",
pk->pkey[0], pk->pkey[1]);
break;
case PUBKEY_ALGO_EDDSA:
case PUBKEY_ALGO_ECDSA:
case PUBKEY_ALGO_ECDH:
{
char *curve = openpgp_oid_to_str (pk->pkey[0]);
if (!curve)
err = gpg_error_from_syserror ();
else
{
err = gcry_sexp_build (&s_pkey, NULL,
pk->pubkey_algo == PUBKEY_ALGO_EDDSA?
"(public-key(ecc(curve%s)(flags eddsa)(q%m)))":
(pk->pubkey_algo == PUBKEY_ALGO_ECDH
&& openpgp_oid_is_cv25519 (pk->pkey[0]))?
"(public-key(ecc(curve%s)(flags djb-tweak)(q%m)))":
"(public-key(ecc(curve%s)(q%m)))",
curve, pk->pkey[1]);
xfree (curve);
}
}
break;
default:
err = gpg_error (GPG_ERR_PUBKEY_ALGO);
break;
}
if (err)
return err;
if (!gcry_pk_get_keygrip (s_pkey, array))
{
char *hexfpr;
hexfpr = hexfingerprint (pk, NULL, 0);
log_info ("error computing keygrip (fpr=%s)\n", hexfpr);
xfree (hexfpr);
memset (array, 0, 20);
err = gpg_error (GPG_ERR_GENERAL);
}
else
{
if (DBG_PACKET)
- log_printhex ("keygrip=", array, 20);
+ log_printhex (array, 20, "keygrip=");
/* FIXME: Save the keygrip in PK. */
}
gcry_sexp_release (s_pkey);
return err;
}
/* Store an allocated buffer with the keygrip of PK encoded as a
hexstring at r_GRIP. Returns 0 on success. */
gpg_error_t
hexkeygrip_from_pk (PKT_public_key *pk, char **r_grip)
{
gpg_error_t err;
unsigned char grip[20];
*r_grip = NULL;
err = keygrip_from_pk (pk, grip);
if (!err)
{
char * buf = xtrymalloc (20*2+1);
if (!buf)
err = gpg_error_from_syserror ();
else
{
bin2hex (grip, 20, buf);
*r_grip = buf;
}
}
return err;
}
diff --git a/g10/pubkey-enc.c b/g10/pubkey-enc.c
index 80043488b..7c02f02a3 100644
--- a/g10/pubkey-enc.c
+++ b/g10/pubkey-enc.c
@@ -1,497 +1,497 @@
/* pubkey-enc.c - Process a public key encoded packet.
* Copyright (C) 1998, 1999, 2000, 2001, 2002, 2006, 2009,
* 2010 Free Software Foundation, Inc.
*
* This file is part of GnuPG.
*
* GnuPG is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* GnuPG is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see .
*/
#include
#include
#include
#include
#include "gpg.h"
#include "../common/util.h"
#include "packet.h"
#include "keydb.h"
#include "trustdb.h"
#include "../common/status.h"
#include "options.h"
#include "main.h"
#include "../common/i18n.h"
#include "pkglue.h"
#include "call-agent.h"
#include "../common/host2net.h"
#include "../common/compliance.h"
static gpg_error_t get_it (ctrl_t ctrl, PKT_pubkey_enc *k,
DEK *dek, PKT_public_key *sk, u32 *keyid);
/* Check that the given algo is mentioned in one of the valid user-ids. */
static int
is_algo_in_prefs (kbnode_t keyblock, preftype_t type, int algo)
{
kbnode_t k;
for (k = keyblock; k; k = k->next)
{
if (k->pkt->pkttype == PKT_USER_ID)
{
PKT_user_id *uid = k->pkt->pkt.user_id;
prefitem_t *prefs = uid->prefs;
if (uid->created && prefs && !uid->flags.revoked && !uid->flags.expired)
{
for (; prefs->type; prefs++)
if (prefs->type == type && prefs->value == algo)
return 1;
}
}
}
return 0;
}
/*
* Get the session key from a pubkey enc packet and return it in DEK,
* which should have been allocated in secure memory by the caller.
*/
gpg_error_t
get_session_key (ctrl_t ctrl, PKT_pubkey_enc * k, DEK * dek)
{
PKT_public_key *sk = NULL;
int rc;
if (DBG_CLOCK)
log_clock ("get_session_key enter");
rc = openpgp_pk_test_algo2 (k->pubkey_algo, PUBKEY_USAGE_ENC);
if (rc)
goto leave;
if ((k->keyid[0] || k->keyid[1]) && !opt.try_all_secrets)
{
sk = xmalloc_clear (sizeof *sk);
sk->pubkey_algo = k->pubkey_algo; /* We want a pubkey with this algo. */
if (!(rc = get_seckey (ctrl, sk, k->keyid)))
{
/* Check compliance. */
if (! gnupg_pk_is_allowed (opt.compliance, PK_USE_DECRYPTION,
sk->pubkey_algo,
sk->pkey, nbits_from_pk (sk), NULL))
{
log_info (_("key %s is not suitable for decryption"
" in %s mode\n"),
keystr_from_pk (sk),
gnupg_compliance_option_string (opt.compliance));
rc = gpg_error (GPG_ERR_PUBKEY_ALGO);
}
else
rc = get_it (ctrl, k, dek, sk, k->keyid);
}
}
else if (opt.skip_hidden_recipients)
rc = gpg_error (GPG_ERR_NO_SECKEY);
else /* Anonymous receiver: Try all available secret keys. */
{
void *enum_context = NULL;
u32 keyid[2];
for (;;)
{
sk = xmalloc_clear (sizeof *sk);
rc = enum_secret_keys (ctrl, &enum_context, sk);
if (rc)
{
sk = NULL; /* enum_secret_keys turns SK into a shallow copy! */
rc = GPG_ERR_NO_SECKEY;
break;
}
if (sk->pubkey_algo != k->pubkey_algo)
continue;
if (!(sk->pubkey_usage & PUBKEY_USAGE_ENC))
continue;
keyid_from_pk (sk, keyid);
if (!opt.quiet)
log_info (_("anonymous recipient; trying secret key %s ...\n"),
keystr (keyid));
/* Check compliance. */
if (! gnupg_pk_is_allowed (opt.compliance, PK_USE_DECRYPTION,
sk->pubkey_algo,
sk->pkey, nbits_from_pk (sk), NULL))
{
log_info (_("key %s is not suitable for decryption"
" in %s mode\n"),
keystr_from_pk (sk),
gnupg_compliance_option_string (opt.compliance));
continue;
}
rc = get_it (ctrl, k, dek, sk, keyid);
if (!rc)
{
if (!opt.quiet)
log_info (_("okay, we are the anonymous recipient.\n"));
sk = NULL;
break;
}
else if (gpg_err_code (rc) == GPG_ERR_FULLY_CANCELED)
{
sk = NULL;
break; /* Don't try any more secret keys. */
}
}
enum_secret_keys (ctrl, &enum_context, NULL); /* free context */
}
leave:
free_public_key (sk);
if (DBG_CLOCK)
log_clock ("get_session_key leave");
return rc;
}
static gpg_error_t
get_it (ctrl_t ctrl,
PKT_pubkey_enc *enc, DEK *dek, PKT_public_key *sk, u32 *keyid)
{
gpg_error_t err;
byte *frame = NULL;
unsigned int n;
size_t nframe;
u16 csum, csum2;
int padding;
gcry_sexp_t s_data;
char *desc;
char *keygrip;
byte fp[MAX_FINGERPRINT_LEN];
size_t fpn;
if (DBG_CLOCK)
log_clock ("decryption start");
/* Get the keygrip. */
err = hexkeygrip_from_pk (sk, &keygrip);
if (err)
goto leave;
/* Convert the data to an S-expression. */
if (sk->pubkey_algo == PUBKEY_ALGO_ELGAMAL
|| sk->pubkey_algo == PUBKEY_ALGO_ELGAMAL_E)
{
if (!enc->data[0] || !enc->data[1])
err = gpg_error (GPG_ERR_BAD_MPI);
else
err = gcry_sexp_build (&s_data, NULL, "(enc-val(elg(a%m)(b%m)))",
enc->data[0], enc->data[1]);
}
else if (sk->pubkey_algo == PUBKEY_ALGO_RSA
|| sk->pubkey_algo == PUBKEY_ALGO_RSA_E)
{
if (!enc->data[0])
err = gpg_error (GPG_ERR_BAD_MPI);
else
err = gcry_sexp_build (&s_data, NULL, "(enc-val(rsa(a%m)))",
enc->data[0]);
}
else if (sk->pubkey_algo == PUBKEY_ALGO_ECDH)
{
if (!enc->data[0] || !enc->data[1])
err = gpg_error (GPG_ERR_BAD_MPI);
else
err = gcry_sexp_build (&s_data, NULL, "(enc-val(ecdh(s%m)(e%m)))",
enc->data[1], enc->data[0]);
}
else
err = gpg_error (GPG_ERR_BUG);
if (err)
goto leave;
if (sk->pubkey_algo == PUBKEY_ALGO_ECDH)
{
fingerprint_from_pk (sk, fp, &fpn);
log_assert (fpn == 20);
}
/* Decrypt. */
desc = gpg_format_keydesc (ctrl, sk, FORMAT_KEYDESC_NORMAL, 1);
err = agent_pkdecrypt (NULL, keygrip,
desc, sk->keyid, sk->main_keyid, sk->pubkey_algo,
s_data, &frame, &nframe, &padding);
xfree (desc);
gcry_sexp_release (s_data);
if (err)
goto leave;
/* Now get the DEK (data encryption key) from the frame
*
* Old versions encode the DEK in this format (msb is left):
*
* 0 1 DEK(16 bytes) CSUM(2 bytes) 0 RND(n bytes) 2
*
* Later versions encode the DEK like this:
*
* 0 2 RND(n bytes) 0 A DEK(k bytes) CSUM(2 bytes)
*
* (mpi_get_buffer already removed the leading zero).
*
* RND are non-zero randow bytes.
* A is the cipher algorithm
* DEK is the encryption key (session key) with length k
* CSUM
*/
if (DBG_CRYPTO)
- log_printhex ("DEK frame:", frame, nframe);
+ log_printhex (frame, nframe, "DEK frame:");
n = 0;
if (sk->pubkey_algo == PUBKEY_ALGO_ECDH)
{
gcry_mpi_t shared_mpi;
gcry_mpi_t decoded;
/* At the beginning the frame are the bytes of shared point MPI. */
err = gcry_mpi_scan (&shared_mpi, GCRYMPI_FMT_USG, frame, nframe, NULL);
if (err)
{
err = gpg_error (GPG_ERR_WRONG_SECKEY);
goto leave;
}
err = pk_ecdh_decrypt (&decoded, fp, enc->data[1]/*encr data as an MPI*/,
shared_mpi, sk->pkey);
mpi_release (shared_mpi);
if(err)
goto leave;
xfree (frame);
err = gcry_mpi_aprint (GCRYMPI_FMT_USG, &frame, &nframe, decoded);
mpi_release (decoded);
if (err)
goto leave;
/* Now the frame are the bytes decrypted but padded session key. */
if (!nframe || nframe <= 8
|| frame[nframe-1] > nframe)
{
err = gpg_error (GPG_ERR_WRONG_SECKEY);
goto leave;
}
nframe -= frame[nframe-1]; /* Remove padding. */
log_assert (!n); /* (used just below) */
}
else
{
if (padding)
{
if (n + 7 > nframe)
{
err = gpg_error (GPG_ERR_WRONG_SECKEY);
goto leave;
}
/* FIXME: Actually the leading zero is required but due to
* the way we encode the output in libgcrypt as an MPI we
* are not able to encode that leading zero. However, when
* using a Smartcard we are doing it the right way and
* therefore we have to skip the zero. This should be fixed
* in gpg-agent of course. */
if (!frame[n])
n++;
if (frame[n] == 1 && frame[nframe - 1] == 2)
{
log_info (_("old encoding of the DEK is not supported\n"));
err = gpg_error (GPG_ERR_CIPHER_ALGO);
goto leave;
}
if (frame[n] != 2) /* Something went wrong. */
{
err = gpg_error (GPG_ERR_WRONG_SECKEY);
goto leave;
}
for (n++; n < nframe && frame[n]; n++) /* Skip the random bytes. */
;
n++; /* Skip the zero byte. */
}
}
if (n + 4 > nframe)
{
err = gpg_error (GPG_ERR_WRONG_SECKEY);
goto leave;
}
dek->keylen = nframe - (n + 1) - 2;
dek->algo = frame[n++];
err = openpgp_cipher_test_algo (dek->algo);
if (err)
{
if (!opt.quiet && gpg_err_code (err) == GPG_ERR_CIPHER_ALGO)
{
log_info (_("cipher algorithm %d%s is unknown or disabled\n"),
dek->algo,
dek->algo == CIPHER_ALGO_IDEA ? " (IDEA)" : "");
}
dek->algo = 0;
goto leave;
}
if (dek->keylen != openpgp_cipher_get_algo_keylen (dek->algo))
{
err = gpg_error (GPG_ERR_WRONG_SECKEY);
goto leave;
}
/* Copy the key to DEK and compare the checksum. */
csum = buf16_to_u16 (frame+nframe-2);
memcpy (dek->key, frame + n, dek->keylen);
for (csum2 = 0, n = 0; n < dek->keylen; n++)
csum2 += dek->key[n];
if (csum != csum2)
{
err = gpg_error (GPG_ERR_WRONG_SECKEY);
goto leave;
}
if (DBG_CLOCK)
log_clock ("decryption ready");
if (DBG_CRYPTO)
- log_printhex ("DEK is:", dek->key, dek->keylen);
+ log_printhex (dek->key, dek->keylen, "DEK is:");
/* Check that the algo is in the preferences and whether it has
* expired. Also print a status line with the key's fingerprint. */
{
PKT_public_key *pk = NULL;
PKT_public_key *mainpk = NULL;
KBNODE pkb = get_pubkeyblock (ctrl, keyid);
if (!pkb)
{
err = -1;
log_error ("oops: public key not found for preference check\n");
}
else if (pkb->pkt->pkt.public_key->selfsigversion > 3
&& dek->algo != CIPHER_ALGO_3DES
&& !opt.quiet
&& !is_algo_in_prefs (pkb, PREFTYPE_SYM, dek->algo))
log_info (_("WARNING: cipher algorithm %s not found in recipient"
" preferences\n"), openpgp_cipher_algo_name (dek->algo));
if (!err)
{
kbnode_t k;
int first = 1;
for (k = pkb; k; k = k->next)
{
if (k->pkt->pkttype == PKT_PUBLIC_KEY
|| k->pkt->pkttype == PKT_PUBLIC_SUBKEY)
{
u32 aki[2];
if (first)
{
first = 0;
mainpk = k->pkt->pkt.public_key;
}
keyid_from_pk (k->pkt->pkt.public_key, aki);
if (aki[0] == keyid[0] && aki[1] == keyid[1])
{
pk = k->pkt->pkt.public_key;
break;
}
}
}
if (!pk)
BUG ();
if (pk->expiredate && pk->expiredate <= make_timestamp ())
{
log_info (_("Note: secret key %s expired at %s\n"),
keystr (keyid), asctimestamp (pk->expiredate));
}
}
if (pk && pk->flags.revoked)
{
log_info (_("Note: key has been revoked"));
log_printf ("\n");
show_revocation_reason (ctrl, pk, 1);
}
if (is_status_enabled () && pk && mainpk)
{
char pkhex[MAX_FINGERPRINT_LEN*2+1];
char mainpkhex[MAX_FINGERPRINT_LEN*2+1];
hexfingerprint (pk, pkhex, sizeof pkhex);
hexfingerprint (mainpk, mainpkhex, sizeof mainpkhex);
/* Note that we do not want to create a trustdb just for
* getting the ownertrust: If there is no trustdb there can't
* be ulitmately trusted key anyway and thus the ownertrust
* value is irrelevant. */
write_status_printf (STATUS_DECRYPTION_KEY, "%s %s %c",
pkhex, mainpkhex,
get_ownertrust_info (ctrl, mainpk, 1));
}
release_kbnode (pkb);
err = 0;
}
leave:
xfree (frame);
xfree (keygrip);
return err;
}
/*
* Get the session key from the given string.
* String is supposed to be formatted as this:
* :
*/
gpg_error_t
get_override_session_key (DEK *dek, const char *string)
{
const char *s;
int i;
if (!string)
return GPG_ERR_BAD_KEY;
dek->algo = atoi (string);
if (dek->algo < 1)
return GPG_ERR_BAD_KEY;
if (!(s = strchr (string, ':')))
return GPG_ERR_BAD_KEY;
s++;
for (i = 0; i < DIM (dek->key) && *s; i++, s += 2)
{
int c = hextobyte (s);
if (c == -1)
return GPG_ERR_BAD_KEY;
dek->key[i] = c;
}
if (*s)
return GPG_ERR_BAD_KEY;
dek->keylen = i;
return 0;
}
diff --git a/g13/call-syshelp.c b/g13/call-syshelp.c
index 8a50c3ff4..b160ba32d 100644
--- a/g13/call-syshelp.c
+++ b/g13/call-syshelp.c
@@ -1,631 +1,631 @@
/* call-syshelp.c - Communication with g13-syshelp
* Copyright (C) 2015 Werner Koch
*
* This file is part of GnuPG.
*
* GnuPG is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* GnuPG is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see .
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include "g13.h"
#include
#include "../common/i18n.h"
#include "g13tuple.h"
#include "keyblob.h"
#include "../common/membuf.h"
#include "create.h"
#include "call-syshelp.h"
/* Local data for this module. A pointer to this is stored in the
CTRL object of each connection. */
struct call_syshelp_s
{
assuan_context_t assctx; /* The Assuan context for the current
g13-syshep connection. */
};
/* Parameter used with the CREATE command. */
struct create_parm_s
{
assuan_context_t ctx;
ctrl_t ctrl;
membuf_t plaintext;
unsigned int expect_plaintext:1;
unsigned int got_plaintext:1;
};
/* Parameter used with the MOUNT command. */
struct mount_parm_s
{
assuan_context_t ctx;
ctrl_t ctrl;
const void *keyblob;
size_t keybloblen;
};
/* Fork off the syshelp tool if this has not already been done. On
success stores the current Assuan context for the syshelp tool at
R_CTX. */
static gpg_error_t
start_syshelp (ctrl_t ctrl, assuan_context_t *r_ctx)
{
gpg_error_t err;
assuan_context_t ctx;
assuan_fd_t no_close_list[3];
int i;
*r_ctx = NULL;
if (ctrl->syshelp_local && (*r_ctx = ctrl->syshelp_local->assctx))
return 0; /* Already set. */
if (opt.verbose)
log_info ("starting a new syshelp\n");
if (!ctrl->syshelp_local)
{
ctrl->syshelp_local = xtrycalloc (1, sizeof *ctrl->syshelp_local);
if (!ctrl->syshelp_local)
return gpg_error_from_syserror ();
}
if (es_fflush (NULL))
{
err = gpg_error_from_syserror ();
log_error ("error flushing pending output: %s\n", gpg_strerror (err));
return err;
}
i = 0;
if (log_get_fd () != -1)
no_close_list[i++] = assuan_fd_from_posix_fd (log_get_fd ());
no_close_list[i++] = assuan_fd_from_posix_fd (es_fileno (es_stderr));
no_close_list[i] = ASSUAN_INVALID_FD;
err = assuan_new (&ctx);
if (err)
{
log_error ("can't allocate assuan context: %s\n", gpg_strerror (err));
return err;
}
/* Call userv to start g13-syshelp. This userv script needs to be
* installed under the name "gnupg-g13-syshelp":
*
* if ( glob service-user root
* )
* reset
* suppress-args
* execute /home/wk/b/gnupg/g13/g13-syshelp -v
* else
* error Nothing to do for this service-user
* fi
* quit
*/
{
const char *argv[4];
argv[0] = "userv";
argv[1] = "root";
argv[2] = "gnupg-g13-syshelp";
argv[3] = NULL;
err = assuan_pipe_connect (ctx, "/usr/bin/userv", argv,
no_close_list, NULL, NULL, 0);
}
if (err)
{
log_error ("can't connect to '%s': %s %s\n",
"g13-syshelp", gpg_strerror (err), gpg_strsource (err));
log_info ("(is userv and its gnupg-g13-syshelp script installed?)\n");
assuan_release (ctx);
return err;
}
*r_ctx = ctrl->syshelp_local->assctx = ctx;
if (DBG_IPC)
log_debug ("connection to g13-syshelp established\n");
return 0;
}
/* Release local resources associated with CTRL. */
void
call_syshelp_release (ctrl_t ctrl)
{
if (!ctrl)
return;
if (ctrl->syshelp_local)
{
assuan_release (ctrl->syshelp_local->assctx);
ctrl->syshelp_local->assctx = NULL;
xfree (ctrl->syshelp_local);
ctrl->syshelp_local = NULL;
}
}
/* Staus callback for call_syshelp_find_device. */
static gpg_error_t
finddevice_status_cb (void *opaque, const char *line)
{
char **r_blockdev = opaque;
char *p;
if ((p = has_leading_keyword (line, "BLOCKDEV")) && *p && !*r_blockdev)
{
*r_blockdev = xtrystrdup (p);
if (!*r_blockdev)
return gpg_error_from_syserror ();
}
return 0;
}
/* Send the FINDDEVICE command to the syshelper. On success the name
* of the block device is stored at R_BLOCKDEV. */
gpg_error_t
call_syshelp_find_device (ctrl_t ctrl, const char *name, char **r_blockdev)
{
gpg_error_t err;
assuan_context_t ctx;
char *line = NULL;
char *blockdev = NULL; /* The result. */
*r_blockdev = NULL;
err = start_syshelp (ctrl, &ctx);
if (err)
goto leave;
line = xtryasprintf ("FINDDEVICE %s", name);
if (!line)
{
err = gpg_error_from_syserror ();
goto leave;
}
err = assuan_transact (ctx, line, NULL, NULL, NULL, NULL,
finddevice_status_cb, &blockdev);
if (err)
goto leave;
if (!blockdev)
{
log_error ("status line for successful FINDDEVICE missing\n");
err = gpg_error (GPG_ERR_UNEXPECTED);
goto leave;
}
*r_blockdev = blockdev;
blockdev = NULL;
leave:
xfree (blockdev);
xfree (line);
return err;
}
static gpg_error_t
getkeyblob_data_cb (void *opaque, const void *data, size_t datalen)
{
membuf_t *mb = opaque;
if (data)
put_membuf (mb, data, datalen);
return 0;
}
/* Send the GTEKEYBLOB command to the syshelper. On success the
* encrypted keyblpob is stored at (R_ENCKEYBLOB,R_ENCKEYBLOBLEN). */
gpg_error_t
call_syshelp_get_keyblob (ctrl_t ctrl,
void **r_enckeyblob, size_t *r_enckeybloblen)
{
gpg_error_t err;
assuan_context_t ctx;
membuf_t mb;
*r_enckeyblob = NULL;
*r_enckeybloblen = 0;
init_membuf (&mb, 512);
err = start_syshelp (ctrl, &ctx);
if (err)
goto leave;
err = assuan_transact (ctx, "GETKEYBLOB",
getkeyblob_data_cb, &mb,
NULL, NULL, NULL, NULL);
if (err)
goto leave;
*r_enckeyblob = get_membuf (&mb, r_enckeybloblen);
if (!*r_enckeyblob)
err = gpg_error_from_syserror ();
leave:
xfree (get_membuf (&mb, NULL));
return err;
}
/* Send the DEVICE command to the syshelper. FNAME is the name of the
device. */
gpg_error_t
call_syshelp_set_device (ctrl_t ctrl, const char *fname)
{
gpg_error_t err;
assuan_context_t ctx;
char *line = NULL;
err = start_syshelp (ctrl, &ctx);
if (err)
goto leave;
line = xtryasprintf ("DEVICE %s", fname);
if (!line)
{
err = gpg_error_from_syserror ();
goto leave;
}
err = assuan_transact (ctx, line, NULL, NULL, NULL, NULL, NULL, NULL);
leave:
xfree (line);
return err;
}
static gpg_error_t
create_status_cb (void *opaque, const char *line)
{
struct create_parm_s *parm = opaque;
if (has_leading_keyword (line, "PLAINTEXT_FOLLOWS"))
parm->expect_plaintext = 1;
return 0;
}
static gpg_error_t
create_data_cb (void *opaque, const void *data, size_t datalen)
{
struct create_parm_s *parm = opaque;
gpg_error_t err = 0;
if (!parm->expect_plaintext)
{
log_error ("status line for data missing\n");
err = gpg_error (GPG_ERR_UNEXPECTED);
}
else if (data)
{
put_membuf (&parm->plaintext, data, datalen);
}
else
{
parm->expect_plaintext = 0;
parm->got_plaintext = 1;
}
return err;
}
static gpg_error_t
create_inq_cb (void *opaque, const char *line)
{
struct create_parm_s *parm = opaque;
gpg_error_t err;
if (has_leading_keyword (line, "ENCKEYBLOB"))
{
void *plaintext;
size_t plaintextlen;
if (!parm->got_plaintext)
err = gpg_error (GPG_ERR_UNEXPECTED);
else if (!(plaintext = get_membuf (&parm->plaintext, &plaintextlen)))
err = gpg_error_from_syserror ();
else
{
void *ciphertext;
size_t ciphertextlen;
- log_printhex ("plain", plaintext, plaintextlen);
+ log_printhex (plaintext, plaintextlen, "plain");
err = g13_encrypt_keyblob (parm->ctrl,
plaintext, plaintextlen,
&ciphertext, &ciphertextlen);
wipememory (plaintext, plaintextlen);
xfree (plaintext);
if (err)
log_error ("error encrypting keyblob: %s\n", gpg_strerror (err));
else
{
err = assuan_send_data (parm->ctx, ciphertext, ciphertextlen);
xfree (ciphertext);
if (err)
log_error ("sending ciphertext to g13-syshelp failed: %s\n",
gpg_strerror (err));
}
}
}
else
err = gpg_error (GPG_ERR_ASS_UNKNOWN_INQUIRE);
return err;
}
/* Run the CREATE command on the current device. CONTTYPES gives the
requested content type for the new container. */
gpg_error_t
call_syshelp_run_create (ctrl_t ctrl, int conttype)
{
gpg_error_t err;
assuan_context_t ctx;
struct create_parm_s parm;
memset (&parm, 0, sizeof parm);
err = start_syshelp (ctrl, &ctx);
if (err)
goto leave;
/* tty_get ("waiting for debugger"); */
/* tty_kill_prompt (); */
parm.ctx = ctx;
parm.ctrl = ctrl;
init_membuf (&parm.plaintext, 512);
if (conttype == CONTTYPE_DM_CRYPT)
{
err = assuan_transact (ctx, "CREATE dm-crypt",
create_data_cb, &parm,
create_inq_cb, &parm,
create_status_cb, &parm);
}
else
{
log_error ("invalid backend type %d given\n", conttype);
err = GPG_ERR_INTERNAL;
goto leave;
}
leave:
xfree (get_membuf (&parm.plaintext, NULL));
return err;
}
static gpg_error_t
mount_status_cb (void *opaque, const char *line)
{
struct mount_parm_s *parm = opaque;
/* Nothing right now. */
(void)parm;
(void)line;
return 0;
}
/* Inquire callback for MOUNT and RESUME. */
static gpg_error_t
mount_inq_cb (void *opaque, const char *line)
{
struct mount_parm_s *parm = opaque;
gpg_error_t err;
if (has_leading_keyword (line, "KEYBLOB"))
{
int setconfidential = !assuan_get_flag (parm->ctx, ASSUAN_CONFIDENTIAL);
if (setconfidential)
assuan_begin_confidential (parm->ctx);
err = assuan_send_data (parm->ctx, parm->keyblob, parm->keybloblen);
if (setconfidential)
assuan_end_confidential (parm->ctx);
if (err)
log_error ("sending keyblob to g13-syshelp failed: %s\n",
gpg_strerror (err));
}
else
err = gpg_error (GPG_ERR_ASS_UNKNOWN_INQUIRE);
return err;
}
/*
* Run the MOUNT command on the current device. CONTTYPES gives the
* requested content type for the new container. MOUNTPOINT the
* desired mount point or NULL for default.
*/
gpg_error_t
call_syshelp_run_mount (ctrl_t ctrl, int conttype, const char *mountpoint,
tupledesc_t tuples)
{
gpg_error_t err;
assuan_context_t ctx;
struct mount_parm_s parm;
memset (&parm, 0, sizeof parm);
err = start_syshelp (ctrl, &ctx);
if (err)
goto leave;
/* tty_get ("waiting for debugger"); */
/* tty_kill_prompt (); */
parm.ctx = ctx;
parm.ctrl = ctrl;
if (conttype == CONTTYPE_DM_CRYPT)
{
ref_tupledesc (tuples);
parm.keyblob = get_tupledesc_data (tuples, &parm.keybloblen);
err = assuan_transact (ctx, "MOUNT dm-crypt",
NULL, NULL,
mount_inq_cb, &parm,
mount_status_cb, &parm);
unref_tupledesc (tuples);
}
else
{
(void)mountpoint; /* Not used. */
log_error ("invalid backend type %d given\n", conttype);
err = GPG_ERR_INTERNAL;
goto leave;
}
leave:
return err;
}
/*
* Run the UMOUNT command on the current device. CONTTYPES gives the
* content type of the container (fixme: Do we really need this?).
*/
gpg_error_t
call_syshelp_run_umount (ctrl_t ctrl, int conttype)
{
gpg_error_t err;
assuan_context_t ctx;
err = start_syshelp (ctrl, &ctx);
if (err)
goto leave;
if (conttype == CONTTYPE_DM_CRYPT)
{
err = assuan_transact (ctx, "UMOUNT dm-crypt",
NULL, NULL,
NULL, NULL,
NULL, NULL);
}
else
{
log_error ("invalid backend type %d given\n", conttype);
err = GPG_ERR_INTERNAL;
goto leave;
}
leave:
return err;
}
/*
* Run the SUSPEND command on the current device. CONTTYPES gives the
* requested content type for the new container.
*/
gpg_error_t
call_syshelp_run_suspend (ctrl_t ctrl, int conttype)
{
gpg_error_t err;
assuan_context_t ctx;
err = start_syshelp (ctrl, &ctx);
if (err)
goto leave;
if (conttype == CONTTYPE_DM_CRYPT)
{
err = assuan_transact (ctx, "SUSPEND dm-crypt",
NULL, NULL,
NULL, NULL,
NULL, NULL);
}
else
{
log_error ("invalid backend type %d given\n", conttype);
err = GPG_ERR_INTERNAL;
goto leave;
}
leave:
return err;
}
/* Run the RESUME command on the current device. CONTTYPES gives the
requested content type for the container. */
gpg_error_t
call_syshelp_run_resume (ctrl_t ctrl, int conttype, tupledesc_t tuples)
{
gpg_error_t err;
assuan_context_t ctx;
struct mount_parm_s parm;
memset (&parm, 0, sizeof parm);
err = start_syshelp (ctrl, &ctx);
if (err)
goto leave;
/* tty_get ("waiting for debugger"); */
/* tty_kill_prompt (); */
parm.ctx = ctx;
parm.ctrl = ctrl;
if (conttype == CONTTYPE_DM_CRYPT)
{
ref_tupledesc (tuples);
parm.keyblob = get_tupledesc_data (tuples, &parm.keybloblen);
err = assuan_transact (ctx, "RESUME dm-crypt",
NULL, NULL,
mount_inq_cb, &parm,
NULL, NULL);
unref_tupledesc (tuples);
}
else
{
log_error ("invalid backend type %d given\n", conttype);
err = GPG_ERR_INTERNAL;
goto leave;
}
leave:
return err;
}
diff --git a/g13/g13tuple.c b/g13/g13tuple.c
index b10ebbc9a..6693826ad 100644
--- a/g13/g13tuple.c
+++ b/g13/g13tuple.c
@@ -1,340 +1,340 @@
/* g13tuple.c - Tuple handling
* Copyright (C) 2009 Free Software Foundation, Inc.
* Copyright (C) 2009, 2015, 2016 Werner Koch
*
* This file is part of GnuPG.
*
* GnuPG is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* GnuPG is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see .
*/
#include
#include
#include
#include
#include
#include
#include "g13.h"
#include "g13tuple.h"
#include "keyblob.h" /* Required for dump_tupledesc. */
/* Definition of the tuple descriptor object. */
struct tupledesc_s
{
unsigned char *data; /* The tuple data. */
size_t datalen; /* The length of the data. */
size_t pos; /* The current position as used by next_tuple. */
int refcount; /* Number of references hold. */
};
/* Append the TAG and the VALUE to the MEMBUF. There is no error
checking here; this is instead done while getting the value back
from the membuf. */
void
append_tuple (membuf_t *membuf, int tag, const void *value, size_t length)
{
unsigned char buf[2];
assert (tag >= 0 && tag <= 0xffff);
assert (length <= 0xffff);
buf[0] = tag >> 8;
buf[1] = tag;
put_membuf (membuf, buf, 2);
buf[0] = length >> 8;
buf[1] = length;
put_membuf (membuf, buf, 2);
if (length)
put_membuf (membuf, value, length);
}
/* Append the unsigned integer VALUE under TAG to MEMBUF. We make
* sure that the most significant bit is always cleared to explicitly
* flag the value as unsigned. */
void
append_tuple_uint (membuf_t *membuf, int tag, unsigned long long value)
{
unsigned char buf[16];
unsigned char *p;
unsigned int len;
p = buf + sizeof buf;
len = 0;
do
{
if (p == buf)
BUG () ;
*--p = (value & 0xff);
value >>= 8;
len++;
}
while (value);
/* Prepend a zero byte if the first byte has its MSB set. */
if ((*p & 0x80))
{
if (p == buf)
BUG () ;
*--p = 0;
len++;
}
append_tuple (membuf, tag, p, len);
}
/* Create a tuple object by moving the ownership of (DATA,DATALEN) to
* a new object. Returns 0 on success and stores the new object at
* R_TUPLEHD. The return object must be released using
* destroy_tuples(). */
gpg_error_t
create_tupledesc (tupledesc_t *r_desc, void *data, size_t datalen)
{
if (datalen < 5 || memcmp (data, "\x00\x00\x00\x01\x01", 5))
return gpg_error (GPG_ERR_NOT_SUPPORTED);
*r_desc = xtrymalloc (sizeof **r_desc);
if (!*r_desc)
return gpg_error_from_syserror ();
(*r_desc)->data = data;
(*r_desc)->datalen = datalen;
(*r_desc)->pos = 0;
(*r_desc)->refcount = 1;
return 0;
}
/* Unref a tuple descriptor and if the refcount is down to 0 release
its allocated storage. */
void
destroy_tupledesc (tupledesc_t tupledesc)
{
if (!tupledesc)
return;
if (!--tupledesc->refcount)
{
xfree (tupledesc->data);
xfree (tupledesc);
}
}
tupledesc_t
ref_tupledesc (tupledesc_t tupledesc)
{
if (tupledesc)
tupledesc->refcount++;
return tupledesc;
}
/* Return a pointer to the memory used to store the tuples. This is
* the data originally provided to create_tupledesc. It is higly
* recommended that the callers uses ref_tupledesc before calling this
* function and unref_tupledesc when the return data will not anymore
* be used. */
const void *
get_tupledesc_data (tupledesc_t tupledesc, size_t *r_datalen)
{
*r_datalen = tupledesc->datalen;
return tupledesc->data;
}
/* Find the first tuple with tag TAG. On success return a pointer to
its value and store the length of the value at R_LENGTH. If no
tuple was found return NULL. For use by next_tuple, the last
position is stored in the descriptor. */
const void *
find_tuple (tupledesc_t tupledesc, unsigned int tag, size_t *r_length)
{
const unsigned char *s;
const unsigned char *s_end; /* Points right behind the data. */
unsigned int t;
size_t n;
s = tupledesc->data;
if (!s)
return NULL;
s_end = s + tupledesc->datalen;
while (s < s_end)
{
/* We use addresses for the overflow check to avoid undefined
behaviour. size_t should work with all flat memory models. */
if ((size_t)s+3 >= (size_t)s_end || (size_t)s + 3 < (size_t)s)
break;
t = s[0] << 8;
t |= s[1];
n = s[2] << 8;
n |= s[3];
s += 4;
if ((size_t)s + n > (size_t)s_end || (size_t)s + n < (size_t)s)
break;
if (t == tag)
{
tupledesc->pos = (s + n) - tupledesc->data;
*r_length = n;
return s;
}
s += n;
}
return NULL;
}
/* Helper for find_tuple_uint and others. */
static gpg_error_t
convert_uint (const unsigned char *s, size_t n, unsigned long long *r_value)
{
unsigned long long value = 0;
*r_value = 0;
if (!s)
return gpg_error (GPG_ERR_NOT_FOUND);
if (!n || (*s & 0x80)) /* No bytes or negative. */
return gpg_error (GPG_ERR_ERANGE);
if (n && !*s) /* Skip a leading zero. */
{
n--;
s++;
}
if (n > sizeof value)
return gpg_error (GPG_ERR_ERANGE);
for (; n; n--, s++)
{
value <<= 8;
value |= *s;
}
*r_value = value;
return 0;
}
/* Similar to find-tuple but expects an unsigned int value and stores
* that at R_VALUE. If the tag was not found GPG_ERR_NOT_FOUND is
* returned and 0 stored at R_VALUE. If the value cannot be converted
* to an unsigned integer GPG_ERR_ERANGE is returned. */
gpg_error_t
find_tuple_uint (tupledesc_t tupledesc, unsigned int tag,
unsigned long long *r_value)
{
const unsigned char *s;
size_t n;
s = find_tuple (tupledesc, tag, &n);
return convert_uint (s, n, r_value);
}
const void *
next_tuple (tupledesc_t tupledesc, unsigned int *r_tag, size_t *r_length)
{
const unsigned char *s;
const unsigned char *s_end; /* Points right behind the data. */
unsigned int t;
size_t n;
s = tupledesc->data;
if (!s)
return NULL;
s_end = s + tupledesc->datalen;
s += tupledesc->pos;
if (s < s_end
&& !((size_t)s + 3 >= (size_t)s_end || (size_t)s + 3 < (size_t)s))
{
t = s[0] << 8;
t |= s[1];
n = s[2] << 8;
n |= s[3];
s += 4;
if (!((size_t)s + n > (size_t)s_end || (size_t)s + n < (size_t)s))
{
tupledesc->pos = (s + n) - tupledesc->data;
*r_tag = t;
*r_length = n;
return s;
}
}
return NULL;
}
/* Return true if BUF has only printable characters. */
static int
all_printable (const void *buf, size_t buflen)
{
const unsigned char *s;
for (s=buf ; buflen; s++, buflen--)
if (*s < 32 || *s > 126)
return 0;
return 1;
}
/* Print information about TUPLES to the log stream. */
void
dump_tupledesc (tupledesc_t tuples)
{
size_t n;
unsigned int tag;
const void *value;
unsigned long long uint;
log_info ("keyblob dump:\n");
tag = KEYBLOB_TAG_BLOBVERSION;
value = find_tuple (tuples, tag, &n);
while (value)
{
log_info (" tag: %-5u len: %-2u value: ", tag, (unsigned int)n);
if (!n)
log_printf ("[none]\n");
else
{
switch (tag)
{
case KEYBLOB_TAG_ENCKEY:
case KEYBLOB_TAG_MACKEY:
log_printf ("[confidential]\n");
break;
case KEYBLOB_TAG_ALGOSTR:
if (n < 100 && all_printable (value, n))
log_printf ("%.*s\n", (int)n, (const char*)value);
else
- log_printhex ("", value, n);
+ log_printhex (value, n, "");
break;
case KEYBLOB_TAG_CONT_NSEC:
case KEYBLOB_TAG_ENC_NSEC:
case KEYBLOB_TAG_ENC_OFF:
if (!convert_uint (value, n, &uint))
log_printf ("%llu\n", uint);
else
- log_printhex ("", value, n);
+ log_printhex (value, n, "");
break;
default:
- log_printhex ("", value, n);
+ log_printhex (value, n, "");
break;
}
}
value = next_tuple (tuples, &tag, &n);
}
}
diff --git a/scd/apdu.c b/scd/apdu.c
index 274f2db01..dfd9451de 100644
--- a/scd/apdu.c
+++ b/scd/apdu.c
@@ -1,3331 +1,3331 @@
/* apdu.c - ISO 7816 APDU functions and low level I/O
* Copyright (C) 2003, 2004, 2008, 2009, 2010,
* 2011 Free Software Foundation, Inc.
*
* This file is part of GnuPG.
*
* GnuPG is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* GnuPG is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see .
*/
/* NOTE: This module is also used by other software, thus the use of
the macro USE_NPTH is mandatory. For GnuPG this macro is
guaranteed to be defined true. */
#include
#include
#include
#include
#include
#include
#include
#ifdef USE_NPTH
# include
# include
# include
#endif
/* If requested include the definitions for the remote APDU protocol
code. */
#ifdef USE_G10CODE_RAPDU
#include "rapdu.h"
#endif /*USE_G10CODE_RAPDU*/
#if defined(GNUPG_SCD_MAIN_HEADER)
#include GNUPG_SCD_MAIN_HEADER
#elif GNUPG_MAJOR_VERSION == 1
/* This is used with GnuPG version < 1.9. The code has been source
copied from the current GnuPG >= 1.9 and is maintained over
there. */
#include "../common/options.h"
#include "errors.h"
#include "memory.h"
#include "../common/util.h"
#include "../common/i18n.h"
#include "dynload.h"
#include "cardglue.h"
#else /* GNUPG_MAJOR_VERSION != 1 */
#include "scdaemon.h"
#include "../common/exechelp.h"
#endif /* GNUPG_MAJOR_VERSION != 1 */
#include "../common/host2net.h"
#include "iso7816.h"
#include "apdu.h"
#define CCID_DRIVER_INCLUDE_USB_IDS 1
#include "ccid-driver.h"
struct dev_list {
struct ccid_dev_table *ccid_table;
const char *portstr;
int idx;
int idx_max;
};
#define MAX_READER 4 /* Number of readers we support concurrently. */
#if defined(_WIN32) || defined(__CYGWIN__)
#define DLSTDCALL __stdcall
#else
#define DLSTDCALL
#endif
#if defined(__APPLE__) || defined(_WIN32) || defined(__CYGWIN__)
typedef unsigned int pcsc_dword_t;
#else
typedef unsigned long pcsc_dword_t;
#endif
/* A structure to collect information pertaining to one reader
slot. */
struct reader_table_s {
int used; /* True if slot is used. */
unsigned short port; /* Port number: 0 = unused, 1 - dev/tty */
/* Function pointers initialized to the various backends. */
int (*connect_card)(int);
int (*disconnect_card)(int);
int (*close_reader)(int);
int (*reset_reader)(int);
int (*get_status_reader)(int, unsigned int *, int);
int (*send_apdu_reader)(int,unsigned char *,size_t,
unsigned char *, size_t *, pininfo_t *);
int (*check_pinpad)(int, int, pininfo_t *);
void (*dump_status_reader)(int);
int (*set_progress_cb)(int, gcry_handler_progress_t, void*);
int (*set_prompt_cb)(int, void (*) (void *, int), void*);
int (*pinpad_verify)(int, int, int, int, int, pininfo_t *);
int (*pinpad_modify)(int, int, int, int, int, pininfo_t *);
struct {
ccid_driver_t handle;
} ccid;
struct {
long context;
long card;
pcsc_dword_t protocol;
pcsc_dword_t verify_ioctl;
pcsc_dword_t modify_ioctl;
int pinmin;
int pinmax;
pcsc_dword_t current_state;
} pcsc;
#ifdef USE_G10CODE_RAPDU
struct {
rapdu_t handle;
} rapdu;
#endif /*USE_G10CODE_RAPDU*/
char *rdrname; /* Name of the connected reader or NULL if unknown. */
unsigned int is_t0:1; /* True if we know that we are running T=0. */
unsigned int is_spr532:1; /* True if we know that the reader is a SPR532. */
unsigned int pinpad_varlen_supported:1; /* True if we know that the reader
supports variable length pinpad
input. */
unsigned int require_get_status:1;
unsigned char atr[33];
size_t atrlen; /* A zero length indicates that the ATR has
not yet been read; i.e. the card is not
ready for use. */
#ifdef USE_NPTH
npth_mutex_t lock;
#endif
};
typedef struct reader_table_s *reader_table_t;
/* A global table to keep track of active readers. */
static struct reader_table_s reader_table[MAX_READER];
#ifdef USE_NPTH
static npth_mutex_t reader_table_lock;
#endif
/* PC/SC constants and function pointer. */
#define PCSC_SCOPE_USER 0
#define PCSC_SCOPE_TERMINAL 1
#define PCSC_SCOPE_SYSTEM 2
#define PCSC_SCOPE_GLOBAL 3
#define PCSC_PROTOCOL_T0 1
#define PCSC_PROTOCOL_T1 2
#ifdef HAVE_W32_SYSTEM
# define PCSC_PROTOCOL_RAW 0x00010000 /* The active protocol. */
#else
# define PCSC_PROTOCOL_RAW 4
#endif
#define PCSC_SHARE_EXCLUSIVE 1
#define PCSC_SHARE_SHARED 2
#define PCSC_SHARE_DIRECT 3
#define PCSC_LEAVE_CARD 0
#define PCSC_RESET_CARD 1
#define PCSC_UNPOWER_CARD 2
#define PCSC_EJECT_CARD 3
#ifdef HAVE_W32_SYSTEM
# define PCSC_UNKNOWN 0x0000 /* The driver is not aware of the status. */
# define PCSC_ABSENT 0x0001 /* Card is absent. */
# define PCSC_PRESENT 0x0002 /* Card is present. */
# define PCSC_SWALLOWED 0x0003 /* Card is present and electrical connected. */
# define PCSC_POWERED 0x0004 /* Card is powered. */
# define PCSC_NEGOTIABLE 0x0005 /* Card is awaiting PTS. */
# define PCSC_SPECIFIC 0x0006 /* Card is ready for use. */
#else
# define PCSC_UNKNOWN 0x0001
# define PCSC_ABSENT 0x0002 /* Card is absent. */
# define PCSC_PRESENT 0x0004 /* Card is present. */
# define PCSC_SWALLOWED 0x0008 /* Card is present and electrical connected. */
# define PCSC_POWERED 0x0010 /* Card is powered. */
# define PCSC_NEGOTIABLE 0x0020 /* Card is awaiting PTS. */
# define PCSC_SPECIFIC 0x0040 /* Card is ready for use. */
#endif
#define PCSC_STATE_UNAWARE 0x0000 /* Want status. */
#define PCSC_STATE_IGNORE 0x0001 /* Ignore this reader. */
#define PCSC_STATE_CHANGED 0x0002 /* State has changed. */
#define PCSC_STATE_UNKNOWN 0x0004 /* Reader unknown. */
#define PCSC_STATE_UNAVAILABLE 0x0008 /* Status unavailable. */
#define PCSC_STATE_EMPTY 0x0010 /* Card removed. */
#define PCSC_STATE_PRESENT 0x0020 /* Card inserted. */
#define PCSC_STATE_ATRMATCH 0x0040 /* ATR matches card. */
#define PCSC_STATE_EXCLUSIVE 0x0080 /* Exclusive Mode. */
#define PCSC_STATE_INUSE 0x0100 /* Shared mode. */
#define PCSC_STATE_MUTE 0x0200 /* Unresponsive card. */
#ifdef HAVE_W32_SYSTEM
# define PCSC_STATE_UNPOWERED 0x0400 /* Card not powerred up. */
#endif
/* Some PC/SC error codes. */
#define PCSC_E_CANCELLED 0x80100002
#define PCSC_E_CANT_DISPOSE 0x8010000E
#define PCSC_E_INSUFFICIENT_BUFFER 0x80100008
#define PCSC_E_INVALID_ATR 0x80100015
#define PCSC_E_INVALID_HANDLE 0x80100003
#define PCSC_E_INVALID_PARAMETER 0x80100004
#define PCSC_E_INVALID_TARGET 0x80100005
#define PCSC_E_INVALID_VALUE 0x80100011
#define PCSC_E_NO_MEMORY 0x80100006
#define PCSC_E_UNKNOWN_READER 0x80100009
#define PCSC_E_TIMEOUT 0x8010000A
#define PCSC_E_SHARING_VIOLATION 0x8010000B
#define PCSC_E_NO_SMARTCARD 0x8010000C
#define PCSC_E_UNKNOWN_CARD 0x8010000D
#define PCSC_E_PROTO_MISMATCH 0x8010000F
#define PCSC_E_NOT_READY 0x80100010
#define PCSC_E_SYSTEM_CANCELLED 0x80100012
#define PCSC_E_NOT_TRANSACTED 0x80100016
#define PCSC_E_READER_UNAVAILABLE 0x80100017
#define PCSC_E_NO_SERVICE 0x8010001D
#define PCSC_E_SERVICE_STOPPED 0x8010001E
#define PCSC_W_RESET_CARD 0x80100068
#define PCSC_W_REMOVED_CARD 0x80100069
/* Fix pcsc-lite ABI incompatibility. */
#ifndef SCARD_CTL_CODE
#ifdef _WIN32
#include
#define SCARD_CTL_CODE(code) CTL_CODE(FILE_DEVICE_SMARTCARD, (code), \
METHOD_BUFFERED, FILE_ANY_ACCESS)
#else
#define SCARD_CTL_CODE(code) (0x42000000 + (code))
#endif
#endif
#define CM_IOCTL_GET_FEATURE_REQUEST SCARD_CTL_CODE(3400)
#define CM_IOCTL_VENDOR_IFD_EXCHANGE SCARD_CTL_CODE(1)
#define FEATURE_VERIFY_PIN_DIRECT 0x06
#define FEATURE_MODIFY_PIN_DIRECT 0x07
#define FEATURE_GET_TLV_PROPERTIES 0x12
#define PCSCv2_PART10_PROPERTY_bEntryValidationCondition 2
#define PCSCv2_PART10_PROPERTY_bTimeOut2 3
#define PCSCv2_PART10_PROPERTY_bMinPINSize 6
#define PCSCv2_PART10_PROPERTY_bMaxPINSize 7
#define PCSCv2_PART10_PROPERTY_wIdVendor 11
#define PCSCv2_PART10_PROPERTY_wIdProduct 12
/* The PC/SC error is defined as a long as per specs. Due to left
shifts bit 31 will get sign extended. We use this mask to fix
it. */
#define PCSC_ERR_MASK(a) ((a) & 0xffffffff)
struct pcsc_io_request_s
{
unsigned long protocol;
unsigned long pci_len;
};
typedef struct pcsc_io_request_s *pcsc_io_request_t;
#ifdef __APPLE__
#pragma pack(1)
#endif
struct pcsc_readerstate_s
{
const char *reader;
void *user_data;
pcsc_dword_t current_state;
pcsc_dword_t event_state;
pcsc_dword_t atrlen;
unsigned char atr[33];
};
#ifdef __APPLE__
#pragma pack()
#endif
typedef struct pcsc_readerstate_s *pcsc_readerstate_t;
long (* DLSTDCALL pcsc_establish_context) (pcsc_dword_t scope,
const void *reserved1,
const void *reserved2,
long *r_context);
long (* DLSTDCALL pcsc_release_context) (long context);
long (* DLSTDCALL pcsc_list_readers) (long context,
const char *groups,
char *readers, pcsc_dword_t*readerslen);
long (* DLSTDCALL pcsc_get_status_change) (long context,
pcsc_dword_t timeout,
pcsc_readerstate_t readerstates,
pcsc_dword_t nreaderstates);
long (* DLSTDCALL pcsc_connect) (long context,
const char *reader,
pcsc_dword_t share_mode,
pcsc_dword_t preferred_protocols,
long *r_card,
pcsc_dword_t *r_active_protocol);
long (* DLSTDCALL pcsc_reconnect) (long card,
pcsc_dword_t share_mode,
pcsc_dword_t preferred_protocols,
pcsc_dword_t initialization,
pcsc_dword_t *r_active_protocol);
long (* DLSTDCALL pcsc_disconnect) (long card,
pcsc_dword_t disposition);
long (* DLSTDCALL pcsc_status) (long card,
char *reader, pcsc_dword_t *readerlen,
pcsc_dword_t *r_state,
pcsc_dword_t *r_protocol,
unsigned char *atr, pcsc_dword_t *atrlen);
long (* DLSTDCALL pcsc_begin_transaction) (long card);
long (* DLSTDCALL pcsc_end_transaction) (long card,
pcsc_dword_t disposition);
long (* DLSTDCALL pcsc_transmit) (long card,
const pcsc_io_request_t send_pci,
const unsigned char *send_buffer,
pcsc_dword_t send_len,
pcsc_io_request_t recv_pci,
unsigned char *recv_buffer,
pcsc_dword_t *recv_len);
long (* DLSTDCALL pcsc_set_timeout) (long context,
pcsc_dword_t timeout);
long (* DLSTDCALL pcsc_control) (long card,
pcsc_dword_t control_code,
const void *send_buffer,
pcsc_dword_t send_len,
void *recv_buffer,
pcsc_dword_t recv_len,
pcsc_dword_t *bytes_returned);
/* Prototypes. */
static int pcsc_vendor_specific_init (int slot);
static int pcsc_get_status (int slot, unsigned int *status, int on_wire);
static int reset_pcsc_reader (int slot);
static int apdu_get_status_internal (int slot, int hang, unsigned int *status,
int on_wire);
static int check_pcsc_pinpad (int slot, int command, pininfo_t *pininfo);
static int pcsc_pinpad_verify (int slot, int class, int ins, int p0, int p1,
pininfo_t *pininfo);
static int pcsc_pinpad_modify (int slot, int class, int ins, int p0, int p1,
pininfo_t *pininfo);
/*
Helper
*/
static int
lock_slot (int slot)
{
#ifdef USE_NPTH
int err;
err = npth_mutex_lock (&reader_table[slot].lock);
if (err)
{
log_error ("failed to acquire apdu lock: %s\n", strerror (err));
return SW_HOST_LOCKING_FAILED;
}
#endif /*USE_NPTH*/
return 0;
}
static int
trylock_slot (int slot)
{
#ifdef USE_NPTH
int err;
err = npth_mutex_trylock (&reader_table[slot].lock);
if (err == EBUSY)
return SW_HOST_BUSY;
else if (err)
{
log_error ("failed to acquire apdu lock: %s\n", strerror (err));
return SW_HOST_LOCKING_FAILED;
}
#endif /*USE_NPTH*/
return 0;
}
static void
unlock_slot (int slot)
{
#ifdef USE_NPTH
int err;
err = npth_mutex_unlock (&reader_table[slot].lock);
if (err)
log_error ("failed to release apdu lock: %s\n", strerror (errno));
#endif /*USE_NPTH*/
}
/* Find an unused reader slot for PORTSTR and put it into the reader
table. Return -1 on error or the index into the reader table.
Acquire slot's lock on successful return. Caller needs to unlock it. */
static int
new_reader_slot (void)
{
int i, reader = -1;
for (i=0; i < MAX_READER; i++)
if (!reader_table[i].used)
{
reader = i;
reader_table[reader].used = 1;
break;
}
if (reader == -1)
{
log_error ("new_reader_slot: out of slots\n");
return -1;
}
if (lock_slot (reader))
{
reader_table[reader].used = 0;
return -1;
}
reader_table[reader].connect_card = NULL;
reader_table[reader].disconnect_card = NULL;
reader_table[reader].close_reader = NULL;
reader_table[reader].reset_reader = NULL;
reader_table[reader].get_status_reader = NULL;
reader_table[reader].send_apdu_reader = NULL;
reader_table[reader].check_pinpad = check_pcsc_pinpad;
reader_table[reader].dump_status_reader = NULL;
reader_table[reader].set_progress_cb = NULL;
reader_table[reader].set_prompt_cb = NULL;
reader_table[reader].pinpad_verify = pcsc_pinpad_verify;
reader_table[reader].pinpad_modify = pcsc_pinpad_modify;
reader_table[reader].is_t0 = 1;
reader_table[reader].is_spr532 = 0;
reader_table[reader].pinpad_varlen_supported = 0;
reader_table[reader].require_get_status = 1;
reader_table[reader].pcsc.verify_ioctl = 0;
reader_table[reader].pcsc.modify_ioctl = 0;
reader_table[reader].pcsc.pinmin = -1;
reader_table[reader].pcsc.pinmax = -1;
reader_table[reader].pcsc.current_state = PCSC_STATE_UNAWARE;
return reader;
}
static void
dump_reader_status (int slot)
{
if (!opt.verbose)
return;
if (reader_table[slot].dump_status_reader)
reader_table[slot].dump_status_reader (slot);
if (reader_table[slot].atrlen)
{
log_info ("slot %d: ATR=", slot);
- log_printhex ("", reader_table[slot].atr, reader_table[slot].atrlen);
+ log_printhex (reader_table[slot].atr, reader_table[slot].atrlen, "");
}
}
static const char *
host_sw_string (long err)
{
switch (err)
{
case 0: return "okay";
case SW_HOST_OUT_OF_CORE: return "out of core";
case SW_HOST_INV_VALUE: return "invalid value";
case SW_HOST_NO_DRIVER: return "no driver";
case SW_HOST_NOT_SUPPORTED: return "not supported";
case SW_HOST_LOCKING_FAILED: return "locking failed";
case SW_HOST_BUSY: return "busy";
case SW_HOST_NO_CARD: return "no card";
case SW_HOST_CARD_INACTIVE: return "card inactive";
case SW_HOST_CARD_IO_ERROR: return "card I/O error";
case SW_HOST_GENERAL_ERROR: return "general error";
case SW_HOST_NO_READER: return "no reader";
case SW_HOST_ABORTED: return "aborted";
case SW_HOST_NO_PINPAD: return "no pinpad";
case SW_HOST_ALREADY_CONNECTED: return "already connected";
case SW_HOST_CANCELLED: return "cancelled";
default: return "unknown host status error";
}
}
const char *
apdu_strerror (int rc)
{
switch (rc)
{
case SW_EOF_REACHED : return "eof reached";
case SW_EEPROM_FAILURE : return "eeprom failure";
case SW_WRONG_LENGTH : return "wrong length";
case SW_CHV_WRONG : return "CHV wrong";
case SW_CHV_BLOCKED : return "CHV blocked";
case SW_REF_DATA_INV : return "referenced data invalidated";
case SW_USE_CONDITIONS : return "use conditions not satisfied";
case SW_BAD_PARAMETER : return "bad parameter";
case SW_NOT_SUPPORTED : return "not supported";
case SW_FILE_NOT_FOUND : return "file not found";
case SW_RECORD_NOT_FOUND:return "record not found";
case SW_REF_NOT_FOUND : return "reference not found";
case SW_NOT_ENOUGH_MEMORY: return "not enough memory space in the file";
case SW_INCONSISTENT_LC: return "Lc inconsistent with TLV structure.";
case SW_INCORRECT_P0_P1: return "incorrect parameters P0,P1";
case SW_BAD_LC : return "Lc inconsistent with P0,P1";
case SW_BAD_P0_P1 : return "bad P0,P1";
case SW_INS_NOT_SUP : return "instruction not supported";
case SW_CLA_NOT_SUP : return "class not supported";
case SW_SUCCESS : return "success";
default:
if ((rc & ~0x00ff) == SW_MORE_DATA)
return "more data available";
if ( (rc & 0x10000) )
return host_sw_string (rc);
return "unknown status error";
}
}
/*
PC/SC Interface
*/
static const char *
pcsc_error_string (long err)
{
const char *s;
if (!err)
return "okay";
if ((err & 0x80100000) != 0x80100000)
return "invalid PC/SC error code";
err &= 0xffff;
switch (err)
{
case 0x0002: s = "cancelled"; break;
case 0x000e: s = "can't dispose"; break;
case 0x0008: s = "insufficient buffer"; break;
case 0x0015: s = "invalid ATR"; break;
case 0x0003: s = "invalid handle"; break;
case 0x0004: s = "invalid parameter"; break;
case 0x0005: s = "invalid target"; break;
case 0x0011: s = "invalid value"; break;
case 0x0006: s = "no memory"; break;
case 0x0013: s = "comm error"; break;
case 0x0001: s = "internal error"; break;
case 0x0014: s = "unknown error"; break;
case 0x0007: s = "waited too long"; break;
case 0x0009: s = "unknown reader"; break;
case 0x000a: s = "timeout"; break;
case 0x000b: s = "sharing violation"; break;
case 0x000c: s = "no smartcard"; break;
case 0x000d: s = "unknown card"; break;
case 0x000f: s = "proto mismatch"; break;
case 0x0010: s = "not ready"; break;
case 0x0012: s = "system cancelled"; break;
case 0x0016: s = "not transacted"; break;
case 0x0017: s = "reader unavailable"; break;
case 0x0065: s = "unsupported card"; break;
case 0x0066: s = "unresponsive card"; break;
case 0x0067: s = "unpowered card"; break;
case 0x0068: s = "reset card"; break;
case 0x0069: s = "removed card"; break;
case 0x006a: s = "inserted card"; break;
case 0x001f: s = "unsupported feature"; break;
case 0x0019: s = "PCI too small"; break;
case 0x001a: s = "reader unsupported"; break;
case 0x001b: s = "duplicate reader"; break;
case 0x001c: s = "card unsupported"; break;
case 0x001d: s = "no service"; break;
case 0x001e: s = "service stopped"; break;
default: s = "unknown PC/SC error code"; break;
}
return s;
}
/* Map PC/SC error codes to our special host status words. */
static int
pcsc_error_to_sw (long ec)
{
int rc;
switch ( PCSC_ERR_MASK (ec) )
{
case 0: rc = 0; break;
case PCSC_E_CANCELLED: rc = SW_HOST_CANCELLED; break;
case PCSC_E_NO_MEMORY: rc = SW_HOST_OUT_OF_CORE; break;
case PCSC_E_TIMEOUT: rc = SW_HOST_CARD_IO_ERROR; break;
case PCSC_E_NO_SERVICE:
case PCSC_E_SERVICE_STOPPED:
case PCSC_E_UNKNOWN_READER: rc = SW_HOST_NO_READER; break;
case PCSC_E_SHARING_VIOLATION: rc = SW_HOST_LOCKING_FAILED; break;
case PCSC_E_NO_SMARTCARD: rc = SW_HOST_NO_CARD; break;
case PCSC_W_REMOVED_CARD: rc = SW_HOST_NO_CARD; break;
case PCSC_E_INVALID_TARGET:
case PCSC_E_INVALID_VALUE:
case PCSC_E_INVALID_HANDLE:
case PCSC_E_INVALID_PARAMETER:
case PCSC_E_INSUFFICIENT_BUFFER: rc = SW_HOST_INV_VALUE; break;
default: rc = SW_HOST_GENERAL_ERROR; break;
}
return rc;
}
static void
dump_pcsc_reader_status (int slot)
{
if (reader_table[slot].pcsc.card)
{
log_info ("reader slot %d: active protocol:", slot);
if ((reader_table[slot].pcsc.protocol & PCSC_PROTOCOL_T0))
log_printf (" T0");
else if ((reader_table[slot].pcsc.protocol & PCSC_PROTOCOL_T1))
log_printf (" T1");
else if ((reader_table[slot].pcsc.protocol & PCSC_PROTOCOL_RAW))
log_printf (" raw");
log_printf ("\n");
}
else
log_info ("reader slot %d: not connected\n", slot);
}
static int
pcsc_get_status (int slot, unsigned int *status, int on_wire)
{
long err;
struct pcsc_readerstate_s rdrstates[1];
(void)on_wire;
memset (rdrstates, 0, sizeof *rdrstates);
rdrstates[0].reader = reader_table[slot].rdrname;
rdrstates[0].current_state = reader_table[slot].pcsc.current_state;
err = pcsc_get_status_change (reader_table[slot].pcsc.context,
0,
rdrstates, 1);
if (err == PCSC_E_TIMEOUT)
err = 0; /* Timeout is no error here. */
if (err)
{
log_error ("pcsc_get_status_change failed: %s (0x%lx)\n",
pcsc_error_string (err), err);
return pcsc_error_to_sw (err);
}
if ((rdrstates[0].event_state & PCSC_STATE_CHANGED))
reader_table[slot].pcsc.current_state =
(rdrstates[0].event_state & ~PCSC_STATE_CHANGED);
if (DBG_CARD_IO)
log_debug
("pcsc_get_status_change: %s%s%s%s%s%s%s%s%s%s\n",
(rdrstates[0].event_state & PCSC_STATE_IGNORE)? " ignore":"",
(rdrstates[0].event_state & PCSC_STATE_CHANGED)? " changed":"",
(rdrstates[0].event_state & PCSC_STATE_UNKNOWN)? " unknown":"",
(rdrstates[0].event_state & PCSC_STATE_UNAVAILABLE)?" unavail":"",
(rdrstates[0].event_state & PCSC_STATE_EMPTY)? " empty":"",
(rdrstates[0].event_state & PCSC_STATE_PRESENT)? " present":"",
(rdrstates[0].event_state & PCSC_STATE_ATRMATCH)? " atr":"",
(rdrstates[0].event_state & PCSC_STATE_EXCLUSIVE)? " excl":"",
(rdrstates[0].event_state & PCSC_STATE_INUSE)? " inuse":"",
(rdrstates[0].event_state & PCSC_STATE_MUTE)? " mute":"" );
*status = 0;
if ( (reader_table[slot].pcsc.current_state & PCSC_STATE_PRESENT) )
{
*status |= APDU_CARD_PRESENT;
if ( !(reader_table[slot].pcsc.current_state & PCSC_STATE_MUTE) )
*status |= APDU_CARD_ACTIVE;
}
#ifndef HAVE_W32_SYSTEM
/* We indicate a useful card if it is not in use by another
application. This is because we only use exclusive access
mode. */
if ( (*status & (APDU_CARD_PRESENT|APDU_CARD_ACTIVE))
== (APDU_CARD_PRESENT|APDU_CARD_ACTIVE)
&& !(reader_table[slot].pcsc.current_state & PCSC_STATE_INUSE) )
*status |= APDU_CARD_USABLE;
#else
/* Some winscard drivers may set EXCLUSIVE and INUSE at the same
time when we are the only user (SCM SCR335) under Windows. */
if ((*status & (APDU_CARD_PRESENT|APDU_CARD_ACTIVE))
== (APDU_CARD_PRESENT|APDU_CARD_ACTIVE))
*status |= APDU_CARD_USABLE;
#endif
if (!on_wire && (rdrstates[0].event_state & PCSC_STATE_CHANGED))
/* Event like sleep/resume occurs, which requires RESET. */
return SW_HOST_NO_READER;
else
return 0;
}
/* Send the APDU of length APDULEN to SLOT and return a maximum of
*BUFLEN data in BUFFER, the actual returned size will be stored at
BUFLEN. Returns: A status word. */
static int
pcsc_send_apdu (int slot, unsigned char *apdu, size_t apdulen,
unsigned char *buffer, size_t *buflen,
pininfo_t *pininfo)
{
long err;
struct pcsc_io_request_s send_pci;
pcsc_dword_t recv_len;
(void)pininfo;
if (!reader_table[slot].atrlen
&& (err = reset_pcsc_reader (slot)))
return err;
if (DBG_CARD_IO)
- log_printhex (" PCSC_data:", apdu, apdulen);
+ log_printhex (apdu, apdulen, " PCSC_data:");
if ((reader_table[slot].pcsc.protocol & PCSC_PROTOCOL_T1))
send_pci.protocol = PCSC_PROTOCOL_T1;
else
send_pci.protocol = PCSC_PROTOCOL_T0;
send_pci.pci_len = sizeof send_pci;
recv_len = *buflen;
err = pcsc_transmit (reader_table[slot].pcsc.card,
&send_pci, apdu, apdulen,
NULL, buffer, &recv_len);
*buflen = recv_len;
if (err)
log_error ("pcsc_transmit failed: %s (0x%lx)\n",
pcsc_error_string (err), err);
/* Handle fatal errors which require shutdown of reader. */
if (err == PCSC_E_NOT_TRANSACTED || err == PCSC_W_RESET_CARD
|| err == PCSC_W_REMOVED_CARD)
{
reader_table[slot].pcsc.current_state = PCSC_STATE_UNAWARE;
scd_kick_the_loop ();
}
return pcsc_error_to_sw (err);
}
/* Do some control with the value of IOCTL_CODE to the card inserted
to SLOT. Input buffer is specified by CNTLBUF of length LEN.
Output buffer is specified by BUFFER of length *BUFLEN, and the
actual output size will be stored at BUFLEN. Returns: A status word.
This routine is used for PIN pad input support. */
static int
control_pcsc (int slot, pcsc_dword_t ioctl_code,
const unsigned char *cntlbuf, size_t len,
unsigned char *buffer, pcsc_dword_t *buflen)
{
long err;
err = pcsc_control (reader_table[slot].pcsc.card, ioctl_code,
cntlbuf, len, buffer, buflen? *buflen:0, buflen);
if (err)
{
log_error ("pcsc_control failed: %s (0x%lx)\n",
pcsc_error_string (err), err);
return pcsc_error_to_sw (err);
}
return 0;
}
static int
close_pcsc_reader (int slot)
{
pcsc_release_context (reader_table[slot].pcsc.context);
return 0;
}
/* Connect a PC/SC card. */
static int
connect_pcsc_card (int slot)
{
long err;
assert (slot >= 0 && slot < MAX_READER);
if (reader_table[slot].pcsc.card)
return SW_HOST_ALREADY_CONNECTED;
reader_table[slot].atrlen = 0;
reader_table[slot].is_t0 = 0;
err = pcsc_connect (reader_table[slot].pcsc.context,
reader_table[slot].rdrname,
PCSC_SHARE_EXCLUSIVE,
PCSC_PROTOCOL_T0|PCSC_PROTOCOL_T1,
&reader_table[slot].pcsc.card,
&reader_table[slot].pcsc.protocol);
if (err)
{
reader_table[slot].pcsc.card = 0;
if (err != PCSC_E_NO_SMARTCARD)
log_error ("pcsc_connect failed: %s (0x%lx)\n",
pcsc_error_string (err), err);
}
else
{
char reader[250];
pcsc_dword_t readerlen, atrlen;
pcsc_dword_t card_state, card_protocol;
pcsc_vendor_specific_init (slot);
atrlen = DIM (reader_table[0].atr);
readerlen = sizeof reader -1 ;
err = pcsc_status (reader_table[slot].pcsc.card,
reader, &readerlen,
&card_state, &card_protocol,
reader_table[slot].atr, &atrlen);
if (err)
log_error ("pcsc_status failed: %s (0x%lx) %lu\n",
pcsc_error_string (err), err, (long unsigned int)readerlen);
else
{
if (atrlen > DIM (reader_table[0].atr))
log_bug ("ATR returned by pcsc_status is too large\n");
reader_table[slot].atrlen = atrlen;
reader_table[slot].is_t0 = !!(card_protocol & PCSC_PROTOCOL_T0);
}
}
dump_reader_status (slot);
return pcsc_error_to_sw (err);
}
static int
disconnect_pcsc_card (int slot)
{
long err;
assert (slot >= 0 && slot < MAX_READER);
if (!reader_table[slot].pcsc.card)
return 0;
err = pcsc_disconnect (reader_table[slot].pcsc.card, PCSC_LEAVE_CARD);
if (err)
{
log_error ("pcsc_disconnect failed: %s (0x%lx)\n",
pcsc_error_string (err), err);
return SW_HOST_CARD_IO_ERROR;
}
reader_table[slot].pcsc.card = 0;
return 0;
}
/* Send an PC/SC reset command and return a status word on error or 0
on success. */
static int
reset_pcsc_reader (int slot)
{
int sw;
sw = disconnect_pcsc_card (slot);
if (!sw)
sw = connect_pcsc_card (slot);
return sw;
}
/* Examine reader specific parameters and initialize. This is mostly
for pinpad input. Called at opening the connection to the reader. */
static int
pcsc_vendor_specific_init (int slot)
{
unsigned char buf[256];
pcsc_dword_t len;
int sw;
int vendor = 0;
int product = 0;
pcsc_dword_t get_tlv_ioctl = (pcsc_dword_t)-1;
unsigned char *p;
len = sizeof (buf);
sw = control_pcsc (slot, CM_IOCTL_GET_FEATURE_REQUEST, NULL, 0, buf, &len);
if (sw)
{
log_error ("pcsc_vendor_specific_init: GET_FEATURE_REQUEST failed: %d\n",
sw);
return SW_NOT_SUPPORTED;
}
else
{
p = buf;
while (p < buf + len)
{
unsigned char code = *p++;
int l = *p++;
unsigned int v = 0;
if (l == 1)
v = p[0];
else if (l == 2)
v = buf16_to_uint (p);
else if (l == 4)
v = buf32_to_uint (p);
if (code == FEATURE_VERIFY_PIN_DIRECT)
reader_table[slot].pcsc.verify_ioctl = v;
else if (code == FEATURE_MODIFY_PIN_DIRECT)
reader_table[slot].pcsc.modify_ioctl = v;
else if (code == FEATURE_GET_TLV_PROPERTIES)
get_tlv_ioctl = v;
if (DBG_CARD_IO)
log_debug ("feature: code=%02X, len=%d, v=%02X\n", code, l, v);
p += l;
}
}
if (get_tlv_ioctl == (pcsc_dword_t)-1)
{
/*
* For system which doesn't support GET_TLV_PROPERTIES,
* we put some heuristics here.
*/
if (reader_table[slot].rdrname)
{
if (strstr (reader_table[slot].rdrname, "SPRx32"))
{
reader_table[slot].is_spr532 = 1;
reader_table[slot].pinpad_varlen_supported = 1;
}
else if (strstr (reader_table[slot].rdrname, "ST-2xxx"))
{
reader_table[slot].pcsc.pinmax = 15;
reader_table[slot].pinpad_varlen_supported = 1;
}
else if (strstr (reader_table[slot].rdrname, "cyberJack")
|| strstr (reader_table[slot].rdrname, "DIGIPASS")
|| strstr (reader_table[slot].rdrname, "Gnuk")
|| strstr (reader_table[slot].rdrname, "KAAN")
|| strstr (reader_table[slot].rdrname, "Trustica"))
reader_table[slot].pinpad_varlen_supported = 1;
}
return 0;
}
len = sizeof (buf);
sw = control_pcsc (slot, get_tlv_ioctl, NULL, 0, buf, &len);
if (sw)
{
log_error ("pcsc_vendor_specific_init: GET_TLV_IOCTL failed: %d\n", sw);
return SW_NOT_SUPPORTED;
}
p = buf;
while (p < buf + len)
{
unsigned char tag = *p++;
int l = *p++;
unsigned int v = 0;
/* Umm... here is little endian, while the encoding above is big. */
if (l == 1)
v = p[0];
else if (l == 2)
v = (((unsigned int)p[1] << 8) | p[0]);
else if (l == 4)
v = (((unsigned int)p[3] << 24) | (p[2] << 16) | (p[1] << 8) | p[0]);
if (tag == PCSCv2_PART10_PROPERTY_bMinPINSize)
reader_table[slot].pcsc.pinmin = v;
else if (tag == PCSCv2_PART10_PROPERTY_bMaxPINSize)
reader_table[slot].pcsc.pinmax = v;
else if (tag == PCSCv2_PART10_PROPERTY_wIdVendor)
vendor = v;
else if (tag == PCSCv2_PART10_PROPERTY_wIdProduct)
product = v;
if (DBG_CARD_IO)
log_debug ("TLV properties: tag=%02X, len=%d, v=%08X\n", tag, l, v);
p += l;
}
if (vendor == VENDOR_VEGA && product == VEGA_ALPHA)
{
/*
* Please read the comment of ccid_vendor_specific_init in
* ccid-driver.c.
*/
const unsigned char cmd[] = { '\xb5', '\x01', '\x00', '\x03', '\x00' };
sw = control_pcsc (slot, CM_IOCTL_VENDOR_IFD_EXCHANGE,
cmd, sizeof (cmd), NULL, 0);
if (sw)
return SW_NOT_SUPPORTED;
}
else if (vendor == VENDOR_SCM && product == SCM_SPR532) /* SCM SPR532 */
{
reader_table[slot].is_spr532 = 1;
reader_table[slot].pinpad_varlen_supported = 1;
}
else if (vendor == 0x046a)
{
/* Cherry ST-2xxx (product == 0x003e) supports TPDU level
* exchange. Other products which only support short APDU level
* exchange only work with shorter keys like RSA 1024.
*/
reader_table[slot].pcsc.pinmax = 15;
reader_table[slot].pinpad_varlen_supported = 1;
}
else if (vendor == 0x0c4b /* Tested with Reiner cyberJack GO */
|| vendor == 0x1a44 /* Tested with Vasco DIGIPASS 920 */
|| vendor == 0x234b /* Tested with FSIJ Gnuk Token */
|| vendor == 0x0d46 /* Tested with KAAN Advanced??? */
|| (vendor == 0x1fc9 && product == 0x81e6) /* Tested with Trustica Cryptoucan */)
reader_table[slot].pinpad_varlen_supported = 1;
return 0;
}
/* Open the PC/SC reader without using the wrapper. Returns -1 on
error or a slot number for the reader. */
static int
open_pcsc_reader (const char *portstr)
{
long err;
int slot;
char *list = NULL;
char *rdrname = NULL;
pcsc_dword_t nreader;
char *p;
slot = new_reader_slot ();
if (slot == -1)
return -1;
/* Fixme: Allocating a context for each slot is not required. One
global context should be sufficient. */
err = pcsc_establish_context (PCSC_SCOPE_SYSTEM, NULL, NULL,
&reader_table[slot].pcsc.context);
if (err)
{
log_error ("pcsc_establish_context failed: %s (0x%lx)\n",
pcsc_error_string (err), err);
reader_table[slot].used = 0;
unlock_slot (slot);
return -1;
}
err = pcsc_list_readers (reader_table[slot].pcsc.context,
NULL, NULL, &nreader);
if (!err)
{
list = xtrymalloc (nreader+1); /* Better add 1 for safety reasons. */
if (!list)
{
log_error ("error allocating memory for reader list\n");
pcsc_release_context (reader_table[slot].pcsc.context);
reader_table[slot].used = 0;
unlock_slot (slot);
return -1 /*SW_HOST_OUT_OF_CORE*/;
}
err = pcsc_list_readers (reader_table[slot].pcsc.context,
NULL, list, &nreader);
}
if (err)
{
log_error ("pcsc_list_readers failed: %s (0x%lx)\n",
pcsc_error_string (err), err);
pcsc_release_context (reader_table[slot].pcsc.context);
reader_table[slot].used = 0;
xfree (list);
unlock_slot (slot);
return -1;
}
p = list;
while (nreader)
{
if (!*p && !p[1])
break;
log_info ("detected reader '%s'\n", p);
if (nreader < (strlen (p)+1))
{
log_error ("invalid response from pcsc_list_readers\n");
break;
}
if (!rdrname && portstr && !strncmp (p, portstr, strlen (portstr)))
rdrname = p;
nreader -= strlen (p)+1;
p += strlen (p) + 1;
}
if (!rdrname)
rdrname = list;
reader_table[slot].rdrname = xtrystrdup (rdrname);
if (!reader_table[slot].rdrname)
{
log_error ("error allocating memory for reader name\n");
pcsc_release_context (reader_table[slot].pcsc.context);
reader_table[slot].used = 0;
unlock_slot (slot);
return -1;
}
xfree (list);
list = NULL;
reader_table[slot].pcsc.card = 0;
reader_table[slot].atrlen = 0;
reader_table[slot].connect_card = connect_pcsc_card;
reader_table[slot].disconnect_card = disconnect_pcsc_card;
reader_table[slot].close_reader = close_pcsc_reader;
reader_table[slot].reset_reader = reset_pcsc_reader;
reader_table[slot].get_status_reader = pcsc_get_status;
reader_table[slot].send_apdu_reader = pcsc_send_apdu;
reader_table[slot].dump_status_reader = dump_pcsc_reader_status;
dump_reader_status (slot);
unlock_slot (slot);
return slot;
}
/* Check whether the reader supports the ISO command code COMMAND
on the pinpad. Return 0 on success. */
static int
check_pcsc_pinpad (int slot, int command, pininfo_t *pininfo)
{
int r;
if (reader_table[slot].pcsc.pinmin >= 0)
pininfo->minlen = reader_table[slot].pcsc.pinmin;
if (reader_table[slot].pcsc.pinmax >= 0)
pininfo->maxlen = reader_table[slot].pcsc.pinmax;
if (!pininfo->minlen)
pininfo->minlen = 1;
if (!pininfo->maxlen)
pininfo->maxlen = 15;
if ((command == ISO7816_VERIFY && reader_table[slot].pcsc.verify_ioctl != 0)
|| (command == ISO7816_CHANGE_REFERENCE_DATA
&& reader_table[slot].pcsc.modify_ioctl != 0))
r = 0; /* Success */
else
r = SW_NOT_SUPPORTED;
if (DBG_CARD_IO)
log_debug ("check_pcsc_pinpad: command=%02X, r=%d\n",
(unsigned int)command, r);
if (reader_table[slot].pinpad_varlen_supported)
pininfo->fixedlen = 0;
return r;
}
#define PIN_VERIFY_STRUCTURE_SIZE 24
static int
pcsc_pinpad_verify (int slot, int class, int ins, int p0, int p1,
pininfo_t *pininfo)
{
int sw;
unsigned char *pin_verify;
int len = PIN_VERIFY_STRUCTURE_SIZE + pininfo->fixedlen;
/*
* The result buffer is only expected to have two-byte result on
* return. However, some implementation uses this buffer for lower
* layer too and it assumes that there is enough space for lower
* layer communication. Such an implementation fails for TPDU
* readers with "insufficient buffer", as it needs header and
* trailer. Six is the number for header + result + trailer (TPDU).
*/
unsigned char result[6];
pcsc_dword_t resultlen = 6;
int no_lc;
if (!reader_table[slot].atrlen
&& (sw = reset_pcsc_reader (slot)))
return sw;
if (pininfo->fixedlen < 0 || pininfo->fixedlen >= 16)
return SW_NOT_SUPPORTED;
pin_verify = xtrymalloc (len);
if (!pin_verify)
return SW_HOST_OUT_OF_CORE;
no_lc = (!pininfo->fixedlen && reader_table[slot].is_spr532);
pin_verify[0] = 0x00; /* bTimeOut */
pin_verify[1] = 0x00; /* bTimeOut2 */
pin_verify[2] = 0x82; /* bmFormatString: Byte, pos=0, left, ASCII. */
pin_verify[3] = pininfo->fixedlen; /* bmPINBlockString */
pin_verify[4] = 0x00; /* bmPINLengthFormat */
pin_verify[5] = pininfo->maxlen; /* wPINMaxExtraDigit */
pin_verify[6] = pininfo->minlen; /* wPINMaxExtraDigit */
pin_verify[7] = 0x02; /* bEntryValidationCondition: Validation key pressed */
if (pininfo->minlen && pininfo->maxlen && pininfo->minlen == pininfo->maxlen)
pin_verify[7] |= 0x01; /* Max size reached. */
pin_verify[8] = 0x01; /* bNumberMessage: One message */
pin_verify[9] = 0x09; /* wLangId: 0x0409: US English */
pin_verify[10] = 0x04; /* wLangId: 0x0409: US English */
pin_verify[11] = 0x00; /* bMsgIndex */
pin_verify[12] = 0x00; /* bTeoPrologue[0] */
pin_verify[13] = 0x00; /* bTeoPrologue[1] */
pin_verify[14] = pininfo->fixedlen + 0x05 - no_lc; /* bTeoPrologue[2] */
pin_verify[15] = pininfo->fixedlen + 0x05 - no_lc; /* ulDataLength */
pin_verify[16] = 0x00; /* ulDataLength */
pin_verify[17] = 0x00; /* ulDataLength */
pin_verify[18] = 0x00; /* ulDataLength */
pin_verify[19] = class; /* abData[0] */
pin_verify[20] = ins; /* abData[1] */
pin_verify[21] = p0; /* abData[2] */
pin_verify[22] = p1; /* abData[3] */
pin_verify[23] = pininfo->fixedlen; /* abData[4] */
if (pininfo->fixedlen)
memset (&pin_verify[24], 0xff, pininfo->fixedlen);
else if (no_lc)
len--;
if (DBG_CARD_IO)
log_debug ("send secure: c=%02X i=%02X p1=%02X p2=%02X len=%d pinmax=%d\n",
class, ins, p0, p1, len, pininfo->maxlen);
sw = control_pcsc (slot, reader_table[slot].pcsc.verify_ioctl,
pin_verify, len, result, &resultlen);
xfree (pin_verify);
if (sw || resultlen < 2)
{
log_error ("control_pcsc failed: %d\n", sw);
return sw? sw: SW_HOST_INCOMPLETE_CARD_RESPONSE;
}
sw = (result[resultlen-2] << 8) | result[resultlen-1];
if (DBG_CARD_IO)
log_debug (" response: sw=%04X datalen=%d\n", sw, (unsigned int)resultlen);
return sw;
}
#define PIN_MODIFY_STRUCTURE_SIZE 29
static int
pcsc_pinpad_modify (int slot, int class, int ins, int p0, int p1,
pininfo_t *pininfo)
{
int sw;
unsigned char *pin_modify;
int len = PIN_MODIFY_STRUCTURE_SIZE + 2 * pininfo->fixedlen;
unsigned char result[6]; /* See the comment at pinpad_verify. */
pcsc_dword_t resultlen = 6;
int no_lc;
if (!reader_table[slot].atrlen
&& (sw = reset_pcsc_reader (slot)))
return sw;
if (pininfo->fixedlen < 0 || pininfo->fixedlen >= 16)
return SW_NOT_SUPPORTED;
pin_modify = xtrymalloc (len);
if (!pin_modify)
return SW_HOST_OUT_OF_CORE;
no_lc = (!pininfo->fixedlen && reader_table[slot].is_spr532);
pin_modify[0] = 0x00; /* bTimeOut */
pin_modify[1] = 0x00; /* bTimeOut2 */
pin_modify[2] = 0x82; /* bmFormatString: Byte, pos=0, left, ASCII. */
pin_modify[3] = pininfo->fixedlen; /* bmPINBlockString */
pin_modify[4] = 0x00; /* bmPINLengthFormat */
pin_modify[5] = 0x00; /* bInsertionOffsetOld */
pin_modify[6] = pininfo->fixedlen; /* bInsertionOffsetNew */
pin_modify[7] = pininfo->maxlen; /* wPINMaxExtraDigit */
pin_modify[8] = pininfo->minlen; /* wPINMaxExtraDigit */
pin_modify[9] = (p0 == 0 ? 0x03 : 0x01);
/* bConfirmPIN
* 0x00: new PIN once
* 0x01: new PIN twice (confirmation)
* 0x02: old PIN and new PIN once
* 0x03: old PIN and new PIN twice (confirmation)
*/
pin_modify[10] = 0x02; /* bEntryValidationCondition: Validation key pressed */
if (pininfo->minlen && pininfo->maxlen && pininfo->minlen == pininfo->maxlen)
pin_modify[10] |= 0x01; /* Max size reached. */
pin_modify[11] = 0x03; /* bNumberMessage: Three messages */
pin_modify[12] = 0x09; /* wLangId: 0x0409: US English */
pin_modify[13] = 0x04; /* wLangId: 0x0409: US English */
pin_modify[14] = 0x00; /* bMsgIndex1 */
pin_modify[15] = 0x01; /* bMsgIndex2 */
pin_modify[16] = 0x02; /* bMsgIndex3 */
pin_modify[17] = 0x00; /* bTeoPrologue[0] */
pin_modify[18] = 0x00; /* bTeoPrologue[1] */
pin_modify[19] = 2 * pininfo->fixedlen + 0x05 - no_lc; /* bTeoPrologue[2] */
pin_modify[20] = 2 * pininfo->fixedlen + 0x05 - no_lc; /* ulDataLength */
pin_modify[21] = 0x00; /* ulDataLength */
pin_modify[22] = 0x00; /* ulDataLength */
pin_modify[23] = 0x00; /* ulDataLength */
pin_modify[24] = class; /* abData[0] */
pin_modify[25] = ins; /* abData[1] */
pin_modify[26] = p0; /* abData[2] */
pin_modify[27] = p1; /* abData[3] */
pin_modify[28] = 2 * pininfo->fixedlen; /* abData[4] */
if (pininfo->fixedlen)
memset (&pin_modify[29], 0xff, 2 * pininfo->fixedlen);
else if (no_lc)
len--;
if (DBG_CARD_IO)
log_debug ("send secure: c=%02X i=%02X p1=%02X p2=%02X len=%d pinmax=%d\n",
class, ins, p0, p1, len, (int)pininfo->maxlen);
sw = control_pcsc (slot, reader_table[slot].pcsc.modify_ioctl,
pin_modify, len, result, &resultlen);
xfree (pin_modify);
if (sw || resultlen < 2)
{
log_error ("control_pcsc failed: %d\n", sw);
return sw? sw : SW_HOST_INCOMPLETE_CARD_RESPONSE;
}
sw = (result[resultlen-2] << 8) | result[resultlen-1];
if (DBG_CARD_IO)
log_debug (" response: sw=%04X datalen=%d\n", sw, (unsigned int)resultlen);
return sw;
}
#ifdef HAVE_LIBUSB
/*
Internal CCID driver interface.
*/
static void
dump_ccid_reader_status (int slot)
{
log_info ("reader slot %d: using ccid driver\n", slot);
}
static int
close_ccid_reader (int slot)
{
ccid_close_reader (reader_table[slot].ccid.handle);
return 0;
}
static int
reset_ccid_reader (int slot)
{
int err;
reader_table_t slotp = reader_table + slot;
unsigned char atr[33];
size_t atrlen;
err = ccid_get_atr (slotp->ccid.handle, atr, sizeof atr, &atrlen);
if (err)
return err;
/* If the reset was successful, update the ATR. */
assert (sizeof slotp->atr >= sizeof atr);
slotp->atrlen = atrlen;
memcpy (slotp->atr, atr, atrlen);
dump_reader_status (slot);
return 0;
}
static int
set_progress_cb_ccid_reader (int slot, gcry_handler_progress_t cb, void *cb_arg)
{
reader_table_t slotp = reader_table + slot;
return ccid_set_progress_cb (slotp->ccid.handle, cb, cb_arg);
}
static int
set_prompt_cb_ccid_reader (int slot, void (*cb) (void *, int ), void *cb_arg)
{
reader_table_t slotp = reader_table + slot;
return ccid_set_prompt_cb (slotp->ccid.handle, cb, cb_arg);
}
static int
get_status_ccid (int slot, unsigned int *status, int on_wire)
{
int rc;
int bits;
rc = ccid_slot_status (reader_table[slot].ccid.handle, &bits, on_wire);
if (rc)
return rc;
if (bits == 0)
*status = (APDU_CARD_USABLE|APDU_CARD_PRESENT|APDU_CARD_ACTIVE);
else if (bits == 1)
*status = APDU_CARD_PRESENT;
else
*status = 0;
return 0;
}
/* Actually send the APDU of length APDULEN to SLOT and return a
maximum of *BUFLEN data in BUFFER, the actual returned size will be
set to BUFLEN. Returns: Internal CCID driver error code. */
static int
send_apdu_ccid (int slot, unsigned char *apdu, size_t apdulen,
unsigned char *buffer, size_t *buflen,
pininfo_t *pininfo)
{
long err;
size_t maxbuflen;
/* If we don't have an ATR, we need to reset the reader first. */
if (!reader_table[slot].atrlen
&& (err = reset_ccid_reader (slot)))
return err;
if (DBG_CARD_IO)
- log_printhex (" raw apdu:", apdu, apdulen);
+ log_printhex (apdu, apdulen, " raw apdu:");
maxbuflen = *buflen;
if (pininfo)
err = ccid_transceive_secure (reader_table[slot].ccid.handle,
apdu, apdulen, pininfo,
buffer, maxbuflen, buflen);
else
err = ccid_transceive (reader_table[slot].ccid.handle,
apdu, apdulen,
buffer, maxbuflen, buflen);
if (err)
log_error ("ccid_transceive failed: (0x%lx)\n",
err);
return err;
}
/* Check whether the CCID reader supports the ISO command code COMMAND
on the pinpad. Return 0 on success. For a description of the pin
parameters, see ccid-driver.c */
static int
check_ccid_pinpad (int slot, int command, pininfo_t *pininfo)
{
unsigned char apdu[] = { 0, 0, 0, 0x81 };
apdu[1] = command;
return ccid_transceive_secure (reader_table[slot].ccid.handle, apdu,
sizeof apdu, pininfo, NULL, 0, NULL);
}
static int
ccid_pinpad_operation (int slot, int class, int ins, int p0, int p1,
pininfo_t *pininfo)
{
unsigned char apdu[4];
int err, sw;
unsigned char result[2];
size_t resultlen = 2;
apdu[0] = class;
apdu[1] = ins;
apdu[2] = p0;
apdu[3] = p1;
err = ccid_transceive_secure (reader_table[slot].ccid.handle,
apdu, sizeof apdu, pininfo,
result, 2, &resultlen);
if (err)
return err;
if (resultlen < 2)
return SW_HOST_INCOMPLETE_CARD_RESPONSE;
sw = (result[resultlen-2] << 8) | result[resultlen-1];
return sw;
}
/* Open the reader and try to read an ATR. */
static int
open_ccid_reader (struct dev_list *dl)
{
int err;
int slot;
int require_get_status;
reader_table_t slotp;
slot = new_reader_slot ();
if (slot == -1)
return -1;
slotp = reader_table + slot;
err = ccid_open_reader (dl->portstr, dl->idx, dl->ccid_table,
&slotp->ccid.handle, &slotp->rdrname);
if (!err)
{
err = ccid_get_atr (slotp->ccid.handle,
slotp->atr, sizeof slotp->atr, &slotp->atrlen);
if (err)
ccid_close_reader (slotp->ccid.handle);
}
if (err)
{
slotp->used = 0;
unlock_slot (slot);
return -1;
}
require_get_status = ccid_require_get_status (slotp->ccid.handle);
reader_table[slot].close_reader = close_ccid_reader;
reader_table[slot].reset_reader = reset_ccid_reader;
reader_table[slot].get_status_reader = get_status_ccid;
reader_table[slot].send_apdu_reader = send_apdu_ccid;
reader_table[slot].check_pinpad = check_ccid_pinpad;
reader_table[slot].dump_status_reader = dump_ccid_reader_status;
reader_table[slot].set_progress_cb = set_progress_cb_ccid_reader;
reader_table[slot].set_prompt_cb = set_prompt_cb_ccid_reader;
reader_table[slot].pinpad_verify = ccid_pinpad_operation;
reader_table[slot].pinpad_modify = ccid_pinpad_operation;
/* Our CCID reader code does not support T=0 at all, thus reset the
flag. */
reader_table[slot].is_t0 = 0;
reader_table[slot].require_get_status = require_get_status;
dump_reader_status (slot);
unlock_slot (slot);
return slot;
}
#endif /* HAVE_LIBUSB */
#ifdef USE_G10CODE_RAPDU
/*
The Remote APDU Interface.
This uses the Remote APDU protocol to contact a reader.
The port number is actually an index into the list of ports as
returned via the protocol.
*/
static int
rapdu_status_to_sw (int status)
{
int rc;
switch (status)
{
case RAPDU_STATUS_SUCCESS: rc = 0; break;
case RAPDU_STATUS_INVCMD:
case RAPDU_STATUS_INVPROT:
case RAPDU_STATUS_INVSEQ:
case RAPDU_STATUS_INVCOOKIE:
case RAPDU_STATUS_INVREADER: rc = SW_HOST_INV_VALUE; break;
case RAPDU_STATUS_TIMEOUT: rc = SW_HOST_CARD_IO_ERROR; break;
case RAPDU_STATUS_CARDIO: rc = SW_HOST_CARD_IO_ERROR; break;
case RAPDU_STATUS_NOCARD: rc = SW_HOST_NO_CARD; break;
case RAPDU_STATUS_CARDCHG: rc = SW_HOST_NO_CARD; break;
case RAPDU_STATUS_BUSY: rc = SW_HOST_BUSY; break;
case RAPDU_STATUS_NEEDRESET: rc = SW_HOST_CARD_INACTIVE; break;
default: rc = SW_HOST_GENERAL_ERROR; break;
}
return rc;
}
static int
close_rapdu_reader (int slot)
{
rapdu_release (reader_table[slot].rapdu.handle);
return 0;
}
static int
reset_rapdu_reader (int slot)
{
int err;
reader_table_t slotp;
rapdu_msg_t msg = NULL;
slotp = reader_table + slot;
err = rapdu_send_cmd (slotp->rapdu.handle, RAPDU_CMD_RESET);
if (err)
{
log_error ("sending rapdu command RESET failed: %s\n",
err < 0 ? strerror (errno): rapdu_strerror (err));
rapdu_msg_release (msg);
return rapdu_status_to_sw (err);
}
err = rapdu_read_msg (slotp->rapdu.handle, &msg);
if (err)
{
log_error ("receiving rapdu message failed: %s\n",
err < 0 ? strerror (errno): rapdu_strerror (err));
rapdu_msg_release (msg);
return rapdu_status_to_sw (err);
}
if (msg->cmd != RAPDU_STATUS_SUCCESS || !msg->datalen)
{
int sw = rapdu_status_to_sw (msg->cmd);
log_error ("rapdu command RESET failed: %s\n",
rapdu_strerror (msg->cmd));
rapdu_msg_release (msg);
return sw;
}
if (msg->datalen > DIM (slotp->atr))
{
log_error ("ATR returned by the RAPDU layer is too large\n");
rapdu_msg_release (msg);
return SW_HOST_INV_VALUE;
}
slotp->atrlen = msg->datalen;
memcpy (slotp->atr, msg->data, msg->datalen);
rapdu_msg_release (msg);
return 0;
}
static int
my_rapdu_get_status (int slot, unsigned int *status, int on_wire)
{
int err;
reader_table_t slotp;
rapdu_msg_t msg = NULL;
int oldslot;
(void)on_wire;
slotp = reader_table + slot;
oldslot = rapdu_set_reader (slotp->rapdu.handle, slot);
err = rapdu_send_cmd (slotp->rapdu.handle, RAPDU_CMD_GET_STATUS);
rapdu_set_reader (slotp->rapdu.handle, oldslot);
if (err)
{
log_error ("sending rapdu command GET_STATUS failed: %s\n",
err < 0 ? strerror (errno): rapdu_strerror (err));
return rapdu_status_to_sw (err);
}
err = rapdu_read_msg (slotp->rapdu.handle, &msg);
if (err)
{
log_error ("receiving rapdu message failed: %s\n",
err < 0 ? strerror (errno): rapdu_strerror (err));
rapdu_msg_release (msg);
return rapdu_status_to_sw (err);
}
if (msg->cmd != RAPDU_STATUS_SUCCESS || !msg->datalen)
{
int sw = rapdu_status_to_sw (msg->cmd);
log_error ("rapdu command GET_STATUS failed: %s\n",
rapdu_strerror (msg->cmd));
rapdu_msg_release (msg);
return sw;
}
*status = msg->data[0];
rapdu_msg_release (msg);
return 0;
}
/* Actually send the APDU of length APDULEN to SLOT and return a
maximum of *BUFLEN data in BUFFER, the actual returned size will be
set to BUFLEN. Returns: APDU error code. */
static int
my_rapdu_send_apdu (int slot, unsigned char *apdu, size_t apdulen,
unsigned char *buffer, size_t *buflen,
pininfo_t *pininfo)
{
int err;
reader_table_t slotp;
rapdu_msg_t msg = NULL;
size_t maxlen = *buflen;
slotp = reader_table + slot;
*buflen = 0;
if (DBG_CARD_IO)
- log_printhex (" APDU_data:", apdu, apdulen);
+ log_printhex (apdu, apdulen, " APDU_data:");
if (apdulen < 4)
{
log_error ("rapdu_send_apdu: APDU is too short\n");
return SW_HOST_INV_VALUE;
}
err = rapdu_send_apdu (slotp->rapdu.handle, apdu, apdulen);
if (err)
{
log_error ("sending rapdu command APDU failed: %s\n",
err < 0 ? strerror (errno): rapdu_strerror (err));
rapdu_msg_release (msg);
return rapdu_status_to_sw (err);
}
err = rapdu_read_msg (slotp->rapdu.handle, &msg);
if (err)
{
log_error ("receiving rapdu message failed: %s\n",
err < 0 ? strerror (errno): rapdu_strerror (err));
rapdu_msg_release (msg);
return rapdu_status_to_sw (err);
}
if (msg->cmd != RAPDU_STATUS_SUCCESS || !msg->datalen)
{
int sw = rapdu_status_to_sw (msg->cmd);
log_error ("rapdu command APDU failed: %s\n",
rapdu_strerror (msg->cmd));
rapdu_msg_release (msg);
return sw;
}
if (msg->datalen > maxlen)
{
log_error ("rapdu response apdu too large\n");
rapdu_msg_release (msg);
return SW_HOST_INV_VALUE;
}
*buflen = msg->datalen;
memcpy (buffer, msg->data, msg->datalen);
rapdu_msg_release (msg);
return 0;
}
static int
open_rapdu_reader (int portno,
const unsigned char *cookie, size_t length,
int (*readfnc) (void *opaque,
void *buffer, size_t size),
void *readfnc_value,
int (*writefnc) (void *opaque,
const void *buffer, size_t size),
void *writefnc_value,
void (*closefnc) (void *opaque),
void *closefnc_value)
{
int err;
int slot;
reader_table_t slotp;
rapdu_msg_t msg = NULL;
slot = new_reader_slot ();
if (slot == -1)
return -1;
slotp = reader_table + slot;
slotp->rapdu.handle = rapdu_new ();
if (!slotp->rapdu.handle)
{
slotp->used = 0;
unlock_slot (slot);
return -1;
}
rapdu_set_reader (slotp->rapdu.handle, portno);
rapdu_set_iofunc (slotp->rapdu.handle,
readfnc, readfnc_value,
writefnc, writefnc_value,
closefnc, closefnc_value);
rapdu_set_cookie (slotp->rapdu.handle, cookie, length);
/* First try to get the current ATR, but if the card is inactive
issue a reset instead. */
err = rapdu_send_cmd (slotp->rapdu.handle, RAPDU_CMD_GET_ATR);
if (err == RAPDU_STATUS_NEEDRESET)
err = rapdu_send_cmd (slotp->rapdu.handle, RAPDU_CMD_RESET);
if (err)
{
log_info ("sending rapdu command GET_ATR/RESET failed: %s\n",
err < 0 ? strerror (errno): rapdu_strerror (err));
goto failure;
}
err = rapdu_read_msg (slotp->rapdu.handle, &msg);
if (err)
{
log_info ("receiving rapdu message failed: %s\n",
err < 0 ? strerror (errno): rapdu_strerror (err));
goto failure;
}
if (msg->cmd != RAPDU_STATUS_SUCCESS || !msg->datalen)
{
log_info ("rapdu command GET ATR failed: %s\n",
rapdu_strerror (msg->cmd));
goto failure;
}
if (msg->datalen > DIM (slotp->atr))
{
log_error ("ATR returned by the RAPDU layer is too large\n");
goto failure;
}
slotp->atrlen = msg->datalen;
memcpy (slotp->atr, msg->data, msg->datalen);
reader_table[slot].close_reader = close_rapdu_reader;
reader_table[slot].reset_reader = reset_rapdu_reader;
reader_table[slot].get_status_reader = my_rapdu_get_status;
reader_table[slot].send_apdu_reader = my_rapdu_send_apdu;
reader_table[slot].check_pinpad = NULL;
reader_table[slot].dump_status_reader = NULL;
reader_table[slot].pinpad_verify = NULL;
reader_table[slot].pinpad_modify = NULL;
dump_reader_status (slot);
rapdu_msg_release (msg);
unlock_slot (slot);
return slot;
failure:
rapdu_msg_release (msg);
rapdu_release (slotp->rapdu.handle);
slotp->used = 0;
unlock_slot (slot);
return -1;
}
#endif /*USE_G10CODE_RAPDU*/
/*
Driver Access
*/
gpg_error_t
apdu_dev_list_start (const char *portstr, struct dev_list **l_p)
{
struct dev_list *dl = xtrymalloc (sizeof (struct dev_list));
*l_p = NULL;
if (!dl)
return gpg_error_from_syserror ();
dl->portstr = portstr;
dl->idx = 0;
npth_mutex_lock (&reader_table_lock);
#ifdef HAVE_LIBUSB
if (opt.disable_ccid)
{
dl->ccid_table = NULL;
dl->idx_max = 1;
}
else
{
gpg_error_t err;
err = ccid_dev_scan (&dl->idx_max, &dl->ccid_table);
if (err)
return err;
if (dl->idx_max == 0)
{
/* If a CCID reader specification has been given, the user does
not want a fallback to other drivers. */
if (portstr && strlen (portstr) > 5 && portstr[4] == ':')
{
if (DBG_READER)
log_debug ("leave: apdu_open_reader => slot=-1 (no ccid)\n");
xfree (dl);
npth_mutex_unlock (&reader_table_lock);
return gpg_error (GPG_ERR_ENODEV);
}
else
dl->idx_max = 1;
}
}
#else
dl->ccid_table = NULL;
dl->idx_max = 1;
#endif /* HAVE_LIBUSB */
*l_p = dl;
return 0;
}
void
apdu_dev_list_finish (struct dev_list *dl)
{
#ifdef HAVE_LIBUSB
if (dl->ccid_table)
ccid_dev_scan_finish (dl->ccid_table, dl->idx_max);
#endif
xfree (dl);
npth_mutex_unlock (&reader_table_lock);
}
/* Open the reader and return an internal slot number or -1 on
error. If PORTSTR is NULL we default to a suitable port (for ctAPI:
the first USB reader. For PC/SC the first listed reader). */
static int
apdu_open_one_reader (const char *portstr)
{
static int pcsc_api_loaded;
int slot;
if (DBG_READER)
log_debug ("enter: apdu_open_reader: portstr=%s\n", portstr);
/* Lets try the PC/SC API */
if (!pcsc_api_loaded)
{
void *handle;
handle = dlopen (opt.pcsc_driver, RTLD_LAZY);
if (!handle)
{
log_error ("apdu_open_reader: failed to open driver '%s': %s\n",
opt.pcsc_driver, dlerror ());
return -1;
}
pcsc_establish_context = dlsym (handle, "SCardEstablishContext");
pcsc_release_context = dlsym (handle, "SCardReleaseContext");
pcsc_list_readers = dlsym (handle, "SCardListReaders");
#if defined(_WIN32) || defined(__CYGWIN__)
if (!pcsc_list_readers)
pcsc_list_readers = dlsym (handle, "SCardListReadersA");
#endif
pcsc_get_status_change = dlsym (handle, "SCardGetStatusChange");
#if defined(_WIN32) || defined(__CYGWIN__)
if (!pcsc_get_status_change)
pcsc_get_status_change = dlsym (handle, "SCardGetStatusChangeA");
#endif
pcsc_connect = dlsym (handle, "SCardConnect");
#if defined(_WIN32) || defined(__CYGWIN__)
if (!pcsc_connect)
pcsc_connect = dlsym (handle, "SCardConnectA");
#endif
pcsc_reconnect = dlsym (handle, "SCardReconnect");
#if defined(_WIN32) || defined(__CYGWIN__)
if (!pcsc_reconnect)
pcsc_reconnect = dlsym (handle, "SCardReconnectA");
#endif
pcsc_disconnect = dlsym (handle, "SCardDisconnect");
pcsc_status = dlsym (handle, "SCardStatus");
#if defined(_WIN32) || defined(__CYGWIN__)
if (!pcsc_status)
pcsc_status = dlsym (handle, "SCardStatusA");
#endif
pcsc_begin_transaction = dlsym (handle, "SCardBeginTransaction");
pcsc_end_transaction = dlsym (handle, "SCardEndTransaction");
pcsc_transmit = dlsym (handle, "SCardTransmit");
pcsc_set_timeout = dlsym (handle, "SCardSetTimeout");
pcsc_control = dlsym (handle, "SCardControl");
if (!pcsc_establish_context
|| !pcsc_release_context
|| !pcsc_list_readers
|| !pcsc_get_status_change
|| !pcsc_connect
|| !pcsc_reconnect
|| !pcsc_disconnect
|| !pcsc_status
|| !pcsc_begin_transaction
|| !pcsc_end_transaction
|| !pcsc_transmit
|| !pcsc_control
/* || !pcsc_set_timeout */)
{
/* Note that set_timeout is currently not used and also not
available under Windows. */
log_error ("apdu_open_reader: invalid PC/SC driver "
"(%d%d%d%d%d%d%d%d%d%d%d%d%d)\n",
!!pcsc_establish_context,
!!pcsc_release_context,
!!pcsc_list_readers,
!!pcsc_get_status_change,
!!pcsc_connect,
!!pcsc_reconnect,
!!pcsc_disconnect,
!!pcsc_status,
!!pcsc_begin_transaction,
!!pcsc_end_transaction,
!!pcsc_transmit,
!!pcsc_set_timeout,
!!pcsc_control );
dlclose (handle);
return -1;
}
pcsc_api_loaded = 1;
}
slot = open_pcsc_reader (portstr);
if (DBG_READER)
log_debug ("leave: apdu_open_reader => slot=%d [pc/sc]\n", slot);
return slot;
}
int
apdu_open_reader (struct dev_list *dl, int app_empty)
{
int slot;
#ifdef HAVE_LIBUSB
if (dl->ccid_table)
{ /* CCID readers. */
int readerno;
/* See whether we want to use the reader ID string or a reader
number. A readerno of -1 indicates that the reader ID string is
to be used. */
if (dl->portstr && strchr (dl->portstr, ':'))
readerno = -1; /* We want to use the readerid. */
else if (dl->portstr)
{
readerno = atoi (dl->portstr);
if (readerno < 0)
{
return -1;
}
}
else
readerno = 0; /* Default. */
if (readerno > 0)
{ /* Use single, the specific reader. */
if (readerno >= dl->idx_max)
return -1;
dl->idx = readerno;
dl->portstr = NULL;
slot = open_ccid_reader (dl);
dl->idx = dl->idx_max;
if (slot >= 0)
return slot;
else
return -1;
}
while (dl->idx < dl->idx_max)
{
unsigned int bai = ccid_get_BAI (dl->idx, dl->ccid_table);
if (DBG_READER)
log_debug ("apdu_open_reader: BAI=%x\n", bai);
/* Check identity by BAI against already opened HANDLEs. */
for (slot = 0; slot < MAX_READER; slot++)
if (reader_table[slot].used
&& reader_table[slot].ccid.handle
&& ccid_compare_BAI (reader_table[slot].ccid.handle, bai))
break;
if (slot == MAX_READER)
{ /* Found a new device. */
if (DBG_READER)
log_debug ("apdu_open_reader: new device=%x\n", bai);
slot = open_ccid_reader (dl);
dl->idx++;
if (slot >= 0)
return slot;
else
{
/* Skip this reader. */
log_error ("ccid open error: skip\n");
continue;
}
}
else
dl->idx++;
}
/* Not found. Try one for PC/SC, only when it's the initial scan. */
if (app_empty && dl->idx == dl->idx_max)
{
dl->idx++;
slot = apdu_open_one_reader (dl->portstr);
}
else
slot = -1;
}
else
#endif
{ /* PC/SC readers. */
if (app_empty && dl->idx == 0)
{
dl->idx++;
slot = apdu_open_one_reader (dl->portstr);
}
else
slot = -1;
}
return slot;
}
/* Open an remote reader and return an internal slot number or -1 on
error. This function is an alternative to apdu_open_reader and used
with remote readers only. Note that the supplied CLOSEFNC will
only be called once and the slot will not be valid afther this.
If PORTSTR is NULL we default to the first available port.
*/
int
apdu_open_remote_reader (const char *portstr,
const unsigned char *cookie, size_t length,
int (*readfnc) (void *opaque,
void *buffer, size_t size),
void *readfnc_value,
int (*writefnc) (void *opaque,
const void *buffer, size_t size),
void *writefnc_value,
void (*closefnc) (void *opaque),
void *closefnc_value)
{
#ifdef USE_G10CODE_RAPDU
return open_rapdu_reader (portstr? atoi (portstr) : 0,
cookie, length,
readfnc, readfnc_value,
writefnc, writefnc_value,
closefnc, closefnc_value);
#else
(void)portstr;
(void)cookie;
(void)length;
(void)readfnc;
(void)readfnc_value;
(void)writefnc;
(void)writefnc_value;
(void)closefnc;
(void)closefnc_value;
#ifdef _WIN32
errno = ENOENT;
#else
errno = ENOSYS;
#endif
return -1;
#endif
}
int
apdu_close_reader (int slot)
{
int sw;
if (DBG_READER)
log_debug ("enter: apdu_close_reader: slot=%d\n", slot);
if (slot < 0 || slot >= MAX_READER || !reader_table[slot].used )
{
if (DBG_READER)
log_debug ("leave: apdu_close_reader => SW_HOST_NO_DRIVER\n");
return SW_HOST_NO_DRIVER;
}
sw = apdu_disconnect (slot);
if (sw)
{
/*
* When the reader/token was removed it might come here.
* It should go through to call CLOSE_READER even if we got an error.
*/
if (DBG_READER)
log_debug ("apdu_close_reader => 0x%x (apdu_disconnect)\n", sw);
}
if (reader_table[slot].close_reader)
{
sw = reader_table[slot].close_reader (slot);
reader_table[slot].used = 0;
if (DBG_READER)
log_debug ("leave: apdu_close_reader => 0x%x (close_reader)\n", sw);
return sw;
}
xfree (reader_table[slot].rdrname);
reader_table[slot].rdrname = NULL;
reader_table[slot].used = 0;
if (DBG_READER)
log_debug ("leave: apdu_close_reader => SW_HOST_NOT_SUPPORTED\n");
return SW_HOST_NOT_SUPPORTED;
}
/* Function suitable for a cleanup function to close all reader. It
should not be used if the reader will be opened again. The reason
for implementing this to properly close USB devices so that they
will startup the next time without error. */
void
apdu_prepare_exit (void)
{
static int sentinel;
int slot;
if (!sentinel)
{
sentinel = 1;
npth_mutex_lock (&reader_table_lock);
for (slot = 0; slot < MAX_READER; slot++)
if (reader_table[slot].used)
{
apdu_disconnect (slot);
if (reader_table[slot].close_reader)
reader_table[slot].close_reader (slot);
xfree (reader_table[slot].rdrname);
reader_table[slot].rdrname = NULL;
reader_table[slot].used = 0;
}
npth_mutex_unlock (&reader_table_lock);
sentinel = 0;
}
}
/* Enumerate all readers and return information on whether this reader
is in use. The caller should start with SLOT set to 0 and
increment it with each call until an error is returned. */
int
apdu_enum_reader (int slot, int *used)
{
if (slot < 0 || slot >= MAX_READER)
return SW_HOST_NO_DRIVER;
*used = reader_table[slot].used;
return 0;
}
/* Connect a card. This is used to power up the card and make sure
that an ATR is available. Depending on the reader backend it may
return an error for an inactive card or if no card is available.
Return -1 on error. Return 1 if reader requires get_status to
watch card removal. Return 0 if it's a token (always with a card),
or it supports INTERRUPT endpoint to watch card removal.
*/
int
apdu_connect (int slot)
{
int sw = 0;
unsigned int status = 0;
if (DBG_READER)
log_debug ("enter: apdu_connect: slot=%d\n", slot);
if (slot < 0 || slot >= MAX_READER || !reader_table[slot].used )
{
if (DBG_READER)
log_debug ("leave: apdu_connect => SW_HOST_NO_DRIVER\n");
return -1;
}
/* Only if the access method provides a connect function we use it.
If not, we expect that the card has been implicitly connected by
apdu_open_reader. */
if (reader_table[slot].connect_card)
{
sw = lock_slot (slot);
if (!sw)
{
sw = reader_table[slot].connect_card (slot);
unlock_slot (slot);
}
}
/* We need to call apdu_get_status_internal, so that the last-status
machinery gets setup properly even if a card is inserted while
scdaemon is fired up and apdu_get_status has not yet been called.
Without that we would force a reset of the card with the next
call to apdu_get_status. */
if (!sw)
sw = apdu_get_status_internal (slot, 1, &status, 1);
if (sw)
;
else if (!(status & APDU_CARD_PRESENT))
sw = SW_HOST_NO_CARD;
else if ((status & APDU_CARD_PRESENT) && !(status & APDU_CARD_ACTIVE))
sw = SW_HOST_CARD_INACTIVE;
if (sw == SW_HOST_CARD_INACTIVE)
{
/* Try power it up again. */
sw = apdu_reset (slot);
}
if (DBG_READER)
log_debug ("leave: apdu_connect => sw=0x%x\n", sw);
if (sw)
return -1;
return reader_table[slot].require_get_status;
}
int
apdu_disconnect (int slot)
{
int sw;
if (DBG_READER)
log_debug ("enter: apdu_disconnect: slot=%d\n", slot);
if (slot < 0 || slot >= MAX_READER || !reader_table[slot].used )
{
if (DBG_READER)
log_debug ("leave: apdu_disconnect => SW_HOST_NO_DRIVER\n");
return SW_HOST_NO_DRIVER;
}
if (reader_table[slot].disconnect_card)
{
sw = lock_slot (slot);
if (!sw)
{
sw = reader_table[slot].disconnect_card (slot);
unlock_slot (slot);
}
}
else
sw = 0;
if (DBG_READER)
log_debug ("leave: apdu_disconnect => sw=0x%x\n", sw);
return sw;
}
/* Set the progress callback of SLOT to CB and its args to CB_ARG. If
CB is NULL the progress callback is removed. */
int
apdu_set_progress_cb (int slot, gcry_handler_progress_t cb, void *cb_arg)
{
int sw;
if (slot < 0 || slot >= MAX_READER || !reader_table[slot].used )
return SW_HOST_NO_DRIVER;
if (reader_table[slot].set_progress_cb)
{
sw = lock_slot (slot);
if (!sw)
{
sw = reader_table[slot].set_progress_cb (slot, cb, cb_arg);
unlock_slot (slot);
}
}
else
sw = 0;
return sw;
}
int
apdu_set_prompt_cb (int slot, void (*cb) (void *, int), void *cb_arg)
{
int sw;
if (slot < 0 || slot >= MAX_READER || !reader_table[slot].used )
return SW_HOST_NO_DRIVER;
if (reader_table[slot].set_prompt_cb)
{
sw = lock_slot (slot);
if (!sw)
{
sw = reader_table[slot].set_prompt_cb (slot, cb, cb_arg);
unlock_slot (slot);
}
}
else
sw = 0;
return sw;
}
/* Do a reset for the card in reader at SLOT. */
int
apdu_reset (int slot)
{
int sw;
if (DBG_READER)
log_debug ("enter: apdu_reset: slot=%d\n", slot);
if (slot < 0 || slot >= MAX_READER || !reader_table[slot].used )
{
if (DBG_READER)
log_debug ("leave: apdu_reset => SW_HOST_NO_DRIVER\n");
return SW_HOST_NO_DRIVER;
}
if ((sw = lock_slot (slot)))
{
if (DBG_READER)
log_debug ("leave: apdu_reset => sw=0x%x (lock_slot)\n", sw);
return sw;
}
if (reader_table[slot].reset_reader)
sw = reader_table[slot].reset_reader (slot);
unlock_slot (slot);
if (DBG_READER)
log_debug ("leave: apdu_reset => sw=0x%x\n", sw);
return sw;
}
/* Return the ATR or NULL if none is available. On success the length
of the ATR is stored at ATRLEN. The caller must free the returned
value. */
unsigned char *
apdu_get_atr (int slot, size_t *atrlen)
{
unsigned char *buf;
if (DBG_READER)
log_debug ("enter: apdu_get_atr: slot=%d\n", slot);
if (slot < 0 || slot >= MAX_READER || !reader_table[slot].used )
{
if (DBG_READER)
log_debug ("leave: apdu_get_atr => NULL (bad slot)\n");
return NULL;
}
if (!reader_table[slot].atrlen)
{
if (DBG_READER)
log_debug ("leave: apdu_get_atr => NULL (no ATR)\n");
return NULL;
}
buf = xtrymalloc (reader_table[slot].atrlen);
if (!buf)
{
if (DBG_READER)
log_debug ("leave: apdu_get_atr => NULL (out of core)\n");
return NULL;
}
memcpy (buf, reader_table[slot].atr, reader_table[slot].atrlen);
*atrlen = reader_table[slot].atrlen;
if (DBG_READER)
log_debug ("leave: apdu_get_atr => atrlen=%zu\n", *atrlen);
return buf;
}
/* Retrieve the status for SLOT. The function does only wait for the
card to become available if HANG is set to true. On success the
bits in STATUS will be set to
APDU_CARD_USABLE (bit 0) = card present and usable
APDU_CARD_PRESENT (bit 1) = card present
APDU_CARD_ACTIVE (bit 2) = card active
(bit 3) = card access locked [not yet implemented]
For most applications, testing bit 0 is sufficient.
*/
static int
apdu_get_status_internal (int slot, int hang, unsigned int *status, int on_wire)
{
int sw;
unsigned int s = 0;
if (slot < 0 || slot >= MAX_READER || !reader_table[slot].used )
return SW_HOST_NO_DRIVER;
if ((sw = hang? lock_slot (slot) : trylock_slot (slot)))
return sw;
if (reader_table[slot].get_status_reader)
sw = reader_table[slot].get_status_reader (slot, &s, on_wire);
unlock_slot (slot);
if (sw)
{
if (on_wire)
reader_table[slot].atrlen = 0;
s = 0;
}
if (status)
*status = s;
return sw;
}
/* See above for a description. */
int
apdu_get_status (int slot, int hang, unsigned int *status)
{
int sw;
if (DBG_READER)
log_debug ("enter: apdu_get_status: slot=%d hang=%d\n", slot, hang);
sw = apdu_get_status_internal (slot, hang, status, 0);
if (DBG_READER)
{
if (status)
log_debug ("leave: apdu_get_status => sw=0x%x status=%u\n",
sw, *status);
else
log_debug ("leave: apdu_get_status => sw=0x%x\n", sw);
}
return sw;
}
/* Check whether the reader supports the ISO command code COMMAND on
the pinpad. Return 0 on success. For a description of the pin
parameters, see ccid-driver.c */
int
apdu_check_pinpad (int slot, int command, pininfo_t *pininfo)
{
if (slot < 0 || slot >= MAX_READER || !reader_table[slot].used )
return SW_HOST_NO_DRIVER;
if (opt.enable_pinpad_varlen)
pininfo->fixedlen = 0;
if (reader_table[slot].check_pinpad)
{
int sw;
if ((sw = lock_slot (slot)))
return sw;
sw = reader_table[slot].check_pinpad (slot, command, pininfo);
unlock_slot (slot);
return sw;
}
else
return SW_HOST_NOT_SUPPORTED;
}
int
apdu_pinpad_verify (int slot, int class, int ins, int p0, int p1,
pininfo_t *pininfo)
{
if (slot < 0 || slot >= MAX_READER || !reader_table[slot].used )
return SW_HOST_NO_DRIVER;
if (reader_table[slot].pinpad_verify)
{
int sw;
if ((sw = lock_slot (slot)))
return sw;
sw = reader_table[slot].pinpad_verify (slot, class, ins, p0, p1,
pininfo);
unlock_slot (slot);
return sw;
}
else
return SW_HOST_NOT_SUPPORTED;
}
int
apdu_pinpad_modify (int slot, int class, int ins, int p0, int p1,
pininfo_t *pininfo)
{
if (slot < 0 || slot >= MAX_READER || !reader_table[slot].used )
return SW_HOST_NO_DRIVER;
if (reader_table[slot].pinpad_modify)
{
int sw;
if ((sw = lock_slot (slot)))
return sw;
sw = reader_table[slot].pinpad_modify (slot, class, ins, p0, p1,
pininfo);
unlock_slot (slot);
return sw;
}
else
return SW_HOST_NOT_SUPPORTED;
}
/* Dispatcher for the actual send_apdu function. Note, that this
function should be called in locked state. */
static int
send_apdu (int slot, unsigned char *apdu, size_t apdulen,
unsigned char *buffer, size_t *buflen, pininfo_t *pininfo)
{
if (slot < 0 || slot >= MAX_READER || !reader_table[slot].used )
return SW_HOST_NO_DRIVER;
if (reader_table[slot].send_apdu_reader)
return reader_table[slot].send_apdu_reader (slot,
apdu, apdulen,
buffer, buflen,
pininfo);
else
return SW_HOST_NOT_SUPPORTED;
}
/* Core APDU tranceiver function. Parameters are described at
apdu_send_le with the exception of PININFO which indicates pinpad
related operations if not NULL. If EXTENDED_MODE is not 0
command chaining or extended length will be used according to these
values:
n < 0 := Use command chaining with the data part limited to -n
in each chunk. If -1 is used a default value is used.
n == 0 := No extended mode or command chaining.
n == 1 := Use extended length for input and output without a
length limit.
n > 1 := Use extended length with up to N bytes.
*/
static int
send_le (int slot, int class, int ins, int p0, int p1,
int lc, const char *data, int le,
unsigned char **retbuf, size_t *retbuflen,
pininfo_t *pininfo, int extended_mode)
{
#define SHORT_RESULT_BUFFER_SIZE 258
/* We allocate 8 extra bytes as a safety margin towards a driver bug. */
unsigned char short_result_buffer[SHORT_RESULT_BUFFER_SIZE+10];
unsigned char *result_buffer = NULL;
size_t result_buffer_size;
unsigned char *result;
size_t resultlen;
unsigned char short_apdu_buffer[5+256+1];
unsigned char *apdu_buffer = NULL;
size_t apdu_buffer_size;
unsigned char *apdu;
size_t apdulen;
int sw;
long rc; /* We need a long here due to PC/SC. */
int did_exact_length_hack = 0;
int use_chaining = 0;
int use_extended_length = 0;
int lc_chunk;
if (slot < 0 || slot >= MAX_READER || !reader_table[slot].used )
return SW_HOST_NO_DRIVER;
if (DBG_CARD_IO)
log_debug ("send apdu: c=%02X i=%02X p1=%02X p2=%02X lc=%d le=%d em=%d\n",
class, ins, p0, p1, lc, le, extended_mode);
if (lc != -1 && (lc > 255 || lc < 0))
{
/* Data does not fit into an APDU. What we do now depends on
the EXTENDED_MODE parameter. */
if (!extended_mode)
return SW_WRONG_LENGTH; /* No way to send such an APDU. */
else if (extended_mode > 0)
use_extended_length = 1;
else if (extended_mode < 0)
{
/* Send APDU using chaining mode. */
if (lc > 16384)
return SW_WRONG_LENGTH; /* Sanity check. */
if ((class&0xf0) != 0)
return SW_HOST_INV_VALUE; /* Upper 4 bits need to be 0. */
use_chaining = extended_mode == -1? 255 : -extended_mode;
use_chaining &= 0xff;
}
else
return SW_HOST_INV_VALUE;
}
else if (lc == -1 && extended_mode > 0)
use_extended_length = 1;
if (le != -1 && (le > (extended_mode > 0? 255:256) || le < 0))
{
/* Expected Data does not fit into an APDU. What we do now
depends on the EXTENDED_MODE parameter. Note that a check
for command chaining does not make sense because we are
looking at Le. */
if (!extended_mode)
return SW_WRONG_LENGTH; /* No way to send such an APDU. */
else if (use_extended_length)
; /* We are already using extended length. */
else if (extended_mode > 0)
use_extended_length = 1;
else
return SW_HOST_INV_VALUE;
}
if ((!data && lc != -1) || (data && lc == -1))
return SW_HOST_INV_VALUE;
if (use_extended_length)
{
if (reader_table[slot].is_t0)
return SW_HOST_NOT_SUPPORTED;
/* Space for: cls/ins/p1/p2+Z+2_byte_Lc+Lc+2_byte_Le. */
apdu_buffer_size = 4 + 1 + (lc >= 0? (2+lc):0) + 2;
apdu_buffer = xtrymalloc (apdu_buffer_size + 10);
if (!apdu_buffer)
return SW_HOST_OUT_OF_CORE;
apdu = apdu_buffer;
}
else
{
apdu_buffer_size = sizeof short_apdu_buffer;
apdu = short_apdu_buffer;
}
if (use_extended_length && (le > 256 || le < 0))
{
/* Two more bytes are needed for status bytes. */
result_buffer_size = le < 0? 4096 : (le + 2);
result_buffer = xtrymalloc (result_buffer_size);
if (!result_buffer)
{
xfree (apdu_buffer);
return SW_HOST_OUT_OF_CORE;
}
result = result_buffer;
}
else
{
result_buffer_size = SHORT_RESULT_BUFFER_SIZE;
result = short_result_buffer;
}
#undef SHORT_RESULT_BUFFER_SIZE
if ((sw = lock_slot (slot)))
{
xfree (apdu_buffer);
xfree (result_buffer);
return sw;
}
do
{
if (use_extended_length)
{
use_chaining = 0;
apdulen = 0;
apdu[apdulen++] = class;
apdu[apdulen++] = ins;
apdu[apdulen++] = p0;
apdu[apdulen++] = p1;
if (lc > 0)
{
apdu[apdulen++] = 0; /* Z byte: Extended length marker. */
apdu[apdulen++] = ((lc >> 8) & 0xff);
apdu[apdulen++] = (lc & 0xff);
memcpy (apdu+apdulen, data, lc);
data += lc;
apdulen += lc;
}
if (le != -1)
{
if (lc <= 0)
apdu[apdulen++] = 0; /* Z byte: Extended length marker. */
apdu[apdulen++] = ((le >> 8) & 0xff);
apdu[apdulen++] = (le & 0xff);
}
}
else
{
apdulen = 0;
apdu[apdulen] = class;
if (use_chaining && lc > 255)
{
apdu[apdulen] |= 0x10;
assert (use_chaining < 256);
lc_chunk = use_chaining;
lc -= use_chaining;
}
else
{
use_chaining = 0;
lc_chunk = lc;
}
apdulen++;
apdu[apdulen++] = ins;
apdu[apdulen++] = p0;
apdu[apdulen++] = p1;
if (lc_chunk != -1)
{
apdu[apdulen++] = lc_chunk;
memcpy (apdu+apdulen, data, lc_chunk);
data += lc_chunk;
apdulen += lc_chunk;
/* T=0 does not allow the use of Lc together with Le;
thus disable Le in this case. */
if (reader_table[slot].is_t0)
le = -1;
}
if (le != -1 && !use_chaining)
apdu[apdulen++] = le; /* Truncation is okay (0 means 256). */
}
exact_length_hack:
/* As a safeguard don't pass any garbage to the driver. */
assert (apdulen <= apdu_buffer_size);
memset (apdu+apdulen, 0, apdu_buffer_size - apdulen);
resultlen = result_buffer_size;
rc = send_apdu (slot, apdu, apdulen, result, &resultlen, pininfo);
if (rc || resultlen < 2)
{
log_info ("apdu_send_simple(%d) failed: %s\n",
slot, apdu_strerror (rc));
unlock_slot (slot);
xfree (apdu_buffer);
xfree (result_buffer);
return rc? rc : SW_HOST_INCOMPLETE_CARD_RESPONSE;
}
sw = (result[resultlen-2] << 8) | result[resultlen-1];
if (!use_extended_length
&& !did_exact_length_hack && SW_EXACT_LENGTH_P (sw))
{
apdu[apdulen-1] = (sw & 0x00ff);
did_exact_length_hack = 1;
goto exact_length_hack;
}
}
while (use_chaining && sw == SW_SUCCESS);
if (apdu_buffer)
{
xfree (apdu_buffer);
apdu_buffer = NULL;
}
/* Store away the returned data but strip the statusword. */
resultlen -= 2;
if (DBG_CARD_IO)
{
log_debug (" response: sw=%04X datalen=%d\n",
sw, (unsigned int)resultlen);
if ( !retbuf && (sw == SW_SUCCESS || (sw & 0xff00) == SW_MORE_DATA))
- log_printhex (" dump: ", result, resultlen);
+ log_printhex (result, resultlen, " dump: ");
}
if (sw == SW_SUCCESS || sw == SW_EOF_REACHED)
{
if (retbuf)
{
*retbuf = xtrymalloc (resultlen? resultlen : 1);
if (!*retbuf)
{
unlock_slot (slot);
xfree (result_buffer);
return SW_HOST_OUT_OF_CORE;
}
*retbuflen = resultlen;
memcpy (*retbuf, result, resultlen);
}
}
else if ((sw & 0xff00) == SW_MORE_DATA)
{
unsigned char *p = NULL, *tmp;
size_t bufsize = 4096;
/* It is likely that we need to return much more data, so we
start off with a large buffer. */
if (retbuf)
{
*retbuf = p = xtrymalloc (bufsize);
if (!*retbuf)
{
unlock_slot (slot);
xfree (result_buffer);
return SW_HOST_OUT_OF_CORE;
}
assert (resultlen < bufsize);
memcpy (p, result, resultlen);
p += resultlen;
}
do
{
int len = (sw & 0x00ff);
if (DBG_CARD_IO)
log_debug ("apdu_send_simple(%d): %d more bytes available\n",
slot, len);
apdu_buffer_size = sizeof short_apdu_buffer;
apdu = short_apdu_buffer;
apdulen = 0;
apdu[apdulen++] = class;
apdu[apdulen++] = 0xC0;
apdu[apdulen++] = 0;
apdu[apdulen++] = 0;
apdu[apdulen++] = len;
assert (apdulen <= apdu_buffer_size);
memset (apdu+apdulen, 0, apdu_buffer_size - apdulen);
resultlen = result_buffer_size;
rc = send_apdu (slot, apdu, apdulen, result, &resultlen, NULL);
if (rc || resultlen < 2)
{
log_error ("apdu_send_simple(%d) for get response failed: %s\n",
slot, apdu_strerror (rc));
unlock_slot (slot);
xfree (result_buffer);
return rc? rc : SW_HOST_INCOMPLETE_CARD_RESPONSE;
}
sw = (result[resultlen-2] << 8) | result[resultlen-1];
resultlen -= 2;
if (DBG_CARD_IO)
{
log_debug (" more: sw=%04X datalen=%d\n",
sw, (unsigned int)resultlen);
if (!retbuf && (sw==SW_SUCCESS || (sw&0xff00)==SW_MORE_DATA))
- log_printhex (" dump: ", result, resultlen);
+ log_printhex (result, resultlen, " dump: ");
}
if ((sw & 0xff00) == SW_MORE_DATA
|| sw == SW_SUCCESS
|| sw == SW_EOF_REACHED )
{
if (retbuf && resultlen)
{
if (p - *retbuf + resultlen > bufsize)
{
bufsize += resultlen > 4096? resultlen: 4096;
tmp = xtryrealloc (*retbuf, bufsize);
if (!tmp)
{
unlock_slot (slot);
xfree (result_buffer);
return SW_HOST_OUT_OF_CORE;
}
p = tmp + (p - *retbuf);
*retbuf = tmp;
}
memcpy (p, result, resultlen);
p += resultlen;
}
}
else
log_info ("apdu_send_simple(%d) "
"got unexpected status %04X from get response\n",
slot, sw);
}
while ((sw & 0xff00) == SW_MORE_DATA);
if (retbuf)
{
*retbuflen = p - *retbuf;
tmp = xtryrealloc (*retbuf, *retbuflen);
if (tmp)
*retbuf = tmp;
}
}
unlock_slot (slot);
xfree (result_buffer);
if (DBG_CARD_IO && retbuf && sw == SW_SUCCESS)
- log_printhex (" dump: ", *retbuf, *retbuflen);
+ log_printhex (*retbuf, *retbuflen, " dump: ");
return sw;
}
/* Send an APDU to the card in SLOT. The APDU is created from all
given parameters: CLASS, INS, P0, P1, LC, DATA, LE. A value of -1
for LC won't sent this field and the data field; in this case DATA
must also be passed as NULL. If EXTENDED_MODE is not 0 command
chaining or extended length will be used; see send_le for details.
The return value is the status word or -1 for an invalid SLOT or
other non card related error. If RETBUF is not NULL, it will
receive an allocated buffer with the returned data. The length of
that data will be put into *RETBUFLEN. The caller is responsible
for releasing the buffer even in case of errors. */
int
apdu_send_le(int slot, int extended_mode,
int class, int ins, int p0, int p1,
int lc, const char *data, int le,
unsigned char **retbuf, size_t *retbuflen)
{
return send_le (slot, class, ins, p0, p1,
lc, data, le,
retbuf, retbuflen,
NULL, extended_mode);
}
/* Send an APDU to the card in SLOT. The APDU is created from all
given parameters: CLASS, INS, P0, P1, LC, DATA. A value of -1 for
LC won't sent this field and the data field; in this case DATA must
also be passed as NULL. If EXTENDED_MODE is not 0 command chaining
or extended length will be used; see send_le for details. The
return value is the status word or -1 for an invalid SLOT or other
non card related error. If RETBUF is not NULL, it will receive an
allocated buffer with the returned data. The length of that data
will be put into *RETBUFLEN. The caller is responsible for
releasing the buffer even in case of errors. */
int
apdu_send (int slot, int extended_mode,
int class, int ins, int p0, int p1,
int lc, const char *data, unsigned char **retbuf, size_t *retbuflen)
{
return send_le (slot, class, ins, p0, p1, lc, data, 256,
retbuf, retbuflen, NULL, extended_mode);
}
/* Send an APDU to the card in SLOT. The APDU is created from all
given parameters: CLASS, INS, P0, P1, LC, DATA. A value of -1 for
LC won't sent this field and the data field; in this case DATA must
also be passed as NULL. If EXTENDED_MODE is not 0 command chaining
or extended length will be used; see send_le for details. The
return value is the status word or -1 for an invalid SLOT or other
non card related error. No data will be returned. */
int
apdu_send_simple (int slot, int extended_mode,
int class, int ins, int p0, int p1,
int lc, const char *data)
{
return send_le (slot, class, ins, p0, p1, lc, data, -1, NULL, NULL, NULL,
extended_mode);
}
/* This is a more generic version of the apdu sending routine. It
* takes an already formatted APDU in APDUDATA or length APDUDATALEN
* and returns with an APDU including the status word. With
* HANDLE_MORE set to true this function will handle the MORE DATA
* status and return all APDUs concatenated with one status word at
* the end. If EXTENDED_LENGTH is != 0 extended lengths are allowed
* with a max. result data length of EXTENDED_LENGTH bytes. The
* function does not return a regular status word but 0 on success.
* If the slot is locked, the function returns immediately with an
* error.
*
* Out of historical reasons the function returns 0 on success and
* outs the status word at the end of the result to be able to get the
* status word in the case of a not provided RETBUF, R_SW can be used
* to store the SW. But note that R_SW qill only be set if the
* function returns 0. */
int
apdu_send_direct (int slot, size_t extended_length,
const unsigned char *apdudata, size_t apdudatalen,
int handle_more, unsigned int *r_sw,
unsigned char **retbuf, size_t *retbuflen)
{
#define SHORT_RESULT_BUFFER_SIZE 258
unsigned char short_result_buffer[SHORT_RESULT_BUFFER_SIZE+10];
unsigned char *result_buffer = NULL;
size_t result_buffer_size;
unsigned char *result;
size_t resultlen;
unsigned char short_apdu_buffer[5+256+10];
unsigned char *apdu_buffer = NULL;
unsigned char *apdu;
size_t apdulen;
int sw;
long rc; /* we need a long here due to PC/SC. */
int class;
if (slot < 0 || slot >= MAX_READER || !reader_table[slot].used )
return SW_HOST_NO_DRIVER;
if (apdudatalen > 65535)
return SW_HOST_INV_VALUE;
if (apdudatalen > sizeof short_apdu_buffer - 5)
{
apdu_buffer = xtrymalloc (apdudatalen + 5);
if (!apdu_buffer)
return SW_HOST_OUT_OF_CORE;
apdu = apdu_buffer;
}
else
{
apdu = short_apdu_buffer;
}
apdulen = apdudatalen;
memcpy (apdu, apdudata, apdudatalen);
class = apdulen? *apdu : 0;
if (extended_length >= 256 && extended_length <= 65536)
{
result_buffer_size = extended_length;
result_buffer = xtrymalloc (result_buffer_size + 10);
if (!result_buffer)
{
xfree (apdu_buffer);
return SW_HOST_OUT_OF_CORE;
}
result = result_buffer;
}
else
{
result_buffer_size = SHORT_RESULT_BUFFER_SIZE;
result = short_result_buffer;
}
#undef SHORT_RESULT_BUFFER_SIZE
if ((sw = trylock_slot (slot)))
{
xfree (apdu_buffer);
xfree (result_buffer);
return sw;
}
resultlen = result_buffer_size;
rc = send_apdu (slot, apdu, apdulen, result, &resultlen, NULL);
xfree (apdu_buffer);
apdu_buffer = NULL;
if (rc || resultlen < 2)
{
log_error ("apdu_send_direct(%d) failed: %s\n",
slot, apdu_strerror (rc));
unlock_slot (slot);
xfree (result_buffer);
return rc? rc : SW_HOST_INCOMPLETE_CARD_RESPONSE;
}
sw = (result[resultlen-2] << 8) | result[resultlen-1];
/* Store away the returned data but strip the statusword. */
resultlen -= 2;
if (DBG_CARD_IO)
{
log_debug (" response: sw=%04X datalen=%d\n",
sw, (unsigned int)resultlen);
if ( !retbuf && (sw == SW_SUCCESS || (sw & 0xff00) == SW_MORE_DATA))
- log_printhex (" dump: ", result, resultlen);
+ log_printhex (result, resultlen, " dump: ");
}
if (handle_more && (sw & 0xff00) == SW_MORE_DATA)
{
unsigned char *p = NULL, *tmp;
size_t bufsize = 4096;
/* It is likely that we need to return much more data, so we
start off with a large buffer. */
if (retbuf)
{
*retbuf = p = xtrymalloc (bufsize + 2);
if (!*retbuf)
{
unlock_slot (slot);
xfree (result_buffer);
return SW_HOST_OUT_OF_CORE;
}
assert (resultlen < bufsize);
memcpy (p, result, resultlen);
p += resultlen;
}
do
{
int len = (sw & 0x00ff);
if (DBG_CARD_IO)
log_debug ("apdu_send_direct(%d): %d more bytes available\n",
slot, len);
apdu = short_apdu_buffer;
apdulen = 0;
apdu[apdulen++] = class;
apdu[apdulen++] = 0xC0;
apdu[apdulen++] = 0;
apdu[apdulen++] = 0;
apdu[apdulen++] = len;
memset (apdu+apdulen, 0, sizeof (short_apdu_buffer) - apdulen);
resultlen = result_buffer_size;
rc = send_apdu (slot, apdu, apdulen, result, &resultlen, NULL);
if (rc || resultlen < 2)
{
log_error ("apdu_send_direct(%d) for get response failed: %s\n",
slot, apdu_strerror (rc));
unlock_slot (slot);
xfree (result_buffer);
return rc ? rc : SW_HOST_INCOMPLETE_CARD_RESPONSE;
}
sw = (result[resultlen-2] << 8) | result[resultlen-1];
resultlen -= 2;
if (DBG_CARD_IO)
{
log_debug (" more: sw=%04X datalen=%d\n",
sw, (unsigned int)resultlen);
if (!retbuf && (sw==SW_SUCCESS || (sw&0xff00)==SW_MORE_DATA))
- log_printhex (" dump: ", result, resultlen);
+ log_printhex (result, resultlen, " dump: ");
}
if ((sw & 0xff00) == SW_MORE_DATA
|| sw == SW_SUCCESS
|| sw == SW_EOF_REACHED )
{
if (retbuf && resultlen)
{
if (p - *retbuf + resultlen > bufsize)
{
bufsize += resultlen > 4096? resultlen: 4096;
tmp = xtryrealloc (*retbuf, bufsize + 2);
if (!tmp)
{
unlock_slot (slot);
xfree (result_buffer);
return SW_HOST_OUT_OF_CORE;
}
p = tmp + (p - *retbuf);
*retbuf = tmp;
}
memcpy (p, result, resultlen);
p += resultlen;
}
}
else
log_info ("apdu_send_direct(%d) "
"got unexpected status %04X from get response\n",
slot, sw);
}
while ((sw & 0xff00) == SW_MORE_DATA);
if (retbuf)
{
*retbuflen = p - *retbuf;
tmp = xtryrealloc (*retbuf, *retbuflen + 2);
if (tmp)
*retbuf = tmp;
}
}
else
{
if (retbuf)
{
*retbuf = xtrymalloc ((resultlen? resultlen : 1)+2);
if (!*retbuf)
{
unlock_slot (slot);
xfree (result_buffer);
return SW_HOST_OUT_OF_CORE;
}
*retbuflen = resultlen;
memcpy (*retbuf, result, resultlen);
}
}
unlock_slot (slot);
xfree (result_buffer);
/* Append the status word. Note that we reserved the two extra
bytes while allocating the buffer. */
if (retbuf)
{
(*retbuf)[(*retbuflen)++] = (sw >> 8);
(*retbuf)[(*retbuflen)++] = sw;
}
if (r_sw)
*r_sw = sw;
if (DBG_CARD_IO && retbuf)
- log_printhex (" dump: ", *retbuf, *retbuflen);
+ log_printhex (*retbuf, *retbuflen, " dump: ");
return 0;
}
const char *
apdu_get_reader_name (int slot)
{
return reader_table[slot].rdrname;
}
gpg_error_t
apdu_init (void)
{
#ifdef USE_NPTH
gpg_error_t err;
int i;
if (npth_mutex_init (&reader_table_lock, NULL))
goto leave;
for (i = 0; i < MAX_READER; i++)
if (npth_mutex_init (&reader_table[i].lock, NULL))
goto leave;
/* All done well. */
return 0;
leave:
err = gpg_error_from_syserror ();
log_error ("apdu: error initializing mutex: %s\n", gpg_strerror (err));
return err;
#endif /*USE_NPTH*/
return 0;
}
diff --git a/scd/app-openpgp.c b/scd/app-openpgp.c
index aa80016f6..8778ed7f7 100644
--- a/scd/app-openpgp.c
+++ b/scd/app-openpgp.c
@@ -1,5400 +1,5400 @@
/* app-openpgp.c - The OpenPGP card application.
* Copyright (C) 2003, 2004, 2005, 2007, 2008,
* 2009, 2013, 2014, 2015 Free Software Foundation, Inc.
*
* This file is part of GnuPG.
*
* GnuPG is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* GnuPG is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see .
*/
/* Some notes:
CHV means Card Holder Verification and is nothing else than a PIN
or password. That term seems to have been used originally with GSM
cards. Version v2 of the specs changes the term to the clearer
term PW for password. We use the terms here interchangeable
because we do not want to change existing strings i18n wise.
Version 2 of the specs also drops the separate PW2 which was
required in v1 due to ISO requirements. It is now possible to have
one physical PW but two reference to it so that they can be
individually be verified (e.g. to implement a forced verification
for one key). Thus you will noticed the use of PW2 with the verify
command but not with change_reference_data because the latter
operates directly on the physical PW.
The Reset Code (RC) as implemented by v2 cards uses the same error
counter as the PW2 of v1 cards. By default no RC is set and thus
that error counter is set to 0. After setting the RC the error
counter will be initialized to 3.
*/
#include
#include
#include
#include
#include
#include
#include
#include
#if GNUPG_MAJOR_VERSION == 1
/* This is used with GnuPG version < 1.9. The code has been source
copied from the current GnuPG >= 1.9 and is maintained over
there. */
#include "options.h"
#include "errors.h"
#include "memory.h"
#include "cardglue.h"
#else /* GNUPG_MAJOR_VERSION != 1 */
#include "scdaemon.h"
#endif /* GNUPG_MAJOR_VERSION != 1 */
#include "../common/util.h"
#include "../common/i18n.h"
#include "iso7816.h"
#include "app-common.h"
#include "../common/tlv.h"
#include "../common/host2net.h"
#include "../common/openpgpdefs.h"
#define KDF_DATA_LENGTH_MIN 90
#define KDF_DATA_LENGTH_MAX 110
/* A table describing the DOs of the card. */
static struct {
int tag;
int constructed;
int get_from; /* Constructed DO with this DO or 0 for direct access. */
unsigned int binary:1;
unsigned int dont_cache:1;
unsigned int flush_on_error:1;
unsigned int get_immediate_in_v11:1; /* Enable a hack to bypass the cache of
this data object if it is used in 1.1
and later versions of the card. This
does not work with composite DO and
is currently only useful for the CHV
status bytes. */
unsigned int try_extlen:2; /* Large object; try to use an extended
length APDU when !=0. The size is
determined by extcap.max_certlen_3
when == 1, and by extcap.max_special_do
when == 2. */
char *desc;
} data_objects[] = {
{ 0x005E, 0, 0, 1, 0, 0, 0, 2, "Login Data" },
{ 0x5F50, 0, 0, 0, 0, 0, 0, 2, "URL" },
{ 0x5F52, 0, 0, 1, 0, 0, 0, 0, "Historical Bytes" },
{ 0x0065, 1, 0, 1, 0, 0, 0, 0, "Cardholder Related Data"},
{ 0x005B, 0, 0x65, 0, 0, 0, 0, 0, "Name" },
{ 0x5F2D, 0, 0x65, 0, 0, 0, 0, 0, "Language preferences" },
{ 0x5F35, 0, 0x65, 0, 0, 0, 0, 0, "Salutation" },
{ 0x006E, 1, 0, 1, 0, 0, 0, 0, "Application Related Data" },
{ 0x004F, 0, 0x6E, 1, 0, 0, 0, 0, "AID" },
{ 0x0073, 1, 0, 1, 0, 0, 0, 0, "Discretionary Data Objects" },
{ 0x0047, 0, 0x6E, 1, 1, 0, 0, 0, "Card Capabilities" },
{ 0x00C0, 0, 0x6E, 1, 1, 0, 0, 0, "Extended Card Capabilities" },
{ 0x00C1, 0, 0x6E, 1, 1, 0, 0, 0, "Algorithm Attributes Signature" },
{ 0x00C2, 0, 0x6E, 1, 1, 0, 0, 0, "Algorithm Attributes Decryption" },
{ 0x00C3, 0, 0x6E, 1, 1, 0, 0, 0, "Algorithm Attributes Authentication" },
{ 0x00C4, 0, 0x6E, 1, 0, 1, 1, 0, "CHV Status Bytes" },
{ 0x00C5, 0, 0x6E, 1, 0, 0, 0, 0, "Fingerprints" },
{ 0x00C6, 0, 0x6E, 1, 0, 0, 0, 0, "CA Fingerprints" },
{ 0x00CD, 0, 0x6E, 1, 0, 0, 0, 0, "Generation time" },
{ 0x007A, 1, 0, 1, 0, 0, 0, 0, "Security Support Template" },
{ 0x0093, 0, 0x7A, 1, 1, 0, 0, 0, "Digital Signature Counter" },
{ 0x0101, 0, 0, 0, 0, 0, 0, 2, "Private DO 1"},
{ 0x0102, 0, 0, 0, 0, 0, 0, 2, "Private DO 2"},
{ 0x0103, 0, 0, 0, 0, 0, 0, 2, "Private DO 3"},
{ 0x0104, 0, 0, 0, 0, 0, 0, 2, "Private DO 4"},
{ 0x7F21, 1, 0, 1, 0, 0, 0, 1, "Cardholder certificate"},
/* V3.0 */
{ 0x7F74, 0, 0, 1, 0, 0, 0, 0, "General Feature Management"},
{ 0x00D5, 0, 0, 1, 0, 0, 0, 0, "AES key data"},
{ 0x00F9, 0, 0, 1, 0, 0, 0, 0, "KDF data object"},
{ 0 }
};
/* Type of keys. */
typedef enum
{
KEY_TYPE_ECC,
KEY_TYPE_RSA,
}
key_type_t;
/* The format of RSA private keys. */
typedef enum
{
RSA_UNKNOWN_FMT,
RSA_STD,
RSA_STD_N,
RSA_CRT,
RSA_CRT_N
}
rsa_key_format_t;
/* One cache item for DOs. */
struct cache_s {
struct cache_s *next;
int tag;
size_t length;
unsigned char data[1];
};
/* Object with application (i.e. OpenPGP card) specific data. */
struct app_local_s {
/* A linked list with cached DOs. */
struct cache_s *cache;
/* Keep track of the public keys. */
struct
{
int read_done; /* True if we have at least tried to read them. */
unsigned char *key; /* This is a malloced buffer with a canonical
encoded S-expression encoding a public
key. Might be NULL if key is not
available. */
size_t keylen; /* The length of the above S-expression. This
is usually only required for cross checks
because the length of an S-expression is
implicitly available. */
} pk[3];
unsigned char status_indicator; /* The card status indicator. */
unsigned int manufacturer:16; /* Manufacturer ID from the s/n. */
/* Keep track of the ISO card capabilities. */
struct
{
unsigned int cmd_chaining:1; /* Command chaining is supported. */
unsigned int ext_lc_le:1; /* Extended Lc and Le are supported. */
} cardcap;
/* Keep track of extended card capabilities. */
struct
{
unsigned int is_v2:1; /* Compatible to v2 or later. */
unsigned int extcap_v3:1; /* Extcap is in v3 format. */
unsigned int has_button:1; /* Has confirmation button or not. */
unsigned int sm_supported:1; /* Secure Messaging is supported. */
unsigned int get_challenge:1;
unsigned int key_import:1;
unsigned int change_force_chv:1;
unsigned int private_dos:1;
unsigned int algo_attr_change:1; /* Algorithm attributes changeable. */
unsigned int has_decrypt:1; /* Support symmetric decryption. */
unsigned int kdf_do:1; /* Support KDF DO. */
unsigned int sm_algo:2; /* Symmetric crypto algo for SM. */
unsigned int pin_blk2:1; /* PIN block 2 format supported. */
unsigned int mse:1; /* MSE command supported. */
unsigned int max_certlen_3:16;
unsigned int max_get_challenge:16; /* Maximum size for get_challenge. */
unsigned int max_special_do:16; /* Maximum size for special DOs. */
} extcap;
/* Flags used to control the application. */
struct
{
unsigned int no_sync:1; /* Do not sync CHV1 and CHV2 */
unsigned int def_chv2:1; /* Use 123456 for CHV2. */
} flags;
/* Pinpad request specified on card. */
struct
{
unsigned int disabled:1; /* No pinpad use because of KDF DO. */
unsigned int specified:1;
int fixedlen_user;
int fixedlen_admin;
} pinpad;
struct
{
key_type_t key_type;
union {
struct {
unsigned int n_bits; /* Size of the modulus in bits. The rest
of this strucuire is only valid if
this is not 0. */
unsigned int e_bits; /* Size of the public exponent in bits. */
rsa_key_format_t format;
} rsa;
struct {
const char *curve;
int flags;
} ecc;
};
} keyattr[3];
};
#define ECC_FLAG_DJB_TWEAK (1 << 0)
#define ECC_FLAG_PUBKEY (1 << 1)
/***** Local prototypes *****/
static unsigned long convert_sig_counter_value (const unsigned char *value,
size_t valuelen);
static unsigned long get_sig_counter (app_t app);
static gpg_error_t do_auth (app_t app, const char *keyidstr,
gpg_error_t (*pincb)(void*, const char *, char **),
void *pincb_arg,
const void *indata, size_t indatalen,
unsigned char **outdata, size_t *outdatalen);
static void parse_algorithm_attribute (app_t app, int keyno);
static gpg_error_t change_keyattr_from_string
(app_t app,
gpg_error_t (*pincb)(void*, const char *, char **),
void *pincb_arg,
const void *value, size_t valuelen);
/* Return the OpenPGP card manufacturer name. */
static const char *
get_manufacturer (unsigned int no)
{
/* Note: Make sure that there is no colon or linefeed in the string. */
switch (no)
{
case 0x0001: return "PPC Card Systems";
case 0x0002: return "Prism";
case 0x0003: return "OpenFortress";
case 0x0004: return "Wewid";
case 0x0005: return "ZeitControl";
case 0x0006: return "Yubico";
case 0x0007: return "OpenKMS";
case 0x0008: return "LogoEmail";
case 0x0009: return "Fidesmo";
case 0x000A: return "Dangerous Things";
case 0x000B: return "Feitian Technologies";
case 0x002A: return "Magrathea";
case 0x0042: return "GnuPG e.V.";
case 0x1337: return "Warsaw Hackerspace";
case 0x2342: return "warpzone"; /* hackerspace Muenster. */
case 0x4354: return "Confidential Technologies"; /* cotech.de */
case 0x5443: return "TIF-IT e.V.";
case 0x63AF: return "Trustica";
case 0xBA53: return "c-base e.V.";
case 0xBD0E: return "Paranoidlabs";
case 0xF517: return "FSIJ";
case 0xF5EC: return "F-Secure";
/* 0x0000 and 0xFFFF are defined as test cards per spec,
* 0xFF00 to 0xFFFE are assigned for use with randomly created
* serial numbers. */
case 0x0000:
case 0xffff: return "test card";
default: return (no & 0xff00) == 0xff00? "unmanaged S/N range":"unknown";
}
}
/* Deconstructor. */
static void
do_deinit (app_t app)
{
if (app && app->app_local)
{
struct cache_s *c, *c2;
int i;
for (c = app->app_local->cache; c; c = c2)
{
c2 = c->next;
xfree (c);
}
for (i=0; i < DIM (app->app_local->pk); i++)
{
xfree (app->app_local->pk[i].key);
app->app_local->pk[i].read_done = 0;
}
xfree (app->app_local);
app->app_local = NULL;
}
}
/* Wrapper around iso7816_get_data which first tries to get the data
from the cache. With GET_IMMEDIATE passed as true, the cache is
bypassed. With TRY_EXTLEN extended lengths APDUs are use if
supported by the card. */
static gpg_error_t
get_cached_data (app_t app, int tag,
unsigned char **result, size_t *resultlen,
int get_immediate, int try_extlen)
{
gpg_error_t err;
int i;
unsigned char *p;
size_t len;
struct cache_s *c;
int exmode;
*result = NULL;
*resultlen = 0;
if (!get_immediate)
{
for (c=app->app_local->cache; c; c = c->next)
if (c->tag == tag)
{
if(c->length)
{
p = xtrymalloc (c->length);
if (!p)
return gpg_error (gpg_err_code_from_errno (errno));
memcpy (p, c->data, c->length);
*result = p;
}
*resultlen = c->length;
return 0;
}
}
if (try_extlen && app->app_local->cardcap.ext_lc_le)
{
if (try_extlen == 1)
exmode = app->app_local->extcap.max_certlen_3;
else if (try_extlen == 2 && app->app_local->extcap.extcap_v3)
exmode = app->app_local->extcap.max_special_do;
else
exmode = 0;
}
else
exmode = 0;
err = iso7816_get_data (app->slot, exmode, tag, &p, &len);
if (err)
return err;
if (len)
*result = p;
*resultlen = len;
/* Check whether we should cache this object. */
if (get_immediate)
return 0;
for (i=0; data_objects[i].tag; i++)
if (data_objects[i].tag == tag)
{
if (data_objects[i].dont_cache)
return 0;
break;
}
/* Okay, cache it. */
for (c=app->app_local->cache; c; c = c->next)
assert (c->tag != tag);
c = xtrymalloc (sizeof *c + len);
if (c)
{
if (len)
memcpy (c->data, p, len);
else
xfree (p);
c->length = len;
c->tag = tag;
c->next = app->app_local->cache;
app->app_local->cache = c;
}
return 0;
}
/* Remove DO at TAG from the cache. */
static void
flush_cache_item (app_t app, int tag)
{
struct cache_s *c, *cprev;
int i;
if (!app->app_local)
return;
for (c=app->app_local->cache, cprev=NULL; c ; cprev=c, c = c->next)
if (c->tag == tag)
{
if (cprev)
cprev->next = c->next;
else
app->app_local->cache = c->next;
xfree (c);
for (c=app->app_local->cache; c ; c = c->next)
{
assert (c->tag != tag); /* Oops: duplicated entry. */
}
return;
}
/* Try again if we have an outer tag. */
for (i=0; data_objects[i].tag; i++)
if (data_objects[i].tag == tag && data_objects[i].get_from
&& data_objects[i].get_from != tag)
flush_cache_item (app, data_objects[i].get_from);
}
/* Flush all entries from the cache which might be out of sync after
an error. */
static void
flush_cache_after_error (app_t app)
{
int i;
for (i=0; data_objects[i].tag; i++)
if (data_objects[i].flush_on_error)
flush_cache_item (app, data_objects[i].tag);
}
/* Flush the entire cache. */
static void
flush_cache (app_t app)
{
if (app && app->app_local)
{
struct cache_s *c, *c2;
for (c = app->app_local->cache; c; c = c2)
{
c2 = c->next;
xfree (c);
}
app->app_local->cache = NULL;
}
}
/* Get the DO identified by TAG from the card in SLOT and return a
buffer with its content in RESULT and NBYTES. The return value is
NULL if not found or a pointer which must be used to release the
buffer holding value. */
static void *
get_one_do (app_t app, int tag, unsigned char **result, size_t *nbytes,
int *r_rc)
{
int rc, i;
unsigned char *buffer;
size_t buflen;
unsigned char *value;
size_t valuelen;
int dummyrc;
int exmode;
if (!r_rc)
r_rc = &dummyrc;
*result = NULL;
*nbytes = 0;
*r_rc = 0;
for (i=0; data_objects[i].tag && data_objects[i].tag != tag; i++)
;
if (app->card_version > 0x0100 && data_objects[i].get_immediate_in_v11)
{
exmode = 0;
rc = iso7816_get_data (app->slot, exmode, tag, &buffer, &buflen);
if (rc)
{
*r_rc = rc;
return NULL;
}
*result = buffer;
*nbytes = buflen;
return buffer;
}
value = NULL;
rc = -1;
if (data_objects[i].tag && data_objects[i].get_from)
{
rc = get_cached_data (app, data_objects[i].get_from,
&buffer, &buflen,
(data_objects[i].dont_cache
|| data_objects[i].get_immediate_in_v11),
data_objects[i].try_extlen);
if (!rc)
{
const unsigned char *s;
s = find_tlv_unchecked (buffer, buflen, tag, &valuelen);
if (!s)
value = NULL; /* not found */
else if (valuelen > buflen - (s - buffer))
{
log_error ("warning: constructed DO too short\n");
value = NULL;
xfree (buffer); buffer = NULL;
}
else
value = buffer + (s - buffer);
}
}
if (!value) /* Not in a constructed DO, try simple. */
{
rc = get_cached_data (app, tag, &buffer, &buflen,
(data_objects[i].dont_cache
|| data_objects[i].get_immediate_in_v11),
data_objects[i].try_extlen);
if (!rc)
{
value = buffer;
valuelen = buflen;
}
}
if (!rc)
{
*nbytes = valuelen;
*result = value;
return buffer;
}
*r_rc = rc;
return NULL;
}
static void
dump_all_do (int slot)
{
int rc, i, j;
unsigned char *buffer;
size_t buflen;
for (i=0; data_objects[i].tag; i++)
{
if (data_objects[i].get_from)
continue;
/* We don't try extended length APDU because such large DO would
be pretty useless in a log file. */
rc = iso7816_get_data (slot, 0, data_objects[i].tag, &buffer, &buflen);
if (gpg_err_code (rc) == GPG_ERR_NO_OBJ)
;
else if (rc)
log_info ("DO '%s' not available: %s\n",
data_objects[i].desc, gpg_strerror (rc));
else
{
if (data_objects[i].binary)
{
log_info ("DO '%s': ", data_objects[i].desc);
- log_printhex ("", buffer, buflen);
+ log_printhex (buffer, buflen, "");
}
else
log_info ("DO '%s': '%.*s'\n",
data_objects[i].desc,
(int)buflen, buffer); /* FIXME: sanitize */
if (data_objects[i].constructed)
{
for (j=0; data_objects[j].tag; j++)
{
const unsigned char *value;
size_t valuelen;
if (j==i || data_objects[i].tag != data_objects[j].get_from)
continue;
value = find_tlv_unchecked (buffer, buflen,
data_objects[j].tag, &valuelen);
if (!value)
; /* not found */
else if (valuelen > buflen - (value - buffer))
log_error ("warning: constructed DO too short\n");
else
{
if (data_objects[j].binary)
{
log_info ("DO '%s': ", data_objects[j].desc);
if (valuelen > 200)
log_info ("[%u]\n", (unsigned int)valuelen);
else
- log_printhex ("", value, valuelen);
+ log_printhex (value, valuelen, "");
}
else
log_info ("DO '%s': '%.*s'\n",
data_objects[j].desc,
(int)valuelen, value); /* FIXME: sanitize */
}
}
}
}
xfree (buffer); buffer = NULL;
}
}
/* Count the number of bits, assuming the A represents an unsigned big
integer of length LEN bytes. */
static unsigned int
count_bits (const unsigned char *a, size_t len)
{
unsigned int n = len * 8;
int i;
for (; len && !*a; len--, a++, n -=8)
;
if (len)
{
for (i=7; i && !(*a & (1<
Where FLAGS is a plain hexadecimal number representing flag values.
The lsb is here the rightmost bit. Defined flags bits are:
Bit 0 = CHV1 and CHV2 are not synchronized
Bit 1 = CHV2 has been set to the default PIN of "123456"
(this implies that bit 0 is also set).
P=
Where PINPAD_REQUEST is in the format of: or ,.
N for user PIN, M for admin PIN. If M is missing it means M=N.
0 means to force not to use pinpad.
*/
static void
parse_login_data (app_t app)
{
unsigned char *buffer, *p;
size_t buflen, len;
void *relptr;
/* Set defaults. */
app->app_local->flags.no_sync = 0;
app->app_local->flags.def_chv2 = 0;
app->app_local->pinpad.specified = 0;
app->app_local->pinpad.fixedlen_user = -1;
app->app_local->pinpad.fixedlen_admin = -1;
/* Read the DO. */
relptr = get_one_do (app, 0x005E, &buffer, &buflen, NULL);
if (!relptr)
return; /* Ooops. */
for (; buflen; buflen--, buffer++)
if (*buffer == '\n')
break;
if (buflen < 2 || buffer[1] != '\x14')
{
xfree (relptr);
return; /* No control sequences. */
}
buflen--;
buffer++;
do
{
buflen--;
buffer++;
if (buflen > 1 && *buffer == 'F' && buffer[1] == '=')
{
/* Flags control sequence found. */
int lastdig = 0;
/* For now we are only interested in the last digit, so skip
any leading digits but bail out on invalid characters. */
for (p=buffer+2, len = buflen-2; len && hexdigitp (p); p++, len--)
lastdig = xtoi_1 (p);
buffer = p;
buflen = len;
if (len && !(*p == '\n' || *p == '\x18'))
goto next; /* Invalid characters in field. */
app->app_local->flags.no_sync = !!(lastdig & 1);
app->app_local->flags.def_chv2 = (lastdig & 3) == 3;
}
else if (buflen > 1 && *buffer == 'P' && buffer[1] == '=')
{
/* Pinpad request control sequence found. */
buffer += 2;
buflen -= 2;
if (buflen)
{
if (digitp (buffer))
{
char *q;
int n, m;
n = strtol (buffer, &q, 10);
if (q >= (char *)buffer + buflen
|| *q == '\x18' || *q == '\n')
m = n;
else
{
if (*q++ != ',' || !digitp (q))
goto next;
m = strtol (q, &q, 10);
}
if (buflen < ((unsigned char *)q - buffer))
break;
buflen -= ((unsigned char *)q - buffer);
buffer = q;
if (buflen && !(*buffer == '\n' || *buffer == '\x18'))
goto next;
app->app_local->pinpad.specified = 1;
app->app_local->pinpad.fixedlen_user = n;
app->app_local->pinpad.fixedlen_admin = m;
}
}
}
next:
/* Skip to FS (0x18) or LF (\n). */
for (; buflen && *buffer != '\x18' && *buffer != '\n'; buflen--)
buffer++;
}
while (buflen && *buffer != '\n');
xfree (relptr);
}
#define MAX_ARGS_STORE_FPR 3
/* Note, that FPR must be at least 20 bytes. */
static gpg_error_t
store_fpr (app_t app, int keynumber, u32 timestamp, unsigned char *fpr,
int algo, ...)
{
unsigned int n, nbits;
unsigned char *buffer, *p;
int tag, tag2;
int rc;
const unsigned char *m[MAX_ARGS_STORE_FPR];
size_t mlen[MAX_ARGS_STORE_FPR];
va_list ap;
int argc;
int i;
n = 6; /* key packet version, 4-byte timestamps, and algorithm */
if (algo == PUBKEY_ALGO_ECDH)
argc = 3;
else
argc = 2;
va_start (ap, algo);
for (i = 0; i < argc; i++)
{
m[i] = va_arg (ap, const unsigned char *);
mlen[i] = va_arg (ap, size_t);
if (algo == PUBKEY_ALGO_RSA || i == 1)
n += 2;
n += mlen[i];
}
va_end (ap);
p = buffer = xtrymalloc (3 + n);
if (!buffer)
return gpg_error_from_syserror ();
*p++ = 0x99; /* ctb */
*p++ = n >> 8; /* 2 byte length header */
*p++ = n;
*p++ = 4; /* key packet version */
*p++ = timestamp >> 24;
*p++ = timestamp >> 16;
*p++ = timestamp >> 8;
*p++ = timestamp;
*p++ = algo;
for (i = 0; i < argc; i++)
{
if (algo == PUBKEY_ALGO_RSA || i == 1)
{
nbits = count_bits (m[i], mlen[i]);
*p++ = nbits >> 8;
*p++ = nbits;
}
memcpy (p, m[i], mlen[i]);
p += mlen[i];
}
gcry_md_hash_buffer (GCRY_MD_SHA1, fpr, buffer, n+3);
xfree (buffer);
tag = (app->card_version > 0x0007? 0xC7 : 0xC6) + keynumber;
flush_cache_item (app, 0xC5);
tag2 = 0xCE + keynumber;
flush_cache_item (app, 0xCD);
rc = iso7816_put_data (app->slot, 0, tag, fpr, 20);
if (rc)
log_error (_("failed to store the fingerprint: %s\n"),gpg_strerror (rc));
if (!rc && app->card_version > 0x0100)
{
unsigned char buf[4];
buf[0] = timestamp >> 24;
buf[1] = timestamp >> 16;
buf[2] = timestamp >> 8;
buf[3] = timestamp;
rc = iso7816_put_data (app->slot, 0, tag2, buf, 4);
if (rc)
log_error (_("failed to store the creation date: %s\n"),
gpg_strerror (rc));
}
return rc;
}
static void
send_fpr_if_not_null (ctrl_t ctrl, const char *keyword,
int number, const unsigned char *fpr)
{
int i;
char buf[41];
char numbuf[25];
for (i=0; i < 20 && !fpr[i]; i++)
;
if (i==20)
return; /* All zero. */
bin2hex (fpr, 20, buf);
if (number == -1)
*numbuf = 0; /* Don't print the key number */
else
sprintf (numbuf, "%d", number);
send_status_info (ctrl, keyword,
numbuf, (size_t)strlen(numbuf),
buf, (size_t)strlen (buf), NULL, 0);
}
static void
send_fprtime_if_not_null (ctrl_t ctrl, const char *keyword,
int number, const unsigned char *stamp)
{
char numbuf1[50], numbuf2[50];
unsigned long value;
value = buf32_to_ulong (stamp);
if (!value)
return;
sprintf (numbuf1, "%d", number);
sprintf (numbuf2, "%lu", value);
send_status_info (ctrl, keyword,
numbuf1, (size_t)strlen(numbuf1),
numbuf2, (size_t)strlen(numbuf2), NULL, 0);
}
static void
send_key_data (ctrl_t ctrl, const char *name,
const unsigned char *a, size_t alen)
{
char *buffer, *buf;
size_t buflen;
buffer = buf = bin2hex (a, alen, NULL);
if (!buffer)
{
log_error ("memory allocation error in send_key_data\n");
return;
}
buflen = strlen (buffer);
/* 768 is the hexified size for the modulus of an 3072 bit key. We
use extra chunks to transmit larger data (i.e for 4096 bit). */
for ( ;buflen > 768; buflen -= 768, buf += 768)
send_status_info (ctrl, "KEY-DATA",
"-", 1,
buf, 768,
NULL, 0);
send_status_info (ctrl, "KEY-DATA",
name, (size_t)strlen(name),
buf, buflen,
NULL, 0);
xfree (buffer);
}
static void
send_key_attr (ctrl_t ctrl, app_t app, const char *keyword, int keyno)
{
char buffer[200];
assert (keyno >=0 && keyno < DIM(app->app_local->keyattr));
if (app->app_local->keyattr[keyno].key_type == KEY_TYPE_RSA)
snprintf (buffer, sizeof buffer, "%d 1 rsa%u %u %d",
keyno+1,
app->app_local->keyattr[keyno].rsa.n_bits,
app->app_local->keyattr[keyno].rsa.e_bits,
app->app_local->keyattr[keyno].rsa.format);
else if (app->app_local->keyattr[keyno].key_type == KEY_TYPE_ECC)
{
snprintf (buffer, sizeof buffer, "%d %d %s",
keyno+1,
keyno==1? PUBKEY_ALGO_ECDH :
(app->app_local->keyattr[keyno].ecc.flags & ECC_FLAG_DJB_TWEAK)?
PUBKEY_ALGO_EDDSA : PUBKEY_ALGO_ECDSA,
app->app_local->keyattr[keyno].ecc.curve);
}
else
snprintf (buffer, sizeof buffer, "%d 0 0 UNKNOWN", keyno+1);
send_status_direct (ctrl, keyword, buffer);
}
#define RSA_SMALL_SIZE_KEY 1952
#define RSA_SMALL_SIZE_OP 2048
static int
determine_rsa_response (app_t app, int keyno)
{
int size;
size = 2 + 3 /* header */
+ 4 /* tag+len */ + (app->app_local->keyattr[keyno].rsa.n_bits+7)/8
+ 2 /* tag+len */ + (app->app_local->keyattr[keyno].rsa.e_bits+7)/8;
return size;
}
/* 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 tag;
int special;
} table[] = {
{ "DISP-NAME", 0x005B },
{ "LOGIN-DATA", 0x005E },
{ "DISP-LANG", 0x5F2D },
{ "DISP-SEX", 0x5F35 },
{ "PUBKEY-URL", 0x5F50 },
{ "KEY-FPR", 0x00C5, 3 },
{ "KEY-TIME", 0x00CD, 4 },
{ "KEY-ATTR", 0x0000, -5 },
{ "CA-FPR", 0x00C6, 3 },
{ "CHV-STATUS", 0x00C4, 1 },
{ "SIG-COUNTER", 0x0093, 2 },
{ "SERIALNO", 0x004F, -1 },
{ "AID", 0x004F },
{ "EXTCAP", 0x0000, -2 },
{ "PRIVATE-DO-1", 0x0101 },
{ "PRIVATE-DO-2", 0x0102 },
{ "PRIVATE-DO-3", 0x0103 },
{ "PRIVATE-DO-4", 0x0104 },
{ "$AUTHKEYID", 0x0000, -3 },
{ "$ENCRKEYID", 0x0000, -6 },
{ "$SIGNKEYID", 0x0000, -7 },
{ "$DISPSERIALNO",0x0000, -4 },
{ "KDF", 0x00F9, 5 },
{ "MANUFACTURER", 0x0000, -8 },
{ NULL, 0 }
};
int idx, i, rc;
void *relptr;
unsigned char *value;
size_t valuelen;
for (idx=0; table[idx].name && strcmp (table[idx].name, name); idx++)
;
if (!table[idx].name)
return gpg_error (GPG_ERR_INV_NAME);
if (table[idx].special == -1)
{
/* The serial number is very special. We could have used the
AID DO to retrieve it. The AID DO is available anyway but
not hex formatted. */
char *serial = app_get_serialno (app);
if (serial)
{
send_status_direct (ctrl, "SERIALNO", serial);
xfree (serial);
}
return 0;
}
if (table[idx].special == -2)
{
char tmp[110];
snprintf (tmp, sizeof tmp,
"gc=%d ki=%d fc=%d pd=%d mcl3=%u aac=%d "
"sm=%d si=%u dec=%d bt=%d kdf=%d",
app->app_local->extcap.get_challenge,
app->app_local->extcap.key_import,
app->app_local->extcap.change_force_chv,
app->app_local->extcap.private_dos,
app->app_local->extcap.max_certlen_3,
app->app_local->extcap.algo_attr_change,
(app->app_local->extcap.sm_supported
? (app->app_local->extcap.sm_algo == 0? CIPHER_ALGO_3DES :
(app->app_local->extcap.sm_algo == 1?
CIPHER_ALGO_AES : CIPHER_ALGO_AES256))
: 0),
app->app_local->status_indicator,
app->app_local->extcap.has_decrypt,
app->app_local->extcap.has_button,
app->app_local->extcap.kdf_do);
send_status_info (ctrl, table[idx].name, tmp, strlen (tmp), NULL, 0);
return 0;
}
if (table[idx].special == -3)
{
char const tmp[] = "OPENPGP.3";
send_status_info (ctrl, table[idx].name, tmp, strlen (tmp), NULL, 0);
return 0;
}
if (table[idx].special == -4)
{
char *serial = app_get_serialno (app);
if (serial)
{
if (strlen (serial) > 16+12)
{
send_status_info (ctrl, table[idx].name, serial+16, 12, NULL, 0);
xfree (serial);
return 0;
}
xfree (serial);
}
return gpg_error (GPG_ERR_INV_NAME);
}
if (table[idx].special == -5)
{
for (i=0; i < 3; i++)
send_key_attr (ctrl, app, table[idx].name, i);
return 0;
}
if (table[idx].special == -6)
{
char const tmp[] = "OPENPGP.2";
send_status_info (ctrl, table[idx].name, tmp, strlen (tmp), NULL, 0);
return 0;
}
if (table[idx].special == -7)
{
char const tmp[] = "OPENPGP.1";
send_status_info (ctrl, table[idx].name, tmp, strlen (tmp), NULL, 0);
return 0;
}
if (table[idx].special == -8)
{
return send_status_printf
(ctrl, table[idx].name, "%u %s",
app->app_local->manufacturer,
get_manufacturer (app->app_local->manufacturer));
}
relptr = get_one_do (app, table[idx].tag, &value, &valuelen, &rc);
if (relptr)
{
if (table[idx].special == 1)
{
char numbuf[7*23];
for (i=0,*numbuf=0; i < valuelen && i < 7; i++)
sprintf (numbuf+strlen (numbuf), " %d", value[i]);
send_status_info (ctrl, table[idx].name,
numbuf, strlen (numbuf), NULL, 0);
}
else if (table[idx].special == 2)
{
char numbuf[50];
sprintf (numbuf, "%lu", convert_sig_counter_value (value, valuelen));
send_status_info (ctrl, table[idx].name,
numbuf, strlen (numbuf), NULL, 0);
}
else if (table[idx].special == 3)
{
if (valuelen >= 60)
for (i=0; i < 3; i++)
send_fpr_if_not_null (ctrl, table[idx].name, i+1, value+i*20);
}
else if (table[idx].special == 4)
{
if (valuelen >= 12)
for (i=0; i < 3; i++)
send_fprtime_if_not_null (ctrl, table[idx].name, i+1, value+i*4);
}
else if (table[idx].special == 5)
{
if ((valuelen == KDF_DATA_LENGTH_MIN
|| valuelen == KDF_DATA_LENGTH_MAX)
&& (value[2] == 0x03))
app->app_local->pinpad.disabled = 1;
else
app->app_local->pinpad.disabled = 0;
send_status_info (ctrl, table[idx].name, value, valuelen, NULL, 0);
}
else
send_status_info (ctrl, table[idx].name, value, valuelen, NULL, 0);
xfree (relptr);
}
else
{
if (table[idx].special == 5)
app->app_local->pinpad.disabled = 0;
}
return rc;
}
/* Return the DISP-NAME without any padding characters. Caller must
* free the result. If not found or empty NULL is returned. */
static char *
get_disp_name (app_t app)
{
int rc;
void *relptr;
unsigned char *value;
size_t valuelen;
char *string;
char *p, *given;
char *result;
relptr = get_one_do (app, 0x005B, &value, &valuelen, &rc);
if (!relptr)
return NULL;
string = xtrymalloc (valuelen + 1);
if (!string)
{
xfree (relptr);
return NULL;
}
memcpy (string, value, valuelen);
string[valuelen] = 0;
xfree (relptr);
/* Swap surname and given name. */
given = strstr (string, "<<");
for (p = string; *p; p++)
if (*p == '<')
*p = ' ';
if (given && given[2])
{
*given = 0;
given += 2;
result = strconcat (given, " ", string, NULL);
}
else
{
result = string;
string = NULL;
}
xfree (string);
return result;
}
/* Return the pretty formatted serialnumber. On error NULL is
* returned. */
static char *
get_disp_serialno (app_t app)
{
char *serial = app_get_serialno (app);
/* For our OpenPGP cards we do not want to show the entire serial
* number but a nicely reformatted actual serial number. */
if (serial && strlen (serial) > 16+12)
{
memmove (serial, serial+16, 4);
serial[4] = ' ';
/* memmove (serial+5, serial+20, 4); */
/* serial[9] = ' '; */
/* memmove (serial+10, serial+24, 4); */
/* serial[14] = 0; */
memmove (serial+5, serial+20, 8);
serial[13] = 0;
}
return serial;
}
/* Return the number of remaining tries for the standard or the admin
* pw. Returns -1 on card error. */
static int
get_remaining_tries (app_t app, int adminpw)
{
void *relptr;
unsigned char *value;
size_t valuelen;
int remaining;
relptr = get_one_do (app, 0x00C4, &value, &valuelen, NULL);
if (!relptr || valuelen < 7)
{
log_error (_("error retrieving CHV status from card\n"));
xfree (relptr);
return -1;
}
remaining = value[adminpw? 6 : 4];
xfree (relptr);
return remaining;
}
/* Retrieve the fingerprint from the card inserted in SLOT and write
the according hex representation to FPR. Caller must have provide
a buffer at FPR of least 41 bytes. Returns 0 on success or an
error code. */
#if GNUPG_MAJOR_VERSION > 1
static gpg_error_t
retrieve_fpr_from_card (app_t app, int keyno, char *fpr)
{
gpg_error_t err = 0;
void *relptr;
unsigned char *value;
size_t valuelen;
assert (keyno >=0 && keyno <= 2);
relptr = get_one_do (app, 0x00C5, &value, &valuelen, NULL);
if (relptr && valuelen >= 60)
bin2hex (value+keyno*20, 20, fpr);
else
err = gpg_error (GPG_ERR_NOT_FOUND);
xfree (relptr);
return err;
}
#endif /*GNUPG_MAJOR_VERSION > 1*/
/* Retrieve the public key material for the RSA key, whose fingerprint
is FPR, from gpg output, which can be read through the stream FP.
The RSA modulus will be stored at the address of M and MLEN, the
public exponent at E and ELEN. Returns zero on success, an error
code on failure. Caller must release the allocated buffers at M
and E if the function returns success. */
#if GNUPG_MAJOR_VERSION > 1
static gpg_error_t
retrieve_key_material (FILE *fp, const char *hexkeyid,
const unsigned char **m, size_t *mlen,
const unsigned char **e, size_t *elen)
{
gcry_error_t err = 0;
char *line = NULL; /* read_line() buffer. */
size_t line_size = 0; /* Helper for for read_line. */
int found_key = 0; /* Helper to find a matching key. */
unsigned char *m_new = NULL;
unsigned char *e_new = NULL;
size_t m_new_n = 0;
size_t e_new_n = 0;
/* Loop over all records until we have found the subkey
corresponding to the fingerprint. Inm general the first record
should be the pub record, but we don't rely on that. Given that
we only need to look at one key, it is sufficient to compare the
keyid so that we don't need to look at "fpr" records. */
for (;;)
{
char *p;
char *fields[6] = { NULL, NULL, NULL, NULL, NULL, NULL };
int nfields;
size_t max_length;
gcry_mpi_t mpi;
int i;
max_length = 4096;
i = read_line (fp, &line, &line_size, &max_length);
if (!i)
break; /* EOF. */
if (i < 0)
{
err = gpg_error_from_syserror ();
goto leave; /* Error. */
}
if (!max_length)
{
err = gpg_error (GPG_ERR_TRUNCATED);
goto leave; /* Line truncated - we better stop processing. */
}
/* Parse the line into fields. */
for (nfields=0, p=line; p && nfields < DIM (fields); nfields++)
{
fields[nfields] = p;
p = strchr (p, ':');
if (p)
*(p++) = 0;
}
if (!nfields)
continue; /* No fields at all - skip line. */
if (!found_key)
{
if ( (!strcmp (fields[0], "sub") || !strcmp (fields[0], "pub") )
&& nfields > 4 && !strcmp (fields[4], hexkeyid))
found_key = 1;
continue;
}
if ( !strcmp (fields[0], "sub") || !strcmp (fields[0], "pub") )
break; /* Next key - stop. */
if ( strcmp (fields[0], "pkd") )
continue; /* Not a key data record. */
if ( nfields < 4 || (i = atoi (fields[1])) < 0 || i > 1
|| (!i && m_new) || (i && e_new))
{
err = gpg_error (GPG_ERR_GENERAL);
goto leave; /* Error: Invalid key data record or not an RSA key. */
}
err = gcry_mpi_scan (&mpi, GCRYMPI_FMT_HEX, fields[3], 0, NULL);
if (err)
mpi = NULL;
else if (!i)
err = gcry_mpi_aprint (GCRYMPI_FMT_STD, &m_new, &m_new_n, mpi);
else
err = gcry_mpi_aprint (GCRYMPI_FMT_STD, &e_new, &e_new_n, mpi);
gcry_mpi_release (mpi);
if (err)
goto leave;
}
if (m_new && e_new)
{
*m = m_new;
*mlen = m_new_n;
m_new = NULL;
*e = e_new;
*elen = e_new_n;
e_new = NULL;
}
else
err = gpg_error (GPG_ERR_GENERAL);
leave:
xfree (m_new);
xfree (e_new);
xfree (line);
return err;
}
#endif /*GNUPG_MAJOR_VERSION > 1*/
static gpg_error_t
rsa_read_pubkey (app_t app, ctrl_t ctrl, u32 created_at, int keyno,
const unsigned char *data, size_t datalen, gcry_sexp_t *r_sexp)
{
gpg_error_t err;
const unsigned char *m, *e;
size_t mlen, elen;
unsigned char *mbuf = NULL, *ebuf = NULL;
m = find_tlv (data, datalen, 0x0081, &mlen);
if (!m)
{
log_error (_("response does not contain the RSA modulus\n"));
return gpg_error (GPG_ERR_CARD);
}
e = find_tlv (data, datalen, 0x0082, &elen);
if (!e)
{
log_error (_("response does not contain the RSA public exponent\n"));
return gpg_error (GPG_ERR_CARD);
}
if (ctrl)
{
send_key_data (ctrl, "n", m, mlen);
send_key_data (ctrl, "e", e, elen);
}
for (; mlen && !*m; mlen--, m++) /* strip leading zeroes */
;
for (; elen && !*e; elen--, e++) /* strip leading zeroes */
;
if (ctrl)
{
unsigned char fprbuf[20];
err = store_fpr (app, keyno, created_at, fprbuf, PUBKEY_ALGO_RSA,
m, mlen, e, elen);
if (err)
return err;
send_fpr_if_not_null (ctrl, "KEY-FPR", -1, fprbuf);
}
mbuf = xtrymalloc (mlen + 1);
if (!mbuf)
{
err = gpg_error_from_syserror ();
goto leave;
}
/* Prepend numbers with a 0 if needed. */
if (mlen && (*m & 0x80))
{
*mbuf = 0;
memcpy (mbuf+1, m, mlen);
mlen++;
}
else
memcpy (mbuf, m, mlen);
ebuf = xtrymalloc (elen + 1);
if (!ebuf)
{
err = gpg_error_from_syserror ();
goto leave;
}
/* Prepend numbers with a 0 if needed. */
if (elen && (*e & 0x80))
{
*ebuf = 0;
memcpy (ebuf+1, e, elen);
elen++;
}
else
memcpy (ebuf, e, elen);
err = gcry_sexp_build (r_sexp, NULL, "(public-key(rsa(n%b)(e%b)))",
(int)mlen, mbuf, (int)elen, ebuf);
leave:
xfree (mbuf);
xfree (ebuf);
return err;
}
/* Determine KDF hash algorithm and KEK encryption algorithm by CURVE. */
static const unsigned char*
ecdh_params (const char *curve)
{
unsigned int nbits;
openpgp_curve_to_oid (curve, &nbits);
/* See RFC-6637 for those constants.
0x03: Number of bytes
0x01: Version for this parameter format
KDF hash algo
KEK symmetric cipher algo
*/
if (nbits <= 256)
return (const unsigned char*)"\x03\x01\x08\x07";
else if (nbits <= 384)
return (const unsigned char*)"\x03\x01\x09\x08";
else
return (const unsigned char*)"\x03\x01\x0a\x09";
}
static gpg_error_t
ecc_read_pubkey (app_t app, ctrl_t ctrl, u32 created_at, int keyno,
const unsigned char *data, size_t datalen, gcry_sexp_t *r_sexp)
{
gpg_error_t err;
unsigned char *qbuf = NULL;
const unsigned char *ecc_q;
size_t ecc_q_len;
gcry_mpi_t oid = NULL;
int n;
const char *curve;
const char *oidstr;
const unsigned char *oidbuf;
size_t oid_len;
int algo;
const char *format;
ecc_q = find_tlv (data, datalen, 0x0086, &ecc_q_len);
if (!ecc_q)
{
log_error (_("response does not contain the EC public key\n"));
return gpg_error (GPG_ERR_CARD);
}
curve = app->app_local->keyattr[keyno].ecc.curve;
oidstr = openpgp_curve_to_oid (curve, NULL);
err = openpgp_oid_from_str (oidstr, &oid);
if (err)
return err;
oidbuf = gcry_mpi_get_opaque (oid, &n);
if (!oidbuf)
{
err = gpg_error_from_syserror ();
goto leave;
}
oid_len = (n+7)/8;
qbuf = xtrymalloc (ecc_q_len + 1);
if (!qbuf)
{
err = gpg_error_from_syserror ();
goto leave;
}
if ((app->app_local->keyattr[keyno].ecc.flags & ECC_FLAG_DJB_TWEAK))
{ /* Prepend 0x40 prefix. */
*qbuf = 0x40;
memcpy (qbuf+1, ecc_q, ecc_q_len);
ecc_q_len++;
}
else
memcpy (qbuf, ecc_q, ecc_q_len);
if (ctrl)
{
send_key_data (ctrl, "q", qbuf, ecc_q_len);
send_key_data (ctrl, "curve", oidbuf, oid_len);
}
if (keyno == 1)
{
if (ctrl)
send_key_data (ctrl, "kdf/kek", ecdh_params (curve), (size_t)4);
algo = PUBKEY_ALGO_ECDH;
}
else
{
if ((app->app_local->keyattr[keyno].ecc.flags & ECC_FLAG_DJB_TWEAK))
algo = PUBKEY_ALGO_EDDSA;
else
algo = PUBKEY_ALGO_ECDSA;
}
if (ctrl)
{
unsigned char fprbuf[20];
err = store_fpr (app, keyno, created_at, fprbuf, algo, oidbuf, oid_len,
qbuf, ecc_q_len, ecdh_params (curve), (size_t)4);
if (err)
goto leave;
send_fpr_if_not_null (ctrl, "KEY-FPR", -1, fprbuf);
}
if (!(app->app_local->keyattr[keyno].ecc.flags & ECC_FLAG_DJB_TWEAK))
format = "(public-key(ecc(curve%s)(q%b)))";
else if (keyno == 1)
format = "(public-key(ecc(curve%s)(flags djb-tweak)(q%b)))";
else
format = "(public-key(ecc(curve%s)(flags eddsa)(q%b)))";
err = gcry_sexp_build (r_sexp, NULL, format,
app->app_local->keyattr[keyno].ecc.curve,
(int)ecc_q_len, qbuf);
leave:
gcry_mpi_release (oid);
xfree (qbuf);
return err;
}
/* Parse tag-length-value data for public key in BUFFER of BUFLEN
length. Key of KEYNO in APP is updated with an S-expression of
public key. When CTRL is not NULL, fingerprint is computed with
CREATED_AT, and fingerprint is written to the card, and key data
and fingerprint are send back to the client side.
*/
static gpg_error_t
read_public_key (app_t app, ctrl_t ctrl, u32 created_at, int keyno,
const unsigned char *buffer, size_t buflen)
{
gpg_error_t err;
const unsigned char *data;
size_t datalen;
gcry_sexp_t s_pkey = NULL;
data = find_tlv (buffer, buflen, 0x7F49, &datalen);
if (!data)
{
log_error (_("response does not contain the public key data\n"));
return gpg_error (GPG_ERR_CARD);
}
if (app->app_local->keyattr[keyno].key_type == KEY_TYPE_RSA)
err = rsa_read_pubkey (app, ctrl, created_at, keyno,
data, datalen, &s_pkey);
else if (app->app_local->keyattr[keyno].key_type == KEY_TYPE_ECC)
err = ecc_read_pubkey (app, ctrl, created_at, keyno,
data, datalen, &s_pkey);
else
err = gpg_error (GPG_ERR_NOT_IMPLEMENTED);
if (!err)
{
unsigned char *keybuf;
size_t len;
len = gcry_sexp_sprint (s_pkey, GCRYSEXP_FMT_CANON, NULL, 0);
keybuf = xtrymalloc (len);
if (!data)
{
err = gpg_error_from_syserror ();
gcry_sexp_release (s_pkey);
return err;
}
gcry_sexp_sprint (s_pkey, GCRYSEXP_FMT_CANON, keybuf, len);
gcry_sexp_release (s_pkey);
app->app_local->pk[keyno].key = keybuf;
/* Decrement for trailing '\0' */
app->app_local->pk[keyno].keylen = len - 1;
}
return err;
}
/* Get the public key for KEYNO and store it as an S-expression with
the APP handle. On error that field gets cleared. If we already
know about the public key we will just return. Note that this does
not mean a key is available; this is solely indicated by the
presence of the app->app_local->pk[KEYNO].key field.
Note that GnuPG 1.x does not need this and it would be too time
consuming to send it just for the fun of it. However, given that we
use the same code in gpg 1.4, we can't use the gcry S-expression
here but need to open encode it. */
#if GNUPG_MAJOR_VERSION > 1
static gpg_error_t
get_public_key (app_t app, int keyno)
{
gpg_error_t err = 0;
unsigned char *buffer;
const unsigned char *m, *e;
size_t buflen;
size_t mlen = 0;
size_t elen = 0;
char *keybuf = NULL;
gcry_sexp_t s_pkey;
size_t len;
if (keyno < 0 || keyno > 2)
return gpg_error (GPG_ERR_INV_ID);
/* Already cached? */
if (app->app_local->pk[keyno].read_done)
return 0;
xfree (app->app_local->pk[keyno].key);
app->app_local->pk[keyno].key = NULL;
app->app_local->pk[keyno].keylen = 0;
m = e = NULL; /* (avoid cc warning) */
if (app->card_version > 0x0100)
{
int exmode, le_value;
/* We may simply read the public key out of these cards. */
if (app->app_local->cardcap.ext_lc_le
&& app->app_local->keyattr[keyno].key_type == KEY_TYPE_RSA
&& app->app_local->keyattr[keyno].rsa.n_bits > RSA_SMALL_SIZE_KEY)
{
exmode = 1; /* Use extended length. */
le_value = determine_rsa_response (app, keyno);
}
else
{
exmode = 0;
le_value = 256; /* Use legacy value. */
}
err = iso7816_read_public_key (app->slot, exmode,
(keyno == 0? "\xB6" :
keyno == 1? "\xB8" : "\xA4"),
2, le_value, &buffer, &buflen);
if (err)
{
log_error (_("reading public key failed: %s\n"), gpg_strerror (err));
goto leave;
}
err = read_public_key (app, NULL, 0U, keyno, buffer, buflen);
}
else
{
/* Due to a design problem in v1.0 cards we can't get the public
key out of these cards without doing a verify on CHV3.
Clearly that is not an option and thus we try to locate the
key using an external helper.
The helper we use here is gpg itself, which should know about
the key in any case. */
char fpr[41];
char *hexkeyid;
char *command = NULL;
FILE *fp;
int ret;
buffer = NULL; /* We don't need buffer. */
err = retrieve_fpr_from_card (app, keyno, fpr);
if (err)
{
log_error ("error while retrieving fpr from card: %s\n",
gpg_strerror (err));
goto leave;
}
hexkeyid = fpr + 24;
ret = gpgrt_asprintf
(&command, "gpg --list-keys --with-colons --with-key-data '%s'", fpr);
if (ret < 0)
{
err = gpg_error_from_syserror ();
goto leave;
}
fp = popen (command, "r");
xfree (command);
if (!fp)
{
err = gpg_error_from_syserror ();
log_error ("running gpg failed: %s\n", gpg_strerror (err));
goto leave;
}
err = retrieve_key_material (fp, hexkeyid, &m, &mlen, &e, &elen);
pclose (fp);
if (err)
{
log_error ("error while retrieving key material through pipe: %s\n",
gpg_strerror (err));
goto leave;
}
err = gcry_sexp_build (&s_pkey, NULL, "(public-key(rsa(n%b)(e%b)))",
(int)mlen, m, (int)elen, e);
if (err)
goto leave;
len = gcry_sexp_sprint (s_pkey, GCRYSEXP_FMT_CANON, NULL, 0);
keybuf = xtrymalloc (len);
if (!keybuf)
{
err = gpg_error_from_syserror ();
gcry_sexp_release (s_pkey);
goto leave;
}
gcry_sexp_sprint (s_pkey, GCRYSEXP_FMT_CANON, keybuf, len);
gcry_sexp_release (s_pkey);
app->app_local->pk[keyno].key = (unsigned char*)keybuf;
/* Decrement for trailing '\0' */
app->app_local->pk[keyno].keylen = len - 1;
}
leave:
/* Set a flag to indicate that we tried to read the key. */
app->app_local->pk[keyno].read_done = 1;
xfree (buffer);
return err;
}
#endif /* GNUPG_MAJOR_VERSION > 1 */
/* Send the KEYPAIRINFO back. KEY needs to be in the range [1,3].
This is used by the LEARN command. */
static gpg_error_t
send_keypair_info (app_t app, ctrl_t ctrl, int key)
{
int keyno = key - 1;
gpg_error_t err = 0;
/* Note that GnuPG 1.x does not need this and it would be too time
consuming to send it just for the fun of it. */
#if GNUPG_MAJOR_VERSION > 1
unsigned char grip[20];
char gripstr[41];
char idbuf[50];
const char *usage;
err = get_public_key (app, keyno);
if (err)
goto leave;
assert (keyno >= 0 && keyno <= 2);
if (!app->app_local->pk[keyno].key)
goto leave; /* No such key - ignore. */
err = keygrip_from_canon_sexp (app->app_local->pk[keyno].key,
app->app_local->pk[keyno].keylen,
grip);
if (err)
goto leave;
bin2hex (grip, 20, gripstr);
switch (keyno)
{
case 0: usage = "sc"; break;
case 1: usage = "e"; break;
case 2: usage = "sa"; break;
default: usage = ""; break;
}
sprintf (idbuf, "OPENPGP.%d", keyno+1);
send_status_info (ctrl, "KEYPAIRINFO",
gripstr, 40,
idbuf, strlen (idbuf),
usage, strlen (usage),
NULL, (size_t)0);
leave:
#endif /* GNUPG_MAJOR_VERSION > 1 */
return err;
}
/* Handle the LEARN command for OpenPGP. */
static gpg_error_t
do_learn_status (app_t app, ctrl_t ctrl, unsigned int flags)
{
(void)flags;
do_getattr (app, ctrl, "EXTCAP");
do_getattr (app, ctrl, "MANUFACTURER");
do_getattr (app, ctrl, "DISP-NAME");
do_getattr (app, ctrl, "DISP-LANG");
do_getattr (app, ctrl, "DISP-SEX");
do_getattr (app, ctrl, "PUBKEY-URL");
do_getattr (app, ctrl, "LOGIN-DATA");
do_getattr (app, ctrl, "KEY-FPR");
if (app->card_version > 0x0100)
do_getattr (app, ctrl, "KEY-TIME");
do_getattr (app, ctrl, "CA-FPR");
do_getattr (app, ctrl, "CHV-STATUS");
do_getattr (app, ctrl, "SIG-COUNTER");
if (app->app_local->extcap.kdf_do)
do_getattr (app, ctrl, "KDF");
if (app->app_local->extcap.private_dos)
{
do_getattr (app, ctrl, "PRIVATE-DO-1");
do_getattr (app, ctrl, "PRIVATE-DO-2");
if (app->did_chv2)
do_getattr (app, ctrl, "PRIVATE-DO-3");
if (app->did_chv3)
do_getattr (app, ctrl, "PRIVATE-DO-4");
}
send_keypair_info (app, ctrl, 1);
send_keypair_info (app, ctrl, 2);
send_keypair_info (app, ctrl, 3);
/* Note: We do not send the Cardholder Certificate, because that is
relatively long and for OpenPGP applications not really needed. */
return 0;
}
/* Handle the READKEY command for OpenPGP. On success a canonical
encoded S-expression with the public key will get stored at PK and
its length (for assertions) at PKLEN; the caller must release that
buffer. On error PK and PKLEN are not changed and an error code is
returned. */
static gpg_error_t
do_readkey (app_t app, int advanced, const char *keyid,
unsigned char **pk, size_t *pklen)
{
#if GNUPG_MAJOR_VERSION > 1
gpg_error_t err;
int keyno;
unsigned char *buf;
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 = get_public_key (app, keyno);
if (err)
return err;
buf = app->app_local->pk[keyno].key;
if (!buf)
return gpg_error (GPG_ERR_NO_PUBKEY);
if (advanced)
{
gcry_sexp_t s_key;
err = gcry_sexp_new (&s_key, buf, app->app_local->pk[keyno].keylen, 0);
if (err)
return err;
*pklen = gcry_sexp_sprint (s_key, GCRYSEXP_FMT_ADVANCED, NULL, 0);
*pk = xtrymalloc (*pklen);
if (!*pk)
{
err = gpg_error_from_syserror ();
*pklen = 0;
return err;
}
gcry_sexp_sprint (s_key, GCRYSEXP_FMT_ADVANCED, *pk, *pklen);
gcry_sexp_release (s_key);
/* Decrement for trailing '\0' */
*pklen = *pklen - 1;
}
else
{
*pklen = app->app_local->pk[keyno].keylen;
*pk = xtrymalloc (*pklen);
if (!*pk)
{
err = gpg_error_from_syserror ();
*pklen = 0;
return err;
}
memcpy (*pk, buf, *pklen);
}
return 0;
#else
return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
#endif
}
/* Read the standard certificate of an OpenPGP v2 card. It is
returned in a freshly allocated buffer with that address stored at
CERT and the length of the certificate stored at CERTLEN. CERTID
needs to be set to "OPENPGP.3". */
static gpg_error_t
do_readcert (app_t app, const char *certid,
unsigned char **cert, size_t *certlen)
{
#if GNUPG_MAJOR_VERSION > 1
gpg_error_t err;
unsigned char *buffer;
size_t buflen;
void *relptr;
*cert = NULL;
*certlen = 0;
if (strcmp (certid, "OPENPGP.3"))
return gpg_error (GPG_ERR_INV_ID);
if (!app->app_local->extcap.is_v2)
return gpg_error (GPG_ERR_NOT_FOUND);
relptr = get_one_do (app, 0x7F21, &buffer, &buflen, NULL);
if (!relptr)
return gpg_error (GPG_ERR_NOT_FOUND);
if (!buflen)
err = gpg_error (GPG_ERR_NOT_FOUND);
else if (!(*cert = xtrymalloc (buflen)))
err = gpg_error_from_syserror ();
else
{
memcpy (*cert, buffer, buflen);
*certlen = buflen;
err = 0;
}
xfree (relptr);
return err;
#else
return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
#endif
}
/* Decide if we use the pinpad of the reader for PIN input according
to the user preference on the card, and the capability of the
reader. This routine is only called when the reader has pinpad.
Returns 0 if we use pinpad, 1 otherwise. */
static int
check_pinpad_request (app_t app, pininfo_t *pininfo, int admin_pin)
{
if (app->app_local->pinpad.disabled)
return 1;
if (app->app_local->pinpad.specified == 0) /* No preference on card. */
{
if (pininfo->fixedlen == 0) /* Reader has varlen capability. */
return 0; /* Then, use pinpad. */
else
/*
* Reader has limited capability, and it may not match PIN of
* the card.
*/
return 1;
}
if (admin_pin)
pininfo->fixedlen = app->app_local->pinpad.fixedlen_admin;
else
pininfo->fixedlen = app->app_local->pinpad.fixedlen_user;
if (pininfo->fixedlen == 0 /* User requests disable pinpad. */
|| pininfo->fixedlen < pininfo->minlen
|| pininfo->fixedlen > pininfo->maxlen
/* Reader doesn't have the capability to input a PIN which
* length is FIXEDLEN. */)
return 1;
return 0;
}
/* Return a string with information about the card for use in a
* prompt. Returns NULL on memory failure. */
static char *
get_prompt_info (app_t app, int chvno, unsigned long sigcount, int remaining)
{
char *serial, *disp_name, *rembuf, *tmpbuf, *result;
serial = get_disp_serialno (app);
if (!serial)
return NULL;
disp_name = get_disp_name (app);
if (chvno == 1)
{
/* TRANSLATORS: Put a \x1f right before a colon. This can be
* used by pinentry to nicely align the names and values. Keep
* the %s at the start and end of the string. */
result = xtryasprintf (_("%s"
"Number\x1f: %s%%0A"
"Holder\x1f: %s%%0A"
"Counter\x1f: %lu"
"%s"),
"\x1e",
serial,
disp_name? disp_name:"",
sigcount,
"");
}
else
{
result = xtryasprintf (_("%s"
"Number\x1f: %s%%0A"
"Holder\x1f: %s"
"%s"),
"\x1e",
serial,
disp_name? disp_name:"",
"");
}
xfree (disp_name);
xfree (serial);
if (remaining != -1)
{
/* TRANSLATORS: This is the number of remaining attempts to
* enter a PIN. Use %%0A (double-percent,0A) for a linefeed. */
rembuf = xtryasprintf (_("Remaining attempts: %d"), remaining);
if (!rembuf)
{
xfree (result);
return NULL;
}
tmpbuf = strconcat (result, "%0A%0A", rembuf, NULL);
xfree (rembuf);
if (!tmpbuf)
{
xfree (result);
return NULL;
}
xfree (result);
result = tmpbuf;
}
return result;
}
/* Compute hash if KDF-DO is available. CHVNO must be 0 for reset
code, 1 or 2 for user pin and 3 for admin pin.
*/
static gpg_error_t
pin2hash_if_kdf (app_t app, int chvno, char *pinvalue, int *r_pinlen)
{
gpg_error_t err = 0;
void *relptr = NULL;
unsigned char *buffer;
size_t buflen;
if (app->app_local->extcap.kdf_do
&& (relptr = get_one_do (app, 0x00F9, &buffer, &buflen, NULL))
&& buflen >= KDF_DATA_LENGTH_MIN && (buffer[2] == 0x03))
{
const char *salt;
unsigned long s2k_count;
char dek[32];
int salt_index;
s2k_count = (((unsigned int)buffer[8] << 24)
| (buffer[9] << 16) | (buffer[10] << 8) | buffer[11]);
if (buflen == KDF_DATA_LENGTH_MIN)
salt_index =14;
else if (buflen == KDF_DATA_LENGTH_MAX)
salt_index = (chvno==3 ? 34 : (chvno==0 ? 24 : 14));
else
{
err = gpg_error (GPG_ERR_INV_DATA);
goto leave;
}
salt = &buffer[salt_index];
err = gcry_kdf_derive (pinvalue, strlen (pinvalue),
GCRY_KDF_ITERSALTED_S2K,
DIGEST_ALGO_SHA256, salt, 8,
s2k_count, sizeof (dek), dek);
if (!err)
{
/* pinvalue has a buffer of MAXLEN_PIN+1, 32 is OK. */
*r_pinlen = 32;
memcpy (pinvalue, dek, *r_pinlen);
wipememory (dek, *r_pinlen);
}
}
else
*r_pinlen = strlen (pinvalue);
leave:
xfree (relptr);
return err;
}
/* Verify a CHV either using the pinentry or if possible by
using a pinpad. PINCB and PINCB_ARG describe the usual callback
for the pinentry. CHVNO must be either 1 or 2. SIGCOUNT is only
used with CHV1. PINVALUE is the address of a pointer which will
receive a newly allocated block with the actual PIN (this is useful
in case that PIN shall be used for another verify operation). The
caller needs to free this value. If the function returns with
success and NULL is stored at PINVALUE, the caller should take this
as an indication that the pinpad has been used.
*/
static gpg_error_t
verify_a_chv (app_t app,
gpg_error_t (*pincb)(void*, const char *, char **),
void *pincb_arg, int chvno, unsigned long sigcount,
char **pinvalue, int *pinlen)
{
int rc = 0;
char *prompt_buffer = NULL;
const char *prompt;
pininfo_t pininfo;
int minlen = 6;
int remaining;
log_assert (chvno == 1 || chvno == 2);
*pinvalue = NULL;
*pinlen = 0;
remaining = get_remaining_tries (app, 0);
if (remaining == -1)
return gpg_error (GPG_ERR_CARD);
if (chvno == 2 && app->app_local->flags.def_chv2)
{
/* Special case for def_chv2 mechanism. */
if (opt.verbose)
log_info (_("using default PIN as %s\n"), "CHV2");
rc = iso7816_verify (app->slot, 0x82, "123456", 6);
if (rc)
{
/* Verification of CHV2 with the default PIN failed,
although the card pretends to have the default PIN set as
CHV2. We better disable the def_chv2 flag now. */
log_info (_("failed to use default PIN as %s: %s"
" - disabling further default use\n"),
"CHV2", gpg_strerror (rc));
app->app_local->flags.def_chv2 = 0;
}
return rc;
}
memset (&pininfo, 0, sizeof pininfo);
pininfo.fixedlen = -1;
pininfo.minlen = minlen;
{
const char *firstline = _("||Please unlock the card");
char *infoblock = get_prompt_info (app, chvno, sigcount,
remaining < 3? remaining : -1);
prompt_buffer = strconcat (firstline, "%0A%0A", infoblock, NULL);
if (prompt_buffer)
prompt = prompt_buffer;
else
prompt = firstline; /* ENOMEM fallback. */
xfree (infoblock);
}
if (!opt.disable_pinpad
&& !iso7816_check_pinpad (app->slot, ISO7816_VERIFY, &pininfo)
&& !check_pinpad_request (app, &pininfo, 0))
{
/* The reader supports the verify command through the pinpad.
Note that the pincb appends a text to the prompt telling the
user to use the pinpad. */
rc = pincb (pincb_arg, prompt, NULL);
prompt = NULL;
xfree (prompt_buffer);
prompt_buffer = NULL;
if (rc)
{
log_info (_("PIN callback returned error: %s\n"),
gpg_strerror (rc));
return rc;
}
rc = iso7816_verify_kp (app->slot, 0x80+chvno, &pininfo);
/* Dismiss the prompt. */
pincb (pincb_arg, NULL, NULL);
log_assert (!*pinvalue);
}
else
{
/* The reader has no pinpad or we don't want to use it. */
rc = pincb (pincb_arg, prompt, pinvalue);
prompt = NULL;
xfree (prompt_buffer);
prompt_buffer = NULL;
if (rc)
{
log_info (_("PIN callback returned error: %s\n"),
gpg_strerror (rc));
return rc;
}
if (strlen (*pinvalue) < minlen)
{
log_error (_("PIN for CHV%d is too short;"
" minimum length is %d\n"), chvno, minlen);
xfree (*pinvalue);
*pinvalue = NULL;
return gpg_error (GPG_ERR_BAD_PIN);
}
rc = pin2hash_if_kdf (app, chvno, *pinvalue, pinlen);
if (!rc)
rc = iso7816_verify (app->slot, 0x80+chvno, *pinvalue, *pinlen);
}
if (rc)
{
log_error (_("verify CHV%d failed: %s\n"), chvno, gpg_strerror (rc));
xfree (*pinvalue);
*pinvalue = NULL;
flush_cache_after_error (app);
}
return rc;
}
/* Verify CHV2 if required. Depending on the configuration of the
card CHV1 will also be verified. */
static gpg_error_t
verify_chv2 (app_t app,
gpg_error_t (*pincb)(void*, const char *, char **),
void *pincb_arg)
{
int rc;
char *pinvalue;
int pinlen;
if (app->did_chv2)
return 0; /* We already verified CHV2. */
rc = verify_a_chv (app, pincb, pincb_arg, 2, 0, &pinvalue, &pinlen);
if (rc)
return rc;
app->did_chv2 = 1;
if (!app->did_chv1 && !app->force_chv1 && pinvalue)
{
/* For convenience we verify CHV1 here too. We do this only if
the card is not configured to require a verification before
each CHV1 controlled operation (force_chv1) and if we are not
using the pinpad (PINVALUE == NULL). */
rc = iso7816_verify (app->slot, 0x81, pinvalue, pinlen);
if (gpg_err_code (rc) == GPG_ERR_BAD_PIN)
rc = gpg_error (GPG_ERR_PIN_NOT_SYNCED);
if (rc)
{
log_error (_("verify CHV%d failed: %s\n"), 1, gpg_strerror (rc));
flush_cache_after_error (app);
}
else
app->did_chv1 = 1;
}
xfree (pinvalue);
return rc;
}
/* Build the prompt to enter the Admin PIN. The prompt depends on the
current sdtate of the card. */
static gpg_error_t
build_enter_admin_pin_prompt (app_t app, char **r_prompt)
{
int remaining;
char *prompt;
char *infoblock;
*r_prompt = NULL;
remaining = get_remaining_tries (app, 1);
if (remaining == -1)
return gpg_error (GPG_ERR_CARD);
if (!remaining)
{
log_info (_("card is permanently locked!\n"));
return gpg_error (GPG_ERR_BAD_PIN);
}
log_info (ngettext("%d Admin PIN attempt remaining before card"
" is permanently locked\n",
"%d Admin PIN attempts remaining before card"
" is permanently locked\n",
remaining), remaining);
infoblock = get_prompt_info (app, 3, 0, remaining < 3? remaining : -1);
/* TRANSLATORS: Do not translate the "|A|" prefix but keep it at
the start of the string. Use %0A (single percent) for a linefeed. */
prompt = strconcat (_("|A|Please enter the Admin PIN"),
"%0A%0A", infoblock, NULL);
xfree (infoblock);
if (!prompt)
return gpg_error_from_syserror ();
*r_prompt = prompt;
return 0;
}
/* Verify CHV3 if required. */
static gpg_error_t
verify_chv3 (app_t app,
gpg_error_t (*pincb)(void*, const char *, char **),
void *pincb_arg)
{
int rc = 0;
#if GNUPG_MAJOR_VERSION != 1
if (!opt.allow_admin)
{
log_info (_("access to admin commands is not configured\n"));
return gpg_error (GPG_ERR_EACCES);
}
#endif
if (!app->did_chv3)
{
pininfo_t pininfo;
int minlen = 8;
char *prompt;
memset (&pininfo, 0, sizeof pininfo);
pininfo.fixedlen = -1;
pininfo.minlen = minlen;
rc = build_enter_admin_pin_prompt (app, &prompt);
if (rc)
return rc;
if (!opt.disable_pinpad
&& !iso7816_check_pinpad (app->slot, ISO7816_VERIFY, &pininfo)
&& !check_pinpad_request (app, &pininfo, 1))
{
/* The reader supports the verify command through the pinpad. */
rc = pincb (pincb_arg, prompt, NULL);
xfree (prompt);
prompt = NULL;
if (rc)
{
log_info (_("PIN callback returned error: %s\n"),
gpg_strerror (rc));
return rc;
}
rc = iso7816_verify_kp (app->slot, 0x83, &pininfo);
/* Dismiss the prompt. */
pincb (pincb_arg, NULL, NULL);
}
else
{
char *pinvalue;
int pinlen;
rc = pincb (pincb_arg, prompt, &pinvalue);
xfree (prompt);
prompt = NULL;
if (rc)
{
log_info (_("PIN callback returned error: %s\n"),
gpg_strerror (rc));
return rc;
}
if (strlen (pinvalue) < minlen)
{
log_error (_("PIN for CHV%d is too short;"
" minimum length is %d\n"), 3, minlen);
xfree (pinvalue);
return gpg_error (GPG_ERR_BAD_PIN);
}
rc = pin2hash_if_kdf (app, 3, pinvalue, &pinlen);
if (!rc)
rc = iso7816_verify (app->slot, 0x83, pinvalue, pinlen);
xfree (pinvalue);
}
if (rc)
{
log_error (_("verify CHV%d failed: %s\n"), 3, gpg_strerror (rc));
flush_cache_after_error (app);
return rc;
}
app->did_chv3 = 1;
}
return rc;
}
/* Handle the SETATTR operation. All arguments are already basically
checked. */
static gpg_error_t
do_setattr (app_t app, const char *name,
gpg_error_t (*pincb)(void*, const char *, char **),
void *pincb_arg,
const unsigned char *value, size_t valuelen)
{
gpg_error_t rc;
int idx;
static struct {
const char *name;
int tag;
int flush_tag; /* The tag which needs to be flushed or 0. */
int need_chv;
int special;
unsigned int need_v2:1;
} table[] = {
{ "DISP-NAME", 0x005B, 0, 3 },
{ "LOGIN-DATA", 0x005E, 0, 3, 2 },
{ "DISP-LANG", 0x5F2D, 0, 3 },
{ "DISP-SEX", 0x5F35, 0, 3 },
{ "PUBKEY-URL", 0x5F50, 0, 3 },
{ "CHV-STATUS-1", 0x00C4, 0, 3, 1 },
{ "CA-FPR-1", 0x00CA, 0x00C6, 3 },
{ "CA-FPR-2", 0x00CB, 0x00C6, 3 },
{ "CA-FPR-3", 0x00CC, 0x00C6, 3 },
{ "PRIVATE-DO-1", 0x0101, 0, 2 },
{ "PRIVATE-DO-2", 0x0102, 0, 3 },
{ "PRIVATE-DO-3", 0x0103, 0, 2 },
{ "PRIVATE-DO-4", 0x0104, 0, 3 },
{ "CERT-3", 0x7F21, 0, 3, 0, 1 },
{ "SM-KEY-ENC", 0x00D1, 0, 3, 0, 1 },
{ "SM-KEY-MAC", 0x00D2, 0, 3, 0, 1 },
{ "KEY-ATTR", 0, 0, 0, 3, 1 },
{ "AESKEY", 0x00D5, 0, 3, 0, 1 },
{ "KDF", 0x00F9, 0, 3, 4, 1 },
{ NULL, 0 }
};
int exmode;
for (idx=0; table[idx].name && strcmp (table[idx].name, name); idx++)
;
if (!table[idx].name)
return gpg_error (GPG_ERR_INV_NAME);
if (table[idx].need_v2 && !app->app_local->extcap.is_v2)
return gpg_error (GPG_ERR_NOT_SUPPORTED); /* Not yet supported. */
if (table[idx].special == 3)
return change_keyattr_from_string (app, pincb, pincb_arg, value, valuelen);
switch (table[idx].need_chv)
{
case 2:
rc = verify_chv2 (app, pincb, pincb_arg);
break;
case 3:
rc = verify_chv3 (app, pincb, pincb_arg);
break;
default:
rc = 0;
}
if (rc)
return rc;
/* Flush the cache before writing it, so that the next get operation
will reread the data from the card and thus get synced in case of
errors (e.g. data truncated by the card). */
flush_cache_item (app, table[idx].flush_tag? table[idx].flush_tag
/* */ : table[idx].tag);
if (app->app_local->cardcap.ext_lc_le && valuelen > 254)
exmode = 1; /* Use extended length w/o a limit. */
else if (app->app_local->cardcap.cmd_chaining && valuelen > 254)
exmode = -254; /* Command chaining with max. 254 bytes. */
else
exmode = 0;
rc = iso7816_put_data (app->slot, exmode, table[idx].tag, value, valuelen);
if (rc)
log_error ("failed to set '%s': %s\n", table[idx].name, gpg_strerror (rc));
if (table[idx].special == 1)
app->force_chv1 = (valuelen && *value == 0);
else if (table[idx].special == 2)
parse_login_data (app);
else if (table[idx].special == 4)
{
app->did_chv1 = 0;
app->did_chv2 = 0;
app->did_chv3 = 0;
if ((valuelen == KDF_DATA_LENGTH_MIN || valuelen == KDF_DATA_LENGTH_MAX)
&& (value[2] == 0x03))
app->app_local->pinpad.disabled = 1;
else
app->app_local->pinpad.disabled = 0;
}
return rc;
}
/* Handle the WRITECERT command for OpenPGP. This rites the standard
certifciate to the card; CERTID needs to be set to "OPENPGP.3".
PINCB and PINCB_ARG are the usual arguments for the pinentry
callback. */
static gpg_error_t
do_writecert (app_t app, ctrl_t ctrl,
const char *certidstr,
gpg_error_t (*pincb)(void*, const char *, char **),
void *pincb_arg,
const unsigned char *certdata, size_t certdatalen)
{
(void)ctrl;
#if GNUPG_MAJOR_VERSION > 1
if (strcmp (certidstr, "OPENPGP.3"))
return gpg_error (GPG_ERR_INV_ID);
if (!certdata || !certdatalen)
return gpg_error (GPG_ERR_INV_ARG);
if (!app->app_local->extcap.is_v2)
return gpg_error (GPG_ERR_NOT_SUPPORTED);
if (certdatalen > app->app_local->extcap.max_certlen_3)
return gpg_error (GPG_ERR_TOO_LARGE);
return do_setattr (app, "CERT-3", pincb, pincb_arg, certdata, certdatalen);
#else
return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
#endif
}
/* Handle the PASSWD command. The following combinations are
possible:
Flags CHVNO Vers. Description
RESET 1 1 Verify CHV3 and set a new CHV1 and CHV2
RESET 1 2 Verify PW3 and set a new PW1.
RESET 2 1 Verify CHV3 and set a new CHV1 and CHV2.
RESET 2 2 Verify PW3 and set a new Reset Code.
RESET 3 any Returns GPG_ERR_INV_ID.
- 1 1 Verify CHV2 and set a new CHV1 and CHV2.
- 1 2 Verify PW1 and set a new PW1.
- 2 1 Verify CHV2 and set a new CHV1 and CHV2.
- 2 2 Verify Reset Code and set a new PW1.
- 3 any Verify CHV3/PW3 and set a new CHV3/PW3.
The CHVNO can be prefixed with "OPENPGP.".
*/
static gpg_error_t
do_change_pin (app_t app, ctrl_t ctrl, const char *chvnostr,
unsigned int flags,
gpg_error_t (*pincb)(void*, const char *, char **),
void *pincb_arg)
{
int rc = 0;
int chvno;
char *resetcode = NULL;
char *oldpinvalue = NULL;
char *pinvalue = NULL;
int reset_mode = !!(flags & APP_CHANGE_FLAG_RESET);
int set_resetcode = 0;
pininfo_t pininfo;
int use_pinpad = 0;
int minlen = 6;
int pinlen0 = 0;
int pinlen = 0;
(void)ctrl;
if (digitp (chvnostr))
chvno = atoi (chvnostr);
else if (!ascii_strcasecmp (chvnostr, "OPENPGP.1"))
chvno = 1;
else if (!ascii_strcasecmp (chvnostr, "OPENPGP.2"))
chvno = 2;
else if (!ascii_strcasecmp (chvnostr, "OPENPGP.3"))
chvno = 3;
else
return gpg_error (GPG_ERR_INV_ID);
memset (&pininfo, 0, sizeof pininfo);
pininfo.fixedlen = -1;
pininfo.minlen = minlen;
if ((flags & APP_CHANGE_FLAG_CLEAR))
return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION);
if (reset_mode && chvno == 3)
{
rc = gpg_error (GPG_ERR_INV_ID);
goto leave;
}
if (!app->app_local->extcap.is_v2)
{
/* Version 1 cards. */
if (reset_mode || chvno == 3)
{
/* We always require that the PIN is entered. */
app->did_chv3 = 0;
rc = verify_chv3 (app, pincb, pincb_arg);
if (rc)
goto leave;
}
else if (chvno == 1 || chvno == 2)
{
/* On a v1.x card CHV1 and CVH2 should always have the same
value, thus we enforce it here. */
int save_force = app->force_chv1;
app->force_chv1 = 0;
app->did_chv1 = 0;
app->did_chv2 = 0;
rc = verify_chv2 (app, pincb, pincb_arg);
app->force_chv1 = save_force;
if (rc)
goto leave;
}
else
{
rc = gpg_error (GPG_ERR_INV_ID);
goto leave;
}
}
else
{
/* Version 2 cards. */
if (!opt.disable_pinpad
&& !iso7816_check_pinpad (app->slot,
ISO7816_CHANGE_REFERENCE_DATA, &pininfo)
&& !check_pinpad_request (app, &pininfo, chvno == 3))
use_pinpad = 1;
if (reset_mode)
{
/* To reset a PIN the Admin PIN is required. */
use_pinpad = 0;
app->did_chv3 = 0;
rc = verify_chv3 (app, pincb, pincb_arg);
if (rc)
goto leave;
if (chvno == 2)
set_resetcode = 1;
}
else if (chvno == 1 || chvno == 3)
{
if (!use_pinpad)
{
char *promptbuf = NULL;
const char *prompt;
if (chvno == 3)
{
minlen = 8;
rc = build_enter_admin_pin_prompt (app, &promptbuf);
if (rc)
goto leave;
prompt = promptbuf;
}
else
prompt = _("||Please enter the PIN");
rc = pincb (pincb_arg, prompt, &oldpinvalue);
xfree (promptbuf);
promptbuf = NULL;
if (rc)
{
log_info (_("PIN callback returned error: %s\n"),
gpg_strerror (rc));
goto leave;
}
if (strlen (oldpinvalue) < minlen)
{
log_info (_("PIN for CHV%d is too short;"
" minimum length is %d\n"), chvno, minlen);
rc = gpg_error (GPG_ERR_BAD_PIN);
goto leave;
}
}
}
else if (chvno == 2)
{
/* There is no PW2 for v2 cards. We use this condition to
allow a PW reset using the Reset Code. */
void *relptr;
unsigned char *value;
size_t valuelen;
int remaining;
use_pinpad = 0;
minlen = 8;
relptr = get_one_do (app, 0x00C4, &value, &valuelen, NULL);
if (!relptr || valuelen < 7)
{
log_error (_("error retrieving CHV status from card\n"));
xfree (relptr);
rc = gpg_error (GPG_ERR_CARD);
goto leave;
}
remaining = value[5];
xfree (relptr);
if (!remaining)
{
log_error (_("Reset Code not or not anymore available\n"));
rc = gpg_error (GPG_ERR_BAD_PIN);
goto leave;
}
rc = pincb (pincb_arg,
_("||Please enter the Reset Code for the card"),
&resetcode);
if (rc)
{
log_info (_("PIN callback returned error: %s\n"),
gpg_strerror (rc));
goto leave;
}
if (strlen (resetcode) < minlen)
{
log_info (_("Reset Code is too short; minimum length is %d\n"),
minlen);
rc = gpg_error (GPG_ERR_BAD_PIN);
goto leave;
}
}
else
{
rc = gpg_error (GPG_ERR_INV_ID);
goto leave;
}
}
if (chvno == 3)
app->did_chv3 = 0;
else
app->did_chv1 = app->did_chv2 = 0;
if (!use_pinpad)
{
/* TRANSLATORS: Do not translate the "|*|" prefixes but
keep it at the start of the string. We need this elsewhere
to get some infos on the string. */
rc = pincb (pincb_arg, set_resetcode? _("|RN|New Reset Code") :
chvno == 3? _("|AN|New Admin PIN") : _("|N|New PIN"),
&pinvalue);
if (rc || pinvalue == NULL)
{
log_error (_("error getting new PIN: %s\n"), gpg_strerror (rc));
goto leave;
}
}
if (resetcode)
{
char *buffer;
buffer = xtrymalloc (strlen (resetcode) + strlen (pinvalue) + 1);
if (!buffer)
rc = gpg_error_from_syserror ();
else
{
strcpy (buffer, resetcode);
rc = pin2hash_if_kdf (app, 0, buffer, &pinlen0);
if (!rc)
{
strcpy (buffer+pinlen0, pinvalue);
rc = pin2hash_if_kdf (app, 0, buffer+pinlen0, &pinlen);
}
if (!rc)
rc = iso7816_reset_retry_counter_with_rc (app->slot, 0x81,
buffer, pinlen0+pinlen);
wipememory (buffer, pinlen0 + pinlen);
xfree (buffer);
}
}
else if (set_resetcode)
{
if (strlen (pinvalue) < 8)
{
log_error (_("Reset Code is too short; minimum length is %d\n"), 8);
rc = gpg_error (GPG_ERR_BAD_PIN);
}
else
{
rc = pin2hash_if_kdf (app, 0, pinvalue, &pinlen);
if (!rc)
rc = iso7816_put_data (app->slot, 0, 0xD3, pinvalue, pinlen);
}
}
else if (reset_mode)
{
rc = pin2hash_if_kdf (app, 1, pinvalue, &pinlen);
if (!rc)
rc = iso7816_reset_retry_counter (app->slot, 0x81, pinvalue, pinlen);
if (!rc && !app->app_local->extcap.is_v2)
rc = iso7816_reset_retry_counter (app->slot, 0x82, pinvalue, pinlen);
}
else if (!app->app_local->extcap.is_v2)
{
/* Version 1 cards. */
if (chvno == 1 || chvno == 2)
{
rc = iso7816_change_reference_data (app->slot, 0x81, NULL, 0,
pinvalue, strlen (pinvalue));
if (!rc)
rc = iso7816_change_reference_data (app->slot, 0x82, NULL, 0,
pinvalue, strlen (pinvalue));
}
else /* CHVNO == 3 */
{
rc = iso7816_change_reference_data (app->slot, 0x80 + chvno, NULL, 0,
pinvalue, strlen (pinvalue));
}
}
else
{
/* Version 2 cards. */
assert (chvno == 1 || chvno == 3);
if (use_pinpad)
{
rc = pincb (pincb_arg,
chvno == 3 ?
_("||Please enter the Admin PIN and New Admin PIN") :
_("||Please enter the PIN and New PIN"), NULL);
if (rc)
{
log_info (_("PIN callback returned error: %s\n"),
gpg_strerror (rc));
goto leave;
}
rc = iso7816_change_reference_data_kp (app->slot, 0x80 + chvno, 0,
&pininfo);
pincb (pincb_arg, NULL, NULL); /* Dismiss the prompt. */
}
else
{
rc = pin2hash_if_kdf (app, chvno, oldpinvalue, &pinlen0);
if (!rc)
rc = pin2hash_if_kdf (app, chvno, pinvalue, &pinlen);
if (!rc)
rc = iso7816_change_reference_data (app->slot, 0x80 + chvno,
oldpinvalue, pinlen0,
pinvalue, pinlen);
}
}
if (pinvalue)
{
wipememory (pinvalue, pinlen);
xfree (pinvalue);
}
if (rc)
flush_cache_after_error (app);
leave:
if (resetcode)
{
wipememory (resetcode, strlen (resetcode));
xfree (resetcode);
}
if (oldpinvalue)
{
wipememory (oldpinvalue, pinlen0);
xfree (oldpinvalue);
}
return rc;
}
/* Check whether a key already exists. KEYIDX is the index of the key
(0..2). If FORCE is TRUE a diagnositic will be printed but no
error returned if the key already exists. The flag GENERATING is
only used to print correct messages. */
static gpg_error_t
does_key_exist (app_t app, int keyidx, int generating, 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, 0, 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 if (generating)
log_info (_("generating new key\n"));
else
log_info (_("writing new key\n"));
return 0;
}
/* Create a TLV tag and value and store it at BUFFER. Return the length
of tag and length. A LENGTH greater than 65535 is truncated. */
static size_t
add_tlv (unsigned char *buffer, unsigned int tag, size_t length)
{
unsigned char *p = buffer;
assert (tag <= 0xffff);
if ( tag > 0xff )
*p++ = tag >> 8;
*p++ = tag;
if (length < 128)
*p++ = length;
else if (length < 256)
{
*p++ = 0x81;
*p++ = length;
}
else
{
if (length > 0xffff)
length = 0xffff;
*p++ = 0x82;
*p++ = length >> 8;
*p++ = length;
}
return p - buffer;
}
static gpg_error_t
build_privkey_template (app_t app, int keyno,
const unsigned char *rsa_n, size_t rsa_n_len,
const unsigned char *rsa_e, size_t rsa_e_len,
const unsigned char *rsa_p, size_t rsa_p_len,
const unsigned char *rsa_q, size_t rsa_q_len,
const unsigned char *rsa_u, size_t rsa_u_len,
const unsigned char *rsa_dp, size_t rsa_dp_len,
const unsigned char *rsa_dq, size_t rsa_dq_len,
unsigned char **result, size_t *resultlen)
{
size_t rsa_e_reqlen;
unsigned char privkey[7*(1+3+3)];
size_t privkey_len;
unsigned char exthdr[2+2+3];
size_t exthdr_len;
unsigned char suffix[2+3];
size_t suffix_len;
unsigned char *tp;
size_t datalen;
unsigned char *template;
size_t template_size;
*result = NULL;
*resultlen = 0;
switch (app->app_local->keyattr[keyno].rsa.format)
{
case RSA_STD:
case RSA_STD_N:
case RSA_CRT:
case RSA_CRT_N:
break;
default:
return gpg_error (GPG_ERR_INV_VALUE);
}
/* Get the required length for E. Rounded up to the nearest byte */
rsa_e_reqlen = (app->app_local->keyattr[keyno].rsa.e_bits + 7) / 8;
assert (rsa_e_len <= rsa_e_reqlen);
/* Build the 7f48 cardholder private key template. */
datalen = 0;
tp = privkey;
tp += add_tlv (tp, 0x91, rsa_e_reqlen);
datalen += rsa_e_reqlen;
tp += add_tlv (tp, 0x92, rsa_p_len);
datalen += rsa_p_len;
tp += add_tlv (tp, 0x93, rsa_q_len);
datalen += rsa_q_len;
if (app->app_local->keyattr[keyno].rsa.format == RSA_CRT
|| app->app_local->keyattr[keyno].rsa.format == RSA_CRT_N)
{
tp += add_tlv (tp, 0x94, rsa_u_len);
datalen += rsa_u_len;
tp += add_tlv (tp, 0x95, rsa_dp_len);
datalen += rsa_dp_len;
tp += add_tlv (tp, 0x96, rsa_dq_len);
datalen += rsa_dq_len;
}
if (app->app_local->keyattr[keyno].rsa.format == RSA_STD_N
|| app->app_local->keyattr[keyno].rsa.format == RSA_CRT_N)
{
tp += add_tlv (tp, 0x97, rsa_n_len);
datalen += rsa_n_len;
}
privkey_len = tp - privkey;
/* Build the extended header list without the private key template. */
tp = exthdr;
*tp++ = keyno ==0 ? 0xb6 : keyno == 1? 0xb8 : 0xa4;
*tp++ = 0;
tp += add_tlv (tp, 0x7f48, privkey_len);
exthdr_len = tp - exthdr;
/* Build the 5f48 suffix of the data. */
tp = suffix;
tp += add_tlv (tp, 0x5f48, datalen);
suffix_len = tp - suffix;
/* Now concatenate everything. */
template_size = (1 + 3 /* 0x4d and len. */
+ exthdr_len
+ privkey_len
+ suffix_len
+ datalen);
tp = template = xtrymalloc_secure (template_size);
if (!template)
return gpg_error_from_syserror ();
tp += add_tlv (tp, 0x4d, exthdr_len + privkey_len + suffix_len + datalen);
memcpy (tp, exthdr, exthdr_len);
tp += exthdr_len;
memcpy (tp, privkey, privkey_len);
tp += privkey_len;
memcpy (tp, suffix, suffix_len);
tp += suffix_len;
memcpy (tp, rsa_e, rsa_e_len);
if (rsa_e_len < rsa_e_reqlen)
{
/* Right justify E. */
memmove (tp + rsa_e_reqlen - rsa_e_len, tp, rsa_e_len);
memset (tp, 0, rsa_e_reqlen - rsa_e_len);
}
tp += rsa_e_reqlen;
memcpy (tp, rsa_p, rsa_p_len);
tp += rsa_p_len;
memcpy (tp, rsa_q, rsa_q_len);
tp += rsa_q_len;
if (app->app_local->keyattr[keyno].rsa.format == RSA_CRT
|| app->app_local->keyattr[keyno].rsa.format == RSA_CRT_N)
{
memcpy (tp, rsa_u, rsa_u_len);
tp += rsa_u_len;
memcpy (tp, rsa_dp, rsa_dp_len);
tp += rsa_dp_len;
memcpy (tp, rsa_dq, rsa_dq_len);
tp += rsa_dq_len;
}
if (app->app_local->keyattr[keyno].rsa.format == RSA_STD_N
|| app->app_local->keyattr[keyno].rsa.format == RSA_CRT_N)
{
memcpy (tp, rsa_n, rsa_n_len);
tp += rsa_n_len;
}
/* Sanity check. We don't know the exact length because we
allocated 3 bytes for the first length header. */
assert (tp - template <= template_size);
*result = template;
*resultlen = tp - template;
return 0;
}
static gpg_error_t
build_ecc_privkey_template (app_t app, int keyno,
const unsigned char *ecc_d, size_t ecc_d_len,
const unsigned char *ecc_q, size_t ecc_q_len,
unsigned char **result, size_t *resultlen)
{
unsigned char privkey[2+2];
size_t privkey_len;
unsigned char exthdr[2+2+1];
size_t exthdr_len;
unsigned char suffix[2+1];
size_t suffix_len;
unsigned char *tp;
size_t datalen;
unsigned char *template;
size_t template_size;
int pubkey_required;
pubkey_required = !!(app->app_local->keyattr[keyno].ecc.flags
& ECC_FLAG_PUBKEY);
*result = NULL;
*resultlen = 0;
/* Build the 7f48 cardholder private key template. */
datalen = 0;
tp = privkey;
tp += add_tlv (tp, 0x92, ecc_d_len);
datalen += ecc_d_len;
if (pubkey_required)
{
tp += add_tlv (tp, 0x99, ecc_q_len);
datalen += ecc_q_len;
}
privkey_len = tp - privkey;
/* Build the extended header list without the private key template. */
tp = exthdr;
*tp++ = keyno ==0 ? 0xb6 : keyno == 1? 0xb8 : 0xa4;
*tp++ = 0;
tp += add_tlv (tp, 0x7f48, privkey_len);
exthdr_len = tp - exthdr;
/* Build the 5f48 suffix of the data. */
tp = suffix;
tp += add_tlv (tp, 0x5f48, datalen);
suffix_len = tp - suffix;
/* Now concatenate everything. */
template_size = (1 + 1 /* 0x4d and len. */
+ exthdr_len
+ privkey_len
+ suffix_len
+ datalen);
if (exthdr_len + privkey_len + suffix_len + datalen >= 128)
template_size++;
tp = template = xtrymalloc_secure (template_size);
if (!template)
return gpg_error_from_syserror ();
tp += add_tlv (tp, 0x4d, exthdr_len + privkey_len + suffix_len + datalen);
memcpy (tp, exthdr, exthdr_len);
tp += exthdr_len;
memcpy (tp, privkey, privkey_len);
tp += privkey_len;
memcpy (tp, suffix, suffix_len);
tp += suffix_len;
memcpy (tp, ecc_d, ecc_d_len);
tp += ecc_d_len;
if (pubkey_required)
{
memcpy (tp, ecc_q, ecc_q_len);
tp += ecc_q_len;
}
assert (tp - template == template_size);
*result = template;
*resultlen = tp - template;
return 0;
}
/* Helper for do_writekley to change the size of a key. Not ethat
this deletes the entire key without asking. */
static gpg_error_t
change_keyattr (app_t app, int keyno, const unsigned char *buf, size_t buflen,
gpg_error_t (*pincb)(void*, const char *, char **),
void *pincb_arg)
{
gpg_error_t err;
assert (keyno >=0 && keyno <= 2);
/* Prepare for storing the key. */
err = verify_chv3 (app, pincb, pincb_arg);
if (err)
return err;
/* Change the attribute. */
err = iso7816_put_data (app->slot, 0, 0xC1+keyno, buf, buflen);
if (err)
log_error ("error changing key attribute (key=%d)\n", keyno+1);
else
log_info ("key attribute changed (key=%d)\n", keyno+1);
flush_cache (app);
parse_algorithm_attribute (app, keyno);
app->did_chv1 = 0;
app->did_chv2 = 0;
app->did_chv3 = 0;
return err;
}
static gpg_error_t
change_rsa_keyattr (app_t app, int keyno, unsigned int nbits,
gpg_error_t (*pincb)(void*, const char *, char **),
void *pincb_arg)
{
gpg_error_t err = 0;
unsigned char *buf;
size_t buflen;
void *relptr;
/* Read the current attributes into a buffer. */
relptr = get_one_do (app, 0xC1+keyno, &buf, &buflen, NULL);
if (!relptr)
err = gpg_error (GPG_ERR_CARD);
else if (buflen < 6)
{
/* Attributes too short. */
xfree (relptr);
err = gpg_error (GPG_ERR_CARD);
}
else
{
/* If key attribute was RSA, we only change n_bits and don't
touch anything else. Before we do so, we round up NBITS to a
sensible way in the same way as gpg's key generation does it.
This may help to sort out problems with a few bits too short
keys. */
nbits = ((nbits + 31) / 32) * 32;
buf[1] = (nbits >> 8);
buf[2] = nbits;
/* If it was not RSA, we need to fill other parts. */
if (buf[0] != PUBKEY_ALGO_RSA)
{
buf[0] = PUBKEY_ALGO_RSA;
buf[3] = 0;
buf[4] = 32;
buf[5] = 0;
buflen = 6;
}
err = change_keyattr (app, keyno, buf, buflen, pincb, pincb_arg);
xfree (relptr);
}
return err;
}
/* Helper to process an setattr command for name KEY-ATTR.
In (VALUE,VALUELEN), it expects following string:
RSA: "--force rsa"
ECC: "--force "
*/
static gpg_error_t
change_keyattr_from_string (app_t app,
gpg_error_t (*pincb)(void*, const char *, char **),
void *pincb_arg,
const void *value, size_t valuelen)
{
gpg_error_t err = 0;
char *string;
int key, keyno, algo;
int n = 0;
/* VALUE is expected to be a string but not guaranteed to be
terminated. Thus copy it to an allocated buffer first. */
string = xtrymalloc (valuelen+1);
if (!string)
return gpg_error_from_syserror ();
memcpy (string, value, valuelen);
string[valuelen] = 0;
/* Because this function deletes the key we require the string
"--force" in the data to make clear that something serious might
happen. */
sscanf (string, "--force %d %d %n", &key, &algo, &n);
if (n < 12)
{
err = gpg_error (GPG_ERR_INV_DATA);
goto leave;
}
keyno = key - 1;
if (keyno < 0 || keyno > 2)
err = gpg_error (GPG_ERR_INV_ID);
else if (algo == PUBKEY_ALGO_RSA)
{
unsigned int nbits;
errno = 0;
nbits = strtoul (string+n+3, NULL, 10);
if (errno)
err = gpg_error (GPG_ERR_INV_DATA);
else if (nbits < 1024)
err = gpg_error (GPG_ERR_TOO_SHORT);
else if (nbits > 4096)
err = gpg_error (GPG_ERR_TOO_LARGE);
else
err = change_rsa_keyattr (app, keyno, nbits, pincb, pincb_arg);
}
else if (algo == PUBKEY_ALGO_ECDH || algo == PUBKEY_ALGO_ECDSA
|| algo == PUBKEY_ALGO_EDDSA)
{
const char *oidstr;
gcry_mpi_t oid;
const unsigned char *oidbuf;
size_t oid_len;
oidstr = openpgp_curve_to_oid (string+n, NULL);
if (!oidstr)
{
err = gpg_error (GPG_ERR_INV_DATA);
goto leave;
}
err = openpgp_oid_from_str (oidstr, &oid);
if (err)
goto leave;
oidbuf = gcry_mpi_get_opaque (oid, &n);
oid_len = (n+7)/8;
/* We have enough room at STRING. */
string[0] = algo;
memcpy (string+1, oidbuf+1, oid_len-1);
err = change_keyattr (app, keyno, string, oid_len, pincb, pincb_arg);
gcry_mpi_release (oid);
}
else
err = gpg_error (GPG_ERR_PUBKEY_ALGO);
leave:
xfree (string);
return err;
}
static gpg_error_t
rsa_writekey (app_t app, gpg_error_t (*pincb)(void*, const char *, char **),
void *pincb_arg, int keyno,
const unsigned char *buf, size_t buflen, int depth)
{
gpg_error_t err;
const unsigned char *tok;
size_t toklen;
int 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 int maxbits;
unsigned char *template = NULL;
unsigned char *tp;
size_t template_len;
unsigned char fprbuf[20];
u32 created_at = 0;
if (app->app_local->keyattr[keyno].key_type != KEY_TYPE_RSA)
{
log_error (_("unsupported algorithm: %s"), "RSA");
err = gpg_error (GPG_ERR_INV_VALUE);
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;
}
maxbits = app->app_local->keyattr[keyno].rsa.n_bits;
nbits = rsa_n? count_bits (rsa_n, rsa_n_len) : 0;
if (opt.verbose)
log_info ("RSA modulus size is %u bits\n", nbits);
if (nbits && nbits != maxbits
&& app->app_local->extcap.algo_attr_change)
{
/* Try to switch the key to a new length. */
err = change_rsa_keyattr (app, keyno, nbits, pincb, pincb_arg);
if (!err)
maxbits = app->app_local->keyattr[keyno].rsa.n_bits;
}
if (nbits != maxbits)
{
log_error (_("RSA modulus missing or not of size %d bits\n"),
(int)maxbits);
err = gpg_error (GPG_ERR_BAD_SECKEY);
goto leave;
}
maxbits = app->app_local->keyattr[keyno].rsa.e_bits;
if (maxbits > 32 && !app->app_local->extcap.is_v2)
maxbits = 32; /* Our code for v1 does only support 32 bits. */
nbits = rsa_e? count_bits (rsa_e, rsa_e_len) : 0;
if (nbits < 2 || nbits > maxbits)
{
log_error (_("RSA public exponent missing or larger than %d bits\n"),
(int)maxbits);
err = gpg_error (GPG_ERR_BAD_SECKEY);
goto leave;
}
maxbits = app->app_local->keyattr[keyno].rsa.n_bits/2;
nbits = rsa_p? count_bits (rsa_p, rsa_p_len) : 0;
if (nbits != maxbits)
{
log_error (_("RSA prime %s missing or not of size %d bits\n"),
"P", (int)maxbits);
err = gpg_error (GPG_ERR_BAD_SECKEY);
goto leave;
}
nbits = rsa_q? count_bits (rsa_q, rsa_q_len) : 0;
if (nbits != maxbits)
{
log_error (_("RSA prime %s missing or not of size %d bits\n"),
"Q", (int)maxbits);
err = gpg_error (GPG_ERR_BAD_SECKEY);
goto leave;
}
/* 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;
if (app->app_local->extcap.is_v2)
{
unsigned char *rsa_u, *rsa_dp, *rsa_dq;
size_t rsa_u_len, rsa_dp_len, rsa_dq_len;
gcry_mpi_t mpi_e, mpi_p, mpi_q;
gcry_mpi_t mpi_u = gcry_mpi_snew (0);
gcry_mpi_t mpi_dp = gcry_mpi_snew (0);
gcry_mpi_t mpi_dq = gcry_mpi_snew (0);
gcry_mpi_t mpi_tmp = gcry_mpi_snew (0);
int exmode;
/* Calculate the u, dp and dq components needed by RSA_CRT cards */
gcry_mpi_scan (&mpi_e, GCRYMPI_FMT_USG, rsa_e, rsa_e_len, NULL);
gcry_mpi_scan (&mpi_p, GCRYMPI_FMT_USG, rsa_p, rsa_p_len, NULL);
gcry_mpi_scan (&mpi_q, GCRYMPI_FMT_USG, rsa_q, rsa_q_len, NULL);
gcry_mpi_invm (mpi_u, mpi_q, mpi_p);
gcry_mpi_sub_ui (mpi_tmp, mpi_p, 1);
gcry_mpi_invm (mpi_dp, mpi_e, mpi_tmp);
gcry_mpi_sub_ui (mpi_tmp, mpi_q, 1);
gcry_mpi_invm (mpi_dq, mpi_e, mpi_tmp);
gcry_mpi_aprint (GCRYMPI_FMT_USG, &rsa_u, &rsa_u_len, mpi_u);
gcry_mpi_aprint (GCRYMPI_FMT_USG, &rsa_dp, &rsa_dp_len, mpi_dp);
gcry_mpi_aprint (GCRYMPI_FMT_USG, &rsa_dq, &rsa_dq_len, mpi_dq);
gcry_mpi_release (mpi_e);
gcry_mpi_release (mpi_p);
gcry_mpi_release (mpi_q);
gcry_mpi_release (mpi_u);
gcry_mpi_release (mpi_dp);
gcry_mpi_release (mpi_dq);
gcry_mpi_release (mpi_tmp);
/* Build the private key template as described in section 4.3.3.7 of
the OpenPGP card specs version 2.0. */
err = build_privkey_template (app, keyno,
rsa_n, rsa_n_len,
rsa_e, rsa_e_len,
rsa_p, rsa_p_len,
rsa_q, rsa_q_len,
rsa_u, rsa_u_len,
rsa_dp, rsa_dp_len,
rsa_dq, rsa_dq_len,
&template, &template_len);
xfree(rsa_u);
xfree(rsa_dp);
xfree(rsa_dq);
if (err)
goto leave;
/* Prepare for storing the key. */
err = verify_chv3 (app, pincb, pincb_arg);
if (err)
goto leave;
/* Store the key. */
if (app->app_local->cardcap.ext_lc_le && template_len > 254)
exmode = 1; /* Use extended length w/o a limit. */
else if (app->app_local->cardcap.cmd_chaining && template_len > 254)
exmode = -254;
else
exmode = 0;
err = iso7816_put_data_odd (app->slot, exmode, 0x3fff,
template, template_len);
}
else
{
/* Build the private key template as described in section 4.3.3.6 of
the OpenPGP card specs version 1.1:
0xC0 public exponent
0xC1 prime p
0xC2 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_syserror ();
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, 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);
/* 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, 0,
(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, keyno, created_at, fprbuf, PUBKEY_ALGO_RSA,
rsa_n, rsa_n_len, rsa_e, rsa_e_len);
if (err)
goto leave;
leave:
xfree (template);
return err;
}
static gpg_error_t
ecc_writekey (app_t app, gpg_error_t (*pincb)(void*, const char *, char **),
void *pincb_arg, int keyno,
const unsigned char *buf, size_t buflen, int depth)
{
gpg_error_t err;
const unsigned char *tok;
size_t toklen;
int last_depth1, last_depth2;
const unsigned char *ecc_q = NULL;
const unsigned char *ecc_d = NULL;
size_t ecc_q_len, ecc_d_len;
const char *curve = NULL;
u32 created_at = 0;
const char *oidstr;
int flag_djb_tweak = 0;
int algo;
gcry_mpi_t oid = NULL;
const unsigned char *oidbuf;
unsigned int n;
size_t oid_len;
unsigned char fprbuf[20];
/* (private-key(ecc(curve%s)(q%m)(d%m))(created-at%d)):
curve = "NIST P-256" */
/* (private-key(ecc(curve%s)(q%m)(d%m))(created-at%d)):
curve = "secp256k1" */
/* (private-key(ecc(curve%s)(flags eddsa)(q%m)(d%m))(created-at%d)):
curve = "Ed25519" */
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 == 5 && !memcmp (tok, "curve", 5))
{
char *curve_name;
if ((err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen)))
goto leave;
curve_name = xtrymalloc (toklen+1);
if (!curve_name)
{
err = gpg_error_from_syserror ();
goto leave;
}
memcpy (curve_name, tok, toklen);
curve_name[toklen] = 0;
curve = openpgp_is_curve_supported (curve_name, NULL, NULL);
xfree (curve_name);
}
else if (tok && toklen == 5 && !memcmp (tok, "flags", 5))
{
if ((err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen)))
goto leave;
if (tok)
{
if ((toklen == 5 && !memcmp (tok, "eddsa", 5))
|| (toklen == 9 && !memcmp (tok, "djb-tweak", 9)))
flag_djb_tweak = 1;
}
}
else if (tok && toklen == 1)
{
const unsigned char **buf2;
size_t *buf2len;
int native = flag_djb_tweak;
switch (*tok)
{
case 'q': buf2 = &ecc_q; buf2len = &ecc_q_len; break;
case 'd': buf2 = &ecc_d; buf2len = &ecc_d_len; native = 0; break;
default: buf2 = NULL; buf2len = NULL; break;
}
if (buf2 && *buf2)
{
err = gpg_error (GPG_ERR_DUP_VALUE);
goto leave;
}
if ((err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen)))
goto leave;
if (tok && buf2)
{
if (!native)
/* Strip off leading zero bytes and save. */
for (;toklen && !*tok; toklen--, tok++)
;
*buf2 = tok;
*buf2len = 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 (!curve)
{
log_error (_("unsupported curve\n"));
err = gpg_error (GPG_ERR_INV_VALUE);
goto leave;
}
if (!created_at)
{
log_error (_("creation timestamp missing\n"));
err = gpg_error (GPG_ERR_INV_VALUE);
goto leave;
}
if (flag_djb_tweak && keyno != 1)
algo = PUBKEY_ALGO_EDDSA;
else if (keyno == 1)
algo = PUBKEY_ALGO_ECDH;
else
algo = PUBKEY_ALGO_ECDSA;
oidstr = openpgp_curve_to_oid (curve, NULL);
err = openpgp_oid_from_str (oidstr, &oid);
if (err)
goto leave;
oidbuf = gcry_mpi_get_opaque (oid, &n);
if (!oidbuf)
{
err = gpg_error_from_syserror ();
goto leave;
}
oid_len = (n+7)/8;
if (app->app_local->keyattr[keyno].key_type != KEY_TYPE_ECC
|| app->app_local->keyattr[keyno].ecc.curve != curve
|| (flag_djb_tweak !=
(app->app_local->keyattr[keyno].ecc.flags & ECC_FLAG_DJB_TWEAK)))
{
if (app->app_local->extcap.algo_attr_change)
{
unsigned char *keyattr;
if (!oid_len)
{
err = gpg_error (GPG_ERR_INTERNAL);
goto leave;
}
keyattr = xtrymalloc (oid_len);
if (!keyattr)
{
err = gpg_error_from_syserror ();
goto leave;
}
keyattr[0] = algo;
memcpy (keyattr+1, oidbuf+1, oid_len-1);
err = change_keyattr (app, keyno, keyattr, oid_len, pincb, pincb_arg);
xfree (keyattr);
if (err)
goto leave;
}
else
{
log_error ("key attribute on card doesn't match\n");
err = gpg_error (GPG_ERR_INV_VALUE);
goto leave;
}
}
if (opt.verbose)
log_info ("ECC private key size is %u bytes\n", (unsigned int)ecc_d_len);
/* 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;
if (app->app_local->extcap.is_v2)
{
/* Build the private key template as described in section 4.3.3.7 of
the OpenPGP card specs version 2.0. */
unsigned char *template;
size_t template_len;
int exmode;
err = build_ecc_privkey_template (app, keyno,
ecc_d, ecc_d_len,
ecc_q, ecc_q_len,
&template, &template_len);
if (err)
goto leave;
/* Prepare for storing the key. */
err = verify_chv3 (app, pincb, pincb_arg);
if (err)
{
xfree (template);
goto leave;
}
/* Store the key. */
if (app->app_local->cardcap.ext_lc_le && template_len > 254)
exmode = 1; /* Use extended length w/o a limit. */
else if (app->app_local->cardcap.cmd_chaining && template_len > 254)
exmode = -254;
else
exmode = 0;
err = iso7816_put_data_odd (app->slot, exmode, 0x3fff,
template, template_len);
xfree (template);
}
else
err = gpg_error (GPG_ERR_NOT_SUPPORTED);
if (err)
{
log_error (_("failed to store the key: %s\n"), gpg_strerror (err));
goto leave;
}
err = store_fpr (app, keyno, created_at, fprbuf, algo, oidbuf, oid_len,
ecc_q, ecc_q_len, ecdh_params (curve), (size_t)4);
leave:
gcry_mpi_release (oid);
return err;
}
/* 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;
(void)ctrl;
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, 0, 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_SECKEY);
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) == 0)
err = rsa_writekey (app, pincb, pincb_arg, keyno, buf, buflen, depth);
else if (tok && toklen == 3 && memcmp ("ecc", tok, toklen) == 0)
err = ecc_writekey (app, pincb, pincb_arg, keyno, buf, buflen, depth);
else
{
err = gpg_error (GPG_ERR_WRONG_PUBKEY_ALGO);
goto leave;
}
leave:
return err;
}
/* Handle the GENKEY command. */
static gpg_error_t
do_genkey (app_t app, ctrl_t ctrl, const char *keynostr, const char *keytype,
unsigned int flags, time_t createtime,
gpg_error_t (*pincb)(void*, const char *, char **),
void *pincb_arg)
{
gpg_error_t err;
char numbuf[30];
unsigned char *buffer = NULL;
const unsigned char *keydata;
size_t buflen, keydatalen;
u32 created_at;
int keyno = atoi (keynostr) - 1;
int force = (flags & 1);
time_t start_at;
int exmode = 0;
int le_value = 256; /* Use legacy value. */
(void)keytype; /* Ignored for OpenPGP cards. */
if (keyno < 0 || keyno > 2)
return gpg_error (GPG_ERR_INV_ID);
/* We flush the cache to increase the traffic before a key
generation. This _might_ help a card to gather more entropy. */
flush_cache (app);
/* 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;
/* Check whether a key already exists. */
err = does_key_exist (app, keyno, 1, force);
if (err)
return err;
if (app->app_local->keyattr[keyno].key_type == KEY_TYPE_RSA)
{
unsigned int keybits = app->app_local->keyattr[keyno].rsa.n_bits;
/* Because we send the key parameter back via status lines we need
to put a limit on the max. allowed keysize. 2048 bit will
already lead to a 527 byte long status line and thus a 4096 bit
key would exceed the Assuan line length limit. */
if (keybits > 4096)
return gpg_error (GPG_ERR_TOO_LARGE);
if (app->app_local->cardcap.ext_lc_le && keybits > RSA_SMALL_SIZE_KEY
&& app->app_local->keyattr[keyno].key_type == KEY_TYPE_RSA)
{
exmode = 1; /* Use extended length w/o a limit. */
le_value = determine_rsa_response (app, keyno);
/* No need to check le_value because it comes from a 16 bit
value and thus can't create an overflow on a 32 bit
system. */
}
}
/* Prepare for key generation by verifying the Admin PIN. */
err = verify_chv3 (app, pincb, pincb_arg);
if (err)
return err;
log_info (_("please wait while key is being generated ...\n"));
start_at = time (NULL);
err = iso7816_generate_keypair (app->slot, exmode, 0x80, 0,
(keyno == 0? "\xB6" :
keyno == 1? "\xB8" : "\xA4"),
2, le_value, &buffer, &buflen);
if (err)
{
log_error (_("generating key failed\n"));
return gpg_error (GPG_ERR_CARD);
}
{
int nsecs = (int)(time (NULL) - start_at);
log_info (ngettext("key generation completed (%d second)\n",
"key generation completed (%d seconds)\n",
nsecs), nsecs);
}
keydata = find_tlv (buffer, buflen, 0x7F49, &keydatalen);
if (!keydata)
{
err = gpg_error (GPG_ERR_CARD);
log_error (_("response does not contain the public key data\n"));
goto leave;
}
created_at = (u32)(createtime? createtime : gnupg_get_time ());
sprintf (numbuf, "%u", created_at);
send_status_info (ctrl, "KEY-CREATED-AT",
numbuf, (size_t)strlen(numbuf), NULL, 0);
err = read_public_key (app, ctrl, created_at, keyno, buffer, buflen);
leave:
xfree (buffer);
return err;
}
static unsigned long
convert_sig_counter_value (const unsigned char *value, size_t valuelen)
{
unsigned long ul;
if (valuelen == 3 )
ul = (value[0] << 16) | (value[1] << 8) | value[2];
else
{
log_error (_("invalid structure of OpenPGP card (DO 0x93)\n"));
ul = 0;
}
return ul;
}
static unsigned long
get_sig_counter (app_t app)
{
void *relptr;
unsigned char *value;
size_t valuelen;
unsigned long ul;
relptr = get_one_do (app, 0x0093, &value, &valuelen, NULL);
if (!relptr)
return 0;
ul = convert_sig_counter_value (value, valuelen);
xfree (relptr);
return ul;
}
static gpg_error_t
compare_fingerprint (app_t app, int keyno, unsigned char *sha1fpr)
{
const unsigned char *fpr;
unsigned char *buffer;
size_t buflen, n;
int rc, i;
assert (keyno >= 0 && keyno <= 2);
rc = get_cached_data (app, 0x006E, &buffer, &buflen, 0, 0);
if (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)
{
xfree (buffer);
log_error (_("error reading fingerprint DO\n"));
return gpg_error (GPG_ERR_GENERAL);
}
fpr += keyno*20;
for (i=0; i < 20; i++)
if (sha1fpr[i] != fpr[i])
{
xfree (buffer);
log_info (_("fingerprint on card does not match requested one\n"));
return gpg_error (GPG_ERR_WRONG_SECKEY);
}
xfree (buffer);
return 0;
}
/* If a fingerprint has been specified check it against the one on the
card. This allows for a meaningful error message in case the key
on the card has been replaced but the shadow information known to
gpg has not been updated. If there is no fingerprint we assume
that this is okay. */
static gpg_error_t
check_against_given_fingerprint (app_t app, const char *fpr, int key)
{
unsigned char tmp[20];
const char *s;
int n;
for (s=fpr, n=0; hexdigitp (s); s++, n++)
;
if (n != 40)
return gpg_error (GPG_ERR_INV_ID);
else if (!*s)
; /* okay */
else
return gpg_error (GPG_ERR_INV_ID);
for (s=fpr, n=0; n < 20; s += 2, n++)
tmp[n] = xtoi_2 (s);
return compare_fingerprint (app, key-1, tmp);
}
/* Compute a digital signature on INDATA which is expected to be the
raw message digest. For this application the KEYIDSTR consists of
the serialnumber and the fingerprint delimited by a slash.
Note that this function may return the error code
GPG_ERR_WRONG_CARD to indicate that the card currently present does
not match the one required for the requested action (e.g. the
serial number does not match).
As a special feature a KEYIDSTR of "OPENPGP.3" redirects the
operation to the auth command.
*/
static gpg_error_t
do_sign (app_t app, const char *keyidstr, int hashalgo,
gpg_error_t (*pincb)(void*, const char *, char **),
void *pincb_arg,
const void *indata, size_t indatalen,
unsigned char **outdata, size_t *outdatalen )
{
static unsigned char rmd160_prefix[15] = /* Object ID is 1.3.36.3.2.1 */
{ 0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2b, 0x24, 0x03,
0x02, 0x01, 0x05, 0x00, 0x04, 0x14 };
static unsigned char sha1_prefix[15] = /* (1.3.14.3.2.26) */
{ 0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2b, 0x0e, 0x03,
0x02, 0x1a, 0x05, 0x00, 0x04, 0x14 };
static unsigned char sha224_prefix[19] = /* (2.16.840.1.101.3.4.2.4) */
{ 0x30, 0x2D, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48,
0x01, 0x65, 0x03, 0x04, 0x02, 0x04, 0x05, 0x00, 0x04,
0x1C };
static unsigned char sha256_prefix[19] = /* (2.16.840.1.101.3.4.2.1) */
{ 0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05,
0x00, 0x04, 0x20 };
static unsigned char sha384_prefix[19] = /* (2.16.840.1.101.3.4.2.2) */
{ 0x30, 0x41, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x02, 0x05,
0x00, 0x04, 0x30 };
static unsigned char sha512_prefix[19] = /* (2.16.840.1.101.3.4.2.3) */
{ 0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x05,
0x00, 0x04, 0x40 };
int rc;
unsigned char data[19+64];
size_t datalen;
unsigned char tmp_sn[20]; /* Actually 16 bytes but also for the fpr. */
const char *s;
int n;
const char *fpr = NULL;
unsigned long sigcount;
int use_auth = 0;
int exmode, le_value;
if (!keyidstr || !*keyidstr)
return gpg_error (GPG_ERR_INV_VALUE);
/* Strip off known prefixes. */
#define X(a,b,c,d) \
if (hashalgo == GCRY_MD_ ## a \
&& (d) \
&& indatalen == sizeof b ## _prefix + (c) \
&& !memcmp (indata, b ## _prefix, sizeof b ## _prefix)) \
{ \
indata = (const char*)indata + sizeof b ## _prefix; \
indatalen -= sizeof b ## _prefix; \
}
if (indatalen == 20)
; /* Assume a plain SHA-1 or RMD160 digest has been given. */
else X(SHA1, sha1, 20, 1)
else X(RMD160, rmd160, 20, 1)
else X(SHA224, sha224, 28, app->app_local->extcap.is_v2)
else X(SHA256, sha256, 32, app->app_local->extcap.is_v2)
else X(SHA384, sha384, 48, app->app_local->extcap.is_v2)
else X(SHA512, sha512, 64, app->app_local->extcap.is_v2)
else if ((indatalen == 28 || indatalen == 32
|| indatalen == 48 || indatalen ==64)
&& app->app_local->extcap.is_v2)
; /* Assume a plain SHA-3 digest has been given. */
else
{
log_error (_("card does not support digest algorithm %s\n"),
gcry_md_algo_name (hashalgo));
/* Or the supplied digest length does not match an algorithm. */
return gpg_error (GPG_ERR_INV_VALUE);
}
#undef X
/* Check whether an OpenPGP card of any version has been requested. */
if (!strcmp (keyidstr, "OPENPGP.1"))
;
else if (!strcmp (keyidstr, "OPENPGP.3"))
use_auth = 1;
else if (strlen (keyidstr) < 32 || strncmp (keyidstr, "D27600012401", 12))
return gpg_error (GPG_ERR_INV_ID);
else
{
for (s=keyidstr, n=0; hexdigitp (s); s++, n++)
;
if (n != 32)
return gpg_error (GPG_ERR_INV_ID);
else if (!*s)
; /* no fingerprint given: we allow this for now. */
else if (*s == '/')
fpr = s + 1;
else
return gpg_error (GPG_ERR_INV_ID);
for (s=keyidstr, n=0; n < 16; s += 2, n++)
tmp_sn[n] = xtoi_2 (s);
if (app->serialnolen != 16)
return gpg_error (GPG_ERR_INV_CARD);
if (memcmp (app->serialno, tmp_sn, 16))
return gpg_error (GPG_ERR_WRONG_CARD);
}
/* If a fingerprint has been specified check it against the one on
the card. This is allows for a meaningful error message in case
the key on the card has been replaced but the shadow information
known to gpg was not updated. If there is no fingerprint, gpg
will detect a bogus signature anyway due to the
verify-after-signing feature. */
rc = fpr? check_against_given_fingerprint (app, fpr, 1) : 0;
if (rc)
return rc;
/* Concatenate prefix and digest. */
#define X(a,b,d) \
if (hashalgo == GCRY_MD_ ## a && (d) ) \
{ \
datalen = sizeof b ## _prefix + indatalen; \
assert (datalen <= sizeof data); \
memcpy (data, b ## _prefix, sizeof b ## _prefix); \
memcpy (data + sizeof b ## _prefix, indata, indatalen); \
}
if (use_auth
|| app->app_local->keyattr[use_auth? 2: 0].key_type == KEY_TYPE_RSA)
{
X(SHA1, sha1, 1)
else X(RMD160, rmd160, 1)
else X(SHA224, sha224, app->app_local->extcap.is_v2)
else X(SHA256, sha256, app->app_local->extcap.is_v2)
else X(SHA384, sha384, app->app_local->extcap.is_v2)
else X(SHA512, sha512, app->app_local->extcap.is_v2)
else
return gpg_error (GPG_ERR_UNSUPPORTED_ALGORITHM);
}
else
{
datalen = indatalen;
memcpy (data, indata, indatalen);
}
#undef X
/* Redirect to the AUTH command if asked to. */
if (use_auth)
{
return do_auth (app, "OPENPGP.3", pincb, pincb_arg,
data, datalen,
outdata, outdatalen);
}
/* Show the number of signature done using this key. */
sigcount = get_sig_counter (app);
log_info (_("signatures created so far: %lu\n"), sigcount);
/* Check CHV if needed. */
if (!app->did_chv1 || app->force_chv1)
{
char *pinvalue;
int pinlen;
rc = verify_a_chv (app, pincb, pincb_arg, 1, sigcount, &pinvalue, &pinlen);
if (rc)
return rc;
app->did_chv1 = 1;
/* For cards with versions < 2 we want to keep CHV1 and CHV2 in
sync, thus we verify CHV2 here using the given PIN. Cards
with version2 to not have the need for a separate CHV2 and
internally use just one. Obviously we can't do that if the
pinpad has been used. */
if (!app->did_chv2 && pinvalue && !app->app_local->extcap.is_v2)
{
rc = iso7816_verify (app->slot, 0x82, pinvalue, pinlen);
if (gpg_err_code (rc) == GPG_ERR_BAD_PIN)
rc = gpg_error (GPG_ERR_PIN_NOT_SYNCED);
if (rc)
{
log_error (_("verify CHV%d failed: %s\n"), 2, gpg_strerror (rc));
xfree (pinvalue);
flush_cache_after_error (app);
return rc;
}
app->did_chv2 = 1;
}
xfree (pinvalue);
}
if (app->app_local->cardcap.ext_lc_le
&& app->app_local->keyattr[0].key_type == KEY_TYPE_RSA
&& app->app_local->keyattr[0].rsa.n_bits > RSA_SMALL_SIZE_OP)
{
exmode = 1; /* Use extended length. */
le_value = app->app_local->keyattr[0].rsa.n_bits / 8;
}
else
{
exmode = 0;
le_value = 0;
}
rc = iso7816_compute_ds (app->slot, exmode, data, datalen, le_value,
outdata, outdatalen);
if (!rc && app->force_chv1)
app->did_chv1 = 0;
return rc;
}
/* Compute a digital signature using the INTERNAL AUTHENTICATE command
on INDATA which is expected to be the raw message digest. For this
application the KEYIDSTR consists of the serialnumber and the
fingerprint delimited by a slash. Optionally the id OPENPGP.3 may
be given.
Note that this function may return the error code
GPG_ERR_WRONG_CARD to indicate that the card currently present does
not match the one required for the requested action (e.g. the
serial number does not match). */
static gpg_error_t
do_auth (app_t app, const char *keyidstr,
gpg_error_t (*pincb)(void*, const char *, char **),
void *pincb_arg,
const void *indata, size_t indatalen,
unsigned char **outdata, size_t *outdatalen )
{
int rc;
unsigned char tmp_sn[20]; /* Actually 16 but we use it also for the fpr. */
const char *s;
int n;
const char *fpr = NULL;
if (!keyidstr || !*keyidstr)
return gpg_error (GPG_ERR_INV_VALUE);
if (app->app_local->keyattr[2].key_type == KEY_TYPE_RSA
&& indatalen > 101) /* For a 2048 bit key. */
return gpg_error (GPG_ERR_INV_VALUE);
if (app->app_local->keyattr[2].key_type == KEY_TYPE_ECC)
{
if (!(app->app_local->keyattr[2].ecc.flags & ECC_FLAG_DJB_TWEAK)
&& (indatalen == 51 || indatalen == 67 || indatalen == 83))
{
const char *p = (const char *)indata + 19;
indata = p;
indatalen -= 19;
}
else
{
const char *p = (const char *)indata + 15;
indata = p;
indatalen -= 15;
}
}
/* Check whether an OpenPGP card of any version has been requested. */
if (!strcmp (keyidstr, "OPENPGP.3"))
;
else if (strlen (keyidstr) < 32 || strncmp (keyidstr, "D27600012401", 12))
return gpg_error (GPG_ERR_INV_ID);
else
{
for (s=keyidstr, n=0; hexdigitp (s); s++, n++)
;
if (n != 32)
return gpg_error (GPG_ERR_INV_ID);
else if (!*s)
; /* no fingerprint given: we allow this for now. */
else if (*s == '/')
fpr = s + 1;
else
return gpg_error (GPG_ERR_INV_ID);
for (s=keyidstr, n=0; n < 16; s += 2, n++)
tmp_sn[n] = xtoi_2 (s);
if (app->serialnolen != 16)
return gpg_error (GPG_ERR_INV_CARD);
if (memcmp (app->serialno, tmp_sn, 16))
return gpg_error (GPG_ERR_WRONG_CARD);
}
/* If a fingerprint has been specified check it against the one on
the card. This is allows for a meaningful error message in case
the key on the card has been replaced but the shadow information
known to gpg was not updated. If there is no fingerprint, gpg
will detect a bogus signature anyway due to the
verify-after-signing feature. */
rc = fpr? check_against_given_fingerprint (app, fpr, 3) : 0;
if (rc)
return rc;
rc = verify_chv2 (app, pincb, pincb_arg);
if (!rc)
{
int exmode, le_value;
if (app->app_local->cardcap.ext_lc_le
&& app->app_local->keyattr[2].key_type == KEY_TYPE_RSA
&& app->app_local->keyattr[2].rsa.n_bits > RSA_SMALL_SIZE_OP)
{
exmode = 1; /* Use extended length. */
le_value = app->app_local->keyattr[2].rsa.n_bits / 8;
}
else
{
exmode = 0;
le_value = 0;
}
rc = iso7816_internal_authenticate (app->slot, exmode,
indata, indatalen, le_value,
outdata, outdatalen);
}
return rc;
}
static gpg_error_t
do_decipher (app_t app, const char *keyidstr,
gpg_error_t (*pincb)(void*, const char *, char **),
void *pincb_arg,
const void *indata, size_t indatalen,
unsigned char **outdata, size_t *outdatalen,
unsigned int *r_info)
{
int rc;
unsigned char tmp_sn[20]; /* actually 16 but we use it also for the fpr. */
const char *s;
int n;
const char *fpr = NULL;
int exmode, le_value;
unsigned char *fixbuf = NULL;
int padind = 0;
int fixuplen = 0;
if (!keyidstr || !*keyidstr || !indatalen)
return gpg_error (GPG_ERR_INV_VALUE);
/* Check whether an OpenPGP card of any version has been requested. */
if (!strcmp (keyidstr, "OPENPGP.2"))
;
else if (strlen (keyidstr) < 32 || strncmp (keyidstr, "D27600012401", 12))
return gpg_error (GPG_ERR_INV_ID);
else
{
for (s=keyidstr, n=0; hexdigitp (s); s++, n++)
;
if (n != 32)
return gpg_error (GPG_ERR_INV_ID);
else if (!*s)
; /* no fingerprint given: we allow this for now. */
else if (*s == '/')
fpr = s + 1;
else
return gpg_error (GPG_ERR_INV_ID);
for (s=keyidstr, n=0; n < 16; s += 2, n++)
tmp_sn[n] = xtoi_2 (s);
if (app->serialnolen != 16)
return gpg_error (GPG_ERR_INV_CARD);
if (memcmp (app->serialno, tmp_sn, 16))
return gpg_error (GPG_ERR_WRONG_CARD);
}
/* If a fingerprint has been specified check it against the one on
the card. This is allows for a meaningful error message in case
the key on the card has been replaced but the shadow information
known to gpg was not updated. If there is no fingerprint, the
decryption won't produce the right plaintext anyway. */
rc = fpr? check_against_given_fingerprint (app, fpr, 2) : 0;
if (rc)
return rc;
rc = verify_chv2 (app, pincb, pincb_arg);
if (rc)
return rc;
if ((indatalen == 16 + 1 || indatalen == 32 + 1)
&& ((char *)indata)[0] == 0x02)
{
/* PSO:DECIPHER with symmetric key. */
padind = -1;
}
else if (app->app_local->keyattr[1].key_type == KEY_TYPE_RSA)
{
/* We might encounter a couple of leading zeroes in the
cryptogram. Due to internal use of MPIs these leading zeroes
are stripped. However the OpenPGP card expects exactly 128
bytes for the cryptogram (for a 1k key). Thus we need to fix
it up. We do this for up to 16 leading zero bytes; a
cryptogram with more than this is with a very high
probability anyway broken. If a signed conversion was used
we may also encounter one leading zero followed by the correct
length. We fix that as well. */
if (indatalen >= (128-16) && indatalen < 128) /* 1024 bit key. */
fixuplen = 128 - indatalen;
else if (indatalen >= (192-16) && indatalen < 192) /* 1536 bit key. */
fixuplen = 192 - indatalen;
else if (indatalen >= (256-16) && indatalen < 256) /* 2048 bit key. */
fixuplen = 256 - indatalen;
else if (indatalen >= (384-16) && indatalen < 384) /* 3072 bit key. */
fixuplen = 384 - indatalen;
else if (indatalen >= (512-16) && indatalen < 512) /* 4096 bit key. */
fixuplen = 512 - indatalen;
else if (!*(const char *)indata && (indatalen == 129
|| indatalen == 193
|| indatalen == 257
|| indatalen == 385
|| indatalen == 513))
fixuplen = -1;
else
fixuplen = 0;
if (fixuplen > 0)
{
/* While we have to prepend stuff anyway, we can also
include the padding byte here so that iso1816_decipher
does not need to do another data mangling. */
fixuplen++;
fixbuf = xtrymalloc (fixuplen + indatalen);
if (!fixbuf)
return gpg_error_from_syserror ();
memset (fixbuf, 0, fixuplen);
memcpy (fixbuf+fixuplen, indata, indatalen);
indata = fixbuf;
indatalen = fixuplen + indatalen;
padind = -1; /* Already padded. */
}
else if (fixuplen < 0)
{
/* We use the extra leading zero as the padding byte. */
padind = -1;
}
}
else if (app->app_local->keyattr[1].key_type == KEY_TYPE_ECC)
{
int old_format_len = 0;
if ((app->app_local->keyattr[1].ecc.flags & ECC_FLAG_DJB_TWEAK))
{
if (indatalen > 32 && (indatalen % 2))
{ /*
* Skip the prefix. It may be 0x40 (in new format), or MPI
* head of 0x00 (in old format).
*/
indata = (const char *)indata + 1;
indatalen--;
}
else if (indatalen < 32)
{ /*
* Old format trancated by MPI handling.
*/
old_format_len = indatalen;
indatalen = 32;
}
}
n = 0;
if (indatalen < 128)
fixuplen = 7;
else
fixuplen = 10;
fixbuf = xtrymalloc (fixuplen + indatalen);
if (!fixbuf)
return gpg_error_from_syserror ();
/* Build 'Cipher DO' */
fixbuf[n++] = '\xa6';
if (indatalen < 128)
fixbuf[n++] = (char)(indatalen+5);
else
{
fixbuf[n++] = 0x81;
fixbuf[n++] = (char)(indatalen+7);
}
fixbuf[n++] = '\x7f';
fixbuf[n++] = '\x49';
if (indatalen < 128)
fixbuf[n++] = (char)(indatalen+2);
else
{
fixbuf[n++] = 0x81;
fixbuf[n++] = (char)(indatalen+3);
}
fixbuf[n++] = '\x86';
if (indatalen < 128)
fixbuf[n++] = (char)indatalen;
else
{
fixbuf[n++] = 0x81;
fixbuf[n++] = (char)indatalen;
}
if (old_format_len)
{
memset (fixbuf+fixuplen, 0, 32 - old_format_len);
memcpy (fixbuf+fixuplen + 32 - old_format_len,
indata, old_format_len);
}
else
{
memcpy (fixbuf+fixuplen, indata, indatalen);
}
indata = fixbuf;
indatalen = fixuplen + indatalen;
padind = -1;
}
else
return gpg_error (GPG_ERR_INV_VALUE);
if (app->app_local->cardcap.ext_lc_le
&& (indatalen > 254
|| (app->app_local->keyattr[1].key_type == KEY_TYPE_RSA
&& app->app_local->keyattr[1].rsa.n_bits > RSA_SMALL_SIZE_OP)))
{
exmode = 1; /* Extended length w/o a limit. */
le_value = app->app_local->keyattr[1].rsa.n_bits / 8;
}
else if (app->app_local->cardcap.cmd_chaining && indatalen > 254)
{
exmode = -254; /* Command chaining with max. 254 bytes. */
le_value = 0;
}
else
exmode = le_value = 0;
rc = iso7816_decipher (app->slot, exmode,
indata, indatalen, le_value, padind,
outdata, outdatalen);
xfree (fixbuf);
if (app->app_local->keyattr[1].key_type == KEY_TYPE_ECC)
{
unsigned char prefix = 0;
if (app->app_local->keyattr[1].ecc.flags & ECC_FLAG_DJB_TWEAK)
prefix = 0x40;
else if ((*outdatalen % 2) == 0) /* No 0x04 -> x-coordinate only */
prefix = 0x41;
if (prefix)
{ /* Add the prefix */
fixbuf = xtrymalloc (*outdatalen + 1);
if (!fixbuf)
{
xfree (*outdata);
return gpg_error_from_syserror ();
}
fixbuf[0] = prefix;
memcpy (fixbuf+1, *outdata, *outdatalen);
xfree (*outdata);
*outdata = fixbuf;
*outdatalen = *outdatalen + 1;
}
}
if (gpg_err_code (rc) == GPG_ERR_CARD /* actual SW is 0x640a */
&& app->app_local->manufacturer == 5
&& app->card_version == 0x0200)
log_info ("NOTE: Cards with manufacturer id 5 and s/n <= 346 (0x15a)"
" do not work with encryption keys > 2048 bits\n");
*r_info |= APP_DECIPHER_INFO_NOPAD;
return rc;
}
/* Perform a simple verify operation for CHV1 and CHV2, so that
further operations won't ask for CHV2 and it is possible to do a
cheap check on the PIN: If there is something wrong with the PIN
entry system, only the regular CHV will get blocked and not the
dangerous CHV3. KEYIDSTR is the usual card's serial number; an
optional fingerprint part will be ignored.
There is a special mode if the keyidstr is "[CHV3]" with
the "[CHV3]" being a literal string: The Admin Pin is checked if
and only if the retry counter is still at 3. */
static gpg_error_t
do_check_pin (app_t app, const char *keyidstr,
gpg_error_t (*pincb)(void*, const char *, char **),
void *pincb_arg)
{
unsigned char tmp_sn[20];
const char *s;
int n;
int admin_pin = 0;
if (!keyidstr || !*keyidstr)
return gpg_error (GPG_ERR_INV_VALUE);
/* Check whether an OpenPGP card of any version has been requested. */
if (strlen (keyidstr) < 32 || strncmp (keyidstr, "D27600012401", 12))
return gpg_error (GPG_ERR_INV_ID);
for (s=keyidstr, n=0; hexdigitp (s); s++, n++)
;
if (n != 32)
return gpg_error (GPG_ERR_INV_ID);
else if (!*s)
; /* No fingerprint given: we allow this for now. */
else if (*s == '/')
; /* We ignore a fingerprint. */
else if (!strcmp (s, "[CHV3]") )
admin_pin = 1;
else
return gpg_error (GPG_ERR_INV_ID);
for (s=keyidstr, n=0; n < 16; s += 2, n++)
tmp_sn[n] = xtoi_2 (s);
if (app->serialnolen != 16)
return gpg_error (GPG_ERR_INV_CARD);
if (memcmp (app->serialno, tmp_sn, 16))
return gpg_error (GPG_ERR_WRONG_CARD);
/* Yes, there is a race conditions: The user might pull the card
right here and we won't notice that. However this is not a
problem and the check above is merely for a graceful failure
between operations. */
if (admin_pin)
{
void *relptr;
unsigned char *value;
size_t valuelen;
int count;
relptr = get_one_do (app, 0x00C4, &value, &valuelen, NULL);
if (!relptr || valuelen < 7)
{
log_error (_("error retrieving CHV status from card\n"));
xfree (relptr);
return gpg_error (GPG_ERR_CARD);
}
count = value[6];
xfree (relptr);
if (!count)
{
log_info (_("card is permanently locked!\n"));
return gpg_error (GPG_ERR_BAD_PIN);
}
else if (count < 3)
{
log_info (_("verification of Admin PIN is currently prohibited "
"through this command\n"));
return gpg_error (GPG_ERR_GENERAL);
}
app->did_chv3 = 0; /* Force verification. */
return verify_chv3 (app, pincb, pincb_arg);
}
else
return verify_chv2 (app, pincb, pincb_arg);
}
/* Show information about card capabilities. */
static void
show_caps (struct app_local_s *s)
{
log_info ("Version-2+ .....: %s\n", s->extcap.is_v2? "yes":"no");
log_info ("Extcap-v3 ......: %s\n", s->extcap.extcap_v3? "yes":"no");
log_info ("Button .........: %s\n", s->extcap.has_button? "yes":"no");
log_info ("SM-Support .....: %s", s->extcap.sm_supported? "yes":"no");
if (s->extcap.sm_supported)
log_printf (" (%s)", s->extcap.sm_algo==2? "3DES":
(s->extcap.sm_algo==2? "AES-128" : "AES-256"));
log_info ("Get-Challenge ..: %s", s->extcap.get_challenge? "yes":"no");
if (s->extcap.get_challenge)
log_printf (" (%u bytes max)", s->extcap.max_get_challenge);
log_info ("Key-Import .....: %s\n", s->extcap.key_import? "yes":"no");
log_info ("Change-Force-PW1: %s\n", s->extcap.change_force_chv? "yes":"no");
log_info ("Private-DOs ....: %s\n", s->extcap.private_dos? "yes":"no");
log_info ("Algo-Attr-Change: %s\n", s->extcap.algo_attr_change? "yes":"no");
log_info ("Symmetric Crypto: %s\n", s->extcap.has_decrypt? "yes":"no");
log_info ("KDF-Support ....: %s\n", s->extcap.kdf_do? "yes":"no");
log_info ("Max-Cert3-Len ..: %u\n", s->extcap.max_certlen_3);
if (s->extcap.extcap_v3)
{
log_info ("PIN-Block-2 ....: %s\n", s->extcap.pin_blk2? "yes":"no");
log_info ("MSE-Support ....: %s\n", s->extcap.mse? "yes":"no");
log_info ("Max-Special-DOs : %u\n", s->extcap.max_special_do);
}
log_info ("Cmd-Chaining ...: %s\n", s->cardcap.cmd_chaining?"yes":"no");
log_info ("Ext-Lc-Le ......: %s\n", s->cardcap.ext_lc_le?"yes":"no");
log_info ("Status-Indicator: %02X\n", s->status_indicator);
log_info ("GnuPG-No-Sync ..: %s\n", s->flags.no_sync? "yes":"no");
log_info ("GnuPG-Def-PW2 ..: %s\n", s->flags.def_chv2? "yes":"no");
}
/* Parse the historical bytes in BUFFER of BUFLEN and store them in
APPLOC. */
static void
parse_historical (struct app_local_s *apploc,
const unsigned char * buffer, size_t buflen)
{
/* Example buffer: 00 31 C5 73 C0 01 80 00 90 00 */
if (buflen < 4)
{
log_error ("warning: historical bytes are too short\n");
return; /* Too short. */
}
if (*buffer)
{
log_error ("warning: bad category indicator in historical bytes\n");
return;
}
/* Skip category indicator. */
buffer++;
buflen--;
/* Get the status indicator. */
apploc->status_indicator = buffer[buflen-3];
buflen -= 3;
/* Parse the compact TLV. */
while (buflen)
{
unsigned int tag = (*buffer & 0xf0) >> 4;
unsigned int len = (*buffer & 0x0f);
if (len+1 > buflen)
{
log_error ("warning: bad Compact-TLV in historical bytes\n");
return; /* Error. */
}
buffer++;
buflen--;
if (tag == 7 && len == 3)
{
/* Card capabilities. */
apploc->cardcap.cmd_chaining = !!(buffer[2] & 0x80);
apploc->cardcap.ext_lc_le = !!(buffer[2] & 0x40);
}
buffer += len;
buflen -= len;
}
}
/*
* Check if the OID in an DER encoding is available by GnuPG/libgcrypt,
* and return the curve name. Return NULL if not available.
* The constant string is not allocated dynamically, never free it.
*/
static const char *
ecc_curve (unsigned char *buf, size_t buflen)
{
gcry_mpi_t oid;
char *oidstr;
const char *result;
unsigned char *oidbuf;
oidbuf = xtrymalloc (buflen + 1);
if (!oidbuf)
return NULL;
memcpy (oidbuf+1, buf, buflen);
oidbuf[0] = buflen;
oid = gcry_mpi_set_opaque (NULL, oidbuf, (buflen+1) * 8);
if (!oid)
{
xfree (oidbuf);
return NULL;
}
oidstr = openpgp_oid_to_str (oid);
gcry_mpi_release (oid);
if (!oidstr)
return NULL;
result = openpgp_oid_to_curve (oidstr, 1);
xfree (oidstr);
return result;
}
/* Parse and optionally show the algorithm attributes for KEYNO.
KEYNO must be in the range 0..2. */
static void
parse_algorithm_attribute (app_t app, int keyno)
{
unsigned char *buffer;
size_t buflen;
void *relptr;
const char desc[3][5] = {"sign", "encr", "auth"};
assert (keyno >=0 && keyno <= 2);
app->app_local->keyattr[keyno].key_type = KEY_TYPE_RSA;
app->app_local->keyattr[keyno].rsa.n_bits = 0;
relptr = get_one_do (app, 0xC1+keyno, &buffer, &buflen, NULL);
if (!relptr)
{
log_error ("error reading DO 0x%02X\n", 0xc1+keyno);
return;
}
if (buflen < 1)
{
log_error ("error reading DO 0x%02X\n", 0xc1+keyno);
xfree (relptr);
return;
}
if (opt.verbose)
log_info ("Key-Attr-%s ..: ", desc[keyno]);
if (*buffer == PUBKEY_ALGO_RSA && (buflen == 5 || buflen == 6))
{
app->app_local->keyattr[keyno].rsa.n_bits = (buffer[1]<<8 | buffer[2]);
app->app_local->keyattr[keyno].rsa.e_bits = (buffer[3]<<8 | buffer[4]);
app->app_local->keyattr[keyno].rsa.format = 0;
if (buflen < 6)
app->app_local->keyattr[keyno].rsa.format = RSA_STD;
else
app->app_local->keyattr[keyno].rsa.format = (buffer[5] == 0? RSA_STD :
buffer[5] == 1? RSA_STD_N :
buffer[5] == 2? RSA_CRT :
buffer[5] == 3? RSA_CRT_N :
RSA_UNKNOWN_FMT);
if (opt.verbose)
log_printf
("RSA, n=%u, e=%u, fmt=%s\n",
app->app_local->keyattr[keyno].rsa.n_bits,
app->app_local->keyattr[keyno].rsa.e_bits,
app->app_local->keyattr[keyno].rsa.format == RSA_STD? "std" :
app->app_local->keyattr[keyno].rsa.format == RSA_STD_N?"std+n":
app->app_local->keyattr[keyno].rsa.format == RSA_CRT? "crt" :
app->app_local->keyattr[keyno].rsa.format == RSA_CRT_N?"crt+n":"?");
}
else if (*buffer == PUBKEY_ALGO_ECDH || *buffer == PUBKEY_ALGO_ECDSA
|| *buffer == PUBKEY_ALGO_EDDSA)
{
const char *curve;
int oidlen = buflen - 1;
app->app_local->keyattr[keyno].ecc.flags = 0;
if (buffer[buflen-1] == 0x00 || buffer[buflen-1] == 0xff)
{ /* Found "pubkey required"-byte for private key template. */
oidlen--;
if (buffer[buflen-1] == 0xff)
app->app_local->keyattr[keyno].ecc.flags |= ECC_FLAG_PUBKEY;
}
curve = ecc_curve (buffer + 1, oidlen);
if (!curve)
- log_printhex ("Curve with OID not supported: ", buffer+1, buflen-1);
+ log_printhex (buffer+1, buflen-1, "Curve with OID not supported: ");
else
{
app->app_local->keyattr[keyno].key_type = KEY_TYPE_ECC;
app->app_local->keyattr[keyno].ecc.curve = curve;
if (*buffer == PUBKEY_ALGO_EDDSA
|| (*buffer == PUBKEY_ALGO_ECDH
&& !strcmp (app->app_local->keyattr[keyno].ecc.curve,
"Curve25519")))
app->app_local->keyattr[keyno].ecc.flags |= ECC_FLAG_DJB_TWEAK;
if (opt.verbose)
log_printf
("ECC, curve=%s%s\n", app->app_local->keyattr[keyno].ecc.curve,
!(app->app_local->keyattr[keyno].ecc.flags & ECC_FLAG_DJB_TWEAK)?
"": keyno==1? " (djb-tweak)": " (eddsa)");
}
}
else if (opt.verbose)
- log_printhex ("", buffer, buflen);
+ log_printhex (buffer, buflen, "");
xfree (relptr);
}
/* Select the OpenPGP application on the card in SLOT. This function
must be used before any other OpenPGP application functions. */
gpg_error_t
app_select_openpgp (app_t app)
{
static char const aid[] = { 0xD2, 0x76, 0x00, 0x01, 0x24, 0x01 };
int slot = app->slot;
int rc;
unsigned char *buffer;
size_t buflen;
void *relptr;
/* Note that the card can't cope with P2=0xCO, thus we need to pass a
special flag value. */
rc = iso7816_select_application (slot, aid, sizeof aid, 0x0001);
if (!rc)
{
unsigned int manufacturer;
app->apptype = "OPENPGP";
app->did_chv1 = 0;
app->did_chv2 = 0;
app->did_chv3 = 0;
app->app_local = NULL;
/* The OpenPGP card returns the serial number as part of the
AID; because we prefer to use OpenPGP serial numbers, we
replace a possibly already set one from a EF.GDO with this
one. Note, that for current OpenPGP cards, no EF.GDO exists
and thus it won't matter at all. */
rc = iso7816_get_data (slot, 0, 0x004F, &buffer, &buflen);
if (rc)
goto leave;
if (opt.verbose)
{
log_info ("AID: ");
- log_printhex ("", buffer, buflen);
+ log_printhex (buffer, buflen, "");
}
app->card_version = buffer[6] << 8;
app->card_version |= buffer[7];
manufacturer = (buffer[8]<<8 | buffer[9]);
xfree (app->serialno);
app->serialno = buffer;
app->serialnolen = buflen;
buffer = NULL;
app->app_local = xtrycalloc (1, sizeof *app->app_local);
if (!app->app_local)
{
rc = gpg_error (gpg_err_code_from_errno (errno));
goto leave;
}
app->app_local->manufacturer = manufacturer;
if (app->card_version >= 0x0200)
app->app_local->extcap.is_v2 = 1;
if (app->card_version >= 0x0300)
app->app_local->extcap.extcap_v3 = 1;
/* Read the historical bytes. */
relptr = get_one_do (app, 0x5f52, &buffer, &buflen, NULL);
if (relptr)
{
if (opt.verbose)
{
log_info ("Historical Bytes: ");
- log_printhex ("", buffer, buflen);
+ log_printhex (buffer, buflen, "");
}
parse_historical (app->app_local, buffer, buflen);
xfree (relptr);
}
/* Read the force-chv1 flag. */
relptr = get_one_do (app, 0x00C4, &buffer, &buflen, NULL);
if (!relptr)
{
log_error (_("can't access %s - invalid OpenPGP card?\n"),
"CHV Status Bytes");
goto leave;
}
app->force_chv1 = (buflen && *buffer == 0);
xfree (relptr);
/* Read the extended capabilities. */
relptr = get_one_do (app, 0x00C0, &buffer, &buflen, NULL);
if (!relptr)
{
log_error (_("can't access %s - invalid OpenPGP card?\n"),
"Extended Capability Flags" );
goto leave;
}
if (buflen)
{
app->app_local->extcap.sm_supported = !!(*buffer & 0x80);
app->app_local->extcap.get_challenge = !!(*buffer & 0x40);
app->app_local->extcap.key_import = !!(*buffer & 0x20);
app->app_local->extcap.change_force_chv = !!(*buffer & 0x10);
app->app_local->extcap.private_dos = !!(*buffer & 0x08);
app->app_local->extcap.algo_attr_change = !!(*buffer & 0x04);
app->app_local->extcap.has_decrypt = !!(*buffer & 0x02);
app->app_local->extcap.kdf_do = !!(*buffer & 0x01);
}
if (buflen >= 10)
{
/* Available with cards of v2 or later. */
app->app_local->extcap.sm_algo = buffer[1];
app->app_local->extcap.max_get_challenge
= (buffer[2] << 8 | buffer[3]);
app->app_local->extcap.max_certlen_3 = (buffer[4] << 8 | buffer[5]);
/* Interpretation is different between v2 and v3, unfortunately. */
if (app->app_local->extcap.extcap_v3)
{
app->app_local->extcap.max_special_do
= (buffer[6] << 8 | buffer[7]);
app->app_local->extcap.pin_blk2 = !!(buffer[8] & 0x01);
app->app_local->extcap.mse= !!(buffer[9] & 0x01);
}
}
xfree (relptr);
/* Some of the first cards accidentally don't set the
CHANGE_FORCE_CHV bit but allow it anyway. */
if (app->card_version <= 0x0100 && manufacturer == 1)
app->app_local->extcap.change_force_chv = 1;
/* Check optional DO of "General Feature Management" for button. */
relptr = get_one_do (app, 0x7f74, &buffer, &buflen, NULL);
if (relptr)
/* It must be: 03 81 01 20 */
app->app_local->extcap.has_button = 1;
parse_login_data (app);
if (opt.verbose)
show_caps (app->app_local);
parse_algorithm_attribute (app, 0);
parse_algorithm_attribute (app, 1);
parse_algorithm_attribute (app, 2);
if (opt.verbose > 1)
dump_all_do (slot);
app->fnc.deinit = do_deinit;
app->fnc.learn_status = do_learn_status;
app->fnc.readcert = do_readcert;
app->fnc.readkey = do_readkey;
app->fnc.getattr = do_getattr;
app->fnc.setattr = do_setattr;
app->fnc.writecert = do_writecert;
app->fnc.writekey = do_writekey;
app->fnc.genkey = do_genkey;
app->fnc.sign = do_sign;
app->fnc.auth = do_auth;
app->fnc.decipher = do_decipher;
app->fnc.change_pin = do_change_pin;
app->fnc.check_pin = do_check_pin;
}
leave:
if (rc)
do_deinit (app);
return rc;
}
diff --git a/scd/app-p15.c b/scd/app-p15.c
index 0ae01bdba..9ad0d165b 100644
--- a/scd/app-p15.c
+++ b/scd/app-p15.c
@@ -1,4231 +1,4231 @@
/* app-p15.c - The pkcs#15 card application.
* Copyright (C) 2005 Free Software Foundation, Inc.
* Copyright (C) 2020 g10 Code GmbH
*
* This file is part of GnuPG.
*
* GnuPG is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* GnuPG is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see .
* SPDX-License-Identifier: GPL-3.0-or-later
*/
/* Information pertaining to the BELPIC developer card samples:
Unblock PUK: "222222111111"
Reset PIN: "333333111111")
e.g. the APDUs 00:20:00:02:08:2C:33:33:33:11:11:11:FF
and 00:24:01:01:08:24:12:34:FF:FF:FF:FF:FF
should change the PIN into 1234.
*/
#include
#include
#include
#include
#include
#include
#include
#include "scdaemon.h"
#include "iso7816.h"
#include "app-common.h"
#include "../common/i18n.h"
#include "../common/tlv.h"
#include "apdu.h" /* fixme: we should move the card detection to a
separate file */
/* Types of cards we know and which needs special treatment. */
typedef enum
{
CARD_TYPE_UNKNOWN,
CARD_TYPE_TCOS,
CARD_TYPE_MICARDO,
CARD_TYPE_CARDOS_50,
CARD_TYPE_BELPIC /* Belgian eID card specs. */
}
card_type_t;
/* The OS of card as specified by card_type_t is not always
* sufficient. Thus we also distinguish the actual product build upon
* the given OS. */
typedef enum
{
CARD_PRODUCT_UNKNOWN,
CARD_PRODUCT_DTRUST /* D-Trust GmbH (bundesdruckerei.de) */
}
card_product_t;
/* A list card types with ATRs noticed with these cards. */
#define X(a) ((unsigned char const *)(a))
static struct
{
size_t atrlen;
unsigned char const *atr;
card_type_t type;
} card_atr_list[] = {
{ 19, X("\x3B\xBA\x13\x00\x81\x31\x86\x5D\x00\x64\x05\x0A\x02\x01\x31\x80"
"\x90\x00\x8B"),
CARD_TYPE_TCOS }, /* SLE44 */
{ 19, X("\x3B\xBA\x14\x00\x81\x31\x86\x5D\x00\x64\x05\x14\x02\x02\x31\x80"
"\x90\x00\x91"),
CARD_TYPE_TCOS }, /* SLE66S */
{ 19, X("\x3B\xBA\x96\x00\x81\x31\x86\x5D\x00\x64\x05\x60\x02\x03\x31\x80"
"\x90\x00\x66"),
CARD_TYPE_TCOS }, /* SLE66P */
{ 27, X("\x3B\xFF\x94\x00\xFF\x80\xB1\xFE\x45\x1F\x03\x00\x68\xD2\x76\x00"
"\x00\x28\xFF\x05\x1E\x31\x80\x00\x90\x00\x23"),
CARD_TYPE_MICARDO }, /* German BMI card */
{ 19, X("\x3B\x6F\x00\xFF\x00\x68\xD2\x76\x00\x00\x28\xFF\x05\x1E\x31\x80"
"\x00\x90\x00"),
CARD_TYPE_MICARDO }, /* German BMI card (ATR due to reader problem) */
{ 26, X("\x3B\xFE\x94\x00\xFF\x80\xB1\xFA\x45\x1F\x03\x45\x73\x74\x45\x49"
"\x44\x20\x76\x65\x72\x20\x31\x2E\x30\x43"),
CARD_TYPE_MICARDO }, /* EstEID (Estonian Big Brother card) */
{ 11, X("\x3b\xd2\x18\x00\x81\x31\xfe\x58\xc9\x01\x14"),
CARD_TYPE_CARDOS_50 }, /* CardOS 5.0 */
{ 0 }
};
#undef X
/* The AID of PKCS15. */
static char const pkcs15_aid[] = { 0xA0, 0, 0, 0, 0x63,
0x50, 0x4B, 0x43, 0x53, 0x2D, 0x31, 0x35 };
/* The Belgian eID variant - they didn't understood why a shared AID
is useful for a standard. Oh well. */
static char const pkcs15be_aid[] = { 0xA0, 0, 0, 0x01, 0x77,
0x50, 0x4B, 0x43, 0x53, 0x2D, 0x31, 0x35 };
/* The PIN types as defined in pkcs#15 v1.1 */
typedef enum
{
PIN_TYPE_BCD = 0,
PIN_TYPE_ASCII_NUMERIC = 1,
PIN_TYPE_UTF8 = 2,
PIN_TYPE_HALF_NIBBLE_BCD = 3,
PIN_TYPE_ISO9564_1 = 4
} pin_type_t;
/* A bit array with for the key usage flags from the
commonKeyAttributes. */
struct keyusage_flags_s
{
unsigned int encrypt: 1;
unsigned int decrypt: 1;
unsigned int sign: 1;
unsigned int sign_recover: 1;
unsigned int wrap: 1;
unsigned int unwrap: 1;
unsigned int verify: 1;
unsigned int verify_recover: 1;
unsigned int derive: 1;
unsigned int non_repudiation: 1;
};
typedef struct keyusage_flags_s keyusage_flags_t;
/* This is an object to store information about a Certificate
Directory File (CDF) in a format suitable for further processing by
us. To keep memory management, simple we use a linked list of
items; i.e. one such object represents one certificate and the list
the entire CDF. */
struct cdf_object_s
{
/* Link to next item when used in a linked list. */
struct cdf_object_s *next;
/* Flags to indicate whether fields are valid. */
unsigned int have_off:1;
/* Length and allocated buffer with the Id of this object.
* This field is used for X.509 in PKCS#11 to make it easier to
* match a private key with a certificate. */
size_t objidlen;
unsigned char *objid;
/* To avoid reading a certificate more than once, we cache it in an
allocated memory IMAGE of IMAGELEN. */
size_t imagelen;
unsigned char *image;
/* The offset and length of the object. They are only valid if
HAVE_OFF is true and set to 0 if HAVE_OFF is false. */
unsigned long off, len;
/* The length of the path as given in the CDF and the path itself.
path[0] is the top DF (usually 0x3f00). The path will never be
empty. */
size_t pathlen;
unsigned short path[1];
};
typedef struct cdf_object_s *cdf_object_t;
/* This is an object to store information about a Private Key
Directory File (PrKDF) in a format suitable for further processing
by us. To keep memory management, simple we use a linked list of
items; i.e. one such object represents one certificate and the list
the entire PrKDF. */
struct prkdf_object_s
{
/* Link to next item when used in a linked list. */
struct prkdf_object_s *next;
/* Flags to indicate whether fields are valid. */
unsigned int keygrip_valid:1;
unsigned int key_reference_valid:1;
unsigned int have_off:1;
/* Flag indicating that the corresponding PIN has already been
* verified. */
unsigned int pin_verified:1;
/* The key's usage flags. */
keyusage_flags_t usageflags;
/* The keygrip of the key. This is used as a cache. */
char keygrip[2*KEYGRIP_LEN+1];
/* The Gcrypt algo identifier for the key. It is valid if the
* keygrip is also valid. */
int keyalgo;
/* The length of the key in bits (e.g. for RSA the length of the
* modulus). It is valid if the keygrip is also valid. */
unsigned int keynbits;
/* Malloced CN from the Subject-DN of the corresponding certificate
* or NULL if not known. */
char *common_name;
/* Malloced SerialNumber from the Subject-DN of the corresponding
* certificate or NULL if not known. */
char *serial_number;
/* Length and allocated buffer with the Id of this object. */
size_t objidlen;
unsigned char *objid;
/* Length and allocated buffer with the authId of this object or
NULL if no authID is known. */
size_t authidlen;
unsigned char *authid;
/* The keyReference and a flag telling whether it is valid. */
unsigned long key_reference;
/* The offset and length of the object. They are only valid if
* HAVE_OFF is true otherwise they are set to 0. */
unsigned long off, len;
/* The length of the path as given in the PrKDF and the path itself.
path[0] is the top DF (usually 0x3f00). */
size_t pathlen;
unsigned short path[1];
};
typedef struct prkdf_object_s *prkdf_object_t;
/* This is an object to store information about a Authentication
Object Directory File (AODF) in a format suitable for further
processing by us. To keep memory management, simple we use a linked
list of items; i.e. one such object represents one authentication
object and the list the entire AOKDF. */
struct aodf_object_s
{
/* Link to next item when used in a linked list. */
struct aodf_object_s *next;
/* Flags to indicate whether fields are valid. */
unsigned int have_off:1;
/* Length and allocated buffer with the Id of this object. */
size_t objidlen;
unsigned char *objid;
/* Length and allocated buffer with the authId of this object or
NULL if no authID is known. */
size_t authidlen;
unsigned char *authid;
/* The file ID of this AODF. */
unsigned short fid;
/* The PIN Flags. */
struct
{
unsigned int case_sensitive: 1;
unsigned int local: 1;
unsigned int change_disabled: 1;
unsigned int unblock_disabled: 1;
unsigned int initialized: 1;
unsigned int needs_padding: 1;
unsigned int unblocking_pin: 1;
unsigned int so_pin: 1;
unsigned int disable_allowed: 1;
unsigned int integrity_protected: 1;
unsigned int confidentiality_protected: 1;
unsigned int exchange_ref_data: 1;
} pinflags;
/* The PIN Type. */
pin_type_t pintype;
/* The minimum length of a PIN. */
unsigned long min_length;
/* The stored length of a PIN. */
unsigned long stored_length;
/* The maximum length of a PIN and a flag telling whether it is valid. */
unsigned long max_length;
int max_length_valid;
/* The pinReference and a flag telling whether it is valid. */
unsigned long pin_reference;
int pin_reference_valid;
/* The padChar and a flag telling whether it is valid. */
char pad_char;
int pad_char_valid;
/* The offset and length of the object. They are only valid if
HAVE_OFF is true and set to 0 if HAVE_OFF is false. */
unsigned long off, len;
/* The length of the path as given in the Aodf and the path itself.
path[0] is the top DF (usually 0x3f00). PATH is optional and thus
may be NULL. Malloced.*/
size_t pathlen;
unsigned short *path;
};
typedef struct aodf_object_s *aodf_object_t;
/* Context local to this application. */
struct app_local_s
{
/* The home DF. Note, that we don't yet support a multilevel
hierarchy. Thus we assume this is directly below the MF. */
unsigned short home_df;
/* The type of the card's OS. */
card_type_t card_type;
/* The vendor's product. */
card_product_t card_product;
/* Flag indicating whether we may use direct path selection. */
int direct_path_selection;
/* Structure with the EFIDs of the objects described in the ODF
file. */
struct
{
unsigned short private_keys;
unsigned short public_keys;
unsigned short trusted_public_keys;
unsigned short secret_keys;
unsigned short certificates;
unsigned short trusted_certificates;
unsigned short useful_certificates;
unsigned short data_objects;
unsigned short auth_objects;
} odf;
/* The PKCS#15 serialnumber from EF(TokeiNFo) or NULL. Malloced. */
unsigned char *serialno;
size_t serialnolen;
/* The manufacturerID from the TokenInfo EF. Malloced. */
char *manufacturer_id;
/* Information on all certificates. */
cdf_object_t certificate_info;
/* Information on all trusted certificates. */
cdf_object_t trusted_certificate_info;
/* Information on all useful certificates. */
cdf_object_t useful_certificate_info;
/* Information on all private keys. */
prkdf_object_t private_key_info;
/* Information on all authentication objects. */
aodf_object_t auth_object_info;
};
/*** Local prototypes. ***/
static gpg_error_t keygrip_from_prkdf (app_t app, prkdf_object_t prkdf);
static gpg_error_t readcert_by_cdf (app_t app, cdf_object_t cdf,
unsigned char **r_cert, size_t *r_certlen);
static char *get_dispserialno (app_t app, prkdf_object_t prkdf);
static gpg_error_t do_getattr (app_t app, ctrl_t ctrl, const char *name);
/* Release the CDF object A */
static void
release_cdflist (cdf_object_t a)
{
while (a)
{
cdf_object_t tmp = a->next;
xfree (a->image);
xfree (a->objid);
xfree (a);
a = tmp;
}
}
/* Release the PrKDF object A. */
static void
release_prkdflist (prkdf_object_t a)
{
while (a)
{
prkdf_object_t tmp = a->next;
xfree (a->common_name);
xfree (a->serial_number);
xfree (a->objid);
xfree (a->authid);
xfree (a);
a = tmp;
}
}
/* Release just one aodf object. */
void
release_aodf_object (aodf_object_t a)
{
if (a)
{
xfree (a->objid);
xfree (a->authid);
xfree (a->path);
xfree (a);
}
}
/* Release the AODF list A. */
static void
release_aodflist (aodf_object_t a)
{
while (a)
{
aodf_object_t tmp = a->next;
release_aodf_object (a);
a = tmp;
}
}
/* Release all local resources. */
static void
do_deinit (app_t app)
{
if (app && app->app_local)
{
release_cdflist (app->app_local->certificate_info);
release_cdflist (app->app_local->trusted_certificate_info);
release_cdflist (app->app_local->useful_certificate_info);
release_prkdflist (app->app_local->private_key_info);
release_aodflist (app->app_local->auth_object_info);
xfree (app->app_local->manufacturer_id);
xfree (app->app_local->serialno);
xfree (app->app_local);
app->app_local = NULL;
}
}
/* Do a select and a read for the file with EFID. EFID_DESC is a
desctription of the EF to be used with error messages. On success
BUFFER and BUFLEN contain the entire content of the EF. The caller
must free BUFFER only on success. */
static gpg_error_t
select_and_read_binary (int slot, unsigned short efid, const char *efid_desc,
unsigned char **buffer, size_t *buflen)
{
gpg_error_t err;
err = iso7816_select_file (slot, efid, 0);
if (err)
{
log_error ("p15: error selecting %s (0x%04X): %s\n",
efid_desc, efid, gpg_strerror (err));
return err;
}
err = iso7816_read_binary (slot, 0, 0, buffer, buflen);
if (err)
{
log_error ("p15: error reading %s (0x%04X): %s\n",
efid_desc, efid, gpg_strerror (err));
return err;
}
return 0;
}
/* This function calls select file to read a file using a complete
path which may or may not start at the master file (MF). */
static gpg_error_t
select_ef_by_path (app_t app, const unsigned short *path, size_t pathlen)
{
gpg_error_t err;
int i, j;
if (!pathlen)
return gpg_error (GPG_ERR_INV_VALUE);
if (pathlen && *path != 0x3f00 )
log_error ("p15: warning: relative path selection not yet implemented\n");
if (app->app_local->direct_path_selection)
{
err = iso7816_select_path (app->slot, path+1, pathlen-1);
if (err)
{
log_error ("p15: error selecting path ");
for (j=0; j < pathlen; j++)
log_printf ("%04hX", path[j]);
log_printf (": %s\n", gpg_strerror (err));
return err;
}
}
else
{
/* FIXME: Need code to remember the last PATH so that we can decide
what select commands to send in case the path does not start off
with 3F00. We might also want to use direct path selection if
supported by the card. */
for (i=0; i < pathlen; i++)
{
err = iso7816_select_file (app->slot, path[i], !(i+1 == pathlen));
if (err)
{
log_error ("p15: error selecting part %d from path ", i);
for (j=0; j < pathlen; j++)
log_printf ("%04hX", path[j]);
log_printf (": %s\n", gpg_strerror (err));
return err;
}
}
}
return 0;
}
/* Parse a cert Id string (or a key Id string) and return the binary
object Id string in a newly allocated buffer stored at R_OBJID and
R_OBJIDLEN. On Error NULL will be stored there and an error code
returned. On success caller needs to free the buffer at R_OBJID. */
static gpg_error_t
parse_certid (app_t app, const char *certid,
unsigned char **r_objid, size_t *r_objidlen)
{
char tmpbuf[10];
const char *s;
size_t objidlen;
unsigned char *objid;
int i;
*r_objid = NULL;
*r_objidlen = 0;
if (certid[0] != 'P' && strlen (certid) == 40) /* This is a keygrip. */
{
prkdf_object_t prkdf;
for (prkdf = app->app_local->private_key_info;
prkdf; prkdf = prkdf->next)
if (!keygrip_from_prkdf (app, prkdf)
&& !strcmp (certid, prkdf->keygrip))
break;
if (!prkdf || !prkdf->objidlen || !prkdf->objid)
return gpg_error (GPG_ERR_NOT_FOUND);
objidlen = prkdf->objidlen;
objid = xtrymalloc (objidlen);
if (!objid)
return gpg_error_from_syserror ();
memcpy (objid, prkdf->objid, prkdf->objidlen);
}
else /* This is a usual keyref. */
{
if (app->app_local->home_df)
snprintf (tmpbuf, sizeof tmpbuf, "P15-%04X.",
(unsigned int)(app->app_local->home_df & 0xffff));
else
strcpy (tmpbuf, "P15.");
if (strncmp (certid, tmpbuf, strlen (tmpbuf)) )
{
if (!strncmp (certid, "P15.", 4)
|| (!strncmp (certid, "P15-", 4)
&& hexdigitp (certid+4)
&& hexdigitp (certid+5)
&& hexdigitp (certid+6)
&& hexdigitp (certid+7)
&& certid[8] == '.'))
return gpg_error (GPG_ERR_NOT_FOUND);
return gpg_error (GPG_ERR_INV_ID);
}
certid += strlen (tmpbuf);
for (s=certid, objidlen=0; hexdigitp (s); s++, objidlen++)
;
if (*s || !objidlen || (objidlen%2))
return gpg_error (GPG_ERR_INV_ID);
objidlen /= 2;
objid = xtrymalloc (objidlen);
if (!objid)
return gpg_error_from_syserror ();
for (s=certid, i=0; i < objidlen; i++, s+=2)
objid[i] = xtoi_2 (s);
}
*r_objid = objid;
*r_objidlen = objidlen;
return 0;
}
/* Find a certificate object by the certificate ID CERTID and store a
pointer to it at R_CDF. */
static gpg_error_t
cdf_object_from_certid (app_t app, const char *certid, cdf_object_t *r_cdf)
{
gpg_error_t err;
size_t objidlen;
unsigned char *objid;
cdf_object_t cdf;
err = parse_certid (app, certid, &objid, &objidlen);
if (err)
return err;
for (cdf = app->app_local->certificate_info; cdf; cdf = cdf->next)
if (cdf->objidlen == objidlen && !memcmp (cdf->objid, objid, objidlen))
break;
if (!cdf)
for (cdf = app->app_local->trusted_certificate_info; cdf; cdf = cdf->next)
if (cdf->objidlen == objidlen && !memcmp (cdf->objid, objid, objidlen))
break;
if (!cdf)
for (cdf = app->app_local->useful_certificate_info; cdf; cdf = cdf->next)
if (cdf->objidlen == objidlen && !memcmp (cdf->objid, objid, objidlen))
break;
xfree (objid);
if (!cdf)
return gpg_error (GPG_ERR_NOT_FOUND);
*r_cdf = cdf;
return 0;
}
/* Find a private key object by the key Id string KEYIDSTR and store a
pointer to it at R_PRKDF. */
static gpg_error_t
prkdf_object_from_keyidstr (app_t app, const char *keyidstr,
prkdf_object_t *r_prkdf)
{
gpg_error_t err;
size_t objidlen;
unsigned char *objid;
prkdf_object_t prkdf;
err = parse_certid (app, keyidstr, &objid, &objidlen);
if (err)
return err;
for (prkdf = app->app_local->private_key_info; prkdf; prkdf = prkdf->next)
if (prkdf->objidlen == objidlen && !memcmp (prkdf->objid, objid, objidlen))
break;
xfree (objid);
if (!prkdf)
return gpg_error (GPG_ERR_NOT_FOUND);
*r_prkdf = prkdf;
return 0;
}
/* Read and parse the Object Directory File and store away the
pointers. ODF_FID shall contain the FID of the ODF.
Example of such a file:
A0 06 30 04 04 02 60 34 = Private Keys
A4 06 30 04 04 02 60 35 = Certificates
A5 06 30 04 04 02 60 36 = Trusted Certificates
A7 06 30 04 04 02 60 37 = Data Objects
A8 06 30 04 04 02 60 38 = Auth Objects
These are all PathOrObjects using the path CHOICE element. The
paths are octet strings of length 2. Using this Path CHOICE
element is recommended, so we only implement that for now.
*/
static gpg_error_t
read_ef_odf (app_t app, unsigned short odf_fid)
{
gpg_error_t err;
unsigned char *buffer, *p;
size_t buflen, n;
unsigned short value;
size_t offset;
unsigned short home_df = 0;
err = select_and_read_binary (app->slot, odf_fid, "ODF", &buffer, &buflen);
if (err)
return err;
if (buflen < 8)
{
log_error ("p15: error: ODF too short\n");
xfree (buffer);
return gpg_error (GPG_ERR_INV_OBJ);
}
home_df = app->app_local->home_df;
p = buffer;
while (buflen && *p && *p != 0xff)
{
if ( buflen >= 8
&& (p[0] & 0xf0) == 0xA0
&& !memcmp (p+1, "\x06\x30\x04\x04\x02", 5) )
{
offset = 6;
}
else if ( buflen >= 12
&& (p[0] & 0xf0) == 0xA0
&& !memcmp (p+1, "\x0a\x30\x08\x04\x06\x3F\x00", 7)
&& (!home_df || home_df == ((p[8]<<8)|p[9])) )
{
/* If we do not know the home DF, we take it from the first
* ODF object. Here are sample values:
* a0 0a 30 08 0406 3f00 5015 4401
* a1 0a 30 08 0406 3f00 5015 4411
* a4 0a 30 08 0406 3f00 5015 4441
* a5 0a 30 08 0406 3f00 5015 4451
* a8 0a 30 08 0406 3f00 5015 4481
* 00000000 */
if (!home_df)
{
home_df = ((p[8]<<8)|p[9]);
app->app_local->home_df = home_df;
log_info ("p15: application directory detected as 0x%04hX\n",
home_df);
/* We assume that direct path selection is possible. */
app->app_local->direct_path_selection = 1;
}
/* We only allow a full path if all files are at the same
level and below the home directory. To extend this we
would need to make use of new data type capable of
keeping a full path. */
offset = 10;
}
else
{
- log_printhex ("p15: ODF format not supported:", p, buflen);
+ log_printhex (p, buflen, "p15: ODF format not supported:");
xfree (buffer);
return gpg_error (GPG_ERR_INV_OBJ);
}
switch ((p[0] & 0x0f))
{
case 0: value = app->app_local->odf.private_keys; break;
case 1: value = app->app_local->odf.public_keys; break;
case 2: value = app->app_local->odf.trusted_public_keys; break;
case 3: value = app->app_local->odf.secret_keys; break;
case 4: value = app->app_local->odf.certificates; break;
case 5: value = app->app_local->odf.trusted_certificates; break;
case 6: value = app->app_local->odf.useful_certificates; break;
case 7: value = app->app_local->odf.data_objects; break;
case 8: value = app->app_local->odf.auth_objects; break;
default: value = 0; break;
}
if (value)
{
log_error ("p15: duplicate object type %d in ODF ignored\n",
(p[0]&0x0f));
continue;
}
value = ((p[offset] << 8) | p[offset+1]);
switch ((p[0] & 0x0f))
{
case 0: app->app_local->odf.private_keys = value; break;
case 1: app->app_local->odf.public_keys = value; break;
case 2: app->app_local->odf.trusted_public_keys = value; break;
case 3: app->app_local->odf.secret_keys = value; break;
case 4: app->app_local->odf.certificates = value; break;
case 5: app->app_local->odf.trusted_certificates = value; break;
case 6: app->app_local->odf.useful_certificates = value; break;
case 7: app->app_local->odf.data_objects = value; break;
case 8: app->app_local->odf.auth_objects = value; break;
default:
log_error ("p15: unknown object type %d in ODF ignored\n",
(p[0]&0x0f));
}
offset += 2;
if (buflen < offset)
break;
p += offset;
buflen -= offset;
}
if (buflen)
{
/* Print a warning if non-null garbage is left over. */
for (n=0; n < buflen && !p[n]; n++)
;
if (n < buflen)
{
log_info ("p15: warning: garbage detected at end of ODF: ");
- log_printhex ("", p, buflen);
+ log_printhex (p, buflen, "");
}
}
xfree (buffer);
return 0;
}
/* Parse the BIT STRING with the keyUsageFlags from the
CommonKeyAttributes. */
static gpg_error_t
parse_keyusage_flags (const unsigned char *der, size_t derlen,
keyusage_flags_t *usageflags)
{
unsigned int bits, mask;
int i, unused, full;
memset (usageflags, 0, sizeof *usageflags);
if (!derlen)
return gpg_error (GPG_ERR_INV_OBJ);
unused = *der++; derlen--;
if ((!derlen && unused) || unused/8 > derlen)
return gpg_error (GPG_ERR_ENCODING_PROBLEM);
full = derlen - (unused+7)/8;
unused %= 8;
mask = 0;
for (i=1; unused; i <<= 1, unused--)
mask |= i;
/* First octet */
if (derlen)
{
bits = *der++; derlen--;
if (full)
full--;
else
{
bits &= ~mask;
mask = 0;
}
}
else
bits = 0;
if ((bits & 0x80)) usageflags->encrypt = 1;
if ((bits & 0x40)) usageflags->decrypt = 1;
if ((bits & 0x20)) usageflags->sign = 1;
if ((bits & 0x10)) usageflags->sign_recover = 1;
if ((bits & 0x08)) usageflags->wrap = 1;
if ((bits & 0x04)) usageflags->unwrap = 1;
if ((bits & 0x02)) usageflags->verify = 1;
if ((bits & 0x01)) usageflags->verify_recover = 1;
/* Second octet. */
if (derlen)
{
bits = *der++; derlen--;
if (full)
full--;
else
{
bits &= ~mask;
}
}
else
bits = 0;
if ((bits & 0x80)) usageflags->derive = 1;
if ((bits & 0x40)) usageflags->non_repudiation = 1;
return 0;
}
/* Read and parse the Private Key Directory Files. */
/*
6034 (privatekeys)
30 33 30 11 0C 08 53 4B 2E 43 48 2E 44 53 03 02 030...SK.CH.DS..
06 80 04 01 07 30 0C 04 01 01 03 03 06 00 40 02 .....0........@.
02 00 50 A1 10 30 0E 30 08 04 06 3F 00 40 16 00 ..P..0.0...?.@..
50 02 02 04 00 30 33 30 11 0C 08 53 4B 2E 43 48 P....030...SK.CH
2E 4B 45 03 02 06 80 04 01 0A 30 0C 04 01 0C 03 .KE.......0.....
03 06 44 00 02 02 00 52 A1 10 30 0E 30 08 04 06 ..D....R..0.0...
3F 00 40 16 00 52 02 02 04 00 30 34 30 12 0C 09 ?.@..R....040...
53 4B 2E 43 48 2E 41 55 54 03 02 06 80 04 01 0A SK.CH.AUT.......
30 0C 04 01 0D 03 03 06 20 00 02 02 00 51 A1 10 0....... ....Q..
30 0E 30 08 04 06 3F 00 40 16 00 51 02 02 04 00 0.0...?.@..Q....
30 37 30 15 0C 0C 53 4B 2E 43 48 2E 44 53 2D 53 070...SK.CH.DS-S
50 58 03 02 06 80 04 01 0A 30 0C 04 01 02 03 03 PX.......0......
06 20 00 02 02 00 53 A1 10 30 0E 30 08 04 06 3F . ....S..0.0...?
00 40 16 00 53 02 02 04 00 00 00 00 00 00 00 00 .@..S...........
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0 30 51: SEQUENCE {
2 30 17: SEQUENCE { -- commonObjectAttributes
4 0C 8: UTF8String 'SK.CH.DS'
14 03 2: BIT STRING 6 unused bits
: '01'B (bit 0)
18 04 1: OCTET STRING --authid
: 07
: }
21 30 12: SEQUENCE { -- commonKeyAttributes
23 04 1: OCTET STRING
: 01
26 03 3: BIT STRING 6 unused bits
: '1000000000'B (bit 9)
31 02 2: INTEGER 80 -- keyReference (optional)
: }
35 A1 16: [1] { -- keyAttributes
37 30 14: SEQUENCE { -- privateRSAKeyAttributes
39 30 8: SEQUENCE { -- objectValue
41 04 6: OCTET STRING --path
: 3F 00 40 16 00 50
: }
49 02 2: INTEGER 1024 -- modulus
: }
: }
: }
*/
static gpg_error_t
read_ef_prkdf (app_t app, unsigned short fid, prkdf_object_t *result)
{
gpg_error_t err;
unsigned char *buffer = NULL;
size_t buflen;
const unsigned char *p;
size_t n, objlen, hdrlen;
int class, tag, constructed, ndef;
prkdf_object_t prkdflist = NULL;
int i;
if (!fid)
return gpg_error (GPG_ERR_NO_DATA); /* No private keys. */
err = select_and_read_binary (app->slot, fid, "PrKDF", &buffer, &buflen);
if (err)
return err;
p = buffer;
n = buflen;
/* FIXME: This shares a LOT of code with read_ef_cdf! */
/* Loop over the records. We stop as soon as we detect a new record
starting with 0x00 or 0xff as these values are commonly used to
pad data blocks and are no valid ASN.1 encoding. */
while (n && *p && *p != 0xff)
{
const unsigned char *pp;
size_t nn;
int where;
const char *errstr = NULL;
prkdf_object_t prkdf = NULL;
unsigned long ul;
const unsigned char *objid;
size_t objidlen;
const unsigned char *authid = NULL;
size_t authidlen = 0;
keyusage_flags_t usageflags;
unsigned long key_reference = 0;
int key_reference_valid = 0;
const char *s;
err = parse_ber_header (&p, &n, &class, &tag, &constructed,
&ndef, &objlen, &hdrlen);
if (!err && (objlen > n || tag != TAG_SEQUENCE))
err = gpg_error (GPG_ERR_INV_OBJ);
if (err)
{
log_error ("p15: error parsing PrKDF record: %s\n",
gpg_strerror (err));
goto leave;
}
pp = p;
nn = objlen;
p += objlen;
n -= objlen;
/* Parse the commonObjectAttributes. */
where = __LINE__;
err = parse_ber_header (&pp, &nn, &class, &tag, &constructed,
&ndef, &objlen, &hdrlen);
if (!err && (objlen > nn || tag != TAG_SEQUENCE))
err = gpg_error (GPG_ERR_INV_OBJ);
if (err)
goto parse_error;
{
const unsigned char *ppp = pp;
size_t nnn = objlen;
pp += objlen;
nn -= objlen;
/* Search the optional AuthId. We need to skip the optional
Label (UTF8STRING) and the optional CommonObjectFlags
(BITSTRING). */
where = __LINE__;
err = parse_ber_header (&ppp, &nnn, &class, &tag, &constructed,
&ndef, &objlen, &hdrlen);
if (!err && (objlen > nnn || class != CLASS_UNIVERSAL))
err = gpg_error (GPG_ERR_INV_OBJ);
if (gpg_err_code (err) == GPG_ERR_EOF)
goto no_authid;
if (err)
goto parse_error;
if (tag == TAG_UTF8_STRING)
{
ppp += objlen; /* Skip the Label. */
nnn -= objlen;
where = __LINE__;
err = parse_ber_header (&ppp, &nnn, &class, &tag, &constructed,
&ndef, &objlen, &hdrlen);
if (!err && (objlen > nnn || class != CLASS_UNIVERSAL))
err = gpg_error (GPG_ERR_INV_OBJ);
if (gpg_err_code (err) == GPG_ERR_EOF)
goto no_authid;
if (err)
goto parse_error;
}
if (tag == TAG_BIT_STRING)
{
ppp += objlen; /* Skip the CommonObjectFlags. */
nnn -= objlen;
where = __LINE__;
err = parse_ber_header (&ppp, &nnn, &class, &tag, &constructed,
&ndef, &objlen, &hdrlen);
if (!err && (objlen > nnn || class != CLASS_UNIVERSAL))
err = gpg_error (GPG_ERR_INV_OBJ);
if (gpg_err_code (err) == GPG_ERR_EOF)
goto no_authid;
if (err)
goto parse_error;
}
if (tag == TAG_OCTET_STRING && objlen)
{
authid = ppp;
authidlen = objlen;
}
no_authid:
;
}
/* Parse the commonKeyAttributes. */
where = __LINE__;
err = parse_ber_header (&pp, &nn, &class, &tag, &constructed,
&ndef, &objlen, &hdrlen);
if (!err && (objlen > nn || tag != TAG_SEQUENCE))
err = gpg_error (GPG_ERR_INV_OBJ);
if (err)
goto parse_error;
{
const unsigned char *ppp = pp;
size_t nnn = objlen;
pp += objlen;
nn -= objlen;
/* Get the Id. */
where = __LINE__;
err = parse_ber_header (&ppp, &nnn, &class, &tag, &constructed,
&ndef, &objlen, &hdrlen);
if (!err && (objlen > nnn
|| class != CLASS_UNIVERSAL || tag != TAG_OCTET_STRING))
err = gpg_error (GPG_ERR_INV_OBJ);
if (err)
goto parse_error;
objid = ppp;
objidlen = objlen;
ppp += objlen;
nnn -= objlen;
/* Get the KeyUsageFlags. */
where = __LINE__;
err = parse_ber_header (&ppp, &nnn, &class, &tag, &constructed,
&ndef, &objlen, &hdrlen);
if (!err && (objlen > nnn
|| class != CLASS_UNIVERSAL || tag != TAG_BIT_STRING))
err = gpg_error (GPG_ERR_INV_OBJ);
if (err)
goto parse_error;
err = parse_keyusage_flags (ppp, objlen, &usageflags);
if (err)
goto parse_error;
ppp += objlen;
nnn -= objlen;
/* Find the keyReference */
where = __LINE__;
err = parse_ber_header (&ppp, &nnn, &class, &tag, &constructed,
&ndef, &objlen, &hdrlen);
if (gpg_err_code (err) == GPG_ERR_EOF)
goto leave_cki;
if (!err && objlen > nnn)
err = gpg_error (GPG_ERR_INV_OBJ);
if (err)
goto parse_error;
if (class == CLASS_UNIVERSAL && tag == TAG_BOOLEAN)
{
/* Skip the native element. */
ppp += objlen;
nnn -= objlen;
err = parse_ber_header (&ppp, &nnn, &class, &tag, &constructed,
&ndef, &objlen, &hdrlen);
if (gpg_err_code (err) == GPG_ERR_EOF)
goto leave_cki;
if (!err && objlen > nnn)
err = gpg_error (GPG_ERR_INV_OBJ);
if (err)
goto parse_error;
}
if (class == CLASS_UNIVERSAL && tag == TAG_BIT_STRING)
{
/* Skip the accessFlags. */
ppp += objlen;
nnn -= objlen;
err = parse_ber_header (&ppp, &nnn, &class, &tag, &constructed,
&ndef, &objlen, &hdrlen);
if (gpg_err_code (err) == GPG_ERR_EOF)
goto leave_cki;
if (!err && objlen > nnn)
err = gpg_error (GPG_ERR_INV_OBJ);
if (err)
goto parse_error;
}
if (class == CLASS_UNIVERSAL && tag == TAG_INTEGER)
{
/* Yep, this is the keyReference. */
for (ul=0; objlen; objlen--)
{
ul <<= 8;
ul |= (*ppp++) & 0xff;
nnn--;
}
key_reference = ul;
key_reference_valid = 1;
}
leave_cki:
;
}
/* Skip subClassAttributes. */
where = __LINE__;
err = parse_ber_header (&pp, &nn, &class, &tag, &constructed,
&ndef, &objlen, &hdrlen);
if (!err && objlen > nn)
err = gpg_error (GPG_ERR_INV_OBJ);
if (err)
goto parse_error;
if (class == CLASS_CONTEXT && tag == 0)
{
pp += objlen;
nn -= objlen;
where = __LINE__;
err = parse_ber_header (&pp, &nn, &class, &tag, &constructed,
&ndef, &objlen, &hdrlen);
}
/* Parse the keyAttributes. */
if (!err && (objlen > nn || class != CLASS_CONTEXT || tag != 1))
err = gpg_error (GPG_ERR_INV_OBJ);
if (err)
goto parse_error;
nn = objlen;
where = __LINE__;
err = parse_ber_header (&pp, &nn, &class, &tag, &constructed,
&ndef, &objlen, &hdrlen);
if (!err && objlen > nn)
err = gpg_error (GPG_ERR_INV_OBJ);
if (err)
goto parse_error;
if (class == CLASS_UNIVERSAL && tag == TAG_SEQUENCE)
; /* RSA */
else if (class == CLASS_CONTEXT)
{
switch (tag)
{
case 0: errstr = "EC key objects are not supported"; break;
case 1: errstr = "DH key objects are not supported"; break;
case 2: errstr = "DSA key objects are not supported"; break;
case 3: errstr = "KEA key objects are not supported"; break;
default: errstr = "unknown privateKeyObject"; break;
}
goto parse_error;
}
else
{
err = gpg_error (GPG_ERR_INV_OBJ);
goto parse_error;
}
nn = objlen;
/* Check that the reference is a Path object. */
where = __LINE__;
err = parse_ber_header (&pp, &nn, &class, &tag, &constructed,
&ndef, &objlen, &hdrlen);
if (!err && objlen > nn)
err = gpg_error (GPG_ERR_INV_OBJ);
if (err)
goto parse_error;
if (class != CLASS_UNIVERSAL || tag != TAG_SEQUENCE)
{
errstr = "unsupported reference type";
goto parse_error;
}
nn = objlen;
/* Parse the Path object. */
where = __LINE__;
err = parse_ber_header (&pp, &nn, &class, &tag, &constructed,
&ndef, &objlen, &hdrlen);
if (!err && objlen > nn)
err = gpg_error (GPG_ERR_INV_OBJ);
if (err)
goto parse_error;
/* Make sure that the next element is a non zero path and of
even length (FID are two bytes each). */
if (class != CLASS_UNIVERSAL || tag != TAG_OCTET_STRING
|| !objlen || (objlen & 1) )
{
errstr = "invalid path reference";
goto parse_error;
}
/* Create a new PrKDF list item. */
prkdf = xtrycalloc (1, (sizeof *prkdf
- sizeof(unsigned short)
+ objlen/2 * sizeof(unsigned short)));
if (!prkdf)
{
err = gpg_error_from_syserror ();
goto leave;
}
prkdf->objidlen = objidlen;
prkdf->objid = xtrymalloc (objidlen);
if (!prkdf->objid)
{
err = gpg_error_from_syserror ();
xfree (prkdf);
goto leave;
}
memcpy (prkdf->objid, objid, objidlen);
if (authid)
{
prkdf->authidlen = authidlen;
prkdf->authid = xtrymalloc (authidlen);
if (!prkdf->authid)
{
err = gpg_error_from_syserror ();
xfree (prkdf->objid);
xfree (prkdf);
goto leave;
}
memcpy (prkdf->authid, authid, authidlen);
}
prkdf->pathlen = objlen/2;
for (i=0; i < prkdf->pathlen; i++, pp += 2, nn -= 2)
prkdf->path[i] = ((pp[0] << 8) | pp[1]);
prkdf->usageflags = usageflags;
prkdf->key_reference = key_reference;
prkdf->key_reference_valid = key_reference_valid;
if (nn)
{
/* An index and length follows. */
prkdf->have_off = 1;
where = __LINE__;
err = parse_ber_header (&pp, &nn, &class, &tag, &constructed,
&ndef, &objlen, &hdrlen);
if (!err && (objlen > nn
|| class != CLASS_UNIVERSAL || tag != TAG_INTEGER))
err = gpg_error (GPG_ERR_INV_OBJ);
if (err)
goto parse_error;
for (ul=0; objlen; objlen--)
{
ul <<= 8;
ul |= (*pp++) & 0xff;
nn--;
}
prkdf->off = ul;
where = __LINE__;
err = parse_ber_header (&pp, &nn, &class, &tag, &constructed,
&ndef, &objlen, &hdrlen);
if (!err && (objlen > nn
|| class != CLASS_CONTEXT || tag != 0))
err = gpg_error (GPG_ERR_INV_OBJ);
if (err)
goto parse_error;
for (ul=0; objlen; objlen--)
{
ul <<= 8;
ul |= (*pp++) & 0xff;
nn--;
}
prkdf->len = ul;
}
if (opt.verbose)
{
log_info ("p15: PrKDF %04hX: id=", fid);
for (i=0; i < prkdf->objidlen; i++)
log_printf ("%02X", prkdf->objid[i]);
log_printf (" path=");
for (i=0; i < prkdf->pathlen; i++)
log_printf ("%s%04hX", i?"/":"",prkdf->path[i]);
if (prkdf->have_off)
log_printf ("[%lu/%lu]", prkdf->off, prkdf->len);
if (prkdf->authid)
{
log_printf (" authid=");
for (i=0; i < prkdf->authidlen; i++)
log_printf ("%02X", prkdf->authid[i]);
}
if (prkdf->key_reference_valid)
log_printf (" keyref=0x%02lX", prkdf->key_reference);
log_info ("p15: usage=");
s = "";
if (prkdf->usageflags.encrypt) log_printf ("%sencrypt", s), s = ",";
if (prkdf->usageflags.decrypt) log_printf ("%sdecrypt", s), s = ",";
if (prkdf->usageflags.sign ) log_printf ("%ssign", s), s = ",";
if (prkdf->usageflags.sign_recover)
log_printf ("%ssign_recover", s), s = ",";
if (prkdf->usageflags.wrap ) log_printf ("%swrap", s), s = ",";
if (prkdf->usageflags.unwrap ) log_printf ("%sunwrap", s), s = ",";
if (prkdf->usageflags.verify ) log_printf ("%sverify", s), s = ",";
if (prkdf->usageflags.verify_recover)
log_printf ("%sverify_recover", s), s = ",";
if (prkdf->usageflags.derive ) log_printf ("%sderive", s), s = ",";
if (prkdf->usageflags.non_repudiation)
log_printf ("%snon_repudiation", s), s = ",";
log_printf ("\n");
}
/* Put it into the list. */
prkdf->next = prkdflist;
prkdflist = prkdf;
prkdf = NULL;
continue; /* Ready. */
parse_error:
log_error ("p15: error parsing PrKDF record (%d): %s - skipped\n",
where, errstr? errstr : gpg_strerror (err));
if (prkdf)
{
xfree (prkdf->objid);
xfree (prkdf->authid);
xfree (prkdf);
}
err = 0;
} /* End looping over all records. */
leave:
xfree (buffer);
if (err)
release_prkdflist (prkdflist);
else
*result = prkdflist;
return err;
}
/* Read and parse the Certificate Directory Files identified by FID.
On success a newlist of CDF object gets stored at RESULT and the
caller is then responsible of releasing this list. On error a
error code is returned and RESULT won't get changed. */
static gpg_error_t
read_ef_cdf (app_t app, unsigned short fid, cdf_object_t *result)
{
gpg_error_t err;
unsigned char *buffer = NULL;
size_t buflen;
const unsigned char *p;
size_t n, objlen, hdrlen;
int class, tag, constructed, ndef;
cdf_object_t cdflist = NULL;
int i;
if (!fid)
return gpg_error (GPG_ERR_NO_DATA); /* No certificates. */
err = select_and_read_binary (app->slot, fid, "CDF", &buffer, &buflen);
if (err)
return err;
p = buffer;
n = buflen;
/* Loop over the records. We stop as soon as we detect a new record
starting with 0x00 or 0xff as these values are commonly used to
pad data blocks and are no valid ASN.1 encoding. */
while (n && *p && *p != 0xff)
{
const unsigned char *pp;
size_t nn;
int where;
const char *errstr = NULL;
cdf_object_t cdf = NULL;
unsigned long ul;
const unsigned char *objid;
size_t objidlen;
err = parse_ber_header (&p, &n, &class, &tag, &constructed,
&ndef, &objlen, &hdrlen);
if (!err && (objlen > n || tag != TAG_SEQUENCE))
err = gpg_error (GPG_ERR_INV_OBJ);
if (err)
{
log_error ("p15: error parsing CDF record: %s\n", gpg_strerror (err));
goto leave;
}
pp = p;
nn = objlen;
p += objlen;
n -= objlen;
/* Skip the commonObjectAttributes. */
where = __LINE__;
err = parse_ber_header (&pp, &nn, &class, &tag, &constructed,
&ndef, &objlen, &hdrlen);
if (!err && (objlen > nn || tag != TAG_SEQUENCE))
err = gpg_error (GPG_ERR_INV_OBJ);
if (err)
goto parse_error;
pp += objlen;
nn -= objlen;
/* Parse the commonCertificateAttributes. */
where = __LINE__;
err = parse_ber_header (&pp, &nn, &class, &tag, &constructed,
&ndef, &objlen, &hdrlen);
if (!err && (objlen > nn || tag != TAG_SEQUENCE))
err = gpg_error (GPG_ERR_INV_OBJ);
if (err)
goto parse_error;
{
const unsigned char *ppp = pp;
size_t nnn = objlen;
pp += objlen;
nn -= objlen;
/* Get the Id. */
where = __LINE__;
err = parse_ber_header (&ppp, &nnn, &class, &tag, &constructed,
&ndef, &objlen, &hdrlen);
if (!err && (objlen > nnn
|| class != CLASS_UNIVERSAL || tag != TAG_OCTET_STRING))
err = gpg_error (GPG_ERR_INV_OBJ);
if (err)
goto parse_error;
objid = ppp;
objidlen = objlen;
}
/* Parse the certAttribute. */
where = __LINE__;
err = parse_ber_header (&pp, &nn, &class, &tag, &constructed,
&ndef, &objlen, &hdrlen);
if (!err && (objlen > nn || class != CLASS_CONTEXT || tag != 1))
err = gpg_error (GPG_ERR_INV_OBJ);
if (err)
goto parse_error;
nn = objlen;
where = __LINE__;
err = parse_ber_header (&pp, &nn, &class, &tag, &constructed,
&ndef, &objlen, &hdrlen);
if (!err && (objlen > nn
|| class != CLASS_UNIVERSAL || tag != TAG_SEQUENCE))
err = gpg_error (GPG_ERR_INV_OBJ);
if (err)
goto parse_error;
nn = objlen;
/* Check that the reference is a Path object. */
where = __LINE__;
err = parse_ber_header (&pp, &nn, &class, &tag, &constructed,
&ndef, &objlen, &hdrlen);
if (!err && objlen > nn)
err = gpg_error (GPG_ERR_INV_OBJ);
if (err)
goto parse_error;
if (class != CLASS_UNIVERSAL || tag != TAG_SEQUENCE)
{
errstr = "unsupported reference type";
goto parse_error;
}
nn = objlen;
/* Parse the Path object. */
where = __LINE__;
err = parse_ber_header (&pp, &nn, &class, &tag, &constructed,
&ndef, &objlen, &hdrlen);
if (!err && objlen > nn)
err = gpg_error (GPG_ERR_INV_OBJ);
if (err)
goto parse_error;
/* Make sure that the next element is a non zero path and of
even length (FID are two bytes each). */
if (class != CLASS_UNIVERSAL || tag != TAG_OCTET_STRING
|| !objlen || (objlen & 1) )
{
errstr = "invalid path reference";
goto parse_error;
}
/* Create a new CDF list item. */
cdf = xtrycalloc (1, (sizeof *cdf
- sizeof(unsigned short)
+ objlen/2 * sizeof(unsigned short)));
if (!cdf)
{
err = gpg_error_from_syserror ();
goto leave;
}
cdf->objidlen = objidlen;
cdf->objid = xtrymalloc (objidlen);
if (!cdf->objid)
{
err = gpg_error_from_syserror ();
xfree (cdf);
goto leave;
}
memcpy (cdf->objid, objid, objidlen);
cdf->pathlen = objlen/2;
for (i=0; i < cdf->pathlen; i++, pp += 2, nn -= 2)
cdf->path[i] = ((pp[0] << 8) | pp[1]);
if (nn)
{
/* An index and length follows. */
cdf->have_off = 1;
where = __LINE__;
err = parse_ber_header (&pp, &nn, &class, &tag, &constructed,
&ndef, &objlen, &hdrlen);
if (!err && (objlen > nn
|| class != CLASS_UNIVERSAL || tag != TAG_INTEGER))
err = gpg_error (GPG_ERR_INV_OBJ);
if (err)
goto parse_error;
for (ul=0; objlen; objlen--)
{
ul <<= 8;
ul |= (*pp++) & 0xff;
nn--;
}
cdf->off = ul;
where = __LINE__;
err = parse_ber_header (&pp, &nn, &class, &tag, &constructed,
&ndef, &objlen, &hdrlen);
if (!err && (objlen > nn
|| class != CLASS_CONTEXT || tag != 0))
err = gpg_error (GPG_ERR_INV_OBJ);
if (err)
goto parse_error;
for (ul=0; objlen; objlen--)
{
ul <<= 8;
ul |= (*pp++) & 0xff;
nn--;
}
cdf->len = ul;
}
if (opt.verbose)
{
log_info ("p15: CDF %04hX: id=", fid);
for (i=0; i < cdf->objidlen; i++)
log_printf ("%02X", cdf->objid[i]);
log_printf (" path=");
for (i=0; i < cdf->pathlen; i++)
log_printf ("%s%04hX", i?"/":"", cdf->path[i]);
if (cdf->have_off)
log_printf ("[%lu/%lu]", cdf->off, cdf->len);
log_printf ("\n");
}
/* Put it into the list. */
cdf->next = cdflist;
cdflist = cdf;
cdf = NULL;
continue; /* Ready. */
parse_error:
log_error ("p15: error parsing CDF record (%d): %s - skipped\n",
where, errstr? errstr : gpg_strerror (err));
xfree (cdf);
err = 0;
} /* End looping over all records. */
leave:
xfree (buffer);
if (err)
release_cdflist (cdflist);
else
*result = cdflist;
return err;
}
/*
SEQUENCE {
SEQUENCE { -- CommonObjectAttributes
UTF8String 'specific PIN for DS'
BIT STRING 0 unused bits
'00000011'B
}
SEQUENCE { -- CommonAuthenticationObjectAttributes
OCTET STRING
07 -- iD
}
[1] { -- typeAttributes
SEQUENCE { -- PinAttributes
BIT STRING 0 unused bits
'0000100000110010'B -- local,initialized,needs-padding
-- exchangeRefData
ENUMERATED 1 -- ascii-numeric
INTEGER 6 -- minLength
INTEGER 6 -- storedLength
INTEGER 8 -- maxLength
[0]
02 -- pinReference
GeneralizedTime 19/04/2002 12:12 GMT -- lastPinChange
SEQUENCE {
OCTET STRING
3F 00 40 16 -- path to DF of PIN
}
}
}
}
*/
/* Read and parse an Authentication Object Directory File identified
by FID. On success a newlist of AODF objects gets stored at RESULT
and the caller is responsible of releasing this list. On error a
error code is returned and RESULT won't get changed. */
static gpg_error_t
read_ef_aodf (app_t app, unsigned short fid, aodf_object_t *result)
{
gpg_error_t err;
unsigned char *buffer = NULL;
size_t buflen;
const unsigned char *p;
size_t n, objlen, hdrlen;
int class, tag, constructed, ndef;
aodf_object_t aodflist = NULL;
int i;
if (!fid)
return gpg_error (GPG_ERR_NO_DATA); /* No authentication objects. */
err = select_and_read_binary (app->slot, fid, "AODF", &buffer, &buflen);
if (err)
return err;
p = buffer;
n = buflen;
/* FIXME: This shares a LOT of code with read_ef_prkdf! */
/* Loop over the records. We stop as soon as we detect a new record
starting with 0x00 or 0xff as these values are commonly used to
pad data blocks and are no valid ASN.1 encoding. */
while (n && *p && *p != 0xff)
{
const unsigned char *pp;
size_t nn;
int where;
const char *errstr = NULL;
aodf_object_t aodf = NULL;
unsigned long ul;
const char *s;
err = parse_ber_header (&p, &n, &class, &tag, &constructed,
&ndef, &objlen, &hdrlen);
if (!err && (objlen > n || tag != TAG_SEQUENCE))
err = gpg_error (GPG_ERR_INV_OBJ);
if (err)
{
log_error ("p15: error parsing AODF record: %s\n",
gpg_strerror (err));
goto leave;
}
pp = p;
nn = objlen;
p += objlen;
n -= objlen;
/* Allocate memory for a new AODF list item. */
aodf = xtrycalloc (1, sizeof *aodf);
if (!aodf)
goto no_core;
aodf->fid = fid;
/* Parse the commonObjectAttributes. */
where = __LINE__;
err = parse_ber_header (&pp, &nn, &class, &tag, &constructed,
&ndef, &objlen, &hdrlen);
if (!err && (objlen > nn || tag != TAG_SEQUENCE))
err = gpg_error (GPG_ERR_INV_OBJ);
if (err)
goto parse_error;
{
const unsigned char *ppp = pp;
size_t nnn = objlen;
pp += objlen;
nn -= objlen;
/* Search the optional AuthId. We need to skip the optional
Label (UTF8STRING) and the optional CommonObjectFlags
(BITSTRING). */
where = __LINE__;
err = parse_ber_header (&ppp, &nnn, &class, &tag, &constructed,
&ndef, &objlen, &hdrlen);
if (!err && (objlen > nnn || class != CLASS_UNIVERSAL))
err = gpg_error (GPG_ERR_INV_OBJ);
if (gpg_err_code (err) == GPG_ERR_EOF)
goto no_authid;
if (err)
goto parse_error;
if (tag == TAG_UTF8_STRING)
{
ppp += objlen; /* Skip the Label. */
nnn -= objlen;
where = __LINE__;
err = parse_ber_header (&ppp, &nnn, &class, &tag, &constructed,
&ndef, &objlen, &hdrlen);
if (!err && (objlen > nnn || class != CLASS_UNIVERSAL))
err = gpg_error (GPG_ERR_INV_OBJ);
if (gpg_err_code (err) == GPG_ERR_EOF)
goto no_authid;
if (err)
goto parse_error;
}
if (tag == TAG_BIT_STRING)
{
ppp += objlen; /* Skip the CommonObjectFlags. */
nnn -= objlen;
where = __LINE__;
err = parse_ber_header (&ppp, &nnn, &class, &tag, &constructed,
&ndef, &objlen, &hdrlen);
if (!err && (objlen > nnn || class != CLASS_UNIVERSAL))
err = gpg_error (GPG_ERR_INV_OBJ);
if (gpg_err_code (err) == GPG_ERR_EOF)
goto no_authid;
if (err)
goto parse_error;
}
if (tag == TAG_OCTET_STRING && objlen)
{
aodf->authidlen = objlen;
aodf->authid = xtrymalloc (objlen);
if (!aodf->authid)
goto no_core;
memcpy (aodf->authid, ppp, objlen);
}
no_authid:
;
}
/* Parse the CommonAuthenticationObjectAttributes. */
where = __LINE__;
err = parse_ber_header (&pp, &nn, &class, &tag, &constructed,
&ndef, &objlen, &hdrlen);
if (!err && (objlen > nn || tag != TAG_SEQUENCE))
err = gpg_error (GPG_ERR_INV_OBJ);
if (err)
goto parse_error;
{
const unsigned char *ppp = pp;
size_t nnn = objlen;
pp += objlen;
nn -= objlen;
/* Get the Id. */
where = __LINE__;
err = parse_ber_header (&ppp, &nnn, &class, &tag, &constructed,
&ndef, &objlen, &hdrlen);
if (!err && (objlen > nnn
|| class != CLASS_UNIVERSAL || tag != TAG_OCTET_STRING))
err = gpg_error (GPG_ERR_INV_OBJ);
if (err)
goto parse_error;
aodf->objidlen = objlen;
aodf->objid = xtrymalloc (objlen);
if (!aodf->objid)
goto no_core;
memcpy (aodf->objid, ppp, objlen);
}
/* Parse the typeAttributes. */
where = __LINE__;
err = parse_ber_header (&pp, &nn, &class, &tag, &constructed,
&ndef, &objlen, &hdrlen);
if (!err && (objlen > nn || class != CLASS_CONTEXT || tag != 1))
err = gpg_error (GPG_ERR_INV_OBJ);
if (err)
goto parse_error;
nn = objlen;
where = __LINE__;
err = parse_ber_header (&pp, &nn, &class, &tag, &constructed,
&ndef, &objlen, &hdrlen);
if (!err && objlen > nn)
err = gpg_error (GPG_ERR_INV_OBJ);
if (err)
goto parse_error;
if (class == CLASS_UNIVERSAL && tag == TAG_SEQUENCE)
; /* PinAttributes */
else if (class == CLASS_CONTEXT)
{
switch (tag)
{
case 0: errstr = "biometric auth types are not supported"; break;
case 1: errstr = "authKey auth types are not supported"; break;
case 2: errstr = "external auth type are not supported"; break;
default: errstr = "unknown privateKeyObject"; break;
}
goto parse_error;
}
else
{
err = gpg_error (GPG_ERR_INV_OBJ);
goto parse_error;
}
nn = objlen;
/* PinFlags */
where = __LINE__;
err = parse_ber_header (&pp, &nn, &class, &tag, &constructed,
&ndef, &objlen, &hdrlen);
if (!err && (objlen > nn || !objlen
|| class != CLASS_UNIVERSAL || tag != TAG_BIT_STRING))
err = gpg_error (GPG_ERR_INV_OBJ);
if (err)
goto parse_error;
{
unsigned int bits, mask;
int unused, full;
unused = *pp++; nn--; objlen--;
if ((!objlen && unused) || unused/8 > objlen)
{
err = gpg_error (GPG_ERR_ENCODING_PROBLEM);
goto parse_error;
}
full = objlen - (unused+7)/8;
unused %= 8;
mask = 0;
for (i=1; unused; i <<= 1, unused--)
mask |= i;
/* The first octet */
bits = 0;
if (objlen)
{
bits = *pp++; nn--; objlen--;
if (full)
full--;
else
{
bits &= ~mask;
mask = 0;
}
}
if ((bits & 0x80)) /* ASN.1 bit 0. */
aodf->pinflags.case_sensitive = 1;
if ((bits & 0x40)) /* ASN.1 bit 1. */
aodf->pinflags.local = 1;
if ((bits & 0x20))
aodf->pinflags.change_disabled = 1;
if ((bits & 0x10))
aodf->pinflags.unblock_disabled = 1;
if ((bits & 0x08))
aodf->pinflags.initialized = 1;
if ((bits & 0x04))
aodf->pinflags.needs_padding = 1;
if ((bits & 0x02))
aodf->pinflags.unblocking_pin = 1;
if ((bits & 0x01))
aodf->pinflags.so_pin = 1;
/* The second octet. */
bits = 0;
if (objlen)
{
bits = *pp++; nn--; objlen--;
if (full)
full--;
else
{
bits &= ~mask;
}
}
if ((bits & 0x80))
aodf->pinflags.disable_allowed = 1;
if ((bits & 0x40))
aodf->pinflags.integrity_protected = 1;
if ((bits & 0x20))
aodf->pinflags.confidentiality_protected = 1;
if ((bits & 0x10))
aodf->pinflags.exchange_ref_data = 1;
/* Skip remaining bits. */
pp += objlen;
nn -= objlen;
}
/* PinType */
where = __LINE__;
err = parse_ber_header (&pp, &nn, &class, &tag, &constructed,
&ndef, &objlen, &hdrlen);
if (!err && (objlen > nn
|| class != CLASS_UNIVERSAL || tag != TAG_ENUMERATED))
err = gpg_error (GPG_ERR_INV_OBJ);
if (!err && objlen > sizeof (ul))
err = gpg_error (GPG_ERR_UNSUPPORTED_ENCODING);
if (err)
goto parse_error;
for (ul=0; objlen; objlen--)
{
ul <<= 8;
ul |= (*pp++) & 0xff;
nn--;
}
aodf->pintype = ul;
/* minLength */
where = __LINE__;
err = parse_ber_header (&pp, &nn, &class, &tag, &constructed,
&ndef, &objlen, &hdrlen);
if (!err && (objlen > nn
|| class != CLASS_UNIVERSAL || tag != TAG_INTEGER))
err = gpg_error (GPG_ERR_INV_OBJ);
if (!err && objlen > sizeof (ul))
err = gpg_error (GPG_ERR_UNSUPPORTED_ENCODING);
if (err)
goto parse_error;
for (ul=0; objlen; objlen--)
{
ul <<= 8;
ul |= (*pp++) & 0xff;
nn--;
}
aodf->min_length = ul;
/* storedLength */
where = __LINE__;
err = parse_ber_header (&pp, &nn, &class, &tag, &constructed,
&ndef, &objlen, &hdrlen);
if (!err && (objlen > nn
|| class != CLASS_UNIVERSAL || tag != TAG_INTEGER))
err = gpg_error (GPG_ERR_INV_OBJ);
if (!err && objlen > sizeof (ul))
err = gpg_error (GPG_ERR_UNSUPPORTED_ENCODING);
if (err)
goto parse_error;
for (ul=0; objlen; objlen--)
{
ul <<= 8;
ul |= (*pp++) & 0xff;
nn--;
}
aodf->stored_length = ul;
/* optional maxLength */
where = __LINE__;
err = parse_ber_header (&pp, &nn, &class, &tag, &constructed,
&ndef, &objlen, &hdrlen);
if (gpg_err_code (err) == GPG_ERR_EOF)
goto ready;
if (!err && objlen > nn)
err = gpg_error (GPG_ERR_INV_OBJ);
if (err)
goto parse_error;
if (class == CLASS_UNIVERSAL && tag == TAG_INTEGER)
{
if (objlen > sizeof (ul))
{
err = gpg_error (GPG_ERR_UNSUPPORTED_ENCODING);
goto parse_error;
}
for (ul=0; objlen; objlen--)
{
ul <<= 8;
ul |= (*pp++) & 0xff;
nn--;
}
aodf->max_length = ul;
aodf->max_length_valid = 1;
where = __LINE__;
err = parse_ber_header (&pp, &nn, &class, &tag, &constructed,
&ndef, &objlen, &hdrlen);
if (gpg_err_code (err) == GPG_ERR_EOF)
goto ready;
if (!err && objlen > nn)
err = gpg_error (GPG_ERR_INV_OBJ);
if (err)
goto parse_error;
}
/* Optional pinReference. */
if (class == CLASS_CONTEXT && tag == 0)
{
if (objlen > sizeof (ul))
{
err = gpg_error (GPG_ERR_UNSUPPORTED_ENCODING);
goto parse_error;
}
for (ul=0; objlen; objlen--)
{
ul <<= 8;
ul |= (*pp++) & 0xff;
nn--;
}
aodf->pin_reference = ul;
aodf->pin_reference_valid = 1;
where = __LINE__;
err = parse_ber_header (&pp, &nn, &class, &tag, &constructed,
&ndef, &objlen, &hdrlen);
if (gpg_err_code (err) == GPG_ERR_EOF)
goto ready;
if (!err && objlen > nn)
err = gpg_error (GPG_ERR_INV_OBJ);
if (err)
goto parse_error;
}
/* Optional padChar. */
if (class == CLASS_UNIVERSAL && tag == TAG_OCTET_STRING)
{
if (objlen != 1)
{
errstr = "padChar is not of size(1)";
goto parse_error;
}
aodf->pad_char = *pp++; nn--;
aodf->pad_char_valid = 1;
where = __LINE__;
err = parse_ber_header (&pp, &nn, &class, &tag, &constructed,
&ndef, &objlen, &hdrlen);
if (gpg_err_code (err) == GPG_ERR_EOF)
goto ready;
if (!err && objlen > nn)
err = gpg_error (GPG_ERR_INV_OBJ);
if (err)
goto parse_error;
}
/* Skip optional lastPinChange. */
if (class == CLASS_UNIVERSAL && tag == TAG_GENERALIZED_TIME)
{
pp += objlen;
nn -= objlen;
where = __LINE__;
err = parse_ber_header (&pp, &nn, &class, &tag, &constructed,
&ndef, &objlen, &hdrlen);
if (gpg_err_code (err) == GPG_ERR_EOF)
goto ready;
if (!err && objlen > nn)
err = gpg_error (GPG_ERR_INV_OBJ);
if (err)
goto parse_error;
}
/* Optional Path object. */
if (class == CLASS_UNIVERSAL || tag == TAG_SEQUENCE)
{
const unsigned char *ppp = pp;
size_t nnn = objlen;
pp += objlen;
nn -= objlen;
where = __LINE__;
err = parse_ber_header (&ppp, &nnn, &class, &tag, &constructed,
&ndef, &objlen, &hdrlen);
if (!err && objlen > nnn)
err = gpg_error (GPG_ERR_INV_OBJ);
if (err)
goto parse_error;
/* Make sure that the next element is a non zero FID and of
even length (FID are two bytes each). */
if (class != CLASS_UNIVERSAL || tag != TAG_OCTET_STRING
|| !objlen || (objlen & 1) )
{
errstr = "invalid path reference";
goto parse_error;
}
aodf->pathlen = objlen/2;
aodf->path = xtrymalloc (aodf->pathlen);
if (!aodf->path)
goto no_core;
for (i=0; i < aodf->pathlen; i++, ppp += 2, nnn -= 2)
aodf->path[i] = ((ppp[0] << 8) | ppp[1]);
if (nnn)
{
/* An index and length follows. */
aodf->have_off = 1;
where = __LINE__;
err = parse_ber_header (&ppp, &nnn, &class, &tag, &constructed,
&ndef, &objlen, &hdrlen);
if (!err && (objlen > nnn
|| class != CLASS_UNIVERSAL || tag != TAG_INTEGER))
err = gpg_error (GPG_ERR_INV_OBJ);
if (err)
goto parse_error;
for (ul=0; objlen; objlen--)
{
ul <<= 8;
ul |= (*ppp++) & 0xff;
nnn--;
}
aodf->off = ul;
where = __LINE__;
err = parse_ber_header (&ppp, &nnn, &class, &tag, &constructed,
&ndef, &objlen, &hdrlen);
if (!err && (objlen > nnn
|| class != CLASS_CONTEXT || tag != 0))
err = gpg_error (GPG_ERR_INV_OBJ);
if (err)
goto parse_error;
for (ul=0; objlen; objlen--)
{
ul <<= 8;
ul |= (*ppp++) & 0xff;
nnn--;
}
aodf->len = ul;
}
}
/* Igonore further objects which might be there due to future
extensions of pkcs#15. */
ready:
if (opt.verbose)
{
log_info ("p15: AODF %04hX: id=", fid);
for (i=0; i < aodf->objidlen; i++)
log_printf ("%02X", aodf->objid[i]);
if (aodf->authid)
{
log_printf (" authid=");
for (i=0; i < aodf->authidlen; i++)
log_printf ("%02X", aodf->authid[i]);
}
if (aodf->pin_reference_valid)
log_printf (" pinref=0x%02lX", aodf->pin_reference);
if (aodf->pathlen)
{
log_printf (" path=");
for (i=0; i < aodf->pathlen; i++)
log_printf ("%s%04hX", i?"/":"",aodf->path[i]);
if (aodf->have_off)
log_printf ("[%lu/%lu]", aodf->off, aodf->len);
}
log_printf (" min=%lu", aodf->min_length);
log_printf (" stored=%lu", aodf->stored_length);
if (aodf->max_length_valid)
log_printf (" max=%lu", aodf->max_length);
if (aodf->pad_char_valid)
log_printf (" pad=0x%02x", aodf->pad_char);
log_info ("p15: flags=");
s = "";
if (aodf->pinflags.case_sensitive)
log_printf ("%scase_sensitive", s), s = ",";
if (aodf->pinflags.local)
log_printf ("%slocal", s), s = ",";
if (aodf->pinflags.change_disabled)
log_printf ("%schange_disabled", s), s = ",";
if (aodf->pinflags.unblock_disabled)
log_printf ("%sunblock_disabled", s), s = ",";
if (aodf->pinflags.initialized)
log_printf ("%sinitialized", s), s = ",";
if (aodf->pinflags.needs_padding)
log_printf ("%sneeds_padding", s), s = ",";
if (aodf->pinflags.unblocking_pin)
log_printf ("%sunblocking_pin", s), s = ",";
if (aodf->pinflags.so_pin)
log_printf ("%sso_pin", s), s = ",";
if (aodf->pinflags.disable_allowed)
log_printf ("%sdisable_allowed", s), s = ",";
if (aodf->pinflags.integrity_protected)
log_printf ("%sintegrity_protected", s), s = ",";
if (aodf->pinflags.confidentiality_protected)
log_printf ("%sconfidentiality_protected", s), s = ",";
if (aodf->pinflags.exchange_ref_data)
log_printf ("%sexchange_ref_data", s), s = ",";
{
char numbuf[50];
switch (aodf->pintype)
{
case PIN_TYPE_BCD: s = "bcd"; break;
case PIN_TYPE_ASCII_NUMERIC: s = "ascii-numeric"; break;
case PIN_TYPE_UTF8: s = "utf8"; break;
case PIN_TYPE_HALF_NIBBLE_BCD: s = "half-nibble-bcd"; break;
case PIN_TYPE_ISO9564_1: s = "iso9564-1"; break;
default:
sprintf (numbuf, "%lu", (unsigned long)aodf->pintype);
s = numbuf;
}
log_printf (" type=%s", s);
}
log_printf ("\n");
}
/* Put it into the list. */
aodf->next = aodflist;
aodflist = aodf;
aodf = NULL;
continue; /* Ready. */
no_core:
err = gpg_error_from_syserror ();
release_aodf_object (aodf);
goto leave;
parse_error:
log_error ("p15: error parsing AODF record (%d): %s - skipped\n",
where, errstr? errstr : gpg_strerror (err));
err = 0;
release_aodf_object (aodf);
} /* End looping over all records. */
leave:
xfree (buffer);
if (err)
release_aodflist (aodflist);
else
*result = aodflist;
return err;
}
/* Print the BIT STRING with the tokenflags from the TokenInfo. */
static void
print_tokeninfo_tokenflags (const unsigned char *der, size_t derlen)
{
unsigned int bits, mask;
int i, unused, full;
int other = 0;
if (!derlen)
{
log_printf (" [invalid object]");
return;
}
unused = *der++; derlen--;
if ((!derlen && unused) || unused/8 > derlen)
{
log_printf (" [wrong encoding]");
return;
}
full = derlen - (unused+7)/8;
unused %= 8;
mask = 0;
for (i=1; unused; i <<= 1, unused--)
mask |= i;
/* First octet */
if (derlen)
{
bits = *der++; derlen--;
if (full)
full--;
else
{
bits &= ~mask;
mask = 0;
}
}
else
bits = 0;
if ((bits & 0x80)) log_printf (" readonly");
if ((bits & 0x40)) log_printf (" loginRequired");
if ((bits & 0x20)) log_printf (" prnGeneration");
if ((bits & 0x10)) log_printf (" eidCompliant");
if ((bits & 0x08)) other = 1;
if ((bits & 0x04)) other = 1;
if ((bits & 0x02)) other = 1;
if ((bits & 0x01)) other = 1;
/* Next octet. */
if (derlen)
other = 1;
if (other)
log_printf (" [unknown]");
}
/* Read and parse the EF(TokenInfo).
TokenInfo ::= SEQUENCE {
version INTEGER {v1(0)} (v1,...),
serialNumber OCTET STRING,
manufacturerID Label OPTIONAL,
label [0] Label OPTIONAL,
tokenflags TokenFlags,
seInfo SEQUENCE OF SecurityEnvironmentInfo OPTIONAL,
recordInfo [1] RecordInfo OPTIONAL,
supportedAlgorithms [2] SEQUENCE OF AlgorithmInfo OPTIONAL,
...,
issuerId [3] Label OPTIONAL,
holderId [4] Label OPTIONAL,
lastUpdate [5] LastUpdate OPTIONAL,
preferredLanguage PrintableString OPTIONAL -- In accordance with
-- IETF RFC 1766
} (CONSTRAINED BY { -- Each AlgorithmInfo.reference value must be unique --})
TokenFlags ::= BIT STRING {
readOnly (0),
loginRequired (1),
prnGeneration (2),
eidCompliant (3)
}
5032:
30 31 02 01 00 04 04 05 45 36 9F 0C 0C 44 2D 54 01......E6...D-T
72 75 73 74 20 47 6D 62 48 80 14 4F 66 66 69 63 rust GmbH..Offic
65 20 69 64 65 6E 74 69 74 79 20 63 61 72 64 03 e identity card.
02 00 40 20 63 61 72 64 03 02 00 40 00 00 00 00 ..@ card...@....
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0 49: SEQUENCE {
2 1: INTEGER 0
5 4: OCTET STRING 05 45 36 9F
11 12: UTF8String 'D-Trust GmbH'
25 20: [0] 'Office identity card'
47 2: BIT STRING
: '00000010'B (bit 1)
: Error: Spurious zero bits in bitstring.
: }
*/
static gpg_error_t
read_ef_tokeninfo (app_t app)
{
gpg_error_t err;
unsigned char *buffer = NULL;
size_t buflen;
const unsigned char *p;
size_t n, objlen, hdrlen;
int class, tag, constructed, ndef;
unsigned long ul;
xfree (app->app_local->manufacturer_id);
app->app_local->manufacturer_id = NULL;
app->app_local->card_product = CARD_PRODUCT_UNKNOWN;
err = select_and_read_binary (app->slot, 0x5032, "TokenInfo",
&buffer, &buflen);
if (err)
return err;
p = buffer;
n = buflen;
err = parse_ber_header (&p, &n, &class, &tag, &constructed,
&ndef, &objlen, &hdrlen);
if (!err && (objlen > n || tag != TAG_SEQUENCE))
err = gpg_error (GPG_ERR_INV_OBJ);
if (err)
{
log_error ("p15: error parsing TokenInfo: %s\n", gpg_strerror (err));
goto leave;
}
n = objlen;
/* Version. */
err = parse_ber_header (&p, &n, &class, &tag, &constructed,
&ndef, &objlen, &hdrlen);
if (!err && (objlen > n || tag != TAG_INTEGER))
err = gpg_error (GPG_ERR_INV_OBJ);
if (err)
goto leave;
for (ul=0; objlen; objlen--)
{
ul <<= 8;
ul |= (*p++) & 0xff;
n--;
}
if (ul)
{
log_error ("p15: invalid version %lu in TokenInfo\n", ul);
err = gpg_error (GPG_ERR_INV_OBJ);
goto leave;
}
if (opt.verbose)
log_info ("p15: TokenInfo:\n");
/* serialNumber. */
err = parse_ber_header (&p, &n, &class, &tag, &constructed,
&ndef, &objlen, &hdrlen);
if (!err && (objlen > n || tag != TAG_OCTET_STRING || !objlen))
err = gpg_error (GPG_ERR_INV_OBJ);
if (err)
goto leave;
xfree (app->app_local->serialno);
app->app_local->serialno = xtrymalloc (objlen);
if (!app->app_local->serialno)
{
err = gpg_error_from_syserror ();
goto leave;
}
memcpy (app->app_local->serialno, p, objlen);
app->app_local->serialnolen = objlen;
if (opt.verbose)
{
/* (We use a separate log_info to avoid the "DBG:" prefix.) */
log_info ("p15: serialNumber .: ");
- log_printhex ("", p, objlen);
+ log_printhex (p, objlen, "");
}
p += objlen;
n -= objlen;
/* Is there an optional manufacturerID? */
err = parse_ber_header (&p, &n, &class, &tag, &constructed,
&ndef, &objlen, &hdrlen);
if (!err && (objlen > n || !objlen))
err = gpg_error (GPG_ERR_INV_OBJ);
if (err)
goto leave;
if (class == CLASS_UNIVERSAL && tag == TAG_UTF8_STRING)
{
if (opt.verbose)
log_info ("p15: manufacturerID: %.*s\n", (int)objlen, p);
app->app_local->manufacturer_id = percent_data_escape (0, NULL,
p, objlen);
p += objlen;
n -= objlen;
/* Get next TLV. */
err = parse_ber_header (&p, &n, &class, &tag, &constructed,
&ndef, &objlen, &hdrlen);
if (!err && (objlen > n || !objlen))
err = gpg_error (GPG_ERR_INV_OBJ);
if (err)
goto leave;
}
if (class == CLASS_CONTEXT && tag == 0)
{
if (opt.verbose)
log_info ("p15: label ........: %.*s\n", (int)objlen, p);
if (objlen > 15 && !memcmp (p, "D-TRUST Card V3", 15)
&& app->app_local->card_type == CARD_TYPE_CARDOS_50)
app->app_local->card_product = CARD_PRODUCT_DTRUST;
p += objlen;
n -= objlen;
/* Get next TLV. */
err = parse_ber_header (&p, &n, &class, &tag, &constructed,
&ndef, &objlen, &hdrlen);
if (!err && (objlen > n || !objlen))
err = gpg_error (GPG_ERR_INV_OBJ);
if (err)
goto leave;
}
/* The next is the mandatory tokenflags object. */
if (class == CLASS_UNIVERSAL && tag == TAG_BIT_STRING)
{
if (opt.verbose)
{
log_info ("p15: tokenflags ...:");
print_tokeninfo_tokenflags (p, objlen);
log_printf ("\n");
}
p += objlen;
n -= objlen;
}
leave:
xfree (buffer);
return err;
}
/* Get all the basic information from the pkcs#15 card, check the
structure and initialize our local context. This is used once at
application initialization. */
static gpg_error_t
read_p15_info (app_t app)
{
gpg_error_t err;
if (!read_ef_tokeninfo (app))
{
/* If we don't have a serial number yet but the TokenInfo provides
one, use that. */
if (!app->serialno && app->app_local->serialno)
{
app->serialno = app->app_local->serialno;
app->serialnolen = app->app_local->serialnolen;
app->app_local->serialno = NULL;
app->app_local->serialnolen = 0;
err = app_munge_serialno (app);
if (err)
return err;
}
}
/* Read the ODF so that we know the location of all directory
files. */
/* Fixme: We might need to get a non-standard ODF FID from TokenInfo. */
err = read_ef_odf (app, 0x5031);
if (err)
return err;
/* Read certificate information. */
assert (!app->app_local->certificate_info);
assert (!app->app_local->trusted_certificate_info);
assert (!app->app_local->useful_certificate_info);
err = read_ef_cdf (app, app->app_local->odf.certificates,
&app->app_local->certificate_info);
if (!err || gpg_err_code (err) == GPG_ERR_NO_DATA)
err = read_ef_cdf (app, app->app_local->odf.trusted_certificates,
&app->app_local->trusted_certificate_info);
if (!err || gpg_err_code (err) == GPG_ERR_NO_DATA)
err = read_ef_cdf (app, app->app_local->odf.useful_certificates,
&app->app_local->useful_certificate_info);
if (gpg_err_code (err) == GPG_ERR_NO_DATA)
err = 0;
if (err)
return err;
/* Read information about private keys. */
assert (!app->app_local->private_key_info);
err = read_ef_prkdf (app, app->app_local->odf.private_keys,
&app->app_local->private_key_info);
if (gpg_err_code (err) == GPG_ERR_NO_DATA)
err = 0;
if (err)
return err;
/* Read information about authentication objects. */
assert (!app->app_local->auth_object_info);
err = read_ef_aodf (app, app->app_local->odf.auth_objects,
&app->app_local->auth_object_info);
if (gpg_err_code (err) == GPG_ERR_NO_DATA)
err = 0;
return err;
}
/* Helper to do_learn_status: Send information about all certificates
listed in CERTINFO back. Use CERTTYPE as type of the
certificate. */
static gpg_error_t
send_certinfo (app_t app, ctrl_t ctrl, const char *certtype,
cdf_object_t certinfo)
{
for (; certinfo; certinfo = certinfo->next)
{
char *buf, *p;
buf = xtrymalloc (9 + certinfo->objidlen*2 + 1);
if (!buf)
return gpg_error_from_syserror ();
p = stpcpy (buf, "P15");
if (app->app_local->home_df)
{
snprintf (p, 6, "-%04X",
(unsigned int)(app->app_local->home_df & 0xffff));
p += 5;
}
p = stpcpy (p, ".");
bin2hex (certinfo->objid, certinfo->objidlen, p);
send_status_info (ctrl, "CERTINFO",
certtype, strlen (certtype),
buf, strlen (buf),
NULL, (size_t)0);
xfree (buf);
}
return 0;
}
/* Get the keygrip of the private key object PRKDF. On success the
* keygrip, the algo and the length are stored in the KEYGRIP,
* KEYALGO, and KEYNBITS fields of the PRKDF object. */
static gpg_error_t
keygrip_from_prkdf (app_t app, prkdf_object_t prkdf)
{
gpg_error_t err;
cdf_object_t cdf;
unsigned char *der;
size_t derlen;
ksba_cert_t cert;
gcry_sexp_t s_pkey = NULL;
/* Easy if we got a cached version. */
if (prkdf->keygrip_valid)
return 0;
xfree (prkdf->common_name);
prkdf->common_name = NULL;
xfree (prkdf->serial_number);
prkdf->serial_number = NULL;
/* FIXME: We should check whether a public key directory file and a
matching public key for PRKDF is available. This should make
extraction of the key much easier. My current test card doesn't
have one, so we can only use the fallback solution by looking for
a matching certificate and extract the key from there. */
/* Look for a matching certificate. A certificate matches if the Id
matches the one of the private key info. */
for (cdf = app->app_local->certificate_info; cdf; cdf = cdf->next)
if (cdf->objidlen == prkdf->objidlen
&& !memcmp (cdf->objid, prkdf->objid, prkdf->objidlen))
break;
if (!cdf)
for (cdf = app->app_local->trusted_certificate_info; cdf; cdf = cdf->next)
if (cdf->objidlen == prkdf->objidlen
&& !memcmp (cdf->objid, prkdf->objid, prkdf->objidlen))
break;
if (!cdf)
for (cdf = app->app_local->useful_certificate_info; cdf; cdf = cdf->next)
if (cdf->objidlen == prkdf->objidlen
&& !memcmp (cdf->objid, prkdf->objid, prkdf->objidlen))
break;
if (!cdf)
{
err = gpg_error (GPG_ERR_NOT_FOUND);
goto leave;
}
err = readcert_by_cdf (app, cdf, &der, &derlen);
if (err)
goto leave;
err = ksba_cert_new (&cert);
if (!err)
err = ksba_cert_init_from_mem (cert, der, derlen);
xfree (der);
if (!err)
err = app_help_get_keygrip_string (cert, prkdf->keygrip, &s_pkey);
if (!err)
{
/* Try to get the CN and the SerialNumber from the certificate;
* we use a very simple approach here which should work in many
* cases. Eventually we should add a rfc-2253 parser into
* libksba to make it easier to parse such a string.
*
* First example string:
* "CN=Otto Schily,O=Miniluv,C=DE"
* Second example string:
* "2.5.4.5=#445452323030303236333531,2.5.4.4=#4B6F6368,"
* "2.5.4.42=#5765726E6572,CN=Werner Koch,OU=For testing"
* " purposes only!,O=Testorganisation,C=DE"
*/
char *dn = ksba_cert_get_subject (cert, 0);
if (dn)
{
char *p, *pend, *buf;
p = strstr (dn, "CN=");
if (p && (p==dn || p[-1] == ','))
{
p += 3;
if (!(pend = strchr (p, ',')))
pend = p + strlen (p);
if (pend && pend > p
&& (prkdf->common_name = xtrymalloc ((pend - p) + 1)))
{
memcpy (prkdf->common_name, p, pend-p);
prkdf->common_name[pend-p] = 0;
}
}
p = strstr (dn, "2.5.4.5=#"); /* OID of the SerialNumber */
if (p && (p==dn || p[-1] == ','))
{
p += 9;
if (!(pend = strchr (p, ',')))
pend = p + strlen (p);
if (pend && pend > p
&& (buf = xtrymalloc ((pend - p) + 1)))
{
memcpy (buf, p, pend-p);
buf[pend-p] = 0;
if (!hex2str (buf, buf, strlen (buf)+1, NULL))
xfree (buf); /* Invalid hex encoding. */
else
prkdf->serial_number = buf;
}
}
ksba_free (dn);
}
}
ksba_cert_release (cert);
if (err)
goto leave;
prkdf->keyalgo = get_pk_algo_from_key (s_pkey);
if (!prkdf->keyalgo)
{
err = gpg_error (GPG_ERR_PUBKEY_ALGO);
goto leave;
}
prkdf->keynbits = gcry_pk_get_nbits (s_pkey);
if (!prkdf->keynbits)
{
err = gpg_error (GPG_ERR_PUBKEY_ALGO);
goto leave;
}
prkdf->keygrip_valid = 1; /* Yeah, got everything. */
leave:
gcry_sexp_release (s_pkey);
return err;
}
/* Return a malloced keyref string for PRKDF. Returns NULL on
* malloc failure. */
static char *
keyref_from_prkdf (app_t app, prkdf_object_t prkdf)
{
char *buf, *p;
buf = xtrymalloc (4 + 5 + prkdf->objidlen*2 + 1);
if (!buf)
return NULL;
p = stpcpy (buf, "P15");
if (app->app_local->home_df)
{
snprintf (p, 6, "-%04X",
(unsigned int)(app->app_local->home_df & 0xffff));
p += 5;
}
p = stpcpy (p, ".");
bin2hex (prkdf->objid, prkdf->objidlen, p);
return buf;
}
/* Helper to do_learn_status: Send information about all known
keypairs back. FIXME: much code duplication from
send_certinfo(). */
static gpg_error_t
send_keypairinfo (app_t app, ctrl_t ctrl, prkdf_object_t prkdf)
{
gpg_error_t err;
for (; prkdf; prkdf = prkdf->next)
{
char *buf;
int j;
buf = keyref_from_prkdf (app, prkdf);
if (!buf)
return gpg_error_from_syserror ();
err = keygrip_from_prkdf (app, prkdf);
if (err)
{
log_error ("p15: error getting keygrip from ");
for (j=0; j < prkdf->pathlen; j++)
log_printf ("%s%04hX", j?"/":"", prkdf->path[j]);
log_printf (": %s\n", gpg_strerror (err));
}
else
{
char usage[5];
size_t usagelen = 0;
if (prkdf->usageflags.sign
|| prkdf->usageflags.sign_recover
|| prkdf->usageflags.non_repudiation)
usage[usagelen++] = 's';
if (prkdf->usageflags.sign
|| prkdf->usageflags.sign_recover)
usage[usagelen++] = 'c';
if (prkdf->usageflags.decrypt
|| prkdf->usageflags.unwrap)
usage[usagelen++] = 'e';
if (prkdf->usageflags.sign
|| prkdf->usageflags.sign_recover)
usage[usagelen++] = 'a';
log_assert (strlen (prkdf->keygrip) == 40);
send_status_info (ctrl, "KEYPAIRINFO",
prkdf->keygrip, 2*KEYGRIP_LEN,
buf, strlen (buf),
usage, usagelen,
NULL, (size_t)0);
}
xfree (buf);
}
return 0;
}
/* This is the handler for the LEARN command. */
static gpg_error_t
do_learn_status (app_t app, ctrl_t ctrl, unsigned int flags)
{
gpg_error_t err;
if ((flags & 1))
err = 0;
else
{
err = do_getattr (app, ctrl, "MANUFACTURER");
if (!err)
err = send_certinfo (app, ctrl, "100",
app->app_local->certificate_info);
if (!err)
err = send_certinfo (app, ctrl, "101",
app->app_local->trusted_certificate_info);
if (!err)
err = send_certinfo (app, ctrl, "102",
app->app_local->useful_certificate_info);
}
if (!err)
err = send_keypairinfo (app, ctrl, app->app_local->private_key_info);
return err;
}
/* Read a certifciate using the information in CDF and return the
certificate in a newly llocated buffer R_CERT and its length
R_CERTLEN. */
static gpg_error_t
readcert_by_cdf (app_t app, cdf_object_t cdf,
unsigned char **r_cert, size_t *r_certlen)
{
gpg_error_t err;
unsigned char *buffer = NULL;
const unsigned char *p, *save_p;
size_t buflen, n;
int class, tag, constructed, ndef;
size_t totobjlen, objlen, hdrlen;
int rootca;
int i;
*r_cert = NULL;
*r_certlen = 0;
/* First check whether it has been cached. */
if (cdf->image)
{
*r_cert = xtrymalloc (cdf->imagelen);
if (!*r_cert)
return gpg_error_from_syserror ();
memcpy (*r_cert, cdf->image, cdf->imagelen);
*r_certlen = cdf->imagelen;
return 0;
}
/* Read the entire file. fixme: This could be optimized by first
reading the header to figure out how long the certificate
actually is. */
err = select_ef_by_path (app, cdf->path, cdf->pathlen);
if (err)
goto leave;
err = iso7816_read_binary_ext (app_get_slot (app), 1, cdf->off, cdf->len,
&buffer, &buflen);
if (!err && (!buflen || *buffer == 0xff))
err = gpg_error (GPG_ERR_NOT_FOUND);
if (err)
{
log_error ("p15: error reading certificate id=");
for (i=0; i < cdf->objidlen; i++)
log_printf ("%02X", cdf->objid[i]);
log_printf (" at ");
for (i=0; i < cdf->pathlen; i++)
log_printf ("%s%04hX", i? "/":"", cdf->path[i]);
log_printf (": %s\n", gpg_strerror (err));
goto leave;
}
/* Check whether this is really a certificate. */
p = buffer;
n = buflen;
err = parse_ber_header (&p, &n, &class, &tag, &constructed,
&ndef, &objlen, &hdrlen);
if (err)
goto leave;
if (class == CLASS_UNIVERSAL && tag == TAG_SEQUENCE && constructed)
rootca = 0;
else if ( class == CLASS_UNIVERSAL && tag == TAG_SET && constructed )
rootca = 1;
else
{
err = gpg_error (GPG_ERR_INV_OBJ);
goto leave;
}
totobjlen = objlen + hdrlen;
assert (totobjlen <= buflen);
err = parse_ber_header (&p, &n, &class, &tag, &constructed,
&ndef, &objlen, &hdrlen);
if (err)
goto leave;
if (!rootca
&& class == CLASS_UNIVERSAL && tag == TAG_OBJECT_ID && !constructed)
{
/* The certificate seems to be contained in a userCertificate
container. Skip this and assume the following sequence is
the certificate. */
if (n < objlen)
{
err = gpg_error (GPG_ERR_INV_OBJ);
goto leave;
}
p += objlen;
n -= objlen;
save_p = p;
err = parse_ber_header (&p, &n, &class, &tag, &constructed,
&ndef, &objlen, &hdrlen);
if (err)
goto leave;
if ( !(class == CLASS_UNIVERSAL && tag == TAG_SEQUENCE && constructed) )
{
err = gpg_error (GPG_ERR_INV_OBJ);
goto leave;
}
totobjlen = objlen + hdrlen;
assert (save_p + totobjlen <= buffer + buflen);
memmove (buffer, save_p, totobjlen);
}
*r_cert = buffer;
buffer = NULL;
*r_certlen = totobjlen;
/* Try to cache it. */
if (!cdf->image && (cdf->image = xtrymalloc (*r_certlen)))
{
memcpy (cdf->image, *r_cert, *r_certlen);
cdf->imagelen = *r_certlen;
}
leave:
xfree (buffer);
return err;
}
/* Handler for the READCERT command.
Read the certificate with id CERTID (as returned by learn_status in
the CERTINFO status lines) and return it in the freshly allocated
buffer to be stored at R_CERT and its length at R_CERTLEN. A error
code will be returned on failure and R_CERT and R_CERTLEN will be
set to (NULL,0). */
static gpg_error_t
do_readcert (app_t app, const char *certid,
unsigned char **r_cert, size_t *r_certlen)
{
gpg_error_t err;
cdf_object_t cdf;
*r_cert = NULL;
*r_certlen = 0;
err = cdf_object_from_certid (app, certid, &cdf);
if (!err)
err = readcert_by_cdf (app, cdf, r_cert, r_certlen);
return err;
}
/* 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)
{
gpg_error_t err;
prkdf_object_t prkdf;
if (!strcmp (name, "$AUTHKEYID")
|| !strcmp (name, "$ENCRKEYID")
|| !strcmp (name, "$SIGNKEYID"))
{
char *buf;
/* We return the ID of the first private key capable of the
* requested action. Note that we do not yet return
* non_repudiation keys for $SIGNKEYID because our D-Trust
* testcard uses rsaPSS, which is not supported by gpgsm and not
* covered by the VS-NfD approval. */
for (prkdf = app->app_local->private_key_info; prkdf;
prkdf = prkdf->next)
{
if (name[1] == 'A' && (prkdf->usageflags.sign
|| prkdf->usageflags.sign_recover))
break;
else if (name[1] == 'E' && (prkdf->usageflags.decrypt
|| prkdf->usageflags.unwrap))
break;
else if (name[1] == 'S' && (prkdf->usageflags.sign
|| prkdf->usageflags.sign_recover))
break;
}
if (prkdf)
{
buf = keyref_from_prkdf (app, prkdf);
if (!buf)
return gpg_error_from_syserror ();
send_status_info (ctrl, name, buf, strlen (buf), NULL, 0);
xfree (buf);
}
return 0;
}
else if (!strcmp (name, "$DISPSERIALNO"))
{
/* For certain cards we return special IDs. There is no
general rule for it so we need to decide case by case. */
if (app->app_local->card_type == CARD_TYPE_BELPIC)
{
/* The eID card has a card number printed on the front matter
which seems to be a good indication. */
unsigned char *buffer;
const unsigned char *p;
size_t buflen, n;
unsigned short path[] = { 0x3F00, 0xDF01, 0x4031 };
err = select_ef_by_path (app, path, DIM(path) );
if (!err)
err = iso7816_read_binary (app->slot, 0, 0, &buffer, &buflen);
if (err)
{
log_error ("p15: error accessing EF(ID): %s\n",
gpg_strerror (err));
return err;
}
p = find_tlv (buffer, buflen, 1, &n);
if (p && n == 12)
{
char tmp[12+2+1];
memcpy (tmp, p, 3);
tmp[3] = '-';
memcpy (tmp+4, p+3, 7);
tmp[11] = '-';
memcpy (tmp+12, p+10, 2);
tmp[14] = 0;
send_status_info (ctrl, name, tmp, strlen (tmp), NULL, 0);
xfree (buffer);
return 0;
}
xfree (buffer);
}
else
{
/* We use the first private key object which has a serial
* number set. If none was found, we parse the first
* object and see whether this has then a serial number. */
for (prkdf = app->app_local->private_key_info; prkdf;
prkdf = prkdf->next)
if (prkdf->serial_number)
break;
if (!prkdf && app->app_local->private_key_info)
{
prkdf = app->app_local->private_key_info;
keygrip_from_prkdf (app, prkdf);
if (!prkdf->serial_number)
prkdf = NULL;
}
if (prkdf)
{
char *sn = get_dispserialno (app, prkdf);
/* Unless there is a bogus S/N in the cert we should
* have a suitable one from the cert here now. */
err = send_status_printf (ctrl, name, "%s", sn);
xfree (sn);
return err;
}
}
/* No abbreviated serial number. */
}
else if (!strcmp (name, "MANUFACTURER"))
{
if (app->app_local->manufacturer_id)
return send_status_printf (ctrl, "MANUFACTURER", "0 %s",
app->app_local->manufacturer_id);
else
return 0;
}
return gpg_error (GPG_ERR_INV_NAME);
}
/* Micardo cards require special treatment. This is a helper for the
crypto functions to manage the security environment. We expect that
the key file has already been selected. FID is the one of the
selected key. */
static gpg_error_t
micardo_mse (app_t app, unsigned short fid)
{
gpg_error_t err;
int recno;
unsigned short refdata = 0;
int se_num;
unsigned char msebuf[10];
/* Read the KeyD file containing extra information on keys. */
err = iso7816_select_file (app->slot, 0x0013, 0);
if (err)
{
log_error ("p15: error reading EF_keyD: %s\n", gpg_strerror (err));
return err;
}
for (recno = 1, se_num = -1; ; recno++)
{
unsigned char *buffer;
size_t buflen;
size_t n, nn;
const unsigned char *p, *pp;
err = iso7816_read_record (app->slot, recno, 1, 0, &buffer, &buflen);
if (gpg_err_code (err) == GPG_ERR_NOT_FOUND)
break; /* ready */
if (err)
{
log_error ("p15: error reading EF_keyD record: %s\n",
gpg_strerror (err));
return err;
}
if (opt.verbose)
{
log_info (buffer, buflen, "p15: keyD record: ");
- log_printhex ("", buffer, buflen);
+ log_printhex (buffer, buflen, "");
}
p = find_tlv (buffer, buflen, 0x83, &n);
if (p && n == 4 && ((p[2]<<8)|p[3]) == fid)
{
refdata = ((p[0]<<8)|p[1]);
/* Locate the SE DO and the there included sec env number. */
p = find_tlv (buffer, buflen, 0x7b, &n);
if (p && n)
{
pp = find_tlv (p, n, 0x80, &nn);
if (pp && nn == 1)
{
se_num = *pp;
xfree (buffer);
break; /* found. */
}
}
}
xfree (buffer);
}
if (se_num == -1)
{
log_error ("p15: CRT for keyfile %04hX not found\n", fid);
return gpg_error (GPG_ERR_NOT_FOUND);
}
/* Restore the security environment to SE_NUM if needed */
if (se_num)
{
err = iso7816_manage_security_env (app->slot, 0xf3, se_num, NULL, 0);
if (err)
{
log_error ("p15: restoring SE to %d failed: %s\n",
se_num, gpg_strerror (err));
return err;
}
}
/* Set the DST reference data. */
msebuf[0] = 0x83;
msebuf[1] = 0x03;
msebuf[2] = 0x80;
msebuf[3] = (refdata >> 8);
msebuf[4] = refdata;
err = iso7816_manage_security_env (app->slot, 0x41, 0xb6, msebuf, 5);
if (err)
{
log_error ("p15: setting SE to reference file %04hX failed: %s\n",
refdata, gpg_strerror (err));
return err;
}
return 0;
}
/* Prepare the verification of the PIN for the key PRKDF by checking
* the AODF and selecting the key file. KEYREF is used for error
* messages. */
static gpg_error_t
prepare_verify_pin (app_t app, const char *keyref,
prkdf_object_t prkdf, aodf_object_t aodf)
{
gpg_error_t err;
int i;
if (opt.verbose)
{
log_info ("p15: using AODF %04hX id=", aodf->fid);
for (i=0; i < aodf->objidlen; i++)
log_printf ("%02X", aodf->objid[i]);
log_printf ("\n");
}
if (aodf->authid && opt.verbose)
log_info ("p15: PIN is controlled by another authentication token\n");
if (aodf->pinflags.integrity_protected
|| aodf->pinflags.confidentiality_protected)
{
log_error ("p15: "
"PIN verification requires unsupported protection method\n");
return gpg_error (GPG_ERR_BAD_PIN_METHOD);
}
if (!aodf->stored_length && aodf->pinflags.needs_padding)
{
log_error ("p15: "
"PIN verification requires padding but no length known\n");
return gpg_error (GPG_ERR_INV_CARD);
}
if (app->app_local->card_product == CARD_PRODUCT_DTRUST)
{
/* According to our protocol analysis we need to select a
* special AID here. Before that the master file needs to be
* selected. (RID A000000167 is assigned to IBM) */
static char const dtrust_aid[] =
{ 0xA0, 0x00, 0x00, 0x01, 0x67, 0x45, 0x53, 0x49, 0x47, 0x4E };
err = iso7816_select_mf (app_get_slot (app));
if (!err)
err = iso7816_select_application (app_get_slot (app),
dtrust_aid, sizeof dtrust_aid, 0);
if (err)
log_error ("p15: error selecting D-TRUST's AID for key %s: %s\n",
keyref, gpg_strerror (err));
}
else
{
/* Standard case: Select the key file. Note that this may
* change the security environment thus we need to do it before
* PIN verification. */
err = select_ef_by_path (app, prkdf->path, prkdf->pathlen);
if (err)
log_error ("p15: error selecting file for key %s: %s\n",
keyref, gpg_strerror (err));
}
return err;
}
static int
any_control_or_space (const char *string)
{
const unsigned char *s;
for (s = string; *string; string++)
if (*s <= 0x20 || *s >= 0x7f)
return 1;
return 0;
}
/* Return a malloced serial number to be shown to the user. PRKDF is
* used to get it from a certificate; PRKDF may be NULL. */
static char *
get_dispserialno (app_t app, prkdf_object_t prkdf)
{
char *serial;
/* We prefer the SerialNumber RDN from the Subject-DN but we don't
* use it if it features a percent sign (special character in pin
* prompts) or has any control character. */
if (prkdf && prkdf->serial_number && *prkdf->serial_number
&& !strchr (prkdf->serial_number, '%')
&& !any_control_or_space (prkdf->serial_number))
{
serial = xtrystrdup (prkdf->serial_number);
}
else
{
serial = app_get_serialno (app);
}
return serial;
}
/* Return an allocated string to be used as prompt. Returns NULL on
* malloc error. */
static char *
make_pin_prompt (app_t app, int remaining, const char *firstline,
prkdf_object_t prkdf)
{
char *serial, *tmpbuf, *result;
serial = get_dispserialno (app, prkdf);
/* TRANSLATORS: Put a \x1f right before a colon. This can be
* used by pinentry to nicely align the names and values. Keep
* the %s at the start and end of the string. */
result = xtryasprintf (_("%s"
"Number\x1f: %s%%0A"
"Holder\x1f: %s"
"%s"),
"\x1e",
serial,
prkdf->common_name? prkdf->common_name: "",
"");
xfree (serial);
if (!result)
return NULL; /* Out of core. */
/* Append a "remaining attempts" info if needed. */
if (remaining != -1 && remaining < 3)
{
char *rembuf;
/* TRANSLATORS: This is the number of remaining attempts to
* enter a PIN. Use %%0A (double-percent,0A) for a linefeed. */
rembuf = xtryasprintf (_("Remaining attempts: %d"), remaining);
if (rembuf)
{
tmpbuf = strconcat (firstline, "%0A%0A", result,
"%0A%0A", rembuf, NULL);
xfree (rembuf);
}
else
tmpbuf = NULL;
xfree (result);
result = tmpbuf;
}
else
{
tmpbuf = strconcat (firstline, "%0A%0A", result, NULL);
xfree (result);
result = tmpbuf;
}
return result;
}
/* Given the private key object PRKDF and its authentication object
* AODF ask for the PIN and verify that PIN. */
static gpg_error_t
verify_pin (app_t app,
gpg_error_t (*pincb)(void*, const char *, char **), void *pincb_arg,
prkdf_object_t prkdf, aodf_object_t aodf)
{
gpg_error_t err;
char *pinvalue;
size_t pinvaluelen;
const char *label;
const char *errstr;
const char *s;
int remaining;
int pin_reference;
int i;
if (!aodf)
return 0;
pin_reference = aodf->pin_reference_valid? aodf->pin_reference : 0;
if (app->app_local->card_type == CARD_TYPE_CARDOS_50)
{
/* We know that this card supports a verify status check. Note
* that in contrast to PIV cards ISO7816_VERIFY_NOT_NEEDED is
* not supported. */
remaining = iso7816_verify_status (app_get_slot (app), pin_reference);
if (remaining < 0)
remaining = -1; /* We don't care about the concrete error. */
if (remaining < 3)
{
if (remaining >= 0)
log_info ("p15: PIN has %d attempts left\n", remaining);
/* On error or if less than 3 better ask. */
prkdf->pin_verified = 0;
}
}
else
remaining = -1; /* Unknown. */
/* Check whether we already verified it. */
if (prkdf->pin_verified)
return 0; /* Already done. */
if (prkdf->usageflags.non_repudiation
&& (app->app_local->card_type == CARD_TYPE_BELPIC
|| app->app_local->card_product == CARD_PRODUCT_DTRUST))
label = _("||Please enter the PIN for the key to create "
"qualified signatures.");
else
label = _("||Please enter the PIN for the standard keys.");
{
char *prompt = make_pin_prompt (app, remaining, label, prkdf);
if (!prompt)
err = gpg_error_from_syserror ();
else
err = pincb (pincb_arg, prompt, &pinvalue);
xfree (prompt);
}
if (err)
{
log_info ("p15: PIN callback returned error: %s\n", gpg_strerror (err));
return err;
}
/* We might need to cope with UTF8 things here. Not sure how
min_length etc. are exactly defined, for now we take them as
a plain octet count. */
if (strlen (pinvalue) < aodf->min_length)
{
log_error ("p15: PIN is too short; minimum length is %lu\n",
aodf->min_length);
err = gpg_error (GPG_ERR_BAD_PIN);
}
else if (aodf->stored_length && strlen (pinvalue) > aodf->stored_length)
{
/* This would otherwise truncate the PIN silently. */
log_error ("p15: PIN is too large; maximum length is %lu\n",
aodf->stored_length);
err = gpg_error (GPG_ERR_BAD_PIN);
}
else if (aodf->max_length_valid && strlen (pinvalue) > aodf->max_length)
{
log_error ("p15: PIN is too large; maximum length is %lu\n",
aodf->max_length);
err = gpg_error (GPG_ERR_BAD_PIN);
}
if (err)
{
xfree (pinvalue);
return err;
}
errstr = NULL;
err = 0;
switch (aodf->pintype)
{
case PIN_TYPE_BCD:
case PIN_TYPE_ASCII_NUMERIC:
for (s=pinvalue; digitp (s); s++)
;
if (*s)
{
errstr = "Non-numeric digits found in PIN";
err = gpg_error (GPG_ERR_BAD_PIN);
}
break;
case PIN_TYPE_UTF8:
break;
case PIN_TYPE_HALF_NIBBLE_BCD:
errstr = "PIN type Half-Nibble-BCD is not supported";
break;
case PIN_TYPE_ISO9564_1:
errstr = "PIN type ISO9564-1 is not supported";
break;
default:
errstr = "Unknown PIN type";
break;
}
if (errstr)
{
log_error ("p15: can't verify PIN: %s\n", errstr);
xfree (pinvalue);
return err? err : gpg_error (GPG_ERR_BAD_PIN_METHOD);
}
if (aodf->pintype == PIN_TYPE_BCD )
{
char *paddedpin;
int ndigits;
for (ndigits=0, s=pinvalue; *s; ndigits++, s++)
;
paddedpin = xtrymalloc (aodf->stored_length+1);
if (!paddedpin)
{
err = gpg_error_from_syserror ();
xfree (pinvalue);
return err;
}
i = 0;
paddedpin[i++] = 0x20 | (ndigits & 0x0f);
for (s=pinvalue; i < aodf->stored_length && *s && s[1]; s = s+2 )
paddedpin[i++] = (((*s - '0') << 4) | ((s[1] - '0') & 0x0f));
if (i < aodf->stored_length && *s)
paddedpin[i++] = (((*s - '0') << 4)
|((aodf->pad_char_valid?aodf->pad_char:0)&0x0f));
if (aodf->pinflags.needs_padding)
{
while (i < aodf->stored_length)
paddedpin[i++] = aodf->pad_char_valid? aodf->pad_char : 0;
}
xfree (pinvalue);
pinvalue = paddedpin;
pinvaluelen = i;
}
else if (aodf->pinflags.needs_padding)
{
char *paddedpin;
paddedpin = xtrymalloc (aodf->stored_length+1);
if (!paddedpin)
{
err = gpg_error_from_syserror ();
xfree (pinvalue);
return err;
}
for (i=0, s=pinvalue; i < aodf->stored_length && *s; i++, s++)
paddedpin[i] = *s;
/* Not sure what padding char to use if none has been set.
For now we use 0x00; maybe a space would be better. */
for (; i < aodf->stored_length; i++)
paddedpin[i] = aodf->pad_char_valid? aodf->pad_char : 0;
paddedpin[i] = 0;
pinvaluelen = i;
xfree (pinvalue);
pinvalue = paddedpin;
}
else
pinvaluelen = strlen (pinvalue);
/* log_printhex (pinvalue, pinvaluelen, */
/* "about to verify with ref %lu pin:", pin_reference); */
err = iso7816_verify (app_get_slot (app), pin_reference,
pinvalue, pinvaluelen);
xfree (pinvalue);
if (err)
{
log_error ("p15: PIN verification failed: %s\n", gpg_strerror (err));
return err;
}
if (opt.verbose)
log_info ("p15: PIN verification succeeded\n");
prkdf->pin_verified = 1;
return 0;
}
/* Handler for the PKSIGN command.
Create the signature and return the allocated result in OUTDATA.
If a PIN is required, the PINCB will be used to ask for the PIN;
that callback should return the PIN in an allocated buffer and
store that as the 3rd argument. */
static gpg_error_t
do_sign (app_t app, const char *keyidstr, int hashalgo,
gpg_error_t (*pincb)(void*, const char *, char **),
void *pincb_arg,
const void *indata, size_t indatalen,
unsigned char **outdata, size_t *outdatalen )
{
static unsigned char sha256_prefix[19] = /* OID: 2.16.840.1.101.3.4.2.1 */
{ 0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48,
0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05, 0x00, 0x04, 0x20 };
static unsigned char sha1_prefix[15] = /* Object ID is 1.3.14.3.2.26 */
{ 0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2b, 0x0e, 0x03,
0x02, 0x1a, 0x05, 0x00, 0x04, 0x14 };
static unsigned char rmd160_prefix[15] = /* Object ID is 1.3.36.3.2.1 */
{ 0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2b, 0x24, 0x03,
0x02, 0x01, 0x05, 0x00, 0x04, 0x14 };
gpg_error_t err;
unsigned char data[32+19]; /* Must be large enough for a SHA-256 digest
* + the largest OID prefix above and also
* fit the 36 bytes of md5sha1. */
prkdf_object_t prkdf; /* The private key object. */
aodf_object_t aodf; /* The associated authentication object. */
int no_data_padding = 0; /* True if the card want the data without padding.*/
int mse_done = 0; /* Set to true if the MSE has been done. */
unsigned int hashlen; /* Length of the hash. */
unsigned int datalen; /* Length of the data to sign (prefix+hash). */
unsigned char *dataptr;
int exmode, le_value;
if (!keyidstr || !*keyidstr)
return gpg_error (GPG_ERR_INV_VALUE);
if (indatalen != 20 && indatalen != 16
&& indatalen != 35 && indatalen != 36
&& indatalen != (32+19))
return gpg_error (GPG_ERR_INV_VALUE);
err = prkdf_object_from_keyidstr (app, keyidstr, &prkdf);
if (err)
return err;
if (!(prkdf->usageflags.sign || prkdf->usageflags.sign_recover
||prkdf->usageflags.non_repudiation))
{
log_error ("p15: key %s may not be used for signing\n", keyidstr);
return gpg_error (GPG_ERR_WRONG_KEY_USAGE);
}
if (!prkdf->authid)
{
log_error ("p15: no authentication object defined for %s\n", keyidstr);
/* fixme: we might want to go ahead and do without PIN
verification. */
return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION);
}
/* Find the authentication object to this private key object. */
for (aodf = app->app_local->auth_object_info; aodf; aodf = aodf->next)
if (aodf->objidlen == prkdf->authidlen
&& !memcmp (aodf->objid, prkdf->authid, prkdf->authidlen))
break;
if (!aodf)
{
log_error ("p15: authentication object for %s missing\n", keyidstr);
return gpg_error (GPG_ERR_INV_CARD);
}
/* We need some more info about the key - get the keygrip to
* populate these fields. */
err = keygrip_from_prkdf (app, prkdf);
if (err)
{
log_error ("p15: keygrip_from_prkdf failed: %s\n", gpg_strerror (err));
return err;
}
/* Prepare PIN verification. This is split so that we can do
* MSE operation for some task after having selected the key file but
* before sending the verify APDU. */
err = prepare_verify_pin (app, keyidstr, prkdf, aodf);
if (err)
return err;
/* Due to the fact that the non-repudiation signature on a BELPIC
card requires a verify immediately before the DSO we set the
MSE before we do the verification. Other cards might also allow
this but I don't want to break anything, thus we do it only
for the BELPIC card here. */
if (app->app_local->card_type == CARD_TYPE_BELPIC)
{
unsigned char mse[5];
mse[0] = 4; /* Length of the template. */
mse[1] = 0x80; /* Algorithm reference tag. */
if (hashalgo == MD_USER_TLS_MD5SHA1)
mse[2] = 0x01; /* Let card do pkcs#1 0xFF padding. */
else
mse[2] = 0x02; /* RSASSA-PKCS1-v1.5 using SHA1. */
mse[3] = 0x84; /* Private key reference tag. */
mse[4] = prkdf->key_reference_valid? prkdf->key_reference : 0x82;
err = iso7816_manage_security_env (app->slot,
0x41, 0xB6,
mse, sizeof mse);
no_data_padding = 1;
mse_done = 1;
}
if (err)
{
log_error ("p15: MSE failed: %s\n", gpg_strerror (err));
return err;
}
/* Now that we have all the information available run the actual PIN
* verification.*/
err = verify_pin (app, pincb, pincb_arg, prkdf, aodf);
if (err)
return err;
/* Prepare the DER object from INDATA. */
if (indatalen == 36)
{
/* No ASN.1 container used. */
if (hashalgo != MD_USER_TLS_MD5SHA1)
return gpg_error (GPG_ERR_UNSUPPORTED_ALGORITHM);
memcpy (data, indata, indatalen);
datalen = hashlen = 36;
}
else if (indatalen == 35)
{
/* Alright, the caller was so kind to send us an already
prepared DER object. Check that it is what we want and that
it matches the hash algorithm. */
if (hashalgo == GCRY_MD_SHA1 && !memcmp (indata, sha1_prefix, 15))
;
else if (hashalgo == GCRY_MD_RMD160
&& !memcmp (indata, rmd160_prefix, 15))
;
else
return gpg_error (GPG_ERR_UNSUPPORTED_ALGORITHM);
memcpy (data, indata, indatalen);
datalen = 35;
hashlen = 20;
}
else if (indatalen == 32 + 19)
{
/* Seems to be a prepared SHA256 DER object. */
if (hashalgo == GCRY_MD_SHA256 && !memcmp (indata, sha256_prefix, 19))
;
else
return gpg_error (GPG_ERR_UNSUPPORTED_ALGORITHM);
memcpy (data, indata, indatalen);
datalen = 51;
hashlen = 32;
}
else
{
/* Need to prepend the prefix. */
if (hashalgo == GCRY_MD_SHA256)
{
memcpy (data, sha256_prefix, 19);
memcpy (data+19, indata, indatalen);
datalen = 51;
hashlen = 32;
}
else if (hashalgo == GCRY_MD_SHA1)
{
memcpy (data, sha1_prefix, 15);
memcpy (data+15, indata, indatalen);
datalen = 35;
hashlen = 20;
}
else if (hashalgo == GCRY_MD_RMD160)
{
memcpy (data, rmd160_prefix, 15);
memcpy (data+15, indata, indatalen);
datalen = 35;
hashlen = 20;
}
else
return gpg_error (GPG_ERR_UNSUPPORTED_ALGORITHM);
}
/* Manage security environment needs to be tweaked for certain cards. */
if (mse_done)
err = 0;
else if (app->app_local->card_type == CARD_TYPE_TCOS)
{
/* TCOS creates signatures always using the local key 0. MSE
may not be used. */
}
else if (app->app_local->card_type == CARD_TYPE_MICARDO)
{
if (!prkdf->pathlen)
err = gpg_error (GPG_ERR_BUG);
else
err = micardo_mse (app, prkdf->path[prkdf->pathlen-1]);
}
else if (prkdf->key_reference_valid)
{
unsigned char mse[3];
mse[0] = 0x84; /* Select asym. key. */
mse[1] = 1;
mse[2] = prkdf->key_reference;
err = iso7816_manage_security_env (app->slot,
0x41, 0xB6,
mse, sizeof mse);
}
if (err)
{
log_error ("p15: MSE failed: %s\n", gpg_strerror (err));
return err;
}
dataptr = data;
if (no_data_padding)
{
dataptr += datalen - hashlen;
datalen = hashlen;
}
if (prkdf->keyalgo == GCRY_PK_RSA && prkdf->keynbits > 2048)
{
exmode = 1;
le_value = prkdf->keynbits / 8;
}
else
{
exmode = 0;
le_value = 0;
}
err = iso7816_compute_ds (app_get_slot (app),
exmode, dataptr, datalen,
le_value, outdata, outdatalen);
return err;
}
/* Handler for the PKAUTH command.
This is basically the same as the PKSIGN command but we first check
that the requested key is suitable for authentication; that is, it
must match the criteria used for the attribute $AUTHKEYID. See
do_sign for calling conventions; there is no HASHALGO, though. */
static gpg_error_t
do_auth (app_t app, const char *keyidstr,
gpg_error_t (*pincb)(void*, const char *, char **),
void *pincb_arg,
const void *indata, size_t indatalen,
unsigned char **outdata, size_t *outdatalen )
{
gpg_error_t err;
prkdf_object_t prkdf;
int algo;
if (!keyidstr || !*keyidstr)
return gpg_error (GPG_ERR_INV_VALUE);
err = prkdf_object_from_keyidstr (app, keyidstr, &prkdf);
if (err)
return err;
if (!prkdf->usageflags.sign)
{
log_error ("p15: key %s may not be used for authentication\n", keyidstr);
return gpg_error (GPG_ERR_WRONG_KEY_USAGE);
}
algo = indatalen == 36? MD_USER_TLS_MD5SHA1 : GCRY_MD_SHA1;
return do_sign (app, keyidstr, algo, pincb, pincb_arg,
indata, indatalen, outdata, outdatalen);
}
/* Handler for the PKDECRYPT command. Decrypt the data in INDATA and
* return the allocated result in OUTDATA. If a PIN is required the
* PINCB will be used to ask for the PIN; it should return the PIN in
* an allocated buffer and put it into PIN. */
static gpg_error_t
do_decipher (app_t app, const char *keyidstr,
gpg_error_t (*pincb)(void*, const char *, char **),
void *pincb_arg,
const void *indata, size_t indatalen,
unsigned char **outdata, size_t *outdatalen,
unsigned int *r_info)
{
gpg_error_t err;
prkdf_object_t prkdf; /* The private key object. */
aodf_object_t aodf; /* The associated authentication object. */
int exmode, le_value, padind;
(void)r_info;
if (!keyidstr || !*keyidstr)
return gpg_error (GPG_ERR_INV_VALUE);
if (!indatalen || !indata || !outdatalen || !outdata)
return gpg_error (GPG_ERR_INV_ARG);
err = prkdf_object_from_keyidstr (app, keyidstr, &prkdf);
if (err)
return err;
if (!(prkdf->usageflags.decrypt || prkdf->usageflags.unwrap))
{
log_error ("p15: key %s may not be used for decruption\n", keyidstr);
return gpg_error (GPG_ERR_WRONG_KEY_USAGE);
}
/* Find the authentication object to this private key object. */
if (!prkdf->authid)
{
log_error ("p15: no authentication object defined for %s\n", keyidstr);
/* fixme: we might want to go ahead and do without PIN
verification. */
return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION);
}
for (aodf = app->app_local->auth_object_info; aodf; aodf = aodf->next)
if (aodf->objidlen == prkdf->authidlen
&& !memcmp (aodf->objid, prkdf->authid, prkdf->authidlen))
break;
if (!aodf)
{
log_error ("p15: authentication object for %s missing\n", keyidstr);
return gpg_error (GPG_ERR_INV_CARD);
}
/* We need some more info about the key - get the keygrip to
* populate these fields. */
err = keygrip_from_prkdf (app, prkdf);
if (err)
{
log_error ("p15: keygrip_from_prkdf failed: %s\n", gpg_strerror (err));
return err;
}
/* Verify the PIN. */
err = prepare_verify_pin (app, keyidstr, prkdf, aodf);
if (!err)
err = verify_pin (app, pincb, pincb_arg, prkdf, aodf);
if (err)
return err;
/* The next is guess work for CardOS. */
if (app->app_local->card_product == CARD_PRODUCT_DTRUST)
{
/* From analyzing an USB trace of a Windows signing application
* we see that the SE is simply reset to 0x14. It seems to be
* sufficient to do this for decryption; signing still works
* with the standard code despite that our trace showed that
* there the SE is restored to 0x09. Note that the special
* D-Trust AID is in any case select by prepare_verify_pin.
*
* Hey, D-Trust please hand over the specs so that you can
* actually sell your cards and we can properly implement it;
* other vendors understand this and do not demand ridiculous
* paper work or complicated procedures to get samples. */
err = iso7816_manage_security_env (app_get_slot (app),
0xF3, 0x14, NULL, 0);
}
else if (prkdf->key_reference_valid)
{
unsigned char mse[6];
/* Note: This works with CardOS but the D-Trust card has the
* problem that the next created signature would be broken. */
mse[0] = 0x80; /* Algorithm reference. */
mse[1] = 1;
mse[2] = 0x0a; /* RSA, no padding. */
mse[3] = 0x84;
mse[4] = 1;
mse[5] = prkdf->key_reference;
err = iso7816_manage_security_env (app_get_slot (app), 0x41, 0xB8,
mse, sizeof mse);
}
/* Check for MSE error. */
if (err)
{
log_error ("p15: MSE failed: %s\n", gpg_strerror (err));
return err;
}
exmode = le_value = 0;
padind = 0;
if (prkdf->keyalgo == GCRY_PK_RSA && prkdf->keynbits > 2048)
{
exmode = 1; /* Extended length w/o a limit. */
le_value = prkdf->keynbits / 8;
}
if (app->app_local->card_product == CARD_PRODUCT_DTRUST)
padind = 0x81;
err = iso7816_decipher (app_get_slot (app), exmode,
indata, indatalen,
le_value, padind,
outdata, outdatalen);
return err;
}
/* Assume that EF(DIR) has been selected. Read its content and figure
out the home EF of pkcs#15. Return that home DF or 0 if not found
and the value at the address of BELPIC indicates whether it was
found by the belpic aid. */
static unsigned short
read_home_df (int slot, int *r_belpic)
{
gpg_error_t err;
unsigned char *buffer;
const unsigned char *p, *pp;
size_t buflen, n, nn;
unsigned short result = 0;
*r_belpic = 0;
err = iso7816_read_binary (slot, 0, 0, &buffer, &buflen);
if (err)
{
log_error ("p15: error reading EF(DIR): %s\n", gpg_strerror (err));
return 0;
}
/* FIXME: We need to scan all records. */
p = find_tlv (buffer, buflen, 0x61, &n);
if (p && n)
{
pp = find_tlv (p, n, 0x4f, &nn);
if (pp && ((nn == sizeof pkcs15_aid && !memcmp (pp, pkcs15_aid, nn))
|| (*r_belpic = (nn == sizeof pkcs15be_aid
&& !memcmp (pp, pkcs15be_aid, nn)))))
{
pp = find_tlv (p, n, 0x50, &nn);
if (pp && opt.verbose)
log_info ("p15: application label from EF(DIR) is '%.*s'\n",
(int)nn, pp);
pp = find_tlv (p, n, 0x51, &nn);
if (pp && nn == 4 && *pp == 0x3f && !pp[1])
{
result = ((pp[2] << 8) | pp[3]);
if (opt.verbose)
log_info ("p15: application directory is 0x%04hX\n", result);
}
}
}
xfree (buffer);
return result;
}
/*
Select the PKCS#15 application on the card in SLOT.
*/
gpg_error_t
app_select_p15 (app_t app)
{
int slot = app->slot;
int rc;
unsigned short def_home_df = 0;
card_type_t card_type = CARD_TYPE_UNKNOWN;
int direct = 0;
int is_belpic = 0;
rc = iso7816_select_application (slot, pkcs15_aid, sizeof pkcs15_aid, 0);
if (rc)
{ /* Not found: Try to locate it from 2F00. We use direct path
selection here because it seems that the Belgian eID card
does only allow for that. Many other cards supports this
selection method too. Note, that we don't use
select_application above for the Belgian card - the call
works but it seems that it does not switch to the correct DF.
Using the 2f02 just works. */
unsigned short path[1] = { 0x2f00 };
rc = iso7816_select_path (slot, path, 1);
if (!rc)
{
direct = 1;
def_home_df = read_home_df (slot, &is_belpic);
if (def_home_df)
{
path[0] = def_home_df;
rc = iso7816_select_path (slot, path, 1);
}
}
}
if (rc)
{ /* Still not found: Try the default DF. */
def_home_df = 0x5015;
rc = iso7816_select_file (slot, def_home_df, 1);
}
if (!rc)
{
/* Determine the type of the card. The general case is to look
it up from the ATR table. For the Belgian eID card we know
it instantly from the AID. */
if (is_belpic)
{
card_type = CARD_TYPE_BELPIC;
}
else
{
unsigned char *atr;
size_t atrlen;
int i;
atr = apdu_get_atr (app->slot, &atrlen);
if (!atr)
rc = gpg_error (GPG_ERR_INV_CARD);
else
{
for (i=0; card_atr_list[i].atrlen; i++)
if (card_atr_list[i].atrlen == atrlen
&& !memcmp (card_atr_list[i].atr, atr, atrlen))
{
card_type = card_atr_list[i].type;
break;
}
xfree (atr);
}
}
}
if (!rc)
{
app->apptype = "P15";
app->app_local = xtrycalloc (1, sizeof *app->app_local);
if (!app->app_local)
{
rc = gpg_error_from_syserror ();
goto leave;
}
/* Set the home DF. Note that we currently can't do that if the
selection via application ID worked. This will store 0 there
instead. FIXME: We either need to figure the home_df via the
DIR file or using the return values from the select file
APDU. */
app->app_local->home_df = def_home_df;
/* Store the card type. FIXME: We might want to put this into
the common APP structure. */
app->app_local->card_type = card_type;
app->app_local->card_product = CARD_PRODUCT_UNKNOWN;
/* Store whether we may and should use direct path selection. */
app->app_local->direct_path_selection = direct;
/* Read basic information and thus check whether this is a real
card. */
rc = read_p15_info (app);
if (rc)
goto leave;
/* Special serial number munging. We need to check for a German
prototype card right here because we need to access to
EF(TokenInfo). We mark such a serial number by the using a
prefix of FF0100. */
if (app->serialnolen == 12
&& !memcmp (app->serialno, "\xD2\x76\0\0\0\0\0\0\0\0\0\0", 12))
{
/* This is a German card with a silly serial number. Try to get
the serial number from the EF(TokenInfo). . */
unsigned char *p;
/* FIXME: actually get it from EF(TokenInfo). */
p = xtrymalloc (3 + app->serialnolen);
if (!p)
rc = gpg_error (gpg_err_code_from_errno (errno));
else
{
memcpy (p, "\xff\x01", 3);
memcpy (p+3, app->serialno, app->serialnolen);
app->serialnolen += 3;
xfree (app->serialno);
app->serialno = p;
}
}
app->fnc.deinit = do_deinit;
app->fnc.learn_status = do_learn_status;
app->fnc.readcert = do_readcert;
app->fnc.getattr = do_getattr;
app->fnc.setattr = NULL;
app->fnc.genkey = NULL;
app->fnc.sign = do_sign;
app->fnc.auth = do_auth;
app->fnc.decipher = do_decipher;
app->fnc.change_pin = NULL;
app->fnc.check_pin = NULL;
leave:
if (rc)
do_deinit (app);
}
return rc;
}
diff --git a/sm/certcheck.c b/sm/certcheck.c
index 5007e479d..14f78dbe6 100644
--- a/sm/certcheck.c
+++ b/sm/certcheck.c
@@ -1,650 +1,650 @@
/* certcheck.c - check one certificate
* Copyright (C) 2001, 2003, 2004 Free Software Foundation, Inc.
* Copyright (C) 2001-2019 Werner Koch
* Copyright (C) 2015-2020 g10 Code GmbH
*
* This file is part of GnuPG.
*
* GnuPG is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* GnuPG is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see .
* SPDX-License-Identifier: GPL-3.0-or-later
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include "gpgsm.h"
#include
#include
#include "keydb.h"
#include "../common/i18n.h"
/* Return the number of bits of the Q parameter from the DSA key
KEY. */
static unsigned int
get_dsa_qbits (gcry_sexp_t key)
{
gcry_sexp_t l1, l2;
gcry_mpi_t q;
unsigned int nbits;
l1 = gcry_sexp_find_token (key, "public-key", 0);
if (!l1)
return 0; /* Does not contain a key object. */
l2 = gcry_sexp_cadr (l1);
gcry_sexp_release (l1);
l1 = gcry_sexp_find_token (l2, "q", 1);
gcry_sexp_release (l2);
if (!l1)
return 0; /* Invalid object. */
q = gcry_sexp_nth_mpi (l1, 1, GCRYMPI_FMT_USG);
gcry_sexp_release (l1);
if (!q)
return 0; /* Missing value. */
nbits = gcry_mpi_get_nbits (q);
gcry_mpi_release (q);
return nbits;
}
static int
do_encode_md (gcry_md_hd_t md, int algo, int pkalgo, unsigned int nbits,
gcry_sexp_t pkey, gcry_mpi_t *r_val)
{
int n;
size_t nframe;
unsigned char *frame;
if (pkalgo == GCRY_PK_DSA || pkalgo == GCRY_PK_ECDSA)
{
unsigned int qbits;
if ( pkalgo == GCRY_PK_ECDSA )
qbits = gcry_pk_get_nbits (pkey);
else
qbits = get_dsa_qbits (pkey);
if ( (qbits%8) )
{
log_error(_("DSA requires the hash length to be a"
" multiple of 8 bits\n"));
return gpg_error (GPG_ERR_INTERNAL);
}
/* Don't allow any Q smaller than 160 bits. We don't want
someone to issue signatures from a key with a 16-bit Q or
something like that, which would look correct but allow
trivial forgeries. Yes, I know this rules out using MD5 with
DSA. ;) */
if (qbits < 160)
{
log_error (_("%s key uses an unsafe (%u bit) hash\n"),
gcry_pk_algo_name (pkalgo), qbits);
return gpg_error (GPG_ERR_INTERNAL);
}
/* Check if we're too short. Too long is safe as we'll
automatically left-truncate. */
nframe = gcry_md_get_algo_dlen (algo);
if (nframe < qbits/8)
{
log_error (_("a %u bit hash is not valid for a %u bit %s key\n"),
(unsigned int)nframe*8,
gcry_pk_get_nbits (pkey),
gcry_pk_algo_name (pkalgo));
/* FIXME: we need to check the requirements for ECDSA. */
if (nframe < 20 || pkalgo == GCRY_PK_DSA )
return gpg_error (GPG_ERR_INTERNAL);
}
frame = xtrymalloc (nframe);
if (!frame)
return out_of_core ();
memcpy (frame, gcry_md_read (md, algo), nframe);
n = nframe;
/* Truncate. */
if (n > qbits/8)
n = qbits/8;
}
else
{
int i;
unsigned char asn[100];
size_t asnlen;
size_t len;
nframe = (nbits+7) / 8;
asnlen = DIM(asn);
if (!algo || gcry_md_test_algo (algo))
return gpg_error (GPG_ERR_DIGEST_ALGO);
if (gcry_md_algo_info (algo, GCRYCTL_GET_ASNOID, asn, &asnlen))
{
log_error ("no object identifier for algo %d\n", algo);
return gpg_error (GPG_ERR_INTERNAL);
}
len = gcry_md_get_algo_dlen (algo);
if ( len + asnlen + 4 > nframe )
{
log_error ("can't encode a %d bit MD into a %d bits frame\n",
(int)(len*8), (int)nbits);
return gpg_error (GPG_ERR_INTERNAL);
}
/* We encode the MD in this way:
*
* 0 A PAD(n bytes) 0 ASN(asnlen bytes) MD(len bytes)
*
* PAD consists of FF bytes.
*/
frame = xtrymalloc (nframe);
if (!frame)
return out_of_core ();
n = 0;
frame[n++] = 0;
frame[n++] = 1; /* block type */
i = nframe - len - asnlen -3 ;
assert ( i > 1 );
memset ( frame+n, 0xff, i ); n += i;
frame[n++] = 0;
memcpy ( frame+n, asn, asnlen ); n += asnlen;
memcpy ( frame+n, gcry_md_read(md, algo), len ); n += len;
assert ( n == nframe );
}
if (DBG_CRYPTO)
{
int j;
log_debug ("encoded hash:");
for (j=0; j < nframe; j++)
log_printf (" %02X", frame[j]);
log_printf ("\n");
}
gcry_mpi_scan (r_val, GCRYMPI_FMT_USG, frame, n, &nframe);
xfree (frame);
return 0;
}
/* Return the public key algorithm id from the S-expression PKEY.
FIXME: libgcrypt should provide such a function. Note that this
implementation uses the names as used by libksba. */
static int
pk_algo_from_sexp (gcry_sexp_t pkey)
{
gcry_sexp_t l1, l2;
const char *name;
size_t n;
int algo;
l1 = gcry_sexp_find_token (pkey, "public-key", 0);
if (!l1)
return 0; /* Not found. */
l2 = gcry_sexp_cadr (l1);
gcry_sexp_release (l1);
name = gcry_sexp_nth_data (l2, 0, &n);
if (!name)
algo = 0; /* Not found. */
else if (n==3 && !memcmp (name, "rsa", 3))
algo = GCRY_PK_RSA;
else if (n==3 && !memcmp (name, "dsa", 3))
algo = GCRY_PK_DSA;
/* Because this function is called only for verification we can
assume that ECC actually means ECDSA. */
else if (n==3 && !memcmp (name, "ecc", 3))
algo = GCRY_PK_ECDSA;
else if (n==13 && !memcmp (name, "ambiguous-rsa", 13))
algo = GCRY_PK_RSA;
else
algo = 0;
gcry_sexp_release (l2);
return algo;
}
/* Return the hash algorithm's algo id from its name given in the
* non-null termnated string in (buffer,buflen). Returns 0 on failure
* or if the algo is not known. */
static int
hash_algo_from_buffer (const void *buffer, size_t buflen)
{
char *string;
int algo;
string = xtrymalloc (buflen + 1);
if (!string)
{
log_error (_("out of core\n"));
return 0;
}
memcpy (string, buffer, buflen);
string[buflen] = 0;
algo = gcry_md_map_name (string);
if (!algo)
log_error ("unknown digest algorithm '%s' used in certificate\n", string);
xfree (string);
return algo;
}
/* Return an unsigned integer from the non-null termnated string
* (buffer,buflen). Returns 0 on failure. */
static unsigned int
uint_from_buffer (const void *buffer, size_t buflen)
{
char *string;
unsigned int val;
string = xtrymalloc (buflen + 1);
if (!string)
{
log_error (_("out of core\n"));
return 0;
}
memcpy (string, buffer, buflen);
string[buflen] = 0;
val = strtoul (string, NULL, 10);
xfree (string);
return val;
}
/* Extract the hash algorithm and the salt length from the sigval. */
static gpg_error_t
extract_pss_params (gcry_sexp_t s_sig, int *r_algo, unsigned int *r_saltlen)
{
gpg_error_t err;
gcry_buffer_t ioarray[2] = { {0}, {0} };
err = gcry_sexp_extract_param (s_sig, "sig-val",
"&'hash-algo''salt-length'",
ioarray+0, ioarray+1, NULL);
if (err)
{
log_error ("extracting params from PSS failed: %s\n", gpg_strerror (err));
return err;
}
*r_algo = hash_algo_from_buffer (ioarray[0].data, ioarray[0].len);
*r_saltlen = uint_from_buffer (ioarray[1].data, ioarray[1].len);
xfree (ioarray[0].data);
xfree (ioarray[1].data);
if (*r_saltlen < 20)
{
log_error ("length of PSS salt too short\n");
gcry_sexp_release (s_sig);
return gpg_error (GPG_ERR_DIGEST_ALGO);
}
if (!*r_algo)
{
return gpg_error (GPG_ERR_DIGEST_ALGO);
}
/* PSS has no hash function firewall like PKCS#1 and thus offers
* a path for hash algorithm replacement. To avoid this it makes
* sense to restrict the allowed hash algorithms and also allow only
* matching salt lengths. According to Peter Gutmann:
* "Beware of bugs in the above signature scheme;
* I have only proved it secure, not implemented it"
* - Apologies to Donald Knuth.
* https://www.metzdowd.com/pipermail/cryptography/2019-November/035449.html
*
* Given the set of supported algorithms currently available in
* Libgcrypt and the extra hash checks we have in some compliance
* modes, it would be hard to trick gpgsm to verify a forged
* signature. However, if eventually someone adds the xor256 hash
* algorithm (1.3.6.1.4.1.3029.3.2) to Libgcrypt we would be doomed.
*/
switch (*r_algo)
{
case GCRY_MD_SHA1:
case GCRY_MD_SHA256:
case GCRY_MD_SHA384:
case GCRY_MD_SHA512:
case GCRY_MD_SHA3_256:
case GCRY_MD_SHA3_384:
case GCRY_MD_SHA3_512:
break;
default:
log_error ("PSS hash algorithm '%s' rejected\n",
gcry_md_algo_name (*r_algo));
return gpg_error (GPG_ERR_DIGEST_ALGO);
}
if (gcry_md_get_algo_dlen (*r_algo) != *r_saltlen)
{
log_error ("PSS hash algorithm '%s' rejected due to salt length %u\n",
gcry_md_algo_name (*r_algo), *r_saltlen);
return gpg_error (GPG_ERR_DIGEST_ALGO);
}
return 0;
}
/* Check the signature on CERT using the ISSUER-CERT. This function
does only test the cryptographic signature and nothing else. It is
assumed that the ISSUER_CERT is valid. */
int
gpgsm_check_cert_sig (ksba_cert_t issuer_cert, ksba_cert_t cert)
{
const char *algoid;
gcry_md_hd_t md;
int rc, algo;
ksba_sexp_t p;
size_t n;
gcry_sexp_t s_sig, s_data, s_pkey;
int use_pss = 0;
unsigned int saltlen;
algo = gcry_md_map_name ( (algoid=ksba_cert_get_digest_algo (cert)));
if (!algo && algoid && !strcmp (algoid, "1.2.840.113549.1.1.10"))
use_pss = 1;
else if (!algo)
{
log_error ("unknown digest algorithm '%s' used certificate\n",
algoid? algoid:"?");
if (algoid
&& ( !strcmp (algoid, "1.2.840.113549.1.1.2")
||!strcmp (algoid, "1.2.840.113549.2.2")))
log_info (_("(this is the MD2 algorithm)\n"));
return gpg_error (GPG_ERR_GENERAL);
}
/* The the signature from the certificate. */
p = ksba_cert_get_sig_val (cert);
n = gcry_sexp_canon_len (p, 0, NULL, NULL);
if (!n)
{
log_error ("libksba did not return a proper S-Exp\n");
ksba_free (p);
return gpg_error (GPG_ERR_BUG);
}
rc = gcry_sexp_sscan ( &s_sig, NULL, (char*)p, n);
ksba_free (p);
if (rc)
{
log_error ("gcry_sexp_scan failed: %s\n", gpg_strerror (rc));
return rc;
}
if (DBG_CRYPTO)
gcry_log_debugsxp ("sigval", s_sig);
if (use_pss)
{
rc = extract_pss_params (s_sig, &algo, &saltlen);
if (rc)
{
gcry_sexp_release (s_sig);
return rc;
}
}
/* Hash the to-be-signed parts of the certificate. */
rc = gcry_md_open (&md, algo, 0);
if (rc)
{
log_error ("md_open failed: %s\n", gpg_strerror (rc));
return rc;
}
if (DBG_HASHING)
gcry_md_debug (md, "hash.cert");
rc = ksba_cert_hash (cert, 1, HASH_FNC, md);
if (rc)
{
log_error ("ksba_cert_hash failed: %s\n", gpg_strerror (rc));
gcry_md_close (md);
return rc;
}
gcry_md_final (md);
/* Get the public key from the certificate. */
p = ksba_cert_get_public_key (issuer_cert);
n = gcry_sexp_canon_len (p, 0, NULL, NULL);
if (!n)
{
log_error ("libksba did not return a proper S-Exp\n");
gcry_md_close (md);
ksba_free (p);
gcry_sexp_release (s_sig);
return gpg_error (GPG_ERR_BUG);
}
rc = gcry_sexp_sscan ( &s_pkey, NULL, (char*)p, n);
ksba_free (p);
if (rc)
{
log_error ("gcry_sexp_scan failed: %s\n", gpg_strerror (rc));
gcry_md_close (md);
gcry_sexp_release (s_sig);
return rc;
}
if (DBG_CRYPTO)
gcry_log_debugsxp ("pubkey:", s_pkey);
if (use_pss)
{
rc = gcry_sexp_build (&s_data, NULL,
"(data (flags pss)"
"(hash %s %b)"
"(salt-length %u))",
hash_algo_to_string (algo),
(int)gcry_md_get_algo_dlen (algo),
gcry_md_read (md, algo),
saltlen);
if (rc)
BUG ();
}
else
{
/* RSA or DSA: Prepare the hash for verification. */
gcry_mpi_t frame;
rc = do_encode_md (md, algo, pk_algo_from_sexp (s_pkey),
gcry_pk_get_nbits (s_pkey), s_pkey, &frame);
if (rc)
{
gcry_md_close (md);
gcry_sexp_release (s_sig);
gcry_sexp_release (s_pkey);
return rc;
}
if ( gcry_sexp_build (&s_data, NULL, "%m", frame) )
BUG ();
gcry_mpi_release (frame);
}
if (DBG_CRYPTO)
gcry_log_debugsxp ("data:", s_data);
/* Verify. */
rc = gcry_pk_verify (s_sig, s_data, s_pkey);
if (DBG_X509)
log_debug ("gcry_pk_verify: %s\n", gpg_strerror (rc));
gcry_md_close (md);
gcry_sexp_release (s_sig);
gcry_sexp_release (s_data);
gcry_sexp_release (s_pkey);
return rc;
}
int
gpgsm_check_cms_signature (ksba_cert_t cert, ksba_const_sexp_t sigval,
gcry_md_hd_t md, int mdalgo, int *r_pkalgo)
{
int rc;
ksba_sexp_t p;
gcry_sexp_t s_sig, s_hash, s_pkey, l1;
size_t n;
const char *s;
int i;
int pkalgo;
int use_pss;
unsigned int saltlen = 0;
if (r_pkalgo)
*r_pkalgo = 0;
n = gcry_sexp_canon_len (sigval, 0, NULL, NULL);
if (!n)
{
log_error ("libksba did not return a proper S-Exp\n");
return gpg_error (GPG_ERR_BUG);
}
rc = gcry_sexp_sscan (&s_sig, NULL, (char*)sigval, n);
if (rc)
{
log_error ("gcry_sexp_scan failed: %s\n", gpg_strerror (rc));
return rc;
}
/* Check whether rsaPSS is needed. This is indicated in the SIG-VAL
* using a flag. Only if we found that flag, we extract the PSS
* parameters for SIG-VAL. */
use_pss = 0;
l1 = gcry_sexp_find_token (s_sig, "flags", 0);
if (l1)
{
/* Note that the flag parser assumes that the list of flags
* contains only strings and in particular not sublist. This is
* always the case or current libksba. */
for (i=1; (s = gcry_sexp_nth_data (l1, i, &n)); i++)
if (n == 3 && !memcmp (s, "pss", 3))
{
use_pss = 1;
break;
}
gcry_sexp_release (l1);
if (use_pss)
{
int algo;
rc = extract_pss_params (s_sig, &algo, &saltlen);
if (rc)
{
gcry_sexp_release (s_sig);
return rc;
}
if (algo != mdalgo)
{
log_error ("PSS hash algo mismatch (%d/%d)\n", mdalgo, algo);
gcry_sexp_release (s_sig);
return gpg_error (GPG_ERR_DIGEST_ALGO);
}
}
}
p = ksba_cert_get_public_key (cert);
n = gcry_sexp_canon_len (p, 0, NULL, NULL);
if (!n)
{
log_error ("libksba did not return a proper S-Exp\n");
ksba_free (p);
gcry_sexp_release (s_sig);
return gpg_error (GPG_ERR_BUG);
}
if (DBG_CRYPTO)
- log_printhex ("public key: ", p, n);
+ log_printhex (p, n, "public key: ");
rc = gcry_sexp_sscan ( &s_pkey, NULL, (char*)p, n);
ksba_free (p);
if (rc)
{
log_error ("gcry_sexp_scan failed: %s\n", gpg_strerror (rc));
gcry_sexp_release (s_sig);
return rc;
}
pkalgo = pk_algo_from_sexp (s_pkey);
if (r_pkalgo)
*r_pkalgo = pkalgo;
if (use_pss)
{
rc = gcry_sexp_build (&s_hash, NULL,
"(data (flags pss)"
"(hash %s %b)"
"(salt-length %u))",
hash_algo_to_string (mdalgo),
(int)gcry_md_get_algo_dlen (mdalgo),
gcry_md_read (md, mdalgo),
saltlen);
if (rc)
BUG ();
}
else
{
/* RSA or DSA: Prepare the hash for verification. */
gcry_mpi_t frame;
rc = do_encode_md (md, mdalgo, pkalgo,
gcry_pk_get_nbits (s_pkey), s_pkey, &frame);
if (rc)
{
gcry_sexp_release (s_sig);
gcry_sexp_release (s_pkey);
return rc;
}
/* put hash into the S-Exp s_hash */
if ( gcry_sexp_build (&s_hash, NULL, "%m", frame) )
BUG ();
gcry_mpi_release (frame);
}
rc = gcry_pk_verify (s_sig, s_hash, s_pkey);
if (DBG_X509)
log_debug ("gcry_pk_verify: %s\n", gpg_strerror (rc));
gcry_sexp_release (s_sig);
gcry_sexp_release (s_hash);
gcry_sexp_release (s_pkey);
return rc;
}
int
gpgsm_create_cms_signature (ctrl_t ctrl, ksba_cert_t cert,
gcry_md_hd_t md, int mdalgo,
unsigned char **r_sigval)
{
int rc;
char *grip, *desc;
size_t siglen;
grip = gpgsm_get_keygrip_hexstring (cert);
if (!grip)
return gpg_error (GPG_ERR_BAD_CERT);
desc = gpgsm_format_keydesc (cert);
rc = gpgsm_agent_pksign (ctrl, grip, desc, gcry_md_read(md, mdalgo),
gcry_md_get_algo_dlen (mdalgo), mdalgo,
r_sigval, &siglen);
xfree (desc);
xfree (grip);
return rc;
}
diff --git a/sm/certdump.c b/sm/certdump.c
index ede12106f..7d09cdabd 100644
--- a/sm/certdump.c
+++ b/sm/certdump.c
@@ -1,861 +1,861 @@
/* certdump.c - Dump a certificate for debugging
* Copyright (C) 2001-2010, 2014-2015 g10 Code GmbH
*
* This file is part of GnuPG.
*
* GnuPG is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* GnuPG is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see .
*/
#include
#include
#include
#include
#include
#include
#include
#include
#ifdef HAVE_LOCALE_H
#include
#endif
#ifdef HAVE_LANGINFO_CODESET
#include
#endif
#include "gpgsm.h"
#include
#include
#include "keydb.h"
#include "../common/i18n.h"
struct dn_array_s {
char *key;
char *value;
int multivalued;
int done;
};
/* Print the first element of an S-Expression. */
void
gpgsm_print_serial (estream_t fp, ksba_const_sexp_t sn)
{
const char *p = (const char *)sn;
unsigned long n;
char *endp;
if (!p)
es_fputs (_("none"), fp);
else if (*p != '(')
es_fputs ("[Internal error - not an S-expression]", fp);
else
{
p++;
n = strtoul (p, &endp, 10);
p = endp;
if (*p++ != ':')
es_fputs ("[Internal Error - invalid S-expression]", fp);
else
es_write_hexstring (fp, p, n, 0, NULL);
}
}
/* Dump the serial number or any other simple S-expression. */
void
gpgsm_dump_serial (ksba_const_sexp_t sn)
{
const char *p = (const char *)sn;
unsigned long n;
char *endp;
if (!p)
log_printf ("none");
else if (*p != '(')
log_printf ("ERROR - not an S-expression");
else
{
p++;
n = strtoul (p, &endp, 10);
p = endp;
if (*p!=':')
log_printf ("ERROR - invalid S-expression");
else
{
for (p++; n; n--, p++)
log_printf ("%02X", *(const unsigned char *)p);
}
}
}
char *
gpgsm_format_serial (ksba_const_sexp_t sn)
{
const char *p = (const char *)sn;
unsigned long n;
char *endp;
char *buffer;
int i;
if (!p)
return NULL;
if (*p != '(')
BUG (); /* Not a valid S-expression. */
p++;
n = strtoul (p, &endp, 10);
p = endp;
if (*p!=':')
BUG (); /* Not a valid S-expression. */
p++;
buffer = xtrymalloc (n*2+1);
if (buffer)
{
for (i=0; n; n--, p++, i+=2)
sprintf (buffer+i, "%02X", *(unsigned char *)p);
buffer[i] = 0;
}
return buffer;
}
void
gpgsm_print_time (estream_t fp, ksba_isotime_t t)
{
if (!t || !*t)
es_fputs (_("none"), fp);
else
es_fprintf (fp, "%.4s-%.2s-%.2s %.2s:%.2s:%s",
t, t+4, t+6, t+9, t+11, t+13);
}
void
gpgsm_dump_string (const char *string)
{
if (!string)
log_printf ("[error]");
else
{
const unsigned char *s;
for (s=(const unsigned char*)string; *s; s++)
{
if (*s < ' ' || (*s >= 0x7f && *s <= 0xa0))
break;
}
if (!*s && *string != '[')
log_printf ("%s", string);
else
{
log_printf ( "[ ");
- log_printhex (NULL, string, strlen (string));
+ log_printhex (string, strlen (string), NULL);
log_printf ( " ]");
}
}
}
/* This simple dump function is mainly used for debugging purposes. */
void
gpgsm_dump_cert (const char *text, ksba_cert_t cert)
{
ksba_sexp_t sexp;
char *p;
char *dn;
ksba_isotime_t t;
log_debug ("BEGIN Certificate '%s':\n", text? text:"");
if (cert)
{
sexp = ksba_cert_get_serial (cert);
log_debug (" serial: ");
gpgsm_dump_serial (sexp);
ksba_free (sexp);
log_printf ("\n");
ksba_cert_get_validity (cert, 0, t);
log_debug (" notBefore: ");
dump_isotime (t);
log_printf ("\n");
ksba_cert_get_validity (cert, 1, t);
log_debug (" notAfter: ");
dump_isotime (t);
log_printf ("\n");
dn = ksba_cert_get_issuer (cert, 0);
log_debug (" issuer: ");
gpgsm_dump_string (dn);
ksba_free (dn);
log_printf ("\n");
dn = ksba_cert_get_subject (cert, 0);
log_debug (" subject: ");
gpgsm_dump_string (dn);
ksba_free (dn);
log_printf ("\n");
log_debug (" hash algo: %s\n", ksba_cert_get_digest_algo (cert));
p = gpgsm_get_fingerprint_string (cert, 0);
log_debug (" SHA1 Fingerprint: %s\n", p);
xfree (p);
}
log_debug ("END Certificate\n");
}
/* Return a new string holding the format serial number and issuer
("#SN/issuer"). No filtering on invalid characters is done.
Caller must release the string. On memory failure NULL is
returned. */
char *
gpgsm_format_sn_issuer (ksba_sexp_t sn, const char *issuer)
{
char *p, *p1;
if (sn && issuer)
{
p1 = gpgsm_format_serial (sn);
if (!p1)
p = xtrystrdup ("[invalid SN]");
else
{
p = xtrymalloc (strlen (p1) + strlen (issuer) + 2 + 1);
if (p)
{
*p = '#';
strcpy (stpcpy (stpcpy (p+1, p1),"/"), issuer);
}
xfree (p1);
}
}
else
p = xtrystrdup ("[invalid SN/issuer]");
return p;
}
/* Log the certificate's name in "#SN/ISSUERDN" format along with
TEXT. */
void
gpgsm_cert_log_name (const char *text, ksba_cert_t cert)
{
log_info ("%s", text? text:"certificate" );
if (cert)
{
ksba_sexp_t sn;
char *p;
p = ksba_cert_get_issuer (cert, 0);
sn = ksba_cert_get_serial (cert);
if (p && sn)
{
log_printf (" #");
gpgsm_dump_serial (sn);
log_printf ("/");
gpgsm_dump_string (p);
}
else
log_printf (" [invalid]");
ksba_free (sn);
xfree (p);
}
log_printf ("\n");
}
/* helper for the rfc2253 string parser */
static const unsigned char *
parse_dn_part (struct dn_array_s *array, const unsigned char *string)
{
static struct {
const char *label;
const char *oid;
} label_map[] = {
/* Warning: When adding new labels, make sure that the buffer
below we be allocated large enough. */
{"EMail", "1.2.840.113549.1.9.1" },
{"T", "2.5.4.12" },
{"GN", "2.5.4.42" },
{"SN", "2.5.4.4" },
{"NameDistinguisher", "0.2.262.1.10.7.20"},
{"ADDR", "2.5.4.16" },
{"BC", "2.5.4.15" },
{"D", "2.5.4.13" },
{"PostalCode", "2.5.4.17" },
{"Pseudo", "2.5.4.65" },
{"SerialNumber", "2.5.4.5" },
{NULL, NULL}
};
const unsigned char *s, *s1;
size_t n;
char *p;
int i;
/* Parse attributeType */
for (s = string+1; *s && *s != '='; s++)
;
if (!*s)
return NULL; /* error */
n = s - string;
if (!n)
return NULL; /* empty key */
/* We need to allocate a few bytes more due to the possible mapping
from the shorter OID to the longer label. */
array->key = p = xtrymalloc (n+10);
if (!array->key)
return NULL;
memcpy (p, string, n);
p[n] = 0;
trim_trailing_spaces (p);
if (digitp (p))
{
for (i=0; label_map[i].label; i++ )
if ( !strcmp (p, label_map[i].oid) )
{
strcpy (p, label_map[i].label);
break;
}
}
string = s + 1;
if (*string == '#')
{ /* hexstring */
string++;
for (s=string; hexdigitp (s); s++)
s++;
n = s - string;
if (!n || (n & 1))
return NULL; /* Empty or odd number of digits. */
n /= 2;
array->value = p = xtrymalloc (n+1);
if (!p)
return NULL;
for (s1=string; n; s1 += 2, n--, p++)
{
*(unsigned char *)p = xtoi_2 (s1);
if (!*p)
*p = 0x01; /* Better print a wrong value than truncating
the string. */
}
*p = 0;
}
else
{ /* regular v3 quoted string */
for (n=0, s=string; *s; s++)
{
if (*s == '\\')
{ /* pair */
s++;
if (*s == ',' || *s == '=' || *s == '+'
|| *s == '<' || *s == '>' || *s == '#' || *s == ';'
|| *s == '\\' || *s == '\"' || *s == ' ')
n++;
else if (hexdigitp (s) && hexdigitp (s+1))
{
s++;
n++;
}
else
return NULL; /* invalid escape sequence */
}
else if (*s == '\"')
return NULL; /* invalid encoding */
else if (*s == ',' || *s == '=' || *s == '+'
|| *s == '<' || *s == '>' || *s == ';' )
break;
else
n++;
}
array->value = p = xtrymalloc (n+1);
if (!p)
return NULL;
for (s=string; n; s++, n--)
{
if (*s == '\\')
{
s++;
if (hexdigitp (s))
{
*(unsigned char *)p++ = xtoi_2 (s);
s++;
}
else
*p++ = *s;
}
else
*p++ = *s;
}
*p = 0;
}
return s;
}
/* Parse a DN and return an array-ized one. This is not a validating
parser and it does not support any old-stylish syntax; KSBA is
expected to return only rfc2253 compatible strings. */
static struct dn_array_s *
parse_dn (const unsigned char *string)
{
struct dn_array_s *array;
size_t arrayidx, arraysize;
int i;
arraysize = 7; /* C,ST,L,O,OU,CN,email */
arrayidx = 0;
array = xtrymalloc ((arraysize+1) * sizeof *array);
if (!array)
return NULL;
while (*string)
{
while (*string == ' ')
string++;
if (!*string)
break; /* ready */
if (arrayidx >= arraysize)
{
struct dn_array_s *a2;
arraysize += 5;
a2 = xtryrealloc (array, (arraysize+1) * sizeof *array);
if (!a2)
goto failure;
array = a2;
}
array[arrayidx].key = NULL;
array[arrayidx].value = NULL;
string = parse_dn_part (array+arrayidx, string);
if (!string)
goto failure;
while (*string == ' ')
string++;
array[arrayidx].multivalued = (*string == '+');
array[arrayidx].done = 0;
arrayidx++;
if (*string && *string != ',' && *string != ';' && *string != '+')
goto failure; /* invalid delimiter */
if (*string)
string++;
}
array[arrayidx].key = NULL;
array[arrayidx].value = NULL;
return array;
failure:
for (i=0; i < arrayidx; i++)
{
xfree (array[i].key);
xfree (array[i].value);
}
xfree (array);
return NULL;
}
/* Print a DN part to STREAM. */
static void
print_dn_part (estream_t stream,
struct dn_array_s *dn, const char *key, int translate)
{
struct dn_array_s *first_dn = dn;
for (; dn->key; dn++)
{
if (!dn->done && !strcmp (dn->key, key))
{
/* Forward to the last multi-valued RDN, so that we can
print them all in reverse in the correct order. Note
that this overrides the standard sequence but that
seems to a reasonable thing to do with multi-valued
RDNs. */
while (dn->multivalued && dn[1].key)
dn++;
next:
if (!dn->done && dn->value && *dn->value)
{
es_fprintf (stream, "/%s=", dn->key);
if (translate)
print_utf8_buffer3 (stream, dn->value, strlen (dn->value),
"/");
else
es_write_sanitized (stream, dn->value, strlen (dn->value),
"/", NULL);
}
dn->done = 1;
if (dn > first_dn && dn[-1].multivalued)
{
dn--;
goto next;
}
}
}
}
/* Print all parts of a DN in a "standard" sequence. We first print
all the known parts, followed by the uncommon ones */
static void
print_dn_parts (estream_t stream,
struct dn_array_s *dn, int translate)
{
const char *stdpart[] = {
"CN", "OU", "O", "STREET", "L", "ST", "C", "EMail", NULL
};
int i;
for (i=0; stdpart[i]; i++)
print_dn_part (stream, dn, stdpart[i], translate);
/* Now print the rest without any specific ordering */
for (; dn->key; dn++)
print_dn_part (stream, dn, dn->key, translate);
}
/* Print the S-Expression in BUF to extended STREAM, which has a valid
length of BUFLEN, as a human readable string in one line to FP. */
static void
pretty_es_print_sexp (estream_t fp, const unsigned char *buf, size_t buflen)
{
size_t len;
gcry_sexp_t sexp;
char *result, *p;
if ( gcry_sexp_sscan (&sexp, NULL, (const char*)buf, buflen) )
{
es_fputs (_("[Error - invalid encoding]"), fp);
return;
}
len = gcry_sexp_sprint (sexp, GCRYSEXP_FMT_ADVANCED, NULL, 0);
assert (len);
result = xtrymalloc (len);
if (!result)
{
es_fputs (_("[Error - out of core]"), fp);
gcry_sexp_release (sexp);
return;
}
len = gcry_sexp_sprint (sexp, GCRYSEXP_FMT_ADVANCED, result, len);
assert (len);
for (p = result; len; len--, p++)
{
if (*p == '\n')
{
if (len > 1) /* Avoid printing the trailing LF. */
es_fputs ("\\n", fp);
}
else if (*p == '\r')
es_fputs ("\\r", fp);
else if (*p == '\v')
es_fputs ("\\v", fp);
else if (*p == '\t')
es_fputs ("\\t", fp);
else
es_putc (*p, fp);
}
xfree (result);
gcry_sexp_release (sexp);
}
/* This is a variant of gpgsm_print_name sending it output to an estream. */
void
gpgsm_es_print_name2 (estream_t fp, const char *name, int translate)
{
const unsigned char *s = (const unsigned char *)name;
int i;
if (!s)
{
es_fputs (_("[Error - No name]"), fp);
}
else if (*s == '<')
{
const char *s2 = strchr ( (char*)s+1, '>');
if (s2)
{
if (translate)
print_utf8_buffer (fp, s + 1, s2 - (char*)s - 1);
else
es_write_sanitized (fp, s + 1, s2 - (char*)s - 1, NULL, NULL);
}
}
else if (*s == '(')
{
pretty_es_print_sexp (fp, s, gcry_sexp_canon_len (s, 0, NULL, NULL));
}
else if (!((*s >= '0' && *s < '9')
|| (*s >= 'A' && *s <= 'Z')
|| (*s >= 'a' && *s <= 'z')))
es_fputs (_("[Error - invalid encoding]"), fp);
else
{
struct dn_array_s *dn = parse_dn (s);
if (!dn)
es_fputs (_("[Error - invalid DN]"), fp);
else
{
print_dn_parts (fp, dn, translate);
for (i=0; dn[i].key; i++)
{
xfree (dn[i].key);
xfree (dn[i].value);
}
xfree (dn);
}
}
}
void
gpgsm_es_print_name (estream_t fp, const char *name)
{
gpgsm_es_print_name2 (fp, name, 1);
}
/* A cookie structure used for the memory stream. */
struct format_name_cookie
{
char *buffer; /* Malloced buffer with the data to deliver. */
size_t size; /* Allocated size of this buffer. */
size_t len; /* strlen (buffer). */
int error; /* system error code if any. */
};
/* The writer function for the memory stream. */
static gpgrt_ssize_t
format_name_writer (void *cookie, const void *buffer, size_t size)
{
struct format_name_cookie *c = cookie;
char *p;
if (!buffer) /* Flush. */
return 0; /* (Actually we could use SIZE because that should be 0 too.) */
if (!c->buffer)
{
p = xtrymalloc (size + 1 + 1);
if (p)
{
c->size = size + 1;
c->buffer = p;
c->len = 0;
}
}
else if (c->len + size < c->len)
{
p = NULL;
gpg_err_set_errno (ENOMEM);
}
else if (c->size < c->len + size)
{
p = xtryrealloc (c->buffer, c->len + size + 1);
if (p)
{
c->size = c->len + size;
c->buffer = p;
}
}
else
p = c->buffer;
if (!p)
{
c->error = errno;
xfree (c->buffer);
c->buffer = NULL;
gpg_err_set_errno (c->error);
return -1;
}
memcpy (p + c->len, buffer, size);
c->len += size;
p[c->len] = 0; /* Terminate string. */
return (gpgrt_ssize_t)size;
}
/* Format NAME which is expected to be in rfc2253 format into a better
human readable format. Caller must free the returned string. NULL
is returned in case of an error. With TRANSLATE set to true the
name will be translated to the native encoding. Note that NAME is
internally always UTF-8 encoded. */
char *
gpgsm_format_name2 (const char *name, int translate)
{
estream_t fp;
struct format_name_cookie cookie;
es_cookie_io_functions_t io = { NULL };
memset (&cookie, 0, sizeof cookie);
io.func_write = format_name_writer;
fp = es_fopencookie (&cookie, "w", io);
if (!fp)
{
int save_errno = errno;
log_error ("error creating memory stream: %s\n", strerror (save_errno));
gpg_err_set_errno (save_errno);
return NULL;
}
gpgsm_es_print_name2 (fp, name, translate);
es_fclose (fp);
if (cookie.error || !cookie.buffer)
{
xfree (cookie.buffer);
gpg_err_set_errno (cookie.error);
return NULL;
}
return cookie.buffer;
}
char *
gpgsm_format_name (const char *name)
{
return gpgsm_format_name2 (name, 1);
}
/* Return fingerprint and a percent escaped name in a human readable
format suitable for status messages like GOODSIG. May return NULL
on error (out of core). */
char *
gpgsm_fpr_and_name_for_status (ksba_cert_t cert)
{
char *fpr, *name, *p;
char *buffer;
fpr = gpgsm_get_fingerprint_hexstring (cert, GCRY_MD_SHA1);
if (!fpr)
return NULL;
name = ksba_cert_get_subject (cert, 0);
if (!name)
{
xfree (fpr);
return NULL;
}
p = gpgsm_format_name2 (name, 0);
ksba_free (name);
name = p;
if (!name)
{
xfree (fpr);
return NULL;
}
buffer = xtrymalloc (strlen (fpr) + 1 + 3*strlen (name) + 1);
if (buffer)
{
const char *s;
p = stpcpy (stpcpy (buffer, fpr), " ");
for (s = name; *s; s++)
{
if (*s < ' ')
{
sprintf (p, "%%%02X", *(const unsigned char*)s);
p += 3;
}
else
*p++ = *s;
}
*p = 0;
}
xfree (fpr);
xfree (name);
return buffer;
}
/* Create a key description for the CERT, this may be passed to the
pinentry. The caller must free the returned string. NULL may be
returned on error. */
char *
gpgsm_format_keydesc (ksba_cert_t cert)
{
char *name, *subject, *buffer;
ksba_isotime_t t;
char created[20];
char expires[20];
char *sn;
ksba_sexp_t sexp;
char *orig_codeset;
name = ksba_cert_get_subject (cert, 0);
subject = name? gpgsm_format_name2 (name, 0) : NULL;
ksba_free (name); name = NULL;
sexp = ksba_cert_get_serial (cert);
sn = sexp? gpgsm_format_serial (sexp) : NULL;
ksba_free (sexp);
ksba_cert_get_validity (cert, 0, t);
if (*t)
sprintf (created, "%.4s-%.2s-%.2s", t, t+4, t+6);
else
*created = 0;
ksba_cert_get_validity (cert, 1, t);
if (*t)
sprintf (expires, "%.4s-%.2s-%.2s", t, t+4, t+6);
else
*expires = 0;
orig_codeset = i18n_switchto_utf8 ();
name = xtryasprintf (_("Please enter the passphrase to unlock the"
" secret key for the X.509 certificate:\n"
"\"%s\"\n"
"S/N %s, ID 0x%08lX,\n"
"created %s, expires %s.\n" ),
subject? subject:"?",
sn? sn: "?",
gpgsm_get_short_fingerprint (cert, NULL),
created, expires);
i18n_switchback (orig_codeset);
if (!name)
{
xfree (subject);
xfree (sn);
return NULL;
}
xfree (subject);
xfree (sn);
buffer = percent_plus_escape (name);
xfree (name);
return buffer;
}
diff --git a/sm/decrypt.c b/sm/decrypt.c
index db0768eea..904a40235 100644
--- a/sm/decrypt.c
+++ b/sm/decrypt.c
@@ -1,648 +1,648 @@
/* decrypt.c - Decrypt a message
* Copyright (C) 2001, 2003, 2010 Free Software Foundation, Inc.
*
* This file is part of GnuPG.
*
* GnuPG is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* GnuPG is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see .
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include "gpgsm.h"
#include
#include
#include "keydb.h"
#include "../common/i18n.h"
#include "../common/compliance.h"
struct decrypt_filter_parm_s
{
int algo;
int mode;
int blklen;
gcry_cipher_hd_t hd;
char iv[16];
size_t ivlen;
int any_data; /* did we push anything through the filter at all? */
unsigned char lastblock[16]; /* to strip the padding we have to
keep this one */
char helpblock[16]; /* needed because there is no block buffering in
libgcrypt (yet) */
int helpblocklen;
};
/* Decrypt the session key and fill in the parm structure. The
algo and the IV is expected to be already in PARM. */
static int
prepare_decryption (ctrl_t ctrl, const char *hexkeygrip, const char *desc,
ksba_const_sexp_t enc_val,
struct decrypt_filter_parm_s *parm)
{
char *seskey = NULL;
size_t n, seskeylen;
int rc;
rc = gpgsm_agent_pkdecrypt (ctrl, hexkeygrip, desc, enc_val,
&seskey, &seskeylen);
if (rc)
{
log_error ("error decrypting session key: %s\n", gpg_strerror (rc));
goto leave;
}
if (DBG_CRYPTO)
- log_printhex ("pkcs1 encoded session key:", seskey, seskeylen);
+ log_printhex (seskey, seskeylen, "pkcs1 encoded session key:");
n=0;
if (seskeylen == 32 || seskeylen == 24 || seskeylen == 16)
{
/* Smells like an AES-128, 3-DES, or AES-256 key. This might
* happen because a SC has already done the unpacking. A better
* solution would be to test for this only after we triggered
* the GPG_ERR_INV_SESSION_KEY. */
}
else
{
if (n + 7 > seskeylen )
{
rc = gpg_error (GPG_ERR_INV_SESSION_KEY);
goto leave;
}
/* FIXME: Actually the leading zero is required but due to the way
we encode the output in libgcrypt as an MPI we are not able to
encode that leading zero. However, when using a Smartcard we are
doing it the right way and therefore we have to skip the zero. This
should be fixed in gpg-agent of course. */
if (!seskey[n])
n++;
if (seskey[n] != 2 ) /* Wrong block type version. */
{
rc = gpg_error (GPG_ERR_INV_SESSION_KEY);
goto leave;
}
for (n++; n < seskeylen && seskey[n]; n++) /* Skip the random bytes. */
;
n++; /* and the zero byte */
if (n >= seskeylen )
{
rc = gpg_error (GPG_ERR_INV_SESSION_KEY);
goto leave;
}
}
if (DBG_CRYPTO)
- log_printhex ("session key:", seskey+n, seskeylen-n);
+ log_printhex (seskey+n, seskeylen-n, "session key:");
rc = gcry_cipher_open (&parm->hd, parm->algo, parm->mode, 0);
if (rc)
{
log_error ("error creating decryptor: %s\n", gpg_strerror (rc));
goto leave;
}
rc = gcry_cipher_setkey (parm->hd, seskey+n, seskeylen-n);
if (gpg_err_code (rc) == GPG_ERR_WEAK_KEY)
{
log_info (_("WARNING: message was encrypted with "
"a weak key in the symmetric cipher.\n"));
rc = 0;
}
if (rc)
{
log_error("key setup failed: %s\n", gpg_strerror(rc) );
goto leave;
}
gcry_cipher_setiv (parm->hd, parm->iv, parm->ivlen);
leave:
xfree (seskey);
return rc;
}
/* This function is called by the KSBA writer just before the actual
write is done. The function must take INLEN bytes from INBUF,
decrypt it and store it inoutbuf which has a maximum size of
maxoutlen. The valid bytes in outbuf should be return in outlen.
Due to different buffer sizes or different length of input and
output, it may happen that fewer bytes are processed or fewer bytes
are written. */
static gpg_error_t
decrypt_filter (void *arg,
const void *inbuf, size_t inlen, size_t *inused,
void *outbuf, size_t maxoutlen, size_t *outlen)
{
struct decrypt_filter_parm_s *parm = arg;
int blklen = parm->blklen;
size_t orig_inlen = inlen;
/* fixme: Should we issue an error when we have not seen one full block? */
if (!inlen)
return gpg_error (GPG_ERR_BUG);
if (maxoutlen < 2*parm->blklen)
return gpg_error (GPG_ERR_BUG);
/* Make some space because we will later need an extra block at the end. */
maxoutlen -= blklen;
if (parm->helpblocklen)
{
int i, j;
for (i=parm->helpblocklen,j=0; i < blklen && j < inlen; i++, j++)
parm->helpblock[i] = ((const char*)inbuf)[j];
inlen -= j;
if (blklen > maxoutlen)
return gpg_error (GPG_ERR_BUG);
if (i < blklen)
{
parm->helpblocklen = i;
*outlen = 0;
}
else
{
parm->helpblocklen = 0;
if (parm->any_data)
{
memcpy (outbuf, parm->lastblock, blklen);
*outlen =blklen;
}
else
*outlen = 0;
gcry_cipher_decrypt (parm->hd, parm->lastblock, blklen,
parm->helpblock, blklen);
parm->any_data = 1;
}
*inused = orig_inlen - inlen;
return 0;
}
if (inlen > maxoutlen)
inlen = maxoutlen;
if (inlen % blklen)
{ /* store the remainder away */
parm->helpblocklen = inlen%blklen;
inlen = inlen/blklen*blklen;
memcpy (parm->helpblock, (const char*)inbuf+inlen, parm->helpblocklen);
}
*inused = inlen + parm->helpblocklen;
if (inlen)
{
assert (inlen >= blklen);
if (parm->any_data)
{
gcry_cipher_decrypt (parm->hd, (char*)outbuf+blklen, inlen,
inbuf, inlen);
memcpy (outbuf, parm->lastblock, blklen);
memcpy (parm->lastblock,(char*)outbuf+inlen, blklen);
*outlen = inlen;
}
else
{
gcry_cipher_decrypt (parm->hd, outbuf, inlen, inbuf, inlen);
memcpy (parm->lastblock, (char*)outbuf+inlen-blklen, blklen);
*outlen = inlen - blklen;
parm->any_data = 1;
}
}
else
*outlen = 0;
return 0;
}
/* Perform a decrypt operation. */
int
gpgsm_decrypt (ctrl_t ctrl, int in_fd, estream_t out_fp)
{
int rc;
gnupg_ksba_io_t b64reader = NULL;
gnupg_ksba_io_t b64writer = NULL;
ksba_reader_t reader;
ksba_writer_t writer;
ksba_cms_t cms = NULL;
ksba_stop_reason_t stopreason;
KEYDB_HANDLE kh;
int recp;
estream_t in_fp = NULL;
struct decrypt_filter_parm_s dfparm;
memset (&dfparm, 0, sizeof dfparm);
audit_set_type (ctrl->audit, AUDIT_TYPE_DECRYPT);
kh = keydb_new ();
if (!kh)
{
log_error (_("failed to allocate keyDB handle\n"));
rc = gpg_error (GPG_ERR_GENERAL);
goto leave;
}
in_fp = es_fdopen_nc (in_fd, "rb");
if (!in_fp)
{
rc = gpg_error_from_syserror ();
log_error ("fdopen() failed: %s\n", strerror (errno));
goto leave;
}
rc = gnupg_ksba_create_reader
(&b64reader, ((ctrl->is_pem? GNUPG_KSBA_IO_PEM : 0)
| (ctrl->is_base64? GNUPG_KSBA_IO_BASE64 : 0)
| (ctrl->autodetect_encoding? GNUPG_KSBA_IO_AUTODETECT : 0)),
in_fp, &reader);
if (rc)
{
log_error ("can't create reader: %s\n", gpg_strerror (rc));
goto leave;
}
rc = gnupg_ksba_create_writer
(&b64writer, ((ctrl->create_pem? GNUPG_KSBA_IO_PEM : 0)
| (ctrl->create_base64? GNUPG_KSBA_IO_BASE64 : 0)),
ctrl->pem_name, out_fp, &writer);
if (rc)
{
log_error ("can't create writer: %s\n", gpg_strerror (rc));
goto leave;
}
rc = ksba_cms_new (&cms);
if (rc)
goto leave;
rc = ksba_cms_set_reader_writer (cms, reader, writer);
if (rc)
{
log_debug ("ksba_cms_set_reader_writer failed: %s\n",
gpg_strerror (rc));
goto leave;
}
audit_log (ctrl->audit, AUDIT_SETUP_READY);
/* Parser loop. */
do
{
rc = ksba_cms_parse (cms, &stopreason);
if (rc)
{
log_debug ("ksba_cms_parse failed: %s\n", gpg_strerror (rc));
goto leave;
}
if (stopreason == KSBA_SR_BEGIN_DATA
|| stopreason == KSBA_SR_DETACHED_DATA)
{
int algo, mode;
const char *algoid;
int any_key = 0;
int is_de_vs; /* Computed compliance with CO_DE_VS. */
audit_log (ctrl->audit, AUDIT_GOT_DATA);
algoid = ksba_cms_get_content_oid (cms, 2/* encryption algo*/);
algo = gcry_cipher_map_name (algoid);
mode = gcry_cipher_mode_from_oid (algoid);
if (!algo || !mode)
{
rc = gpg_error (GPG_ERR_UNSUPPORTED_ALGORITHM);
log_error ("unsupported algorithm '%s'\n", algoid? algoid:"?");
if (algoid && !strcmp (algoid, "1.2.840.113549.3.2"))
log_info (_("(this is the RC2 algorithm)\n"));
else if (!algoid)
log_info (_("(this does not seem to be an encrypted"
" message)\n"));
{
char numbuf[50];
sprintf (numbuf, "%d", rc);
gpgsm_status2 (ctrl, STATUS_ERROR, "decrypt.algorithm",
numbuf, algoid?algoid:"?", NULL);
audit_log_s (ctrl->audit, AUDIT_BAD_DATA_CIPHER_ALGO, algoid);
}
/* If it seems that this is not an encrypted message we
return a more sensible error code. */
if (!algoid)
rc = gpg_error (GPG_ERR_NO_DATA);
goto leave;
}
/* Check compliance. */
if (! gnupg_cipher_is_allowed (opt.compliance, 0, algo, mode))
{
log_error (_("cipher algorithm '%s'"
" may not be used in %s mode\n"),
gcry_cipher_algo_name (algo),
gnupg_compliance_option_string (opt.compliance));
rc = gpg_error (GPG_ERR_CIPHER_ALGO);
goto leave;
}
/* For CMS, CO_DE_VS demands CBC mode. */
is_de_vs = gnupg_cipher_is_compliant (CO_DE_VS, algo, mode);
audit_log_i (ctrl->audit, AUDIT_DATA_CIPHER_ALGO, algo);
dfparm.algo = algo;
dfparm.mode = mode;
dfparm.blklen = gcry_cipher_get_algo_blklen (algo);
if (dfparm.blklen > sizeof (dfparm.helpblock))
return gpg_error (GPG_ERR_BUG);
rc = ksba_cms_get_content_enc_iv (cms,
dfparm.iv,
sizeof (dfparm.iv),
&dfparm.ivlen);
if (rc)
{
log_error ("error getting IV: %s\n", gpg_strerror (rc));
goto leave;
}
for (recp=0; !any_key; recp++)
{
char *issuer;
ksba_sexp_t serial;
ksba_sexp_t enc_val;
char *hexkeygrip = NULL;
char *desc = NULL;
char kidbuf[16+1];
*kidbuf = 0;
rc = ksba_cms_get_issuer_serial (cms, recp, &issuer, &serial);
if (rc == -1 && recp)
break; /* no more recipients */
audit_log_i (ctrl->audit, AUDIT_NEW_RECP, recp);
if (rc)
log_error ("recp %d - error getting info: %s\n",
recp, gpg_strerror (rc));
else
{
ksba_cert_t cert = NULL;
log_debug ("recp %d - issuer: '%s'\n",
recp, issuer? issuer:"[NONE]");
log_debug ("recp %d - serial: ", recp);
gpgsm_dump_serial (serial);
log_printf ("\n");
if (ctrl->audit)
{
char *tmpstr = gpgsm_format_sn_issuer (serial, issuer);
audit_log_s (ctrl->audit, AUDIT_RECP_NAME, tmpstr);
xfree (tmpstr);
}
keydb_search_reset (kh);
rc = keydb_search_issuer_sn (ctrl, kh, issuer, serial);
if (rc)
{
log_error ("failed to find the certificate: %s\n",
gpg_strerror(rc));
goto oops;
}
rc = keydb_get_cert (kh, &cert);
if (rc)
{
log_error ("failed to get cert: %s\n", gpg_strerror (rc));
goto oops;
}
/* Print the ENC_TO status line. Note that we can
do so only if we have the certificate. This is
in contrast to gpg where the keyID is commonly
included in the encrypted messages. It is too
cumbersome to retrieve the used algorithm, thus
we don't print it for now. We also record the
keyid for later use. */
{
unsigned long kid[2];
kid[0] = gpgsm_get_short_fingerprint (cert, kid+1);
snprintf (kidbuf, sizeof kidbuf, "%08lX%08lX",
kid[1], kid[0]);
gpgsm_status2 (ctrl, STATUS_ENC_TO,
kidbuf, "0", "0", NULL);
}
/* Put the certificate into the audit log. */
audit_log_cert (ctrl->audit, AUDIT_SAVE_CERT, cert, 0);
/* Just in case there is a problem with the own
certificate we print this message - should never
happen of course */
rc = gpgsm_cert_use_decrypt_p (cert);
if (rc)
{
char numbuf[50];
sprintf (numbuf, "%d", rc);
gpgsm_status2 (ctrl, STATUS_ERROR, "decrypt.keyusage",
numbuf, NULL);
rc = 0;
}
hexkeygrip = gpgsm_get_keygrip_hexstring (cert);
desc = gpgsm_format_keydesc (cert);
{
unsigned int nbits;
int pk_algo = gpgsm_get_key_algo_info (cert, &nbits);
/* Check compliance. */
if (!gnupg_pk_is_allowed (opt.compliance,
PK_USE_DECRYPTION,
pk_algo, NULL, nbits, NULL))
{
char kidstr[10+1];
snprintf (kidstr, sizeof kidstr, "0x%08lX",
gpgsm_get_short_fingerprint (cert, NULL));
log_info
(_("key %s is not suitable for decryption"
" in %s mode\n"),
kidstr,
gnupg_compliance_option_string (opt.compliance));
rc = gpg_error (GPG_ERR_PUBKEY_ALGO);
goto oops;
}
/* Check that all certs are compliant with CO_DE_VS. */
is_de_vs =
(is_de_vs
&& gnupg_pk_is_compliant (CO_DE_VS, pk_algo, NULL,
nbits, NULL));
}
oops:
if (rc)
{
/* We cannot check compliance of certs that we
* don't have. */
is_de_vs = 0;
}
xfree (issuer);
xfree (serial);
ksba_cert_release (cert);
}
if (!hexkeygrip)
;
else if (!(enc_val = ksba_cms_get_enc_val (cms, recp)))
log_error ("recp %d - error getting encrypted session key\n",
recp);
else
{
rc = prepare_decryption (ctrl,
hexkeygrip, desc, enc_val, &dfparm);
xfree (enc_val);
if (rc)
{
log_info ("decrypting session key failed: %s\n",
gpg_strerror (rc));
if (gpg_err_code (rc) == GPG_ERR_NO_SECKEY && *kidbuf)
gpgsm_status2 (ctrl, STATUS_NO_SECKEY, kidbuf, NULL);
}
else
{ /* setup the bulk decrypter */
any_key = 1;
ksba_writer_set_filter (writer,
decrypt_filter,
&dfparm);
if (is_de_vs)
gpgsm_status (ctrl, STATUS_DECRYPTION_COMPLIANCE_MODE,
gnupg_status_compliance_flag (CO_DE_VS));
}
audit_log_ok (ctrl->audit, AUDIT_RECP_RESULT, rc);
}
xfree (hexkeygrip);
xfree (desc);
}
/* If we write an audit log add the unused recipients to the
log as well. */
if (ctrl->audit && any_key)
{
for (;; recp++)
{
char *issuer;
ksba_sexp_t serial;
int tmp_rc;
tmp_rc = ksba_cms_get_issuer_serial (cms, recp,
&issuer, &serial);
if (tmp_rc == -1)
break; /* no more recipients */
audit_log_i (ctrl->audit, AUDIT_NEW_RECP, recp);
if (tmp_rc)
log_error ("recp %d - error getting info: %s\n",
recp, gpg_strerror (rc));
else
{
char *tmpstr = gpgsm_format_sn_issuer (serial, issuer);
audit_log_s (ctrl->audit, AUDIT_RECP_NAME, tmpstr);
xfree (tmpstr);
xfree (issuer);
xfree (serial);
}
}
}
if (!any_key)
{
rc = gpg_error (GPG_ERR_NO_SECKEY);
goto leave;
}
}
else if (stopreason == KSBA_SR_END_DATA)
{
ksba_writer_set_filter (writer, NULL, NULL);
if (dfparm.any_data)
{ /* write the last block with padding removed */
int i, npadding = dfparm.lastblock[dfparm.blklen-1];
if (!npadding || npadding > dfparm.blklen)
{
log_error ("invalid padding with value %d\n", npadding);
rc = gpg_error (GPG_ERR_INV_DATA);
goto leave;
}
rc = ksba_writer_write (writer,
dfparm.lastblock,
dfparm.blklen - npadding);
if (rc)
goto leave;
for (i=dfparm.blklen - npadding; i < dfparm.blklen; i++)
{
if (dfparm.lastblock[i] != npadding)
{
log_error ("inconsistent padding\n");
rc = gpg_error (GPG_ERR_INV_DATA);
goto leave;
}
}
}
}
}
while (stopreason != KSBA_SR_READY);
rc = gnupg_ksba_finish_writer (b64writer);
if (rc)
{
log_error ("write failed: %s\n", gpg_strerror (rc));
goto leave;
}
gpgsm_status (ctrl, STATUS_DECRYPTION_OKAY, NULL);
leave:
audit_log_ok (ctrl->audit, AUDIT_DECRYPTION_RESULT, rc);
if (rc)
{
gpgsm_status (ctrl, STATUS_DECRYPTION_FAILED, NULL);
log_error ("message decryption failed: %s <%s>\n",
gpg_strerror (rc), gpg_strsource (rc));
}
ksba_cms_release (cms);
gnupg_ksba_destroy_reader (b64reader);
gnupg_ksba_destroy_writer (b64writer);
keydb_release (kh);
es_fclose (in_fp);
if (dfparm.hd)
gcry_cipher_close (dfparm.hd);
return rc;
}
diff --git a/sm/fingerprint.c b/sm/fingerprint.c
index fbcec5883..3a57b7ba5 100644
--- a/sm/fingerprint.c
+++ b/sm/fingerprint.c
@@ -1,345 +1,345 @@
/* fingerprint.c - Get the fingerprint
* Copyright (C) 2001 Free Software Foundation, Inc.
*
* This file is part of GnuPG.
*
* GnuPG is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* GnuPG is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see .
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include "gpgsm.h"
#include
#include
#include "../common/host2net.h"
/* Return the fingerprint of the certificate (we can't put this into
libksba because we need libgcrypt support). The caller must
provide an array of sufficient length or NULL so that the function
allocates the array. If r_len is not NULL, the length of the
digest is returned; well, this can also be done by using
gcry_md_get_algo_dlen(). If algo is 0, a SHA-1 will be used.
If there is a problem , the function does never return NULL but a
digest of all 0xff.
*/
unsigned char *
gpgsm_get_fingerprint (ksba_cert_t cert, int algo,
unsigned char *array, int *r_len)
{
gcry_md_hd_t md;
int rc, len;
if (!algo)
algo = GCRY_MD_SHA1;
len = gcry_md_get_algo_dlen (algo);
assert (len);
if (!array)
array = xmalloc (len);
if (r_len)
*r_len = len;
/* Fist check whether we have cached the fingerprint. */
if (algo == GCRY_MD_SHA1)
{
size_t buflen;
assert (len >= 20);
if (!ksba_cert_get_user_data (cert, "sha1-fingerprint",
array, len, &buflen)
&& buflen == 20)
return array;
}
/* No, need to compute it. */
rc = gcry_md_open (&md, algo, 0);
if (rc)
{
log_error ("md_open failed: %s\n", gpg_strerror (rc));
memset (array, 0xff, len); /* better return an invalid fpr than NULL */
return array;
}
rc = ksba_cert_hash (cert, 0, HASH_FNC, md);
if (rc)
{
log_error ("ksba_cert_hash failed: %s\n", gpg_strerror (rc));
gcry_md_close (md);
memset (array, 0xff, len); /* better return an invalid fpr than NULL */
return array;
}
gcry_md_final (md);
memcpy (array, gcry_md_read(md, algo), len );
gcry_md_close (md);
/* Cache an SHA-1 fingerprint. */
if ( algo == GCRY_MD_SHA1 )
ksba_cert_set_user_data (cert, "sha1-fingerprint", array, 20);
return array;
}
/* Return an allocated buffer with the formatted fingerprint */
char *
gpgsm_get_fingerprint_string (ksba_cert_t cert, int algo)
{
unsigned char digest[MAX_DIGEST_LEN];
char *buf;
int len;
if (!algo)
algo = GCRY_MD_SHA1;
len = gcry_md_get_algo_dlen (algo);
assert (len <= MAX_DIGEST_LEN );
gpgsm_get_fingerprint (cert, algo, digest, NULL);
buf = xmalloc (len*3+1);
bin2hexcolon (digest, len, buf);
return buf;
}
/* Return an allocated buffer with the formatted fingerprint as one
large hexnumber */
char *
gpgsm_get_fingerprint_hexstring (ksba_cert_t cert, int algo)
{
unsigned char digest[MAX_DIGEST_LEN];
char *buf;
int len;
if (!algo)
algo = GCRY_MD_SHA1;
len = gcry_md_get_algo_dlen (algo);
assert (len <= MAX_DIGEST_LEN );
gpgsm_get_fingerprint (cert, algo, digest, NULL);
buf = xmalloc (len*2+1);
bin2hex (digest, len, buf);
return buf;
}
/* Return a certificate ID. These are the last 4 bytes of the SHA-1
fingerprint. If R_HIGH is not NULL the next 4 bytes are stored
there. */
unsigned long
gpgsm_get_short_fingerprint (ksba_cert_t cert, unsigned long *r_high)
{
unsigned char digest[20];
gpgsm_get_fingerprint (cert, GCRY_MD_SHA1, digest, NULL);
if (r_high)
*r_high = buf32_to_ulong (digest+12);
return buf32_to_ulong (digest + 16);
}
/* Return the so called KEYGRIP which is the SHA-1 hash of the public
key parameters expressed as an canoncial encoded S-Exp. ARRAY must
be 20 bytes long. Returns ARRAY or a newly allocated buffer if ARRAY was
given as NULL. May return NULL on error. */
unsigned char *
gpgsm_get_keygrip (ksba_cert_t cert, unsigned char *array)
{
gcry_sexp_t s_pkey;
int rc;
ksba_sexp_t p;
size_t n;
p = ksba_cert_get_public_key (cert);
if (!p)
return NULL; /* oops */
if (DBG_X509)
log_debug ("get_keygrip for public key\n");
n = gcry_sexp_canon_len (p, 0, NULL, NULL);
if (!n)
{
log_error ("libksba did not return a proper S-Exp\n");
return NULL;
}
rc = gcry_sexp_sscan ( &s_pkey, NULL, (char*)p, n);
xfree (p);
if (rc)
{
log_error ("gcry_sexp_scan failed: %s\n", gpg_strerror (rc));
return NULL;
}
array = gcry_pk_get_keygrip (s_pkey, array);
gcry_sexp_release (s_pkey);
if (!array)
{
log_error ("can't calculate keygrip\n");
return NULL;
}
if (DBG_X509)
- log_printhex ("keygrip=", array, 20);
+ log_printhex (array, 20, "keygrip=");
return array;
}
/* Return an allocated buffer with the keygrip of CERT encoded as a
hexstring. NULL is returned in case of error. */
char *
gpgsm_get_keygrip_hexstring (ksba_cert_t cert)
{
unsigned char grip[20];
char *buf;
if (!gpgsm_get_keygrip (cert, grip))
return NULL;
buf = xtrymalloc (20*2+1);
if (buf)
bin2hex (grip, 20, buf);
return buf;
}
/* Return the PK algorithm used by CERT as well as the length in bits
of the public key at NBITS. */
int
gpgsm_get_key_algo_info (ksba_cert_t cert, unsigned int *nbits)
{
gcry_sexp_t s_pkey;
int rc;
ksba_sexp_t p;
size_t n;
gcry_sexp_t l1, l2;
const char *name;
char namebuf[128];
if (nbits)
*nbits = 0;
p = ksba_cert_get_public_key (cert);
if (!p)
return 0;
n = gcry_sexp_canon_len (p, 0, NULL, NULL);
if (!n)
{
xfree (p);
return 0;
}
rc = gcry_sexp_sscan (&s_pkey, NULL, (char *)p, n);
xfree (p);
if (rc)
return 0;
if (nbits)
*nbits = gcry_pk_get_nbits (s_pkey);
/* Breaking the algorithm out of the S-exp is a bit of a challenge ... */
l1 = gcry_sexp_find_token (s_pkey, "public-key", 0);
if (!l1)
{
gcry_sexp_release (s_pkey);
return 0;
}
l2 = gcry_sexp_cadr (l1);
gcry_sexp_release (l1);
l1 = l2;
name = gcry_sexp_nth_data (l1, 0, &n);
if (name)
{
if (n > sizeof namebuf -1)
n = sizeof namebuf -1;
memcpy (namebuf, name, n);
namebuf[n] = 0;
}
else
*namebuf = 0;
gcry_sexp_release (l1);
gcry_sexp_release (s_pkey);
return gcry_pk_map_name (namebuf);
}
/* For certain purposes we need a certificate id which has an upper
limit of the size. We use the hash of the issuer name and the
serial number for this. In most cases the serial number is not
that large and the resulting string can be passed on an assuan
command line. Everything is hexencoded with the serialnumber
delimited from the hash by a dot.
The caller must free the string.
*/
char *
gpgsm_get_certid (ksba_cert_t cert)
{
ksba_sexp_t serial;
char *p;
char *endp;
unsigned char hash[20];
unsigned long n;
char *certid;
int i;
p = ksba_cert_get_issuer (cert, 0);
if (!p)
return NULL; /* Ooops: No issuer */
gcry_md_hash_buffer (GCRY_MD_SHA1, hash, p, strlen (p));
xfree (p);
serial = ksba_cert_get_serial (cert);
if (!serial)
return NULL; /* oops: no serial number */
p = (char *)serial;
if (*p != '(')
{
log_error ("Ooops: invalid serial number\n");
xfree (serial);
return NULL;
}
p++;
n = strtoul (p, &endp, 10);
p = endp;
if (*p != ':')
{
log_error ("Ooops: invalid serial number (no colon)\n");
xfree (serial);
return NULL;
}
p++;
certid = xtrymalloc ( 40 + 1 + n*2 + 1);
if (!certid)
{
xfree (serial);
return NULL; /* out of core */
}
for (i=0, endp = certid; i < 20; i++, endp += 2 )
sprintf (endp, "%02X", hash[i]);
*endp++ = '.';
for (i=0; i < n; i++, endp += 2)
sprintf (endp, "%02X", ((unsigned char*)p)[i]);
*endp = 0;
xfree (serial);
return certid;
}
diff --git a/sm/import.c b/sm/import.c
index 8796cd206..ca693824a 100644
--- a/sm/import.c
+++ b/sm/import.c
@@ -1,939 +1,939 @@
/* import.c - Import certificates
* Copyright (C) 2001, 2003, 2004, 2009, 2010 Free Software Foundation, Inc.
*
* This file is part of GnuPG.
*
* GnuPG is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* GnuPG is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see .
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include "gpgsm.h"
#include
#include
#include "keydb.h"
#include "../common/exechelp.h"
#include "../common/i18n.h"
#include "../common/sysutils.h"
#include "../kbx/keybox.h" /* for KEYBOX_FLAG_* */
#include "../common/membuf.h"
#include "minip12.h"
/* The arbitrary limit of one PKCS#12 object. */
#define MAX_P12OBJ_SIZE 128 /*kb*/
struct stats_s {
unsigned long count;
unsigned long imported;
unsigned long unchanged;
unsigned long not_imported;
unsigned long secret_read;
unsigned long secret_imported;
unsigned long secret_dups;
};
struct rsa_secret_key_s
{
gcry_mpi_t n; /* public modulus */
gcry_mpi_t e; /* public exponent */
gcry_mpi_t d; /* exponent */
gcry_mpi_t p; /* prime p. */
gcry_mpi_t q; /* prime q. */
gcry_mpi_t u; /* inverse of p mod q. */
};
static gpg_error_t parse_p12 (ctrl_t ctrl, ksba_reader_t reader,
struct stats_s *stats);
static void
print_imported_status (ctrl_t ctrl, ksba_cert_t cert, int new_cert)
{
char *fpr;
fpr = gpgsm_get_fingerprint_hexstring (cert, GCRY_MD_SHA1);
if (new_cert)
gpgsm_status2 (ctrl, STATUS_IMPORTED, fpr, "[X.509]", NULL);
gpgsm_status2 (ctrl, STATUS_IMPORT_OK,
new_cert? "1":"0", fpr, NULL);
xfree (fpr);
}
/* Print an IMPORT_PROBLEM status. REASON is one of:
0 := "No specific reason given".
1 := "Invalid Certificate".
2 := "Issuer Certificate missing".
3 := "Certificate Chain too long".
4 := "Error storing certificate".
*/
static void
print_import_problem (ctrl_t ctrl, ksba_cert_t cert, int reason)
{
char *fpr = NULL;
char buf[25];
int i;
sprintf (buf, "%d", reason);
if (cert)
{
fpr = gpgsm_get_fingerprint_hexstring (cert, GCRY_MD_SHA1);
/* detetect an error (all high) value */
for (i=0; fpr[i] == 'F'; i++)
;
if (!fpr[i])
{
xfree (fpr);
fpr = NULL;
}
}
gpgsm_status2 (ctrl, STATUS_IMPORT_PROBLEM, buf, fpr, NULL);
xfree (fpr);
}
void
print_imported_summary (ctrl_t ctrl, struct stats_s *stats)
{
char buf[14*25];
if (!opt.quiet)
{
log_info (_("total number processed: %lu\n"), stats->count);
if (stats->imported)
{
log_info (_(" imported: %lu"), stats->imported );
log_printf ("\n");
}
if (stats->unchanged)
log_info (_(" unchanged: %lu\n"), stats->unchanged);
if (stats->secret_read)
log_info (_(" secret keys read: %lu\n"), stats->secret_read );
if (stats->secret_imported)
log_info (_(" secret keys imported: %lu\n"), stats->secret_imported );
if (stats->secret_dups)
log_info (_(" secret keys unchanged: %lu\n"), stats->secret_dups );
if (stats->not_imported)
log_info (_(" not imported: %lu\n"), stats->not_imported);
}
sprintf(buf, "%lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu",
stats->count,
0l /*stats->no_user_id*/,
stats->imported,
0l /*stats->imported_rsa*/,
stats->unchanged,
0l /*stats->n_uids*/,
0l /*stats->n_subk*/,
0l /*stats->n_sigs*/,
0l /*stats->n_revoc*/,
stats->secret_read,
stats->secret_imported,
stats->secret_dups,
0l /*stats->skipped_new_keys*/,
stats->not_imported
);
gpgsm_status (ctrl, STATUS_IMPORT_RES, buf);
}
static void
check_and_store (ctrl_t ctrl, struct stats_s *stats,
ksba_cert_t cert, int depth)
{
int rc;
if (stats)
stats->count++;
if ( depth >= 50 )
{
log_error (_("certificate chain too long\n"));
if (stats)
stats->not_imported++;
print_import_problem (ctrl, cert, 3);
return;
}
/* Some basic checks, but don't care about missing certificates;
this is so that we are able to import entire certificate chains
w/o requiring a special order (i.e. root-CA first). This used
to be different but because gpgsm_verify even imports
certificates without any checks, it doesn't matter much and the
code gets much cleaner. A housekeeping function to remove
certificates w/o an anchor would be nice, though.
Optionally we do a full validation in addition to the basic test.
*/
rc = gpgsm_basic_cert_check (ctrl, cert);
if (!rc && ctrl->with_validation)
rc = gpgsm_validate_chain (ctrl, cert, "", NULL, 0, NULL, 0, NULL);
if (!rc || (!ctrl->with_validation
&& (gpg_err_code (rc) == GPG_ERR_MISSING_CERT
|| gpg_err_code (rc) == GPG_ERR_MISSING_ISSUER_CERT)))
{
int existed;
if (!keydb_store_cert (ctrl, cert, 0, &existed))
{
ksba_cert_t next = NULL;
if (!existed)
{
print_imported_status (ctrl, cert, 1);
if (stats)
stats->imported++;
}
else
{
print_imported_status (ctrl, cert, 0);
if (stats)
stats->unchanged++;
}
if (opt.verbose > 1 && existed)
{
if (depth)
log_info ("issuer certificate already in DB\n");
else
log_info ("certificate already in DB\n");
}
else if (opt.verbose && !existed)
{
if (depth)
log_info ("issuer certificate imported\n");
else
log_info ("certificate imported\n");
}
/* Now lets walk up the chain and import all certificates up
the chain. This is required in case we already stored
parent certificates in the ephemeral keybox. Do not
update the statistics, though. */
if (!gpgsm_walk_cert_chain (ctrl, cert, &next))
{
check_and_store (ctrl, NULL, next, depth+1);
ksba_cert_release (next);
}
}
else
{
log_error (_("error storing certificate\n"));
if (stats)
stats->not_imported++;
print_import_problem (ctrl, cert, 4);
}
}
else
{
log_error (_("basic certificate checks failed - not imported\n"));
if (stats)
stats->not_imported++;
/* We keep the test for GPG_ERR_MISSING_CERT only in case
GPG_ERR_MISSING_CERT has been used instead of the newer
GPG_ERR_MISSING_ISSUER_CERT. */
print_import_problem
(ctrl, cert,
gpg_err_code (rc) == GPG_ERR_MISSING_ISSUER_CERT? 2 :
gpg_err_code (rc) == GPG_ERR_MISSING_CERT? 2 :
gpg_err_code (rc) == GPG_ERR_BAD_CERT? 1 : 0);
}
}
static int
import_one (ctrl_t ctrl, struct stats_s *stats, int in_fd)
{
int rc;
gnupg_ksba_io_t b64reader = NULL;
ksba_reader_t reader;
ksba_cert_t cert = NULL;
ksba_cms_t cms = NULL;
estream_t fp = NULL;
ksba_content_type_t ct;
int any = 0;
fp = es_fdopen_nc (in_fd, "rb");
if (!fp)
{
rc = gpg_error_from_syserror ();
log_error ("fdopen() failed: %s\n", strerror (errno));
goto leave;
}
rc = gnupg_ksba_create_reader
(&b64reader, ((ctrl->is_pem? GNUPG_KSBA_IO_PEM : 0)
| (ctrl->is_base64? GNUPG_KSBA_IO_BASE64 : 0)
| (ctrl->autodetect_encoding? GNUPG_KSBA_IO_AUTODETECT : 0)
| GNUPG_KSBA_IO_MULTIPEM),
fp, &reader);
if (rc)
{
log_error ("can't create reader: %s\n", gpg_strerror (rc));
goto leave;
}
/* We need to loop here to handle multiple PEM objects in one
file. */
do
{
ksba_cms_release (cms); cms = NULL;
ksba_cert_release (cert); cert = NULL;
ct = ksba_cms_identify (reader);
if (ct == KSBA_CT_SIGNED_DATA)
{ /* This is probably a signed-only message - import the certs */
ksba_stop_reason_t stopreason;
int i;
rc = ksba_cms_new (&cms);
if (rc)
goto leave;
rc = ksba_cms_set_reader_writer (cms, reader, NULL);
if (rc)
{
log_error ("ksba_cms_set_reader_writer failed: %s\n",
gpg_strerror (rc));
goto leave;
}
do
{
rc = ksba_cms_parse (cms, &stopreason);
if (rc)
{
log_error ("ksba_cms_parse failed: %s\n", gpg_strerror (rc));
goto leave;
}
if (stopreason == KSBA_SR_BEGIN_DATA)
log_info ("not a certs-only message\n");
}
while (stopreason != KSBA_SR_READY);
for (i=0; (cert=ksba_cms_get_cert (cms, i)); i++)
{
check_and_store (ctrl, stats, cert, 0);
ksba_cert_release (cert);
cert = NULL;
}
if (!i)
log_error ("no certificate found\n");
else
any = 1;
}
else if (ct == KSBA_CT_PKCS12)
{
/* This seems to be a pkcs12 message. */
rc = parse_p12 (ctrl, reader, stats);
if (!rc)
any = 1;
}
else if (ct == KSBA_CT_NONE)
{ /* Failed to identify this message - assume a certificate */
rc = ksba_cert_new (&cert);
if (rc)
goto leave;
rc = ksba_cert_read_der (cert, reader);
if (rc)
goto leave;
check_and_store (ctrl, stats, cert, 0);
any = 1;
}
else
{
log_error ("can't extract certificates from input\n");
rc = gpg_error (GPG_ERR_NO_DATA);
}
ksba_reader_clear (reader, NULL, NULL);
}
while (!gnupg_ksba_reader_eof_seen (b64reader));
leave:
if (any && gpg_err_code (rc) == GPG_ERR_EOF)
rc = 0;
ksba_cms_release (cms);
ksba_cert_release (cert);
gnupg_ksba_destroy_reader (b64reader);
es_fclose (fp);
return rc;
}
/* Re-import certifciates. IN_FD is a list of linefeed delimited
fingerprints t re-import. The actual re-import is done by clearing
the ephemeral flag. */
static int
reimport_one (ctrl_t ctrl, struct stats_s *stats, int in_fd)
{
gpg_error_t err = 0;
estream_t fp = NULL;
char line[100]; /* Sufficient for a fingerprint. */
KEYDB_HANDLE kh;
KEYDB_SEARCH_DESC desc;
ksba_cert_t cert = NULL;
unsigned int flags;
kh = keydb_new ();
if (!kh)
{
err = gpg_error (GPG_ERR_ENOMEM);;
log_error (_("failed to allocate keyDB handle\n"));
goto leave;
}
keydb_set_ephemeral (kh, 1);
fp = es_fdopen_nc (in_fd, "r");
if (!fp)
{
err = gpg_error_from_syserror ();
log_error ("es_fdopen(%d) failed: %s\n", in_fd, gpg_strerror (err));
goto leave;
}
while (es_fgets (line, DIM(line)-1, fp) )
{
if (*line && line[strlen(line)-1] != '\n')
{
err = gpg_error (GPG_ERR_LINE_TOO_LONG);
goto leave;
}
trim_spaces (line);
if (!*line)
continue;
stats->count++;
err = classify_user_id (line, &desc, 0);
if (err)
{
print_import_problem (ctrl, NULL, 0);
stats->not_imported++;
continue;
}
keydb_search_reset (kh);
err = keydb_search (ctrl, kh, &desc, 1);
if (err)
{
print_import_problem (ctrl, NULL, 0);
stats->not_imported++;
continue;
}
ksba_cert_release (cert);
cert = NULL;
err = keydb_get_cert (kh, &cert);
if (err)
{
log_error ("keydb_get_cert() failed: %s\n", gpg_strerror (err));
print_import_problem (ctrl, NULL, 1);
stats->not_imported++;
continue;
}
err = keydb_get_flags (kh, KEYBOX_FLAG_BLOB, 0, &flags);
if (err)
{
log_error (_("error getting stored flags: %s\n"), gpg_strerror (err));
print_imported_status (ctrl, cert, 0);
stats->not_imported++;
continue;
}
if ( !(flags & KEYBOX_FLAG_BLOB_EPHEMERAL) )
{
print_imported_status (ctrl, cert, 0);
stats->unchanged++;
continue;
}
err = keydb_set_cert_flags (ctrl, cert, 1, KEYBOX_FLAG_BLOB, 0,
KEYBOX_FLAG_BLOB_EPHEMERAL, 0);
if (err)
{
log_error ("clearing ephemeral flag failed: %s\n",
gpg_strerror (err));
print_import_problem (ctrl, cert, 0);
stats->not_imported++;
continue;
}
print_imported_status (ctrl, cert, 1);
stats->imported++;
}
err = 0;
if (es_ferror (fp))
{
err = gpg_error_from_syserror ();
log_error ("error reading fd %d: %s\n", in_fd, gpg_strerror (err));
goto leave;
}
leave:
ksba_cert_release (cert);
keydb_release (kh);
es_fclose (fp);
return err;
}
int
gpgsm_import (ctrl_t ctrl, int in_fd, int reimport_mode)
{
int rc;
struct stats_s stats;
memset (&stats, 0, sizeof stats);
if (reimport_mode)
rc = reimport_one (ctrl, &stats, in_fd);
else
rc = import_one (ctrl, &stats, in_fd);
print_imported_summary (ctrl, &stats);
/* If we never printed an error message do it now so that a command
line invocation will return with an error (log_error keeps a
global errorcount) */
if (rc && !log_get_errorcount (0))
log_error (_("error importing certificate: %s\n"), gpg_strerror (rc));
return rc;
}
int
gpgsm_import_files (ctrl_t ctrl, int nfiles, char **files,
int (*of)(const char *fname))
{
int rc = 0;
struct stats_s stats;
memset (&stats, 0, sizeof stats);
if (!nfiles)
rc = import_one (ctrl, &stats, 0);
else
{
for (; nfiles && !rc ; nfiles--, files++)
{
int fd = of (*files);
rc = import_one (ctrl, &stats, fd);
close (fd);
if (rc == -1)
rc = 0;
}
}
print_imported_summary (ctrl, &stats);
/* If we never printed an error message do it now so that a command
line invocation will return with an error (log_error keeps a
global errorcount) */
if (rc && !log_get_errorcount (0))
log_error (_("error importing certificate: %s\n"), gpg_strerror (rc));
return rc;
}
/* Check that the RSA secret key SKEY is valid. Swap parameters to
the libgcrypt standard. */
static gpg_error_t
rsa_key_check (struct rsa_secret_key_s *skey)
{
int err = 0;
gcry_mpi_t t = gcry_mpi_snew (0);
gcry_mpi_t t1 = gcry_mpi_snew (0);
gcry_mpi_t t2 = gcry_mpi_snew (0);
gcry_mpi_t phi = gcry_mpi_snew (0);
/* Check that n == p * q. */
gcry_mpi_mul (t, skey->p, skey->q);
if (gcry_mpi_cmp( t, skey->n) )
{
log_error ("RSA oops: n != p * q\n");
err++;
}
/* Check that p is less than q. */
if (gcry_mpi_cmp (skey->p, skey->q) > 0)
{
gcry_mpi_t tmp;
log_info ("swapping secret primes\n");
tmp = gcry_mpi_copy (skey->p);
gcry_mpi_set (skey->p, skey->q);
gcry_mpi_set (skey->q, tmp);
gcry_mpi_release (tmp);
/* Recompute u. */
gcry_mpi_invm (skey->u, skey->p, skey->q);
}
/* Check that e divides neither p-1 nor q-1. */
gcry_mpi_sub_ui (t, skey->p, 1 );
gcry_mpi_div (NULL, t, t, skey->e, 0);
if (!gcry_mpi_cmp_ui( t, 0) )
{
log_error ("RSA oops: e divides p-1\n");
err++;
}
gcry_mpi_sub_ui (t, skey->q, 1);
gcry_mpi_div (NULL, t, t, skey->e, 0);
if (!gcry_mpi_cmp_ui( t, 0))
{
log_info ("RSA oops: e divides q-1\n" );
err++;
}
/* Check that d is correct. */
gcry_mpi_sub_ui (t1, skey->p, 1);
gcry_mpi_sub_ui (t2, skey->q, 1);
gcry_mpi_mul (phi, t1, t2);
gcry_mpi_invm (t, skey->e, phi);
if (gcry_mpi_cmp (t, skey->d))
{
/* No: try universal exponent. */
gcry_mpi_gcd (t, t1, t2);
gcry_mpi_div (t, NULL, phi, t, 0);
gcry_mpi_invm (t, skey->e, t);
if (gcry_mpi_cmp (t, skey->d))
{
log_error ("RSA oops: bad secret exponent\n");
err++;
}
}
/* Check for correctness of u. */
gcry_mpi_invm (t, skey->p, skey->q);
if (gcry_mpi_cmp (t, skey->u))
{
log_info ("RSA oops: bad u parameter\n");
err++;
}
if (err)
log_info ("RSA secret key check failed\n");
gcry_mpi_release (t);
gcry_mpi_release (t1);
gcry_mpi_release (t2);
gcry_mpi_release (phi);
return err? gpg_error (GPG_ERR_BAD_SECKEY):0;
}
/* Object passed to store_cert_cb. */
struct store_cert_parm_s
{
gpg_error_t err; /* First error seen. */
struct stats_s *stats; /* The stats object. */
ctrl_t ctrl; /* The control object. */
};
/* Helper to store the DER encoded certificate CERTDATA of length
CERTDATALEN. */
static void
store_cert_cb (void *opaque,
const unsigned char *certdata, size_t certdatalen)
{
struct store_cert_parm_s *parm = opaque;
gpg_error_t err;
ksba_cert_t cert;
err = ksba_cert_new (&cert);
if (err)
{
if (!parm->err)
parm->err = err;
return;
}
err = ksba_cert_init_from_mem (cert, certdata, certdatalen);
if (err)
{
log_error ("failed to parse a certificate: %s\n", gpg_strerror (err));
if (!parm->err)
parm->err = err;
}
else
check_and_store (parm->ctrl, parm->stats, cert, 0);
ksba_cert_release (cert);
}
/* Assume that the reader is at a pkcs#12 message and try to import
certificates from that stupid format. We will transfer secret
keys to the agent. */
static gpg_error_t
parse_p12 (ctrl_t ctrl, ksba_reader_t reader, struct stats_s *stats)
{
gpg_error_t err = 0;
char buffer[1024];
size_t ntotal, nread;
membuf_t p12mbuf;
char *p12buffer = NULL;
size_t p12buflen;
size_t p12bufoff;
gcry_mpi_t *kparms = NULL;
struct rsa_secret_key_s sk;
char *passphrase = NULL;
unsigned char *key = NULL;
size_t keylen;
void *kek = NULL;
size_t keklen;
unsigned char *wrappedkey = NULL;
size_t wrappedkeylen;
gcry_cipher_hd_t cipherhd = NULL;
gcry_sexp_t s_key = NULL;
unsigned char grip[20];
int bad_pass = 0;
int i;
struct store_cert_parm_s store_cert_parm;
memset (&store_cert_parm, 0, sizeof store_cert_parm);
store_cert_parm.ctrl = ctrl;
store_cert_parm.stats = stats;
init_membuf (&p12mbuf, 4096);
ntotal = 0;
while (!(err = ksba_reader_read (reader, buffer, sizeof buffer, &nread)))
{
if (ntotal >= MAX_P12OBJ_SIZE*1024)
{
/* Arbitrary limit to avoid DoS attacks. */
err = gpg_error (GPG_ERR_TOO_LARGE);
log_error ("pkcs#12 object is larger than %dk\n", MAX_P12OBJ_SIZE);
break;
}
put_membuf (&p12mbuf, buffer, nread);
ntotal += nread;
}
if (gpg_err_code (err) == GPG_ERR_EOF)
err = 0;
if (!err)
{
p12buffer = get_membuf (&p12mbuf, &p12buflen);
if (!p12buffer)
err = gpg_error_from_syserror ();
}
if (err)
{
log_error (_("error reading input: %s\n"), gpg_strerror (err));
goto leave;
}
/* GnuPG 2.0.4 accidentally created binary P12 files with the string
"The passphrase is %s encoded.\n\n" prepended to the ASN.1 data.
We fix that here. */
if (p12buflen > 29 && !memcmp (p12buffer, "The passphrase is ", 18))
{
for (p12bufoff=18;
p12bufoff < p12buflen && p12buffer[p12bufoff] != '\n';
p12bufoff++)
;
p12bufoff++;
if (p12bufoff < p12buflen && p12buffer[p12bufoff] == '\n')
p12bufoff++;
}
else
p12bufoff = 0;
err = gpgsm_agent_ask_passphrase
(ctrl,
i18n_utf8 ("Please enter the passphrase to unprotect the PKCS#12 object."),
0, &passphrase);
if (err)
goto leave;
kparms = p12_parse (p12buffer + p12bufoff, p12buflen - p12bufoff,
passphrase, store_cert_cb, &store_cert_parm, &bad_pass);
xfree (passphrase);
passphrase = NULL;
if (!kparms)
{
log_error ("error parsing or decrypting the PKCS#12 file\n");
err = gpg_error (GPG_ERR_INV_OBJ);
goto leave;
}
/* print_mpi (" n", kparms[0]); */
/* print_mpi (" e", kparms[1]); */
/* print_mpi (" d", kparms[2]); */
/* print_mpi (" p", kparms[3]); */
/* print_mpi (" q", kparms[4]); */
/* print_mpi ("dmp1", kparms[5]); */
/* print_mpi ("dmq1", kparms[6]); */
/* print_mpi (" u", kparms[7]); */
sk.n = kparms[0];
sk.e = kparms[1];
sk.d = kparms[2];
sk.q = kparms[3];
sk.p = kparms[4];
sk.u = kparms[7];
err = rsa_key_check (&sk);
if (err)
goto leave;
/* print_mpi (" n", sk.n); */
/* print_mpi (" e", sk.e); */
/* print_mpi (" d", sk.d); */
/* print_mpi (" p", sk.p); */
/* print_mpi (" q", sk.q); */
/* print_mpi (" u", sk.u); */
/* Create an S-expression from the parameters. */
err = gcry_sexp_build (&s_key, NULL,
"(private-key(rsa(n%m)(e%m)(d%m)(p%m)(q%m)(u%m)))",
sk.n, sk.e, sk.d, sk.p, sk.q, sk.u, NULL);
for (i=0; i < 8; i++)
gcry_mpi_release (kparms[i]);
gcry_free (kparms);
kparms = NULL;
if (err)
{
log_error ("failed to create S-expression from key: %s\n",
gpg_strerror (err));
goto leave;
}
/* Compute the keygrip. */
if (!gcry_pk_get_keygrip (s_key, grip))
{
err = gpg_error (GPG_ERR_GENERAL);
log_error ("can't calculate keygrip\n");
goto leave;
}
- log_printhex ("keygrip=", grip, 20);
+ log_printhex (grip, 20, "keygrip=");
/* Convert to canonical encoding using a function which pads it to a
multiple of 64 bits. We need this padding for AESWRAP. */
err = make_canon_sexp_pad (s_key, 1, &key, &keylen);
if (err)
{
log_error ("error creating canonical S-expression\n");
goto leave;
}
gcry_sexp_release (s_key);
s_key = NULL;
/* Get the current KEK. */
err = gpgsm_agent_keywrap_key (ctrl, 0, &kek, &keklen);
if (err)
{
log_error ("error getting the KEK: %s\n", gpg_strerror (err));
goto leave;
}
/* Wrap the key. */
err = gcry_cipher_open (&cipherhd, GCRY_CIPHER_AES128,
GCRY_CIPHER_MODE_AESWRAP, 0);
if (err)
goto leave;
err = gcry_cipher_setkey (cipherhd, kek, keklen);
if (err)
goto leave;
xfree (kek);
kek = NULL;
wrappedkeylen = keylen + 8;
wrappedkey = xtrymalloc (wrappedkeylen);
if (!wrappedkey)
{
err = gpg_error_from_syserror ();
goto leave;
}
err = gcry_cipher_encrypt (cipherhd, wrappedkey, wrappedkeylen, key, keylen);
if (err)
goto leave;
xfree (key);
key = NULL;
gcry_cipher_close (cipherhd);
cipherhd = NULL;
/* Send the wrapped key to the agent. */
err = gpgsm_agent_import_key (ctrl, wrappedkey, wrappedkeylen);
if (!err)
{
stats->count++;
stats->secret_read++;
stats->secret_imported++;
}
else if ( gpg_err_code (err) == GPG_ERR_EEXIST )
{
err = 0;
stats->count++;
stats->secret_read++;
stats->secret_dups++;
}
/* If we did not get an error from storing the secret key we return
a possible error from parsing the certificates. We do this after
storing the secret keys so that a bad certificate does not
inhibit our chance to store the secret key. */
if (!err && store_cert_parm.err)
err = store_cert_parm.err;
leave:
if (kparms)
{
for (i=0; i < 8; i++)
gcry_mpi_release (kparms[i]);
gcry_free (kparms);
kparms = NULL;
}
xfree (key);
gcry_sexp_release (s_key);
xfree (passphrase);
gcry_cipher_close (cipherhd);
xfree (wrappedkey);
xfree (kek);
xfree (get_membuf (&p12mbuf, NULL));
xfree (p12buffer);
if (bad_pass)
{
/* We only write a plain error code and not direct
BAD_PASSPHRASE because the pkcs12 parser might issue this
message multiple times, BAD_PASSPHRASE in general requires a
keyID and parts of the import might actually succeed so that
IMPORT_PROBLEM is also not appropriate. */
gpgsm_status_with_err_code (ctrl, STATUS_ERROR,
"import.parsep12", GPG_ERR_BAD_PASSPHRASE);
}
return err;
}
diff --git a/sm/verify.c b/sm/verify.c
index a047f9456..6d2f11055 100644
--- a/sm/verify.c
+++ b/sm/verify.c
@@ -1,696 +1,696 @@
/* verify.c - Verify a messages signature
* Copyright (C) 2001, 2002, 2003, 2007,
* 2010 Free Software Foundation, Inc.
*
* This file is part of GnuPG.
*
* GnuPG is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* GnuPG is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see .
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include "gpgsm.h"
#include