diff --git a/agent/cvt-openpgp.c b/agent/cvt-openpgp.c index 5f944934a..cadc87180 100644 --- a/agent/cvt-openpgp.c +++ b/agent/cvt-openpgp.c @@ -1,1469 +1,1470 @@ /* 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 "i18n.h" #include "cvt-openpgp.h" +#include "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 if (!strcmp (curve, openpgp_curve_to_oid ("Ed25519", NULL))) err = gcry_sexp_build (&s_pkey, NULL, "(public-key(ecc(curve %s)(flags eddsa)(q%m)))", "Ed25519", pkey[0]); else err = gcry_sexp_build (&s_pkey, NULL, "(public-key(ecc(curve %s)(q%m)))", 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 if (!strcmp (curve, openpgp_curve_to_oid ("Ed25519", NULL))) { /* Do not store the OID as name but the real name and the EdDSA flag. */ err = gcry_sexp_build (&s_skey, NULL, "(private-key(ecc(curve%s)(flags eddsa)" "(q%m)(d%m)))", "Ed25519", skey[0], skey[1]); } else err = gcry_sexp_build (&s_skey, NULL, "(private-key(ecc(curve%s)(q%m)(d%m)))", 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 if (!strcmp (curve, openpgp_curve_to_oid ("Ed25519", NULL))) { /* Do not store the OID as name but the real name and the EdDSA flag. */ err = gcry_sexp_build (&s_skey, NULL, "(protected-private-key(ecc(curve%s)(flags eddsa)(q%m)" "(protected openpgp-native%S)))", "Ed25519", skey[0], transfer_key); } else err = gcry_sexp_build (&s_skey, NULL, "(protected-private-key(ecc(curve%s)(q%m)" "(protected openpgp-native%S)))", 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 = p[ndata-2] << 8 | p[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 = (data[ndata-2] << 8 | data[ndata-1]); + 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 == ((p[0] << 8 | p[1]) + 7)/8 + 2)) + 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 int 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, 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 || curve) { /* Encrypted parameters and ECC parameters need or can be stored as opaque. */ skey[skeyidx] = gcry_mpi_set_opaque_copy (NULL, value, valuelen*8); if (!skey[skeyidx]) goto outofmem; if (is_enc) 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_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_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 (!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) + 100); if (!pi) return gpg_error_from_syserror (); pi->max_length = 100; 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 (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); 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, 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, 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, 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)) 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++ ) { if (gcry_mpi_get_flag (array[i], GCRYMPI_FLAG_OPAQUE)) { const void *s; unsigned int n; s = gcry_mpi_get_opaque (array[i], &n); nbits[j] = n; n = (n+7)/8; narr[j] = n; bufarr[j] = gcry_is_secure (s)? xtrymalloc_secure (n):xtrymalloc (n); if (!bufarr[j]) { err = gpg_error_from_syserror (); for (i = 0; i < j; i++) xfree (bufarr[i]); return err; } memcpy (bufarr[j], s, n); } else { 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")) { 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); if (flags) { gcry_sexp_t param = gcry_sexp_find_token (flags, "param", 0); if (param) { gcry_sexp_release (param); array[6] = array[0]; array[7] = array[1]; err = gcry_sexp_extract_param (list, NULL, "pabgnh?", array+0, array+1, array+2, array+3, array+4, array+5, NULL); if (array[5] == NULL) { array[5] = GCRYMPI_CONST_ONE; npkey += 6; nskey += 6; } format = "pabgnhqd?"; } } } else if (!strcmp (name, "ecdsa")) { algoname = "ecdsa"; format = "pabgnqd?"; npkey = 6; nskey = 7; err = gcry_sexp_extract_param (list, NULL, format, array+0, array+1, array+2, array+3, array+4, array+5, array+6, NULL); } else if (!strcmp (name, "ecdh")) { algoname = "ecdh"; format = "pabgnqd?"; npkey = 6; nskey= 7; err = gcry_sexp_extract_param (list, NULL, format, array+0, array+1, array+2, array+3, array+4, array+5, array+6, 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) { if (format[0] == '/') /* It is opaque data qualifier, skip it. */ *r_elems = format+1; else *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/common/b64enc.c b/common/b64enc.c index 91ba69d48..087f27c9d 100644 --- a/common/b64enc.c +++ b/common/b64enc.c @@ -1,432 +1,432 @@ /* b64enc.c - Simple Base64 encoder. * Copyright (C) 2001, 2003, 2004, 2008, 2010, * 2011 Free Software Foundation, Inc. * * This file is part of GnuPG. * * This file is free software; you can redistribute it and/or modify * it 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. * * This file 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 "i18n.h" #include "util.h" #define B64ENC_DID_HEADER 1 #define B64ENC_DID_TRAILER 2 #define B64ENC_NO_LINEFEEDS 16 #define B64ENC_USE_PGPCRC 32 /* The base-64 character list */ static unsigned char bintoasc[64] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" "abcdefghijklmnopqrstuvwxyz" "0123456789+/"; /* Stuff required to create the OpenPGP CRC. This crc_table has been created using this code: #include #include #define CRCPOLY 0x864CFB int main (void) { int i, j; uint32_t t; uint32_t crc_table[256]; crc_table[0] = 0; for (i=j=0; j < 128; j++ ) { t = crc_table[j]; if ( (t & 0x00800000) ) { t <<= 1; crc_table[i++] = t ^ CRCPOLY; crc_table[i++] = t; } else { t <<= 1; crc_table[i++] = t; crc_table[i++] = t ^ CRCPOLY; } } puts ("static const u32 crc_table[256] = {"); for (i=j=0; i < 256; i++) { printf ("%s 0x%08lx", j? "":" ", (unsigned long)crc_table[i]); if (i != 255) { putchar (','); if ( ++j > 5) { j = 0; putchar ('\n'); } } } puts ("\n};"); return 0; } */ #define CRCINIT 0xB704CE static const u32 crc_table[256] = { 0x00000000, 0x00864cfb, 0x018ad50d, 0x010c99f6, 0x0393e6e1, 0x0315aa1a, 0x021933ec, 0x029f7f17, 0x07a18139, 0x0727cdc2, 0x062b5434, 0x06ad18cf, 0x043267d8, 0x04b42b23, 0x05b8b2d5, 0x053efe2e, 0x0fc54e89, 0x0f430272, 0x0e4f9b84, 0x0ec9d77f, 0x0c56a868, 0x0cd0e493, 0x0ddc7d65, 0x0d5a319e, 0x0864cfb0, 0x08e2834b, 0x09ee1abd, 0x09685646, 0x0bf72951, 0x0b7165aa, 0x0a7dfc5c, 0x0afbb0a7, 0x1f0cd1e9, 0x1f8a9d12, 0x1e8604e4, 0x1e00481f, 0x1c9f3708, 0x1c197bf3, 0x1d15e205, 0x1d93aefe, 0x18ad50d0, 0x182b1c2b, 0x192785dd, 0x19a1c926, 0x1b3eb631, 0x1bb8faca, 0x1ab4633c, 0x1a322fc7, 0x10c99f60, 0x104fd39b, 0x11434a6d, 0x11c50696, 0x135a7981, 0x13dc357a, 0x12d0ac8c, 0x1256e077, 0x17681e59, 0x17ee52a2, 0x16e2cb54, 0x166487af, 0x14fbf8b8, 0x147db443, 0x15712db5, 0x15f7614e, 0x3e19a3d2, 0x3e9fef29, 0x3f9376df, 0x3f153a24, 0x3d8a4533, 0x3d0c09c8, 0x3c00903e, 0x3c86dcc5, 0x39b822eb, 0x393e6e10, 0x3832f7e6, 0x38b4bb1d, 0x3a2bc40a, 0x3aad88f1, 0x3ba11107, 0x3b275dfc, 0x31dced5b, 0x315aa1a0, 0x30563856, 0x30d074ad, 0x324f0bba, 0x32c94741, 0x33c5deb7, 0x3343924c, 0x367d6c62, 0x36fb2099, 0x37f7b96f, 0x3771f594, 0x35ee8a83, 0x3568c678, 0x34645f8e, 0x34e21375, 0x2115723b, 0x21933ec0, 0x209fa736, 0x2019ebcd, 0x228694da, 0x2200d821, 0x230c41d7, 0x238a0d2c, 0x26b4f302, 0x2632bff9, 0x273e260f, 0x27b86af4, 0x252715e3, 0x25a15918, 0x24adc0ee, 0x242b8c15, 0x2ed03cb2, 0x2e567049, 0x2f5ae9bf, 0x2fdca544, 0x2d43da53, 0x2dc596a8, 0x2cc90f5e, 0x2c4f43a5, 0x2971bd8b, 0x29f7f170, 0x28fb6886, 0x287d247d, 0x2ae25b6a, 0x2a641791, 0x2b688e67, 0x2beec29c, 0x7c3347a4, 0x7cb50b5f, 0x7db992a9, 0x7d3fde52, 0x7fa0a145, 0x7f26edbe, 0x7e2a7448, 0x7eac38b3, 0x7b92c69d, 0x7b148a66, 0x7a181390, 0x7a9e5f6b, 0x7801207c, 0x78876c87, 0x798bf571, 0x790db98a, 0x73f6092d, 0x737045d6, 0x727cdc20, 0x72fa90db, 0x7065efcc, 0x70e3a337, 0x71ef3ac1, 0x7169763a, 0x74578814, 0x74d1c4ef, 0x75dd5d19, 0x755b11e2, 0x77c46ef5, 0x7742220e, 0x764ebbf8, 0x76c8f703, 0x633f964d, 0x63b9dab6, 0x62b54340, 0x62330fbb, 0x60ac70ac, 0x602a3c57, 0x6126a5a1, 0x61a0e95a, 0x649e1774, 0x64185b8f, 0x6514c279, 0x65928e82, 0x670df195, 0x678bbd6e, 0x66872498, 0x66016863, 0x6cfad8c4, 0x6c7c943f, 0x6d700dc9, 0x6df64132, 0x6f693e25, 0x6fef72de, 0x6ee3eb28, 0x6e65a7d3, 0x6b5b59fd, 0x6bdd1506, 0x6ad18cf0, 0x6a57c00b, 0x68c8bf1c, 0x684ef3e7, 0x69426a11, 0x69c426ea, 0x422ae476, 0x42aca88d, 0x43a0317b, 0x43267d80, 0x41b90297, 0x413f4e6c, 0x4033d79a, 0x40b59b61, 0x458b654f, 0x450d29b4, 0x4401b042, 0x4487fcb9, 0x461883ae, 0x469ecf55, 0x479256a3, 0x47141a58, 0x4defaaff, 0x4d69e604, 0x4c657ff2, 0x4ce33309, 0x4e7c4c1e, 0x4efa00e5, 0x4ff69913, 0x4f70d5e8, 0x4a4e2bc6, 0x4ac8673d, 0x4bc4fecb, 0x4b42b230, 0x49ddcd27, 0x495b81dc, 0x4857182a, 0x48d154d1, 0x5d26359f, 0x5da07964, 0x5cace092, 0x5c2aac69, 0x5eb5d37e, 0x5e339f85, 0x5f3f0673, 0x5fb94a88, 0x5a87b4a6, 0x5a01f85d, 0x5b0d61ab, 0x5b8b2d50, 0x59145247, 0x59921ebc, 0x589e874a, 0x5818cbb1, 0x52e37b16, 0x526537ed, 0x5369ae1b, 0x53efe2e0, 0x51709df7, 0x51f6d10c, 0x50fa48fa, 0x507c0401, 0x5542fa2f, 0x55c4b6d4, 0x54c82f22, 0x544e63d9, 0x56d11cce, 0x56575035, 0x575bc9c3, 0x57dd8538 }; static gpg_error_t enc_start (struct b64state *state, FILE *fp, estream_t stream, const char *title) { memset (state, 0, sizeof *state); state->fp = fp; state->stream = stream; state->lasterr = 0; if (title && !*title) state->flags |= B64ENC_NO_LINEFEEDS; else if (title) { if (!strncmp (title, "PGP ", 4)) { state->flags |= B64ENC_USE_PGPCRC; state->crc = CRCINIT; } state->title = xtrystrdup (title); if (!state->title) state->lasterr = gpg_error_from_syserror (); } return state->lasterr; } /* Prepare for base-64 writing to the stream FP. If TITLE is not NULL and not an empty string, this string will be used as the title for the armor lines, with TITLE being an empty string, we don't write the header lines and furthermore even don't write any linefeeds. If TITLE starts with "PGP " the OpenPGP CRC checksum will be written as well. With TITLE beeing NULL, we merely don't write header but make sure that lines are not too long. Note, that we don't write any output unless at least one byte get written using b64enc_write. */ gpg_error_t b64enc_start (struct b64state *state, FILE *fp, const char *title) { return enc_start (state, fp, NULL, title); } /* Same as b64enc_start but takes an estream. */ gpg_error_t b64enc_start_es (struct b64state *state, estream_t fp, const char *title) { return enc_start (state, NULL, fp, title); } static int my_fputs (const char *string, struct b64state *state) { if (state->stream) return es_fputs (string, state->stream); else return fputs (string, state->fp); } /* Write NBYTES from BUFFER to the Base 64 stream identified by STATE. With BUFFER and NBYTES being 0, merely do a fflush on the stream. */ gpg_error_t b64enc_write (struct b64state *state, const void *buffer, size_t nbytes) { unsigned char radbuf[4]; int idx, quad_count; const unsigned char *p; if (state->lasterr) return state->lasterr; if (!nbytes) { if (buffer) if (state->stream? es_fflush (state->stream) : fflush (state->fp)) goto write_error; return 0; } if (!(state->flags & B64ENC_DID_HEADER)) { if (state->title) { if ( my_fputs ("-----BEGIN ", state) == EOF || my_fputs (state->title, state) == EOF || my_fputs ("-----\n", state) == EOF) goto write_error; if ( (state->flags & B64ENC_USE_PGPCRC) && my_fputs ("\n", state) == EOF) goto write_error; } state->flags |= B64ENC_DID_HEADER; } idx = state->idx; quad_count = state->quad_count; assert (idx < 4); memcpy (radbuf, state->radbuf, idx); if ( (state->flags & B64ENC_USE_PGPCRC) ) { size_t n; u32 crc = state->crc; for (p=buffer, n=nbytes; n; p++, n-- ) - crc = (crc << 8) ^ crc_table[((crc >> 16)&0xff) ^ *p]; + crc = ((u32)crc << 8) ^ crc_table[((crc >> 16)&0xff) ^ *p]; state->crc = (crc & 0x00ffffff); } for (p=buffer; nbytes; p++, nbytes--) { radbuf[idx++] = *p; if (idx > 2) { char tmp[4]; tmp[0] = bintoasc[(*radbuf >> 2) & 077]; tmp[1] = bintoasc[(((*radbuf<<4)&060)|((radbuf[1] >> 4)&017))&077]; tmp[2] = bintoasc[(((radbuf[1]<<2)&074)|((radbuf[2]>>6)&03))&077]; tmp[3] = bintoasc[radbuf[2]&077]; if (state->stream) { for (idx=0; idx < 4; idx++) es_putc (tmp[idx], state->stream); idx = 0; if (es_ferror (state->stream)) goto write_error; } else { for (idx=0; idx < 4; idx++) putc (tmp[idx], state->fp); idx = 0; if (ferror (state->fp)) goto write_error; } if (++quad_count >= (64/4)) { quad_count = 0; if (!(state->flags & B64ENC_NO_LINEFEEDS) && my_fputs ("\n", state) == EOF) goto write_error; } } } memcpy (state->radbuf, radbuf, idx); state->idx = idx; state->quad_count = quad_count; return 0; write_error: state->lasterr = gpg_error_from_syserror (); if (state->title) { xfree (state->title); state->title = NULL; } return state->lasterr; } gpg_error_t b64enc_finish (struct b64state *state) { gpg_error_t err = 0; unsigned char radbuf[4]; int idx, quad_count; char tmp[4]; if (state->lasterr) return state->lasterr; if (!(state->flags & B64ENC_DID_HEADER)) goto cleanup; /* Flush the base64 encoding */ idx = state->idx; quad_count = state->quad_count; assert (idx < 4); memcpy (radbuf, state->radbuf, idx); if (idx) { tmp[0] = bintoasc[(*radbuf>>2)&077]; if (idx == 1) { tmp[1] = bintoasc[((*radbuf << 4) & 060) & 077]; tmp[2] = '='; tmp[3] = '='; } else { tmp[1] = bintoasc[(((*radbuf<<4)&060)|((radbuf[1]>>4)&017))&077]; tmp[2] = bintoasc[((radbuf[1] << 2) & 074) & 077]; tmp[3] = '='; } if (state->stream) { for (idx=0; idx < 4; idx++) es_putc (tmp[idx], state->stream); idx = 0; if (es_ferror (state->stream)) goto write_error; } else { for (idx=0; idx < 4; idx++) putc (tmp[idx], state->fp); idx = 0; if (ferror (state->fp)) goto write_error; } if (++quad_count >= (64/4)) { quad_count = 0; if (!(state->flags & B64ENC_NO_LINEFEEDS) && my_fputs ("\n", state) == EOF) goto write_error; } } /* Finish the last line and write the trailer. */ if (quad_count && !(state->flags & B64ENC_NO_LINEFEEDS) && my_fputs ("\n", state) == EOF) goto write_error; if ( (state->flags & B64ENC_USE_PGPCRC) ) { /* Write the CRC. */ my_fputs ("=", state); radbuf[0] = state->crc >>16; radbuf[1] = state->crc >> 8; radbuf[2] = state->crc; tmp[0] = bintoasc[(*radbuf>>2)&077]; tmp[1] = bintoasc[(((*radbuf<<4)&060)|((radbuf[1]>>4)&017))&077]; tmp[2] = bintoasc[(((radbuf[1]<<2)&074)|((radbuf[2]>>6)&03))&077]; tmp[3] = bintoasc[radbuf[2]&077]; if (state->stream) { for (idx=0; idx < 4; idx++) es_putc (tmp[idx], state->stream); if (es_ferror (state->stream)) goto write_error; } else { for (idx=0; idx < 4; idx++) putc (tmp[idx], state->fp); if (ferror (state->fp)) goto write_error; } if (!(state->flags & B64ENC_NO_LINEFEEDS) && my_fputs ("\n", state) == EOF) goto write_error; } if (state->title) { if ( my_fputs ("-----END ", state) == EOF || my_fputs (state->title, state) == EOF || my_fputs ("-----\n", state) == EOF) goto write_error; } goto cleanup; write_error: err = gpg_error_from_syserror (); cleanup: if (state->title) { xfree (state->title); state->title = NULL; } state->fp = NULL; state->stream = NULL; state->lasterr = err; return err; } diff --git a/common/dns-cert.c b/common/dns-cert.c index 4e297bf92..317ebb1d8 100644 --- a/common/dns-cert.c +++ b/common/dns-cert.c @@ -1,370 +1,372 @@ /* dns-cert.c - DNS CERT code (rfc-4398) * Copyright (C) 2005, 2006, 2009 Free Software Foundation, Inc. * * This file is part of GNUPG. * * This file is free software; you can redistribute it and/or modify * it 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. * * This file 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 #ifdef USE_DNS_CERT # ifdef HAVE_W32_SYSTEM # ifdef HAVE_WINSOCK2_H # include # endif # include # else # include # include # include # endif # include #endif #ifdef USE_ADNS # include #endif #include "util.h" +#include "host2net.h" #include "dns-cert.h" /* Not every installation has gotten around to supporting CERTs yet... */ #ifndef T_CERT #define T_CERT 37 #endif /* ADNS has no support for CERT yet. */ #define my_adns_r_cert 37 /* Certificate types according to RFC-4398. */ #define CERTTYPE_PKIX 1 /* X.509 as per PKIX. */ #define CERTTYPE_SPKI 2 /* SPKI certificate. */ #define CERTTYPE_PGP 3 /* OpenPGP packet. */ #define CERTTYPE_IPKIX 4 /* The URL of an X.509 data object. */ #define CERTTYPE_ISPKI 5 /* The URL of an SPKI certificate. */ #define CERTTYPE_IPGP 6 /* The fingerprint and URL of an OpenPGP packet.*/ #define CERTTYPE_ACPKIX 7 /* Attribute Certificate. */ #define CERTTYPE_IACPKIX 8 /* The URL of an Attribute Certificate. */ #define CERTTYPE_URI 253 /* URI private. */ #define CERTTYPE_OID 254 /* OID private. */ /* Returns 0 on success or an error code. If a PGP CERT record was found, a new estream with that key will be returned at R_KEY and the other return parameters are set to NULL/0. If an IPGP CERT record was found the fingerprint is stored as an allocated block at R_FPR and its length at R_FPRLEN; an URL is is allocated as a string and returned at R_URL. Note that this function returns the first CERT found with a supported type; it is expected that only one CERT record is used. */ gpg_error_t get_dns_cert (const char *name, estream_t *r_key, unsigned char **r_fpr, size_t *r_fprlen, char **r_url) { #ifdef USE_DNS_CERT #ifdef USE_ADNS gpg_error_t err; adns_state state; adns_answer *answer = NULL; unsigned int ctype; int count; *r_key = NULL; *r_fpr = NULL; *r_fprlen = 0; *r_url = NULL; if (adns_init (&state, adns_if_noerrprint, NULL)) { err = gpg_err_make (default_errsource, gpg_err_code_from_syserror ()); log_error ("error initializing adns: %s\n", strerror (errno)); return err; } if (adns_synchronous (state, name, (adns_r_unknown | my_adns_r_cert), adns_qf_quoteok_query, &answer)) { err = gpg_err_make (default_errsource, gpg_err_code_from_syserror ()); /* log_error ("DNS query failed: %s\n", strerror (errno)); */ adns_finish (state); return err; } if (answer->status != adns_s_ok) { /* log_error ("DNS query returned an error: %s (%s)\n", */ /* adns_strerror (answer->status), */ /* adns_errabbrev (answer->status)); */ err = gpg_err_make (default_errsource, GPG_ERR_NOT_FOUND); goto leave; } err = gpg_err_make (default_errsource, GPG_ERR_NOT_FOUND); for (count = 0; count < answer->nrrs; count++) { int datalen = answer->rrs.byteblock[count].len; const unsigned char *data = answer->rrs.byteblock[count].data; if (datalen < 5) continue; /* Truncated CERT record - skip. */ - ctype = ((data[0] << 8) | data[1]); + ctype = buf16_to_uint (data); /* (key tag and algorithm fields are not required.) */ data += 5; datalen -= 5; if (ctype == CERTTYPE_PGP && datalen >= 11) { /* CERT type is PGP. Gpg checks for a minimum length of 11, thus we do the same. */ *r_key = es_fopenmem_init (0, "rwb", data, datalen); if (!*r_key) err = gpg_err_make (default_errsource, gpg_err_code_from_syserror ()); else err = 0; goto leave; } else if (ctype == CERTTYPE_IPGP && datalen && datalen < 1023 && datalen >= data[0] + 1 && r_fpr && r_fprlen && r_url) { /* CERT type is IPGP. We made sure that the data is plausible and that the caller requested this information. */ *r_fprlen = data[0]; if (*r_fprlen) { *r_fpr = xtrymalloc (*r_fprlen); if (!*r_fpr) { err = gpg_err_make (default_errsource, gpg_err_code_from_syserror ()); goto leave; } memcpy (*r_fpr, data + 1, *r_fprlen); } else *r_fpr = NULL; if (datalen > *r_fprlen + 1) { *r_url = xtrymalloc (datalen - (*r_fprlen + 1) + 1); if (!*r_url) { err = gpg_err_make (default_errsource, gpg_err_code_from_syserror ()); xfree (*r_fpr); *r_fpr = NULL; goto leave; } memcpy (*r_url, data + (*r_fprlen + 1), datalen - (*r_fprlen + 1)); (*r_url)[datalen - (*r_fprlen + 1)] = '\0'; } else *r_url = NULL; err = 0; goto leave; } } leave: adns_free (answer); adns_finish (state); return err; #else /*!USE_ADNS*/ gpg_error_t err; unsigned char *answer; int r; u16 count; *r_key = NULL; *r_fpr = NULL; *r_fprlen = 0; *r_url = NULL; /* Allocate a 64k buffer which is the limit for an DNS response. */ answer = xtrymalloc (65536); if (!answer) return gpg_err_make (default_errsource, gpg_err_code_from_syserror ()); err = gpg_err_make (default_errsource, GPG_ERR_NOT_FOUND); r = res_query (name, C_IN, T_CERT, answer, 65536); /* Not too big, not too small, no errors and at least 1 answer. */ if (r >= sizeof (HEADER) && r <= 65536 && (((HEADER *) answer)->rcode) == NOERROR && (count = ntohs (((HEADER *) answer)->ancount))) { int rc; unsigned char *pt, *emsg; emsg = &answer[r]; pt = &answer[sizeof (HEADER)]; /* Skip over the query */ rc = dn_skipname (pt, emsg); if (rc == -1) { err = gpg_err_make (default_errsource, GPG_ERR_INV_OBJ); goto leave; } pt += rc + QFIXEDSZ; /* There are several possible response types for a CERT request. We're interested in the PGP (a key) and IPGP (a URI) types. Skip all others. TODO: A key is better than a URI since we've gone through all this bother to fetch it, so favor that if we have both PGP and IPGP? */ while (count-- > 0 && pt < emsg) { u16 type, class, dlen, ctype; rc = dn_skipname (pt, emsg); /* the name we just queried for */ if (rc == -1) { err = gpg_err_make (default_errsource, GPG_ERR_INV_OBJ); goto leave; } pt += rc; /* Truncated message? 15 bytes takes us to the point where we start looking at the ctype. */ if ((emsg - pt) < 15) break; - type = *pt++ << 8; - type |= *pt++; + type = buf16_to_u16 (pt); + pt += 2; - class = *pt++ << 8; + class = buf16_to_u16 (pt); + pt += 2; class |= *pt++; - /* We asked for IN and got something else !? */ + if (class != C_IN) break; /* ttl */ pt += 4; /* data length */ - dlen = *pt++ << 8; - dlen |= *pt++; + dlen = buf16_to_u16 (pt); + pt += 2; /* We asked for CERT and got something else - might be a CNAME, so loop around again. */ if (type != T_CERT) { pt += dlen; continue; } /* The CERT type */ - ctype = *pt++ << 8; - ctype |= *pt++; + ctype = buf16_to_u16 (pt); + pt += 2; /* Skip the CERT key tag and algo which we don't need. */ pt += 3; dlen -= 5; /* 15 bytes takes us to here */ if (ctype == CERTTYPE_PGP && dlen) { /* PGP type */ *r_key = es_fopenmem_init (0, "rwb", pt, dlen); if (!*r_key) err = gpg_err_make (default_errsource, gpg_err_code_from_syserror ()); else err = 0; goto leave; } else if (ctype == CERTTYPE_IPGP && dlen && dlen < 1023 && dlen >= pt[0] + 1) { /* IPGP type */ *r_fprlen = pt[0]; if (*r_fprlen) { *r_fpr = xtrymalloc (*r_fprlen); if (!*r_fpr) { err = gpg_err_make (default_errsource, gpg_err_code_from_syserror ()); goto leave; } memcpy (*r_fpr, &pt[1], *r_fprlen); } else *r_fpr = NULL; if (dlen > *r_fprlen + 1) { *r_url = xtrymalloc (dlen - (*r_fprlen + 1) + 1); if (!*r_fpr) { err = gpg_err_make (default_errsource, gpg_err_code_from_syserror ()); xfree (*r_fpr); *r_fpr = NULL; goto leave; } memcpy (*r_url, &pt[*r_fprlen + 1], dlen - (*r_fprlen + 1)); (*r_url)[dlen - (*r_fprlen + 1)] = '\0'; } else *r_url = NULL; err = 0; goto leave; } /* Neither type matches, so go around to the next answer. */ pt += dlen; } } leave: xfree (answer); return err; #endif /*!USE_ADNS */ #else /* !USE_DNS_CERT */ (void)name; *r_key = NULL; *r_fpr = NULL; *r_fprlen = 0; *r_url = NULL; return gpg_err_make (default_errsource, GPG_ERR_NOT_SUPPORTED); #endif } diff --git a/common/host2net.h b/common/host2net.h index dd20e36ce..be5e5202a 100644 --- a/common/host2net.h +++ b/common/host2net.h @@ -1,52 +1,112 @@ /* host2net.h - Endian conversion macros - * Copyright (C) 1998, 2014 Werner Koch + * Copyright (C) 1998, 2014, 2015 Werner Koch * * This file is part of GnuPG. * * This file is free software; you can redistribute it and/or modify * it 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. * * This file 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 . */ #ifndef GNUPG_COMMON_HOST2NET_H #define GNUPG_COMMON_HOST2NET_H #include "types.h" -#define buftoulong( p ) ((*(byte*)(p) << 24) | (*((byte*)(p)+1)<< 16) | \ - (*((byte*)(p)+2) << 8) | (*((byte*)(p)+3))) -#define buftoushort( p ) ((*((byte*)(p)) << 8) | (*((byte*)(p)+1))) #define ulongtobuf( p, a ) do { \ ((byte*)p)[0] = a >> 24; \ ((byte*)p)[1] = a >> 16; \ ((byte*)p)[2] = a >> 8; \ ((byte*)p)[3] = a ; \ } while(0) #define ushorttobuf( p, a ) do { \ ((byte*)p)[0] = a >> 8; \ ((byte*)p)[1] = a ; \ } while(0) -#define buftou32( p) buftoulong( (p) ) -#define u32tobuf( p, a) ulongtobuf( (p), (a) ) + + +static inline unsigned long +buf16_to_ulong (const void *buffer) +{ + const unsigned char *p = buffer; + + return (((unsigned long)p[0] << 8) | p[1]); +} + +static inline unsigned int +buf16_to_uint (const void *buffer) +{ + const unsigned char *p = buffer; + + return (((unsigned int)p[0] << 8) | p[1]); +} + +static inline unsigned short +buf16_to_ushort (const void *buffer) +{ + const unsigned char *p = buffer; + + return (((unsigned short)p[0] << 8) | p[1]); +} + +static inline u16 +buf16_to_u16 (const void *buffer) +{ + const unsigned char *p = buffer; + + return (((u16)p[0] << 8) | p[1]); +} + +static inline size_t +buf32_to_size_t (const void *buffer) +{ + const unsigned char *p = buffer; + + return (((size_t)p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]); +} + +static inline unsigned long +buf32_to_ulong (const void *buffer) +{ + const unsigned char *p = buffer; + + return (((unsigned long)p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]); +} + +static inline unsigned int +buf32_to_uint (const void *buffer) +{ + const unsigned char *p = buffer; + + return (((unsigned int)p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]); +} + +static inline u32 +buf32_to_u32 (const void *buffer) +{ + const unsigned char *p = buffer; + + return (((u32)p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]); +} #endif /*GNUPG_COMMON_HOST2NET_H*/ diff --git a/common/iobuf.c b/common/iobuf.c index badbf78da..ca74bd71e 100644 --- a/common/iobuf.c +++ b/common/iobuf.c @@ -1,2605 +1,2608 @@ /* iobuf.c - File Handling for OpenPGP. * Copyright (C) 1998, 1999, 2000, 2001, 2003, 2004, 2006, 2007, 2008, * 2009, 2010, 2011 Free Software Foundation, Inc. * * This file is part of GnuPG. * * This file is free software; you can redistribute it and/or modify * it 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. * * This file 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 # ifdef HAVE_WINSOCK2_H # include # endif # include #endif #ifdef __riscos__ # include # include #endif /* __riscos__ */ #include #include "util.h" #include "sysutils.h" #include "iobuf.h" /*-- Begin configurable part. --*/ /* The size of the internal buffers. NOTE: If you change this value you MUST also adjust the regression test "armored_key_8192" in armor.test! */ #define IOBUF_BUFFER_SIZE 8192 /* To avoid a potential DoS with compression packets we better limit the number of filters in a chain. */ #define MAX_NESTING_FILTER 64 /*-- End configurable part. --*/ #ifdef HAVE_W32_SYSTEM # ifdef HAVE_W32CE_SYSTEM # define FD_FOR_STDIN (es_fileno (es_stdin)) # define FD_FOR_STDOUT (es_fileno (es_stdout)) # else # define FD_FOR_STDIN (GetStdHandle (STD_INPUT_HANDLE)) # define FD_FOR_STDOUT (GetStdHandle (STD_OUTPUT_HANDLE)) # endif #else /*!HAVE_W32_SYSTEM*/ # define FD_FOR_STDIN (0) # define FD_FOR_STDOUT (1) #endif /*!HAVE_W32_SYSTEM*/ /* The context used by the file filter. */ typedef struct { gnupg_fd_t fp; /* Open file pointer or handle. */ int keep_open; int no_cache; int eof_seen; int print_only_name; /* Flags indicating that fname is not a real file. */ char fname[1]; /* Name of the file. */ } file_filter_ctx_t; /* The context used by the estream filter. */ typedef struct { estream_t fp; /* Open estream handle. */ int keep_open; int no_cache; int eof_seen; int print_only_name; /* Flags indicating that fname is not a real file. */ char fname[1]; /* Name of the file. */ } file_es_filter_ctx_t; /* Object to control the "close cache". */ struct close_cache_s { struct close_cache_s *next; gnupg_fd_t fp; char fname[1]; }; typedef struct close_cache_s *close_cache_t; static close_cache_t close_cache; #ifdef HAVE_W32_SYSTEM typedef struct { int sock; int keep_open; int no_cache; int eof_seen; int print_only_name; /* Flag indicating that fname is not a real file. */ char fname[1]; /* Name of the file */ } sock_filter_ctx_t; #endif /*HAVE_W32_SYSTEM*/ /* The first partial length header block must be of size 512 * to make it easier (and efficienter) we use a min. block size of 512 * for all chunks (but the last one) */ #define OP_MIN_PARTIAL_CHUNK 512 #define OP_MIN_PARTIAL_CHUNK_2POW 9 /* The context we use for the block filter (used to handle OpenPGP length information header). */ typedef struct { int use; size_t size; size_t count; int partial; /* 1 = partial header, 2 in last partial packet. */ char *buffer; /* Used for partial header. */ size_t buflen; /* Used size of buffer. */ int first_c; /* First character of a partial header (which is > 0). */ int eof; } block_filter_ctx_t; /* Global flag to tell whether special file names are enabled. See gpg.c for an explanation of these file names. FIXME: it does not belong into the iobuf subsystem. */ static int special_names_enabled; /* Local prototypes. */ static int underflow (iobuf_t a); static int translate_file_handle (int fd, int for_write); /* This is a replacement for strcmp. Under W32 it does not distinguish between backslash and slash. */ static int fd_cache_strcmp (const char *a, const char *b) { #ifdef HAVE_DOSISH_SYSTEM for (; *a && *b; a++, b++) { if (*a != *b && !((*a == '/' && *b == '\\') || (*a == '\\' && *b == '/')) ) break; } return *(const unsigned char *)a - *(const unsigned char *)b; #else return strcmp (a, b); #endif } /* * Invalidate (i.e. close) a cached iobuf */ static int fd_cache_invalidate (const char *fname) { close_cache_t cc; int rc = 0; assert (fname); if (DBG_IOBUF) log_debug ("fd_cache_invalidate (%s)\n", fname); for (cc = close_cache; cc; cc = cc->next) { if (cc->fp != GNUPG_INVALID_FD && !fd_cache_strcmp (cc->fname, fname)) { if (DBG_IOBUF) log_debug (" did (%s)\n", cc->fname); #ifdef HAVE_W32_SYSTEM if (!CloseHandle (cc->fp)) rc = -1; #else rc = close (cc->fp); #endif cc->fp = GNUPG_INVALID_FD; } } return rc; } /* Try to sync changes to the disk. This is to avoid data loss during a system crash in write/close/rename cycle on some file systems. */ static int fd_cache_synchronize (const char *fname) { int err = 0; #ifdef HAVE_FSYNC close_cache_t cc; if (DBG_IOBUF) log_debug ("fd_cache_synchronize (%s)\n", fname); for (cc=close_cache; cc; cc = cc->next ) { if (cc->fp != GNUPG_INVALID_FD && !fd_cache_strcmp (cc->fname, fname)) { if (DBG_IOBUF) log_debug (" did (%s)\n", cc->fname); err = fsync (cc->fp); } } #else (void)fname; #endif /*HAVE_FSYNC*/ return err; } static gnupg_fd_t direct_open (const char *fname, const char *mode, int mode700) { #ifdef HAVE_W32_SYSTEM unsigned long da, cd, sm; HANDLE hfile; /* Note, that we do not handle all mode combinations */ /* According to the ReactOS source it seems that open() of the * standard MSW32 crt does open the file in shared mode which is * something new for MS applications ;-) */ if (strchr (mode, '+')) { if (fd_cache_invalidate (fname)) return GNUPG_INVALID_FD; da = GENERIC_READ | GENERIC_WRITE; cd = OPEN_EXISTING; sm = FILE_SHARE_READ | FILE_SHARE_WRITE; } else if (strchr (mode, 'w')) { if (fd_cache_invalidate (fname)) return GNUPG_INVALID_FD; da = GENERIC_WRITE; cd = CREATE_ALWAYS; sm = FILE_SHARE_WRITE; } else { da = GENERIC_READ; cd = OPEN_EXISTING; sm = FILE_SHARE_READ; } #ifdef HAVE_W32CE_SYSTEM { wchar_t *wfname = utf8_to_wchar (fname); if (wfname) { hfile = CreateFile (wfname, da, sm, NULL, cd, FILE_ATTRIBUTE_NORMAL, NULL); xfree (wfname); } else hfile = INVALID_HANDLE_VALUE; } #else hfile = CreateFile (fname, da, sm, NULL, cd, FILE_ATTRIBUTE_NORMAL, NULL); #endif return hfile; #else /*!HAVE_W32_SYSTEM*/ int oflag; int cflag = S_IRUSR | S_IWUSR; if (!mode700) cflag |= S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH; /* Note, that we do not handle all mode combinations */ if (strchr (mode, '+')) { if (fd_cache_invalidate (fname)) return GNUPG_INVALID_FD; oflag = O_RDWR; } else if (strchr (mode, 'w')) { if (fd_cache_invalidate (fname)) return GNUPG_INVALID_FD; oflag = O_WRONLY | O_CREAT | O_TRUNC; } else { oflag = O_RDONLY; } #ifdef O_BINARY if (strchr (mode, 'b')) oflag |= O_BINARY; #endif #ifdef __riscos__ { struct stat buf; /* Don't allow iobufs on directories */ if (!stat (fname, &buf) && S_ISDIR (buf.st_mode) && !S_ISREG (buf.st_mode)) return __set_errno (EISDIR); } #endif return open (fname, oflag, cflag); #endif /*!HAVE_W32_SYSTEM*/ } /* * Instead of closing an FD we keep it open and cache it for later reuse * Note that this caching strategy only works if the process does not chdir. */ static void fd_cache_close (const char *fname, gnupg_fd_t fp) { close_cache_t cc; assert (fp); if (!fname || !*fname) { #ifdef HAVE_W32_SYSTEM CloseHandle (fp); #else close (fp); #endif if (DBG_IOBUF) log_debug ("fd_cache_close (%d) real\n", (int)fp); return; } /* try to reuse a slot */ for (cc = close_cache; cc; cc = cc->next) { if (cc->fp == GNUPG_INVALID_FD && !fd_cache_strcmp (cc->fname, fname)) { cc->fp = fp; if (DBG_IOBUF) log_debug ("fd_cache_close (%s) used existing slot\n", fname); return; } } /* add a new one */ if (DBG_IOBUF) log_debug ("fd_cache_close (%s) new slot created\n", fname); cc = xcalloc (1, sizeof *cc + strlen (fname)); strcpy (cc->fname, fname); cc->fp = fp; cc->next = close_cache; close_cache = cc; } /* * Do an direct_open on FNAME but first try to reuse one from the fd_cache */ static gnupg_fd_t fd_cache_open (const char *fname, const char *mode) { close_cache_t cc; assert (fname); for (cc = close_cache; cc; cc = cc->next) { if (cc->fp != GNUPG_INVALID_FD && !fd_cache_strcmp (cc->fname, fname)) { gnupg_fd_t fp = cc->fp; cc->fp = GNUPG_INVALID_FD; if (DBG_IOBUF) log_debug ("fd_cache_open (%s) using cached fp\n", fname); #ifdef HAVE_W32_SYSTEM if (SetFilePointer (fp, 0, NULL, FILE_BEGIN) == 0xffffffff) { log_error ("rewind file failed on handle %p: ec=%d\n", fp, (int) GetLastError ()); fp = GNUPG_INVALID_FD; } #else if (lseek (fp, 0, SEEK_SET) == (off_t) - 1) { log_error ("can't rewind fd %d: %s\n", fp, strerror (errno)); fp = GNUPG_INVALID_FD; } #endif return fp; } } if (DBG_IOBUF) log_debug ("fd_cache_open (%s) not cached\n", fname); return direct_open (fname, mode, 0); } /**************** * Read data from a file into buf which has an allocated length of *LEN. * return the number of read bytes in *LEN. OPAQUE is the FILE * of * the stream. A is not used. * control may be: * IOBUFCTRL_INIT: called just before the function is linked into the * list of function. This can be used to prepare internal * data structures of the function. * IOBUFCTRL_FREE: called just before the function is removed from the * list of functions and can be used to release internal * data structures or close a file etc. * IOBUFCTRL_UNDERFLOW: called by iobuf_underflow to fill the buffer * with new stuff. *RET_LEN is the available size of the * buffer, and should be set to the number of bytes * which were put into the buffer. The function * returns 0 to indicate success, -1 on EOF and * GPG_ERR_xxxxx for other errors. * * IOBUFCTRL_FLUSH: called by iobuf_flush() to write out the collected stuff. * *RET_LAN is the number of bytes in BUF. * * IOBUFCTRL_CANCEL: send to all filters on behalf of iobuf_cancel. The * filter may take appropriate action on this message. */ static int file_filter (void *opaque, int control, iobuf_t chain, byte * buf, size_t * ret_len) { file_filter_ctx_t *a = opaque; gnupg_fd_t f = a->fp; size_t size = *ret_len; size_t nbytes = 0; int rc = 0; (void)chain; /* Not used. */ if (control == IOBUFCTRL_UNDERFLOW) { assert (size); /* We need a buffer. */ if (a->eof_seen) { rc = -1; *ret_len = 0; } else { #ifdef HAVE_W32_SYSTEM unsigned long nread; nbytes = 0; if (!ReadFile (f, buf, size, &nread, NULL)) { int ec = (int) GetLastError (); if (ec != ERROR_BROKEN_PIPE) { rc = gpg_error_from_errno (ec); log_error ("%s: read error: ec=%d\n", a->fname, ec); } } else if (!nread) { a->eof_seen = 1; rc = -1; } else { nbytes = nread; } #else int n; nbytes = 0; do { n = read (f, buf, size); } while (n == -1 && errno == EINTR); if (n == -1) { /* error */ if (errno != EPIPE) { rc = gpg_error_from_syserror (); log_error ("%s: read error: %s\n", a->fname, strerror (errno)); } } else if (!n) { /* eof */ a->eof_seen = 1; rc = -1; } else { nbytes = n; } #endif *ret_len = nbytes; } } else if (control == IOBUFCTRL_FLUSH) { if (size) { #ifdef HAVE_W32_SYSTEM byte *p = buf; unsigned long n; nbytes = size; do { if (size && !WriteFile (f, p, nbytes, &n, NULL)) { int ec = (int) GetLastError (); rc = gpg_error_from_errno (ec); log_error ("%s: write error: ec=%d\n", a->fname, ec); break; } p += n; nbytes -= n; } while (nbytes); nbytes = p - buf; #else byte *p = buf; int n; nbytes = size; do { do { n = write (f, p, nbytes); } while (n == -1 && errno == EINTR); if (n > 0) { p += n; nbytes -= n; } } while (n != -1 && nbytes); if (n == -1) { rc = gpg_error_from_syserror (); log_error ("%s: write error: %s\n", a->fname, strerror (errno)); } nbytes = p - buf; #endif } *ret_len = nbytes; } else if (control == IOBUFCTRL_INIT) { a->eof_seen = 0; a->keep_open = 0; a->no_cache = 0; } else if (control == IOBUFCTRL_DESC) { *(char **) buf = "file_filter(fd)"; } else if (control == IOBUFCTRL_FREE) { if (f != FD_FOR_STDIN && f != FD_FOR_STDOUT) { if (DBG_IOBUF) log_debug ("%s: close fd/handle %d\n", a->fname, FD2INT (f)); if (!a->keep_open) fd_cache_close (a->no_cache ? NULL : a->fname, f); } f = GNUPG_INVALID_FD; xfree (a); /* We can free our context now. */ } return rc; } /* Similar to file_filter but using the estream system. */ static int file_es_filter (void *opaque, int control, iobuf_t chain, byte * buf, size_t * ret_len) { file_es_filter_ctx_t *a = opaque; estream_t f = a->fp; size_t size = *ret_len; size_t nbytes = 0; int rc = 0; (void)chain; /* Not used. */ if (control == IOBUFCTRL_UNDERFLOW) { assert (size); /* We need a buffer. */ if (a->eof_seen) { rc = -1; *ret_len = 0; } else { nbytes = 0; rc = es_read (f, buf, size, &nbytes); if (rc == -1) { /* error */ rc = gpg_error_from_syserror (); log_error ("%s: read error: %s\n", a->fname, strerror (errno)); } else if (!nbytes) { /* eof */ a->eof_seen = 1; rc = -1; } *ret_len = nbytes; } } else if (control == IOBUFCTRL_FLUSH) { if (size) { byte *p = buf; size_t nwritten; nbytes = size; do { nwritten = 0; if (es_write (f, p, nbytes, &nwritten)) { rc = gpg_error_from_syserror (); log_error ("%s: write error: %s\n", a->fname, strerror (errno)); break; } p += nwritten; nbytes -= nwritten; } while (nbytes); nbytes = p - buf; } *ret_len = nbytes; } else if (control == IOBUFCTRL_INIT) { a->eof_seen = 0; a->no_cache = 0; } else if (control == IOBUFCTRL_DESC) { *(char **) buf = "estream_filter"; } else if (control == IOBUFCTRL_FREE) { if (f != es_stdin && f != es_stdout) { if (DBG_IOBUF) log_debug ("%s: es_fclose %p\n", a->fname, f); if (!a->keep_open) es_fclose (f); } f = NULL; xfree (a); /* We can free our context now. */ } return rc; } #ifdef HAVE_W32_SYSTEM /* Because network sockets are special objects under Lose32 we have to use a dedicated filter for them. */ static int sock_filter (void *opaque, int control, iobuf_t chain, byte * buf, size_t * ret_len) { sock_filter_ctx_t *a = opaque; size_t size = *ret_len; size_t nbytes = 0; int rc = 0; (void)chain; if (control == IOBUFCTRL_UNDERFLOW) { assert (size); /* need a buffer */ if (a->eof_seen) { rc = -1; *ret_len = 0; } else { int nread; nread = recv (a->sock, buf, size, 0); if (nread == SOCKET_ERROR) { int ec = (int) WSAGetLastError (); rc = gpg_error_from_errno (ec); log_error ("socket read error: ec=%d\n", ec); } else if (!nread) { a->eof_seen = 1; rc = -1; } else { nbytes = nread; } *ret_len = nbytes; } } else if (control == IOBUFCTRL_FLUSH) { if (size) { byte *p = buf; int n; nbytes = size; do { n = send (a->sock, p, nbytes, 0); if (n == SOCKET_ERROR) { int ec = (int) WSAGetLastError (); rc = gpg_error_from_errno (ec); log_error ("socket write error: ec=%d\n", ec); break; } p += n; nbytes -= n; } while (nbytes); nbytes = p - buf; } *ret_len = nbytes; } else if (control == IOBUFCTRL_INIT) { a->eof_seen = 0; a->keep_open = 0; a->no_cache = 0; } else if (control == IOBUFCTRL_DESC) { *(char **) buf = "sock_filter"; } else if (control == IOBUFCTRL_FREE) { if (!a->keep_open) closesocket (a->sock); xfree (a); /* we can free our context now */ } return rc; } #endif /*HAVE_W32_SYSTEM*/ /**************** * This is used to implement the block write mode. * Block reading is done on a byte by byte basis in readbyte(), * without a filter */ static int block_filter (void *opaque, int control, iobuf_t chain, byte * buffer, size_t * ret_len) { block_filter_ctx_t *a = opaque; char *buf = (char *)buffer; size_t size = *ret_len; int c, needed, rc = 0; char *p; if (control == IOBUFCTRL_UNDERFLOW) { size_t n = 0; p = buf; assert (size); /* need a buffer */ if (a->eof) /* don't read any further */ rc = -1; while (!rc && size) { if (!a->size) { /* get the length bytes */ if (a->partial == 2) { a->eof = 1; if (!n) rc = -1; break; } else if (a->partial) { /* These OpenPGP introduced huffman like encoded length * bytes are really a mess :-( */ if (a->first_c) { c = a->first_c; a->first_c = 0; } else if ((c = iobuf_get (chain)) == -1) { log_error ("block_filter: 1st length byte missing\n"); rc = GPG_ERR_BAD_DATA; break; } if (c < 192) { a->size = c; a->partial = 2; if (!a->size) { a->eof = 1; if (!n) rc = -1; break; } } else if (c < 224) { a->size = (c - 192) * 256; if ((c = iobuf_get (chain)) == -1) { log_error ("block_filter: 2nd length byte missing\n"); rc = GPG_ERR_BAD_DATA; break; } a->size += c + 192; a->partial = 2; if (!a->size) { a->eof = 1; if (!n) rc = -1; break; } } else if (c == 255) { - a->size = iobuf_get (chain) << 24; + a->size = (size_t)iobuf_get (chain) << 24; a->size |= iobuf_get (chain) << 16; a->size |= iobuf_get (chain) << 8; if ((c = iobuf_get (chain)) == -1) { log_error ("block_filter: invalid 4 byte length\n"); rc = GPG_ERR_BAD_DATA; break; } a->size |= c; a->partial = 2; if (!a->size) { a->eof = 1; if (!n) rc = -1; break; } } else { /* Next partial body length. */ a->size = 1 << (c & 0x1f); } /* log_debug("partial: ctx=%p c=%02x size=%u\n", a, c, a->size); */ } else BUG (); } while (!rc && size && a->size) { needed = size < a->size ? size : a->size; c = iobuf_read (chain, p, needed); if (c < needed) { if (c == -1) c = 0; log_error ("block_filter %p: read error (size=%lu,a->size=%lu)\n", a, (ulong) size + c, (ulong) a->size + c); rc = GPG_ERR_BAD_DATA; } else { size -= c; a->size -= c; p += c; n += c; } } } *ret_len = n; } else if (control == IOBUFCTRL_FLUSH) { if (a->partial) { /* the complicated openpgp scheme */ size_t blen, n, nbytes = size + a->buflen; assert (a->buflen <= OP_MIN_PARTIAL_CHUNK); if (nbytes < OP_MIN_PARTIAL_CHUNK) { /* not enough to write a partial block out; so we store it */ if (!a->buffer) a->buffer = xmalloc (OP_MIN_PARTIAL_CHUNK); memcpy (a->buffer + a->buflen, buf, size); a->buflen += size; } else { /* okay, we can write out something */ /* do this in a loop to use the most efficient block lengths */ p = buf; do { /* find the best matching block length - this is limited * by the size of the internal buffering */ for (blen = OP_MIN_PARTIAL_CHUNK * 2, c = OP_MIN_PARTIAL_CHUNK_2POW + 1; blen <= nbytes; blen *= 2, c++) ; blen /= 2; c--; /* write the partial length header */ assert (c <= 0x1f); /*;-) */ c |= 0xe0; iobuf_put (chain, c); if ((n = a->buflen)) { /* write stuff from the buffer */ assert (n == OP_MIN_PARTIAL_CHUNK); if (iobuf_write (chain, a->buffer, n)) rc = gpg_error_from_syserror (); a->buflen = 0; nbytes -= n; } if ((n = nbytes) > blen) n = blen; if (n && iobuf_write (chain, p, n)) rc = gpg_error_from_syserror (); p += n; nbytes -= n; } while (!rc && nbytes >= OP_MIN_PARTIAL_CHUNK); /* store the rest in the buffer */ if (!rc && nbytes) { assert (!a->buflen); assert (nbytes < OP_MIN_PARTIAL_CHUNK); if (!a->buffer) a->buffer = xmalloc (OP_MIN_PARTIAL_CHUNK); memcpy (a->buffer, p, nbytes); a->buflen = nbytes; } } } else BUG (); } else if (control == IOBUFCTRL_INIT) { if (DBG_IOBUF) log_debug ("init block_filter %p\n", a); if (a->partial) a->count = 0; else if (a->use == 1) a->count = a->size = 0; else a->count = a->size; /* force first length bytes */ a->eof = 0; a->buffer = NULL; a->buflen = 0; } else if (control == IOBUFCTRL_DESC) { *(char **) buf = "block_filter"; } else if (control == IOBUFCTRL_FREE) { if (a->use == 2) { /* write the end markers */ if (a->partial) { u32 len; /* write out the remaining bytes without a partial header * the length of this header may be 0 - but if it is * the first block we are not allowed to use a partial header * and frankly we can't do so, because this length must be * a power of 2. This is _really_ complicated because we * have to check the possible length of a packet prior * to it's creation: a chain of filters becomes complicated * and we need a lot of code to handle compressed packets etc. * :-((((((( */ /* construct header */ len = a->buflen; /*log_debug("partial: remaining length=%u\n", len ); */ if (len < 192) rc = iobuf_put (chain, len); else if (len < 8384) { if (!(rc = iobuf_put (chain, ((len - 192) / 256) + 192))) rc = iobuf_put (chain, ((len - 192) % 256)); } else { /* use a 4 byte header */ if (!(rc = iobuf_put (chain, 0xff))) if (!(rc = iobuf_put (chain, (len >> 24) & 0xff))) if (!(rc = iobuf_put (chain, (len >> 16) & 0xff))) if (!(rc = iobuf_put (chain, (len >> 8) & 0xff))) rc = iobuf_put (chain, len & 0xff); } if (!rc && len) rc = iobuf_write (chain, a->buffer, len); if (rc) { log_error ("block_filter: write error: %s\n", strerror (errno)); rc = gpg_error_from_syserror (); } xfree (a->buffer); a->buffer = NULL; a->buflen = 0; } else BUG (); } else if (a->size) { log_error ("block_filter: pending bytes!\n"); } if (DBG_IOBUF) log_debug ("free block_filter %p\n", a); xfree (a); /* we can free our context now */ } return rc; } static void print_chain (iobuf_t a) { if (!DBG_IOBUF) return; for (; a; a = a->chain) { size_t dummy_len = 0; const char *desc = "[none]"; if (a->filter) a->filter (a->filter_ov, IOBUFCTRL_DESC, NULL, (byte *) & desc, &dummy_len); log_debug ("iobuf chain: %d.%d '%s' filter_eof=%d start=%d len=%d\n", a->no, a->subno, desc?desc:"?", a->filter_eof, (int) a->d.start, (int) a->d.len); } } int iobuf_print_chain (iobuf_t a) { print_chain (a); return 0; } /**************** * Allocate a new io buffer, with no function assigned. * Use is the desired usage: 1 for input, 2 for output, 3 for temp buffer * BUFSIZE is a suggested buffer size. */ iobuf_t iobuf_alloc (int use, size_t bufsize) { iobuf_t a; static int number = 0; a = xcalloc (1, sizeof *a); a->use = use; a->d.buf = xmalloc (bufsize); a->d.size = bufsize; a->no = ++number; a->subno = 0; a->opaque = NULL; a->real_fname = NULL; return a; } int iobuf_close (iobuf_t a) { iobuf_t a2; size_t dummy_len = 0; int rc = 0; if (a && a->directfp) { fclose (a->directfp); xfree (a->real_fname); if (DBG_IOBUF) log_debug ("iobuf_close -> %p\n", a->directfp); return 0; } for (; a && !rc; a = a2) { a2 = a->chain; if (a->use == 2 && (rc = iobuf_flush (a))) log_error ("iobuf_flush failed on close: %s\n", gpg_strerror (rc)); if (DBG_IOBUF) log_debug ("iobuf-%d.%d: close '%s'\n", a->no, a->subno, a->desc?a->desc:"?"); if (a->filter && (rc = a->filter (a->filter_ov, IOBUFCTRL_FREE, a->chain, NULL, &dummy_len))) log_error ("IOBUFCTRL_FREE failed on close: %s\n", gpg_strerror (rc)); xfree (a->real_fname); if (a->d.buf) { memset (a->d.buf, 0, a->d.size); /* erase the buffer */ xfree (a->d.buf); } xfree (a); } return rc; } int iobuf_cancel (iobuf_t a) { const char *s; iobuf_t a2; int rc; #if defined(HAVE_W32_SYSTEM) || defined(__riscos__) char *remove_name = NULL; #endif if (a && a->use == 2) { s = iobuf_get_real_fname (a); if (s && *s) { #if defined(HAVE_W32_SYSTEM) || defined(__riscos__) remove_name = xstrdup (s); #else remove (s); #endif } } /* send a cancel message to all filters */ for (a2 = a; a2; a2 = a2->chain) { size_t dummy; if (a2->filter) a2->filter (a2->filter_ov, IOBUFCTRL_CANCEL, a2->chain, NULL, &dummy); } rc = iobuf_close (a); #if defined(HAVE_W32_SYSTEM) || defined(__riscos__) if (remove_name) { /* Argg, MSDOS does not allow to remove open files. So * we have to do it here */ #ifdef HAVE_W32CE_SYSTEM wchar_t *wtmp = utf8_to_wchar (remove_name); if (wtmp) DeleteFile (wtmp); xfree (wtmp); #else remove (remove_name); #endif xfree (remove_name); } #endif return rc; } /**************** * create a temporary iobuf, which can be used to collect stuff * in an iobuf and later be written by iobuf_write_temp() to another * iobuf. */ iobuf_t iobuf_temp () { iobuf_t a; a = iobuf_alloc (3, IOBUF_BUFFER_SIZE); return a; } iobuf_t iobuf_temp_with_content (const char *buffer, size_t length) { iobuf_t a; + int i; a = iobuf_alloc (3, length); - memcpy (a->d.buf, buffer, length); + /* memcpy (a->d.buf, buffer, length); */ + for (i=0; i < length; i++) + a->d.buf[i] = buffer[i]; a->d.len = length; return a; } void iobuf_enable_special_filenames (int yes) { special_names_enabled = yes; } /* See whether the filename has the form "-&nnnn", where n is a non-zero number. Returns this number or -1 if it is not the case. */ static int check_special_filename (const char *fname) { if (special_names_enabled && fname && *fname == '-' && fname[1] == '&') { int i; fname += 2; for (i = 0; digitp (fname+i); i++) ; if (!fname[i]) return atoi (fname); } return -1; } /* This fucntion returns true if FNAME indicates a PIPE (stdout or stderr) or a special file name if those are enabled. */ int iobuf_is_pipe_filename (const char *fname) { if (!fname || (*fname=='-' && !fname[1]) ) return 1; return check_special_filename (fname) != -1; } /* Either open the file specified by the file descriptor FD or - if FD is -1, the file with name FNAME. As of now MODE is assumed to be "rb" if FNAME is used. In contrast to iobuf_fdopen the file descriptor FD will not be closed during an iobuf_close. */ iobuf_t iobuf_open_fd_or_name (gnupg_fd_t fd, const char *fname, const char *mode) { iobuf_t a; if (fd == GNUPG_INVALID_FD) a = iobuf_open (fname); else a = iobuf_fdopen_nc (FD2INT(fd), mode); return a; } /**************** * Create a head iobuf for reading from a file * returns: NULL if an error occures and sets errno */ iobuf_t iobuf_open (const char *fname) { iobuf_t a; gnupg_fd_t fp; file_filter_ctx_t *fcx; size_t len = 0; int print_only = 0; int fd; if (!fname || (*fname == '-' && !fname[1])) { fp = FD_FOR_STDIN; fname = "[stdin]"; print_only = 1; } else if ((fd = check_special_filename (fname)) != -1) return iobuf_fdopen (translate_file_handle (fd, 0), "rb"); else if ((fp = fd_cache_open (fname, "rb")) == GNUPG_INVALID_FD) return NULL; a = iobuf_alloc (1, IOBUF_BUFFER_SIZE); fcx = xmalloc (sizeof *fcx + strlen (fname)); fcx->fp = fp; fcx->print_only_name = print_only; strcpy (fcx->fname, fname); if (!print_only) a->real_fname = xstrdup (fname); a->filter = file_filter; a->filter_ov = fcx; file_filter (fcx, IOBUFCTRL_DESC, NULL, (byte *) & a->desc, &len); file_filter (fcx, IOBUFCTRL_INIT, NULL, NULL, &len); if (DBG_IOBUF) log_debug ("iobuf-%d.%d: open '%s' fd=%d\n", a->no, a->subno, fname, FD2INT (fcx->fp)); return a; } static iobuf_t do_iobuf_fdopen (int fd, const char *mode, int keep_open) { iobuf_t a; gnupg_fd_t fp; file_filter_ctx_t *fcx; size_t len; fp = INT2FD (fd); a = iobuf_alloc (strchr (mode, 'w') ? 2 : 1, IOBUF_BUFFER_SIZE); fcx = xmalloc (sizeof *fcx + 20); fcx->fp = fp; fcx->print_only_name = 1; fcx->keep_open = keep_open; sprintf (fcx->fname, "[fd %d]", fd); a->filter = file_filter; a->filter_ov = fcx; file_filter (fcx, IOBUFCTRL_DESC, NULL, (byte *) & a->desc, &len); file_filter (fcx, IOBUFCTRL_INIT, NULL, NULL, &len); if (DBG_IOBUF) log_debug ("iobuf-%d.%d: fdopen%s '%s'\n", a->no, a->subno, keep_open? "_nc":"", fcx->fname); iobuf_ioctl (a, IOBUF_IOCTL_NO_CACHE, 1, NULL); return a; } /* Create a head iobuf for reading or writing from/to a file Returns: * NULL and sets ERRNO if an error occured. */ iobuf_t iobuf_fdopen (int fd, const char *mode) { return do_iobuf_fdopen (fd, mode, 0); } iobuf_t iobuf_fdopen_nc (int fd, const char *mode) { return do_iobuf_fdopen (fd, mode, 1); } iobuf_t iobuf_esopen (estream_t estream, const char *mode, int keep_open) { iobuf_t a; file_es_filter_ctx_t *fcx; size_t len; a = iobuf_alloc (strchr (mode, 'w') ? 2 : 1, IOBUF_BUFFER_SIZE); fcx = xtrymalloc (sizeof *fcx + 30); fcx->fp = estream; fcx->print_only_name = 1; fcx->keep_open = keep_open; sprintf (fcx->fname, "[fd %p]", estream); a->filter = file_es_filter; a->filter_ov = fcx; file_es_filter (fcx, IOBUFCTRL_DESC, NULL, (byte *) & a->desc, &len); file_es_filter (fcx, IOBUFCTRL_INIT, NULL, NULL, &len); if (DBG_IOBUF) log_debug ("iobuf-%d.%d: esopen%s '%s'\n", a->no, a->subno, keep_open? "_nc":"", fcx->fname); return a; } iobuf_t iobuf_sockopen (int fd, const char *mode) { iobuf_t a; #ifdef HAVE_W32_SYSTEM sock_filter_ctx_t *scx; size_t len; a = iobuf_alloc (strchr (mode, 'w') ? 2 : 1, IOBUF_BUFFER_SIZE); scx = xmalloc (sizeof *scx + 25); scx->sock = fd; scx->print_only_name = 1; sprintf (scx->fname, "[sock %d]", fd); a->filter = sock_filter; a->filter_ov = scx; sock_filter (scx, IOBUFCTRL_DESC, NULL, (byte *) & a->desc, &len); sock_filter (scx, IOBUFCTRL_INIT, NULL, NULL, &len); if (DBG_IOBUF) log_debug ("iobuf-%d.%d: sockopen '%s'\n", a->no, a->subno, scx->fname); iobuf_ioctl (a, IOBUF_IOCTL_NO_CACHE, 1, NULL); #else a = iobuf_fdopen (fd, mode); #endif return a; } /**************** * Create an iobuf for writing to a file; the file will be created. * With MODE700 set the file is created with that mode (Unix only). */ iobuf_t iobuf_create (const char *fname, int mode700) { iobuf_t a; gnupg_fd_t fp; file_filter_ctx_t *fcx; size_t len; int print_only = 0; int fd; if (!fname || (*fname == '-' && !fname[1])) { fp = FD_FOR_STDOUT; fname = "[stdout]"; print_only = 1; } else if ((fd = check_special_filename (fname)) != -1) return iobuf_fdopen (translate_file_handle (fd, 1), "wb"); else if ((fp = direct_open (fname, "wb", mode700)) == GNUPG_INVALID_FD) return NULL; a = iobuf_alloc (2, IOBUF_BUFFER_SIZE); fcx = xmalloc (sizeof *fcx + strlen (fname)); fcx->fp = fp; fcx->print_only_name = print_only; strcpy (fcx->fname, fname); if (!print_only) a->real_fname = xstrdup (fname); a->filter = file_filter; a->filter_ov = fcx; file_filter (fcx, IOBUFCTRL_DESC, NULL, (byte *) & a->desc, &len); file_filter (fcx, IOBUFCTRL_INIT, NULL, NULL, &len); if (DBG_IOBUF) log_debug ("iobuf-%d.%d: create '%s'\n", a->no, a->subno, a->desc?a->desc:"?"); return a; } iobuf_t iobuf_openrw (const char *fname) { iobuf_t a; gnupg_fd_t fp; file_filter_ctx_t *fcx; size_t len; if (!fname) return NULL; else if ((fp = direct_open (fname, "r+b", 0)) == GNUPG_INVALID_FD) return NULL; a = iobuf_alloc (2, IOBUF_BUFFER_SIZE); fcx = xmalloc (sizeof *fcx + strlen (fname)); fcx->fp = fp; strcpy (fcx->fname, fname); a->real_fname = xstrdup (fname); a->filter = file_filter; a->filter_ov = fcx; file_filter (fcx, IOBUFCTRL_DESC, NULL, (byte *) & a->desc, &len); file_filter (fcx, IOBUFCTRL_INIT, NULL, NULL, &len); if (DBG_IOBUF) log_debug ("iobuf-%d.%d: openrw '%s'\n", a->no, a->subno, a->desc?a->desc:"?"); return a; } int iobuf_ioctl (iobuf_t a, iobuf_ioctl_t cmd, int intval, void *ptrval) { if (cmd == IOBUF_IOCTL_KEEP_OPEN) { /* Keep system filepointer/descriptor open. This was used in the past by http.c; this ioctl is not directly used anymore. */ if (DBG_IOBUF) log_debug ("iobuf-%d.%d: ioctl '%s' keep_open=%d\n", a ? a->no : -1, a ? a->subno : -1, a && a->desc ? a->desc : "?", intval); for (; a; a = a->chain) if (!a->chain && a->filter == file_filter) { file_filter_ctx_t *b = a->filter_ov; b->keep_open = intval; return 0; } #ifdef HAVE_W32_SYSTEM else if (!a->chain && a->filter == sock_filter) { sock_filter_ctx_t *b = a->filter_ov; b->keep_open = intval; return 0; } #endif } else if (cmd == IOBUF_IOCTL_INVALIDATE_CACHE) { if (DBG_IOBUF) log_debug ("iobuf-*.*: ioctl '%s' invalidate\n", ptrval ? (char *) ptrval : "?"); if (!a && !intval && ptrval) { if (fd_cache_invalidate (ptrval)) return -1; return 0; } } else if (cmd == IOBUF_IOCTL_NO_CACHE) { if (DBG_IOBUF) log_debug ("iobuf-%d.%d: ioctl '%s' no_cache=%d\n", a ? a->no : -1, a ? a->subno : -1, a && a->desc? a->desc : "?", intval); for (; a; a = a->chain) if (!a->chain && a->filter == file_filter) { file_filter_ctx_t *b = a->filter_ov; b->no_cache = intval; return 0; } #ifdef HAVE_W32_SYSTEM else if (!a->chain && a->filter == sock_filter) { sock_filter_ctx_t *b = a->filter_ov; b->no_cache = intval; return 0; } #endif } else if (cmd == IOBUF_IOCTL_FSYNC) { /* Do a fsync on the open fd and return any errors to the caller of iobuf_ioctl. Note that we work on a file name here. */ if (DBG_IOBUF) log_debug ("iobuf-*.*: ioctl '%s' fsync\n", ptrval? (const char*)ptrval:""); if (!a && !intval && ptrval) { return fd_cache_synchronize (ptrval); } } return -1; } /**************** * Register an i/o filter. */ int iobuf_push_filter (iobuf_t a, int (*f) (void *opaque, int control, iobuf_t chain, byte * buf, size_t * len), void *ov) { return iobuf_push_filter2 (a, f, ov, 0); } int iobuf_push_filter2 (iobuf_t a, int (*f) (void *opaque, int control, iobuf_t chain, byte * buf, size_t * len), void *ov, int rel_ov) { iobuf_t b; size_t dummy_len = 0; int rc = 0; if (a->directfp) BUG (); if (a->use == 2 && (rc = iobuf_flush (a))) return rc; if (a->subno >= MAX_NESTING_FILTER) { log_error ("i/o filter too deeply nested - corrupted data?\n"); return GPG_ERR_BAD_DATA; } /* make a copy of the current stream, so that * A is the new stream and B the original one. * The contents of the buffers are transferred to the * new stream. */ b = xmalloc (sizeof *b); memcpy (b, a, sizeof *b); /* fixme: it is stupid to keep a copy of the name at every level * but we need the name somewhere because the name known by file_filter * may have been released when we need the name of the file */ b->real_fname = a->real_fname ? xstrdup (a->real_fname) : NULL; /* remove the filter stuff from the new stream */ a->filter = NULL; a->filter_ov = NULL; a->filter_ov_owner = 0; a->filter_eof = 0; if (a->use == 3) a->use = 2; /* make a write stream from a temp stream */ if (a->use == 2) { /* allocate a fresh buffer for the original stream */ b->d.buf = xmalloc (a->d.size); b->d.len = 0; b->d.start = 0; } else { /* allocate a fresh buffer for the new stream */ a->d.buf = xmalloc (a->d.size); a->d.len = 0; a->d.start = 0; } /* disable nlimit for the new stream */ a->ntotal = b->ntotal + b->nbytes; a->nlimit = a->nbytes = 0; a->nofast &= ~1; /* make a link from the new stream to the original stream */ a->chain = b; a->opaque = b->opaque; /* setup the function on the new stream */ a->filter = f; a->filter_ov = ov; a->filter_ov_owner = rel_ov; a->subno = b->subno + 1; f (ov, IOBUFCTRL_DESC, NULL, (byte *) & a->desc, &dummy_len); if (DBG_IOBUF) { log_debug ("iobuf-%d.%d: push '%s'\n", a->no, a->subno, a->desc?a->desc:"?"); print_chain (a); } /* now we can initialize the new function if we have one */ if (a->filter && (rc = a->filter (a->filter_ov, IOBUFCTRL_INIT, a->chain, NULL, &dummy_len))) log_error ("IOBUFCTRL_INIT failed: %s\n", gpg_strerror (rc)); return rc; } /**************** * Remove an i/o filter. */ static int pop_filter (iobuf_t a, int (*f) (void *opaque, int control, iobuf_t chain, byte * buf, size_t * len), void *ov) { iobuf_t b; size_t dummy_len = 0; int rc = 0; if (a->directfp) BUG (); if (DBG_IOBUF) log_debug ("iobuf-%d.%d: pop '%s'\n", a->no, a->subno, a->desc?a->desc:"?"); if (!a->filter) { /* this is simple */ b = a->chain; assert (b); xfree (a->d.buf); xfree (a->real_fname); memcpy (a, b, sizeof *a); xfree (b); return 0; } for (b = a; b; b = b->chain) if (b->filter == f && (!ov || b->filter_ov == ov)) break; if (!b) log_bug ("pop_filter(): filter function not found\n"); /* flush this stream if it is an output stream */ if (a->use == 2 && (rc = iobuf_flush (b))) { log_error ("iobuf_flush failed in pop_filter: %s\n", gpg_strerror (rc)); return rc; } /* and tell the filter to free it self */ if (b->filter && (rc = b->filter (b->filter_ov, IOBUFCTRL_FREE, b->chain, NULL, &dummy_len))) { log_error ("IOBUFCTRL_FREE failed: %s\n", gpg_strerror (rc)); return rc; } if (b->filter_ov && b->filter_ov_owner) { xfree (b->filter_ov); b->filter_ov = NULL; } /* and see how to remove it */ if (a == b && !b->chain) log_bug ("can't remove the last filter from the chain\n"); else if (a == b) { /* remove the first iobuf from the chain */ /* everything from b is copied to a. This is save because * a flush has been done on the to be removed entry */ b = a->chain; xfree (a->d.buf); xfree (a->real_fname); memcpy (a, b, sizeof *a); xfree (b); if (DBG_IOBUF) log_debug ("iobuf-%d.%d: popped filter\n", a->no, a->subno); } else if (!b->chain) { /* remove the last iobuf from the chain */ log_bug ("Ohh jeee, trying to remove a head filter\n"); } else { /* remove an intermediate iobuf from the chain */ log_bug ("Ohh jeee, trying to remove an intermediate filter\n"); } return rc; } /**************** * read underflow: read more bytes into the buffer and return * the first byte or -1 on EOF. */ static int underflow (iobuf_t a) { size_t len; int rc; assert (a->d.start == a->d.len); if (a->use == 3) return -1; /* EOF because a temp buffer can't do an underflow */ if (a->filter_eof) { if (a->chain) { iobuf_t b = a->chain; if (DBG_IOBUF) log_debug ("iobuf-%d.%d: pop '%s' in underflow\n", a->no, a->subno, a->desc?a->desc:"?"); xfree (a->d.buf); xfree (a->real_fname); memcpy (a, b, sizeof *a); xfree (b); print_chain (a); } else a->filter_eof = 0; /* for the top level filter */ if (DBG_IOBUF) log_debug ("iobuf-%d.%d: underflow: eof (due to filter eof)\n", a->no, a->subno); return -1; /* return one(!) EOF */ } if (a->error) { if (DBG_IOBUF) log_debug ("iobuf-%d.%d: error\n", a->no, a->subno); return -1; } if (a->directfp) { FILE *fp = a->directfp; len = fread (a->d.buf, 1, a->d.size, fp); if (len < a->d.size) { if (ferror (fp)) a->error = gpg_error_from_syserror (); } a->d.len = len; a->d.start = 0; return len ? a->d.buf[a->d.start++] : -1; } if (a->filter) { len = a->d.size; if (DBG_IOBUF) log_debug ("iobuf-%d.%d: underflow: req=%lu\n", a->no, a->subno, (ulong) len); rc = a->filter (a->filter_ov, IOBUFCTRL_UNDERFLOW, a->chain, a->d.buf, &len); if (DBG_IOBUF) { log_debug ("iobuf-%d.%d: underflow: got=%lu rc=%d\n", a->no, a->subno, (ulong) len, rc); /* if( a->no == 1 ) */ /* log_hexdump (" data:", a->d.buf, len); */ } if (a->use == 1 && rc == -1) { /* EOF: we can remove the filter */ size_t dummy_len = 0; /* and tell the filter to free itself */ if ((rc = a->filter (a->filter_ov, IOBUFCTRL_FREE, a->chain, NULL, &dummy_len))) log_error ("IOBUFCTRL_FREE failed: %s\n", gpg_strerror (rc)); if (a->filter_ov && a->filter_ov_owner) { xfree (a->filter_ov); a->filter_ov = NULL; } a->filter = NULL; a->desc = NULL; a->filter_ov = NULL; a->filter_eof = 1; if (!len && a->chain) { iobuf_t b = a->chain; if (DBG_IOBUF) log_debug ("iobuf-%d.%d: pop in underflow (!len)\n", a->no, a->subno); xfree (a->d.buf); xfree (a->real_fname); memcpy (a, b, sizeof *a); xfree (b); print_chain (a); } } else if (rc) a->error = rc; if (!len) { if (DBG_IOBUF) log_debug ("iobuf-%d.%d: underflow: eof\n", a->no, a->subno); return -1; } a->d.len = len; a->d.start = 0; return a->d.buf[a->d.start++]; } else { if (DBG_IOBUF) log_debug ("iobuf-%d.%d: underflow: eof (no filter)\n", a->no, a->subno); return -1; /* no filter; return EOF */ } } int iobuf_flush (iobuf_t a) { size_t len; int rc; if (a->directfp) return 0; if (a->use == 3) { /* increase the temp buffer */ unsigned char *newbuf; size_t newsize = a->d.size + IOBUF_BUFFER_SIZE; if (DBG_IOBUF) log_debug ("increasing temp iobuf from %lu to %lu\n", (ulong) a->d.size, (ulong) newsize); newbuf = xmalloc (newsize); memcpy (newbuf, a->d.buf, a->d.len); xfree (a->d.buf); a->d.buf = newbuf; a->d.size = newsize; return 0; } else if (a->use != 2) log_bug ("flush on non-output iobuf\n"); else if (!a->filter) log_bug ("iobuf_flush: no filter\n"); len = a->d.len; rc = a->filter (a->filter_ov, IOBUFCTRL_FLUSH, a->chain, a->d.buf, &len); if (!rc && len != a->d.len) { log_info ("iobuf_flush did not write all!\n"); rc = GPG_ERR_INTERNAL; } else if (rc) a->error = rc; a->d.len = 0; return rc; } /**************** * Read a byte from the iobuf; returns -1 on EOF */ int iobuf_readbyte (iobuf_t a) { int c; if (a->nlimit && a->nbytes >= a->nlimit) return -1; /* forced EOF */ if (a->d.start < a->d.len) { c = a->d.buf[a->d.start++]; } else if ((c = underflow (a)) == -1) return -1; /* EOF */ a->nbytes++; return c; } int iobuf_read (iobuf_t a, void *buffer, unsigned int buflen) { unsigned char *buf = (unsigned char *)buffer; int c, n; if (a->nlimit) { /* Handle special cases. */ for (n = 0; n < buflen; n++) { if ((c = iobuf_readbyte (a)) == -1) { if (!n) return -1; /* eof */ break; } else if (buf) *buf = c; if (buf) buf++; } return n; } n = 0; do { if (n < buflen && a->d.start < a->d.len) { unsigned size = a->d.len - a->d.start; if (size > buflen - n) size = buflen - n; if (buf) memcpy (buf, a->d.buf + a->d.start, size); n += size; a->d.start += size; if (buf) buf += size; } if (n < buflen) { if ((c = underflow (a)) == -1) { a->nbytes += n; return n ? n : -1 /*EOF*/; } if (buf) *buf++ = c; n++; } } while (n < buflen); a->nbytes += n; return n; } /**************** * Have a look at the iobuf. * NOTE: This only works in special cases. */ int iobuf_peek (iobuf_t a, byte * buf, unsigned buflen) { int n = 0; if (a->filter_eof) return -1; if (!(a->d.start < a->d.len)) { if (underflow (a) == -1) return -1; /* And unget this character. */ assert (a->d.start == 1); a->d.start = 0; } for (n = 0; n < buflen && (a->d.start + n) < a->d.len; n++, buf++) *buf = a->d.buf[n]; return n; } int iobuf_writebyte (iobuf_t a, unsigned int c) { int rc; if (a->directfp) BUG (); if (a->d.len == a->d.size) if ((rc=iobuf_flush (a))) return rc; assert (a->d.len < a->d.size); a->d.buf[a->d.len++] = c; return 0; } int iobuf_write (iobuf_t a, const void *buffer, unsigned int buflen) { const unsigned char *buf = (const unsigned char *)buffer; int rc; if (a->directfp) BUG (); do { if (buflen && a->d.len < a->d.size) { unsigned size = a->d.size - a->d.len; if (size > buflen) size = buflen; memcpy (a->d.buf + a->d.len, buf, size); buflen -= size; buf += size; a->d.len += size; } if (buflen) { rc = iobuf_flush (a); if (rc) return rc; } } while (buflen); return 0; } int iobuf_writestr (iobuf_t a, const char *buf) { int rc; for (; *buf; buf++) if ((rc=iobuf_writebyte (a, *buf))) return rc; return 0; } /**************** * copy the contents of TEMP to A. */ int iobuf_write_temp (iobuf_t a, iobuf_t temp) { while (temp->chain) pop_filter (temp, temp->filter, NULL); return iobuf_write (a, temp->d.buf, temp->d.len); } /**************** * copy the contents of the temp io stream to BUFFER. */ size_t iobuf_temp_to_buffer (iobuf_t a, byte * buffer, size_t buflen) { size_t n = a->d.len; if (n > buflen) n = buflen; memcpy (buffer, a->d.buf, n); return n; } /**************** * Call this function to terminate processing of the temp stream * without closing it. This removes all filters from the stream * makes sure that iobuf_get_temp_{buffer,length}() returns correct * values. */ void iobuf_flush_temp (iobuf_t temp) { while (temp->chain) pop_filter (temp, temp->filter, NULL); } /**************** * Set a limit on how many bytes may be read from the input stream A. * Setting the limit to 0 disables this feature. */ void iobuf_set_limit (iobuf_t a, off_t nlimit) { if (nlimit) a->nofast |= 1; else a->nofast &= ~1; a->nlimit = nlimit; a->ntotal += a->nbytes; a->nbytes = 0; } /* Return the length of an open file A. IF OVERFLOW is not NULL it will be set to true if the file is larger than what off_t can cope with. The function return 0 on error or on overflow condition. */ off_t iobuf_get_filelength (iobuf_t a, int *overflow) { struct stat st; if (overflow) *overflow = 0; if ( a->directfp ) { FILE *fp = a->directfp; if ( !fstat(fileno(fp), &st) ) return st.st_size; log_error("fstat() failed: %s\n", strerror(errno) ); return 0; } /* Hmmm: file_filter may have already been removed */ for ( ; a; a = a->chain ) if ( !a->chain && a->filter == file_filter ) { file_filter_ctx_t *b = a->filter_ov; gnupg_fd_t fp = b->fp; #if defined(HAVE_W32_SYSTEM) ulong size; static int (* __stdcall get_file_size_ex) (void *handle, LARGE_INTEGER *r_size); static int get_file_size_ex_initialized; if (!get_file_size_ex_initialized) { void *handle; handle = dlopen ("kernel32.dll", RTLD_LAZY); if (handle) { get_file_size_ex = dlsym (handle, "GetFileSizeEx"); if (!get_file_size_ex) dlclose (handle); } get_file_size_ex_initialized = 1; } if (get_file_size_ex) { /* This is a newer system with GetFileSizeEx; we use this then because it seem that GetFileSize won't return a proper error in case a file is larger than 4GB. */ LARGE_INTEGER exsize; if (get_file_size_ex (fp, &exsize)) { if (!exsize.u.HighPart) return exsize.u.LowPart; if (overflow) *overflow = 1; return 0; } } else { if ((size=GetFileSize (fp, NULL)) != 0xffffffff) return size; } log_error ("GetFileSize for handle %p failed: %s\n", fp, w32_strerror (0)); #else if ( !fstat (FD2INT (fp), &st) ) return st.st_size; log_error("fstat() failed: %s\n", strerror(errno) ); #endif break/*the for loop*/; } return 0; } /* Return the file descriptor of the underlying file or -1 if it is not available. */ int iobuf_get_fd (iobuf_t a) { if (a->directfp) return fileno ( (FILE*)a->directfp ); for ( ; a; a = a->chain ) if (!a->chain && a->filter == file_filter) { file_filter_ctx_t *b = a->filter_ov; gnupg_fd_t fp = b->fp; return FD2INT (fp); } return -1; } /**************** * Tell the file position, where the next read will take place */ off_t iobuf_tell (iobuf_t a) { return a->ntotal + a->nbytes; } #if !defined(HAVE_FSEEKO) && !defined(fseeko) #ifdef HAVE_LIMITS_H # include #endif #ifndef LONG_MAX # define LONG_MAX ((long) ((unsigned long) -1 >> 1)) #endif #ifndef LONG_MIN # define LONG_MIN (-1 - LONG_MAX) #endif /**************** * A substitute for fseeko, for hosts that don't have it. */ static int fseeko (FILE * stream, off_t newpos, int whence) { while (newpos != (long) newpos) { long pos = newpos < 0 ? LONG_MIN : LONG_MAX; if (fseek (stream, pos, whence) != 0) return -1; newpos -= pos; whence = SEEK_CUR; } return fseek (stream, (long) newpos, whence); } #endif /**************** * This is a very limited implementation. It simply discards all internal * buffering and removes all filters but the first one. */ int iobuf_seek (iobuf_t a, off_t newpos) { file_filter_ctx_t *b = NULL; if (a->directfp) { FILE *fp = a->directfp; if (fseeko (fp, newpos, SEEK_SET)) { log_error ("can't seek: %s\n", strerror (errno)); return -1; } clearerr (fp); } else if (a->use != 3) /* Not a temp stream. */ { for (; a; a = a->chain) { if (!a->chain && a->filter == file_filter) { b = a->filter_ov; break; } } if (!a) return -1; #ifdef HAVE_W32_SYSTEM if (SetFilePointer (b->fp, newpos, NULL, FILE_BEGIN) == 0xffffffff) { log_error ("SetFilePointer failed on handle %p: ec=%d\n", b->fp, (int) GetLastError ()); return -1; } #else if (lseek (b->fp, newpos, SEEK_SET) == (off_t) - 1) { log_error ("can't lseek: %s\n", strerror (errno)); return -1; } #endif } if (a->use != 3) a->d.len = 0; /* Discard the buffer unless it is a temp stream. */ a->d.start = 0; a->nbytes = 0; a->nlimit = 0; a->nofast &= ~1; a->ntotal = newpos; a->error = 0; /* remove filters, but the last */ if (a->chain) log_debug ("pop_filter called in iobuf_seek - please report\n"); while (a->chain) pop_filter (a, a->filter, NULL); return 0; } /**************** * Retrieve the real filename. This is the filename actually used on * disk and not a made up one. Returns NULL if no real filename is * available. */ const char * iobuf_get_real_fname (iobuf_t a) { if (a->real_fname) return a->real_fname; /* the old solution */ for (; a; a = a->chain) if (!a->chain && a->filter == file_filter) { file_filter_ctx_t *b = a->filter_ov; return b->print_only_name ? NULL : b->fname; } return NULL; } /**************** * Retrieve the filename. This name should only be used in diagnostics. */ const char * iobuf_get_fname (iobuf_t a) { for (; a; a = a->chain) if (!a->chain && a->filter == file_filter) { file_filter_ctx_t *b = a->filter_ov; return b->fname; } return NULL; } /* Same as iobuf_get_fname but never returns NULL. */ const char * iobuf_get_fname_nonnull (iobuf_t a) { const char *fname; fname = iobuf_get_fname (a); return fname? fname : "[?]"; } /**************** * enable partial block mode as described in the OpenPGP draft. * LEN is the first length byte on read, but ignored on writes. */ void iobuf_set_partial_block_mode (iobuf_t a, size_t len) { block_filter_ctx_t *ctx = xcalloc (1, sizeof *ctx); assert (a->use == 1 || a->use == 2); ctx->use = a->use; if (!len) { if (a->use == 1) log_debug ("pop_filter called in set_partial_block_mode" " - please report\n"); pop_filter (a, block_filter, NULL); } else { ctx->partial = 1; ctx->size = 0; ctx->first_c = len; iobuf_push_filter (a, block_filter, ctx); } } /**************** * Same as fgets() but if the buffer is too short a larger one will * be allocated up to some limit *max_length. * A line is considered a byte stream ending in a LF. * Returns the length of the line. EOF is indicated by a line of * length zero. The last LF may be missing due to an EOF. * is max_length is zero on return, the line has been truncated. * * Note: The buffer is allocated with enough space to append a CR,LF,EOL */ unsigned int iobuf_read_line (iobuf_t a, byte ** addr_of_buffer, unsigned *length_of_buffer, unsigned *max_length) { int c; char *buffer = (char *)*addr_of_buffer; unsigned length = *length_of_buffer; unsigned nbytes = 0; unsigned maxlen = *max_length; char *p; if (!buffer) { /* must allocate a new buffer */ length = 256; buffer = xmalloc (length); *addr_of_buffer = (unsigned char *)buffer; *length_of_buffer = length; } length -= 3; /* reserve 3 bytes (cr,lf,eol) */ p = buffer; while ((c = iobuf_get (a)) != -1) { if (nbytes == length) { /* increase the buffer */ if (length > maxlen) { /* this is out limit */ /* skip the rest of the line */ while (c != '\n' && (c = iobuf_get (a)) != -1) ; *p++ = '\n'; /* always append a LF (we have reserved space) */ nbytes++; *max_length = 0; /* indicate truncation */ break; } length += 3; /* correct for the reserved byte */ length += length < 1024 ? 256 : 1024; buffer = xrealloc (buffer, length); *addr_of_buffer = (unsigned char *)buffer; *length_of_buffer = length; length -= 3; /* and reserve again */ p = buffer + nbytes; } *p++ = c; nbytes++; if (c == '\n') break; } *p = 0; /* make sure the line is a string */ return nbytes; } static int translate_file_handle (int fd, int for_write) { #if defined(HAVE_W32CE_SYSTEM) /* This is called only with one of the special filenames. Under W32CE the FD here is not a file descriptor but a rendezvous id, thus we need to finish the pipe first. */ fd = _assuan_w32ce_finish_pipe (fd, for_write); #elif defined(HAVE_W32_SYSTEM) { int x; (void)for_write; if (fd == 0) x = (int) GetStdHandle (STD_INPUT_HANDLE); else if (fd == 1) x = (int) GetStdHandle (STD_OUTPUT_HANDLE); else if (fd == 2) x = (int) GetStdHandle (STD_ERROR_HANDLE); else x = fd; if (x == -1) log_debug ("GetStdHandle(%d) failed: ec=%d\n", fd, (int) GetLastError ()); fd = x; } #else (void)for_write; #endif return fd; } void iobuf_skip_rest (iobuf_t a, unsigned long n, int partial) { if ( partial ) { for (;;) { if (a->nofast || a->d.start >= a->d.len) { if (iobuf_readbyte (a) == -1) { break; } } else { unsigned long count = a->d.len - a->d.start; a->nbytes += count; a->d.start = a->d.len; } } } else { unsigned long remaining = n; while (remaining > 0) { if (a->nofast || a->d.start >= a->d.len) { if (iobuf_readbyte (a) == -1) { break; } --remaining; } else { unsigned long count = a->d.len - a->d.start; if (count > remaining) { count = remaining; } a->nbytes += count; a->d.start += count; remaining -= count; } } } } diff --git a/common/pka.c b/common/pka.c index d47216298..4ead97f63 100644 --- a/common/pka.c +++ b/common/pka.c @@ -1,337 +1,339 @@ /* pka.c - DNS Public Key Association RR access * Copyright (C) 2005, 2009 Free Software Foundation, Inc. * * This file is part of GnuPG. * * This file is free software; you can redistribute it and/or modify * it 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. * * This file 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 #ifdef USE_DNS_PKA #include #ifdef _WIN32 # ifdef HAVE_WINSOCK2_H # include # endif # include #else #include #include #include #endif #endif /* USE_DNS_PKA */ #ifdef USE_ADNS # include #endif #include "util.h" +#include "host2net.h" #include "pka.h" #ifdef USE_DNS_PKA /* Parse the TXT resource record. Format is: v=pka1;fpr=a4d94e92b0986ab5ee9dcd755de249965b0358a2;uri=string For simplicity white spaces are not allowed. Because we expect to use a new RRTYPE for this in the future we define the TXT really strict for simplicity: No white spaces, case sensitivity of the names, order must be as given above. Only URI is optional. This function modifies BUFFER. On success 0 is returned, the 20 byte fingerprint stored at FPR and BUFFER contains the URI or an empty string. */ static int parse_txt_record (char *buffer, unsigned char *fpr) { char *p, *pend; int i; p = buffer; pend = strchr (p, ';'); if (!pend) return -1; *pend++ = 0; if (strcmp (p, "v=pka1")) return -1; /* Wrong or missing version. */ p = pend; pend = strchr (p, ';'); if (pend) *pend++ = 0; if (strncmp (p, "fpr=", 4)) return -1; /* Missing fingerprint part. */ p += 4; for (i=0; i < 20 && hexdigitp (p) && hexdigitp (p+1); i++, p += 2) fpr[i] = xtoi_2 (p); if (i != 20) return -1; /* Fingerprint consists not of exactly 40 hexbytes. */ p = pend; if (!p || !*p) { *buffer = 0; return 0; /* Success (no URI given). */ } if (strncmp (p, "uri=", 4)) return -1; /* Unknown part. */ p += 4; /* There is an URI, copy it to the start of the buffer. */ while (*p) *buffer++ = *p++; *buffer = 0; return 0; } /* For the given email ADDRESS lookup the PKA information in the DNS. On success the 20 byte SHA-1 fingerprint is stored at FPR and the URI will be returned in an allocated buffer. Note that the URI might be an zero length string as this information is optional. Caller must xfree the returned string. On error NULL is returned and the 20 bytes at FPR are not defined. */ char * get_pka_info (const char *address, unsigned char *fpr) { #ifdef USE_ADNS int rc; adns_state state; const char *domain; char *name; adns_answer *answer = NULL; char *buffer = NULL; domain = strrchr (address, '@'); if (!domain || domain == address || !domain[1]) return NULL; /* Invalid mail address given. */ name = xtrymalloc (strlen (address) + 5 + 1); if (!name) return NULL; memcpy (name, address, domain - address); strcpy (stpcpy (name + (domain-address), "._pka."), domain+1); rc = adns_init (&state, adns_if_noerrprint, NULL); if (rc) { log_error ("error initializing adns: %s\n", strerror (errno)); xfree (name); return NULL; } rc = adns_synchronous (state, name, adns_r_txt, adns_qf_quoteok_query, &answer); xfree (name); if (rc) { log_error ("DNS query failed: %s\n", strerror (errno)); adns_finish (state); return NULL; } if (answer->status != adns_s_ok || answer->type != adns_r_txt || !answer->nrrs) { log_error ("DNS query returned an error: %s (%s)\n", adns_strerror (answer->status), adns_errabbrev (answer->status)); adns_free (answer); adns_finish (state); return NULL; } /* We use a PKA records iff there is exactly one record. */ if (answer->nrrs == 1 && answer->rrs.manyistr[0]->i != -1) { buffer = xtrystrdup (answer->rrs.manyistr[0]->str); if (parse_txt_record (buffer, fpr)) { xfree (buffer); buffer = NULL; /* Not a valid gpg trustdns RR. */ } } adns_free (answer); adns_finish (state); return buffer; #else /*!USE_ADNS*/ unsigned char answer[PACKETSZ]; int anslen; int qdcount, ancount; int rc; unsigned char *p, *pend; const char *domain; char *name; HEADER header; domain = strrchr (address, '@'); if (!domain || domain == address || !domain[1]) return NULL; /* invalid mail address given. */ name = xtrymalloc (strlen (address) + 5 + 1); if (!name) return NULL; memcpy (name, address, domain - address); strcpy (stpcpy (name + (domain-address), "._pka."), domain+1); anslen = res_query (name, C_IN, T_TXT, answer, PACKETSZ); xfree (name); if (anslen < sizeof(HEADER)) return NULL; /* DNS resolver returned a too short answer. */ /* Don't despair: A good compiler should optimize this away, as header is just 32 byte and constant at compile time. It's one way to comply with strict aliasing rules. */ memcpy (&header, answer, sizeof (header)); if ( (rc=header.rcode) != NOERROR ) return NULL; /* DNS resolver returned an error. */ /* We assume that PACKETSZ is large enough and don't do dynmically expansion of the buffer. */ if (anslen > PACKETSZ) return NULL; /* DNS resolver returned a too long answer */ qdcount = ntohs (header.qdcount); ancount = ntohs (header.ancount); if (!ancount) return NULL; /* Got no answer. */ p = answer + sizeof (HEADER); pend = answer + anslen; /* Actually points directly behind the buffer. */ while (qdcount-- && p < pend) { rc = dn_skipname (p, pend); if (rc == -1) return NULL; p += rc + QFIXEDSZ; } if (ancount > 1) return NULL; /* more than one possible gpg trustdns record - none used. */ while (ancount-- && p <= pend) { unsigned int type, class, txtlen, n; char *buffer, *bufp; rc = dn_skipname (p, pend); if (rc == -1) return NULL; p += rc; if (p >= pend - 10) return NULL; /* RR too short. */ - type = *p++ << 8; - type |= *p++; - class = *p++ << 8; - class |= *p++; + type = buf16_to_uint (p); + p += 2; + class = buf16_to_uint (p); + p += 2; p += 4; - txtlen = *p++ << 8; - txtlen |= *p++; + txtlen = buf16_to_uint (p); + p += 2; + if (type != T_TXT || class != C_IN) return NULL; /* Answer does not match the query. */ buffer = bufp = xmalloc (txtlen + 1); while (txtlen && p < pend) { for (n = *p++, txtlen--; txtlen && n && p < pend; txtlen--, n--) *bufp++ = *p++; } *bufp = 0; if (parse_txt_record (buffer, fpr)) { xfree (buffer); return NULL; /* Not a valid gpg trustdns RR. */ } return buffer; } return NULL; #endif /*!USE_ADNS*/ } #else /* !USE_DNS_PKA */ /* Dummy version of the function if we can't use the resolver functions. */ char * get_pka_info (const char *address, unsigned char *fpr) { (void)address; (void)fpr; return NULL; } #endif /* !USE_DNS_PKA */ #ifdef TEST int main(int argc,char *argv[]) { unsigned char fpr[20]; char *uri; int i; if (argc < 2) { fprintf (stderr, "usage: pka mail-addresses\n"); return 1; } argc--; argv++; for (; argc; argc--, argv++) { uri = get_pka_info ( *argv, fpr ); printf ("%s", *argv); if (uri) { putchar (' '); for (i=0; i < 20; i++) printf ("%02X", fpr[i]); if (*uri) printf (" %s", uri); xfree (uri); } putchar ('\n'); } return 0; } #endif /* TEST */ /* Local Variables: compile-command: "cc -DUSE_DNS_PKA -DTEST -I.. -I../include -Wall -g -o pka pka.c -lresolv ../tools/no-libgcrypt.o ../jnlib/libjnlib.a" End: */ diff --git a/common/srv.c b/common/srv.c index 7a0c42d4f..2107aa528 100644 --- a/common/srv.c +++ b/common/srv.c @@ -1,331 +1,333 @@ /* srv.c - DNS SRV code * Copyright (C) 2003, 2009 Free Software Foundation, Inc. * * This file is part of GnuPG. * * This file is free software; you can redistribute it and/or modify * it 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. * * This file 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 #ifdef _WIN32 # ifdef HAVE_WINSOCK2_H # include # endif # include #else #include #include #include #endif #include #include #include #include #ifdef USE_ADNS # include #endif #include "util.h" +#include "host2net.h" #include "srv.h" /* Not every installation has gotten around to supporting SRVs yet... */ #ifndef T_SRV #define T_SRV 33 #endif static int priosort(const void *a,const void *b) { const struct srventry *sa=a,*sb=b; if(sa->priority>sb->priority) return 1; else if(sa->prioritypriority) return -1; else return 0; } int getsrv (const char *name,struct srventry **list) { int srvcount=0; u16 count; int i, rc; *list = NULL; #ifdef USE_ADNS { adns_state state; adns_answer *answer = NULL; rc = adns_init (&state, adns_if_noerrprint, NULL); if (rc) { log_error ("error initializing adns: %s\n", strerror (errno)); return -1; } rc = adns_synchronous (state, name, adns_r_srv, adns_qf_quoteok_query, &answer); if (rc) { log_error ("DNS query failed: %s\n", strerror (errno)); adns_finish (state); return -1; } if (answer->status != adns_s_ok || answer->type != adns_r_srv || !answer->nrrs) { log_error ("DNS query returned an error or no records: %s (%s)\n", adns_strerror (answer->status), adns_errabbrev (answer->status)); adns_free (answer); adns_finish (state); return 0; } for (count = 0; count < answer->nrrs; count++) { struct srventry *srv = NULL; struct srventry *newlist; if (strlen (answer->rrs.srvha[count].ha.host) >= MAXDNAME) { log_info ("hostname in SRV record too long - skipped\n"); continue; } newlist = xtryrealloc (*list, (srvcount+1)*sizeof(struct srventry)); if (!newlist) goto fail; *list = newlist; memset (&(*list)[srvcount], 0, sizeof(struct srventry)); srv = &(*list)[srvcount]; srvcount++; srv->priority = answer->rrs.srvha[count].priority; srv->weight = answer->rrs.srvha[count].weight; srv->port = answer->rrs.srvha[count].port; strcpy (srv->target, answer->rrs.srvha[count].ha.host); } adns_free (answer); adns_finish (state); } #else /*!USE_ADNS*/ { unsigned char answer[2048]; HEADER *header = (HEADER *)answer; unsigned char *pt, *emsg; int r; u16 dlen; r = res_query (name, C_IN, T_SRV, answer, sizeof answer); if (r < sizeof (HEADER) || r > sizeof answer) return -1; if (header->rcode != NOERROR || !(count=ntohs (header->ancount))) return 0; /* Error or no record found. */ emsg = &answer[r]; pt = &answer[sizeof(HEADER)]; /* Skip over the query */ rc = dn_skipname (pt, emsg); if (rc == -1) goto fail; pt += rc + QFIXEDSZ; while (count-- > 0 && pt < emsg) { struct srventry *srv=NULL; u16 type,class; struct srventry *newlist; newlist = xtryrealloc (*list, (srvcount+1)*sizeof(struct srventry)); if (!newlist) goto fail; *list = newlist; memset(&(*list)[srvcount],0,sizeof(struct srventry)); srv=&(*list)[srvcount]; srvcount++; rc = dn_skipname(pt,emsg); /* the name we just queried for */ if (rc == -1) goto fail; pt+=rc; /* Truncated message? */ if((emsg-pt)<16) goto fail; - type=*pt++ << 8; - type|=*pt++; + type = buf16_to_u16 (pt); + pt += 2; /* We asked for SRV and got something else !? */ if(type!=T_SRV) goto fail; - class=*pt++ << 8; - class|=*pt++; + class = buf16_to_u16 (pt); + pt += 2; /* We asked for IN and got something else !? */ if(class!=C_IN) goto fail; - pt+=4; /* ttl */ - dlen=*pt++ << 8; - dlen|=*pt++; - srv->priority=*pt++ << 8; - srv->priority|=*pt++; - srv->weight=*pt++ << 8; - srv->weight|=*pt++; - srv->port=*pt++ << 8; - srv->port|=*pt++; + pt += 4; /* ttl */ + dlen = buf16_to_u16 (pt); + pt += 2; + + srv->priority = buf16_to_ushort (pt); + pt += 2; + srv->weight = buf16_to_ushort (pt); + pt += 2; + srv->port = buf16_to_ushort (pt); + pt += 2; /* Get the name. 2782 doesn't allow name compression, but dn_expand still works to pull the name out of the packet. */ rc = dn_expand(answer,emsg,pt,srv->target,MAXDNAME); if (rc == 1 && srv->target[0] == 0) /* "." */ { xfree(*list); *list = NULL; return 0; } if (rc == -1) goto fail; pt += rc; /* Corrupt packet? */ if (dlen != rc+6) goto fail; } } #endif /*!USE_ADNS*/ /* Now we have an array of all the srv records. */ /* Order by priority */ qsort(*list,srvcount,sizeof(struct srventry),priosort); /* For each priority, move the zero-weighted items first. */ for (i=0; i < srvcount; i++) { int j; for (j=i;j < srvcount && (*list)[i].priority == (*list)[j].priority; j++) { if((*list)[j].weight==0) { /* Swap j with i */ if(j!=i) { struct srventry temp; memcpy (&temp,&(*list)[j],sizeof(struct srventry)); memcpy (&(*list)[j],&(*list)[i],sizeof(struct srventry)); memcpy (&(*list)[i],&temp,sizeof(struct srventry)); } break; } } } /* Run the RFC-2782 weighting algorithm. We don't need very high quality randomness for this, so regular libc srand/rand is sufficient. Fixme: It is a bit questionaly to reinitalize srand - better use a gnupg fucntion for this. */ srand(time(NULL)*getpid()); for (i=0; i < srvcount; i++) { int j; float prio_count=0,chose; for (j=i; j < srvcount && (*list)[i].priority == (*list)[j].priority; j++) { prio_count+=(*list)[j].weight; (*list)[j].run_count=prio_count; } chose=prio_count*rand()/RAND_MAX; for (j=i;j. */ #include #include #include #include #include #if GNUPG_MAJOR_VERSION == 1 #define GPG_ERR_EOF (-1) #define GPG_ERR_BAD_BER (1) /*G10ERR_GENERAL*/ #define GPG_ERR_INV_SEXP (45) /*G10ERR_INV_ARG*/ typedef int gpg_error_t; #define gpg_make_err(x,n) (n) #else #include #endif #include "util.h" #include "tlv.h" static const unsigned char * do_find_tlv (const unsigned char *buffer, size_t length, int tag, size_t *nbytes, int nestlevel) { const unsigned char *s = buffer; size_t n = length; size_t len; int this_tag; int composite; for (;;) { buffer = s; if (n < 2) return NULL; /* Buffer definitely too short for tag and length. */ if (!*s || *s == 0xff) { /* Skip optional filler between TLV objects. */ s++; n--; continue; } composite = !!(*s & 0x20); if ((*s & 0x1f) == 0x1f) { /* more tag bytes to follow */ s++; n--; if (n < 2) return NULL; /* buffer definitely too short for tag and length. */ if ((*s & 0x1f) == 0x1f) return NULL; /* We support only up to 2 bytes. */ this_tag = (s[-1] << 8) | (s[0] & 0x7f); } else this_tag = s[0]; len = s[1]; s += 2; n -= 2; if (len < 0x80) ; else if (len == 0x81) { /* One byte length follows. */ if (!n) return NULL; /* we expected 1 more bytes with the length. */ len = s[0]; s++; n--; } else if (len == 0x82) { /* Two byte length follows. */ if (n < 2) return NULL; /* We expected 2 more bytes with the length. */ - len = (s[0] << 8) | s[1]; + len = ((size_t)s[0] << 8) | s[1]; s += 2; n -= 2; } else return NULL; /* APDU limit is 65535, thus it does not make sense to assume longer length fields. */ if (composite && nestlevel < 100) { /* Dive into this composite DO after checking for a too deep nesting. */ const unsigned char *tmp_s; size_t tmp_len; tmp_s = do_find_tlv (s, len, tag, &tmp_len, nestlevel+1); if (tmp_s) { *nbytes = tmp_len; return tmp_s; } } if (this_tag == tag) { *nbytes = len; return s; } if (len > n) return NULL; /* Buffer too short to skip to the next tag. */ s += len; n -= len; } } /* Locate a TLV encoded data object in BUFFER of LENGTH and return a pointer to value as well as its length in NBYTES. Return NULL if it was not found or if the object does not fit into the buffer. */ const unsigned char * find_tlv (const unsigned char *buffer, size_t length, int tag, size_t *nbytes) { const unsigned char *p; p = do_find_tlv (buffer, length, tag, nbytes, 0); if (p && *nbytes > (length - (p-buffer))) p = NULL; /* Object longer than buffer. */ return p; } /* Locate a TLV encoded data object in BUFFER of LENGTH and return a pointer to value as well as its length in NBYTES. Return NULL if it was not found. Note, that the function does not check whether the value fits into the provided buffer. */ const unsigned char * find_tlv_unchecked (const unsigned char *buffer, size_t length, int tag, size_t *nbytes) { return do_find_tlv (buffer, length, tag, nbytes, 0); } /* ASN.1 BER parser: Parse BUFFER of length SIZE and return the tag and the length part from the TLV triplet. Update BUFFER and SIZE on success. */ gpg_error_t parse_ber_header (unsigned char const **buffer, size_t *size, int *r_class, int *r_tag, int *r_constructed, int *r_ndef, size_t *r_length, size_t *r_nhdr) { int c; unsigned long tag; const unsigned char *buf = *buffer; size_t length = *size; *r_ndef = 0; *r_length = 0; *r_nhdr = 0; /* Get the tag. */ if (!length) return gpg_err_make (default_errsource, GPG_ERR_EOF); c = *buf++; length--; ++*r_nhdr; *r_class = (c & 0xc0) >> 6; *r_constructed = !!(c & 0x20); tag = c & 0x1f; if (tag == 0x1f) { tag = 0; do { tag <<= 7; if (!length) return gpg_err_make (default_errsource, GPG_ERR_EOF); c = *buf++; length--; ++*r_nhdr; tag |= c & 0x7f; } while (c & 0x80); } *r_tag = tag; /* Get the length. */ if (!length) return gpg_err_make (default_errsource, GPG_ERR_EOF); c = *buf++; length--; ++*r_nhdr; if ( !(c & 0x80) ) *r_length = c; else if (c == 0x80) *r_ndef = 1; else if (c == 0xff) return gpg_err_make (default_errsource, GPG_ERR_BAD_BER); else { unsigned long len = 0; int count = c & 0x7f; if (count > sizeof (len) || count > sizeof (size_t)) return gpg_err_make (default_errsource, GPG_ERR_BAD_BER); for (; count; count--) { len <<= 8; if (!length) return gpg_err_make (default_errsource, GPG_ERR_EOF); c = *buf++; length--; ++*r_nhdr; len |= c & 0xff; } *r_length = len; } /* Without this kludge some example certs can't be parsed. */ if (*r_class == CLASS_UNIVERSAL && !*r_tag) *r_length = 0; *buffer = buf; *size = length; return 0; } /* FIXME: The following function should not go into this file but for now it is easier to keep it here. */ /* Return the next token of an canonical encoded S-expression. BUF is the pointer to the S-expression and BUFLEN is a pointer to the length of this S-expression (used to validate the syntax). Both are updated to reflect the new position. The token itself is returned as a pointer into the original buffer at TOK and TOKLEN. If a parentheses is the next token, TOK will be set to NULL. TOKLEN is checked to be within the bounds. On error an error code is returned and no pointer is not guaranteed to point to a meaningful value. DEPTH should be initialized to 0 and will reflect on return the actual depth of the tree. To detect the end of the S-expression it is advisable to check DEPTH after a successful return. depth = 0; while (!(err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen)) && depth) process_token (tok, toklen); if (err) handle_error (); */ gpg_error_t parse_sexp (unsigned char const **buf, size_t *buflen, int *depth, unsigned char const **tok, size_t *toklen) { const unsigned char *s; size_t n, vlen; s = *buf; n = *buflen; *tok = NULL; *toklen = 0; if (!n) return *depth ? gpg_err_make (default_errsource, GPG_ERR_INV_SEXP) : 0; if (*s == '(') { s++; n--; (*depth)++; *buf = s; *buflen = n; return 0; } if (*s == ')') { if (!*depth) return gpg_err_make (default_errsource, GPG_ERR_INV_SEXP); *toklen = 1; s++; n--; (*depth)--; *buf = s; *buflen = n; return 0; } for (vlen=0; n && *s && *s != ':' && (*s >= '0' && *s <= '9'); s++, n--) vlen = vlen*10 + (*s - '0'); if (!n || *s != ':') return gpg_err_make (default_errsource, GPG_ERR_INV_SEXP); s++; n--; if (vlen > n) return gpg_err_make (default_errsource, GPG_ERR_INV_SEXP); *tok = s; *toklen = vlen; s += vlen; n -= vlen; *buf = s; *buflen = n; return 0; } diff --git a/dirmngr/ldap.c b/dirmngr/ldap.c index 00df167e2..c59619897 100644 --- a/dirmngr/ldap.c +++ b/dirmngr/ldap.c @@ -1,843 +1,844 @@ /* ldap.c - LDAP access * Copyright (C) 2002 Klarälvdalens Datakonsult AB * Copyright (C) 2003, 2004, 2005, 2007, 2008, 2010 g10 Code GmbH * * 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 #include #include #include "dirmngr.h" #include "exechelp.h" #include "crlfetch.h" #include "ldapserver.h" #include "misc.h" #include "ldap-wrapper.h" +#include "host2net.h" #define UNENCODED_URL_CHARS "abcdefghijklmnopqrstuvwxyz" \ "ABCDEFGHIJKLMNOPQRSTUVWXYZ" \ "01234567890" \ "$-_.+!*'()," #define USERCERTIFICATE "userCertificate" #define CACERTIFICATE "caCertificate" #define X509CACERT "x509caCert" #define USERSMIMECERTIFICATE "userSMIMECertificate" /* Definition for the context of the cert fetch functions. */ struct cert_fetch_context_s { ksba_reader_t reader; /* The reader used (shallow copy). */ unsigned char *tmpbuf; /* Helper buffer. */ size_t tmpbufsize; /* Allocated size of tmpbuf. */ int truncated; /* Flag to indicate a truncated output. */ }; /* Add HOST and PORT to our list of LDAP servers. Fixme: We should better use an extra list of servers. */ static void add_server_to_servers (const char *host, int port) { ldap_server_t server; ldap_server_t last = NULL; const char *s; if (!port) port = 389; for (server=opt.ldapservers; server; server = server->next) { if (!strcmp (server->host, host) && server->port == port) return; /* already in list... */ last = server; } /* We assume that the host names are all supplied by our configuration files and thus are sane. To keep this assumption we must reject all invalid host names. */ for (s=host; *s; s++) if (!strchr ("abcdefghijklmnopqrstuvwxyz" "ABCDEFGHIJKLMNOPQRSTUVWXYZ" "01234567890.-", *s)) { log_error (_("invalid char 0x%02x in host name - not added\n"), *s); return; } log_info (_("adding '%s:%d' to the ldap server list\n"), host, port); server = xtrycalloc (1, sizeof *s); if (!server) log_error (_("malloc failed: %s\n"), strerror (errno)); else { server->host = xstrdup (host); server->port = port; if (last) last->next = server; else opt.ldapservers = server; } } /* Perform an LDAP query. Returns an gpg error code or 0 on success. The function returns a new reader object at READER. */ static gpg_error_t run_ldap_wrapper (ctrl_t ctrl, int ignore_timeout, int multi_mode, const char *proxy, const char *host, int port, const char *user, const char *pass, const char *dn, const char *filter, const char *attr, const char *url, ksba_reader_t *reader) { const char *argv[40]; int argc; char portbuf[30], timeoutbuf[30]; *reader = NULL; argc = 0; if (pass) /* Note, that the password most be the first item. */ { argv[argc++] = "--pass"; argv[argc++] = pass; } if (opt.verbose) argv[argc++] = "-vv"; argv[argc++] = "--log-with-pid"; if (multi_mode) argv[argc++] = "--multi"; if (opt.ldaptimeout) { sprintf (timeoutbuf, "%u", opt.ldaptimeout); argv[argc++] = "--timeout"; argv[argc++] = timeoutbuf; if (ignore_timeout) argv[argc++] = "--only-search-timeout"; } if (proxy) { argv[argc++] = "--proxy"; argv[argc++] = proxy; } if (host) { argv[argc++] = "--host"; argv[argc++] = host; } if (port) { sprintf (portbuf, "%d", port); argv[argc++] = "--port"; argv[argc++] = portbuf; } if (user) { argv[argc++] = "--user"; argv[argc++] = user; } if (dn) { argv[argc++] = "--dn"; argv[argc++] = dn; } if (filter) { argv[argc++] = "--filter"; argv[argc++] = filter; } if (attr) { argv[argc++] = "--attr"; argv[argc++] = attr; } argv[argc++] = url? url : "ldap://"; argv[argc] = NULL; return ldap_wrapper (ctrl, reader, argv); } /* Perform a LDAP query using a given URL. On success a new ksba reader is returned. If HOST or PORT are not 0, they are used to override the values from the URL. */ gpg_error_t url_fetch_ldap (ctrl_t ctrl, const char *url, const char *host, int port, ksba_reader_t *reader) { gpg_error_t err; err = run_ldap_wrapper (ctrl, 1, /* Ignore explicit timeout because CRLs might be very large. */ 0, opt.ldap_proxy, host, port, NULL, NULL, NULL, NULL, NULL, url, reader); /* FIXME: This option might be used for DoS attacks. Because it will enlarge the list of servers to consult without a limit and all LDAP queries w/o a host are will then try each host in turn. */ if (!err && opt.add_new_ldapservers && !opt.ldap_proxy) { if (host) add_server_to_servers (host, port); else if (url) { char *tmp = host_and_port_from_url (url, &port); if (tmp) { add_server_to_servers (tmp, port); xfree (tmp); } } } /* If the lookup failed and we are not only using the proxy, we try again using our default list of servers. */ if (err && !(opt.ldap_proxy && opt.only_ldap_proxy)) { struct ldapserver_iter iter; if (DBG_LOOKUP) log_debug ("no hostname in URL or query failed; " "trying all default hostnames\n"); for (ldapserver_iter_begin (&iter, ctrl); err && ! ldapserver_iter_end_p (&iter); ldapserver_iter_next (&iter)) { ldap_server_t server = iter.server; err = run_ldap_wrapper (ctrl, 0, 0, NULL, server->host, server->port, NULL, NULL, NULL, NULL, NULL, url, reader); if (!err) break; } } return err; } /* Perform an LDAP query on all configured servers. On error the error code of the last try is returned. */ gpg_error_t attr_fetch_ldap (ctrl_t ctrl, const char *dn, const char *attr, ksba_reader_t *reader) { gpg_error_t err = gpg_error (GPG_ERR_CONFIGURATION); struct ldapserver_iter iter; *reader = NULL; /* FIXME; we might want to look at the Base SN to try matching servers first. */ for (ldapserver_iter_begin (&iter, ctrl); ! ldapserver_iter_end_p (&iter); ldapserver_iter_next (&iter)) { ldap_server_t server = iter.server; err = run_ldap_wrapper (ctrl, 0, 0, opt.ldap_proxy, server->host, server->port, server->user, server->pass, dn, "objectClass=*", attr, NULL, reader); if (!err) break; /* Probably found a result. Ready. */ } return err; } /* Parse PATTERN and return a new strlist to be used for the actual LDAP query. Bit 0 of the flags field is set if that pattern is actually a base specification. Caller must release the returned strlist. NULL is returned on error. * Possible patterns: * * KeyID * Fingerprint * OpenPGP userid * x Email address Indicated by a left angle bracket. * Exact word match in user id or subj. name * x Subj. DN indicated bu a leading slash * Issuer DN * Serial number + subj. DN * x Substring match indicated by a leading '*; is also the default. */ strlist_t parse_one_pattern (const char *pattern) { strlist_t result = NULL; char *p; switch (*pattern) { case '<': /* Email. */ { pattern++; result = xmalloc (sizeof *result + 5 + strlen (pattern)); result->next = NULL; result->flags = 0; p = stpcpy (stpcpy (result->d, "mail="), pattern); if (p[-1] == '>') *--p = 0; if (!*result->d) /* Error. */ { xfree (result); result = NULL; } break; } case '/': /* Subject DN. */ pattern++; if (*pattern) { result = xmalloc (sizeof *result + strlen (pattern)); result->next = NULL; result->flags = 1; /* Base spec. */ strcpy (result->d, pattern); } break; case '#': /* Issuer DN. */ pattern++; if (*pattern == '/') /* Just issuer DN. */ { pattern++; } else /* Serial number + issuer DN */ { } break; case '*': pattern++; default: /* Take as substring match. */ { const char format[] = "(|(sn=*%s*)(|(cn=*%s*)(mail=*%s*)))"; if (*pattern) { result = xmalloc (sizeof *result + strlen (format) + 3 * strlen (pattern)); result->next = NULL; result->flags = 0; sprintf (result->d, format, pattern, pattern, pattern); } } break; } return result; } /* Take the string STRING and escape it accoring to the URL rules. Retun a newly allocated string. */ static char * escape4url (const char *string) { const char *s; char *buf, *p; size_t n; if (!string) string = ""; for (s=string,n=0; *s; s++) if (strchr (UNENCODED_URL_CHARS, *s)) n++; else n += 3; buf = malloc (n+1); if (!buf) return NULL; for (s=string,p=buf; *s; s++) if (strchr (UNENCODED_URL_CHARS, *s)) *p++ = *s; else { sprintf (p, "%%%02X", *(const unsigned char *)s); p += 3; } *p = 0; return buf; } /* Create a LDAP URL from DN and FILTER and return it in URL. We don't need the host and port because this will be specified using the override options. */ static gpg_error_t make_url (char **url, const char *dn, const char *filter) { gpg_error_t err; char *u_dn, *u_filter; char const attrs[] = (USERCERTIFICATE "," /* USERSMIMECERTIFICATE "," */ CACERTIFICATE "," X509CACERT ); *url = NULL; u_dn = escape4url (dn); if (!u_dn) return gpg_error_from_errno (errno); u_filter = escape4url (filter); if (!u_filter) { err = gpg_error_from_errno (errno); xfree (u_dn); return err; } *url = malloc ( 8 + strlen (u_dn) + 1 + strlen (attrs) + 5 + strlen (u_filter) + 1 ); if (!*url) { err = gpg_error_from_errno (errno); xfree (u_dn); xfree (u_filter); return err; } stpcpy (stpcpy (stpcpy (stpcpy (stpcpy (stpcpy (*url, "ldap:///"), u_dn), "?"), attrs), "?sub?"), u_filter); xfree (u_dn); xfree (u_filter); return 0; } /* Prepare an LDAP query to return the attribute ATTR for the DN. All configured default servers are queried until one responds. This function returns an error code or 0 and a CONTEXT on success. */ gpg_error_t start_default_fetch_ldap (ctrl_t ctrl, cert_fetch_context_t *context, const char *dn, const char *attr) { gpg_error_t err; struct ldapserver_iter iter; *context = xtrycalloc (1, sizeof **context); if (!*context) return gpg_error_from_errno (errno); /* FIXME; we might want to look at the Base SN to try matching servers first. */ err = gpg_error (GPG_ERR_CONFIGURATION); for (ldapserver_iter_begin (&iter, ctrl); ! ldapserver_iter_end_p (&iter); ldapserver_iter_next (&iter)) { ldap_server_t server = iter.server; err = run_ldap_wrapper (ctrl, 0, 1, opt.ldap_proxy, server->host, server->port, server->user, server->pass, dn, "objectClass=*", attr, NULL, &(*context)->reader); if (!err) break; /* Probably found a result. */ } if (err) { xfree (*context); *context = NULL; } return err; } /* Prepare an LDAP query to return certificates maching PATTERNS using the SERVER. This function returns an error code or 0 and a CONTEXT on success. */ gpg_error_t start_cert_fetch_ldap (ctrl_t ctrl, cert_fetch_context_t *context, strlist_t patterns, const ldap_server_t server) { gpg_error_t err; const char *host; int port; const char *user; const char *pass; const char *base; const char *argv[50]; int argc; char portbuf[30], timeoutbuf[30]; *context = NULL; if (server) { host = server->host; port = server->port; user = server->user; pass = server->pass; base = server->base; } else /* Use a default server. */ return gpg_error (GPG_ERR_NOT_IMPLEMENTED); if (!base) base = ""; argc = 0; if (pass) /* Note: Must be the first item. */ { argv[argc++] = "--pass"; argv[argc++] = pass; } if (opt.verbose) argv[argc++] = "-vv"; argv[argc++] = "--log-with-pid"; argv[argc++] = "--multi"; if (opt.ldaptimeout) { sprintf (timeoutbuf, "%u", opt.ldaptimeout); argv[argc++] = "--timeout"; argv[argc++] = timeoutbuf; } if (opt.ldap_proxy) { argv[argc++] = "--proxy"; argv[argc++] = opt.ldap_proxy; } if (host) { argv[argc++] = "--host"; argv[argc++] = host; } if (port) { sprintf (portbuf, "%d", port); argv[argc++] = "--port"; argv[argc++] = portbuf; } if (user) { argv[argc++] = "--user"; argv[argc++] = user; } for (; patterns; patterns = patterns->next) { strlist_t sl; char *url; if (argc >= DIM (argv) - 1) { /* Too many patterns. It does not make sense to allow an arbitrary number of patters because the length of the command line is limited anyway. */ /* fixme: cleanup. */ return gpg_error (GPG_ERR_RESOURCE_LIMIT); } sl = parse_one_pattern (patterns->d); if (!sl) { log_error (_("start_cert_fetch: invalid pattern '%s'\n"), patterns->d); /* fixme: cleanup argv. */ return gpg_error (GPG_ERR_INV_USER_ID); } if ((sl->flags & 1)) err = make_url (&url, sl->d, "objectClass=*"); else err = make_url (&url, base, sl->d); free_strlist (sl); if (err) { /* fixme: cleanup argv. */ return err; } argv[argc++] = url; } argv[argc] = NULL; *context = xtrycalloc (1, sizeof **context); if (!*context) return gpg_error_from_errno (errno); err = ldap_wrapper (ctrl, &(*context)->reader, argv); if (err) { xfree (*context); *context = NULL; } return err; } /* Read a fixed amount of data from READER into BUFFER. */ static gpg_error_t read_buffer (ksba_reader_t reader, unsigned char *buffer, size_t count) { gpg_error_t err; size_t nread; while (count) { err = ksba_reader_read (reader, buffer, count, &nread); if (err) return err; buffer += nread; count -= nread; } return 0; } /* Fetch the next certificate. Return 0 on success, GPG_ERR_EOF if no (more) certificates are available or any other error code. GPG_ERR_TRUNCATED may be returned to indicate that the result has been truncated. */ gpg_error_t fetch_next_cert_ldap (cert_fetch_context_t context, unsigned char **value, size_t *valuelen) { gpg_error_t err; unsigned char hdr[5]; char *p, *pend; - int n; + unsigned long n; int okay = 0; /* int is_cms = 0; */ *value = NULL; *valuelen = 0; err = 0; while (!err) { err = read_buffer (context->reader, hdr, 5); if (err) break; - n = (hdr[1] << 24)|(hdr[2]<<16)|(hdr[3]<<8)|hdr[4]; + n = buf32_to_ulong (hdr+1); if (*hdr == 'V' && okay) { #if 0 /* That code is not yet ready. */ if (is_cms) { /* The certificate needs to be parsed from CMS data. */ ksba_cms_t cms; ksba_stop_reason_t stopreason; int i; err = ksba_cms_new (&cms); if (err) goto leave; err = ksba_cms_set_reader_writer (cms, context->reader, NULL); if (err) { log_error ("ksba_cms_set_reader_writer failed: %s\n", gpg_strerror (err)); goto leave; } do { err = ksba_cms_parse (cms, &stopreason); if (err) { log_error ("ksba_cms_parse failed: %s\n", gpg_strerror (err)); goto leave; } if (stopreason == KSBA_SR_BEGIN_DATA) log_error ("userSMIMECertificate is 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 #endif { *value = xtrymalloc (n); if (!*value) return gpg_error_from_errno (errno); *valuelen = n; err = read_buffer (context->reader, *value, n); break; /* Ready or error. */ } } else if (!n && *hdr == 'A') okay = 0; else if (n) { if (n > context->tmpbufsize) { xfree (context->tmpbuf); context->tmpbufsize = 0; context->tmpbuf = xtrymalloc (n+1); if (!context->tmpbuf) return gpg_error_from_errno (errno); context->tmpbufsize = n; } err = read_buffer (context->reader, context->tmpbuf, n); if (err) break; if (*hdr == 'A') { p = context->tmpbuf; p[n] = 0; /*(we allocated one extra byte for this.)*/ /* fixme: is_cms = 0; */ if ( (pend = strchr (p, ';')) ) *pend = 0; /* Strip off the extension. */ if (!ascii_strcasecmp (p, USERCERTIFICATE)) { if (DBG_LOOKUP) log_debug ("fetch_next_cert_ldap: got attribute '%s'\n", USERCERTIFICATE); okay = 1; } else if (!ascii_strcasecmp (p, CACERTIFICATE)) { if (DBG_LOOKUP) log_debug ("fetch_next_cert_ldap: got attribute '%s'\n", CACERTIFICATE); okay = 1; } else if (!ascii_strcasecmp (p, X509CACERT)) { if (DBG_LOOKUP) log_debug ("fetch_next_cert_ldap: got attribute '%s'\n", CACERTIFICATE); okay = 1; } /* else if (!ascii_strcasecmp (p, USERSMIMECERTIFICATE)) */ /* { */ /* if (DBG_LOOKUP) */ /* log_debug ("fetch_next_cert_ldap: got attribute '%s'\n", */ /* USERSMIMECERTIFICATE); */ /* okay = 1; */ /* is_cms = 1; */ /* } */ else { if (DBG_LOOKUP) log_debug ("fetch_next_cert_ldap: got attribute '%s'" " - ignored\n", p); okay = 0; } } else if (*hdr == 'E') { p = context->tmpbuf; p[n] = 0; /*(we allocated one extra byte for this.)*/ if (!strcmp (p, "truncated")) { context->truncated = 1; log_info (_("ldap_search hit the size limit of" " the server\n")); } } } } if (err) { xfree (*value); *value = NULL; *valuelen = 0; if (gpg_err_code (err) == GPG_ERR_EOF && context->truncated) { context->truncated = 0; /* So that the next call would return EOF. */ err = gpg_error (GPG_ERR_TRUNCATED); } } return err; } void end_cert_fetch_ldap (cert_fetch_context_t context) { if (context) { ksba_reader_t reader = context->reader; xfree (context->tmpbuf); xfree (context); ldap_wrapper_release_context (reader); ksba_reader_release (reader); } } diff --git a/g10/build-packet.c b/g10/build-packet.c index cda753ca1..e44350e44 100644 --- a/g10/build-packet.c +++ b/g10/build-packet.c @@ -1,1344 +1,1342 @@ /* build-packet.c - assemble packets and write them * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, * 2006, 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 #include "gpg.h" #include "util.h" #include "packet.h" #include "status.h" #include "iobuf.h" #include "i18n.h" #include "options.h" +#include "host2net.h" static int do_user_id( IOBUF out, int ctb, PKT_user_id *uid ); static int do_key (iobuf_t out, int ctb, PKT_public_key *pk); static int do_symkey_enc( IOBUF out, int ctb, PKT_symkey_enc *enc ); static int do_pubkey_enc( IOBUF out, int ctb, PKT_pubkey_enc *enc ); static u32 calc_plaintext( PKT_plaintext *pt ); static int do_plaintext( IOBUF out, int ctb, PKT_plaintext *pt ); static int do_encrypted( IOBUF out, int ctb, PKT_encrypted *ed ); static int do_encrypted_mdc( IOBUF out, int ctb, PKT_encrypted *ed ); static int do_compressed( IOBUF out, int ctb, PKT_compressed *cd ); static int do_signature( IOBUF out, int ctb, PKT_signature *sig ); static int do_onepass_sig( IOBUF out, int ctb, PKT_onepass_sig *ops ); static int calc_header_length( u32 len, int new_ctb ); static int write_16(IOBUF inp, u16 a); static int write_32(IOBUF inp, u32 a); static int write_header( IOBUF out, int ctb, u32 len ); static int write_sign_packet_header( IOBUF out, int ctb, u32 len ); static int write_header2( IOBUF out, int ctb, u32 len, int hdrlen ); static int write_new_header( IOBUF out, int ctb, u32 len, int hdrlen ); /**************** * Build a packet and write it to INP * Returns: 0 := okay * >0 := error * Note: Caller must free the packet */ int build_packet( IOBUF out, PACKET *pkt ) { int new_ctb=0, rc=0, ctb; int pkttype; if( DBG_PACKET ) log_debug("build_packet() type=%d\n", pkt->pkttype ); assert( pkt->pkt.generic ); switch ((pkttype = pkt->pkttype)) { case PKT_PUBLIC_KEY: if (pkt->pkt.public_key->seckey_info) pkttype = PKT_SECRET_KEY; break; case PKT_PUBLIC_SUBKEY: if (pkt->pkt.public_key->seckey_info) pkttype = PKT_SECRET_SUBKEY; break; case PKT_PLAINTEXT: new_ctb = pkt->pkt.plaintext->new_ctb; break; case PKT_ENCRYPTED: case PKT_ENCRYPTED_MDC: new_ctb = pkt->pkt.encrypted->new_ctb; break; case PKT_COMPRESSED:new_ctb = pkt->pkt.compressed->new_ctb; break; case PKT_USER_ID: if( pkt->pkt.user_id->attrib_data ) pkttype = PKT_ATTRIBUTE; break; default: break; } if( new_ctb || pkttype > 15 ) /* new format */ ctb = 0xc0 | (pkttype & 0x3f); else ctb = 0x80 | ((pkttype & 15)<<2); switch( pkttype ) { case PKT_ATTRIBUTE: case PKT_USER_ID: rc = do_user_id( out, ctb, pkt->pkt.user_id ); break; case PKT_OLD_COMMENT: case PKT_COMMENT: /* Ignore these. Theoretically, this will never be called as we have no way to output comment packets any longer, but just in case there is some code path that would end up outputting a comment that was written before comments were dropped (in the public key?) this is a no-op. */ break; case PKT_PUBLIC_SUBKEY: case PKT_PUBLIC_KEY: case PKT_SECRET_SUBKEY: case PKT_SECRET_KEY: rc = do_key (out, ctb, pkt->pkt.public_key); break; case PKT_SYMKEY_ENC: rc = do_symkey_enc( out, ctb, pkt->pkt.symkey_enc ); break; case PKT_PUBKEY_ENC: rc = do_pubkey_enc( out, ctb, pkt->pkt.pubkey_enc ); break; case PKT_PLAINTEXT: rc = do_plaintext( out, ctb, pkt->pkt.plaintext ); break; case PKT_ENCRYPTED: rc = do_encrypted( out, ctb, pkt->pkt.encrypted ); break; case PKT_ENCRYPTED_MDC: rc = do_encrypted_mdc( out, ctb, pkt->pkt.encrypted ); break; case PKT_COMPRESSED: rc = do_compressed( out, ctb, pkt->pkt.compressed ); break; case PKT_SIGNATURE: rc = do_signature( out, ctb, pkt->pkt.signature ); break; case PKT_ONEPASS_SIG: rc = do_onepass_sig( out, ctb, pkt->pkt.onepass_sig ); break; case PKT_RING_TRUST: break; /* ignore it (keyring.c does write it directly)*/ case PKT_MDC: /* we write it directly, so we should never see it here. */ default: log_bug("invalid packet type in build_packet()\n"); break; } return rc; } /* * Write the mpi A to OUT. */ gpg_error_t gpg_mpi_write (iobuf_t out, gcry_mpi_t a) { int rc; if (gcry_mpi_get_flag (a, GCRYMPI_FLAG_OPAQUE)) { unsigned int nbits; const void *p; unsigned int lenhdr[2]; p = gcry_mpi_get_opaque (a, &nbits); lenhdr[0] = nbits >> 8; lenhdr[1] = nbits; rc = iobuf_write (out, lenhdr, 2); if (!rc) rc = iobuf_write (out, p, (nbits+7)/8); } else { char buffer[(MAX_EXTERN_MPI_BITS+7)/8+2]; /* 2 is for the mpi length. */ size_t nbytes; nbytes = DIM(buffer); rc = gcry_mpi_print (GCRYMPI_FMT_PGP, buffer, nbytes, &nbytes, a ); if( !rc ) rc = iobuf_write( out, buffer, nbytes ); else if (gpg_err_code(rc) == GPG_ERR_TOO_SHORT ) { log_info ("mpi too large (%u bits)\n", gcry_mpi_get_nbits (a)); /* The buffer was too small. We better tell the user about the MPI. */ rc = gpg_error (GPG_ERR_TOO_LARGE); } } return rc; } /* * Write an opaque MPI to the output stream without length info. */ gpg_error_t gpg_mpi_write_nohdr (iobuf_t out, gcry_mpi_t a) { int rc; if (gcry_mpi_get_flag (a, GCRYMPI_FLAG_OPAQUE)) { unsigned int nbits; const void *p; p = gcry_mpi_get_opaque (a, &nbits); rc = iobuf_write (out, p, (nbits+7)/8); } else rc = gpg_error (GPG_ERR_BAD_MPI); return rc; } /* Calculate the length of a packet described by PKT. */ u32 calc_packet_length( PACKET *pkt ) { u32 n=0; int new_ctb = 0; assert( pkt->pkt.generic ); switch( pkt->pkttype ) { case PKT_PLAINTEXT: n = calc_plaintext( pkt->pkt.plaintext ); new_ctb = pkt->pkt.plaintext->new_ctb; break; case PKT_ATTRIBUTE: case PKT_USER_ID: case PKT_COMMENT: case PKT_PUBLIC_KEY: case PKT_SECRET_KEY: case PKT_SYMKEY_ENC: case PKT_PUBKEY_ENC: case PKT_ENCRYPTED: case PKT_SIGNATURE: case PKT_ONEPASS_SIG: case PKT_RING_TRUST: case PKT_COMPRESSED: default: log_bug("invalid packet type in calc_packet_length()"); break; } n += calc_header_length(n, new_ctb); return n; } static gpg_error_t write_fake_data (IOBUF out, gcry_mpi_t a) { unsigned int n; void *p; if (!a) return 0; p = gcry_mpi_get_opaque ( a, &n); if (!p) return 0; /* For example due to a read error in parse-packet.c:read_rest. */ return iobuf_write (out, p, (n+7)/8 ); } static int do_user_id( IOBUF out, int ctb, PKT_user_id *uid ) { int rc; if (uid->attrib_data) { write_header(out, ctb, uid->attrib_len); rc = iobuf_write( out, uid->attrib_data, uid->attrib_len ); } else { write_header2( out, ctb, uid->len, 2 ); rc = iobuf_write( out, uid->name, uid->len ); } return rc; } static int do_key (iobuf_t out, int ctb, PKT_public_key *pk) { gpg_error_t err = 0; int i, nskey, npkey; iobuf_t a = iobuf_temp(); /* Build in a self-enlarging buffer. */ /* Write the version number - if none is specified, use 4 */ if ( !pk->version ) iobuf_put ( a, 4 ); else iobuf_put ( a, pk->version ); write_32 (a, pk->timestamp ); iobuf_put (a, pk->pubkey_algo ); /* Get number of secret and public parameters. They are held in one array first the public ones, then the secret ones. */ nskey = pubkey_get_nskey (pk->pubkey_algo); npkey = pubkey_get_npkey (pk->pubkey_algo); /* If we don't have any public parameters - which is for example the case if we don't know the algorithm used - the parameters are stored as one blob in a faked (opaque) MPI. */ if (!npkey) { write_fake_data (a, pk->pkey[0]); goto leave; } assert (npkey < nskey); for (i=0; i < npkey; i++ ) { if ( (pk->pubkey_algo == PUBKEY_ALGO_ECDSA && (i == 0)) || (pk->pubkey_algo == PUBKEY_ALGO_EDDSA && (i == 0)) || (pk->pubkey_algo == PUBKEY_ALGO_ECDH && (i == 0 || i == 2))) err = gpg_mpi_write_nohdr (a, pk->pkey[i]); else err = gpg_mpi_write (a, pk->pkey[i]); if (err) goto leave; } if (pk->seckey_info) { /* This is a secret key packet. */ struct seckey_info *ski = pk->seckey_info; /* Build the header for protected (encrypted) secret parameters. */ if (ski->is_protected) { /* OpenPGP protection according to rfc2440. */ iobuf_put (a, ski->sha1chk? 0xfe : 0xff); iobuf_put (a, ski->algo); if (ski->s2k.mode >= 1000) { /* These modes are not possible in OpenPGP, we use them to implement our extensions, 101 can be viewed as a private/experimental extension (this is not specified in rfc2440 but the same scheme is used for all other algorithm identifiers). */ iobuf_put (a, 101); iobuf_put (a, ski->s2k.hash_algo); iobuf_write (a, "GNU", 3 ); iobuf_put (a, ski->s2k.mode - 1000); } else { iobuf_put (a, ski->s2k.mode); iobuf_put (a, ski->s2k.hash_algo); } if (ski->s2k.mode == 1 || ski->s2k.mode == 3) iobuf_write (a, ski->s2k.salt, 8); if (ski->s2k.mode == 3) iobuf_put (a, ski->s2k.count); /* For our special modes 1001, 1002 we do not need an IV. */ if (ski->s2k.mode != 1001 && ski->s2k.mode != 1002) iobuf_write (a, ski->iv, ski->ivlen); } else /* Not protected. */ iobuf_put (a, 0 ); if (ski->s2k.mode == 1001) ; /* GnuPG extension - don't write a secret key at all. */ else if (ski->s2k.mode == 1002) { /* GnuPG extension - divert to OpenPGP smartcard. */ /* Length of the serial number or 0 for no serial number. */ iobuf_put (a, ski->ivlen ); /* The serial number gets stored in the IV field. */ iobuf_write (a, ski->iv, ski->ivlen); } else if (ski->is_protected) { /* The secret key is protected - write it out as it is. */ byte *p; unsigned int ndatabits; assert (gcry_mpi_get_flag (pk->pkey[npkey], GCRYMPI_FLAG_OPAQUE)); p = gcry_mpi_get_opaque (pk->pkey[npkey], &ndatabits); iobuf_write (a, p, (ndatabits+7)/8 ); } else { /* Non-protected key. */ for ( ; i < nskey; i++ ) if ( (err = gpg_mpi_write (a, pk->pkey[i]))) goto leave; write_16 (a, ski->csum ); } } leave: if (!err) { /* Build the header of the packet - which we must do after writing all the other stuff, so that we know the length of the packet */ write_header2 (out, ctb, iobuf_get_temp_length(a), pk->hdrbytes); /* And finally write it out to the real stream. */ err = iobuf_write_temp (out, a); } iobuf_close (a); /* Close the temporary buffer */ return err; } static int do_symkey_enc( IOBUF out, int ctb, PKT_symkey_enc *enc ) { int rc = 0; IOBUF a = iobuf_temp(); assert( enc->version == 4 ); switch( enc->s2k.mode ) { case 0: case 1: case 3: break; default: log_bug("do_symkey_enc: s2k=%d\n", enc->s2k.mode ); } iobuf_put( a, enc->version ); iobuf_put( a, enc->cipher_algo ); iobuf_put( a, enc->s2k.mode ); iobuf_put( a, enc->s2k.hash_algo ); if( enc->s2k.mode == 1 || enc->s2k.mode == 3 ) { iobuf_write(a, enc->s2k.salt, 8 ); if( enc->s2k.mode == 3 ) iobuf_put(a, enc->s2k.count); } if( enc->seskeylen ) iobuf_write(a, enc->seskey, enc->seskeylen ); write_header(out, ctb, iobuf_get_temp_length(a) ); rc = iobuf_write_temp( out, a ); iobuf_close(a); return rc; } static int do_pubkey_enc( IOBUF out, int ctb, PKT_pubkey_enc *enc ) { int rc = 0; int n, i; IOBUF a = iobuf_temp(); iobuf_put (a, 3); /* Version. */ if ( enc->throw_keyid ) { write_32(a, 0 ); /* Don't tell Eve who can decrypt the message. */ write_32(a, 0 ); } else { write_32(a, enc->keyid[0] ); write_32(a, enc->keyid[1] ); } iobuf_put(a,enc->pubkey_algo ); n = pubkey_get_nenc( enc->pubkey_algo ); if ( !n ) write_fake_data( a, enc->data[0] ); for (i=0; i < n && !rc ; i++ ) { if (enc->pubkey_algo == PUBKEY_ALGO_ECDH && i == 1) rc = gpg_mpi_write_nohdr (a, enc->data[i]); else rc = gpg_mpi_write (a, enc->data[i]); } if (!rc) { write_header (out, ctb, iobuf_get_temp_length(a) ); rc = iobuf_write_temp (out, a); } iobuf_close(a); return rc; } static u32 calc_plaintext( PKT_plaintext *pt ) { /* Truncate namelen to the maximum 255 characters. Note this means that a function that calls build_packet with an illegal literal packet will get it back legalized. */ if(pt->namelen>255) pt->namelen=255; return pt->len? (1 + 1 + pt->namelen + 4 + pt->len) : 0; } static int do_plaintext( IOBUF out, int ctb, PKT_plaintext *pt ) { int i, rc = 0; u32 n; byte buf[1000]; /* this buffer has the plaintext! */ int nbytes; write_header(out, ctb, calc_plaintext( pt ) ); iobuf_put(out, pt->mode ); iobuf_put(out, pt->namelen ); for(i=0; i < pt->namelen; i++ ) iobuf_put(out, pt->name[i] ); rc = write_32(out, pt->timestamp ); if (rc) return rc; n = 0; while( (nbytes=iobuf_read(pt->buf, buf, 1000)) != -1 ) { rc = iobuf_write (out, buf, nbytes); if (rc) break; n += nbytes; } wipememory(buf,1000); /* burn the buffer */ if( (ctb&0x40) && !pt->len ) iobuf_set_partial_block_mode(out, 0 ); /* turn off partial */ if( pt->len && n != pt->len ) log_error("do_plaintext(): wrote %lu bytes but expected %lu bytes\n", (ulong)n, (ulong)pt->len ); return rc; } static int do_encrypted( IOBUF out, int ctb, PKT_encrypted *ed ) { int rc = 0; u32 n; n = ed->len ? (ed->len + ed->extralen) : 0; write_header(out, ctb, n ); /* This is all. The caller has to write the real data */ return rc; } static int do_encrypted_mdc( IOBUF out, int ctb, PKT_encrypted *ed ) { int rc = 0; u32 n; assert( ed->mdc_method ); /* Take version number and the following MDC packet in account. */ n = ed->len ? (ed->len + ed->extralen + 1 + 22) : 0; write_header(out, ctb, n ); iobuf_put(out, 1 ); /* version */ /* This is all. The caller has to write the real data */ return rc; } static int do_compressed( IOBUF out, int ctb, PKT_compressed *cd ) { int rc = 0; /* We must use the old convention and don't use blockmode for the sake of PGP 2 compatibility. However if the new_ctb flag was set, CTB is already formatted as new style and write_header2 does create a partial length encoding using new the new style. */ write_header2(out, ctb, 0, 0); iobuf_put(out, cd->algorithm ); /* This is all. The caller has to write the real data */ return rc; } /**************** * Delete all subpackets of type REQTYPE and return a bool whether a packet * was deleted. */ int delete_sig_subpkt (subpktarea_t *area, sigsubpkttype_t reqtype ) { int buflen; sigsubpkttype_t type; byte *buffer, *bufstart; size_t n; size_t unused = 0; int okay = 0; if( !area ) return 0; buflen = area->len; buffer = area->data; for(;;) { if( !buflen ) { okay = 1; break; } bufstart = buffer; n = *buffer++; buflen--; if( n == 255 ) { if( buflen < 4 ) break; - n = (((size_t)buffer[0] << 24) - | (buffer[1] << 16) - | (buffer[2] << 8) - | buffer[3]); + n = buf32_to_size_t (buffer); buffer += 4; buflen -= 4; } else if( n >= 192 ) { if( buflen < 2 ) break; n = (( n - 192 ) << 8) + *buffer + 192; buffer++; buflen--; } if( buflen < n ) break; type = *buffer & 0x7f; if( type == reqtype ) { buffer++; buflen--; n--; if( n > buflen ) break; buffer += n; /* point to next subpkt */ buflen -= n; memmove (bufstart, buffer, buflen); /* shift */ unused += buffer - bufstart; buffer = bufstart; } else { buffer += n; buflen -=n; } } if (!okay) log_error ("delete_subpkt: buffer shorter than subpacket\n"); assert (unused <= area->len); area->len -= unused; return !!unused; } /**************** * Create or update a signature subpacket for SIG of TYPE. This * functions knows where to put the data (hashed or unhashed). The * function may move data from the unhashed part to the hashed one. * Note: All pointers into sig->[un]hashed (e.g. returned by * parse_sig_subpkt) are not valid after a call to this function. The * data to put into the subpaket should be in a buffer with a length * of buflen. */ void build_sig_subpkt (PKT_signature *sig, sigsubpkttype_t type, const byte *buffer, size_t buflen ) { byte *p; int critical, hashed; subpktarea_t *oldarea, *newarea; size_t nlen, n, n0; critical = (type & SIGSUBPKT_FLAG_CRITICAL); type &= ~SIGSUBPKT_FLAG_CRITICAL; /* Sanity check buffer sizes */ if(parse_one_sig_subpkt(buffer,buflen,type)<0) BUG(); switch(type) { case SIGSUBPKT_NOTATION: case SIGSUBPKT_POLICY: case SIGSUBPKT_REV_KEY: case SIGSUBPKT_SIGNATURE: /* we do allow multiple subpackets */ break; default: /* we don't allow multiple subpackets */ delete_sig_subpkt(sig->hashed,type); delete_sig_subpkt(sig->unhashed,type); break; } /* Any special magic that needs to be done for this type so the packet doesn't need to be reparsed? */ switch(type) { case SIGSUBPKT_NOTATION: sig->flags.notation=1; break; case SIGSUBPKT_POLICY: sig->flags.policy_url=1; break; case SIGSUBPKT_PREF_KS: sig->flags.pref_ks=1; break; case SIGSUBPKT_EXPORTABLE: if(buffer[0]) sig->flags.exportable=1; else sig->flags.exportable=0; break; case SIGSUBPKT_REVOCABLE: if(buffer[0]) sig->flags.revocable=1; else sig->flags.revocable=0; break; case SIGSUBPKT_TRUST: sig->trust_depth=buffer[0]; sig->trust_value=buffer[1]; break; case SIGSUBPKT_REGEXP: sig->trust_regexp=buffer; break; /* This should never happen since we don't currently allow creating such a subpacket, but just in case... */ case SIGSUBPKT_SIG_EXPIRE: - if(buffer_to_u32(buffer)+sig->timestamp<=make_timestamp()) + if(buf32_to_u32(buffer)+sig->timestamp<=make_timestamp()) sig->flags.expired=1; else sig->flags.expired=0; break; default: break; } if( (buflen+1) >= 8384 ) nlen = 5; /* write 5 byte length header */ else if( (buflen+1) >= 192 ) nlen = 2; /* write 2 byte length header */ else nlen = 1; /* just a 1 byte length header */ switch( type ) { /* The issuer being unhashed is a historical oddity. It should work equally as well hashed. Of course, if even an unhashed issuer is tampered with, it makes it awfully hard to verify the sig... */ case SIGSUBPKT_ISSUER: case SIGSUBPKT_SIGNATURE: hashed = 0; break; default: hashed = 1; break; } if( critical ) type |= SIGSUBPKT_FLAG_CRITICAL; oldarea = hashed? sig->hashed : sig->unhashed; /* Calculate new size of the area and allocate */ n0 = oldarea? oldarea->len : 0; n = n0 + nlen + 1 + buflen; /* length, type, buffer */ if (oldarea && n <= oldarea->size) { /* fits into the unused space */ newarea = oldarea; /*log_debug ("updating area for type %d\n", type );*/ } else if (oldarea) { newarea = xrealloc (oldarea, sizeof (*newarea) + n - 1); newarea->size = n; /*log_debug ("reallocating area for type %d\n", type );*/ } else { newarea = xmalloc (sizeof (*newarea) + n - 1); newarea->size = n; /*log_debug ("allocating area for type %d\n", type );*/ } newarea->len = n; p = newarea->data + n0; if (nlen == 5) { *p++ = 255; *p++ = (buflen+1) >> 24; *p++ = (buflen+1) >> 16; *p++ = (buflen+1) >> 8; *p++ = (buflen+1); *p++ = type; memcpy (p, buffer, buflen); } else if (nlen == 2) { *p++ = (buflen+1-192) / 256 + 192; *p++ = (buflen+1-192) % 256; *p++ = type; memcpy (p, buffer, buflen); } else { *p++ = buflen+1; *p++ = type; memcpy (p, buffer, buflen); } if (hashed) sig->hashed = newarea; else sig->unhashed = newarea; } /**************** * Put all the required stuff from SIG into subpackets of sig. * Hmmm, should we delete those subpackets which are in a wrong area? */ void build_sig_subpkt_from_sig( PKT_signature *sig ) { u32 u; byte buf[8]; u = sig->keyid[0]; buf[0] = (u >> 24) & 0xff; buf[1] = (u >> 16) & 0xff; buf[2] = (u >> 8) & 0xff; buf[3] = u & 0xff; u = sig->keyid[1]; buf[4] = (u >> 24) & 0xff; buf[5] = (u >> 16) & 0xff; buf[6] = (u >> 8) & 0xff; buf[7] = u & 0xff; build_sig_subpkt( sig, SIGSUBPKT_ISSUER, buf, 8 ); u = sig->timestamp; buf[0] = (u >> 24) & 0xff; buf[1] = (u >> 16) & 0xff; buf[2] = (u >> 8) & 0xff; buf[3] = u & 0xff; build_sig_subpkt( sig, SIGSUBPKT_SIG_CREATED, buf, 4 ); if(sig->expiredate) { if(sig->expiredate>sig->timestamp) u=sig->expiredate-sig->timestamp; else u=1; /* A 1-second expiration time is the shortest one OpenPGP has */ buf[0] = (u >> 24) & 0xff; buf[1] = (u >> 16) & 0xff; buf[2] = (u >> 8) & 0xff; buf[3] = u & 0xff; /* Mark this CRITICAL, so if any implementation doesn't understand sigs that can expire, it'll just disregard this sig altogether. */ build_sig_subpkt( sig, SIGSUBPKT_SIG_EXPIRE | SIGSUBPKT_FLAG_CRITICAL, buf, 4 ); } } void build_attribute_subpkt(PKT_user_id *uid,byte type, const void *buf,u32 buflen, const void *header,u32 headerlen) { byte *attrib; int idx; if(1+headerlen+buflen>8383) idx=5; else if(1+headerlen+buflen>191) idx=2; else idx=1; /* realloc uid->attrib_data to the right size */ uid->attrib_data=xrealloc(uid->attrib_data, uid->attrib_len+idx+1+headerlen+buflen); attrib=&uid->attrib_data[uid->attrib_len]; if(idx==5) { attrib[0]=255; attrib[1]=(1+headerlen+buflen) >> 24; attrib[2]=(1+headerlen+buflen) >> 16; attrib[3]=(1+headerlen+buflen) >> 8; attrib[4]=1+headerlen+buflen; } else if(idx==2) { attrib[0]=(1+headerlen+buflen-192) / 256 + 192; attrib[1]=(1+headerlen+buflen-192) % 256; } else attrib[0]=1+headerlen+buflen; /* Good luck finding a JPEG this small! */ attrib[idx++]=type; /* Tack on our data at the end */ if(headerlen>0) memcpy(&attrib[idx],header,headerlen); memcpy(&attrib[idx+headerlen],buf,buflen); uid->attrib_len+=idx+headerlen+buflen; } struct notation * string_to_notation(const char *string,int is_utf8) { const char *s; int saw_at=0; struct notation *notation; notation=xmalloc_clear(sizeof(*notation)); if(*string=='-') { notation->flags.ignore=1; string++; } if(*string=='!') { notation->flags.critical=1; string++; } /* If and when the IETF assigns some official name tags, we'll have to add them here. */ for( s=string ; *s != '='; s++ ) { if( *s=='@') saw_at++; /* -notationname is legal without an = sign */ if(!*s && notation->flags.ignore) break; if( !*s || !isascii (*s) || (!isgraph(*s) && !isspace(*s)) ) { log_error(_("a notation name must have only printable characters" " or spaces, and end with an '='\n") ); goto fail; } } notation->name=xmalloc((s-string)+1); strncpy(notation->name,string,s-string); notation->name[s-string]='\0'; if(!saw_at && !opt.expert) { log_error(_("a user notation name must contain the '@' character\n")); goto fail; } if (saw_at > 1) { log_error(_("a notation name must not contain more than" " one '@' character\n")); goto fail; } if(*s) { const char *i=s+1; int highbit=0; /* we only support printable text - therefore we enforce the use of only printable characters (an empty value is valid) */ for(s++; *s ; s++ ) { if ( !isascii (*s) ) highbit=1; else if (iscntrl(*s)) { log_error(_("a notation value must not use any" " control characters\n")); goto fail; } } if(!highbit || is_utf8) notation->value=xstrdup(i); else notation->value=native_to_utf8(i); } return notation; fail: free_notation(notation); return NULL; } struct notation * sig_to_notation(PKT_signature *sig) { const byte *p; size_t len; int seq=0,crit; struct notation *list=NULL; while((p=enum_sig_subpkt(sig->hashed,SIGSUBPKT_NOTATION,&len,&seq,&crit))) { int n1,n2; struct notation *n=NULL; if(len<8) { log_info(_("WARNING: invalid notation data found\n")); continue; } n1=(p[4]<<8)|p[5]; n2=(p[6]<<8)|p[7]; if(8+n1+n2!=len) { log_info(_("WARNING: invalid notation data found\n")); continue; } n=xmalloc_clear(sizeof(*n)); n->name=xmalloc(n1+1); memcpy(n->name,&p[8],n1); n->name[n1]='\0'; if(p[0]&0x80) { n->value=xmalloc(n2+1); memcpy(n->value,&p[8+n1],n2); n->value[n2]='\0'; } else { n->bdat=xmalloc(n2); n->blen=n2; memcpy(n->bdat,&p[8+n1],n2); n->value=xmalloc(2+strlen(_("not human readable"))+2+1); strcpy(n->value,"[ "); strcat(n->value,_("not human readable")); strcat(n->value," ]"); } n->flags.critical=crit; n->next=list; list=n; } return list; } void free_notation(struct notation *notation) { while(notation) { struct notation *n=notation; xfree(n->name); xfree(n->value); xfree(n->altvalue); xfree(n->bdat); notation=n->next; xfree(n); } } static int do_signature( IOBUF out, int ctb, PKT_signature *sig ) { int rc = 0; int n, i; IOBUF a = iobuf_temp(); if ( !sig->version ) iobuf_put( a, 3 ); else iobuf_put( a, sig->version ); if ( sig->version < 4 ) iobuf_put (a, 5 ); /* Constant */ iobuf_put (a, sig->sig_class ); if ( sig->version < 4 ) { write_32(a, sig->timestamp ); write_32(a, sig->keyid[0] ); write_32(a, sig->keyid[1] ); } iobuf_put(a, sig->pubkey_algo ); iobuf_put(a, sig->digest_algo ); if ( sig->version >= 4 ) { size_t nn; /* Timestamp and keyid must have been packed into the subpackets prior to the call of this function, because these subpackets are hashed. */ nn = sig->hashed? sig->hashed->len : 0; write_16(a, nn); if (nn) iobuf_write( a, sig->hashed->data, nn ); nn = sig->unhashed? sig->unhashed->len : 0; write_16(a, nn); if (nn) iobuf_write( a, sig->unhashed->data, nn ); } iobuf_put(a, sig->digest_start[0] ); iobuf_put(a, sig->digest_start[1] ); n = pubkey_get_nsig( sig->pubkey_algo ); if ( !n ) write_fake_data( a, sig->data[0] ); for (i=0; i < n && !rc ; i++ ) rc = gpg_mpi_write (a, sig->data[i] ); if (!rc) { if ( is_RSA(sig->pubkey_algo) && sig->version < 4 ) write_sign_packet_header(out, ctb, iobuf_get_temp_length(a) ); else write_header(out, ctb, iobuf_get_temp_length(a) ); rc = iobuf_write_temp( out, a ); } iobuf_close(a); return rc; } static int do_onepass_sig( IOBUF out, int ctb, PKT_onepass_sig *ops ) { int rc = 0; IOBUF a = iobuf_temp(); iobuf_put (a, 3); /* Version. */ iobuf_put(a, ops->sig_class ); iobuf_put(a, ops->digest_algo ); iobuf_put(a, ops->pubkey_algo ); write_32(a, ops->keyid[0] ); write_32(a, ops->keyid[1] ); iobuf_put(a, ops->last ); write_header(out, ctb, iobuf_get_temp_length(a) ); rc = iobuf_write_temp( out, a ); iobuf_close(a); return rc; } static int write_16(IOBUF out, u16 a) { iobuf_put(out, a>>8); if( iobuf_put(out,a) ) return -1; return 0; } static int write_32(IOBUF out, u32 a) { iobuf_put(out, a>> 24); iobuf_put(out, a>> 16); iobuf_put(out, a>> 8); return iobuf_put(out, a); } /**************** * calculate the length of a header */ static int calc_header_length( u32 len, int new_ctb ) { if( !len ) return 1; /* only the ctb */ if( new_ctb ) { if( len < 192 ) return 2; if( len < 8384 ) return 3; else return 6; } if( len < 256 ) return 2; if( len < 65536 ) return 3; return 5; } /**************** * Write the CTB and the packet length */ static int write_header( IOBUF out, int ctb, u32 len ) { return write_header2( out, ctb, len, 0 ); } static int write_sign_packet_header (IOBUF out, int ctb, u32 len) { (void)ctb; /* Work around a bug in the pgp read function for signature packets, which are not correctly coded and silently assume at some point 2 byte length headers.*/ iobuf_put (out, 0x89 ); iobuf_put (out, len >> 8 ); return iobuf_put (out, len) == -1 ? -1:0; } /**************** * If HDRLEN is > 0, try to build a header of this length. We need * this so that we can hash packets without reading them again. If * len is 0, write a partial or indeterminate length header, unless * hdrlen is specified in which case write an actual zero length * (using the specified hdrlen). */ static int write_header2( IOBUF out, int ctb, u32 len, int hdrlen ) { if( ctb & 0x40 ) return write_new_header( out, ctb, len, hdrlen ); if( hdrlen ) { if( hdrlen == 2 && len < 256 ) ; else if( hdrlen == 3 && len < 65536 ) ctb |= 1; else ctb |= 2; } else { if( !len ) ctb |= 3; else if( len < 256 ) ; else if( len < 65536 ) ctb |= 1; else ctb |= 2; } if( iobuf_put(out, ctb ) ) return -1; if( len || hdrlen ) { if( ctb & 2 ) { if(iobuf_put(out, len >> 24 )) return -1; if(iobuf_put(out, len >> 16 )) return -1; } if( ctb & 3 ) if(iobuf_put(out, len >> 8 )) return -1; if( iobuf_put(out, len ) ) return -1; } return 0; } static int write_new_header( IOBUF out, int ctb, u32 len, int hdrlen ) { if( hdrlen ) log_bug("can't cope with hdrlen yet\n"); if( iobuf_put(out, ctb ) ) return -1; if( !len ) { iobuf_set_partial_block_mode(out, 512 ); } else { if( len < 192 ) { if( iobuf_put(out, len ) ) return -1; } else if( len < 8384 ) { len -= 192; if( iobuf_put( out, (len / 256) + 192) ) return -1; if( iobuf_put( out, (len % 256) ) ) return -1; } else { if( iobuf_put( out, 0xff ) ) return -1; if( iobuf_put( out, (len >> 24)&0xff ) ) return -1; if( iobuf_put( out, (len >> 16)&0xff ) ) return -1; if( iobuf_put( out, (len >> 8)&0xff ) ) return -1; if( iobuf_put( out, len & 0xff ) ) return -1; } } return 0; } diff --git a/g10/call-agent.c b/g10/call-agent.c index dc9d1575a..4bac8a0ef 100644 --- a/g10/call-agent.c +++ b/g10/call-agent.c @@ -1,2416 +1,2417 @@ /* call-agent.c - Divert GPG operations to the agent. * Copyright (C) 2001-2003, 2006-2011, 2013 Free Software Foundation, Inc. * Copyright (C) 2013-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 #ifdef HAVE_LOCALE_H #include #endif #include "gpg.h" #include #include "util.h" #include "membuf.h" #include "options.h" #include "i18n.h" #include "asshelp.h" #include "sysutils.h" #include "call-agent.h" #include "status.h" #include "../common/shareddefs.h" +#include "host2net.h" #ifndef DBG_ASSUAN # define DBG_ASSUAN 1 #endif #define CONTROL_D ('D' - 'A' + 1) static assuan_context_t agent_ctx = NULL; static int did_early_card_test; struct default_inq_parm_s { ctrl_t ctrl; assuan_context_t ctx; struct { u32 *keyid; u32 *mainkeyid; int pubkey_algo; } keyinfo; }; struct cipher_parm_s { struct default_inq_parm_s *dflt; assuan_context_t ctx; unsigned char *ciphertext; size_t ciphertextlen; }; struct writecert_parm_s { struct default_inq_parm_s *dflt; const unsigned char *certdata; size_t certdatalen; }; struct writekey_parm_s { struct default_inq_parm_s *dflt; const unsigned char *keydata; size_t keydatalen; }; struct genkey_parm_s { struct default_inq_parm_s *dflt; const char *keyparms; const char *passphrase; }; struct import_key_parm_s { struct default_inq_parm_s *dflt; const void *key; size_t keylen; }; struct cache_nonce_parm_s { char **cache_nonce_addr; char **passwd_nonce_addr; }; struct scd_genkey_parm_s { struct agent_card_genkey_s *cgk; char *savedbytes; /* Malloced space to save key parameter chunks. */ }; static gpg_error_t learn_status_cb (void *opaque, const char *line); /* If RC is not 0, write an appropriate status message. */ static void status_sc_op_failure (int rc) { switch (gpg_err_code (rc)) { case 0: break; case GPG_ERR_CANCELED: case GPG_ERR_FULLY_CANCELED: write_status_text (STATUS_SC_OP_FAILURE, "1"); break; case GPG_ERR_BAD_PIN: write_status_text (STATUS_SC_OP_FAILURE, "2"); break; default: write_status (STATUS_SC_OP_FAILURE); break; } } static gpg_error_t membuf_data_cb (void *opaque, const void *buffer, size_t length) { membuf_t *data = opaque; if (buffer) put_membuf (data, buffer, length); return 0; } /* This is the default inquiry callback. It mainly handles the Pinentry notifications. */ static gpg_error_t default_inq_cb (void *opaque, const char *line) { gpg_error_t err = 0; struct default_inq_parm_s *parm = opaque; if (has_leading_keyword (line, "PINENTRY_LAUNCHED")) { err = gpg_proxy_pinentry_notify (parm->ctrl, line); if (err) log_error (_("failed to proxy %s inquiry to client\n"), "PINENTRY_LAUNCHED"); /* We do not pass errors to avoid breaking other code. */ } else if ((has_leading_keyword (line, "PASSPHRASE") || has_leading_keyword (line, "NEW_PASSPHRASE")) && opt.pinentry_mode == PINENTRY_MODE_LOOPBACK) { if (have_static_passphrase ()) { const char *s = get_static_passphrase (); err = assuan_send_data (parm->ctx, s, strlen (s)); } else { char *pw; if (parm->keyinfo.keyid) emit_status_need_passphrase (parm->keyinfo.keyid, parm->keyinfo.mainkeyid, parm->keyinfo.pubkey_algo); pw = cpr_get_hidden ("passphrase.enter", _("Enter passphrase: ")); cpr_kill_prompt (); if (*pw == CONTROL_D && !pw[1]) err = gpg_error (GPG_ERR_CANCELED); else err = assuan_send_data (parm->ctx, pw, strlen (pw)); xfree (pw); } } else log_debug ("ignoring gpg-agent inquiry '%s'\n", line); return err; } /* Check whether gnome-keyring hijacked the gpg-agent. */ static void check_hijacking (assuan_context_t ctx) { membuf_t mb; char *string; init_membuf (&mb, 64); /* AGENT_ID is a command implemented by gnome-keyring-daemon. It does not return any data but an OK line with a remark. */ if (assuan_transact (ctx, "AGENT_ID", membuf_data_cb, &mb, NULL, NULL, NULL, NULL)) { xfree (get_membuf (&mb, NULL)); return; /* Error - Probably not hijacked. */ } put_membuf (&mb, "", 1); string = get_membuf (&mb, NULL); if (!string || !*string) { /* Definitely hijacked - show a warning prompt. */ static int shown; const char warn1[] = "The GNOME keyring manager hijacked the GnuPG agent."; const char warn2[] = "GnuPG will not work properly - please configure that " "tool to not interfere with the GnuPG system!"; log_info ("WARNING: %s\n", warn1); log_info ("WARNING: %s\n", warn2); /* (GPG_ERR_SOURCRE_GPG, GPG_ERR_NO_AGENT) */ write_status_text (STATUS_ERROR, "check_hijacking 33554509"); xfree (string); string = strconcat (warn1, "\n\n", warn2, NULL); if (string && !shown && !opt.batch) { /* NB: The Pinentry based prompt will only work if a gnome-keyring manager passes invalid commands on to the original gpg-agent. */ char *cmd, *cmdargs; cmdargs = percent_plus_escape (string); cmd = strconcat ("GET_CONFIRMATION ", cmdargs, NULL); xfree (cmdargs); if (cmd) { struct default_inq_parm_s dfltparm; memset (&dfltparm, 0, sizeof dfltparm); dfltparm.ctx = ctx; assuan_transact (ctx, cmd, NULL, NULL, default_inq_cb, &dfltparm, NULL, NULL); xfree (cmd); shown = 1; } } } xfree (string); } /* Try to connect to the agent via socket or fork it off and work by pipes. Handle the server's initial greeting */ static int start_agent (ctrl_t ctrl, int for_card) { int rc; (void)ctrl; /* Not yet used. */ /* Fixme: We need a context for each thread or serialize the access to the agent. */ if (agent_ctx) rc = 0; else { rc = start_new_gpg_agent (&agent_ctx, GPG_ERR_SOURCE_DEFAULT, opt.homedir, opt.agent_program, opt.lc_ctype, opt.lc_messages, opt.session_env, opt.autostart, opt.verbose, DBG_ASSUAN, NULL, NULL); if (!opt.autostart && gpg_err_code (rc) == GPG_ERR_NO_AGENT) { static int shown; if (!shown) { shown = 1; log_info (_("no gpg-agent running in this session\n")); } } else if (!rc) { /* Tell the agent that we support Pinentry notifications. No error checking so that it will work also with older agents. */ assuan_transact (agent_ctx, "OPTION allow-pinentry-notify", NULL, NULL, NULL, NULL, NULL, NULL); /* Tell the agent about what version we are aware. This is here used to indirectly enable GPG_ERR_FULLY_CANCELED. */ assuan_transact (agent_ctx, "OPTION agent-awareness=2.1.0", NULL, NULL, NULL, NULL, NULL, NULL); /* Pass on the pinentry mode. */ if (opt.pinentry_mode) { char *tmp = xasprintf ("OPTION pinentry-mode=%s", str_pinentry_mode (opt.pinentry_mode)); rc = assuan_transact (agent_ctx, tmp, NULL, NULL, NULL, NULL, NULL, NULL); xfree (tmp); if (rc) log_error ("setting pinentry mode '%s' failed: %s\n", str_pinentry_mode (opt.pinentry_mode), gpg_strerror (rc)); } check_hijacking (agent_ctx); } } if (!rc && for_card && !did_early_card_test) { /* Request the serial number of the card for an early test. */ struct agent_card_info_s info; memset (&info, 0, sizeof info); rc = assuan_transact (agent_ctx, "SCD SERIALNO openpgp", NULL, NULL, NULL, NULL, learn_status_cb, &info); if (rc) { switch (gpg_err_code (rc)) { case GPG_ERR_NOT_SUPPORTED: case GPG_ERR_NO_SCDAEMON: write_status_text (STATUS_CARDCTRL, "6"); break; case GPG_ERR_OBJ_TERM_STATE: write_status_text (STATUS_CARDCTRL, "7"); break; default: write_status_text (STATUS_CARDCTRL, "4"); log_info ("selecting openpgp failed: %s\n", gpg_strerror (rc)); break; } } if (!rc && is_status_enabled () && info.serialno) { char *buf; buf = xasprintf ("3 %s", info.serialno); write_status_text (STATUS_CARDCTRL, buf); xfree (buf); } agent_release_card_info (&info); if (!rc) did_early_card_test = 1; } return rc; } /* Return a new malloced string by unescaping the string S. Escaping is percent escaping and '+'/space mapping. A binary nul will silently be replaced by a 0xFF. Function returns NULL to indicate an out of memory status. */ static char * unescape_status_string (const unsigned char *s) { return percent_plus_unescape (s, 0xff); } /* Take a 20 byte hexencoded string and put it into the the provided 20 byte buffer FPR in binary format. */ static int unhexify_fpr (const char *hexstr, unsigned char *fpr) { const char *s; int n; for (s=hexstr, n=0; hexdigitp (s); s++, n++) ; if (*s || (n != 40)) return 0; /* no fingerprint (invalid or wrong length). */ for (s=hexstr, n=0; *s; s += 2, n++) fpr[n] = xtoi_2 (s); return 1; /* okay */ } /* Take the serial number from LINE and return it verbatim in a newly allocated string. We make sure that only hex characters are returned. */ static char * store_serialno (const char *line) { const char *s; char *p; for (s=line; hexdigitp (s); s++) ; p = xtrymalloc (s + 1 - line); if (p) { memcpy (p, line, s-line); p[s-line] = 0; } return p; } /* This is a dummy data line callback. */ static gpg_error_t dummy_data_cb (void *opaque, const void *buffer, size_t length) { (void)opaque; (void)buffer; (void)length; return 0; } /* A simple callback used to return the serialnumber of a card. */ static gpg_error_t get_serialno_cb (void *opaque, const char *line) { char **serialno = opaque; const char *keyword = line; const char *s; int keywordlen, n; for (keywordlen=0; *line && !spacep (line); line++, keywordlen++) ; while (spacep (line)) line++; if (keywordlen == 8 && !memcmp (keyword, "SERIALNO", keywordlen)) { if (*serialno) return gpg_error (GPG_ERR_CONFLICT); /* Unexpected status line. */ for (n=0,s=line; hexdigitp (s); s++, n++) ; if (!n || (n&1)|| !(spacep (s) || !*s) ) return gpg_error (GPG_ERR_ASS_PARAMETER); *serialno = xtrymalloc (n+1); if (!*serialno) return out_of_core (); memcpy (*serialno, line, n); (*serialno)[n] = 0; } return 0; } /* Release the card info structure INFO. */ void agent_release_card_info (struct agent_card_info_s *info) { int i; if (!info) return; xfree (info->serialno); info->serialno = NULL; xfree (info->apptype); info->apptype = NULL; xfree (info->disp_name); info->disp_name = NULL; xfree (info->disp_lang); info->disp_lang = NULL; xfree (info->pubkey_url); info->pubkey_url = NULL; xfree (info->login_data); info->login_data = NULL; info->cafpr1valid = info->cafpr2valid = info->cafpr3valid = 0; info->fpr1valid = info->fpr2valid = info->fpr3valid = 0; for (i=0; i < DIM(info->private_do); i++) { xfree (info->private_do[i]); info->private_do[i] = NULL; } } static gpg_error_t learn_status_cb (void *opaque, const char *line) { struct agent_card_info_s *parm = opaque; const char *keyword = line; int keywordlen; int i; for (keywordlen=0; *line && !spacep (line); line++, keywordlen++) ; while (spacep (line)) line++; if (keywordlen == 8 && !memcmp (keyword, "SERIALNO", keywordlen)) { xfree (parm->serialno); parm->serialno = store_serialno (line); parm->is_v2 = (strlen (parm->serialno) >= 16 && xtoi_2 (parm->serialno+12) >= 2 ); } else if (keywordlen == 7 && !memcmp (keyword, "APPTYPE", keywordlen)) { xfree (parm->apptype); parm->apptype = unescape_status_string (line); } else if (keywordlen == 9 && !memcmp (keyword, "DISP-NAME", keywordlen)) { xfree (parm->disp_name); parm->disp_name = unescape_status_string (line); } else if (keywordlen == 9 && !memcmp (keyword, "DISP-LANG", keywordlen)) { xfree (parm->disp_lang); parm->disp_lang = unescape_status_string (line); } else if (keywordlen == 8 && !memcmp (keyword, "DISP-SEX", keywordlen)) { parm->disp_sex = *line == '1'? 1 : *line == '2' ? 2: 0; } else if (keywordlen == 10 && !memcmp (keyword, "PUBKEY-URL", keywordlen)) { xfree (parm->pubkey_url); parm->pubkey_url = unescape_status_string (line); } else if (keywordlen == 10 && !memcmp (keyword, "LOGIN-DATA", keywordlen)) { xfree (parm->login_data); parm->login_data = unescape_status_string (line); } else if (keywordlen == 11 && !memcmp (keyword, "SIG-COUNTER", keywordlen)) { parm->sig_counter = strtoul (line, NULL, 0); } else if (keywordlen == 10 && !memcmp (keyword, "CHV-STATUS", keywordlen)) { char *p, *buf; buf = p = unescape_status_string (line); if (buf) { while (spacep (p)) p++; parm->chv1_cached = atoi (p); while (*p && !spacep (p)) p++; while (spacep (p)) p++; for (i=0; *p && i < 3; i++) { parm->chvmaxlen[i] = atoi (p); while (*p && !spacep (p)) p++; while (spacep (p)) p++; } for (i=0; *p && i < 3; i++) { parm->chvretry[i] = atoi (p); while (*p && !spacep (p)) p++; while (spacep (p)) p++; } xfree (buf); } } else if (keywordlen == 6 && !memcmp (keyword, "EXTCAP", keywordlen)) { char *p, *p2, *buf; int abool; buf = p = unescape_status_string (line); if (buf) { for (p = strtok (buf, " "); p; p = strtok (NULL, " ")) { p2 = strchr (p, '='); if (p2) { *p2++ = 0; abool = (*p2 == '1'); if (!strcmp (p, "ki")) parm->extcap.ki = abool; else if (!strcmp (p, "aac")) parm->extcap.aac = abool; else if (!strcmp (p, "si")) parm->status_indicator = strtoul (p2, NULL, 10); } } xfree (buf); } } else if (keywordlen == 7 && !memcmp (keyword, "KEY-FPR", keywordlen)) { int no = atoi (line); while (*line && !spacep (line)) line++; while (spacep (line)) line++; if (no == 1) parm->fpr1valid = unhexify_fpr (line, parm->fpr1); else if (no == 2) parm->fpr2valid = unhexify_fpr (line, parm->fpr2); else if (no == 3) parm->fpr3valid = unhexify_fpr (line, parm->fpr3); } else if (keywordlen == 8 && !memcmp (keyword, "KEY-TIME", keywordlen)) { int no = atoi (line); while (* line && !spacep (line)) line++; while (spacep (line)) line++; if (no == 1) parm->fpr1time = strtoul (line, NULL, 10); else if (no == 2) parm->fpr2time = strtoul (line, NULL, 10); else if (no == 3) parm->fpr3time = strtoul (line, NULL, 10); } else if (keywordlen == 6 && !memcmp (keyword, "CA-FPR", keywordlen)) { int no = atoi (line); while (*line && !spacep (line)) line++; while (spacep (line)) line++; if (no == 1) parm->cafpr1valid = unhexify_fpr (line, parm->cafpr1); else if (no == 2) parm->cafpr2valid = unhexify_fpr (line, parm->cafpr2); else if (no == 3) parm->cafpr3valid = unhexify_fpr (line, parm->cafpr3); } else if (keywordlen == 8 && !memcmp (keyword, "KEY-ATTR", keywordlen)) { int keyno, algo, nbits; sscanf (line, "%d %d %d", &keyno, &algo, &nbits); keyno--; if (keyno >= 0 && keyno < DIM (parm->key_attr)) { parm->key_attr[keyno].algo = algo; parm->key_attr[keyno].nbits = nbits; } } else if (keywordlen == 12 && !memcmp (keyword, "PRIVATE-DO-", 11) && strchr("1234", keyword[11])) { int no = keyword[11] - '1'; assert (no >= 0 && no <= 3); xfree (parm->private_do[no]); parm->private_do[no] = unescape_status_string (line); } return 0; } /* Call the scdaemon to learn about a smartcard */ int agent_scd_learn (struct agent_card_info_s *info) { int rc; struct default_inq_parm_s parm; struct agent_card_info_s dummyinfo; if (!info) info = &dummyinfo; memset (info, 0, sizeof *info); memset (&parm, 0, sizeof parm); rc = start_agent (NULL, 1); if (rc) return rc; /* Send the serialno command to initialize the connection. We don't care about the data returned. If the card has already been initialized, this is a very fast command. The main reason we need to do this here is to handle a card removed case so that an "l" command in --card-edit can be used to show ta newly inserted card. We request the openpgp card because that is what we expect. */ rc = assuan_transact (agent_ctx, "SCD SERIALNO openpgp", NULL, NULL, NULL, NULL, NULL, NULL); if (rc) return rc; parm.ctx = agent_ctx; rc = assuan_transact (agent_ctx, "LEARN --sendinfo", dummy_data_cb, NULL, default_inq_cb, &parm, learn_status_cb, info); /* Also try to get the key attributes. */ if (!rc) agent_scd_getattr ("KEY-ATTR", info); if (info == &dummyinfo) agent_release_card_info (info); return rc; } /* Send an APDU to the current card. On success the status word is stored at R_SW. With HEXAPDU being NULL only a RESET command is send to scd. With HEXAPDU being the string "undefined" the command "SERIALNO undefined" is send to scd. */ gpg_error_t agent_scd_apdu (const char *hexapdu, unsigned int *r_sw) { gpg_error_t err; /* Start the agent but not with the card flag so that we do not autoselect the openpgp application. */ err = start_agent (NULL, 0); if (err) return err; if (!hexapdu) { err = assuan_transact (agent_ctx, "SCD RESET", NULL, NULL, NULL, NULL, NULL, NULL); } else if (!strcmp (hexapdu, "undefined")) { err = assuan_transact (agent_ctx, "SCD SERIALNO undefined", NULL, NULL, NULL, NULL, NULL, NULL); } else { char line[ASSUAN_LINELENGTH]; membuf_t mb; unsigned char *data; size_t datalen; init_membuf (&mb, 256); snprintf (line, DIM(line)-1, "SCD APDU %s", hexapdu); err = assuan_transact (agent_ctx, line, membuf_data_cb, &mb, NULL, NULL, NULL, NULL); if (!err) { data = get_membuf (&mb, &datalen); if (!data) err = gpg_error_from_syserror (); else if (datalen < 2) /* Ooops */ err = gpg_error (GPG_ERR_CARD); else { - *r_sw = (data[datalen-2] << 8) | data[datalen-1]; + *r_sw = buf16_to_uint (data+datalen-2); } xfree (data); } } return err; } int agent_keytocard (const char *hexgrip, int keyno, int force, const char *serialno, const char *timestamp) { int rc; char line[ASSUAN_LINELENGTH]; struct default_inq_parm_s parm; memset (&parm, 0, sizeof parm); parm.ctx = agent_ctx; snprintf (line, DIM(line)-1, "KEYTOCARD %s%s %s OPENPGP.%d %s", force?"--force ": "", hexgrip, serialno, keyno, timestamp); line[DIM(line)-1] = 0; rc = start_agent (NULL, 1); if (rc) return rc; rc = assuan_transact (agent_ctx, line, NULL, NULL, default_inq_cb, &parm, NULL, NULL); if (rc) return rc; return rc; } /* Call the agent to retrieve a data object. This function returns the data in the same structure as used by the learn command. It is allowed to update such a structure using this commmand. */ int agent_scd_getattr (const char *name, struct agent_card_info_s *info) { int rc; char line[ASSUAN_LINELENGTH]; struct default_inq_parm_s parm; memset (&parm, 0, sizeof parm); if (!*name) return gpg_error (GPG_ERR_INV_VALUE); /* We assume that NAME does not need escaping. */ if (12 + strlen (name) > DIM(line)-1) return gpg_error (GPG_ERR_TOO_LARGE); stpcpy (stpcpy (line, "SCD GETATTR "), name); rc = start_agent (NULL, 1); if (rc) return rc; parm.ctx = agent_ctx; rc = assuan_transact (agent_ctx, line, NULL, NULL, default_inq_cb, &parm, learn_status_cb, info); return rc; } /* Send an setattr command to the SCdaemon. SERIALNO is not actually used here but required by gpg 1.4's implementation of this code in cardglue.c. */ int agent_scd_setattr (const char *name, const unsigned char *value, size_t valuelen, const char *serialno) { int rc; char line[ASSUAN_LINELENGTH]; char *p; struct default_inq_parm_s parm; memset (&parm, 0, sizeof parm); (void)serialno; if (!*name || !valuelen) return gpg_error (GPG_ERR_INV_VALUE); /* We assume that NAME does not need escaping. */ if (12 + strlen (name) > DIM(line)-1) return gpg_error (GPG_ERR_TOO_LARGE); p = stpcpy (stpcpy (line, "SCD SETATTR "), name); *p++ = ' '; for (; valuelen; value++, valuelen--) { if (p >= line + DIM(line)-5 ) return gpg_error (GPG_ERR_TOO_LARGE); if (*value < ' ' || *value == '+' || *value == '%') { sprintf (p, "%%%02X", *value); p += 3; } else if (*value == ' ') *p++ = '+'; else *p++ = *value; } *p = 0; rc = start_agent (NULL, 1); if (!rc) { parm.ctx = agent_ctx; rc = assuan_transact (agent_ctx, line, NULL, NULL, default_inq_cb, &parm, NULL, NULL); } status_sc_op_failure (rc); return rc; } /* Handle a CERTDATA inquiry. Note, we only send the data, assuan_transact takes care of flushing and writing the END command. */ static gpg_error_t inq_writecert_parms (void *opaque, const char *line) { int rc; struct writecert_parm_s *parm = opaque; if (has_leading_keyword (line, "CERTDATA")) { rc = assuan_send_data (parm->dflt->ctx, parm->certdata, parm->certdatalen); } else rc = default_inq_cb (parm->dflt, line); return rc; } /* Send a WRITECERT command to the SCdaemon. */ int agent_scd_writecert (const char *certidstr, const unsigned char *certdata, size_t certdatalen) { int rc; char line[ASSUAN_LINELENGTH]; struct writecert_parm_s parms; struct default_inq_parm_s dfltparm; memset (&dfltparm, 0, sizeof dfltparm); rc = start_agent (NULL, 1); if (rc) return rc; memset (&parms, 0, sizeof parms); snprintf (line, DIM(line)-1, "SCD WRITECERT %s", certidstr); line[DIM(line)-1] = 0; dfltparm.ctx = agent_ctx; parms.dflt = &dfltparm; parms.certdata = certdata; parms.certdatalen = certdatalen; rc = assuan_transact (agent_ctx, line, NULL, NULL, inq_writecert_parms, &parms, NULL, NULL); return rc; } /* Handle a KEYDATA inquiry. Note, we only send the data, assuan_transact takes care of flushing and writing the end */ static gpg_error_t inq_writekey_parms (void *opaque, const char *line) { int rc; struct writekey_parm_s *parm = opaque; if (has_leading_keyword (line, "KEYDATA")) { rc = assuan_send_data (parm->dflt->ctx, parm->keydata, parm->keydatalen); } else rc = default_inq_cb (parm->dflt, line); return rc; } /* Send a WRITEKEY command to the SCdaemon. */ int agent_scd_writekey (int keyno, const char *serialno, const unsigned char *keydata, size_t keydatalen) { int rc; char line[ASSUAN_LINELENGTH]; struct writekey_parm_s parms; struct default_inq_parm_s dfltparm; memset (&dfltparm, 0, sizeof dfltparm); (void)serialno; rc = start_agent (NULL, 1); if (rc) return rc; memset (&parms, 0, sizeof parms); snprintf (line, DIM(line)-1, "SCD WRITEKEY --force OPENPGP.%d", keyno); line[DIM(line)-1] = 0; dfltparm.ctx = agent_ctx; parms.dflt = &dfltparm; parms.keydata = keydata; parms.keydatalen = keydatalen; rc = assuan_transact (agent_ctx, line, NULL, NULL, inq_writekey_parms, &parms, NULL, NULL); status_sc_op_failure (rc); return rc; } static gpg_error_t scd_genkey_cb_append_savedbytes (struct scd_genkey_parm_s *parm, const char *line) { gpg_error_t err = 0; char *p; if (!parm->savedbytes) { parm->savedbytes = xtrystrdup (line); if (!parm->savedbytes) err = gpg_error_from_syserror (); } else { p = xtrymalloc (strlen (parm->savedbytes) + strlen (line) + 1); if (!p) err = gpg_error_from_syserror (); else { strcpy (stpcpy (p, parm->savedbytes), line); xfree (parm->savedbytes); parm->savedbytes = p; } } return err; } /* Status callback for the SCD GENKEY command. */ static gpg_error_t scd_genkey_cb (void *opaque, const char *line) { struct scd_genkey_parm_s *parm = opaque; const char *keyword = line; int keywordlen; gpg_error_t rc = 0; for (keywordlen=0; *line && !spacep (line); line++, keywordlen++) ; while (spacep (line)) line++; if (keywordlen == 7 && !memcmp (keyword, "KEY-FPR", keywordlen)) { parm->cgk->fprvalid = unhexify_fpr (line, parm->cgk->fpr); } else if (keywordlen == 8 && !memcmp (keyword, "KEY-DATA", keywordlen)) { gcry_mpi_t a; const char *name = line; while (*line && !spacep (line)) line++; while (spacep (line)) line++; if (*name == '-' && spacep (name+1)) rc = scd_genkey_cb_append_savedbytes (parm, line); else { if (parm->savedbytes) { rc = scd_genkey_cb_append_savedbytes (parm, line); if (!rc) rc = gcry_mpi_scan (&a, GCRYMPI_FMT_HEX, parm->savedbytes, 0, NULL); } else rc = gcry_mpi_scan (&a, GCRYMPI_FMT_HEX, line, 0, NULL); if (rc) log_error ("error parsing received key data: %s\n", gpg_strerror (rc)); else if (*name == 'n' && spacep (name+1)) parm->cgk->n = a; else if (*name == 'e' && spacep (name+1)) parm->cgk->e = a; else { log_info ("unknown parameter name in received key data\n"); gcry_mpi_release (a); rc = gpg_error (GPG_ERR_INV_PARAMETER); } xfree (parm->savedbytes); parm->savedbytes = NULL; } } else if (keywordlen == 14 && !memcmp (keyword,"KEY-CREATED-AT", keywordlen)) { parm->cgk->created_at = (u32)strtoul (line, NULL, 10); } else if (keywordlen == 8 && !memcmp (keyword, "PROGRESS", keywordlen)) { write_status_text (STATUS_PROGRESS, line); } return rc; } /* Send a GENKEY command to the SCdaemon. SERIALNO is not used in this implementation. If CREATEDATE is not 0, it will be passed to SCDAEMON so that the key is created with this timestamp. INFO will receive information about the generated key. */ int agent_scd_genkey (struct agent_card_genkey_s *info, int keyno, int force, const char *serialno, u32 createtime) { int rc; char line[ASSUAN_LINELENGTH]; gnupg_isotime_t tbuf; struct scd_genkey_parm_s parms; struct default_inq_parm_s dfltparm; memset (&dfltparm, 0, sizeof dfltparm); (void)serialno; memset (&parms, 0, sizeof parms); parms.cgk = info; rc = start_agent (NULL, 1); if (rc) return rc; if (createtime) epoch2isotime (tbuf, createtime); else *tbuf = 0; snprintf (line, DIM(line)-1, "SCD GENKEY %s%s %s %d", *tbuf? "--timestamp=":"", tbuf, force? "--force":"", keyno); line[DIM(line)-1] = 0; dfltparm.ctx = agent_ctx; memset (info, 0, sizeof *info); rc = assuan_transact (agent_ctx, line, NULL, NULL, default_inq_cb, &dfltparm, scd_genkey_cb, &parms); xfree (parms.savedbytes); status_sc_op_failure (rc); return rc; } /* Issue an SCD SERIALNO openpgp command and if SERIALNO is not NULL ask the user to insert the requested card. */ gpg_error_t select_openpgp (const char *serialno) { gpg_error_t err; /* Send the serialno command to initialize the connection. Without a given S/N we don't care about the data returned. If the card has already been initialized, this is a very fast command. We request the openpgp card because that is what we expect. Note that an opt.limit_card_insert_tries of 1 means: No tries at all whereas 0 means do not limit the number of tries. Due to the sue of a pinentry prompt with a cancel option we use it here in a boolean sense. */ if (!serialno || opt.limit_card_insert_tries == 1) err = assuan_transact (agent_ctx, "SCD SERIALNO openpgp", NULL, NULL, NULL, NULL, NULL, NULL); else { char *this_sn = NULL; char *desc; int ask; char *want_sn; char *p; want_sn = xtrystrdup (serialno); if (!want_sn) return gpg_error_from_syserror (); p = strchr (want_sn, '/'); if (p) *p = 0; do { ask = 0; err = assuan_transact (agent_ctx, "SCD SERIALNO openpgp", NULL, NULL, NULL, NULL, get_serialno_cb, &this_sn); if (gpg_err_code (err) == GPG_ERR_CARD_NOT_PRESENT) ask = 1; else if (gpg_err_code (err) == GPG_ERR_NOT_SUPPORTED) ask = 2; else if (err) ; else if (this_sn) { if (strcmp (want_sn, this_sn)) ask = 2; } xfree (this_sn); this_sn = NULL; if (ask) { char *formatted = NULL; char *ocodeset = i18n_switchto_utf8 (); if (!strncmp (want_sn, "D27600012401", 12) && strlen (want_sn) == 32 ) formatted = xtryasprintf ("(%.4s) %.8s", want_sn + 16, want_sn + 20); err = 0; desc = xtryasprintf ("%s:\n\n" " \"%s\"", ask == 1 ? _("Please insert the card with serial number") : _("Please remove the current card and " "insert the one with serial number"), formatted? formatted : want_sn); if (!desc) err = gpg_error_from_syserror (); xfree (formatted); i18n_switchback (ocodeset); if (!err) err = gpg_agent_get_confirmation (desc); xfree (desc); } } while (ask && !err); xfree (want_sn); } return err; } /* Send a READCERT command to the SCdaemon. */ int agent_scd_readcert (const char *certidstr, void **r_buf, size_t *r_buflen) { int rc; char line[ASSUAN_LINELENGTH]; membuf_t data; size_t len; struct default_inq_parm_s dfltparm; memset (&dfltparm, 0, sizeof dfltparm); *r_buf = NULL; rc = start_agent (NULL, 1); if (rc) return rc; dfltparm.ctx = agent_ctx; init_membuf (&data, 2048); snprintf (line, DIM(line)-1, "SCD READCERT %s", certidstr); line[DIM(line)-1] = 0; rc = assuan_transact (agent_ctx, line, membuf_data_cb, &data, default_inq_cb, &dfltparm, NULL, NULL); if (rc) { xfree (get_membuf (&data, &len)); return rc; } *r_buf = get_membuf (&data, r_buflen); if (!*r_buf) return gpg_error (GPG_ERR_ENOMEM); return 0; } /* Change the PIN of an OpenPGP card or reset the retry counter. CHVNO 1: Change the PIN 2: For v1 cards: Same as 1. For v2 cards: Reset the PIN using the Reset Code. 3: Change the admin PIN 101: Set a new PIN and reset the retry counter 102: For v1 cars: Same as 101. For v2 cards: Set a new Reset Code. SERIALNO is not used. */ int agent_scd_change_pin (int chvno, const char *serialno) { int rc; char line[ASSUAN_LINELENGTH]; const char *reset = ""; struct default_inq_parm_s dfltparm; memset (&dfltparm, 0, sizeof dfltparm); (void)serialno; if (chvno >= 100) reset = "--reset"; chvno %= 100; rc = start_agent (NULL, 1); if (rc) return rc; dfltparm.ctx = agent_ctx; snprintf (line, DIM(line)-1, "SCD PASSWD %s %d", reset, chvno); line[DIM(line)-1] = 0; rc = assuan_transact (agent_ctx, line, NULL, NULL, default_inq_cb, &dfltparm, NULL, NULL); status_sc_op_failure (rc); return rc; } /* Perform a CHECKPIN operation. SERIALNO should be the serial number of the card - optionally followed by the fingerprint; however the fingerprint is ignored here. */ int agent_scd_checkpin (const char *serialno) { int rc; char line[ASSUAN_LINELENGTH]; struct default_inq_parm_s dfltparm; memset (&dfltparm, 0, sizeof dfltparm); rc = start_agent (NULL, 1); if (rc) return rc; dfltparm.ctx = agent_ctx; snprintf (line, DIM(line)-1, "SCD CHECKPIN %s", serialno); line[DIM(line)-1] = 0; rc = assuan_transact (agent_ctx, line, NULL, NULL, default_inq_cb, &dfltparm, NULL, NULL); status_sc_op_failure (rc); return rc; } /* Dummy function, only used by the gpg 1.4 implementation. */ void agent_clear_pin_cache (const char *sn) { (void)sn; } /* Note: All strings shall be UTF-8. On success the caller needs to free the string stored at R_PASSPHRASE. On error NULL will be stored at R_PASSPHRASE and an appropriate fpf error code returned. */ gpg_error_t agent_get_passphrase (const char *cache_id, const char *err_msg, const char *prompt, const char *desc_msg, int repeat, int check, char **r_passphrase) { int rc; char line[ASSUAN_LINELENGTH]; char *arg1 = NULL; char *arg2 = NULL; char *arg3 = NULL; char *arg4 = NULL; membuf_t data; struct default_inq_parm_s dfltparm; memset (&dfltparm, 0, sizeof dfltparm); *r_passphrase = NULL; rc = start_agent (NULL, 0); if (rc) return rc; dfltparm.ctx = agent_ctx; /* Check that the gpg-agent understands the repeat option. */ if (assuan_transact (agent_ctx, "GETINFO cmd_has_option GET_PASSPHRASE repeat", NULL, NULL, NULL, NULL, NULL, NULL)) return gpg_error (GPG_ERR_NOT_SUPPORTED); if (cache_id && *cache_id) if (!(arg1 = percent_plus_escape (cache_id))) goto no_mem; if (err_msg && *err_msg) if (!(arg2 = percent_plus_escape (err_msg))) goto no_mem; if (prompt && *prompt) if (!(arg3 = percent_plus_escape (prompt))) goto no_mem; if (desc_msg && *desc_msg) if (!(arg4 = percent_plus_escape (desc_msg))) goto no_mem; snprintf (line, DIM(line)-1, "GET_PASSPHRASE --data --repeat=%d%s -- %s %s %s %s", repeat, check? " --check --qualitybar":"", arg1? arg1:"X", arg2? arg2:"X", arg3? arg3:"X", arg4? arg4:"X"); line[DIM(line)-1] = 0; xfree (arg1); xfree (arg2); xfree (arg3); xfree (arg4); init_membuf_secure (&data, 64); rc = assuan_transact (agent_ctx, line, membuf_data_cb, &data, default_inq_cb, &dfltparm, NULL, NULL); if (rc) xfree (get_membuf (&data, NULL)); else { put_membuf (&data, "", 1); *r_passphrase = get_membuf (&data, NULL); if (!*r_passphrase) rc = gpg_error_from_syserror (); } return rc; no_mem: rc = gpg_error_from_syserror (); xfree (arg1); xfree (arg2); xfree (arg3); xfree (arg4); return rc; } gpg_error_t agent_clear_passphrase (const char *cache_id) { int rc; char line[ASSUAN_LINELENGTH]; struct default_inq_parm_s dfltparm; memset (&dfltparm, 0, sizeof dfltparm); if (!cache_id || !*cache_id) return 0; rc = start_agent (NULL, 0); if (rc) return rc; dfltparm.ctx = agent_ctx; snprintf (line, DIM(line)-1, "CLEAR_PASSPHRASE %s", cache_id); line[DIM(line)-1] = 0; return assuan_transact (agent_ctx, line, NULL, NULL, default_inq_cb, &dfltparm, NULL, NULL); } /* Ask the agent to pop up a confirmation dialog with the text DESC and an okay and cancel button. */ gpg_error_t gpg_agent_get_confirmation (const char *desc) { int rc; char *tmp; char line[ASSUAN_LINELENGTH]; struct default_inq_parm_s dfltparm; memset (&dfltparm, 0, sizeof dfltparm); rc = start_agent (NULL, 0); if (rc) return rc; dfltparm.ctx = agent_ctx; tmp = percent_plus_escape (desc); if (!tmp) return gpg_error_from_syserror (); snprintf (line, DIM(line)-1, "GET_CONFIRMATION %s", tmp); line[DIM(line)-1] = 0; xfree (tmp); rc = assuan_transact (agent_ctx, line, NULL, NULL, default_inq_cb, &dfltparm, NULL, NULL); return rc; } /* Return the S2K iteration count as computed by gpg-agent. */ gpg_error_t agent_get_s2k_count (unsigned long *r_count) { gpg_error_t err; membuf_t data; char *buf; *r_count = 0; err = start_agent (NULL, 0); if (err) return err; init_membuf (&data, 32); err = assuan_transact (agent_ctx, "GETINFO s2k_count", membuf_data_cb, &data, NULL, NULL, NULL, NULL); if (err) xfree (get_membuf (&data, NULL)); else { put_membuf (&data, "", 1); buf = get_membuf (&data, NULL); if (!buf) err = gpg_error_from_syserror (); else { *r_count = strtoul (buf, NULL, 10); xfree (buf); } } return err; } /* Ask the agent whether a secret key for the given public key is available. Returns 0 if available. */ gpg_error_t agent_probe_secret_key (ctrl_t ctrl, PKT_public_key *pk) { gpg_error_t err; char line[ASSUAN_LINELENGTH]; char *hexgrip; err = start_agent (ctrl, 0); if (err) return err; err = hexkeygrip_from_pk (pk, &hexgrip); if (err) return err; snprintf (line, sizeof line, "HAVEKEY %s", hexgrip); xfree (hexgrip); err = assuan_transact (agent_ctx, line, NULL, NULL, NULL, NULL, NULL, NULL); return err; } /* Ask the agent whether a secret key is available for any of the keys (primary or sub) in KEYBLOCK. Returns 0 if available. */ gpg_error_t agent_probe_any_secret_key (ctrl_t ctrl, kbnode_t keyblock) { gpg_error_t err; char line[ASSUAN_LINELENGTH]; char *p; kbnode_t kbctx, node; int nkeys; unsigned char grip[20]; err = start_agent (ctrl, 0); if (err) return err; err = gpg_error (GPG_ERR_NO_SECKEY); /* Just in case no key was found in KEYBLOCK. */ p = stpcpy (line, "HAVEKEY"); for (kbctx=NULL, nkeys=0; (node = walk_kbnode (keyblock, &kbctx, 0)); ) if (node->pkt->pkttype == PKT_PUBLIC_KEY || node->pkt->pkttype == PKT_PUBLIC_SUBKEY || node->pkt->pkttype == PKT_SECRET_KEY || node->pkt->pkttype == PKT_SECRET_SUBKEY) { if (nkeys && ((p - line) + 41) > (ASSUAN_LINELENGTH - 2)) { err = assuan_transact (agent_ctx, line, NULL, NULL, NULL, NULL, NULL, NULL); if (err != gpg_err_code (GPG_ERR_NO_SECKEY)) break; /* Seckey available or unexpected error - ready. */ p = stpcpy (line, "HAVEKEY"); nkeys = 0; } err = keygrip_from_pk (node->pkt->pkt.public_key, grip); if (err) return err; *p++ = ' '; bin2hex (grip, 20, p); p += 40; nkeys++; } if (!err && nkeys) err = assuan_transact (agent_ctx, line, NULL, NULL, NULL, NULL, NULL, NULL); return err; } static gpg_error_t keyinfo_status_cb (void *opaque, const char *line) { char **serialno = opaque; const char *s, *s2; if ((s = has_leading_keyword (line, "KEYINFO ")) && !*serialno) { s = strchr (s, ' '); if (s && s[1] == 'T' && s[2] == ' ' && s[3]) { s += 3; s2 = strchr (s, ' '); if ( s2 > s ) { *serialno = xtrymalloc ((s2 - s)+1); if (*serialno) { memcpy (*serialno, s, s2 - s); (*serialno)[s2 - s] = 0; } } } } return 0; } /* Return the serial number for a secret key. If the returned serial number is NULL, the key is not stored on a smartcard. Caller needs to free R_SERIALNO. */ gpg_error_t agent_get_keyinfo (ctrl_t ctrl, const char *hexkeygrip, char **r_serialno) { gpg_error_t err; char line[ASSUAN_LINELENGTH]; char *serialno = NULL; *r_serialno = NULL; err = start_agent (ctrl, 0); if (err) return err; if (!hexkeygrip || strlen (hexkeygrip) != 40) return gpg_error (GPG_ERR_INV_VALUE); snprintf (line, DIM(line)-1, "KEYINFO %s", hexkeygrip); line[DIM(line)-1] = 0; err = assuan_transact (agent_ctx, line, NULL, NULL, NULL, NULL, keyinfo_status_cb, &serialno); if (!err && serialno) { /* Sanity check for bad characters. */ if (strpbrk (serialno, ":\n\r")) err = GPG_ERR_INV_VALUE; } if (err) xfree (serialno); else *r_serialno = serialno; return err; } /* Status callback for agent_import_key, agent_export_key and agent_genkey. */ static gpg_error_t cache_nonce_status_cb (void *opaque, const char *line) { struct cache_nonce_parm_s *parm = opaque; const char *keyword = line; int keywordlen; for (keywordlen=0; *line && !spacep (line); line++, keywordlen++) ; while (spacep (line)) line++; if (keywordlen == 11 && !memcmp (keyword, "CACHE_NONCE", keywordlen)) { if (parm->cache_nonce_addr) { xfree (*parm->cache_nonce_addr); *parm->cache_nonce_addr = xtrystrdup (line); } } else if (keywordlen == 12 && !memcmp (keyword, "PASSWD_NONCE", keywordlen)) { if (parm->passwd_nonce_addr) { xfree (*parm->passwd_nonce_addr); *parm->passwd_nonce_addr = xtrystrdup (line); } } return 0; } /* Handle a KEYPARMS inquiry. Note, we only send the data, assuan_transact takes care of flushing and writing the end */ static gpg_error_t inq_genkey_parms (void *opaque, const char *line) { struct genkey_parm_s *parm = opaque; gpg_error_t err; if (has_leading_keyword (line, "KEYPARAM")) { err = assuan_send_data (parm->dflt->ctx, parm->keyparms, strlen (parm->keyparms)); } else if (has_leading_keyword (line, "NEWPASSWD") && parm->passphrase) { err = assuan_send_data (parm->dflt->ctx, parm->passphrase, strlen (parm->passphrase)); } else err = default_inq_cb (parm->dflt, line); return err; } /* Call the agent to generate a new key. KEYPARMS is the usual S-expression giving the parameters of the key. gpg-agent passes it gcry_pk_genkey. If NO_PROTECTION is true the agent is advised not to protect the generated key. If NO_PROTECTION is not set and PASSPHRASE is not NULL the agent is requested to protect the key with that passphrase instead of asking for one. */ gpg_error_t agent_genkey (ctrl_t ctrl, char **cache_nonce_addr, const char *keyparms, int no_protection, const char *passphrase, gcry_sexp_t *r_pubkey) { gpg_error_t err; struct genkey_parm_s gk_parm; struct cache_nonce_parm_s cn_parm; struct default_inq_parm_s dfltparm; membuf_t data; size_t len; unsigned char *buf; char line[ASSUAN_LINELENGTH]; memset (&dfltparm, 0, sizeof dfltparm); dfltparm.ctrl = ctrl; *r_pubkey = NULL; err = start_agent (ctrl, 0); if (err) return err; dfltparm.ctx = agent_ctx; err = assuan_transact (agent_ctx, "RESET", NULL, NULL, NULL, NULL, NULL, NULL); if (err) return err; init_membuf (&data, 1024); gk_parm.dflt = &dfltparm; gk_parm.keyparms = keyparms; gk_parm.passphrase = passphrase; snprintf (line, sizeof line, "GENKEY%s%s%s", no_protection? " --no-protection" : passphrase ? " --inq-passwd" : /* */ "", cache_nonce_addr && *cache_nonce_addr? " ":"", cache_nonce_addr && *cache_nonce_addr? *cache_nonce_addr:""); cn_parm.cache_nonce_addr = cache_nonce_addr; cn_parm.passwd_nonce_addr = NULL; err = assuan_transact (agent_ctx, line, membuf_data_cb, &data, inq_genkey_parms, &gk_parm, cache_nonce_status_cb, &cn_parm); if (err) { xfree (get_membuf (&data, &len)); return err; } buf = get_membuf (&data, &len); if (!buf) err = gpg_error_from_syserror (); else { err = gcry_sexp_sscan (r_pubkey, NULL, buf, len); xfree (buf); } return err; } /* Call the agent to read the public key part for a given keygrip. If FROMCARD is true, the key is directly read from the current smartcard. In this case HEXKEYGRIP should be the keyID (e.g. OPENPGP.3). */ gpg_error_t agent_readkey (ctrl_t ctrl, int fromcard, const char *hexkeygrip, unsigned char **r_pubkey) { gpg_error_t err; membuf_t data; size_t len; unsigned char *buf; char line[ASSUAN_LINELENGTH]; struct default_inq_parm_s dfltparm; memset (&dfltparm, 0, sizeof dfltparm); dfltparm.ctrl = ctrl; *r_pubkey = NULL; err = start_agent (ctrl, 0); if (err) return err; dfltparm.ctx = agent_ctx; err = assuan_transact (agent_ctx, "RESET",NULL, NULL, NULL, NULL, NULL, NULL); if (err) return err; snprintf (line, DIM(line)-1, "%sREADKEY %s", fromcard? "SCD ":"", hexkeygrip); init_membuf (&data, 1024); err = assuan_transact (agent_ctx, line, membuf_data_cb, &data, default_inq_cb, &dfltparm, NULL, NULL); if (err) { xfree (get_membuf (&data, &len)); return err; } buf = get_membuf (&data, &len); if (!buf) return gpg_error_from_syserror (); if (!gcry_sexp_canon_len (buf, len, NULL, NULL)) { xfree (buf); return gpg_error (GPG_ERR_INV_SEXP); } *r_pubkey = buf; return 0; } /* Call the agent to do a sign operation using the key identified by the hex string KEYGRIP. DESC is a description of the key to be displayed if the agent needs to ask for the PIN. DIGEST and DIGESTLEN is the hash value to sign and DIGESTALGO the algorithm id used to compute the digest. If CACHE_NONCE is used the agent is advised to first try a passphrase associated with that nonce. */ gpg_error_t agent_pksign (ctrl_t ctrl, const char *cache_nonce, const char *keygrip, const char *desc, u32 *keyid, u32 *mainkeyid, int pubkey_algo, unsigned char *digest, size_t digestlen, int digestalgo, gcry_sexp_t *r_sigval) { gpg_error_t err; char line[ASSUAN_LINELENGTH]; membuf_t data; struct default_inq_parm_s dfltparm; memset (&dfltparm, 0, sizeof dfltparm); dfltparm.ctrl = ctrl; dfltparm.keyinfo.keyid = keyid; dfltparm.keyinfo.mainkeyid = mainkeyid; dfltparm.keyinfo.pubkey_algo = pubkey_algo; *r_sigval = NULL; err = start_agent (ctrl, 0); if (err) return err; dfltparm.ctx = agent_ctx; if (digestlen*2 + 50 > DIM(line)) return gpg_error (GPG_ERR_GENERAL); err = assuan_transact (agent_ctx, "RESET", NULL, NULL, NULL, NULL, NULL, NULL); if (err) return err; snprintf (line, DIM(line)-1, "SIGKEY %s", keygrip); line[DIM(line)-1] = 0; err = assuan_transact (agent_ctx, line, NULL, NULL, NULL, NULL, NULL, NULL); if (err) return err; if (desc) { snprintf (line, DIM(line)-1, "SETKEYDESC %s", desc); line[DIM(line)-1] = 0; err = assuan_transact (agent_ctx, line, NULL, NULL, NULL, NULL, NULL, NULL); if (err) return err; } snprintf (line, sizeof line, "SETHASH %d ", digestalgo); bin2hex (digest, digestlen, line + strlen (line)); err = assuan_transact (agent_ctx, line, NULL, NULL, NULL, NULL, NULL, NULL); if (err) return err; init_membuf (&data, 1024); snprintf (line, sizeof line, "PKSIGN%s%s", cache_nonce? " -- ":"", cache_nonce? cache_nonce:""); err = assuan_transact (agent_ctx, line, membuf_data_cb, &data, default_inq_cb, &dfltparm, NULL, NULL); if (err) xfree (get_membuf (&data, NULL)); else { unsigned char *buf; size_t len; buf = get_membuf (&data, &len); if (!buf) err = gpg_error_from_syserror (); else { err = gcry_sexp_sscan (r_sigval, NULL, buf, len); xfree (buf); } } return err; } /* Handle a CIPHERTEXT inquiry. Note, we only send the data, assuan_transact takes care of flushing and writing the END. */ static gpg_error_t inq_ciphertext_cb (void *opaque, const char *line) { struct cipher_parm_s *parm = opaque; int rc; if (has_leading_keyword (line, "CIPHERTEXT")) { assuan_begin_confidential (parm->ctx); rc = assuan_send_data (parm->dflt->ctx, parm->ciphertext, parm->ciphertextlen); assuan_end_confidential (parm->ctx); } else rc = default_inq_cb (parm->dflt, line); return rc; } /* Check whether there is any padding info from the agent. */ static gpg_error_t padding_info_cb (void *opaque, const char *line) { int *r_padding = opaque; const char *s; if ((s=has_leading_keyword (line, "PADDING"))) { *r_padding = atoi (s); } return 0; } /* Call the agent to do a decrypt operation using the key identified by the hex string KEYGRIP and the input data S_CIPHERTEXT. On the success the decoded value is stored verbatim at R_BUF and its length at R_BUF; the callers needs to release it. KEYID, MAINKEYID and PUBKEY_ALGO are used to construct additional promots or status messages. The padding information is stored at R_PADDING with -1 for not known. */ gpg_error_t agent_pkdecrypt (ctrl_t ctrl, const char *keygrip, const char *desc, u32 *keyid, u32 *mainkeyid, int pubkey_algo, gcry_sexp_t s_ciphertext, unsigned char **r_buf, size_t *r_buflen, int *r_padding) { gpg_error_t err; char line[ASSUAN_LINELENGTH]; membuf_t data; size_t n, len; char *p, *buf, *endp; struct default_inq_parm_s dfltparm; memset (&dfltparm, 0, sizeof dfltparm); dfltparm.ctrl = ctrl; dfltparm.keyinfo.keyid = keyid; dfltparm.keyinfo.mainkeyid = mainkeyid; dfltparm.keyinfo.pubkey_algo = pubkey_algo; if (!keygrip || strlen(keygrip) != 40 || !s_ciphertext || !r_buf || !r_buflen || !r_padding) return gpg_error (GPG_ERR_INV_VALUE); *r_buf = NULL; *r_padding = -1; err = start_agent (ctrl, 0); if (err) return err; dfltparm.ctx = agent_ctx; err = assuan_transact (agent_ctx, "RESET", NULL, NULL, NULL, NULL, NULL, NULL); if (err) return err; snprintf (line, sizeof line, "SETKEY %s", keygrip); err = assuan_transact (agent_ctx, line, NULL, NULL, NULL, NULL, NULL, NULL); if (err) return err; if (desc) { snprintf (line, DIM(line)-1, "SETKEYDESC %s", desc); line[DIM(line)-1] = 0; err = assuan_transact (agent_ctx, line, NULL, NULL, NULL, NULL, NULL, NULL); if (err) return err; } init_membuf_secure (&data, 1024); { struct cipher_parm_s parm; parm.dflt = &dfltparm; parm.ctx = agent_ctx; err = make_canon_sexp (s_ciphertext, &parm.ciphertext, &parm.ciphertextlen); if (err) return err; err = assuan_transact (agent_ctx, "PKDECRYPT", membuf_data_cb, &data, inq_ciphertext_cb, &parm, padding_info_cb, r_padding); xfree (parm.ciphertext); } if (err) { xfree (get_membuf (&data, &len)); return err; } put_membuf (&data, "", 1); /* Make sure it is 0 terminated. */ buf = get_membuf (&data, &len); if (!buf) return gpg_error_from_syserror (); assert (len); /* (we forced Nul termination.) */ if (*buf != '(') { xfree (buf); return gpg_error (GPG_ERR_INV_SEXP); } if (len < 13 || memcmp (buf, "(5:value", 8) ) /* "(5:valueN:D)\0" */ { xfree (buf); return gpg_error (GPG_ERR_INV_SEXP); } len -= 10; /* Count only the data of the second part. */ p = buf + 8; /* Skip leading parenthesis and the value tag. */ n = strtoul (p, &endp, 10); if (!n || *endp != ':') { xfree (buf); return gpg_error (GPG_ERR_INV_SEXP); } endp++; if (endp-p+n > len) { xfree (buf); return gpg_error (GPG_ERR_INV_SEXP); /* Oops: Inconsistent S-Exp. */ } memmove (buf, endp, n); *r_buflen = n; *r_buf = buf; return 0; } /* Retrieve a key encryption key from the agent. With FOREXPORT true the key shall be used for export, with false for import. On success the new key is stored at R_KEY and its length at R_KEKLEN. */ gpg_error_t agent_keywrap_key (ctrl_t ctrl, int forexport, void **r_kek, size_t *r_keklen) { gpg_error_t err; membuf_t data; size_t len; unsigned char *buf; char line[ASSUAN_LINELENGTH]; struct default_inq_parm_s dfltparm; memset (&dfltparm, 0, sizeof dfltparm); dfltparm.ctrl = ctrl; *r_kek = NULL; err = start_agent (ctrl, 0); if (err) return err; dfltparm.ctx = agent_ctx; snprintf (line, DIM(line)-1, "KEYWRAP_KEY %s", forexport? "--export":"--import"); init_membuf_secure (&data, 64); err = assuan_transact (agent_ctx, line, membuf_data_cb, &data, default_inq_cb, &dfltparm, NULL, NULL); if (err) { xfree (get_membuf (&data, &len)); return err; } buf = get_membuf (&data, &len); if (!buf) return gpg_error_from_syserror (); *r_kek = buf; *r_keklen = len; return 0; } /* Handle the inquiry for an IMPORT_KEY command. */ static gpg_error_t inq_import_key_parms (void *opaque, const char *line) { struct import_key_parm_s *parm = opaque; gpg_error_t err; if (has_leading_keyword (line, "KEYDATA")) { err = assuan_send_data (parm->dflt->ctx, parm->key, parm->keylen); } else err = default_inq_cb (parm->dflt, line); return err; } /* Call the agent to import a key into the agent. */ gpg_error_t agent_import_key (ctrl_t ctrl, const char *desc, char **cache_nonce_addr, const void *key, size_t keylen, int unattended) { gpg_error_t err; struct import_key_parm_s parm; struct cache_nonce_parm_s cn_parm; char line[ASSUAN_LINELENGTH]; struct default_inq_parm_s dfltparm; memset (&dfltparm, 0, sizeof dfltparm); dfltparm.ctrl = ctrl; err = start_agent (ctrl, 0); if (err) return err; dfltparm.ctx = agent_ctx; if (desc) { snprintf (line, DIM(line)-1, "SETKEYDESC %s", desc); line[DIM(line)-1] = 0; err = assuan_transact (agent_ctx, line, NULL, NULL, NULL, NULL, NULL, NULL); if (err) return err; } parm.dflt = &dfltparm; parm.key = key; parm.keylen = keylen; snprintf (line, sizeof line, "IMPORT_KEY%s%s%s", unattended? " --unattended":"", cache_nonce_addr && *cache_nonce_addr? " ":"", cache_nonce_addr && *cache_nonce_addr? *cache_nonce_addr:""); cn_parm.cache_nonce_addr = cache_nonce_addr; cn_parm.passwd_nonce_addr = NULL; err = assuan_transact (agent_ctx, line, NULL, NULL, inq_import_key_parms, &parm, cache_nonce_status_cb, &cn_parm); return err; } /* Receive a secret key from the agent. HEXKEYGRIP is the hexified keygrip, DESC a prompt to be displayed with the agent's passphrase question (needs to be plus+percent escaped). If CACHE_NONCE_ADDR is not NULL the agent is advised to first try a passphrase associated with that nonce. On success the key is stored as a canonical S-expression at R_RESULT and R_RESULTLEN. */ gpg_error_t agent_export_key (ctrl_t ctrl, const char *hexkeygrip, const char *desc, char **cache_nonce_addr, unsigned char **r_result, size_t *r_resultlen) { gpg_error_t err; struct cache_nonce_parm_s cn_parm; membuf_t data; size_t len; unsigned char *buf; char line[ASSUAN_LINELENGTH]; struct default_inq_parm_s dfltparm; memset (&dfltparm, 0, sizeof dfltparm); dfltparm.ctrl = ctrl; *r_result = NULL; err = start_agent (ctrl, 0); if (err) return err; dfltparm.ctx = agent_ctx; if (desc) { snprintf (line, DIM(line)-1, "SETKEYDESC %s", desc); err = assuan_transact (agent_ctx, line, NULL, NULL, NULL, NULL, NULL, NULL); if (err) return err; } snprintf (line, DIM(line)-1, "EXPORT_KEY --openpgp %s%s %s", cache_nonce_addr && *cache_nonce_addr? "--cache-nonce=":"", cache_nonce_addr && *cache_nonce_addr? *cache_nonce_addr:"", hexkeygrip); init_membuf_secure (&data, 1024); cn_parm.cache_nonce_addr = cache_nonce_addr; cn_parm.passwd_nonce_addr = NULL; err = assuan_transact (agent_ctx, line, membuf_data_cb, &data, default_inq_cb, &dfltparm, cache_nonce_status_cb, &cn_parm); if (err) { xfree (get_membuf (&data, &len)); return err; } buf = get_membuf (&data, &len); if (!buf) return gpg_error_from_syserror (); *r_result = buf; *r_resultlen = len; return 0; } /* Ask the agent to delete the key identified by HEXKEYGRIP. If DESC is not NULL, display DESC instead of the default description message. */ gpg_error_t agent_delete_key (ctrl_t ctrl, const char *hexkeygrip, const char *desc) { gpg_error_t err; char line[ASSUAN_LINELENGTH]; struct default_inq_parm_s dfltparm; memset (&dfltparm, 0, sizeof dfltparm); dfltparm.ctrl = ctrl; err = start_agent (ctrl, 0); if (err) return err; if (!hexkeygrip || strlen (hexkeygrip) != 40) return gpg_error (GPG_ERR_INV_VALUE); if (desc) { snprintf (line, DIM(line)-1, "SETKEYDESC %s", desc); err = assuan_transact (agent_ctx, line, NULL, NULL, NULL, NULL, NULL, NULL); if (err) return err; } snprintf (line, DIM(line)-1, "DELETE_KEY %s", hexkeygrip); err = assuan_transact (agent_ctx, line, NULL, NULL, default_inq_cb, &dfltparm, NULL, NULL); return err; } /* Ask the agent to change the passphrase of the key identified by HEXKEYGRIP. If DESC is not NULL, display DESC instead of the default description message. If CACHE_NONCE_ADDR is not NULL the agent is advised to first try a passphrase associated with that nonce. If PASSWD_NONCE_ADDR is not NULL the agent will try to use the passphrase associated with that nonce. */ gpg_error_t agent_passwd (ctrl_t ctrl, const char *hexkeygrip, const char *desc, char **cache_nonce_addr, char **passwd_nonce_addr) { gpg_error_t err; struct cache_nonce_parm_s cn_parm; char line[ASSUAN_LINELENGTH]; struct default_inq_parm_s dfltparm; memset (&dfltparm, 0, sizeof dfltparm); dfltparm.ctrl = ctrl; err = start_agent (ctrl, 0); if (err) return err; dfltparm.ctx = agent_ctx; if (!hexkeygrip || strlen (hexkeygrip) != 40) return gpg_error (GPG_ERR_INV_VALUE); if (desc) { snprintf (line, DIM(line)-1, "SETKEYDESC %s", desc); err = assuan_transact (agent_ctx, line, NULL, NULL, NULL, NULL, NULL, NULL); if (err) return err; } snprintf (line, DIM(line)-1, "PASSWD %s%s %s%s %s", cache_nonce_addr && *cache_nonce_addr? "--cache-nonce=":"", cache_nonce_addr && *cache_nonce_addr? *cache_nonce_addr:"", passwd_nonce_addr && *passwd_nonce_addr? "--passwd-nonce=":"", passwd_nonce_addr && *passwd_nonce_addr? *passwd_nonce_addr:"", hexkeygrip); cn_parm.cache_nonce_addr = cache_nonce_addr; cn_parm.passwd_nonce_addr = passwd_nonce_addr; err = assuan_transact (agent_ctx, line, NULL, NULL, default_inq_cb, &dfltparm, cache_nonce_status_cb, &cn_parm); return err; } /* Return the version reported by gpg-agent. */ gpg_error_t agent_get_version (ctrl_t ctrl, char **r_version) { gpg_error_t err; membuf_t data; err = start_agent (ctrl, 0); if (err) return err; init_membuf (&data, 64); err = assuan_transact (agent_ctx, "GETINFO version", membuf_data_cb, &data, NULL, NULL, NULL, NULL); if (err) { xfree (get_membuf (&data, NULL)); *r_version = NULL; } else { put_membuf (&data, "", 1); *r_version = get_membuf (&data, NULL); if (!*r_version) err = gpg_error_from_syserror (); } return err; } diff --git a/g10/getkey.c b/g10/getkey.c index 62d2d3306..30c454b21 100644 --- a/g10/getkey.c +++ b/g10/getkey.c @@ -1,3042 +1,3044 @@ /* getkey.c - Get a key from the database * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, * 2007, 2008, 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 "gpg.h" #include "util.h" #include "packet.h" #include "iobuf.h" #include "keydb.h" #include "options.h" #include "main.h" #include "trustdb.h" #include "i18n.h" #include "keyserver-internal.h" #include "call-agent.h" +#include "host2net.h" + #define MAX_PK_CACHE_ENTRIES PK_UID_CACHE_SIZE #define MAX_UID_CACHE_ENTRIES PK_UID_CACHE_SIZE #if MAX_PK_CACHE_ENTRIES < 2 #error We need the cache for key creation #endif struct getkey_ctx_s { int exact; int want_secret; /* The caller requested only secret keys. */ KBNODE keyblock; KBPOS kbpos; KBNODE found_key; /* Pointer into some keyblock. */ strlist_t extra_list; /* Will be freed when releasing the context. */ int req_usage; int req_algo; KEYDB_HANDLE kr_handle; int not_allocated; int nitems; KEYDB_SEARCH_DESC items[1]; }; #if 0 static struct { int any; int okay_count; int nokey_count; int error_count; } lkup_stats[21]; #endif typedef struct keyid_list { struct keyid_list *next; char fpr[MAX_FINGERPRINT_LEN]; u32 keyid[2]; } *keyid_list_t; #if MAX_PK_CACHE_ENTRIES typedef struct pk_cache_entry { struct pk_cache_entry *next; u32 keyid[2]; PKT_public_key *pk; } *pk_cache_entry_t; static pk_cache_entry_t pk_cache; static int pk_cache_entries; /* Number of entries in pk cache. */ static int pk_cache_disabled; #endif #if MAX_UID_CACHE_ENTRIES < 5 #error we really need the userid cache #endif typedef struct user_id_db { struct user_id_db *next; keyid_list_t keyids; int len; char name[1]; } *user_id_db_t; static user_id_db_t user_id_db; static int uid_cache_entries; /* Number of entries in uid cache. */ static void merge_selfsigs (kbnode_t keyblock); static int lookup (getkey_ctx_t ctx, kbnode_t *ret_keyblock, int want_secret); #if 0 static void print_stats () { int i; for (i = 0; i < DIM (lkup_stats); i++) { if (lkup_stats[i].any) fprintf (stderr, "lookup stats: mode=%-2d ok=%-6d nokey=%-6d err=%-6d\n", i, lkup_stats[i].okay_count, lkup_stats[i].nokey_count, lkup_stats[i].error_count); } } #endif void cache_public_key (PKT_public_key * pk) { #if MAX_PK_CACHE_ENTRIES pk_cache_entry_t ce, ce2; u32 keyid[2]; if (pk_cache_disabled) return; if (pk->flags.dont_cache) return; if (is_ELGAMAL (pk->pubkey_algo) || pk->pubkey_algo == PUBKEY_ALGO_DSA || pk->pubkey_algo == PUBKEY_ALGO_ECDSA || pk->pubkey_algo == PUBKEY_ALGO_EDDSA || pk->pubkey_algo == PUBKEY_ALGO_ECDH || is_RSA (pk->pubkey_algo)) { keyid_from_pk (pk, keyid); } else return; /* Don't know how to get the keyid. */ for (ce = pk_cache; ce; ce = ce->next) if (ce->keyid[0] == keyid[0] && ce->keyid[1] == keyid[1]) { if (DBG_CACHE) log_debug ("cache_public_key: already in cache\n"); return; } if (pk_cache_entries >= MAX_PK_CACHE_ENTRIES) { int n; /* Remove the last 50% of the entries. */ for (ce = pk_cache, n = 0; ce && n < pk_cache_entries/2; n++) ce = ce->next; if (ce != pk_cache && ce->next) { ce2 = ce->next; ce->next = NULL; ce = ce2; for (; ce; ce = ce2) { ce2 = ce->next; free_public_key (ce->pk); xfree (ce); pk_cache_entries--; } } assert (pk_cache_entries < MAX_PK_CACHE_ENTRIES); } pk_cache_entries++; ce = xmalloc (sizeof *ce); ce->next = pk_cache; pk_cache = ce; ce->pk = copy_public_key (NULL, pk); ce->keyid[0] = keyid[0]; ce->keyid[1] = keyid[1]; #endif } /* Return a const utf-8 string with the text "[User ID not found]". This function is required so that we don't need to switch gettext's encoding temporary. */ static const char * user_id_not_found_utf8 (void) { static char *text; if (!text) text = native_to_utf8 (_("[User ID not found]")); return text; } /* Return the user ID from the given keyblock. * We use the primary uid flag which has been set by the merge_selfsigs * function. The returned value is only valid as long as then given * keyblock is not changed. */ static const char * get_primary_uid (KBNODE keyblock, size_t * uidlen) { KBNODE k; const char *s; for (k = keyblock; k; k = k->next) { if (k->pkt->pkttype == PKT_USER_ID && !k->pkt->pkt.user_id->attrib_data && k->pkt->pkt.user_id->is_primary) { *uidlen = k->pkt->pkt.user_id->len; return k->pkt->pkt.user_id->name; } } s = user_id_not_found_utf8 (); *uidlen = strlen (s); return s; } static void release_keyid_list (keyid_list_t k) { while (k) { keyid_list_t k2 = k->next; xfree (k); k = k2; } } /**************** * Store the association of keyid and userid * Feed only public keys to this function. */ static void cache_user_id (KBNODE keyblock) { user_id_db_t r; const char *uid; size_t uidlen; keyid_list_t keyids = NULL; KBNODE k; for (k = keyblock; k; k = k->next) { if (k->pkt->pkttype == PKT_PUBLIC_KEY || k->pkt->pkttype == PKT_PUBLIC_SUBKEY) { keyid_list_t a = xmalloc_clear (sizeof *a); /* Hmmm: For a long list of keyids it might be an advantage * to append the keys. */ fingerprint_from_pk (k->pkt->pkt.public_key, a->fpr, NULL); keyid_from_pk (k->pkt->pkt.public_key, a->keyid); /* First check for duplicates. */ for (r = user_id_db; r; r = r->next) { keyid_list_t b = r->keyids; for (b = r->keyids; b; b = b->next) { if (!memcmp (b->fpr, a->fpr, MAX_FINGERPRINT_LEN)) { if (DBG_CACHE) log_debug ("cache_user_id: already in cache\n"); release_keyid_list (keyids); xfree (a); return; } } } /* Now put it into the cache. */ a->next = keyids; keyids = a; } } if (!keyids) BUG (); /* No key no fun. */ uid = get_primary_uid (keyblock, &uidlen); if (uid_cache_entries >= MAX_UID_CACHE_ENTRIES) { /* fixme: use another algorithm to free some cache slots */ r = user_id_db; user_id_db = r->next; release_keyid_list (r->keyids); xfree (r); uid_cache_entries--; } r = xmalloc (sizeof *r + uidlen - 1); r->keyids = keyids; r->len = uidlen; memcpy (r->name, uid, r->len); r->next = user_id_db; user_id_db = r; uid_cache_entries++; } void getkey_disable_caches () { #if MAX_PK_CACHE_ENTRIES { pk_cache_entry_t ce, ce2; for (ce = pk_cache; ce; ce = ce2) { ce2 = ce->next; free_public_key (ce->pk); xfree (ce); } pk_cache_disabled = 1; pk_cache_entries = 0; pk_cache = NULL; } #endif /* fixme: disable user id cache ? */ } static void pk_from_block (GETKEY_CTX ctx, PKT_public_key * pk, KBNODE keyblock) { KBNODE a = ctx->found_key ? ctx->found_key : keyblock; assert (a->pkt->pkttype == PKT_PUBLIC_KEY || a->pkt->pkttype == PKT_PUBLIC_SUBKEY); copy_public_key (pk, a->pkt->pkt.public_key); } /* Get a public key and store it into the allocated pk can be called * with PK set to NULL to just read it into some internal * structures. */ int get_pubkey (PKT_public_key * pk, u32 * keyid) { int internal = 0; int rc = 0; #if MAX_PK_CACHE_ENTRIES if (pk) { /* Try to get it from the cache. We don't do this when pk is NULL as it does not guarantee that the user IDs are cached. */ pk_cache_entry_t ce; for (ce = pk_cache; ce; ce = ce->next) { if (ce->keyid[0] == keyid[0] && ce->keyid[1] == keyid[1]) { copy_public_key (pk, ce->pk); return 0; } } } #endif /* More init stuff. */ if (!pk) { pk = xmalloc_clear (sizeof *pk); internal++; } /* Do a lookup. */ { struct getkey_ctx_s ctx; KBNODE kb = NULL; memset (&ctx, 0, sizeof ctx); ctx.exact = 1; /* Use the key ID exactly as given. */ ctx.not_allocated = 1; ctx.kr_handle = keydb_new (); ctx.nitems = 1; ctx.items[0].mode = KEYDB_SEARCH_MODE_LONG_KID; ctx.items[0].u.kid[0] = keyid[0]; ctx.items[0].u.kid[1] = keyid[1]; ctx.req_algo = pk->req_algo; ctx.req_usage = pk->req_usage; rc = lookup (&ctx, &kb, 0); if (!rc) { pk_from_block (&ctx, pk, kb); } get_pubkey_end (&ctx); release_kbnode (kb); } if (!rc) goto leave; rc = GPG_ERR_NO_PUBKEY; leave: if (!rc) cache_public_key (pk); if (internal) free_public_key (pk); return rc; } /* Get a public key and store it into the allocated pk. This function differs from get_pubkey() in that it does not do a check of the key to avoid recursion. It should be used only in very certain cases. It will only retrieve primary keys. */ int get_pubkey_fast (PKT_public_key * pk, u32 * keyid) { int rc = 0; KEYDB_HANDLE hd; KBNODE keyblock; u32 pkid[2]; assert (pk); #if MAX_PK_CACHE_ENTRIES { /* Try to get it from the cache */ pk_cache_entry_t ce; for (ce = pk_cache; ce; ce = ce->next) { if (ce->keyid[0] == keyid[0] && ce->keyid[1] == keyid[1]) { if (pk) copy_public_key (pk, ce->pk); return 0; } } } #endif hd = keydb_new (); rc = keydb_search_kid (hd, keyid); if (gpg_err_code (rc) == GPG_ERR_NOT_FOUND) { keydb_release (hd); return GPG_ERR_NO_PUBKEY; } rc = keydb_get_keyblock (hd, &keyblock); keydb_release (hd); if (rc) { log_error ("keydb_get_keyblock failed: %s\n", gpg_strerror (rc)); return GPG_ERR_NO_PUBKEY; } assert (keyblock && keyblock->pkt && (keyblock->pkt->pkttype == PKT_PUBLIC_KEY || keyblock->pkt->pkttype == PKT_PUBLIC_SUBKEY)); keyid_from_pk (keyblock->pkt->pkt.public_key, pkid); if (keyid[0] == pkid[0] && keyid[1] == pkid[1]) copy_public_key (pk, keyblock->pkt->pkt.public_key); else rc = GPG_ERR_NO_PUBKEY; release_kbnode (keyblock); /* Not caching key here since it won't have all of the fields properly set. */ return rc; } KBNODE get_pubkeyblock (u32 * keyid) { struct getkey_ctx_s ctx; int rc = 0; KBNODE keyblock = NULL; memset (&ctx, 0, sizeof ctx); /* No need to set exact here because we want the entire block. */ ctx.not_allocated = 1; ctx.kr_handle = keydb_new (); ctx.nitems = 1; ctx.items[0].mode = KEYDB_SEARCH_MODE_LONG_KID; ctx.items[0].u.kid[0] = keyid[0]; ctx.items[0].u.kid[1] = keyid[1]; rc = lookup (&ctx, &keyblock, 0); get_pubkey_end (&ctx); return rc ? NULL : keyblock; } /* * Get a public key and store it into PK. This functions check that a * corresponding secret key is available. With no secret key it does * not succeeed. */ gpg_error_t get_seckey (PKT_public_key *pk, u32 *keyid) { gpg_error_t err; struct getkey_ctx_s ctx; kbnode_t keyblock = NULL; memset (&ctx, 0, sizeof ctx); ctx.exact = 1; /* Use the key ID exactly as given. */ ctx.not_allocated = 1; ctx.kr_handle = keydb_new (); ctx.nitems = 1; ctx.items[0].mode = KEYDB_SEARCH_MODE_LONG_KID; ctx.items[0].u.kid[0] = keyid[0]; ctx.items[0].u.kid[1] = keyid[1]; ctx.req_algo = pk->req_algo; ctx.req_usage = pk->req_usage; err = lookup (&ctx, &keyblock, 1); if (!err) { pk_from_block (&ctx, pk, keyblock); } get_pubkey_end (&ctx); release_kbnode (keyblock); if (!err) err = agent_probe_secret_key (/*ctrl*/NULL, pk); return err; } static int skip_unusable (void *dummy, u32 * keyid, PKT_user_id * uid) { int unusable = 0; KBNODE keyblock; (void) dummy; keyblock = get_pubkeyblock (keyid); if (!keyblock) { log_error ("error checking usability status of %s\n", keystr (keyid)); goto leave; } /* Is the user ID in question revoked/expired? */ if (uid) { KBNODE node; for (node = keyblock; node; node = node->next) { if (node->pkt->pkttype == PKT_USER_ID) { if (cmp_user_ids (uid, node->pkt->pkt.user_id) == 0 && (node->pkt->pkt.user_id->is_revoked || node->pkt->pkt.user_id->is_expired)) { unusable = 1; break; } } } } if (!unusable) unusable = pk_is_disabled (keyblock->pkt->pkt.public_key); leave: release_kbnode (keyblock); return unusable; } /* Try to get the pubkey by the userid. This function looks for the * first pubkey certificate which has the given name in a user_id. If * PK has the pubkey algo set, the function will only return a pubkey * with that algo. If NAMELIST is NULL, the first key is returned. * The caller should provide storage for the PK or pass NULL if it is * not needed. If RET_KB is not NULL the function stores the entire * keyblock at that address. */ static int key_byname (GETKEY_CTX *retctx, strlist_t namelist, PKT_public_key *pk, int want_secret, int include_unusable, KBNODE * ret_kb, KEYDB_HANDLE * ret_kdbhd) { int rc = 0; int n; strlist_t r; GETKEY_CTX ctx; KBNODE help_kb = NULL; if (retctx) { /* Reset the returned context in case of error. */ assert (!ret_kdbhd); /* Not allowed because the handle is stored in the context. */ *retctx = NULL; } if (ret_kdbhd) *ret_kdbhd = NULL; if (!namelist) { ctx = xmalloc_clear (sizeof *ctx); ctx->nitems = 1; ctx->items[0].mode = KEYDB_SEARCH_MODE_FIRST; if (!include_unusable) ctx->items[0].skipfnc = skip_unusable; } else { /* Build the search context. */ for (n = 0, r = namelist; r; r = r->next) n++; ctx = xmalloc_clear (sizeof *ctx + (n - 1) * sizeof ctx->items); ctx->nitems = n; for (n = 0, r = namelist; r; r = r->next, n++) { gpg_error_t err; err = classify_user_id (r->d, &ctx->items[n], 1); if (ctx->items[n].exact) ctx->exact = 1; if (err) { xfree (ctx); return gpg_err_code (err); /* FIXME: remove gpg_err_code. */ } if (!include_unusable && ctx->items[n].mode != KEYDB_SEARCH_MODE_SHORT_KID && ctx->items[n].mode != KEYDB_SEARCH_MODE_LONG_KID && ctx->items[n].mode != KEYDB_SEARCH_MODE_FPR16 && ctx->items[n].mode != KEYDB_SEARCH_MODE_FPR20 && ctx->items[n].mode != KEYDB_SEARCH_MODE_FPR) ctx->items[n].skipfnc = skip_unusable; } } ctx->want_secret = want_secret; ctx->kr_handle = keydb_new (); if (!ret_kb) ret_kb = &help_kb; if (pk) { ctx->req_algo = pk->req_algo; ctx->req_usage = pk->req_usage; } rc = lookup (ctx, ret_kb, want_secret); if (!rc && pk) { pk_from_block (ctx, pk, *ret_kb); } release_kbnode (help_kb); if (retctx) /* Caller wants the context. */ *retctx = ctx; else { if (ret_kdbhd) { *ret_kdbhd = ctx->kr_handle; ctx->kr_handle = NULL; } get_pubkey_end (ctx); } return rc; } /* Find a public key from NAME and return the keyblock or the key. If ret_kdb is not NULL, the KEYDB handle used to locate this keyblock is returned and the caller is responsible for closing it. If a key was not found (or if local search has been disabled) and NAME is a valid RFC822 mailbox and --auto-key-locate has been enabled, we try to import the key via the online mechanisms defined by --auto-key-locate. */ int get_pubkey_byname (ctrl_t ctrl, GETKEY_CTX * retctx, PKT_public_key * pk, const char *name, KBNODE * ret_keyblock, KEYDB_HANDLE * ret_kdbhd, int include_unusable, int no_akl) { int rc; strlist_t namelist = NULL; struct akl *akl; int is_mbox; int nodefault = 0; int anylocalfirst = 0; if (retctx) *retctx = NULL; is_mbox = is_valid_mailbox (name); /* Check whether the default local search has been disabled. This is the case if either the "nodefault" or the "local" keyword are in the list of auto key locate mechanisms. ANYLOCALFIRST is set if the search order has the local method before any other or if "local" is used first by default. This makes sure that if a RETCTX is used it gets only set if a local search has precedence over the other search methods and only then a followup call to get_pubkey_next shall succeed. */ if (!no_akl) { for (akl = opt.auto_key_locate; akl; akl = akl->next) if (akl->type == AKL_NODEFAULT || akl->type == AKL_LOCAL) { nodefault = 1; break; } for (akl = opt.auto_key_locate; akl; akl = akl->next) if (akl->type != AKL_NODEFAULT) { if (akl->type == AKL_LOCAL) anylocalfirst = 1; break; } } if (!nodefault) anylocalfirst = 1; if (nodefault && is_mbox) { /* Nodefault but a mailbox - let the AKL locate the key. */ rc = GPG_ERR_NO_PUBKEY; } else { add_to_strlist (&namelist, name); rc = key_byname (retctx, namelist, pk, 0, include_unusable, ret_keyblock, ret_kdbhd); } /* If the requested name resembles a valid mailbox and automatic retrieval has been enabled, we try to import the key. */ if (gpg_err_code (rc) == GPG_ERR_NO_PUBKEY && !no_akl && is_mbox) { for (akl = opt.auto_key_locate; akl; akl = akl->next) { unsigned char *fpr = NULL; size_t fpr_len; int did_key_byname = 0; int no_fingerprint = 0; const char *mechanism = "?"; switch (akl->type) { case AKL_NODEFAULT: /* This is a dummy mechanism. */ mechanism = "None"; rc = GPG_ERR_NO_PUBKEY; break; case AKL_LOCAL: mechanism = "Local"; did_key_byname = 1; if (retctx) { get_pubkey_end (*retctx); *retctx = NULL; } add_to_strlist (&namelist, name); rc = key_byname (anylocalfirst ? retctx : NULL, namelist, pk, 0, include_unusable, ret_keyblock, ret_kdbhd); break; case AKL_CERT: mechanism = "DNS CERT"; glo_ctrl.in_auto_key_retrieve++; rc = keyserver_import_cert (ctrl, name, &fpr, &fpr_len); glo_ctrl.in_auto_key_retrieve--; break; case AKL_PKA: mechanism = "PKA"; glo_ctrl.in_auto_key_retrieve++; rc = keyserver_import_pka (ctrl, name, &fpr, &fpr_len); glo_ctrl.in_auto_key_retrieve--; break; case AKL_LDAP: mechanism = "LDAP"; glo_ctrl.in_auto_key_retrieve++; rc = keyserver_import_ldap (ctrl, name, &fpr, &fpr_len); glo_ctrl.in_auto_key_retrieve--; break; case AKL_KEYSERVER: /* Strictly speaking, we don't need to only use a valid mailbox for the getname search, but it helps cut down on the problem of searching for something like "john" and getting a whole lot of keys back. */ if (opt.keyserver) { mechanism = opt.keyserver->uri; glo_ctrl.in_auto_key_retrieve++; rc = keyserver_import_name (ctrl, name, &fpr, &fpr_len, opt.keyserver); glo_ctrl.in_auto_key_retrieve--; } else { mechanism = "Unconfigured keyserver"; rc = GPG_ERR_NO_PUBKEY; } break; case AKL_SPEC: { struct keyserver_spec *keyserver; mechanism = akl->spec->uri; keyserver = keyserver_match (akl->spec); glo_ctrl.in_auto_key_retrieve++; rc = keyserver_import_name (ctrl, name, &fpr, &fpr_len, keyserver); glo_ctrl.in_auto_key_retrieve--; } break; } /* Use the fingerprint of the key that we actually fetched. This helps prevent problems where the key that we fetched doesn't have the same name that we used to fetch it. In the case of CERT and PKA, this is an actual security requirement as the URL might point to a key put in by an attacker. By forcing the use of the fingerprint, we won't use the attacker's key here. */ if (!rc && fpr) { char fpr_string[MAX_FINGERPRINT_LEN * 2 + 1]; assert (fpr_len <= MAX_FINGERPRINT_LEN); free_strlist (namelist); namelist = NULL; bin2hex (fpr, fpr_len, fpr_string); if (opt.verbose) log_info ("auto-key-locate found fingerprint %s\n", fpr_string); add_to_strlist (&namelist, fpr_string); } else if (!rc && !fpr && !did_key_byname) { no_fingerprint = 1; rc = GPG_ERR_NO_PUBKEY; } xfree (fpr); fpr = NULL; if (!rc && !did_key_byname) { if (retctx) { get_pubkey_end (*retctx); *retctx = NULL; } rc = key_byname (anylocalfirst ? retctx : NULL, namelist, pk, 0, include_unusable, ret_keyblock, ret_kdbhd); } if (!rc) { /* Key found. */ log_info (_("automatically retrieved '%s' via %s\n"), name, mechanism); break; } if (gpg_err_code (rc) != GPG_ERR_NO_PUBKEY || opt.verbose || no_fingerprint) log_info (_("error retrieving '%s' via %s: %s\n"), name, mechanism, no_fingerprint ? _("No fingerprint") : gpg_strerror (rc)); } } if (rc && retctx) { get_pubkey_end (*retctx); *retctx = NULL; } if (retctx && *retctx) { assert (!(*retctx)->extra_list); (*retctx)->extra_list = namelist; } else free_strlist (namelist); return rc; } int get_pubkey_bynames (GETKEY_CTX * retctx, PKT_public_key * pk, strlist_t names, KBNODE * ret_keyblock) { return key_byname (retctx, names, pk, 0, 1, ret_keyblock, NULL); } int get_pubkey_next (GETKEY_CTX ctx, PKT_public_key * pk, KBNODE * ret_keyblock) { return gpg_err_code (getkey_next (ctx, pk, ret_keyblock)); } void get_pubkey_end (GETKEY_CTX ctx) { getkey_end (ctx); } /* Search for a key with the given standard fingerprint. In contrast * to get_pubkey_byfprint we assume a right padded fingerprint of the * standard length. PK may be NULL to only put the result into the * internal caches. */ gpg_error_t get_pubkey_byfpr (PKT_public_key *pk, const byte *fpr) { gpg_error_t err; struct getkey_ctx_s ctx; kbnode_t kb = NULL; memset (&ctx, 0, sizeof ctx); ctx.exact = 1; ctx.not_allocated = 1; ctx.kr_handle = keydb_new (); ctx.nitems = 1; ctx.items[0].mode = KEYDB_SEARCH_MODE_FPR; memcpy (ctx.items[0].u.fpr, fpr, MAX_FINGERPRINT_LEN); err = lookup (&ctx, &kb, 0); if (!err && pk) pk_from_block (&ctx, pk, kb); release_kbnode (kb); get_pubkey_end (&ctx); return err; } /* Search for a key with the given fingerprint. * FIXME: * We should replace this with the _byname function. This can be done * by creating a userID conforming to the unified fingerprint style. */ int get_pubkey_byfprint (PKT_public_key * pk, const byte * fprint, size_t fprint_len) { int rc; if (fprint_len == 20 || fprint_len == 16) { struct getkey_ctx_s ctx; KBNODE kb = NULL; memset (&ctx, 0, sizeof ctx); ctx.exact = 1; ctx.not_allocated = 1; ctx.kr_handle = keydb_new (); ctx.nitems = 1; ctx.items[0].mode = fprint_len == 16 ? KEYDB_SEARCH_MODE_FPR16 : KEYDB_SEARCH_MODE_FPR20; memcpy (ctx.items[0].u.fpr, fprint, fprint_len); rc = lookup (&ctx, &kb, 0); if (!rc && pk) pk_from_block (&ctx, pk, kb); release_kbnode (kb); get_pubkey_end (&ctx); } else rc = GPG_ERR_GENERAL; /* Oops */ return rc; } /* Get a public key and store it into the allocated pk. This function differs from get_pubkey_byfprint() in that it does not do a check of the key to avoid recursion. It should be used only in very certain cases. PK may be NULL to check just for the existance of the key. */ int get_pubkey_byfprint_fast (PKT_public_key * pk, const byte * fprint, size_t fprint_len) { int rc = 0; KEYDB_HANDLE hd; KBNODE keyblock; byte fprbuf[MAX_FINGERPRINT_LEN]; int i; for (i = 0; i < MAX_FINGERPRINT_LEN && i < fprint_len; i++) fprbuf[i] = fprint[i]; while (i < MAX_FINGERPRINT_LEN) fprbuf[i++] = 0; hd = keydb_new (); rc = keydb_search_fpr (hd, fprbuf); if (gpg_err_code (rc) == GPG_ERR_NOT_FOUND) { keydb_release (hd); return GPG_ERR_NO_PUBKEY; } rc = keydb_get_keyblock (hd, &keyblock); keydb_release (hd); if (rc) { log_error ("keydb_get_keyblock failed: %s\n", gpg_strerror (rc)); return GPG_ERR_NO_PUBKEY; } assert (keyblock->pkt->pkttype == PKT_PUBLIC_KEY || keyblock->pkt->pkttype == PKT_PUBLIC_SUBKEY); if (pk) copy_public_key (pk, keyblock->pkt->pkt.public_key); release_kbnode (keyblock); /* Not caching key here since it won't have all of the fields properly set. */ return 0; } /* Search for a key with the given fingerprint and return the * complete keyblock which may have more than only this key. */ int get_keyblock_byfprint (KBNODE * ret_keyblock, const byte * fprint, size_t fprint_len) { int rc; if (fprint_len == 20 || fprint_len == 16) { struct getkey_ctx_s ctx; memset (&ctx, 0, sizeof ctx); ctx.not_allocated = 1; ctx.kr_handle = keydb_new (); ctx.nitems = 1; ctx.items[0].mode = (fprint_len == 16 ? KEYDB_SEARCH_MODE_FPR16 : KEYDB_SEARCH_MODE_FPR20); memcpy (ctx.items[0].u.fpr, fprint, fprint_len); rc = lookup (&ctx, ret_keyblock, 0); get_pubkey_end (&ctx); } else rc = GPG_ERR_GENERAL; /* Oops */ return rc; } /* Get a secret key by NAME and store it into PK. If NAME is NULL use * the default key. This functions checks that a corresponding secret * key is available. With no secret key it does not succeeed. */ gpg_error_t get_seckey_byname (PKT_public_key *pk, const char *name) { gpg_error_t err; strlist_t namelist = NULL; int include_unusable = 1; /* If we have no name, try to use the default secret key. If we have no default, we'll use the first usable one. */ if (!name && opt.def_secret_key && *opt.def_secret_key) add_to_strlist (&namelist, opt.def_secret_key); else if (name) add_to_strlist (&namelist, name); else include_unusable = 0; err = key_byname (NULL, namelist, pk, 1, include_unusable, NULL, NULL); free_strlist (namelist); return err; } /* Search for a key with the given fingerprint. * FIXME: * We should replace this with the _byname function. This can be done * by creating a userID conforming to the unified fingerprint style. */ gpg_error_t get_seckey_byfprint (PKT_public_key *pk, const byte * fprint, size_t fprint_len) { gpg_error_t err; if (fprint_len == 20 || fprint_len == 16) { struct getkey_ctx_s ctx; kbnode_t kb = NULL; memset (&ctx, 0, sizeof ctx); ctx.exact = 1; ctx.not_allocated = 1; ctx.kr_handle = keydb_new (); ctx.nitems = 1; ctx.items[0].mode = fprint_len == 16 ? KEYDB_SEARCH_MODE_FPR16 : KEYDB_SEARCH_MODE_FPR20; memcpy (ctx.items[0].u.fpr, fprint, fprint_len); err = lookup (&ctx, &kb, 1); if (!err && pk) pk_from_block (&ctx, pk, kb); release_kbnode (kb); get_pubkey_end (&ctx); } else err = gpg_error (GPG_ERR_BUG); return err; } /* Search for a secret key with the given fingerprint and return the complete keyblock which may have more than only this key. Return an error if no corresponding secret key is available. */ gpg_error_t get_seckeyblock_byfprint (kbnode_t *ret_keyblock, const byte *fprint, size_t fprint_len) { gpg_error_t err; struct getkey_ctx_s ctx; if (fprint_len != 20 && fprint_len == 16) return gpg_error (GPG_ERR_BUG); memset (&ctx, 0, sizeof ctx); ctx.not_allocated = 1; ctx.kr_handle = keydb_new (); ctx.nitems = 1; ctx.items[0].mode = (fprint_len == 16 ? KEYDB_SEARCH_MODE_FPR16 : KEYDB_SEARCH_MODE_FPR20); memcpy (ctx.items[0].u.fpr, fprint, fprint_len); err = lookup (&ctx, ret_keyblock, 1); get_pubkey_end (&ctx); return err; } /* The new function to return a key. FIXME: Document it. */ gpg_error_t getkey_bynames (getkey_ctx_t *retctx, PKT_public_key *pk, strlist_t names, int want_secret, kbnode_t *ret_keyblock) { return key_byname (retctx, names, pk, want_secret, 1, ret_keyblock, NULL); } /* Get a key by name and store it into PK if that is not NULL. If * RETCTX is not NULL return the search context which needs to be * released by the caller using getkey_end. If NAME is NULL use the * default key (see below). On success and if RET_KEYBLOCK is not * NULL the found keyblock is stored at this address. WANT_SECRET * passed as true requires that a secret key is available for the * selected key. * * If WANT_SECRET is true and NAME is NULL and a default key has been * defined that defined key is used. In all other cases the first * available key is used. * * FIXME: Explain what is up with unusable keys. * * FIXME: We also have the get_pubkey_byname function which has a * different semantic. Should be merged with this one. */ gpg_error_t getkey_byname (getkey_ctx_t *retctx, PKT_public_key *pk, const char *name, int want_secret, kbnode_t *ret_keyblock) { gpg_error_t err; strlist_t namelist = NULL; int with_unusable = 1; if (want_secret && !name && opt.def_secret_key && *opt.def_secret_key) add_to_strlist (&namelist, opt.def_secret_key); else if (name) add_to_strlist (&namelist, name); else with_unusable = 0; err = key_byname (retctx, namelist, pk, want_secret, with_unusable, ret_keyblock, NULL); /* FIXME: Check that we really return GPG_ERR_NO_SECKEY if WANT_SECRET has been used. */ free_strlist (namelist); return err; } /* The new function to return the next key. */ gpg_error_t getkey_next (getkey_ctx_t ctx, PKT_public_key *pk, kbnode_t *ret_keyblock) { int rc; /* Fixme: Make sure this is proper gpg_error */ /* We need to disable the caching so that for an exact key search we won't get the result back from the cache and thus end up in an endless loop. Disabling this here is sufficient because although the result has been cached, if won't be used then. */ keydb_disable_caching (ctx->kr_handle); rc = lookup (ctx, ret_keyblock, ctx->want_secret); if (!rc && pk && ret_keyblock) pk_from_block (ctx, pk, *ret_keyblock); return rc; } /* The new function to finish a key listing. */ void getkey_end (getkey_ctx_t ctx) { if (ctx) { memset (&ctx->kbpos, 0, sizeof ctx->kbpos); keydb_release (ctx->kr_handle); free_strlist (ctx->extra_list); if (!ctx->not_allocated) xfree (ctx); } } /************************************************ ************* Merging stuff ******************** ************************************************/ /* Set the mainkey_id fields for all keys in KEYBLOCK. This is usually done by merge_selfsigs but at some places we only need the main_kid but the the full merging. The function also guarantees that all pk->keyids are computed. */ void setup_main_keyids (kbnode_t keyblock) { u32 kid[2], mainkid[2]; kbnode_t kbctx, node; PKT_public_key *pk; if (keyblock->pkt->pkttype != PKT_PUBLIC_KEY) BUG (); pk = keyblock->pkt->pkt.public_key; keyid_from_pk (pk, mainkid); for (kbctx=NULL; (node = walk_kbnode (keyblock, &kbctx, 0)); ) { if (!(node->pkt->pkttype == PKT_PUBLIC_KEY || node->pkt->pkttype == PKT_PUBLIC_SUBKEY)) continue; pk = node->pkt->pkt.public_key; keyid_from_pk (pk, kid); /* Make sure pk->keyid is set. */ if (!pk->main_keyid[0] && !pk->main_keyid[1]) { pk->main_keyid[0] = mainkid[0]; pk->main_keyid[1] = mainkid[1]; } } } /* Merge all self-signatures with the keys. */ void merge_keys_and_selfsig (KBNODE keyblock) { if (!keyblock) ; else if (keyblock->pkt->pkttype == PKT_PUBLIC_KEY) merge_selfsigs (keyblock); else log_debug ("FIXME: merging secret key blocks is not anymore available\n"); } static int parse_key_usage (PKT_signature * sig) { int key_usage = 0; const byte *p; size_t n; byte flags; p = parse_sig_subpkt (sig->hashed, SIGSUBPKT_KEY_FLAGS, &n); if (p && n) { /* First octet of the keyflags. */ flags = *p; if (flags & 1) { key_usage |= PUBKEY_USAGE_CERT; flags &= ~1; } if (flags & 2) { key_usage |= PUBKEY_USAGE_SIG; flags &= ~2; } /* We do not distinguish between encrypting communications and encrypting storage. */ if (flags & (0x04 | 0x08)) { key_usage |= PUBKEY_USAGE_ENC; flags &= ~(0x04 | 0x08); } if (flags & 0x20) { key_usage |= PUBKEY_USAGE_AUTH; flags &= ~0x20; } if (flags) key_usage |= PUBKEY_USAGE_UNKNOWN; if (!key_usage) key_usage |= PUBKEY_USAGE_NONE; } else if (p) /* Key flags of length zero. */ key_usage |= PUBKEY_USAGE_NONE; /* We set PUBKEY_USAGE_UNKNOWN to indicate that this key has a capability that we do not handle. This serves to distinguish between a zero key usage which we handle as the default capabilities for that algorithm, and a usage that we do not handle. Likewise we use PUBKEY_USAGE_NONE to indicate that key_flags have been given but they do not specify any usage. */ return key_usage; } /* Apply information from SIGNODE (which is the valid self-signature * associated with that UID) to the UIDNODE: * - wether the UID has been revoked * - assumed creation date of the UID * - temporary store the keyflags here * - temporary store the key expiration time here * - mark whether the primary user ID flag hat been set. * - store the preferences */ static void fixup_uidnode (KBNODE uidnode, KBNODE signode, u32 keycreated) { PKT_user_id *uid = uidnode->pkt->pkt.user_id; PKT_signature *sig = signode->pkt->pkt.signature; const byte *p, *sym, *hash, *zip; size_t n, nsym, nhash, nzip; sig->flags.chosen_selfsig = 1;/* We chose this one. */ uid->created = 0; /* Not created == invalid. */ if (IS_UID_REV (sig)) { uid->is_revoked = 1; return; /* Has been revoked. */ } else uid->is_revoked = 0; uid->expiredate = sig->expiredate; if (sig->flags.expired) { uid->is_expired = 1; return; /* Has expired. */ } else uid->is_expired = 0; uid->created = sig->timestamp; /* This one is okay. */ uid->selfsigversion = sig->version; /* If we got this far, it's not expired :) */ uid->is_expired = 0; /* Store the key flags in the helper variable for later processing. */ uid->help_key_usage = parse_key_usage (sig); /* Ditto for the key expiration. */ p = parse_sig_subpkt (sig->hashed, SIGSUBPKT_KEY_EXPIRE, NULL); - if (p && buffer_to_u32 (p)) - uid->help_key_expire = keycreated + buffer_to_u32 (p); + if (p && buf32_to_u32 (p)) + uid->help_key_expire = keycreated + buf32_to_u32 (p); else uid->help_key_expire = 0; /* Set the primary user ID flag - we will later wipe out some * of them to only have one in our keyblock. */ uid->is_primary = 0; p = parse_sig_subpkt (sig->hashed, SIGSUBPKT_PRIMARY_UID, NULL); if (p && *p) uid->is_primary = 2; /* We could also query this from the unhashed area if it is not in * the hased area and then later try to decide which is the better * there should be no security problem with this. * For now we only look at the hashed one. */ /* Now build the preferences list. These must come from the hashed section so nobody can modify the ciphers a key is willing to accept. */ p = parse_sig_subpkt (sig->hashed, SIGSUBPKT_PREF_SYM, &n); sym = p; nsym = p ? n : 0; p = parse_sig_subpkt (sig->hashed, SIGSUBPKT_PREF_HASH, &n); hash = p; nhash = p ? n : 0; p = parse_sig_subpkt (sig->hashed, SIGSUBPKT_PREF_COMPR, &n); zip = p; nzip = p ? n : 0; if (uid->prefs) xfree (uid->prefs); n = nsym + nhash + nzip; if (!n) uid->prefs = NULL; else { uid->prefs = xmalloc (sizeof (*uid->prefs) * (n + 1)); n = 0; for (; nsym; nsym--, n++) { uid->prefs[n].type = PREFTYPE_SYM; uid->prefs[n].value = *sym++; } for (; nhash; nhash--, n++) { uid->prefs[n].type = PREFTYPE_HASH; uid->prefs[n].value = *hash++; } for (; nzip; nzip--, n++) { uid->prefs[n].type = PREFTYPE_ZIP; uid->prefs[n].value = *zip++; } uid->prefs[n].type = PREFTYPE_NONE; /* End of list marker */ uid->prefs[n].value = 0; } /* See whether we have the MDC feature. */ uid->flags.mdc = 0; p = parse_sig_subpkt (sig->hashed, SIGSUBPKT_FEATURES, &n); if (p && n && (p[0] & 0x01)) uid->flags.mdc = 1; /* And the keyserver modify flag. */ uid->flags.ks_modify = 1; p = parse_sig_subpkt (sig->hashed, SIGSUBPKT_KS_FLAGS, &n); if (p && n && (p[0] & 0x80)) uid->flags.ks_modify = 0; } static void sig_to_revoke_info (PKT_signature * sig, struct revoke_info *rinfo) { rinfo->date = sig->timestamp; rinfo->algo = sig->pubkey_algo; rinfo->keyid[0] = sig->keyid[0]; rinfo->keyid[1] = sig->keyid[1]; } /* Note that R_REVOKED may be set to 0, 1 or 2. */ static void merge_selfsigs_main (KBNODE keyblock, int *r_revoked, struct revoke_info *rinfo) { PKT_public_key *pk = NULL; KBNODE k; u32 kid[2]; u32 sigdate, uiddate, uiddate2; KBNODE signode, uidnode, uidnode2; u32 curtime = make_timestamp (); unsigned int key_usage = 0; u32 keytimestamp = 0; u32 key_expire = 0; int key_expire_seen = 0; byte sigversion = 0; *r_revoked = 0; memset (rinfo, 0, sizeof (*rinfo)); if (keyblock->pkt->pkttype != PKT_PUBLIC_KEY) BUG (); pk = keyblock->pkt->pkt.public_key; keytimestamp = pk->timestamp; keyid_from_pk (pk, kid); pk->main_keyid[0] = kid[0]; pk->main_keyid[1] = kid[1]; if (pk->version < 4) { /* Before v4 the key packet itself contains the expiration date * and there was no way to change it, so we start with the one * from the key packet. */ key_expire = pk->max_expiredate; key_expire_seen = 1; } /* First pass: Find the latest direct key self-signature. We assume * that the newest one overrides all others. */ /* In case this key was already merged. */ xfree (pk->revkey); pk->revkey = NULL; pk->numrevkeys = 0; signode = NULL; sigdate = 0; /* Helper variable to find the latest signature. */ for (k = keyblock; k && k->pkt->pkttype != PKT_USER_ID; k = k->next) { if (k->pkt->pkttype == PKT_SIGNATURE) { PKT_signature *sig = k->pkt->pkt.signature; if (sig->keyid[0] == kid[0] && sig->keyid[1] == kid[1]) { if (check_key_signature (keyblock, k, NULL)) ; /* Signature did not verify. */ else if (IS_KEY_REV (sig)) { /* Key has been revoked - there is no way to * override such a revocation, so we theoretically * can stop now. We should not cope with expiration * times for revocations here because we have to * assume that an attacker can generate all kinds of * signatures. However due to the fact that the key * has been revoked it does not harm either and by * continuing we gather some more info on that * key. */ *r_revoked = 1; sig_to_revoke_info (sig, rinfo); } else if (IS_KEY_SIG (sig)) { /* Add any revocation keys onto the pk. This is particularly interesting since we normally only get data from the most recent 1F signature, but you need multiple 1F sigs to properly handle revocation keys (PGP does it this way, and a revocation key could be sensitive and hence in a different signature). */ if (sig->revkey) { int i; pk->revkey = xrealloc (pk->revkey, sizeof (struct revocation_key) * (pk->numrevkeys + sig->numrevkeys)); for (i = 0; i < sig->numrevkeys; i++) memcpy (&pk->revkey[pk->numrevkeys++], sig->revkey[i], sizeof (struct revocation_key)); } if (sig->timestamp >= sigdate) { if (sig->flags.expired) ; /* Signature has expired - ignore it. */ else { sigdate = sig->timestamp; signode = k; if (sig->version > sigversion) sigversion = sig->version; } } } } } } /* Remove dupes from the revocation keys. */ if (pk->revkey) { int i, j, x, changed = 0; for (i = 0; i < pk->numrevkeys; i++) { for (j = i + 1; j < pk->numrevkeys; j++) { if (memcmp (&pk->revkey[i], &pk->revkey[j], sizeof (struct revocation_key)) == 0) { /* remove j */ for (x = j; x < pk->numrevkeys - 1; x++) pk->revkey[x] = pk->revkey[x + 1]; pk->numrevkeys--; j--; changed = 1; } } } if (changed) pk->revkey = xrealloc (pk->revkey, pk->numrevkeys * sizeof (struct revocation_key)); } if (signode) { /* Some information from a direct key signature take precedence * over the same information given in UID sigs. */ PKT_signature *sig = signode->pkt->pkt.signature; const byte *p; key_usage = parse_key_usage (sig); p = parse_sig_subpkt (sig->hashed, SIGSUBPKT_KEY_EXPIRE, NULL); - if (p && buffer_to_u32 (p)) + if (p && buf32_to_u32 (p)) { - key_expire = keytimestamp + buffer_to_u32 (p); + key_expire = keytimestamp + buf32_to_u32 (p); key_expire_seen = 1; } /* Mark that key as valid: One direct key signature should * render a key as valid. */ pk->flags.valid = 1; } /* Pass 1.5: Look for key revocation signatures that were not made by the key (i.e. did a revocation key issue a revocation for us?). Only bother to do this if there is a revocation key in the first place and we're not revoked already. */ if (!*r_revoked && pk->revkey) for (k = keyblock; k && k->pkt->pkttype != PKT_USER_ID; k = k->next) { if (k->pkt->pkttype == PKT_SIGNATURE) { PKT_signature *sig = k->pkt->pkt.signature; if (IS_KEY_REV (sig) && (sig->keyid[0] != kid[0] || sig->keyid[1] != kid[1])) { int rc = check_revocation_keys (pk, sig); if (rc == 0) { *r_revoked = 2; sig_to_revoke_info (sig, rinfo); /* Don't continue checking since we can't be any more revoked than this. */ break; } else if (gpg_err_code (rc) == GPG_ERR_NO_PUBKEY) pk->flags.maybe_revoked = 1; /* A failure here means the sig did not verify, was not issued by a revocation key, or a revocation key loop was broken. If a revocation key isn't findable, however, the key might be revoked and we don't know it. */ /* TODO: In the future handle subkey and cert revocations? PGP doesn't, but it's in 2440. */ } } } /* Second pass: Look at the self-signature of all user IDs. */ signode = uidnode = NULL; sigdate = 0; /* Helper variable to find the latest signature in one UID. */ for (k = keyblock; k && k->pkt->pkttype != PKT_PUBLIC_SUBKEY; k = k->next) { if (k->pkt->pkttype == PKT_USER_ID) { if (uidnode && signode) { fixup_uidnode (uidnode, signode, keytimestamp); pk->flags.valid = 1; } uidnode = k; signode = NULL; sigdate = 0; } else if (k->pkt->pkttype == PKT_SIGNATURE && uidnode) { PKT_signature *sig = k->pkt->pkt.signature; if (sig->keyid[0] == kid[0] && sig->keyid[1] == kid[1]) { if (check_key_signature (keyblock, k, NULL)) ; /* signature did not verify */ else if ((IS_UID_SIG (sig) || IS_UID_REV (sig)) && sig->timestamp >= sigdate) { /* Note: we allow to invalidate cert revocations * by a newer signature. An attacker can't use this * because a key should be revoced with a key revocation. * The reason why we have to allow for that is that at * one time an email address may become invalid but later * the same email address may become valid again (hired, * fired, hired again). */ sigdate = sig->timestamp; signode = k; signode->pkt->pkt.signature->flags.chosen_selfsig = 0; if (sig->version > sigversion) sigversion = sig->version; } } } } if (uidnode && signode) { fixup_uidnode (uidnode, signode, keytimestamp); pk->flags.valid = 1; } /* If the key isn't valid yet, and we have --allow-non-selfsigned-uid set, then force it valid. */ if (!pk->flags.valid && opt.allow_non_selfsigned_uid) { if (opt.verbose) log_info (_("Invalid key %s made valid by" " --allow-non-selfsigned-uid\n"), keystr_from_pk (pk)); pk->flags.valid = 1; } /* The key STILL isn't valid, so try and find an ultimately trusted signature. */ if (!pk->flags.valid) { uidnode = NULL; for (k = keyblock; k && k->pkt->pkttype != PKT_PUBLIC_SUBKEY; k = k->next) { if (k->pkt->pkttype == PKT_USER_ID) uidnode = k; else if (k->pkt->pkttype == PKT_SIGNATURE && uidnode) { PKT_signature *sig = k->pkt->pkt.signature; if (sig->keyid[0] != kid[0] || sig->keyid[1] != kid[1]) { PKT_public_key *ultimate_pk; ultimate_pk = xmalloc_clear (sizeof (*ultimate_pk)); /* We don't want to use the full get_pubkey to avoid infinite recursion in certain cases. There is no reason to check that an ultimately trusted key is still valid - if it has been revoked or the user should also renmove the ultimate trust flag. */ if (get_pubkey_fast (ultimate_pk, sig->keyid) == 0 && check_key_signature2 (keyblock, k, ultimate_pk, NULL, NULL, NULL, NULL) == 0 && get_ownertrust (ultimate_pk) == TRUST_ULTIMATE) { free_public_key (ultimate_pk); pk->flags.valid = 1; break; } free_public_key (ultimate_pk); } } } } /* Record the highest selfsig version so we know if this is a v3 key through and through, or a v3 key with a v4 selfsig somewhere. This is useful in a few places to know if the key must be treated as PGP2-style or OpenPGP-style. Note that a selfsig revocation with a higher version number will also raise this value. This is okay since such a revocation must be issued by the user (i.e. it cannot be issued by someone else to modify the key behavior.) */ pk->selfsigversion = sigversion; /* Now that we had a look at all user IDs we can now get some information * from those user IDs. */ if (!key_usage) { /* Find the latest user ID with key flags set. */ uiddate = 0; /* Helper to find the latest user ID. */ for (k = keyblock; k && k->pkt->pkttype != PKT_PUBLIC_SUBKEY; k = k->next) { if (k->pkt->pkttype == PKT_USER_ID) { PKT_user_id *uid = k->pkt->pkt.user_id; if (uid->help_key_usage && uid->created > uiddate) { key_usage = uid->help_key_usage; uiddate = uid->created; } } } } if (!key_usage) { /* No key flags at all: get it from the algo. */ key_usage = openpgp_pk_algo_usage (pk->pubkey_algo); } else { /* Check that the usage matches the usage as given by the algo. */ int x = openpgp_pk_algo_usage (pk->pubkey_algo); if (x) /* Mask it down to the actual allowed usage. */ key_usage &= x; } /* Whatever happens, it's a primary key, so it can certify. */ pk->pubkey_usage = key_usage | PUBKEY_USAGE_CERT; if (!key_expire_seen) { /* Find the latest valid user ID with a key expiration set * Note, that this may be a different one from the above because * some user IDs may have no expiration date set. */ uiddate = 0; for (k = keyblock; k && k->pkt->pkttype != PKT_PUBLIC_SUBKEY; k = k->next) { if (k->pkt->pkttype == PKT_USER_ID) { PKT_user_id *uid = k->pkt->pkt.user_id; if (uid->help_key_expire && uid->created > uiddate) { key_expire = uid->help_key_expire; uiddate = uid->created; } } } } /* Currently only v3 keys have a maximum expiration date, but I'll bet v5 keys get this feature again. */ if (key_expire == 0 || (pk->max_expiredate && key_expire > pk->max_expiredate)) key_expire = pk->max_expiredate; pk->has_expired = key_expire >= curtime ? 0 : key_expire; pk->expiredate = key_expire; /* Fixme: we should see how to get rid of the expiretime fields but * this needs changes at other places too. */ /* And now find the real primary user ID and delete all others. */ uiddate = uiddate2 = 0; uidnode = uidnode2 = NULL; for (k = keyblock; k && k->pkt->pkttype != PKT_PUBLIC_SUBKEY; k = k->next) { if (k->pkt->pkttype == PKT_USER_ID && !k->pkt->pkt.user_id->attrib_data) { PKT_user_id *uid = k->pkt->pkt.user_id; if (uid->is_primary) { if (uid->created > uiddate) { uiddate = uid->created; uidnode = k; } else if (uid->created == uiddate && uidnode) { /* The dates are equal, so we need to do a different (and arbitrary) comparison. This should rarely, if ever, happen. It's good to try and guarantee that two different GnuPG users with two different keyrings at least pick the same primary. */ if (cmp_user_ids (uid, uidnode->pkt->pkt.user_id) > 0) uidnode = k; } } else { if (uid->created > uiddate2) { uiddate2 = uid->created; uidnode2 = k; } else if (uid->created == uiddate2 && uidnode2) { if (cmp_user_ids (uid, uidnode2->pkt->pkt.user_id) > 0) uidnode2 = k; } } } } if (uidnode) { for (k = keyblock; k && k->pkt->pkttype != PKT_PUBLIC_SUBKEY; k = k->next) { if (k->pkt->pkttype == PKT_USER_ID && !k->pkt->pkt.user_id->attrib_data) { PKT_user_id *uid = k->pkt->pkt.user_id; if (k != uidnode) uid->is_primary = 0; } } } else if (uidnode2) { /* None is flagged primary - use the latest user ID we have, and disambiguate with the arbitrary packet comparison. */ uidnode2->pkt->pkt.user_id->is_primary = 1; } else { /* None of our uids were self-signed, so pick the one that sorts first to be the primary. This is the best we can do here since there are no self sigs to date the uids. */ uidnode = NULL; for (k = keyblock; k && k->pkt->pkttype != PKT_PUBLIC_SUBKEY; k = k->next) { if (k->pkt->pkttype == PKT_USER_ID && !k->pkt->pkt.user_id->attrib_data) { if (!uidnode) { uidnode = k; uidnode->pkt->pkt.user_id->is_primary = 1; continue; } else { if (cmp_user_ids (k->pkt->pkt.user_id, uidnode->pkt->pkt.user_id) > 0) { uidnode->pkt->pkt.user_id->is_primary = 0; uidnode = k; uidnode->pkt->pkt.user_id->is_primary = 1; } else k->pkt->pkt.user_id->is_primary = 0; /* just to be safe */ } } } } } /* Convert a buffer to a signature. Useful for 0x19 embedded sigs. Caller must free the signature when they are done. */ static PKT_signature * buf_to_sig (const byte * buf, size_t len) { PKT_signature *sig = xmalloc_clear (sizeof (PKT_signature)); IOBUF iobuf = iobuf_temp_with_content (buf, len); int save_mode = set_packet_list_mode (0); if (parse_signature (iobuf, PKT_SIGNATURE, len, sig) != 0) { xfree (sig); sig = NULL; } set_packet_list_mode (save_mode); iobuf_close (iobuf); return sig; } static void merge_selfsigs_subkey (KBNODE keyblock, KBNODE subnode) { PKT_public_key *mainpk = NULL, *subpk = NULL; PKT_signature *sig; KBNODE k; u32 mainkid[2]; u32 sigdate = 0; KBNODE signode; u32 curtime = make_timestamp (); unsigned int key_usage = 0; u32 keytimestamp = 0; u32 key_expire = 0; const byte *p; if (subnode->pkt->pkttype != PKT_PUBLIC_SUBKEY) BUG (); mainpk = keyblock->pkt->pkt.public_key; if (mainpk->version < 4) return;/* (actually this should never happen) */ keyid_from_pk (mainpk, mainkid); subpk = subnode->pkt->pkt.public_key; keytimestamp = subpk->timestamp; subpk->flags.valid = 0; subpk->main_keyid[0] = mainpk->main_keyid[0]; subpk->main_keyid[1] = mainpk->main_keyid[1]; /* Find the latest key binding self-signature. */ signode = NULL; sigdate = 0; /* Helper to find the latest signature. */ for (k = subnode->next; k && k->pkt->pkttype != PKT_PUBLIC_SUBKEY; k = k->next) { if (k->pkt->pkttype == PKT_SIGNATURE) { sig = k->pkt->pkt.signature; if (sig->keyid[0] == mainkid[0] && sig->keyid[1] == mainkid[1]) { if (check_key_signature (keyblock, k, NULL)) ; /* Signature did not verify. */ else if (IS_SUBKEY_REV (sig)) { /* Note that this means that the date on a revocation sig does not matter - even if the binding sig is dated after the revocation sig, the subkey is still marked as revoked. This seems ok, as it is just as easy to make new subkeys rather than re-sign old ones as the problem is in the distribution. Plus, PGP (7) does this the same way. */ subpk->flags.revoked = 1; sig_to_revoke_info (sig, &subpk->revoked); /* Although we could stop now, we continue to * figure out other information like the old expiration * time. */ } else if (IS_SUBKEY_SIG (sig) && sig->timestamp >= sigdate) { if (sig->flags.expired) ; /* Signature has expired - ignore it. */ else { sigdate = sig->timestamp; signode = k; signode->pkt->pkt.signature->flags.chosen_selfsig = 0; } } } } } /* No valid key binding. */ if (!signode) return; sig = signode->pkt->pkt.signature; sig->flags.chosen_selfsig = 1; /* So we know which selfsig we chose later. */ key_usage = parse_key_usage (sig); if (!key_usage) { /* No key flags at all: get it from the algo. */ key_usage = openpgp_pk_algo_usage (subpk->pubkey_algo); } else { /* Check that the usage matches the usage as given by the algo. */ int x = openpgp_pk_algo_usage (subpk->pubkey_algo); if (x) /* Mask it down to the actual allowed usage. */ key_usage &= x; } subpk->pubkey_usage = key_usage; p = parse_sig_subpkt (sig->hashed, SIGSUBPKT_KEY_EXPIRE, NULL); - if (p && buffer_to_u32 (p)) - key_expire = keytimestamp + buffer_to_u32 (p); + if (p && buf32_to_u32 (p)) + key_expire = keytimestamp + buf32_to_u32 (p); else key_expire = 0; subpk->has_expired = key_expire >= curtime ? 0 : key_expire; subpk->expiredate = key_expire; /* Algo doesn't exist. */ if (openpgp_pk_test_algo (subpk->pubkey_algo)) return; subpk->flags.valid = 1; /* Find the most recent 0x19 embedded signature on our self-sig. */ if (!subpk->flags.backsig) { int seq = 0; size_t n; PKT_signature *backsig = NULL; sigdate = 0; /* We do this while() since there may be other embedded signatures in the future. We only want 0x19 here. */ while ((p = enum_sig_subpkt (sig->hashed, SIGSUBPKT_SIGNATURE, &n, &seq, NULL))) if (n > 3 && ((p[0] == 3 && p[2] == 0x19) || (p[0] == 4 && p[1] == 0x19))) { PKT_signature *tempsig = buf_to_sig (p, n); if (tempsig) { if (tempsig->timestamp > sigdate) { if (backsig) free_seckey_enc (backsig); backsig = tempsig; sigdate = backsig->timestamp; } else free_seckey_enc (tempsig); } } seq = 0; /* It is safe to have this in the unhashed area since the 0x19 is located on the selfsig for convenience, not security. */ while ((p = enum_sig_subpkt (sig->unhashed, SIGSUBPKT_SIGNATURE, &n, &seq, NULL))) if (n > 3 && ((p[0] == 3 && p[2] == 0x19) || (p[0] == 4 && p[1] == 0x19))) { PKT_signature *tempsig = buf_to_sig (p, n); if (tempsig) { if (tempsig->timestamp > sigdate) { if (backsig) free_seckey_enc (backsig); backsig = tempsig; sigdate = backsig->timestamp; } else free_seckey_enc (tempsig); } } if (backsig) { /* At ths point, backsig contains the most recent 0x19 sig. Let's see if it is good. */ /* 2==valid, 1==invalid, 0==didn't check */ if (check_backsig (mainpk, subpk, backsig) == 0) subpk->flags.backsig = 2; else subpk->flags.backsig = 1; free_seckey_enc (backsig); } } } /* * Merge information from the self-signatures with the key, so that * we can later use them more easy. * The function works by first applying the self signatures to the * primary key and the to each subkey. * Here are the rules we use to decide which inormation from which * self-signature is used: * We check all self signatures or validity and ignore all invalid signatures. * All signatures are then ordered by their creation date .... * For the primary key: * FIXME the docs */ static void merge_selfsigs (KBNODE keyblock) { KBNODE k; int revoked; struct revoke_info rinfo; PKT_public_key *main_pk; prefitem_t *prefs; unsigned int mdc_feature; if (keyblock->pkt->pkttype != PKT_PUBLIC_KEY) { if (keyblock->pkt->pkttype == PKT_SECRET_KEY) { log_error ("expected public key but found secret key " "- must stop\n"); /* We better exit here because a public key is expected at other places too. FIXME: Figure this out earlier and don't get to here at all */ g10_exit (1); } BUG (); } merge_selfsigs_main (keyblock, &revoked, &rinfo); /* Now merge in the data from each of the subkeys. */ for (k = keyblock; k; k = k->next) { if (k->pkt->pkttype == PKT_PUBLIC_SUBKEY) { merge_selfsigs_subkey (keyblock, k); } } main_pk = keyblock->pkt->pkt.public_key; if (revoked || main_pk->has_expired || !main_pk->flags.valid) { /* If the primary key is revoked, expired, or invalid we * better set the appropriate flags on that key and all * subkeys. */ for (k = keyblock; k; k = k->next) { if (k->pkt->pkttype == PKT_PUBLIC_KEY || k->pkt->pkttype == PKT_PUBLIC_SUBKEY) { PKT_public_key *pk = k->pkt->pkt.public_key; if (!main_pk->flags.valid) pk->flags.valid = 0; if (revoked && !pk->flags.revoked) { pk->flags.revoked = revoked; memcpy (&pk->revoked, &rinfo, sizeof (rinfo)); } if (main_pk->has_expired) pk->has_expired = main_pk->has_expired; } } return; } /* Set the preference list of all keys to those of the primary real * user ID. Note: we use these preferences when we don't know by * which user ID the key has been selected. * fixme: we should keep atoms of commonly used preferences or * use reference counting to optimize the preference lists storage. * FIXME: it might be better to use the intersection of * all preferences. * Do a similar thing for the MDC feature flag. */ prefs = NULL; mdc_feature = 0; for (k = keyblock; k && k->pkt->pkttype != PKT_PUBLIC_SUBKEY; k = k->next) { if (k->pkt->pkttype == PKT_USER_ID && !k->pkt->pkt.user_id->attrib_data && k->pkt->pkt.user_id->is_primary) { prefs = k->pkt->pkt.user_id->prefs; mdc_feature = k->pkt->pkt.user_id->flags.mdc; break; } } for (k = keyblock; k; k = k->next) { if (k->pkt->pkttype == PKT_PUBLIC_KEY || k->pkt->pkttype == PKT_PUBLIC_SUBKEY) { PKT_public_key *pk = k->pkt->pkt.public_key; if (pk->prefs) xfree (pk->prefs); pk->prefs = copy_prefs (prefs); pk->flags.mdc = mdc_feature; } } } /* See whether the key fits our requirements and in case we do not * request the primary key, select a suitable subkey. * * Returns: True when a suitable key has been found. * * We have to distinguish four cases: FIXME! * 1. No usage and no primary key requested * Examples for this case are that we have a keyID to be used * for decrytion or verification. * 2. No usage but primary key requested * This is the case for all functions which work on an * entire keyblock, e.g. for editing or listing * 3. Usage and primary key requested * FXME * 4. Usage but no primary key requested * FIXME * FIXME: Tell what is going to happen here and something about the rationale * Note: We don't use this function if no specific usage is requested; * This way the getkey functions can be used for plain key listings. * * CTX ist the keyblock we are investigating, if FOUNDK is not NULL this * is the key we actually found by looking at the keyid or a fingerprint and * may either point to the primary or one of the subkeys. */ static int finish_lookup (GETKEY_CTX ctx) { KBNODE keyblock = ctx->keyblock; KBNODE k; KBNODE foundk = NULL; PKT_user_id *foundu = NULL; #define USAGE_MASK (PUBKEY_USAGE_SIG|PUBKEY_USAGE_ENC|PUBKEY_USAGE_CERT) unsigned int req_usage = (ctx->req_usage & USAGE_MASK); /* Request the primary if we're certifying another key, and also if signing data while --pgp6 or --pgp7 is on since pgp 6 and 7 do not understand signatures made by a signing subkey. PGP 8 does. */ int req_prim = (ctx->req_usage & PUBKEY_USAGE_CERT) || ((PGP6 || PGP7) && (ctx->req_usage & PUBKEY_USAGE_SIG)); u32 latest_date; KBNODE latest_key; u32 curtime = make_timestamp (); assert (keyblock->pkt->pkttype == PKT_PUBLIC_KEY); ctx->found_key = NULL; if (ctx->exact) { for (k = keyblock; k; k = k->next) { if ((k->flag & 1)) { assert (k->pkt->pkttype == PKT_PUBLIC_KEY || k->pkt->pkttype == PKT_PUBLIC_SUBKEY); foundk = k; break; } } } for (k = keyblock; k; k = k->next) { if ((k->flag & 2)) { assert (k->pkt->pkttype == PKT_USER_ID); foundu = k->pkt->pkt.user_id; break; } } if (DBG_CACHE) log_debug ("finish_lookup: checking key %08lX (%s)(req_usage=%x)\n", (ulong) keyid_from_pk (keyblock->pkt->pkt.public_key, NULL), foundk ? "one" : "all", req_usage); if (!req_usage) { latest_key = foundk ? foundk : keyblock; goto found; } latest_date = 0; latest_key = NULL; /* Do not look at subkeys if a certification key is requested. */ if ((!foundk || foundk->pkt->pkttype == PKT_PUBLIC_SUBKEY) && !req_prim) { KBNODE nextk; /* Either start a loop or check just this one subkey. */ for (k = foundk ? foundk : keyblock; k; k = nextk) { PKT_public_key *pk; nextk = k->next; if (k->pkt->pkttype != PKT_PUBLIC_SUBKEY) continue; if (foundk) nextk = NULL; /* what a hack */ pk = k->pkt->pkt.public_key; if (DBG_CACHE) log_debug ("\tchecking subkey %08lX\n", (ulong) keyid_from_pk (pk, NULL)); if (!pk->flags.valid) { if (DBG_CACHE) log_debug ("\tsubkey not valid\n"); continue; } if (pk->flags.revoked) { if (DBG_CACHE) log_debug ("\tsubkey has been revoked\n"); continue; } if (pk->has_expired) { if (DBG_CACHE) log_debug ("\tsubkey has expired\n"); continue; } if (pk->timestamp > curtime && !opt.ignore_valid_from) { if (DBG_CACHE) log_debug ("\tsubkey not yet valid\n"); continue; } if (!((pk->pubkey_usage & USAGE_MASK) & req_usage)) { if (DBG_CACHE) log_debug ("\tusage does not match: want=%x have=%x\n", req_usage, pk->pubkey_usage); continue; } if (DBG_CACHE) 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 = k; } } } /* Okay now try the primary key unless we want an exact * key ID match on a subkey */ if ((!latest_key && !(ctx->exact && foundk != keyblock)) || req_prim) { PKT_public_key *pk; if (DBG_CACHE && !foundk && !req_prim) log_debug ("\tno suitable subkeys found - trying primary\n"); pk = keyblock->pkt->pkt.public_key; if (!pk->flags.valid) { if (DBG_CACHE) log_debug ("\tprimary key not valid\n"); } else if (pk->flags.revoked) { if (DBG_CACHE) log_debug ("\tprimary key has been revoked\n"); } else if (pk->has_expired) { if (DBG_CACHE) log_debug ("\tprimary key has expired\n"); } else if (!((pk->pubkey_usage & USAGE_MASK) & req_usage)) { if (DBG_CACHE) log_debug ("\tprimary key usage does not match: " "want=%x have=%x\n", req_usage, pk->pubkey_usage); } else /* Okay. */ { if (DBG_CACHE) log_debug ("\tprimary key may be used\n"); latest_key = keyblock; latest_date = pk->timestamp; } } if (!latest_key) { if (DBG_CACHE) log_debug ("\tno suitable key found - giving up\n"); return 0; /* Not found. */ } found: if (DBG_CACHE) log_debug ("\tusing key %08lX\n", (ulong) keyid_from_pk (latest_key->pkt->pkt.public_key, NULL)); if (latest_key) { PKT_public_key *pk = latest_key->pkt->pkt.public_key; if (pk->user_id) free_user_id (pk->user_id); pk->user_id = scopy_user_id (foundu); } ctx->found_key = latest_key; if (latest_key != keyblock && opt.verbose) { char *tempkeystr = xstrdup (keystr_from_pk (latest_key->pkt->pkt.public_key)); log_info (_("using subkey %s instead of primary key %s\n"), tempkeystr, keystr_from_pk (keyblock->pkt->pkt.public_key)); xfree (tempkeystr); } cache_user_id (keyblock); return 1; /* Found. */ } /* The main function to lookup a key. On success the found keyblock is stored at RET_KEYBLOCK and also in CTX. If WANT_SECRET is true a corresponding secret key is required. */ static int lookup (getkey_ctx_t ctx, kbnode_t *ret_keyblock, int want_secret) { int rc; int no_suitable_key = 0; rc = 0; while (!(rc = keydb_search (ctx->kr_handle, ctx->items, ctx->nitems, NULL))) { /* If we are searching for the first key we have to make sure that the next iteration does not do an implicit reset. This can be triggered by an empty key ring. */ if (ctx->nitems && ctx->items->mode == KEYDB_SEARCH_MODE_FIRST) ctx->items->mode = KEYDB_SEARCH_MODE_NEXT; rc = keydb_get_keyblock (ctx->kr_handle, &ctx->keyblock); if (rc) { log_error ("keydb_get_keyblock failed: %s\n", gpg_strerror (rc)); rc = 0; goto skip; } if (want_secret && agent_probe_any_secret_key (NULL, ctx->keyblock)) goto skip; /* No secret key available. */ /* Warning: node flag bits 0 and 1 should be preserved by * merge_selfsigs. For secret keys, premerge did tranfer the * keys to the keyblock. */ merge_selfsigs (ctx->keyblock); if (finish_lookup (ctx)) { no_suitable_key = 0; goto found; } else no_suitable_key = 1; skip: /* Release resources and continue search. */ release_kbnode (ctx->keyblock); ctx->keyblock = NULL; } found: if (rc && gpg_err_code (rc) != GPG_ERR_NOT_FOUND && gpg_err_code (rc) != GPG_ERR_LEGACY_KEY) log_error ("keydb_search failed: %s\n", gpg_strerror (rc)); if (!rc) { *ret_keyblock = ctx->keyblock; /* Return the keyblock. */ ctx->keyblock = NULL; } else if (gpg_err_code (rc) == GPG_ERR_NOT_FOUND && no_suitable_key) rc = want_secret? GPG_ERR_UNUSABLE_SECKEY : GPG_ERR_UNUSABLE_PUBKEY; else if (gpg_err_code (rc) == GPG_ERR_NOT_FOUND) rc = want_secret? GPG_ERR_NO_SECKEY : GPG_ERR_NO_PUBKEY; release_kbnode (ctx->keyblock); ctx->keyblock = NULL; return rc; } /* * Enumerate certain secret keys. Caller must use these procedure: * 1) create a void pointer and initialize it to NULL * 2) pass this void pointer by reference to this function * and provide space for the secret key (pass a buffer for sk) * 3) call this function as long as it does not return an error. * The error code GPG_ERR_EOF indicates the end of the listing. * 4) Always call this function a last time with SK set to NULL, * so that can free it's context. */ gpg_error_t enum_secret_keys (void **context, PKT_public_key *sk) { gpg_error_t err = 0; const char *name; struct { int eof; int state; strlist_t sl; kbnode_t keyblock; kbnode_t node; } *c = *context; if (!c) { /* Make a new context. */ c = xtrycalloc (1, sizeof *c); if (!c) return gpg_error_from_syserror (); *context = c; } if (!sk) { /* Free the context. */ release_kbnode (c->keyblock); xfree (c); *context = NULL; return 0; } if (c->eof) return gpg_error (GPG_ERR_EOF); for (;;) { /* Loop until we have a keyblock. */ while (!c->keyblock) { /* Loop over the list of secret keys. */ do { name = NULL; switch (c->state) { case 0: /* First try to use the --default-key. */ if (opt.def_secret_key && *opt.def_secret_key) name = opt.def_secret_key; c->state = 1; break; case 1: /* Init list of keys to try. */ c->sl = opt.secret_keys_to_try; c->state++; break; case 2: /* Get next item from list. */ if (c->sl) { name = c->sl->d; c->sl = c->sl->next; } else c->state++; break; default: /* No more names to check - stop. */ c->eof = 1; return gpg_error (GPG_ERR_EOF); } } while (!name || !*name); err = getkey_byname (NULL, NULL, name, 1, &c->keyblock); if (err) { /* getkey_byname might return a keyblock even in the error case - I have not checked. Thus better release it. */ release_kbnode (c->keyblock); c->keyblock = NULL; } else c->node = c->keyblock; } /* Get the next key from the current keyblock. */ for (; c->node; c->node = c->node->next) { if (c->node->pkt->pkttype == PKT_PUBLIC_KEY || c->node->pkt->pkttype == PKT_PUBLIC_SUBKEY) { copy_public_key (sk, c->node->pkt->pkt.public_key); c->node = c->node->next; return 0; /* Found. */ } } /* Dispose the keyblock and continue. */ release_kbnode (c->keyblock); c->keyblock = NULL; } } /********************************************* *********** User ID printing helpers ******* *********************************************/ /* Return a string with a printable representation of the user_id. * this string must be freed by xfree. */ static char * get_user_id_string (u32 * keyid) { user_id_db_t r; int pass = 0; /* Try it two times; second pass reads from key resources. */ do { for (r = user_id_db; r; r = r->next) { keyid_list_t a; for (a = r->keyids; a; a = a->next) { if (a->keyid[0] == keyid[0] && a->keyid[1] == keyid[1]) { return xasprintf ("%s %.*s", keystr (keyid), r->len, r->name); } } } } while (++pass < 2 && !get_pubkey (NULL, keyid)); return xasprintf ("%s [?]", keystr (keyid)); } char * get_user_id_string_native (u32 * keyid) { char *p = get_user_id_string (keyid); char *p2 = utf8_to_native (p, strlen (p), 0); xfree (p); return p2; } char * get_long_user_id_string (u32 * keyid) { user_id_db_t r; keyid_list_t a; int pass = 0; /* Try it two times; second pass reads from key resources. */ do { for (r = user_id_db; r; r = r->next) { for (a = r->keyids; a; a = a->next) { if (a->keyid[0] == keyid[0] && a->keyid[1] == keyid[1]) { return xasprintf ("%08lX%08lX %.*s", (ulong) keyid[0], (ulong) keyid[1], r->len, r->name); } } } } while (++pass < 2 && !get_pubkey (NULL, keyid)); return xasprintf ("%08lX%08lX [?]", (ulong) keyid[0], (ulong) keyid[1]); } /* Please try to use get_user_id_native instead of this one. */ char * get_user_id (u32 * keyid, size_t * rn) { user_id_db_t r; char *p; int pass = 0; /* Try it two times; second pass reads from key resources. */ do { for (r = user_id_db; r; r = r->next) { keyid_list_t a; for (a = r->keyids; a; a = a->next) { if (a->keyid[0] == keyid[0] && a->keyid[1] == keyid[1]) { /* An empty string as user id is possible. Make sure that the malloc allocates one byte and does not bail out. */ p = xmalloc (r->len? r->len : 1); memcpy (p, r->name, r->len); *rn = r->len; return p; } } } } while (++pass < 2 && !get_pubkey (NULL, keyid)); p = xstrdup (user_id_not_found_utf8 ()); *rn = strlen (p); return p; } /* Please try to use get_user_id_byfpr_native instead of this one. */ char * get_user_id_native (u32 * keyid) { size_t rn; char *p = get_user_id (keyid, &rn); char *p2 = utf8_to_native (p, rn, 0); xfree (p); return p2; } /* Return a user id from the caching by looking it up using the FPR which mustbe of size MAX_FINGERPRINT_LEN. */ char * get_user_id_byfpr (const byte *fpr, size_t *rn) { user_id_db_t r; char *p; int pass = 0; /* Try it two times; second pass reads from key resources. */ do { for (r = user_id_db; r; r = r->next) { keyid_list_t a; for (a = r->keyids; a; a = a->next) { if (!memcmp (a->fpr, fpr, MAX_FINGERPRINT_LEN)) { /* An empty string as user id is possible. Make sure that the malloc allocates one byte and does not bail out. */ p = xmalloc (r->len? r->len : 1); memcpy (p, r->name, r->len); *rn = r->len; return p; } } } } while (++pass < 2 && !get_pubkey_byfpr (NULL, fpr)); p = xstrdup (user_id_not_found_utf8 ()); *rn = strlen (p); return p; } char * get_user_id_byfpr_native (const byte *fpr) { size_t rn; char *p = get_user_id_byfpr (fpr, &rn); char *p2 = utf8_to_native (p, rn, 0); xfree (p); return p2; } KEYDB_HANDLE get_ctx_handle (GETKEY_CTX ctx) { return ctx->kr_handle; } static void free_akl (struct akl *akl) { if (akl->spec) free_keyserver_spec (akl->spec); xfree (akl); } void release_akl (void) { while (opt.auto_key_locate) { struct akl *akl2 = opt.auto_key_locate; opt.auto_key_locate = opt.auto_key_locate->next; free_akl (akl2); } } /* Returns false on error. */ int parse_auto_key_locate (char *options) { char *tok; while ((tok = optsep (&options))) { struct akl *akl, *check, *last = NULL; int dupe = 0; if (tok[0] == '\0') continue; akl = xmalloc_clear (sizeof (*akl)); if (ascii_strcasecmp (tok, "clear") == 0) { xfree (akl); free_akl (opt.auto_key_locate); opt.auto_key_locate = NULL; continue; } else if (ascii_strcasecmp (tok, "nodefault") == 0) akl->type = AKL_NODEFAULT; else if (ascii_strcasecmp (tok, "local") == 0) akl->type = AKL_LOCAL; else if (ascii_strcasecmp (tok, "ldap") == 0) akl->type = AKL_LDAP; else if (ascii_strcasecmp (tok, "keyserver") == 0) akl->type = AKL_KEYSERVER; #ifdef USE_DNS_CERT else if (ascii_strcasecmp (tok, "cert") == 0) akl->type = AKL_CERT; #endif #ifdef USE_DNS_PKA else if (ascii_strcasecmp (tok, "pka") == 0) akl->type = AKL_PKA; #endif else if ((akl->spec = parse_keyserver_uri (tok, 1))) akl->type = AKL_SPEC; else { free_akl (akl); return 0; } /* We must maintain the order the user gave us */ for (check = opt.auto_key_locate; check; last = check, check = check->next) { /* Check for duplicates */ if (check->type == akl->type && (akl->type != AKL_SPEC || (akl->type == AKL_SPEC && strcmp (check->spec->uri, akl->spec->uri) == 0))) { dupe = 1; free_akl (akl); break; } } if (!dupe) { if (last) last->next = akl; else opt.auto_key_locate = akl; } } return 1; } /* Return true if a secret key or secret subkey is available for one of the public keys in KEYBLOCK. */ int have_any_secret_key (ctrl_t ctrl, kbnode_t keyblock) { kbnode_t node; for (node = keyblock; node; node = node->next) if ((node->pkt->pkttype == PKT_PUBLIC_KEY || node->pkt->pkttype == PKT_PUBLIC_SUBKEY) && !agent_probe_secret_key (ctrl, node->pkt->pkt.public_key)) return 1; return 0; } /* Return true if a secret key is available for the public key with * the given KEYID. This is just a fast check and does not tell us * whether the secret key is valid. It merely tells os whether there * is some secret key. */ int have_secret_key_with_kid (u32 *keyid) { gpg_error_t err; KEYDB_HANDLE kdbhd; KEYDB_SEARCH_DESC desc; kbnode_t keyblock; kbnode_t node; int result = 0; kdbhd = keydb_new (); memset (&desc, 0, sizeof desc); desc.mode = KEYDB_SEARCH_MODE_LONG_KID; desc.u.kid[0] = keyid[0]; desc.u.kid[1] = keyid[1]; while (!result && !(err = keydb_search (kdbhd, &desc, 1, NULL))) { err = keydb_get_keyblock (kdbhd, &keyblock); if (err) { log_error (_("error reading keyblock: %s\n"), gpg_strerror (err)); break; } for (node = keyblock; node; node = node->next) { /* Bit 0 of the flags is set if the search found the key using that key or subkey. */ if ((node->flag & 1)) { assert (node->pkt->pkttype == PKT_PUBLIC_KEY || node->pkt->pkttype == PKT_PUBLIC_SUBKEY); if (!agent_probe_secret_key (NULL, node->pkt->pkt.public_key)) { result = 1; break; } } } release_kbnode (keyblock); } keydb_release (kdbhd); return result; } diff --git a/g10/keygen.c b/g10/keygen.c index 078957108..11bfbd436 100644 --- a/g10/keygen.c +++ b/g10/keygen.c @@ -1,4782 +1,4781 @@ /* keygen.c - generate a key pair * Copyright (C) 1998-2007, 2009-2011 Free Software Foundation, Inc. * Copyright (C) 2014, 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 #include #include "gpg.h" #include "util.h" #include "main.h" #include "packet.h" #include "ttyio.h" #include "options.h" #include "keydb.h" #include "trustdb.h" #include "status.h" #include "i18n.h" #include "keyserver-internal.h" #include "call-agent.h" #include "pkglue.h" #include "../common/shareddefs.h" +#include "host2net.h" + /* The default algorithms. If you change them remember to change them also in gpg.c:gpgconf_list. You should also check that the value is inside the bounds enforced by ask_keysize and gen_xxx. */ #define DEFAULT_STD_ALGO PUBKEY_ALGO_RSA #define DEFAULT_STD_KEYSIZE 2048 #define DEFAULT_STD_CURVE NULL #define DEFAULT_STD_SUBALGO PUBKEY_ALGO_RSA #define DEFAULT_STD_SUBKEYSIZE 2048 #define DEFAULT_STD_SUBCURVE NULL /* Flag bits used during key generation. */ #define KEYGEN_FLAG_NO_PROTECTION 1 #define KEYGEN_FLAG_TRANSIENT_KEY 2 /* Maximum number of supported algorithm preferences. */ #define MAX_PREFS 30 enum para_name { pKEYTYPE, pKEYLENGTH, pKEYCURVE, pKEYUSAGE, pSUBKEYTYPE, pSUBKEYLENGTH, pSUBKEYCURVE, pSUBKEYUSAGE, pAUTHKEYTYPE, pNAMEREAL, pNAMEEMAIL, pNAMECOMMENT, pPREFERENCES, pREVOKER, pUSERID, pCREATIONDATE, pKEYCREATIONDATE, /* Same in seconds since epoch. */ pEXPIREDATE, pKEYEXPIRE, /* in n seconds */ pSUBKEYEXPIRE, /* in n seconds */ pPASSPHRASE, pSERIALNO, pCARDBACKUPKEY, pHANDLE, pKEYSERVER }; struct para_data_s { struct para_data_s *next; int lnr; enum para_name key; union { u32 expire; u32 creation; unsigned int usage; struct revocation_key revkey; char value[1]; } u; }; struct output_control_s { int lnr; int dryrun; unsigned int keygen_flags; int use_files; struct { char *fname; char *newfname; IOBUF stream; armor_filter_context_t *afx; } pub; }; struct opaque_data_usage_and_pk { unsigned int usage; PKT_public_key *pk; }; static int prefs_initialized = 0; static byte sym_prefs[MAX_PREFS]; static int nsym_prefs; static byte hash_prefs[MAX_PREFS]; static int nhash_prefs; static byte zip_prefs[MAX_PREFS]; static int nzip_prefs; static int mdc_available,ks_modify; static void do_generate_keypair( struct para_data_s *para, struct output_control_s *outctrl, int card ); static int write_keyblock (iobuf_t out, kbnode_t node); static gpg_error_t gen_card_key (int algo, int keyno, int is_primary, kbnode_t pub_root, u32 *timestamp, u32 expireval); static int gen_card_key_with_backup (int algo, int keyno, int is_primary, kbnode_t pub_root, u32 timestamp, u32 expireval, struct para_data_s *para); static void print_status_key_created (int letter, PKT_public_key *pk, const char *handle) { byte array[MAX_FINGERPRINT_LEN], *s; char *buf, *p; size_t i, n; if (!handle) handle = ""; buf = xmalloc (MAX_FINGERPRINT_LEN*2+31 + strlen (handle) + 1); p = buf; if (letter || pk) { *p++ = letter; *p++ = ' '; fingerprint_from_pk (pk, array, &n); s = array; for (i=0; i < n ; i++, s++, p += 2) sprintf (p, "%02X", *s); } if (*handle) { *p++ = ' '; for (i=0; handle[i] && i < 100; i++) *p++ = isspace ((unsigned int)handle[i])? '_':handle[i]; } *p = 0; write_status_text ((letter || pk)?STATUS_KEY_CREATED:STATUS_KEY_NOT_CREATED, buf); xfree (buf); } static void print_status_key_not_created (const char *handle) { print_status_key_created (0, NULL, handle); } static void write_uid( KBNODE root, const char *s ) { PACKET *pkt = xmalloc_clear(sizeof *pkt ); size_t n = strlen(s); pkt->pkttype = PKT_USER_ID; pkt->pkt.user_id = xmalloc_clear( sizeof *pkt->pkt.user_id + n - 1 ); pkt->pkt.user_id->len = n; pkt->pkt.user_id->ref = 1; strcpy(pkt->pkt.user_id->name, s); add_kbnode( root, new_kbnode( pkt ) ); } static void do_add_key_flags (PKT_signature *sig, unsigned int use) { byte buf[1]; buf[0] = 0; /* The spec says that all primary keys MUST be able to certify. */ if(sig->sig_class!=0x18) buf[0] |= 0x01; if (use & PUBKEY_USAGE_SIG) buf[0] |= 0x02; if (use & PUBKEY_USAGE_ENC) buf[0] |= 0x04 | 0x08; if (use & PUBKEY_USAGE_AUTH) buf[0] |= 0x20; build_sig_subpkt (sig, SIGSUBPKT_KEY_FLAGS, buf, 1); } int keygen_add_key_expire (PKT_signature *sig, void *opaque) { PKT_public_key *pk = opaque; byte buf[8]; u32 u; if (pk->expiredate) { if (pk->expiredate > pk->timestamp) u = pk->expiredate - pk->timestamp; else u = 1; buf[0] = (u >> 24) & 0xff; buf[1] = (u >> 16) & 0xff; buf[2] = (u >> 8) & 0xff; buf[3] = u & 0xff; build_sig_subpkt (sig, SIGSUBPKT_KEY_EXPIRE, buf, 4); } else { /* Make sure we don't leave a key expiration subpacket lying around */ delete_sig_subpkt (sig->hashed, SIGSUBPKT_KEY_EXPIRE); } return 0; } static int keygen_add_key_flags_and_expire (PKT_signature *sig, void *opaque) { struct opaque_data_usage_and_pk *oduap = opaque; do_add_key_flags (sig, oduap->usage); return keygen_add_key_expire (sig, oduap->pk); } static int set_one_pref (int val, int type, const char *item, byte *buf, int *nbuf) { int i; for (i=0; i < *nbuf; i++ ) if (buf[i] == val) { log_info (_("preference '%s' duplicated\n"), item); return -1; } if (*nbuf >= MAX_PREFS) { if(type==1) log_info(_("too many cipher preferences\n")); else if(type==2) log_info(_("too many digest preferences\n")); else if(type==3) log_info(_("too many compression preferences\n")); else BUG(); return -1; } buf[(*nbuf)++] = val; return 0; } /* * Parse the supplied string and use it to set the standard * preferences. The string may be in a form like the one printed by * "pref" (something like: "S10 S3 H3 H2 Z2 Z1") or the actual * cipher/hash/compress names. Use NULL to set the default * preferences. Returns: 0 = okay */ int keygen_set_std_prefs (const char *string,int personal) { byte sym[MAX_PREFS], hash[MAX_PREFS], zip[MAX_PREFS]; int nsym=0, nhash=0, nzip=0, val, rc=0; int mdc=1, modify=0; /* mdc defaults on, modify defaults off. */ char dummy_string[20*4+1]; /* Enough for 20 items. */ if (!string || !ascii_strcasecmp (string, "default")) { if (opt.def_preference_list) string=opt.def_preference_list; else { int any_compress = 0; dummy_string[0]='\0'; /* The rationale why we use the order AES256,192,128 is for compatibility reasons with PGP. If gpg would define AES128 first, we would get the somewhat confusing situation: gpg -r pgpkey -r gpgkey ---gives--> AES256 gpg -r gpgkey -r pgpkey ---gives--> AES Note that by using --personal-cipher-preferences it is possible to prefer AES128. */ /* Make sure we do not add more than 15 items here, as we could overflow the size of dummy_string. We currently have at most 12. */ if ( !openpgp_cipher_test_algo (CIPHER_ALGO_AES256) ) strcat(dummy_string,"S9 "); if ( !openpgp_cipher_test_algo (CIPHER_ALGO_AES192) ) strcat(dummy_string,"S8 "); if ( !openpgp_cipher_test_algo (CIPHER_ALGO_AES) ) strcat(dummy_string,"S7 "); strcat(dummy_string,"S2 "); /* 3DES */ /* The default hash algo order is: SHA-256, SHA-384, SHA-512, SHA-224, SHA-1. */ if (!openpgp_md_test_algo (DIGEST_ALGO_SHA256)) strcat (dummy_string, "H8 "); if (!openpgp_md_test_algo (DIGEST_ALGO_SHA384)) strcat (dummy_string, "H9 "); if (!openpgp_md_test_algo (DIGEST_ALGO_SHA512)) strcat (dummy_string, "H10 "); if (!openpgp_md_test_algo (DIGEST_ALGO_SHA224)) strcat (dummy_string, "H11 "); strcat (dummy_string, "H2 "); /* SHA-1 */ if(!check_compress_algo(COMPRESS_ALGO_ZLIB)) { strcat(dummy_string,"Z2 "); any_compress = 1; } if(!check_compress_algo(COMPRESS_ALGO_BZIP2)) { strcat(dummy_string,"Z3 "); any_compress = 1; } if(!check_compress_algo(COMPRESS_ALGO_ZIP)) { strcat(dummy_string,"Z1 "); any_compress = 1; } /* In case we have no compress algo at all, declare that we prefer no compresssion. */ if (!any_compress) strcat(dummy_string,"Z0 "); /* Remove the trailing space. */ if (*dummy_string && dummy_string[strlen (dummy_string)-1] == ' ') dummy_string[strlen (dummy_string)-1] = 0; string=dummy_string; } } else if (!ascii_strcasecmp (string, "none")) string = ""; if(strlen(string)) { char *tok,*prefstring; prefstring=xstrdup(string); /* need a writable string! */ while((tok=strsep(&prefstring," ,"))) { if((val=string_to_cipher_algo (tok))) { if(set_one_pref(val,1,tok,sym,&nsym)) rc=-1; } else if((val=string_to_digest_algo (tok))) { if(set_one_pref(val,2,tok,hash,&nhash)) rc=-1; } else if((val=string_to_compress_algo(tok))>-1) { if(set_one_pref(val,3,tok,zip,&nzip)) rc=-1; } else if (ascii_strcasecmp(tok,"mdc")==0) mdc=1; else if (ascii_strcasecmp(tok,"no-mdc")==0) mdc=0; else if (ascii_strcasecmp(tok,"ks-modify")==0) modify=1; else if (ascii_strcasecmp(tok,"no-ks-modify")==0) modify=0; else { log_info (_("invalid item '%s' in preference string\n"),tok); rc=-1; } } xfree(prefstring); } if(!rc) { if(personal) { if(personal==PREFTYPE_SYM) { xfree(opt.personal_cipher_prefs); if(nsym==0) opt.personal_cipher_prefs=NULL; else { int i; opt.personal_cipher_prefs= xmalloc(sizeof(prefitem_t *)*(nsym+1)); for (i=0; iref=1; uid->prefs=xmalloc((sizeof(prefitem_t *)* (nsym_prefs+nhash_prefs+nzip_prefs+1))); for(i=0;iprefs[j].type=PREFTYPE_SYM; uid->prefs[j].value=sym_prefs[i]; } for(i=0;iprefs[j].type=PREFTYPE_HASH; uid->prefs[j].value=hash_prefs[i]; } for(i=0;iprefs[j].type=PREFTYPE_ZIP; uid->prefs[j].value=zip_prefs[i]; } uid->prefs[j].type=PREFTYPE_NONE; uid->prefs[j].value=0; uid->flags.mdc=mdc_available; uid->flags.ks_modify=ks_modify; return uid; } static void add_feature_mdc (PKT_signature *sig,int enabled) { const byte *s; size_t n; int i; char *buf; s = parse_sig_subpkt (sig->hashed, SIGSUBPKT_FEATURES, &n ); /* Already set or cleared */ if (s && n && ((enabled && (s[0] & 0x01)) || (!enabled && !(s[0] & 0x01)))) return; if (!s || !n) { /* create a new one */ n = 1; buf = xmalloc_clear (n); } else { buf = xmalloc (n); memcpy (buf, s, n); } if(enabled) buf[0] |= 0x01; /* MDC feature */ else buf[0] &= ~0x01; /* Are there any bits set? */ for(i=0;ihashed, SIGSUBPKT_FEATURES); else build_sig_subpkt (sig, SIGSUBPKT_FEATURES, buf, n); xfree (buf); } static void add_keyserver_modify (PKT_signature *sig,int enabled) { const byte *s; size_t n; int i; char *buf; /* The keyserver modify flag is a negative flag (i.e. no-modify) */ enabled=!enabled; s = parse_sig_subpkt (sig->hashed, SIGSUBPKT_KS_FLAGS, &n ); /* Already set or cleared */ if (s && n && ((enabled && (s[0] & 0x80)) || (!enabled && !(s[0] & 0x80)))) return; if (!s || !n) { /* create a new one */ n = 1; buf = xmalloc_clear (n); } else { buf = xmalloc (n); memcpy (buf, s, n); } if(enabled) buf[0] |= 0x80; /* no-modify flag */ else buf[0] &= ~0x80; /* Are there any bits set? */ for(i=0;ihashed, SIGSUBPKT_KS_FLAGS); else build_sig_subpkt (sig, SIGSUBPKT_KS_FLAGS, buf, n); xfree (buf); } int keygen_upd_std_prefs (PKT_signature *sig, void *opaque) { (void)opaque; if (!prefs_initialized) keygen_set_std_prefs (NULL, 0); if (nsym_prefs) build_sig_subpkt (sig, SIGSUBPKT_PREF_SYM, sym_prefs, nsym_prefs); else { delete_sig_subpkt (sig->hashed, SIGSUBPKT_PREF_SYM); delete_sig_subpkt (sig->unhashed, SIGSUBPKT_PREF_SYM); } if (nhash_prefs) build_sig_subpkt (sig, SIGSUBPKT_PREF_HASH, hash_prefs, nhash_prefs); else { delete_sig_subpkt (sig->hashed, SIGSUBPKT_PREF_HASH); delete_sig_subpkt (sig->unhashed, SIGSUBPKT_PREF_HASH); } if (nzip_prefs) build_sig_subpkt (sig, SIGSUBPKT_PREF_COMPR, zip_prefs, nzip_prefs); else { delete_sig_subpkt (sig->hashed, SIGSUBPKT_PREF_COMPR); delete_sig_subpkt (sig->unhashed, SIGSUBPKT_PREF_COMPR); } /* Make sure that the MDC feature flag is set if needed. */ add_feature_mdc (sig,mdc_available); add_keyserver_modify (sig,ks_modify); keygen_add_keyserver_url(sig,NULL); return 0; } /**************** * Add preference to the self signature packet. * This is only called for packets with version > 3. */ int keygen_add_std_prefs (PKT_signature *sig, void *opaque) { PKT_public_key *pk = opaque; do_add_key_flags (sig, pk->pubkey_usage); keygen_add_key_expire (sig, opaque ); keygen_upd_std_prefs (sig, opaque); keygen_add_keyserver_url (sig,NULL); return 0; } int keygen_add_keyserver_url(PKT_signature *sig, void *opaque) { const char *url=opaque; if(!url) url=opt.def_keyserver_url; if(url) build_sig_subpkt(sig,SIGSUBPKT_PREF_KS,url,strlen(url)); else delete_sig_subpkt (sig->hashed,SIGSUBPKT_PREF_KS); return 0; } int keygen_add_notations(PKT_signature *sig,void *opaque) { struct notation *notation; /* We always start clean */ delete_sig_subpkt(sig->hashed,SIGSUBPKT_NOTATION); delete_sig_subpkt(sig->unhashed,SIGSUBPKT_NOTATION); sig->flags.notation=0; for(notation=opaque;notation;notation=notation->next) if(!notation->flags.ignore) { unsigned char *buf; unsigned int n1,n2; n1=strlen(notation->name); if(notation->altvalue) n2=strlen(notation->altvalue); else if(notation->bdat) n2=notation->blen; else n2=strlen(notation->value); buf = xmalloc( 8 + n1 + n2 ); /* human readable or not */ buf[0] = notation->bdat?0:0x80; buf[1] = buf[2] = buf[3] = 0; buf[4] = n1 >> 8; buf[5] = n1; buf[6] = n2 >> 8; buf[7] = n2; memcpy(buf+8, notation->name, n1 ); if(notation->altvalue) memcpy(buf+8+n1, notation->altvalue, n2 ); else if(notation->bdat) memcpy(buf+8+n1, notation->bdat, n2 ); else memcpy(buf+8+n1, notation->value, n2 ); build_sig_subpkt( sig, SIGSUBPKT_NOTATION | (notation->flags.critical?SIGSUBPKT_FLAG_CRITICAL:0), buf, 8+n1+n2 ); xfree(buf); } return 0; } int keygen_add_revkey (PKT_signature *sig, void *opaque) { struct revocation_key *revkey = opaque; byte buf[2+MAX_FINGERPRINT_LEN]; buf[0] = revkey->class; buf[1] = revkey->algid; memcpy (&buf[2], revkey->fpr, MAX_FINGERPRINT_LEN); build_sig_subpkt (sig, SIGSUBPKT_REV_KEY, buf, 2+MAX_FINGERPRINT_LEN); /* All sigs with revocation keys set are nonrevocable. */ sig->flags.revocable = 0; buf[0] = 0; build_sig_subpkt (sig, SIGSUBPKT_REVOCABLE, buf, 1); parse_revkeys (sig); return 0; } /* Create a back-signature. If TIMESTAMP is not NULL, use it for the signature creation time. */ gpg_error_t make_backsig (PKT_signature *sig, PKT_public_key *pk, PKT_public_key *sub_pk, PKT_public_key *sub_psk, u32 timestamp, const char *cache_nonce) { gpg_error_t err; PKT_signature *backsig; cache_public_key (sub_pk); err = make_keysig_packet (&backsig, pk, NULL, sub_pk, sub_psk, 0x19, 0, timestamp, 0, NULL, NULL, cache_nonce); if (err) log_error ("make_keysig_packet failed for backsig: %s\n", gpg_strerror (err)); else { /* Get it into a binary packed form. */ IOBUF backsig_out = iobuf_temp(); PACKET backsig_pkt; init_packet (&backsig_pkt); backsig_pkt.pkttype = PKT_SIGNATURE; backsig_pkt.pkt.signature = backsig; err = build_packet (backsig_out, &backsig_pkt); free_packet (&backsig_pkt); if (err) log_error ("build_packet failed for backsig: %s\n", gpg_strerror (err)); else { size_t pktlen = 0; byte *buf = iobuf_get_temp_buffer (backsig_out); /* Remove the packet header. */ if(buf[0]&0x40) { if (buf[1] < 192) { pktlen = buf[1]; buf += 2; } else if(buf[1] < 224) { pktlen = (buf[1]-192)*256; pktlen += buf[2]+192; buf += 3; } else if (buf[1] == 255) { - pktlen = buf[2] << 24; - pktlen |= buf[3] << 16; - pktlen |= buf[4] << 8; - pktlen |= buf[5]; + pktlen = buf32_to_size_t (buf+2); buf += 6; } else BUG (); } else { int mark = 1; switch (buf[0]&3) { case 3: BUG (); break; case 2: - pktlen = buf[mark++] << 24; + pktlen = (size_t)buf[mark++] << 24; pktlen |= buf[mark++] << 16; case 1: pktlen |= buf[mark++] << 8; case 0: pktlen |= buf[mark++]; } buf += mark; } /* Now make the binary blob into a subpacket. */ build_sig_subpkt (sig, SIGSUBPKT_SIGNATURE, buf, pktlen); iobuf_close (backsig_out); } } return err; } /* Write a direct key signature to the first key in ROOT using the key PSK. REVKEY is describes the direct key signature and TIMESTAMP is the timestamp to set on the signature. */ static gpg_error_t write_direct_sig (KBNODE root, PKT_public_key *psk, struct revocation_key *revkey, u32 timestamp, const char *cache_nonce) { gpg_error_t err; PACKET *pkt; PKT_signature *sig; KBNODE node; PKT_public_key *pk; if (opt.verbose) log_info (_("writing direct signature\n")); /* Get the pk packet from the pub_tree. */ node = find_kbnode (root, PKT_PUBLIC_KEY); if (!node) BUG (); pk = node->pkt->pkt.public_key; /* We have to cache the key, so that the verification of the signature creation is able to retrieve the public key. */ cache_public_key (pk); /* Make the signature. */ err = make_keysig_packet (&sig, pk, NULL,NULL, psk, 0x1F, 0, timestamp, 0, keygen_add_revkey, revkey, cache_nonce); if (err) { log_error ("make_keysig_packet failed: %s\n", gpg_strerror (err) ); return err; } pkt = xmalloc_clear (sizeof *pkt); pkt->pkttype = PKT_SIGNATURE; pkt->pkt.signature = sig; add_kbnode (root, new_kbnode (pkt)); return err; } /* Write a self-signature to the first user id in ROOT using the key PSK. USE and TIMESTAMP give the extra data we need for the signature. */ static gpg_error_t write_selfsigs (KBNODE root, PKT_public_key *psk, unsigned int use, u32 timestamp, const char *cache_nonce) { gpg_error_t err; PACKET *pkt; PKT_signature *sig; PKT_user_id *uid; KBNODE node; PKT_public_key *pk; if (opt.verbose) log_info (_("writing self signature\n")); /* Get the uid packet from the list. */ node = find_kbnode (root, PKT_USER_ID); if (!node) BUG(); /* No user id packet in tree. */ uid = node->pkt->pkt.user_id; /* Get the pk packet from the pub_tree. */ node = find_kbnode (root, PKT_PUBLIC_KEY); if (!node) BUG(); pk = node->pkt->pkt.public_key; /* The usage has not yet been set - do it now. */ pk->pubkey_usage = use; /* We have to cache the key, so that the verification of the signature creation is able to retrieve the public key. */ cache_public_key (pk); /* Make the signature. */ err = make_keysig_packet (&sig, pk, uid, NULL, psk, 0x13, 0, timestamp, 0, keygen_add_std_prefs, pk, cache_nonce); if (err) { log_error ("make_keysig_packet failed: %s\n", gpg_strerror (err)); return err; } pkt = xmalloc_clear (sizeof *pkt); pkt->pkttype = PKT_SIGNATURE; pkt->pkt.signature = sig; add_kbnode (root, new_kbnode (pkt)); return err; } /* Write the key binding signature. If TIMESTAMP is not NULL use the signature creation time. PRI_PSK is the key use for signing. SUB_PSK is a key used to create a back-signature; that one is only used if USE has the PUBKEY_USAGE_SIG capability. */ static int write_keybinding (KBNODE root, PKT_public_key *pri_psk, PKT_public_key *sub_psk, unsigned int use, u32 timestamp, const char *cache_nonce) { gpg_error_t err; PACKET *pkt; PKT_signature *sig; KBNODE node; PKT_public_key *pri_pk, *sub_pk; struct opaque_data_usage_and_pk oduap; if (opt.verbose) log_info(_("writing key binding signature\n")); /* Get the primary pk packet from the tree. */ node = find_kbnode (root, PKT_PUBLIC_KEY); if (!node) BUG(); pri_pk = node->pkt->pkt.public_key; /* We have to cache the key, so that the verification of the * signature creation is able to retrieve the public key. */ cache_public_key (pri_pk); /* Find the last subkey. */ sub_pk = NULL; for (node = root; node; node = node->next ) { if (node->pkt->pkttype == PKT_PUBLIC_SUBKEY) sub_pk = node->pkt->pkt.public_key; } if (!sub_pk) BUG(); /* Make the signature. */ oduap.usage = use; oduap.pk = sub_pk; err = make_keysig_packet (&sig, pri_pk, NULL, sub_pk, pri_psk, 0x18, 0, timestamp, 0, keygen_add_key_flags_and_expire, &oduap, cache_nonce); if (err) { log_error ("make_keysig_packeto failed: %s\n", gpg_strerror (err)); return err; } /* Make a backsig. */ if (use & PUBKEY_USAGE_SIG) { err = make_backsig (sig, pri_pk, sub_pk, sub_psk, timestamp, cache_nonce); if (err) return err; } pkt = xmalloc_clear ( sizeof *pkt ); pkt->pkttype = PKT_SIGNATURE; pkt->pkt.signature = sig; add_kbnode (root, new_kbnode (pkt) ); return err; } static gpg_error_t ecckey_from_sexp (gcry_mpi_t *array, gcry_sexp_t sexp, int algo) { gpg_error_t err; gcry_sexp_t list, l2; char *curve; int i; const char *oidstr; unsigned int nbits; array[0] = NULL; array[1] = NULL; array[2] = NULL; list = gcry_sexp_find_token (sexp, "public-key", 0); if (!list) return gpg_error (GPG_ERR_INV_OBJ); l2 = gcry_sexp_cadr (list); gcry_sexp_release (list); list = l2; if (!list) return gpg_error (GPG_ERR_NO_OBJ); l2 = gcry_sexp_find_token (list, "curve", 0); if (!l2) { err = gpg_error (GPG_ERR_NO_OBJ); goto leave; } curve = gcry_sexp_nth_string (l2, 1); if (!curve) { err = gpg_error (GPG_ERR_NO_OBJ); goto leave; } gcry_sexp_release (l2); oidstr = openpgp_curve_to_oid (curve, &nbits); if (!oidstr) { /* That can't happen because we used one of the curves gpg_curve_to_oid knows about. */ err = gpg_error (GPG_ERR_INV_OBJ); goto leave; } err = openpgp_oid_from_str (oidstr, &array[0]); if (err) goto leave; l2 = gcry_sexp_find_token (list, "q", 0); if (!l2) { err = gpg_error (GPG_ERR_NO_OBJ); goto leave; } array[1] = gcry_sexp_nth_mpi (l2, 1, GCRYMPI_FMT_USG); gcry_sexp_release (l2); if (!array[1]) { err = gpg_error (GPG_ERR_INV_OBJ); goto leave; } gcry_sexp_release (list); if (algo == PUBKEY_ALGO_ECDH) { array[2] = pk_ecdh_default_params (nbits); if (!array[2]) { err = gpg_error_from_syserror (); goto leave; } } leave: if (err) { for (i=0; i < 3; i++) { gcry_mpi_release (array[i]); array[i] = NULL; } } return err; } /* Extract key parameters from SEXP and store them in ARRAY. ELEMS is a string where each character denotes a parameter name. TOPNAME is the name of the top element above the elements. */ static int key_from_sexp (gcry_mpi_t *array, gcry_sexp_t sexp, const char *topname, const char *elems) { gcry_sexp_t list, l2; const char *s; int i, idx; int rc = 0; list = gcry_sexp_find_token (sexp, topname, 0); if (!list) return gpg_error (GPG_ERR_INV_OBJ); l2 = gcry_sexp_cadr (list); gcry_sexp_release (list); list = l2; if (!list) return gpg_error (GPG_ERR_NO_OBJ); for (idx=0,s=elems; *s; s++, idx++) { l2 = gcry_sexp_find_token (list, s, 1); if (!l2) { rc = gpg_error (GPG_ERR_NO_OBJ); /* required parameter not found */ goto leave; } array[idx] = gcry_sexp_nth_mpi (l2, 1, GCRYMPI_FMT_USG); gcry_sexp_release (l2); if (!array[idx]) { rc = gpg_error (GPG_ERR_INV_OBJ); /* required parameter invalid */ goto leave; } } gcry_sexp_release (list); leave: if (rc) { for (i=0; itimestamp = timestamp; pk->version = 4; if (expireval) pk->expiredate = pk->timestamp + expireval; pk->pubkey_algo = algo; if (algo == PUBKEY_ALGO_ECDSA || algo == PUBKEY_ALGO_EDDSA || algo == PUBKEY_ALGO_ECDH ) err = ecckey_from_sexp (pk->pkey, s_key, algo); else err = key_from_sexp (pk->pkey, s_key, "public-key", algoelem); if (err) { log_error ("key_from_sexp failed: %s\n", gpg_strerror (err) ); gcry_sexp_release (s_key); free_public_key (pk); return err; } gcry_sexp_release (s_key); pkt = xtrycalloc (1, sizeof *pkt); if (!pkt) { err = gpg_error_from_syserror (); free_public_key (pk); return err; } pkt->pkttype = is_subkey ? PKT_PUBLIC_SUBKEY : PKT_PUBLIC_KEY; pkt->pkt.public_key = pk; add_kbnode (pub_root, new_kbnode (pkt)); return 0; } /* Common code for the key generation fucntion gen_xxx. */ static int common_gen (const char *keyparms, int algo, const char *algoelem, kbnode_t pub_root, u32 timestamp, u32 expireval, int is_subkey, int keygen_flags, const char *passphrase, char **cache_nonce_addr) { int err; PACKET *pkt; PKT_public_key *pk; gcry_sexp_t s_key; err = agent_genkey (NULL, cache_nonce_addr, keyparms, !!(keygen_flags & KEYGEN_FLAG_NO_PROTECTION), passphrase, &s_key); if (err) { log_error ("agent_genkey failed: %s\n", gpg_strerror (err) ); return err; } pk = xtrycalloc (1, sizeof *pk); if (!pk) { err = gpg_error_from_syserror (); gcry_sexp_release (s_key); return err; } pk->timestamp = timestamp; pk->version = 4; if (expireval) pk->expiredate = pk->timestamp + expireval; pk->pubkey_algo = algo; if (algo == PUBKEY_ALGO_ECDSA || algo == PUBKEY_ALGO_EDDSA || algo == PUBKEY_ALGO_ECDH ) err = ecckey_from_sexp (pk->pkey, s_key, algo); else err = key_from_sexp (pk->pkey, s_key, "public-key", algoelem); if (err) { log_error ("key_from_sexp failed: %s\n", gpg_strerror (err) ); gcry_sexp_release (s_key); free_public_key (pk); return err; } gcry_sexp_release (s_key); pkt = xtrycalloc (1, sizeof *pkt); if (!pkt) { err = gpg_error_from_syserror (); free_public_key (pk); return err; } pkt->pkttype = is_subkey ? PKT_PUBLIC_SUBKEY : PKT_PUBLIC_KEY; pkt->pkt.public_key = pk; add_kbnode (pub_root, new_kbnode (pkt)); return 0; } /* * Generate an Elgamal key. */ static int gen_elg (int algo, unsigned int nbits, KBNODE pub_root, u32 timestamp, u32 expireval, int is_subkey, int keygen_flags, const char *passphrase, char **cache_nonce_addr) { int err; char *keyparms; char nbitsstr[35]; assert (is_ELGAMAL (algo)); if (nbits < 1024) { nbits = 2048; log_info (_("keysize invalid; using %u bits\n"), nbits ); } else if (nbits > 4096) { nbits = 4096; log_info (_("keysize invalid; using %u bits\n"), nbits ); } if ((nbits % 32)) { nbits = ((nbits + 31) / 32) * 32; log_info (_("keysize rounded up to %u bits\n"), nbits ); } /* Note that we use transient-key only if no-protection has also been enabled. */ snprintf (nbitsstr, sizeof nbitsstr, "%u", nbits); keyparms = xtryasprintf ("(genkey(%s(nbits %zu:%s)%s))", algo == GCRY_PK_ELG_E ? "openpgp-elg" : algo == GCRY_PK_ELG ? "elg" : "x-oops" , strlen (nbitsstr), nbitsstr, ((keygen_flags & KEYGEN_FLAG_TRANSIENT_KEY) && (keygen_flags & KEYGEN_FLAG_NO_PROTECTION))? "(transient-key)" : "" ); if (!keyparms) err = gpg_error_from_syserror (); else { err = common_gen (keyparms, algo, "pgy", pub_root, timestamp, expireval, is_subkey, keygen_flags, passphrase, cache_nonce_addr); xfree (keyparms); } return err; } /* * Generate an DSA key */ static gpg_error_t gen_dsa (unsigned int nbits, KBNODE pub_root, u32 timestamp, u32 expireval, int is_subkey, int keygen_flags, const char *passphrase, char **cache_nonce_addr) { int err; unsigned int qbits; char *keyparms; char nbitsstr[35]; char qbitsstr[35]; if (nbits < 768) { nbits = 2048; log_info(_("keysize invalid; using %u bits\n"), nbits ); } else if ( nbits > 3072 ) { nbits = 3072; log_info(_("keysize invalid; using %u bits\n"), nbits ); } if( (nbits % 64) ) { nbits = ((nbits + 63) / 64) * 64; log_info(_("keysize rounded up to %u bits\n"), nbits ); } /* To comply with FIPS rules we round up to the next value unless in expert mode. */ if (!opt.expert && nbits > 1024 && (nbits % 1024)) { nbits = ((nbits + 1023) / 1024) * 1024; log_info(_("keysize rounded up to %u bits\n"), nbits ); } /* Figure out a q size based on the key size. FIPS 180-3 says: L = 1024, N = 160 L = 2048, N = 224 L = 2048, N = 256 L = 3072, N = 256 2048/256 is an odd pair since there is also a 2048/224 and 3072/256. Matching sizes is not a very exact science. We'll do 256 qbits for nbits over 2047, 224 for nbits over 1024 but less than 2048, and 160 for 1024 (DSA1). */ if (nbits > 2047) qbits = 256; else if ( nbits > 1024) qbits = 224; else qbits = 160; if (qbits != 160 ) log_info (_("WARNING: some OpenPGP programs can't" " handle a DSA key with this digest size\n")); snprintf (nbitsstr, sizeof nbitsstr, "%u", nbits); snprintf (qbitsstr, sizeof qbitsstr, "%u", qbits); keyparms = xtryasprintf ("(genkey(dsa(nbits %zu:%s)(qbits %zu:%s)%s))", strlen (nbitsstr), nbitsstr, strlen (qbitsstr), qbitsstr, ((keygen_flags & KEYGEN_FLAG_TRANSIENT_KEY) && (keygen_flags & KEYGEN_FLAG_NO_PROTECTION))? "(transient-key)" : "" ); if (!keyparms) err = gpg_error_from_syserror (); else { err = common_gen (keyparms, PUBKEY_ALGO_DSA, "pqgy", pub_root, timestamp, expireval, is_subkey, keygen_flags, passphrase, cache_nonce_addr); xfree (keyparms); } return err; } /* * Generate an ECC key */ static gpg_error_t gen_ecc (int algo, const char *curve, kbnode_t pub_root, u32 timestamp, u32 expireval, int is_subkey, int keygen_flags, const char *passphrase, char **cache_nonce_addr) { gpg_error_t err; char *keyparms; assert (algo == PUBKEY_ALGO_ECDSA || algo == PUBKEY_ALGO_EDDSA || algo == PUBKEY_ALGO_ECDH); if (!curve || !*curve) return gpg_error (GPG_ERR_UNKNOWN_CURVE); /* Note that we use the "comp" flag with EdDSA to request the use of a 0x40 compression prefix octet. */ if (algo == PUBKEY_ALGO_EDDSA) keyparms = xtryasprintf ("(genkey(ecc(curve %zu:%s)(flags eddsa comp%s)))", strlen (curve), curve, (((keygen_flags & KEYGEN_FLAG_TRANSIENT_KEY) && (keygen_flags & KEYGEN_FLAG_NO_PROTECTION))? " transient-key" : "")); else keyparms = xtryasprintf ("(genkey(ecc(curve %zu:%s)(flags nocomp%s)))", strlen (curve), curve, (((keygen_flags & KEYGEN_FLAG_TRANSIENT_KEY) && (keygen_flags & KEYGEN_FLAG_NO_PROTECTION))? " transient-key" : "")); if (!keyparms) err = gpg_error_from_syserror (); else { err = common_gen (keyparms, algo, "", pub_root, timestamp, expireval, is_subkey, keygen_flags, passphrase, cache_nonce_addr); xfree (keyparms); } return err; } /* * Generate an RSA key. */ static int gen_rsa (int algo, unsigned int nbits, KBNODE pub_root, u32 timestamp, u32 expireval, int is_subkey, int keygen_flags, const char *passphrase, char **cache_nonce_addr) { int err; char *keyparms; char nbitsstr[35]; const unsigned maxsize = (opt.flags.large_rsa ? 8192 : 4096); assert (is_RSA(algo)); if (!nbits) nbits = DEFAULT_STD_KEYSIZE; if (nbits < 1024) { nbits = 2048; log_info (_("keysize invalid; using %u bits\n"), nbits ); } else if (nbits > maxsize) { nbits = maxsize; log_info (_("keysize invalid; using %u bits\n"), nbits ); } if ((nbits % 32)) { nbits = ((nbits + 31) / 32) * 32; log_info (_("keysize rounded up to %u bits\n"), nbits ); } snprintf (nbitsstr, sizeof nbitsstr, "%u", nbits); keyparms = xtryasprintf ("(genkey(rsa(nbits %zu:%s)%s))", strlen (nbitsstr), nbitsstr, ((keygen_flags & KEYGEN_FLAG_TRANSIENT_KEY) && (keygen_flags & KEYGEN_FLAG_NO_PROTECTION))? "(transient-key)" : "" ); if (!keyparms) err = gpg_error_from_syserror (); else { err = common_gen (keyparms, algo, "ne", pub_root, timestamp, expireval, is_subkey, keygen_flags, passphrase, cache_nonce_addr); xfree (keyparms); } return err; } /**************** * check valid days: * return 0 on error or the multiplier */ static int check_valid_days( const char *s ) { if( !digitp(s) ) return 0; for( s++; *s; s++) if( !digitp(s) ) break; if( !*s ) return 1; if( s[1] ) return 0; /* e.g. "2323wc" */ if( *s == 'd' || *s == 'D' ) return 1; if( *s == 'w' || *s == 'W' ) return 7; if( *s == 'm' || *s == 'M' ) return 30; if( *s == 'y' || *s == 'Y' ) return 365; return 0; } static void print_key_flags(int flags) { if(flags&PUBKEY_USAGE_SIG) tty_printf("%s ",_("Sign")); if(flags&PUBKEY_USAGE_CERT) tty_printf("%s ",_("Certify")); if(flags&PUBKEY_USAGE_ENC) tty_printf("%s ",_("Encrypt")); if(flags&PUBKEY_USAGE_AUTH) tty_printf("%s ",_("Authenticate")); } /* Returns the key flags */ static unsigned int ask_key_flags(int algo,int subkey) { /* TRANSLATORS: Please use only plain ASCII characters for the translation. If this is not possible use single digits. The string needs to 8 bytes long. Here is a description of the functions: s = Toggle signing capability e = Toggle encryption capability a = Toggle authentication capability q = Finish */ const char *togglers=_("SsEeAaQq"); char *answer=NULL; const char *s; unsigned int current=0; unsigned int possible=openpgp_pk_algo_usage(algo); if ( strlen(togglers) != 8 ) { tty_printf ("NOTE: Bad translation at %s:%d. " "Please report.\n", __FILE__, __LINE__); togglers = "11223300"; } /* Only primary keys may certify. */ if(subkey) possible&=~PUBKEY_USAGE_CERT; /* Preload the current set with the possible set, minus authentication, since nobody really uses auth yet. */ current=possible&~PUBKEY_USAGE_AUTH; for(;;) { tty_printf("\n"); tty_printf(_("Possible actions for a %s key: "), openpgp_pk_algo_name (algo)); print_key_flags(possible); tty_printf("\n"); tty_printf(_("Current allowed actions: ")); print_key_flags(current); tty_printf("\n\n"); if(possible&PUBKEY_USAGE_SIG) tty_printf(_(" (%c) Toggle the sign capability\n"), togglers[0]); if(possible&PUBKEY_USAGE_ENC) tty_printf(_(" (%c) Toggle the encrypt capability\n"), togglers[2]); if(possible&PUBKEY_USAGE_AUTH) tty_printf(_(" (%c) Toggle the authenticate capability\n"), togglers[4]); tty_printf(_(" (%c) Finished\n"),togglers[6]); tty_printf("\n"); xfree(answer); answer = cpr_get("keygen.flags",_("Your selection? ")); cpr_kill_prompt(); if (*answer == '=') { /* Hack to allow direct entry of the capabilities. */ current = 0; for (s=answer+1; *s; s++) { if ((*s == 's' || *s == 'S') && (possible&PUBKEY_USAGE_SIG)) current |= PUBKEY_USAGE_SIG; else if ((*s == 'e' || *s == 'E') && (possible&PUBKEY_USAGE_ENC)) current |= PUBKEY_USAGE_ENC; else if ((*s == 'a' || *s == 'A') && (possible&PUBKEY_USAGE_AUTH)) current |= PUBKEY_USAGE_AUTH; else if (!subkey && *s == 'c') { /* Accept 'c' for the primary key because USAGE_CERT will will be set anyway. This is for folks who want to experiment with a cert-only primary key. */ current |= PUBKEY_USAGE_CERT; } } break; } else if (strlen(answer)>1) tty_printf(_("Invalid selection.\n")); else if(*answer=='\0' || *answer==togglers[6] || *answer==togglers[7]) break; else if((*answer==togglers[0] || *answer==togglers[1]) && possible&PUBKEY_USAGE_SIG) { if(current&PUBKEY_USAGE_SIG) current&=~PUBKEY_USAGE_SIG; else current|=PUBKEY_USAGE_SIG; } else if((*answer==togglers[2] || *answer==togglers[3]) && possible&PUBKEY_USAGE_ENC) { if(current&PUBKEY_USAGE_ENC) current&=~PUBKEY_USAGE_ENC; else current|=PUBKEY_USAGE_ENC; } else if((*answer==togglers[4] || *answer==togglers[5]) && possible&PUBKEY_USAGE_AUTH) { if(current&PUBKEY_USAGE_AUTH) current&=~PUBKEY_USAGE_AUTH; else current|=PUBKEY_USAGE_AUTH; } else tty_printf(_("Invalid selection.\n")); } xfree(answer); return current; } /* Check whether we have a key for the key with HEXGRIP. Returns 0 if there is no such key or the OpenPGP algo number for the key. */ static int check_keygrip (ctrl_t ctrl, const char *hexgrip) { gpg_error_t err; unsigned char *public; size_t publiclen; const char *algostr; if (hexgrip[0] == '&') hexgrip++; err = agent_readkey (ctrl, 0, hexgrip, &public); if (err) return 0; publiclen = gcry_sexp_canon_len (public, 0, NULL, NULL); get_pk_algo_from_canon_sexp (public, publiclen, &algostr); xfree (public); /* FIXME: Mapping of ECC algorithms is probably not correct. */ if (!algostr) return 0; else if (!strcmp (algostr, "rsa")) return PUBKEY_ALGO_RSA; else if (!strcmp (algostr, "dsa")) return PUBKEY_ALGO_DSA; else if (!strcmp (algostr, "elg")) return PUBKEY_ALGO_ELGAMAL_E; else if (!strcmp (algostr, "ecc")) return PUBKEY_ALGO_ECDH; else if (!strcmp (algostr, "ecdsa")) return PUBKEY_ALGO_ECDSA; else if (!strcmp (algostr, "eddsa")) return PUBKEY_ALGO_EDDSA; else return 0; } /* Ask for an algorithm. The function returns the algorithm id to * create. If ADDMODE is false the function won't show an option to * create the primary and subkey combined and won't set R_USAGE * either. If a combined algorithm has been selected, the subkey * algorithm is stored at R_SUBKEY_ALGO. If R_KEYGRIP is given, the * user has the choice to enter the keygrip of an existing key. That * keygrip is then stored at this address. The caller needs to free * it. */ static int ask_algo (ctrl_t ctrl, int addmode, int *r_subkey_algo, unsigned int *r_usage, char **r_keygrip) { char *keygrip = NULL; char *answer = NULL; int algo; int dummy_algo; if (!r_subkey_algo) r_subkey_algo = &dummy_algo; tty_printf (_("Please select what kind of key you want:\n")); #if GPG_USE_RSA if (!addmode) tty_printf (_(" (%d) RSA and RSA (default)\n"), 1 ); #endif if (!addmode) tty_printf (_(" (%d) DSA and Elgamal\n"), 2 ); tty_printf (_(" (%d) DSA (sign only)\n"), 3 ); #if GPG_USE_RSA tty_printf (_(" (%d) RSA (sign only)\n"), 4 ); #endif if (addmode) { tty_printf (_(" (%d) Elgamal (encrypt only)\n"), 5 ); #if GPG_USE_RSA tty_printf (_(" (%d) RSA (encrypt only)\n"), 6 ); #endif } if (opt.expert) { tty_printf (_(" (%d) DSA (set your own capabilities)\n"), 7 ); #if GPG_USE_RSA tty_printf (_(" (%d) RSA (set your own capabilities)\n"), 8 ); #endif } #if GPG_USE_ECDSA || GPG_USE_ECDH || GPG_USE_EDDSA if (opt.expert && !addmode) tty_printf (_(" (%d) ECC and ECC\n"), 9 ); if (opt.expert) tty_printf (_(" (%d) ECC (sign only)\n"), 10 ); if (opt.expert) tty_printf (_(" (%d) ECC (set your own capabilities)\n"), 11 ); if (opt.expert && addmode) tty_printf (_(" (%d) ECC (encrypt only)\n"), 12 ); #endif if (opt.expert && r_keygrip) tty_printf (_(" (%d) Existing key\n"), 13 ); for (;;) { *r_usage = 0; *r_subkey_algo = 0; xfree (answer); answer = cpr_get ("keygen.algo", _("Your selection? ")); cpr_kill_prompt (); algo = *answer? atoi (answer) : 1; if ((algo == 1 || !strcmp (answer, "rsa+rsa")) && !addmode) { algo = PUBKEY_ALGO_RSA; *r_subkey_algo = PUBKEY_ALGO_RSA; break; } else if ((algo == 2 || !strcmp (answer, "dsa+elg")) && !addmode) { algo = PUBKEY_ALGO_DSA; *r_subkey_algo = PUBKEY_ALGO_ELGAMAL_E; break; } else if (algo == 3 || !strcmp (answer, "dsa")) { algo = PUBKEY_ALGO_DSA; *r_usage = PUBKEY_USAGE_SIG; break; } else if (algo == 4 || !strcmp (answer, "rsa/s")) { algo = PUBKEY_ALGO_RSA; *r_usage = PUBKEY_USAGE_SIG; break; } else if ((algo == 5 || !strcmp (answer, "elg")) && addmode) { algo = PUBKEY_ALGO_ELGAMAL_E; *r_usage = PUBKEY_USAGE_ENC; break; } else if ((algo == 6 || !strcmp (answer, "rsa/e")) && addmode) { algo = PUBKEY_ALGO_RSA; *r_usage = PUBKEY_USAGE_ENC; break; } else if ((algo == 7 || !strcmp (answer, "dsa/*")) && opt.expert) { algo = PUBKEY_ALGO_DSA; *r_usage = ask_key_flags (algo, addmode); break; } else if ((algo == 8 || !strcmp (answer, "rsa/*")) && opt.expert) { algo = PUBKEY_ALGO_RSA; *r_usage = ask_key_flags (algo, addmode); break; } else if ((algo == 9 || !strcmp (answer, "ecc+ecc")) && opt.expert && !addmode) { algo = PUBKEY_ALGO_ECDSA; *r_subkey_algo = PUBKEY_ALGO_ECDH; break; } else if ((algo == 10 || !strcmp (answer, "ecc/s")) && opt.expert) { algo = PUBKEY_ALGO_ECDSA; *r_usage = PUBKEY_USAGE_SIG; break; } else if ((algo == 11 || !strcmp (answer, "ecc/*")) && opt.expert) { algo = PUBKEY_ALGO_ECDSA; *r_usage = ask_key_flags (algo, addmode); break; } else if ((algo == 12 || !strcmp (answer, "ecc/e")) && opt.expert && addmode) { algo = PUBKEY_ALGO_ECDH; *r_usage = PUBKEY_USAGE_ENC; break; } else if ((algo == 13 || !strcmp (answer, "keygrip")) && opt.expert && r_keygrip) { for (;;) { xfree (answer); answer = tty_get (_("Enter the keygrip: ")); tty_kill_prompt (); trim_spaces (answer); if (!*answer) { xfree (answer); answer = NULL; continue; } if (strlen (answer) != 40 && !(answer[0] == '&' && strlen (answer+1) == 40)) tty_printf (_("Not a valid keygrip (expecting 40 hex digits)\n")); else if (!(algo = check_keygrip (ctrl, answer)) ) tty_printf (_("No key with this keygrip\n")); else break; /* Okay. */ } xfree (keygrip); keygrip = answer; answer = NULL; *r_usage = ask_key_flags (algo, addmode); break; } else tty_printf (_("Invalid selection.\n")); } xfree(answer); if (r_keygrip) *r_keygrip = keygrip; return algo; } /* Ask for the key size. ALGO is the algorithm. If PRIMARY_KEYSIZE is not 0, the function asks for the size of the encryption subkey. */ static unsigned ask_keysize (int algo, unsigned int primary_keysize) { unsigned int nbits, min, def = DEFAULT_STD_KEYSIZE, max=4096; int for_subkey = !!primary_keysize; int autocomp = 0; if(opt.expert) min=512; else min=1024; if (primary_keysize && !opt.expert) { /* Deduce the subkey size from the primary key size. */ if (algo == PUBKEY_ALGO_DSA && primary_keysize > 3072) nbits = 3072; /* For performance reasons we don't support more than 3072 bit DSA. However we won't see this case anyway because DSA can't be used as an encryption subkey ;-). */ else nbits = primary_keysize; autocomp = 1; goto leave; } switch(algo) { case PUBKEY_ALGO_DSA: def=2048; max=3072; break; case PUBKEY_ALGO_ECDSA: case PUBKEY_ALGO_ECDH: min=256; def=256; max=521; break; case PUBKEY_ALGO_EDDSA: min=255; def=255; max=441; break; case PUBKEY_ALGO_RSA: min=1024; break; } tty_printf(_("%s keys may be between %u and %u bits long.\n"), openpgp_pk_algo_name (algo), min, max); for (;;) { char *prompt, *answer; if (for_subkey) prompt = xasprintf (_("What keysize do you want " "for the subkey? (%u) "), def); else prompt = xasprintf (_("What keysize do you want? (%u) "), def); answer = cpr_get ("keygen.size", prompt); cpr_kill_prompt (); nbits = *answer? atoi (answer): def; xfree(prompt); xfree(answer); if(nbitsmax) tty_printf(_("%s keysizes must be in the range %u-%u\n"), openpgp_pk_algo_name (algo), min, max); else break; } tty_printf (_("Requested keysize is %u bits\n"), nbits); leave: if (algo == PUBKEY_ALGO_DSA && (nbits % 64)) { nbits = ((nbits + 63) / 64) * 64; if (!autocomp) tty_printf (_("rounded up to %u bits\n"), nbits); } else if (algo == PUBKEY_ALGO_EDDSA) { if (nbits != 255 && nbits != 441) { if (nbits < 256) nbits = 255; else nbits = 441; if (!autocomp) tty_printf (_("rounded to %u bits\n"), nbits); } } else if (algo == PUBKEY_ALGO_ECDH || algo == PUBKEY_ALGO_ECDSA) { if (nbits != 256 && nbits != 384 && nbits != 521) { if (nbits < 256) nbits = 256; else if (nbits < 384) nbits = 384; else nbits = 521; if (!autocomp) tty_printf (_("rounded to %u bits\n"), nbits); } } else if ((nbits % 32)) { nbits = ((nbits + 31) / 32) * 32; if (!autocomp) tty_printf (_("rounded up to %u bits\n"), nbits ); } return nbits; } /* Ask for the curve. ALGO is the selected algorithm which this function may adjust. Returns a malloced string with the name of the curve. BOTH tells that gpg creates a primary and subkey. */ static char * ask_curve (int *algo, int both) { struct { const char *name; int available; int expert_only; int fix_curve; const char *pretty_name; } curves[] = { #if GPG_USE_EDDSA { "Curve25519", 0, 0, 1, "Curve 25519" }, #endif #if GPG_USE_ECDSA || GPG_USE_ECDH { "NIST P-256", 0, 1, 0, }, { "NIST P-384", 0, 0, 0, }, { "NIST P-521", 0, 1, 0, }, { "brainpoolP256r1", 0, 1, 0, "Brainpool P-256" }, { "brainpoolP384r1", 0, 1, 0, "Brainpool P-384" }, { "brainpoolP512r1", 0, 1, 0, "Brainpool P-512" }, { "secp256k1", 0, 1, 0 }, #endif }; int idx; char *answer; char *result = NULL; gcry_sexp_t keyparms; tty_printf (_("Please select which elliptic curve you want:\n")); again: keyparms = NULL; for (idx=0; idx < DIM(curves); idx++) { int rc; curves[idx].available = 0; if (!opt.expert && curves[idx].expert_only) continue; /* FIXME: The strcmp below is a temporary hack during development. It shall be removed as soon as we have proper Curve25519 support in Libgcrypt. */ gcry_sexp_release (keyparms); rc = gcry_sexp_build (&keyparms, NULL, "(public-key(ecc(curve %s)))", (!strcmp (curves[idx].name, "Curve25519") ? "Ed25519" : curves[idx].name)); if (rc) continue; if (!gcry_pk_get_curve (keyparms, 0, NULL)) continue; if (both && curves[idx].fix_curve) { /* Both Curve 25519 keys are to be created. Check that Libgcrypt also supports the real Curve25519. */ gcry_sexp_release (keyparms); rc = gcry_sexp_build (&keyparms, NULL, "(public-key(ecc(curve %s)))", curves[idx].name); if (rc) continue; if (!gcry_pk_get_curve (keyparms, 0, NULL)) continue; } curves[idx].available = 1; tty_printf (" (%d) %s\n", idx + 1, curves[idx].pretty_name? curves[idx].pretty_name:curves[idx].name); } gcry_sexp_release (keyparms); for (;;) { answer = cpr_get ("keygen.curve", _("Your selection? ")); cpr_kill_prompt (); idx = *answer? atoi (answer) : 1; if (*answer && !idx) { /* See whether the user entered the name of the curve. */ for (idx=0; idx < DIM(curves); idx++) { if (!opt.expert && curves[idx].expert_only) continue; if (!stricmp (curves[idx].name, answer) || (curves[idx].pretty_name && !stricmp (curves[idx].pretty_name, answer))) break; } if (idx == DIM(curves)) idx = -1; } else idx--; xfree(answer); answer = NULL; if (idx < 0 || idx >= DIM (curves) || !curves[idx].available) tty_printf (_("Invalid selection.\n")); else { if (curves[idx].fix_curve) { log_info ("WARNING: Curve25519 is not yet part of the" " OpenPGP standard.\n"); if (!cpr_get_answer_is_yes("experimental_curve.override", "Use this curve anyway? (y/N) ") ) goto again; } /* If the user selected a signing algorithm and Curve25519 we need to update the algo and and the curve name. */ if ((*algo == PUBKEY_ALGO_ECDSA || *algo == PUBKEY_ALGO_EDDSA) && curves[idx].fix_curve) { *algo = PUBKEY_ALGO_EDDSA; result = xstrdup ("Ed25519"); } else result = xstrdup (curves[idx].name); break; } } if (!result) result = xstrdup (curves[0].name); return result; } /**************** * Parse an expire string and return its value in seconds. * Returns (u32)-1 on error. * This isn't perfect since scan_isodatestr returns unix time, and * OpenPGP actually allows a 32-bit time *plus* a 32-bit offset. * Because of this, we only permit setting expirations up to 2106, but * OpenPGP could theoretically allow up to 2242. I think we'll all * just cope for the next few years until we get a 64-bit time_t or * similar. */ u32 parse_expire_string( const char *string ) { int mult; u32 seconds; u32 abs_date = 0; u32 curtime = make_timestamp (); time_t tt; if (!*string) seconds = 0; else if (!strncmp (string, "seconds=", 8)) seconds = atoi (string+8); else if ((abs_date = scan_isodatestr(string)) && (abs_date+86400/2) > curtime) seconds = (abs_date+86400/2) - curtime; else if ((tt = isotime2epoch (string)) != (time_t)(-1)) seconds = (u32)tt - curtime; else if ((mult = check_valid_days (string))) seconds = atoi (string) * 86400L * mult; else seconds = (u32)(-1); return seconds; } /* Parsean Creation-Date string which is either "1986-04-26" or "19860426T042640". Returns 0 on error. */ static u32 parse_creation_string (const char *string) { u32 seconds; if (!*string) seconds = 0; else if ( !strncmp (string, "seconds=", 8) ) seconds = atoi (string+8); else if ( !(seconds = scan_isodatestr (string))) { time_t tmp = isotime2epoch (string); seconds = (tmp == (time_t)(-1))? 0 : tmp; } return seconds; } /* object == 0 for a key, and 1 for a sig */ u32 ask_expire_interval(int object,const char *def_expire) { u32 interval; char *answer; switch(object) { case 0: if(def_expire) BUG(); tty_printf(_("Please specify how long the key should be valid.\n" " 0 = key does not expire\n" " = key expires in n days\n" " w = key expires in n weeks\n" " m = key expires in n months\n" " y = key expires in n years\n")); break; case 1: if(!def_expire) BUG(); tty_printf(_("Please specify how long the signature should be valid.\n" " 0 = signature does not expire\n" " = signature expires in n days\n" " w = signature expires in n weeks\n" " m = signature expires in n months\n" " y = signature expires in n years\n")); break; default: BUG(); } /* Note: The elgamal subkey for DSA has no expiration date because * it must be signed with the DSA key and this one has the expiration * date */ answer = NULL; for(;;) { u32 curtime; xfree(answer); if(object==0) answer = cpr_get("keygen.valid",_("Key is valid for? (0) ")); else { char *prompt; #define PROMPTSTRING _("Signature is valid for? (%s) ") /* This will actually end up larger than necessary because of the 2 bytes for '%s' */ prompt=xmalloc(strlen(PROMPTSTRING)+strlen(def_expire)+1); sprintf(prompt,PROMPTSTRING,def_expire); #undef PROMPTSTRING answer = cpr_get("siggen.valid",prompt); xfree(prompt); if(*answer=='\0') answer=xstrdup(def_expire); } cpr_kill_prompt(); trim_spaces(answer); curtime = make_timestamp (); interval = parse_expire_string( answer ); if( interval == (u32)-1 ) { tty_printf(_("invalid value\n")); continue; } if( !interval ) { tty_printf((object==0) ? _("Key does not expire at all\n") : _("Signature does not expire at all\n")); } else { tty_printf(object==0 ? _("Key expires at %s\n") : _("Signature expires at %s\n"), asctimestamp((ulong)(curtime + interval) ) ); #if SIZEOF_TIME_T <= 4 && !defined (HAVE_UNSIGNED_TIME_T) if ( (time_t)((ulong)(curtime+interval)) < 0 ) tty_printf (_("Your system can't display dates beyond 2038.\n" "However, it will be correctly handled up to" " 2106.\n")); else #endif /*SIZEOF_TIME_T*/ if ( (time_t)((unsigned long)(curtime+interval)) < curtime ) { tty_printf (_("invalid value\n")); continue; } } if( cpr_enabled() || cpr_get_answer_is_yes("keygen.valid.okay", _("Is this correct? (y/N) ")) ) break; } xfree(answer); return interval; } u32 ask_expiredate() { u32 x = ask_expire_interval(0,NULL); return x? make_timestamp() + x : 0; } static PKT_user_id * uid_from_string (const char *string) { size_t n; PKT_user_id *uid; n = strlen (string); uid = xmalloc_clear (sizeof *uid + n); uid->len = n; strcpy (uid->name, string); uid->ref = 1; return uid; } /* Ask for a user ID. With a MODE of 1 an extra help prompt is printed for use during a new key creation. If KEYBLOCK is not NULL the function prevents the creation of an already existing user ID. IF FULL is not set some prompts are not shown. */ static char * ask_user_id (int mode, int full, KBNODE keyblock) { char *answer; char *aname, *acomment, *amail, *uid; if ( !mode ) { /* TRANSLATORS: This is the new string telling the user what gpg is now going to do (i.e. ask for the parts of the user ID). Note that if you do not translate this string, a different string will be used, which might still have a correct translation. */ const char *s1 = N_("\n" "GnuPG needs to construct a user ID to identify your key.\n" "\n"); const char *s2 = _(s1); if (!strcmp (s1, s2)) { /* There is no translation for the string thus we to use the old info text. gettext has no way to tell whether a translation is actually available, thus we need to to compare again. */ /* TRANSLATORS: This string is in general not anymore used but you should keep your existing translation. In case the new string is not translated this old string will be used. */ const char *s3 = N_("\n" "You need a user ID to identify your key; " "the software constructs the user ID\n" "from the Real Name, Comment and Email Address in this form:\n" " \"Heinrich Heine (Der Dichter) \"\n\n"); const char *s4 = _(s3); if (strcmp (s3, s4)) s2 = s3; /* A translation exists - use it. */ } tty_printf ("%s", s2) ; } uid = aname = acomment = amail = NULL; for(;;) { char *p; int fail=0; if( !aname ) { for(;;) { xfree(aname); aname = cpr_get("keygen.name",_("Real name: ")); trim_spaces(aname); cpr_kill_prompt(); if( opt.allow_freeform_uid ) break; if( strpbrk( aname, "<>" ) ) tty_printf(_("Invalid character in name\n")); else if( digitp(aname) ) tty_printf(_("Name may not start with a digit\n")); else if( strlen(aname) < 5 ) tty_printf(_("Name must be at least 5 characters long\n")); else break; } } if( !amail ) { for(;;) { xfree(amail); amail = cpr_get("keygen.email",_("Email address: ")); trim_spaces(amail); cpr_kill_prompt(); if( !*amail || opt.allow_freeform_uid ) break; /* no email address is okay */ else if ( !is_valid_mailbox (amail) ) tty_printf(_("Not a valid email address\n")); else break; } } if (!acomment) { if (full) { for(;;) { xfree(acomment); acomment = cpr_get("keygen.comment",_("Comment: ")); trim_spaces(acomment); cpr_kill_prompt(); if( !*acomment ) break; /* no comment is okay */ else if( strpbrk( acomment, "()" ) ) tty_printf(_("Invalid character in comment\n")); else break; } } else { xfree (acomment); acomment = xstrdup (""); } } xfree(uid); uid = p = xmalloc(strlen(aname)+strlen(amail)+strlen(acomment)+12+10); p = stpcpy(p, aname ); if( *acomment ) p = stpcpy(stpcpy(stpcpy(p," ("), acomment),")"); if( *amail ) p = stpcpy(stpcpy(stpcpy(p," <"), amail),">"); /* Append a warning if the RNG is switched into fake mode. */ if ( random_is_faked () ) strcpy(p, " (insecure!)" ); /* print a note in case that UTF8 mapping has to be done */ for(p=uid; *p; p++ ) { if( *p & 0x80 ) { tty_printf(_("You are using the '%s' character set.\n"), get_native_charset() ); break; } } tty_printf(_("You selected this USER-ID:\n \"%s\"\n\n"), uid); if( !*amail && !opt.allow_freeform_uid && (strchr( aname, '@' ) || strchr( acomment, '@'))) { fail = 1; tty_printf(_("Please don't put the email address " "into the real name or the comment\n") ); } if (!fail && keyblock) { PKT_user_id *uidpkt = uid_from_string (uid); KBNODE node; for (node=keyblock; node && !fail; node=node->next) if (!is_deleted_kbnode (node) && node->pkt->pkttype == PKT_USER_ID && !cmp_user_ids (uidpkt, node->pkt->pkt.user_id)) fail = 1; if (fail) tty_printf (_("Such a user ID already exists on this key!\n")); free_user_id (uidpkt); } for(;;) { /* TRANSLATORS: These are the allowed answers in lower and uppercase. Below you will find the matching string which should be translated accordingly and the letter changed to match the one in the answer string. n = Change name c = Change comment e = Change email o = Okay (ready, continue) q = Quit */ const char *ansstr = _("NnCcEeOoQq"); if( strlen(ansstr) != 10 ) BUG(); if( cpr_enabled() ) { answer = xstrdup (ansstr + (fail?8:6)); answer[1] = 0; } else if (full) { answer = cpr_get("keygen.userid.cmd", fail? _("Change (N)ame, (C)omment, (E)mail or (Q)uit? ") : _("Change (N)ame, (C)omment, (E)mail or (O)kay/(Q)uit? ")); cpr_kill_prompt(); } else { answer = cpr_get("keygen.userid.cmd", fail? _("Change (N)ame, (E)mail, or (Q)uit? ") : _("Change (N)ame, (E)mail, or (O)kay/(Q)uit? ")); cpr_kill_prompt(); } if( strlen(answer) > 1 ) ; else if( *answer == ansstr[0] || *answer == ansstr[1] ) { xfree(aname); aname = NULL; break; } else if( *answer == ansstr[2] || *answer == ansstr[3] ) { xfree(acomment); acomment = NULL; break; } else if( *answer == ansstr[4] || *answer == ansstr[5] ) { xfree(amail); amail = NULL; break; } else if( *answer == ansstr[6] || *answer == ansstr[7] ) { if( fail ) { tty_printf(_("Please correct the error first\n")); } else { xfree(aname); aname = NULL; xfree(acomment); acomment = NULL; xfree(amail); amail = NULL; break; } } else if( *answer == ansstr[8] || *answer == ansstr[9] ) { xfree(aname); aname = NULL; xfree(acomment); acomment = NULL; xfree(amail); amail = NULL; xfree(uid); uid = NULL; break; } xfree(answer); } xfree(answer); if (!amail && !acomment) break; xfree(uid); uid = NULL; } if( uid ) { char *p = native_to_utf8( uid ); xfree( uid ); uid = p; } return uid; } /* MODE 0 - standard 1 - Ask for passphrase of the card backup key. */ #if 0 static DEK * do_ask_passphrase (STRING2KEY **ret_s2k, int mode, int *r_canceled) { DEK *dek = NULL; STRING2KEY *s2k; const char *errtext = NULL; const char *custdesc = NULL; tty_printf(_("You need a Passphrase to protect your secret key.\n\n") ); if (mode == 1) custdesc = _("Please enter a passphrase to protect the off-card " "backup of the new encryption key."); s2k = xmalloc_secure( sizeof *s2k ); for(;;) { s2k->mode = opt.s2k_mode; s2k->hash_algo = S2K_DIGEST_ALGO; dek = passphrase_to_dek_ext (NULL, 0, opt.s2k_cipher_algo, s2k, 2, errtext, custdesc, NULL, r_canceled); if (!dek && *r_canceled) { xfree(dek); dek = NULL; xfree(s2k); s2k = NULL; break; } else if( !dek ) { errtext = N_("passphrase not correctly repeated; try again"); tty_printf(_("%s.\n"), _(errtext)); } else if( !dek->keylen ) { xfree(dek); dek = NULL; xfree(s2k); s2k = NULL; tty_printf(_( "You don't want a passphrase - this is probably a *bad* idea!\n" "I will do it anyway. You can change your passphrase at any time,\n" "using this program with the option \"--edit-key\".\n\n")); break; } else break; /* okay */ } *ret_s2k = s2k; return dek; } #endif /* 0 */ /* Basic key generation. Here we divert to the actual generation routines based on the requested algorithm. */ static int do_create (int algo, unsigned int nbits, const char *curve, KBNODE pub_root, u32 timestamp, u32 expiredate, int is_subkey, int keygen_flags, const char *passphrase, char **cache_nonce_addr) { gpg_error_t err; /* Fixme: The entropy collecting message should be moved to a libgcrypt progress handler. */ if (!opt.batch) tty_printf (_( "We need to generate a lot of random bytes. It is a good idea to perform\n" "some other action (type on the keyboard, move the mouse, utilize the\n" "disks) during the prime generation; this gives the random number\n" "generator a better chance to gain enough entropy.\n") ); if (algo == PUBKEY_ALGO_ELGAMAL_E) err = gen_elg (algo, nbits, pub_root, timestamp, expiredate, is_subkey, keygen_flags, passphrase, cache_nonce_addr); else if (algo == PUBKEY_ALGO_DSA) err = gen_dsa (nbits, pub_root, timestamp, expiredate, is_subkey, keygen_flags, passphrase, cache_nonce_addr); else if (algo == PUBKEY_ALGO_ECDSA || algo == PUBKEY_ALGO_EDDSA || algo == PUBKEY_ALGO_ECDH) err = gen_ecc (algo, curve, pub_root, timestamp, expiredate, is_subkey, keygen_flags, passphrase, cache_nonce_addr); else if (algo == PUBKEY_ALGO_RSA) err = gen_rsa (algo, nbits, pub_root, timestamp, expiredate, is_subkey, keygen_flags, passphrase, cache_nonce_addr); else BUG(); return err; } /* Generate a new user id packet or return NULL if canceled. If KEYBLOCK is not NULL the function prevents the creation of an already existing user ID. */ PKT_user_id * generate_user_id (KBNODE keyblock) { char *p; p = ask_user_id (1, 1, keyblock); if (!p) return NULL; /* Canceled. */ return uid_from_string (p); } /* Append R to the linked list PARA. */ static void append_to_parameter (struct para_data_s *para, struct para_data_s *r) { assert (para); while (para->next) para = para->next; para->next = r; } /* Release the parameter list R. */ static void release_parameter_list (struct para_data_s *r) { struct para_data_s *r2; for (; r ; r = r2) { r2 = r->next; if (r->key == pPASSPHRASE && *r->u.value) wipememory (r->u.value, strlen (r->u.value)); xfree (r); } } static struct para_data_s * get_parameter( struct para_data_s *para, enum para_name key ) { struct para_data_s *r; for( r = para; r && r->key != key; r = r->next ) ; return r; } static const char * get_parameter_value( struct para_data_s *para, enum para_name key ) { struct para_data_s *r = get_parameter( para, key ); return (r && *r->u.value)? r->u.value : NULL; } /* This is similar to get_parameter_value but also returns the empty string. This is required so that quick_generate_keypair can use an empty Passphrase to specify no-protection. */ static const char * get_parameter_passphrase (struct para_data_s *para) { struct para_data_s *r = get_parameter (para, pPASSPHRASE); return r ? r->u.value : NULL; } static int get_parameter_algo( struct para_data_s *para, enum para_name key, int *r_default) { int i; struct para_data_s *r = get_parameter( para, key ); if (r_default) *r_default = 0; if (!r) return -1; /* Note that we need to handle the ECC algorithms specified as strings directly because Libgcrypt folds them all to ECC. */ if (!ascii_strcasecmp (r->u.value, "default")) { /* Note: If you change this default algo, remember to change it also in gpg.c:gpgconf_list. */ i = DEFAULT_STD_ALGO; if (r_default) *r_default = 1; } else if (digitp (r->u.value)) i = atoi( r->u.value ); else if (!strcmp (r->u.value, "ELG-E") || !strcmp (r->u.value, "ELG")) i = PUBKEY_ALGO_ELGAMAL_E; else if (!ascii_strcasecmp (r->u.value, "EdDSA")) i = PUBKEY_ALGO_EDDSA; else if (!ascii_strcasecmp (r->u.value, "ECDSA")) i = PUBKEY_ALGO_ECDSA; else if (!ascii_strcasecmp (r->u.value, "ECDH")) i = PUBKEY_ALGO_ECDH; else i = map_pk_gcry_to_openpgp (gcry_pk_map_name (r->u.value)); if (i == PUBKEY_ALGO_RSA_E || i == PUBKEY_ALGO_RSA_S) i = 0; /* we don't want to allow generation of these algorithms */ return i; } /* * Parse the usage parameter and set the keyflags. Returns -1 on * error, 0 for no usage given or 1 for usage available. */ static int parse_parameter_usage (const char *fname, struct para_data_s *para, enum para_name key) { struct para_data_s *r = get_parameter( para, key ); char *p, *pn; unsigned int use; if( !r ) return 0; /* none (this is an optional parameter)*/ use = 0; pn = r->u.value; while ( (p = strsep (&pn, " \t,")) ) { if ( !*p) ; else if ( !ascii_strcasecmp (p, "sign") ) use |= PUBKEY_USAGE_SIG; else if ( !ascii_strcasecmp (p, "encrypt") ) use |= PUBKEY_USAGE_ENC; else if ( !ascii_strcasecmp (p, "auth") ) use |= PUBKEY_USAGE_AUTH; else { log_error("%s:%d: invalid usage list\n", fname, r->lnr ); return -1; /* error */ } } r->u.usage = use; return 1; } static int parse_revocation_key (const char *fname, struct para_data_s *para, enum para_name key) { struct para_data_s *r = get_parameter( para, key ); struct revocation_key revkey; char *pn; int i; if( !r ) return 0; /* none (this is an optional parameter) */ pn = r->u.value; revkey.class=0x80; revkey.algid=atoi(pn); if(!revkey.algid) goto fail; /* Skip to the fpr */ while(*pn && *pn!=':') pn++; if(*pn!=':') goto fail; pn++; for(i=0;iu.revkey,&revkey,sizeof(struct revocation_key)); return 0; fail: log_error("%s:%d: invalid revocation key\n", fname, r->lnr ); return -1; /* error */ } static u32 get_parameter_u32( struct para_data_s *para, enum para_name key ) { struct para_data_s *r = get_parameter( para, key ); if( !r ) return 0; if( r->key == pKEYCREATIONDATE ) return r->u.creation; if( r->key == pKEYEXPIRE || r->key == pSUBKEYEXPIRE ) return r->u.expire; if( r->key == pKEYUSAGE || r->key == pSUBKEYUSAGE ) return r->u.usage; return (unsigned int)strtoul( r->u.value, NULL, 10 ); } static unsigned int get_parameter_uint( struct para_data_s *para, enum para_name key ) { return get_parameter_u32( para, key ); } static struct revocation_key * get_parameter_revkey( struct para_data_s *para, enum para_name key ) { struct para_data_s *r = get_parameter( para, key ); return r? &r->u.revkey : NULL; } static int proc_parameter_file( struct para_data_s *para, const char *fname, struct output_control_s *outctrl, int card ) { struct para_data_s *r; const char *s1, *s2, *s3; size_t n; char *p; int is_default = 0; int have_user_id = 0; int err, algo; /* Check that we have all required parameters. */ r = get_parameter( para, pKEYTYPE ); if(r) { algo = get_parameter_algo (para, pKEYTYPE, &is_default); if (openpgp_pk_test_algo2 (algo, PUBKEY_USAGE_SIG)) { log_error ("%s:%d: invalid algorithm\n", fname, r->lnr ); return -1; } } else { log_error ("%s: no Key-Type specified\n",fname); return -1; } err = parse_parameter_usage (fname, para, pKEYUSAGE); if (!err) { /* Default to algo capabilities if key-usage is not provided and no default algorithm has been requested. */ r = xmalloc_clear(sizeof(*r)); r->key = pKEYUSAGE; r->u.usage = (is_default ? (PUBKEY_USAGE_CERT | PUBKEY_USAGE_SIG) : openpgp_pk_algo_usage(algo)); append_to_parameter (para, r); } else if (err == -1) return -1; else { r = get_parameter (para, pKEYUSAGE); if (r && (r->u.usage & ~openpgp_pk_algo_usage (algo))) { log_error ("%s:%d: specified Key-Usage not allowed for algo %d\n", fname, r->lnr, algo); return -1; } } is_default = 0; r = get_parameter( para, pSUBKEYTYPE ); if(r) { algo = get_parameter_algo (para, pSUBKEYTYPE, &is_default); if (openpgp_pk_test_algo (algo)) { log_error ("%s:%d: invalid algorithm\n", fname, r->lnr ); return -1; } err = parse_parameter_usage (fname, para, pSUBKEYUSAGE); if (!err) { /* Default to algo capabilities if subkey-usage is not provided */ r = xmalloc_clear (sizeof(*r)); r->key = pSUBKEYUSAGE; r->u.usage = (is_default ? PUBKEY_USAGE_ENC : openpgp_pk_algo_usage (algo)); append_to_parameter (para, r); } else if (err == -1) return -1; else { r = get_parameter (para, pSUBKEYUSAGE); if (r && (r->u.usage & ~openpgp_pk_algo_usage (algo))) { log_error ("%s:%d: specified Subkey-Usage not allowed" " for algo %d\n", fname, r->lnr, algo); return -1; } } } if( get_parameter_value( para, pUSERID ) ) have_user_id=1; else { /* create the formatted user ID */ s1 = get_parameter_value( para, pNAMEREAL ); s2 = get_parameter_value( para, pNAMECOMMENT ); s3 = get_parameter_value( para, pNAMEEMAIL ); if( s1 || s2 || s3 ) { n = (s1?strlen(s1):0) + (s2?strlen(s2):0) + (s3?strlen(s3):0); r = xmalloc_clear( sizeof *r + n + 20 ); r->key = pUSERID; p = r->u.value; if( s1 ) p = stpcpy(p, s1 ); if( s2 ) p = stpcpy(stpcpy(stpcpy(p," ("), s2 ),")"); if( s3 ) p = stpcpy(stpcpy(stpcpy(p," <"), s3 ),">"); append_to_parameter (para, r); have_user_id=1; } } if(!have_user_id) { log_error("%s: no User-ID specified\n",fname); return -1; } /* Set preferences, if any. */ keygen_set_std_prefs(get_parameter_value( para, pPREFERENCES ), 0); /* Set keyserver, if any. */ s1=get_parameter_value( para, pKEYSERVER ); if(s1) { struct keyserver_spec *spec; spec = parse_keyserver_uri (s1, 1); if(spec) { free_keyserver_spec(spec); opt.def_keyserver_url=s1; } else { log_error("%s:%d: invalid keyserver url\n", fname, r->lnr ); return -1; } } /* Set revoker, if any. */ if (parse_revocation_key (fname, para, pREVOKER)) return -1; /* Make KEYCREATIONDATE from Creation-Date. */ r = get_parameter (para, pCREATIONDATE); if (r && *r->u.value) { u32 seconds; seconds = parse_creation_string (r->u.value); if (!seconds) { log_error ("%s:%d: invalid creation date\n", fname, r->lnr ); return -1; } r->u.creation = seconds; r->key = pKEYCREATIONDATE; /* Change that entry. */ } /* Make KEYEXPIRE from Expire-Date. */ r = get_parameter( para, pEXPIREDATE ); if( r && *r->u.value ) { u32 seconds; seconds = parse_expire_string( r->u.value ); if( seconds == (u32)-1 ) { log_error("%s:%d: invalid expire date\n", fname, r->lnr ); return -1; } r->u.expire = seconds; r->key = pKEYEXPIRE; /* change hat entry */ /* also set it for the subkey */ r = xmalloc_clear( sizeof *r + 20 ); r->key = pSUBKEYEXPIRE; r->u.expire = seconds; append_to_parameter (para, r); } do_generate_keypair( para, outctrl, card ); return 0; } /**************** * Kludge to allow non interactive key generation controlled * by a parameter file. * Note, that string parameters are expected to be in UTF-8 */ static void read_parameter_file( const char *fname ) { static struct { const char *name; enum para_name key; } keywords[] = { { "Key-Type", pKEYTYPE}, { "Key-Length", pKEYLENGTH }, { "Key-Curve", pKEYCURVE }, { "Key-Usage", pKEYUSAGE }, { "Subkey-Type", pSUBKEYTYPE }, { "Subkey-Length", pSUBKEYLENGTH }, { "Subkey-Curve", pSUBKEYCURVE }, { "Subkey-Usage", pSUBKEYUSAGE }, { "Name-Real", pNAMEREAL }, { "Name-Email", pNAMEEMAIL }, { "Name-Comment", pNAMECOMMENT }, { "Expire-Date", pEXPIREDATE }, { "Creation-Date", pCREATIONDATE }, { "Passphrase", pPASSPHRASE }, { "Preferences", pPREFERENCES }, { "Revoker", pREVOKER }, { "Handle", pHANDLE }, { "Keyserver", pKEYSERVER }, { NULL, 0 } }; IOBUF fp; byte *line; unsigned int maxlen, nline; char *p; int lnr; const char *err = NULL; struct para_data_s *para, *r; int i; struct output_control_s outctrl; memset( &outctrl, 0, sizeof( outctrl ) ); outctrl.pub.afx = new_armor_context (); if( !fname || !*fname) fname = "-"; fp = iobuf_open (fname); if (fp && is_secured_file (iobuf_get_fd (fp))) { iobuf_close (fp); fp = NULL; gpg_err_set_errno (EPERM); } if (!fp) { log_error (_("can't open '%s': %s\n"), fname, strerror(errno) ); return; } iobuf_ioctl (fp, IOBUF_IOCTL_NO_CACHE, 1, NULL); lnr = 0; err = NULL; para = NULL; maxlen = 1024; line = NULL; while ( iobuf_read_line (fp, &line, &nline, &maxlen) ) { char *keyword, *value; lnr++; if( !maxlen ) { err = "line too long"; break; } for( p = line; isspace(*(byte*)p); p++ ) ; if( !*p || *p == '#' ) continue; keyword = p; if( *keyword == '%' ) { for( ; !isspace(*(byte*)p); p++ ) ; if( *p ) *p++ = 0; for( ; isspace(*(byte*)p); p++ ) ; value = p; trim_trailing_ws( value, strlen(value) ); if( !ascii_strcasecmp( keyword, "%echo" ) ) log_info("%s\n", value ); else if( !ascii_strcasecmp( keyword, "%dry-run" ) ) outctrl.dryrun = 1; else if( !ascii_strcasecmp( keyword, "%ask-passphrase" ) ) ; /* Dummy for backward compatibility. */ else if( !ascii_strcasecmp( keyword, "%no-ask-passphrase" ) ) ; /* Dummy for backward compatibility. */ else if( !ascii_strcasecmp( keyword, "%no-protection" ) ) outctrl.keygen_flags |= KEYGEN_FLAG_NO_PROTECTION; else if( !ascii_strcasecmp( keyword, "%transient-key" ) ) outctrl.keygen_flags |= KEYGEN_FLAG_TRANSIENT_KEY; else if( !ascii_strcasecmp( keyword, "%commit" ) ) { outctrl.lnr = lnr; if (proc_parameter_file( para, fname, &outctrl, 0 )) print_status_key_not_created (get_parameter_value (para, pHANDLE)); release_parameter_list( para ); para = NULL; } else if( !ascii_strcasecmp( keyword, "%pubring" ) ) { if( outctrl.pub.fname && !strcmp( outctrl.pub.fname, value ) ) ; /* still the same file - ignore it */ else { xfree( outctrl.pub.newfname ); outctrl.pub.newfname = xstrdup( value ); outctrl.use_files = 1; } } else if( !ascii_strcasecmp( keyword, "%secring" ) ) { /* Ignore this command. */ } else log_info("skipping control '%s' (%s)\n", keyword, value ); continue; } if( !(p = strchr( p, ':' )) || p == keyword ) { err = "missing colon"; break; } if( *p ) *p++ = 0; for( ; isspace(*(byte*)p); p++ ) ; if( !*p ) { err = "missing argument"; break; } value = p; trim_trailing_ws( value, strlen(value) ); for(i=0; keywords[i].name; i++ ) { if( !ascii_strcasecmp( keywords[i].name, keyword ) ) break; } if( !keywords[i].name ) { err = "unknown keyword"; break; } if( keywords[i].key != pKEYTYPE && !para ) { err = "parameter block does not start with \"Key-Type\""; break; } if( keywords[i].key == pKEYTYPE && para ) { outctrl.lnr = lnr; if (proc_parameter_file( para, fname, &outctrl, 0 )) print_status_key_not_created (get_parameter_value (para, pHANDLE)); release_parameter_list( para ); para = NULL; } else { for( r = para; r; r = r->next ) { if( r->key == keywords[i].key ) break; } if( r ) { err = "duplicate keyword"; break; } } r = xmalloc_clear( sizeof *r + strlen( value ) ); r->lnr = lnr; r->key = keywords[i].key; strcpy( r->u.value, value ); r->next = para; para = r; } if( err ) log_error("%s:%d: %s\n", fname, lnr, err ); else if( iobuf_error (fp) ) { log_error("%s:%d: read error\n", fname, lnr); } else if( para ) { outctrl.lnr = lnr; if (proc_parameter_file( para, fname, &outctrl, 0 )) print_status_key_not_created (get_parameter_value (para, pHANDLE)); } if( outctrl.use_files ) { /* close open streams */ iobuf_close( outctrl.pub.stream ); /* Must invalidate that ugly cache to actually close it. */ if (outctrl.pub.fname) iobuf_ioctl (NULL, IOBUF_IOCTL_INVALIDATE_CACHE, 0, (char*)outctrl.pub.fname); xfree( outctrl.pub.fname ); xfree( outctrl.pub.newfname ); } release_parameter_list( para ); iobuf_close (fp); release_armor_context (outctrl.pub.afx); } /* Helper for quick_generate_keypair. */ static struct para_data_s * quickgen_set_para (struct para_data_s *para, int for_subkey, int algo, int nbits, const char *curve) { struct para_data_s *r; r = xmalloc_clear (sizeof *r + 20); r->key = for_subkey? pSUBKEYUSAGE : pKEYUSAGE; strcpy (r->u.value, for_subkey ? "encrypt" : "sign"); r->next = para; para = r; r = xmalloc_clear (sizeof *r + 20); r->key = for_subkey? pSUBKEYTYPE : pKEYTYPE; sprintf (r->u.value, "%d", algo); r->next = para; para = r; if (curve) { r = xmalloc_clear (sizeof *r + strlen (curve)); r->key = for_subkey? pSUBKEYCURVE : pKEYCURVE; strcpy (r->u.value, curve); r->next = para; para = r; } else { r = xmalloc_clear (sizeof *r + 20); r->key = for_subkey? pSUBKEYLENGTH : pKEYLENGTH; sprintf (r->u.value, "%u", nbits); r->next = para; para = r; } return para; } /* * Unattended generation of a standard key. */ void quick_generate_keypair (const char *uid) { gpg_error_t err; struct para_data_s *para = NULL; struct para_data_s *r; struct output_control_s outctrl; int use_tty; memset (&outctrl, 0, sizeof outctrl); use_tty = (!opt.batch && !opt.answer_yes && !cpr_enabled () && gnupg_isatty (fileno (stdin)) && gnupg_isatty (fileno (stdout)) && gnupg_isatty (fileno (stderr))); r = xmalloc_clear (sizeof *r + strlen (uid)); r->key = pUSERID; strcpy (r->u.value, uid); r->next = para; para = r; uid = trim_spaces (r->u.value); if (!*uid || (!opt.allow_freeform_uid && !is_valid_user_id (uid))) { log_error (_("Key generation failed: %s\n"), gpg_strerror (GPG_ERR_INV_USER_ID)); goto leave; } /* If gpg is directly used on the console ask whether a key with the given user id shall really be created. */ if (use_tty) { tty_printf (_("About to create a key for:\n \"%s\"\n\n"), uid); if (!cpr_get_answer_is_yes_def ("quick_keygen.okay", _("Continue? (Y/n) "), 1)) goto leave; } /* Check whether such a user ID already exists. */ { KEYDB_HANDLE kdbhd; KEYDB_SEARCH_DESC desc; memset (&desc, 0, sizeof desc); desc.mode = KEYDB_SEARCH_MODE_EXACT; desc.u.name = uid; kdbhd = keydb_new (); err = keydb_search (kdbhd, &desc, 1, NULL); keydb_release (kdbhd); if (gpg_err_code (err) != GPG_ERR_NOT_FOUND) { log_info (_("A key for \"%s\" already exists\n"), uid); if (opt.answer_yes) ; else if (!use_tty || !cpr_get_answer_is_yes_def ("quick_keygen.force", _("Create anyway? (y/N) "), 0)) { log_inc_errorcount (); /* we used log_info */ goto leave; } log_info (_("creating anyway\n")); } } para = quickgen_set_para (para, 0, DEFAULT_STD_ALGO, DEFAULT_STD_KEYSIZE, DEFAULT_STD_CURVE); para = quickgen_set_para (para, 1, DEFAULT_STD_SUBALGO, DEFAULT_STD_SUBKEYSIZE, DEFAULT_STD_SUBCURVE); /* If the pinentry loopback mode is not and we have a static passphrase (i.e. set with --passphrase{,-fd,-file} while in batch mode), we use that passphrase for the new key. */ if (opt.pinentry_mode != PINENTRY_MODE_LOOPBACK && have_static_passphrase ()) { const char *s = get_static_passphrase (); r = xmalloc_clear (sizeof *r + strlen (s)); r->key = pPASSPHRASE; strcpy (r->u.value, s); r->next = para; para = r; } proc_parameter_file (para, "[internal]", &outctrl, 0); leave: release_parameter_list (para); } /* * Generate a keypair (fname is only used in batch mode) If * CARD_SERIALNO is not NULL the function will create the keys on an * OpenPGP Card. If CARD_BACKUP_KEY has been set and CARD_SERIALNO is * NOT NULL, the encryption key for the card is generated on the host, * imported to the card and a backup file created by gpg-agent. If * FULL is not set only the basic prompts are used (except for batch * mode). */ void generate_keypair (ctrl_t ctrl, int full, const char *fname, const char *card_serialno, int card_backup_key) { unsigned int nbits; char *uid = NULL; int algo; unsigned int use; int both = 0; u32 expire; struct para_data_s *para = NULL; struct para_data_s *r; struct output_control_s outctrl; #ifndef ENABLE_CARD_SUPPORT (void)card_backup_key; #endif memset( &outctrl, 0, sizeof( outctrl ) ); if (opt.batch && card_serialno) { /* We don't yet support unattended key generation. */ log_error (_("can't do this in batch mode\n")); return; } if (opt.batch) { read_parameter_file( fname ); return; } if (card_serialno) { #ifdef ENABLE_CARD_SUPPORT r = xcalloc (1, sizeof *r + strlen (card_serialno) ); r->key = pSERIALNO; strcpy( r->u.value, card_serialno); r->next = para; para = r; algo = PUBKEY_ALGO_RSA; r = xcalloc (1, sizeof *r + 20 ); r->key = pKEYTYPE; sprintf( r->u.value, "%d", algo ); r->next = para; para = r; r = xcalloc (1, sizeof *r + 20 ); r->key = pKEYUSAGE; strcpy (r->u.value, "sign"); r->next = para; para = r; r = xcalloc (1, sizeof *r + 20 ); r->key = pSUBKEYTYPE; sprintf( r->u.value, "%d", algo ); r->next = para; para = r; r = xcalloc (1, sizeof *r + 20 ); r->key = pSUBKEYUSAGE; strcpy (r->u.value, "encrypt"); r->next = para; para = r; r = xcalloc (1, sizeof *r + 20 ); r->key = pAUTHKEYTYPE; sprintf( r->u.value, "%d", algo ); r->next = para; para = r; if (card_backup_key) { r = xcalloc (1, sizeof *r + 1); r->key = pCARDBACKUPKEY; strcpy (r->u.value, "1"); r->next = para; para = r; } #endif /*ENABLE_CARD_SUPPORT*/ } else if (full) /* Full featured key generation. */ { int subkey_algo; char *curve = NULL; /* Fixme: To support creating a primary key by keygrip we better also define the keyword for the parameter file. Note that the subkey case will never be asserted if a keygrip has been given. */ algo = ask_algo (ctrl, 0, &subkey_algo, &use, NULL); if (subkey_algo) { /* Create primary and subkey at once. */ both = 1; if (algo == PUBKEY_ALGO_ECDSA || algo == PUBKEY_ALGO_EDDSA || algo == PUBKEY_ALGO_ECDH) { curve = ask_curve (&algo, both); r = xmalloc_clear( sizeof *r + 20 ); r->key = pKEYTYPE; sprintf( r->u.value, "%d", algo); r->next = para; para = r; nbits = 0; r = xmalloc_clear (sizeof *r + strlen (curve)); r->key = pKEYCURVE; strcpy (r->u.value, curve); r->next = para; para = r; } else { r = xmalloc_clear( sizeof *r + 20 ); r->key = pKEYTYPE; sprintf( r->u.value, "%d", algo); r->next = para; para = r; nbits = ask_keysize (algo, 0); r = xmalloc_clear( sizeof *r + 20 ); r->key = pKEYLENGTH; sprintf( r->u.value, "%u", nbits); r->next = para; para = r; } r = xmalloc_clear( sizeof *r + 20 ); r->key = pKEYUSAGE; strcpy( r->u.value, "sign" ); r->next = para; para = r; r = xmalloc_clear( sizeof *r + 20 ); r->key = pSUBKEYTYPE; sprintf( r->u.value, "%d", subkey_algo); r->next = para; para = r; r = xmalloc_clear( sizeof *r + 20 ); r->key = pSUBKEYUSAGE; strcpy( r->u.value, "encrypt" ); r->next = para; para = r; if (algo == PUBKEY_ALGO_ECDSA || algo == PUBKEY_ALGO_EDDSA || algo == PUBKEY_ALGO_ECDH) { if (algo == PUBKEY_ALGO_EDDSA && subkey_algo == PUBKEY_ALGO_ECDH) { /* Need to switch to a different curve for the encryption key. */ xfree (curve); curve = xstrdup ("Curve25519"); } r = xmalloc_clear (sizeof *r + strlen (curve)); r->key = pSUBKEYCURVE; strcpy (r->u.value, curve); r->next = para; para = r; } } else /* Create only a single key. */ { /* For ECC we need to ask for the curve before storing the algo because ask_curve may change the algo. */ if (algo == PUBKEY_ALGO_ECDSA || algo == PUBKEY_ALGO_EDDSA || algo == PUBKEY_ALGO_ECDH) { curve = ask_curve (&algo, 0); nbits = 0; r = xmalloc_clear (sizeof *r + strlen (curve)); r->key = pKEYCURVE; strcpy (r->u.value, curve); r->next = para; para = r; } r = xmalloc_clear( sizeof *r + 20 ); r->key = pKEYTYPE; sprintf( r->u.value, "%d", algo ); r->next = para; para = r; if (use) { r = xmalloc_clear( sizeof *r + 25 ); r->key = pKEYUSAGE; sprintf( r->u.value, "%s%s%s", (use & PUBKEY_USAGE_SIG)? "sign ":"", (use & PUBKEY_USAGE_ENC)? "encrypt ":"", (use & PUBKEY_USAGE_AUTH)? "auth":"" ); r->next = para; para = r; } nbits = 0; } if (algo == PUBKEY_ALGO_ECDSA || algo == PUBKEY_ALGO_EDDSA || algo == PUBKEY_ALGO_ECDH) { /* The curve has already been set. */ } else { nbits = ask_keysize (both? subkey_algo : algo, nbits); r = xmalloc_clear( sizeof *r + 20 ); r->key = both? pSUBKEYLENGTH : pKEYLENGTH; sprintf( r->u.value, "%u", nbits); r->next = para; para = r; } xfree (curve); } else /* Default key generation. */ { tty_printf ( _("Note: Use \"%s %s\"" " for a full featured key generation dialog.\n"), NAME_OF_INSTALLED_GPG, "--full-gen-key" ); para = quickgen_set_para (para, 0, DEFAULT_STD_ALGO, DEFAULT_STD_KEYSIZE, DEFAULT_STD_CURVE); para = quickgen_set_para (para, 1, DEFAULT_STD_SUBALGO, DEFAULT_STD_SUBKEYSIZE, DEFAULT_STD_SUBCURVE); } expire = full? ask_expire_interval (0, NULL) : 0; r = xcalloc (1, sizeof *r + 20); r->key = pKEYEXPIRE; r->u.expire = expire; r->next = para; para = r; r = xcalloc (1, sizeof *r + 20); r->key = pSUBKEYEXPIRE; r->u.expire = expire; r->next = para; para = r; uid = ask_user_id (0, full, NULL); if (!uid) { log_error(_("Key generation canceled.\n")); release_parameter_list( para ); return; } r = xcalloc (1, sizeof *r + strlen (uid)); r->key = pUSERID; strcpy (r->u.value, uid); r->next = para; para = r; proc_parameter_file (para, "[internal]", &outctrl, !!card_serialno); release_parameter_list (para); } #if 0 /* not required */ /* Generate a raw key and return it as a secret key packet. The function will ask for the passphrase and return a protected as well as an unprotected copy of a new secret key packet. 0 is returned on success and the caller must then free the returned values. */ static int generate_raw_key (int algo, unsigned int nbits, u32 created_at, PKT_secret_key **r_sk_unprotected, PKT_secret_key **r_sk_protected) { int rc; DEK *dek = NULL; STRING2KEY *s2k = NULL; PKT_secret_key *sk = NULL; int i; size_t nskey, npkey; gcry_sexp_t s_parms, s_key; int canceled; npkey = pubkey_get_npkey (algo); nskey = pubkey_get_nskey (algo); assert (nskey <= PUBKEY_MAX_NSKEY && npkey < nskey); if (nbits < 512) { nbits = 512; log_info (_("keysize invalid; using %u bits\n"), nbits ); } if ((nbits % 32)) { nbits = ((nbits + 31) / 32) * 32; log_info(_("keysize rounded up to %u bits\n"), nbits ); } dek = do_ask_passphrase (&s2k, 1, &canceled); if (canceled) { rc = gpg_error (GPG_ERR_CANCELED); goto leave; } sk = xmalloc_clear (sizeof *sk); sk->timestamp = created_at; sk->version = 4; sk->pubkey_algo = algo; if ( !is_RSA (algo) ) { log_error ("only RSA is supported for offline generated keys\n"); rc = gpg_error (GPG_ERR_NOT_IMPLEMENTED); goto leave; } rc = gcry_sexp_build (&s_parms, NULL, "(genkey(rsa(nbits %d)))", (int)nbits); if (rc) log_bug ("gcry_sexp_build failed: %s\n", gpg_strerror (rc)); rc = gcry_pk_genkey (&s_key, s_parms); gcry_sexp_release (s_parms); if (rc) { log_error ("gcry_pk_genkey failed: %s\n", gpg_strerror (rc) ); goto leave; } rc = key_from_sexp (sk->skey, s_key, "private-key", "nedpqu"); gcry_sexp_release (s_key); if (rc) { log_error ("key_from_sexp failed: %s\n", gpg_strerror (rc) ); goto leave; } for (i=npkey; i < nskey; i++) sk->csum += checksum_mpi (sk->skey[i]); if (r_sk_unprotected) *r_sk_unprotected = copy_secret_key (NULL, sk); rc = genhelp_protect (dek, s2k, sk); if (rc) goto leave; if (r_sk_protected) { *r_sk_protected = sk; sk = NULL; } leave: if (sk) free_secret_key (sk); xfree (dek); xfree (s2k); return rc; } #endif /* ENABLE_CARD_SUPPORT */ /* Create and delete a dummy packet to start off a list of kbnodes. */ static void start_tree(KBNODE *tree) { PACKET *pkt; pkt=xmalloc_clear(sizeof(*pkt)); pkt->pkttype=PKT_NONE; *tree=new_kbnode(pkt); delete_kbnode(*tree); } static void do_generate_keypair (struct para_data_s *para, struct output_control_s *outctrl, int card) { gpg_error_t err; KBNODE pub_root = NULL; const char *s; PKT_public_key *pri_psk = NULL; PKT_public_key *sub_psk = NULL; struct revocation_key *revkey; int did_sub = 0; u32 timestamp; char *cache_nonce = NULL; if (outctrl->dryrun) { log_info("dry-run mode - key generation skipped\n"); return; } if ( outctrl->use_files ) { if ( outctrl->pub.newfname ) { iobuf_close(outctrl->pub.stream); outctrl->pub.stream = NULL; if (outctrl->pub.fname) iobuf_ioctl (NULL, IOBUF_IOCTL_INVALIDATE_CACHE, 0, (char*)outctrl->pub.fname); xfree( outctrl->pub.fname ); outctrl->pub.fname = outctrl->pub.newfname; outctrl->pub.newfname = NULL; if (is_secured_filename (outctrl->pub.fname) ) { outctrl->pub.stream = NULL; gpg_err_set_errno (EPERM); } else outctrl->pub.stream = iobuf_create (outctrl->pub.fname, 0); if (!outctrl->pub.stream) { log_error(_("can't create '%s': %s\n"), outctrl->pub.newfname, strerror(errno) ); return; } if (opt.armor) { outctrl->pub.afx->what = 1; push_armor_filter (outctrl->pub.afx, outctrl->pub.stream); } } assert( outctrl->pub.stream ); if (opt.verbose) log_info (_("writing public key to '%s'\n"), outctrl->pub.fname ); } /* We create the packets as a tree of kbnodes. Because the structure we create is known in advance we simply generate a linked list. The first packet is a dummy packet which we flag as deleted. The very first packet must always be a KEY packet. */ start_tree (&pub_root); timestamp = get_parameter_u32 (para, pKEYCREATIONDATE); if (!timestamp) timestamp = make_timestamp (); /* Note that, depending on the backend (i.e. the used scdaemon version), the card key generation may update TIMESTAMP for each key. Thus we need to pass TIMESTAMP to all signing function to make sure that the binding signature is done using the timestamp of the corresponding (sub)key and not that of the primary key. An alternative implementation could tell the signing function the node of the subkey but that is more work than just to pass the current timestamp. */ if (!card) err = do_create (get_parameter_algo( para, pKEYTYPE, NULL ), get_parameter_uint( para, pKEYLENGTH ), get_parameter_value (para, pKEYCURVE), pub_root, timestamp, get_parameter_u32( para, pKEYEXPIRE ), 0, outctrl->keygen_flags, get_parameter_passphrase (para), &cache_nonce); else err = gen_card_key (PUBKEY_ALGO_RSA, 1, 1, pub_root, ×tamp, get_parameter_u32 (para, pKEYEXPIRE)); /* Get the pointer to the generated public key packet. */ if (!err) { pri_psk = pub_root->next->pkt->pkt.public_key; assert (pri_psk); } if (!err && (revkey = get_parameter_revkey (para, pREVOKER))) err = write_direct_sig (pub_root, pri_psk, revkey, timestamp, cache_nonce); if (!err && (s = get_parameter_value (para, pUSERID))) { write_uid (pub_root, s ); err = write_selfsigs (pub_root, pri_psk, get_parameter_uint (para, pKEYUSAGE), timestamp, cache_nonce); } /* Write the auth key to the card before the encryption key. This is a partial workaround for a PGP bug (as of this writing, all versions including 8.1), that causes it to try and encrypt to the most recent subkey regardless of whether that subkey is actually an encryption type. In this case, the auth key is an RSA key so it succeeds. */ if (!err && card && get_parameter (para, pAUTHKEYTYPE)) { err = gen_card_key (PUBKEY_ALGO_RSA, 3, 0, pub_root, ×tamp, get_parameter_u32 (para, pKEYEXPIRE)); if (!err) err = write_keybinding (pub_root, pri_psk, NULL, PUBKEY_USAGE_AUTH, timestamp, cache_nonce); } if (!err && get_parameter (para, pSUBKEYTYPE)) { sub_psk = NULL; if (!card) { err = do_create (get_parameter_algo (para, pSUBKEYTYPE, NULL), get_parameter_uint (para, pSUBKEYLENGTH), get_parameter_value (para, pSUBKEYCURVE), pub_root, timestamp, get_parameter_u32 (para, pSUBKEYEXPIRE), 1, outctrl->keygen_flags, get_parameter_passphrase (para), &cache_nonce); /* Get the pointer to the generated public subkey packet. */ if (!err) { kbnode_t node; for (node = pub_root; node; node = node->next) if (node->pkt->pkttype == PKT_PUBLIC_SUBKEY) sub_psk = node->pkt->pkt.public_key; assert (sub_psk); } } else { if ((s = get_parameter_value (para, pCARDBACKUPKEY))) { /* A backup of the encryption key has been requested. Generate the key in software and import it then to the card. Write a backup file. */ err = gen_card_key_with_backup (PUBKEY_ALGO_RSA, 2, 0, pub_root, timestamp, get_parameter_u32 (para, pKEYEXPIRE), para); } else { err = gen_card_key (PUBKEY_ALGO_RSA, 2, 0, pub_root, ×tamp, get_parameter_u32 (para, pKEYEXPIRE)); } } if (!err) err = write_keybinding (pub_root, pri_psk, sub_psk, get_parameter_uint (para, pSUBKEYUSAGE), timestamp, cache_nonce); did_sub = 1; } if (!err && outctrl->use_files) /* Direct write to specified files. */ { err = write_keyblock (outctrl->pub.stream, pub_root); if (err) log_error ("can't write public key: %s\n", gpg_strerror (err)); } else if (!err) /* Write to the standard keyrings. */ { KEYDB_HANDLE pub_hd = keydb_new (); err = keydb_locate_writable (pub_hd, NULL); if (err) log_error (_("no writable public keyring found: %s\n"), gpg_strerror (err)); if (!err && opt.verbose) { log_info (_("writing public key to '%s'\n"), keydb_get_resource_name (pub_hd)); } if (!err) { err = keydb_insert_keyblock (pub_hd, pub_root); if (err) log_error (_("error writing public keyring '%s': %s\n"), keydb_get_resource_name (pub_hd), gpg_strerror (err)); } keydb_release (pub_hd); if (!err) { int no_enc_rsa; PKT_public_key *pk; no_enc_rsa = ((get_parameter_algo (para, pKEYTYPE, NULL) == PUBKEY_ALGO_RSA) && get_parameter_uint (para, pKEYUSAGE) && !((get_parameter_uint (para, pKEYUSAGE) & PUBKEY_USAGE_ENC)) ); pk = find_kbnode (pub_root, PKT_PUBLIC_KEY)->pkt->pkt.public_key; keyid_from_pk (pk, pk->main_keyid); register_trusted_keyid (pk->main_keyid); update_ownertrust (pk, ((get_ownertrust (pk) & ~TRUST_MASK) | TRUST_ULTIMATE )); gen_standard_revoke (pk, cache_nonce); if (!opt.batch) { tty_printf (_("public and secret key created and signed.\n") ); tty_printf ("\n"); list_keyblock (pub_root, 0, 1, 1, NULL); } if (!opt.batch && (get_parameter_algo (para, pKEYTYPE, NULL) == PUBKEY_ALGO_DSA || no_enc_rsa ) && !get_parameter (para, pSUBKEYTYPE) ) { tty_printf(_("Note that this key cannot be used for " "encryption. You may want to use\n" "the command \"--edit-key\" to generate a " "subkey for this purpose.\n") ); } } } if (err) { if (opt.batch) log_error ("key generation failed: %s\n", gpg_strerror (err) ); else tty_printf (_("Key generation failed: %s\n"), gpg_strerror (err) ); write_status_error (card? "card_key_generate":"key_generate", err); print_status_key_not_created ( get_parameter_value (para, pHANDLE) ); } else { PKT_public_key *pk = find_kbnode (pub_root, PKT_PUBLIC_KEY)->pkt->pkt.public_key; print_status_key_created (did_sub? 'B':'P', pk, get_parameter_value (para, pHANDLE)); } release_kbnode (pub_root); xfree (cache_nonce); } /* Add a new subkey to an existing key. Returns 0 if a new key has been generated and put into the keyblocks. */ gpg_error_t generate_subkeypair (ctrl_t ctrl, kbnode_t keyblock) { gpg_error_t err = 0; kbnode_t node; PKT_public_key *pri_psk = NULL; PKT_public_key *sub_psk = NULL; int algo; unsigned int use; u32 expire; unsigned int nbits = 0; char *curve = NULL; u32 cur_time; char *hexgrip = NULL; char *serialno = NULL; /* Break out the primary key. */ node = find_kbnode (keyblock, PKT_PUBLIC_KEY); if (!node) { log_error ("Oops; primary key missing in keyblock!\n"); err = gpg_error (GPG_ERR_BUG); goto leave; } pri_psk = node->pkt->pkt.public_key; cur_time = make_timestamp (); if (pri_psk->timestamp > cur_time) { ulong d = pri_psk->timestamp - cur_time; log_info ( d==1 ? _("key has been created %lu second " "in future (time warp or clock problem)\n") : _("key has been created %lu seconds " "in future (time warp or clock problem)\n"), d ); if (!opt.ignore_time_conflict) { err = gpg_error (GPG_ERR_TIME_CONFLICT); goto leave; } } if (pri_psk->version < 4) { log_info (_("Note: creating subkeys for v3 keys " "is not OpenPGP compliant\n")); err = gpg_error (GPG_ERR_CONFLICT); goto leave; } err = hexkeygrip_from_pk (pri_psk, &hexgrip); if (err) goto leave; if (agent_get_keyinfo (NULL, hexgrip, &serialno)) { tty_printf (_("Secret parts of primary key are not available.\n")); goto leave; } if (serialno) tty_printf (_("Secret parts of primary key are stored on-card.\n")); xfree (hexgrip); hexgrip = NULL; algo = ask_algo (ctrl, 1, NULL, &use, &hexgrip); assert (algo); if (hexgrip) nbits = 0; else if (algo == PUBKEY_ALGO_ECDSA || algo == PUBKEY_ALGO_EDDSA || algo == PUBKEY_ALGO_ECDH) curve = ask_curve (&algo, 0); else nbits = ask_keysize (algo, 0); expire = ask_expire_interval (0, NULL); if (!cpr_enabled() && !cpr_get_answer_is_yes("keygen.sub.okay", _("Really create? (y/N) "))) { err = gpg_error (GPG_ERR_CANCELED); goto leave; } if (hexgrip) err = do_create_from_keygrip (ctrl, algo, hexgrip, keyblock, cur_time, expire, 1); else err = do_create (algo, nbits, curve, keyblock, cur_time, expire, 1, 0, NULL, NULL); if (err) goto leave; /* Get the pointer to the generated public subkey packet. */ for (node = keyblock; node; node = node->next) if (node->pkt->pkttype == PKT_PUBLIC_SUBKEY) sub_psk = node->pkt->pkt.public_key; /* Write the binding signature. */ err = write_keybinding (keyblock, pri_psk, sub_psk, use, cur_time, NULL); if (err) goto leave; write_status_text (STATUS_KEY_CREATED, "S"); leave: xfree (curve); xfree (hexgrip); xfree (serialno); if (err) log_error (_("Key generation failed: %s\n"), gpg_strerror (err) ); return err; } #ifdef ENABLE_CARD_SUPPORT /* Generate a subkey on a card. */ gpg_error_t generate_card_subkeypair (kbnode_t pub_keyblock, int keyno, const char *serialno) { gpg_error_t err = 0; kbnode_t node; PKT_public_key *pri_pk = NULL; int algo; unsigned int use; u32 expire; u32 cur_time; struct para_data_s *para = NULL; assert (keyno >= 1 && keyno <= 3); para = xtrycalloc (1, sizeof *para + strlen (serialno) ); if (!para) { err = gpg_error_from_syserror (); goto leave; } para->key = pSERIALNO; strcpy (para->u.value, serialno); /* Break out the primary secret key */ node = find_kbnode (pub_keyblock, PKT_PUBLIC_KEY); if (!node) { log_error ("Oops; publkic key lost!\n"); err = gpg_error (GPG_ERR_INTERNAL); goto leave; } pri_pk = node->pkt->pkt.public_key; cur_time = make_timestamp(); if (pri_pk->timestamp > cur_time) { ulong d = pri_pk->timestamp - cur_time; log_info (d==1 ? _("key has been created %lu second " "in future (time warp or clock problem)\n") : _("key has been created %lu seconds " "in future (time warp or clock problem)\n"), d ); if (!opt.ignore_time_conflict) { err = gpg_error (GPG_ERR_TIME_CONFLICT); goto leave; } } if (pri_pk->version < 4) { log_info (_("Note: creating subkeys for v3 keys " "is not OpenPGP compliant\n")); err = gpg_error (GPG_ERR_NOT_SUPPORTED); goto leave; } algo = PUBKEY_ALGO_RSA; expire = ask_expire_interval (0, NULL); if (keyno == 1) use = PUBKEY_USAGE_SIG; else if (keyno == 2) use = PUBKEY_USAGE_ENC; else use = PUBKEY_USAGE_AUTH; if (!cpr_enabled() && !cpr_get_answer_is_yes("keygen.cardsub.okay", _("Really create? (y/N) "))) { err = gpg_error (GPG_ERR_CANCELED); goto leave; } /* Note, that depending on the backend, the card key generation may update CUR_TIME. */ err = gen_card_key (algo, keyno, 0, pub_keyblock, &cur_time, expire); /* Get the pointer to the generated public subkey packet. */ if (!err) { PKT_public_key *sub_pk = NULL; for (node = pub_keyblock; node; node = node->next) if (node->pkt->pkttype == PKT_PUBLIC_SUBKEY) sub_pk = node->pkt->pkt.public_key; assert (sub_pk); err = write_keybinding (pub_keyblock, pri_pk, sub_pk, use, cur_time, NULL); } leave: if (err) log_error (_("Key generation failed: %s\n"), gpg_strerror (err) ); else write_status_text (STATUS_KEY_CREATED, "S"); release_parameter_list (para); return err; } #endif /* !ENABLE_CARD_SUPPORT */ /* * Write a keyblock to an output stream */ static int write_keyblock( IOBUF out, KBNODE node ) { for( ; node ; node = node->next ) { if(!is_deleted_kbnode(node)) { int rc = build_packet( out, node->pkt ); if( rc ) { log_error("build_packet(%d) failed: %s\n", node->pkt->pkttype, gpg_strerror (rc) ); return rc; } } } return 0; } /* Note that timestamp is an in/out arg. */ static gpg_error_t gen_card_key (int algo, int keyno, int is_primary, kbnode_t pub_root, u32 *timestamp, u32 expireval) { #ifdef ENABLE_CARD_SUPPORT gpg_error_t err; struct agent_card_genkey_s info; PACKET *pkt; PKT_public_key *pk; if (algo != PUBKEY_ALGO_RSA) return gpg_error (GPG_ERR_PUBKEY_ALGO); pk = xtrycalloc (1, sizeof *pk ); if (!pk) return gpg_error_from_syserror (); pkt = xtrycalloc (1, sizeof *pkt); if (!pkt) { xfree (pk); return gpg_error_from_syserror (); } /* Note: SCD knows the serialnumber, thus there is no point in passing it. */ err = agent_scd_genkey (&info, keyno, 1, NULL, *timestamp); /* The code below is not used because we force creation of * the a card key (3rd arg). * if (gpg_err_code (rc) == GPG_ERR_EEXIST) * { * tty_printf ("\n"); * log_error ("WARNING: key does already exists!\n"); * tty_printf ("\n"); * if ( cpr_get_answer_is_yes( "keygen.card.replace_key", * _("Replace existing key? "))) * rc = agent_scd_genkey (&info, keyno, 1); * } */ if (!err && (!info.n || !info.e)) { log_error ("communication error with SCD\n"); gcry_mpi_release (info.n); gcry_mpi_release (info.e); err = gpg_error (GPG_ERR_GENERAL); } if (err) { log_error ("key generation failed: %s\n", gpg_strerror (err)); xfree (pkt); xfree (pk); return err; } /* Send the learn command so that the agent creates a shadow key for card key. We need to do that now so that we are able to create the self-signatures. */ err = agent_scd_learn (NULL); if (err) { /* Oops: Card removed during generation. */ log_error (_("OpenPGP card not available: %s\n"), gpg_strerror (err)); xfree (pkt); xfree (pk); return err; } if (*timestamp != info.created_at) log_info ("NOTE: the key does not use the suggested creation date\n"); *timestamp = info.created_at; pk->timestamp = info.created_at; pk->version = 4; if (expireval) pk->expiredate = pk->timestamp + expireval; pk->pubkey_algo = algo; pk->pkey[0] = info.n; pk->pkey[1] = info.e; pkt->pkttype = is_primary ? PKT_PUBLIC_KEY : PKT_PUBLIC_SUBKEY; pkt->pkt.public_key = pk; add_kbnode (pub_root, new_kbnode (pkt)); return 0; #else (void)algo; (void)keyno; (void)is_primary; (void)pub_root; (void)timestamp; (void)expireval; return gpg_error (GPG_ERR_NOT_SUPPORTED); #endif /*!ENABLE_CARD_SUPPORT*/ } static int gen_card_key_with_backup (int algo, int keyno, int is_primary, KBNODE pub_root, u32 timestamp, u32 expireval, struct para_data_s *para) { #if ENABLE_CARD_SUPPORT && 0 /* FIXME: Move this to gpg-agent. */ int rc; const char *s; PACKET *pkt; PKT_secret_key *sk, *sk_unprotected = NULL, *sk_protected = NULL; PKT_public_key *pk; size_t n; int i; unsigned int nbits; /* Get the size of the key directly from the card. */ { struct agent_card_info_s info; memset (&info, 0, sizeof info); if (!agent_scd_getattr ("KEY-ATTR", &info) && info.key_attr[1].algo) nbits = info.key_attr[1].nbits; else nbits = 1024; /* All pre-v2.0 cards. */ agent_release_card_info (&info); } /* Create a key of this size in memory. */ rc = generate_raw_key (algo, nbits, timestamp, &sk_unprotected, &sk_protected); if (rc) return rc; /* Store the key to the card. */ rc = save_unprotected_key_to_card (sk_unprotected, keyno); if (rc) { log_error (_("storing key onto card failed: %s\n"), gpg_strerror (rc)); free_secret_key (sk_unprotected); free_secret_key (sk_protected); write_status_errcode ("save_key_to_card", rc); return rc; } /* Get rid of the secret key parameters and store the serial numer. */ sk = sk_unprotected; n = pubkey_get_nskey (sk->pubkey_algo); for (i=pubkey_get_npkey (sk->pubkey_algo); i < n; i++) { gcry_mpi_release (sk->skey[i]); sk->skey[i] = NULL; } i = pubkey_get_npkey (sk->pubkey_algo); sk->skey[i] = gcry_mpi_set_opaque (NULL, xstrdup ("dummydata"), 10*8); sk->is_protected = 1; sk->protect.s2k.mode = 1002; s = get_parameter_value (para, pSERIALNO); assert (s); for (sk->protect.ivlen=0; sk->protect.ivlen < 16 && *s && s[1]; sk->protect.ivlen++, s += 2) sk->protect.iv[sk->protect.ivlen] = xtoi_2 (s); /* Now write the *protected* secret key to the file. */ { char name_buffer[50]; char *fname; IOBUF fp; mode_t oldmask; keyid_from_sk (sk, NULL); snprintf (name_buffer, sizeof name_buffer, "sk_%08lX%08lX.gpg", (ulong)sk->keyid[0], (ulong)sk->keyid[1]); fname = make_filename (backup_dir, name_buffer, NULL); /* Note that the umask call is not anymore needed because iobuf_create now takes care of it. However, it does not harm and thus we keep it. */ oldmask = umask (077); if (is_secured_filename (fname)) { fp = NULL; gpg_err_set_errno (EPERM); } else fp = iobuf_create (fname, 1); umask (oldmask); if (!fp) { rc = gpg_error_from_syserror (); log_error (_("can't create backup file '%s': %s\n"), fname, strerror(errno) ); xfree (fname); free_secret_key (sk_unprotected); free_secret_key (sk_protected); return rc; } pkt = xcalloc (1, sizeof *pkt); pkt->pkttype = PKT_SECRET_KEY; pkt->pkt.secret_key = sk_protected; sk_protected = NULL; rc = build_packet (fp, pkt); if (rc) { log_error("build packet failed: %s\n", gpg_strerror (rc)); iobuf_cancel (fp); } else { unsigned char array[MAX_FINGERPRINT_LEN]; char *fprbuf, *p; iobuf_close (fp); iobuf_ioctl (NULL, IOBUF_IOCTL_INVALIDATE_CACHE, 0, (char*)fname); log_info (_("Note: backup of card key saved to '%s'\n"), fname); fingerprint_from_sk (sk, array, &n); p = fprbuf = xmalloc (MAX_FINGERPRINT_LEN*2 + 1 + 1); for (i=0; i < n ; i++, p += 2) sprintf (p, "%02X", array[i]); *p++ = ' '; *p = 0; write_status_text_and_buffer (STATUS_BACKUP_KEY_CREATED, fprbuf, fname, strlen (fname), 0); xfree (fprbuf); } free_packet (pkt); xfree (pkt); xfree (fname); if (rc) { free_secret_key (sk_unprotected); return rc; } } /* Create the public key from the secret key. */ pk = xcalloc (1, sizeof *pk ); pk->timestamp = sk->timestamp; pk->version = sk->version; if (expireval) pk->expiredate = sk->expiredate = sk->timestamp + expireval; pk->pubkey_algo = sk->pubkey_algo; n = pubkey_get_npkey (sk->pubkey_algo); for (i=0; i < n; i++) pk->pkey[i] = mpi_copy (sk->skey[i]); /* Build packets and add them to the node lists. */ pkt = xcalloc (1,sizeof *pkt); pkt->pkttype = is_primary ? PKT_PUBLIC_KEY : PKT_PUBLIC_SUBKEY; pkt->pkt.public_key = pk; add_kbnode(pub_root, new_kbnode( pkt )); pkt = xcalloc (1,sizeof *pkt); pkt->pkttype = is_primary ? PKT_SECRET_KEY : PKT_SECRET_SUBKEY; pkt->pkt.secret_key = sk; add_kbnode(sec_root, new_kbnode( pkt )); return 0; #else # if __GCC__ && ENABLE_CARD_SUPPORT # warning Card support still missing # endif (void)algo; (void)keyno; (void)is_primary; (void)pub_root; (void)timestamp; (void)expireval; (void)para; return gpg_error (GPG_ERR_NOT_SUPPORTED); #endif /*!ENABLE_CARD_SUPPORT*/ } #if 0 int save_unprotected_key_to_card (PKT_public_key *sk, int keyno) { int rc; unsigned char *rsa_n = NULL; unsigned char *rsa_e = NULL; unsigned char *rsa_p = NULL; unsigned char *rsa_q = NULL; size_t rsa_n_len, rsa_e_len, rsa_p_len, rsa_q_len; unsigned char *sexp = NULL; unsigned char *p; char numbuf[55], numbuf2[50]; assert (is_RSA (sk->pubkey_algo)); assert (!sk->is_protected); /* Copy the parameters into straight buffers. */ gcry_mpi_aprint (GCRYMPI_FMT_USG, &rsa_n, &rsa_n_len, sk->skey[0]); gcry_mpi_aprint (GCRYMPI_FMT_USG, &rsa_e, &rsa_e_len, sk->skey[1]); gcry_mpi_aprint (GCRYMPI_FMT_USG, &rsa_p, &rsa_p_len, sk->skey[3]); gcry_mpi_aprint (GCRYMPI_FMT_USG, &rsa_q, &rsa_q_len, sk->skey[4]); if (!rsa_n || !rsa_e || !rsa_p || !rsa_q) { rc = GPG_ERR_INV_ARG; goto leave; } /* Put the key into an S-expression. */ sexp = p = xmalloc_secure (30 + rsa_n_len + rsa_e_len + rsa_p_len + rsa_q_len + 4*sizeof (numbuf) + 25 + sizeof(numbuf) + 20); p = stpcpy (p,"(11:private-key(3:rsa(1:n"); sprintf (numbuf, "%u:", (unsigned int)rsa_n_len); p = stpcpy (p, numbuf); memcpy (p, rsa_n, rsa_n_len); p += rsa_n_len; sprintf (numbuf, ")(1:e%u:", (unsigned int)rsa_e_len); p = stpcpy (p, numbuf); memcpy (p, rsa_e, rsa_e_len); p += rsa_e_len; sprintf (numbuf, ")(1:p%u:", (unsigned int)rsa_p_len); p = stpcpy (p, numbuf); memcpy (p, rsa_p, rsa_p_len); p += rsa_p_len; sprintf (numbuf, ")(1:q%u:", (unsigned int)rsa_q_len); p = stpcpy (p, numbuf); memcpy (p, rsa_q, rsa_q_len); p += rsa_q_len; p = stpcpy (p,"))(10:created-at"); sprintf (numbuf2, "%lu", (unsigned long)sk->timestamp); sprintf (numbuf, "%lu:", (unsigned long)strlen (numbuf2)); p = stpcpy (stpcpy (stpcpy (p, numbuf), numbuf2), "))"); /* Fixme: Unfortunately we don't have the serialnumber available - thus we can't pass it down to the agent. */ rc = agent_scd_writekey (keyno, NULL, sexp, p - sexp); leave: xfree (sexp); xfree (rsa_n); xfree (rsa_e); xfree (rsa_p); xfree (rsa_q); return rc; } #endif /*ENABLE_CARD_SUPPORT*/ diff --git a/g10/keyid.c b/g10/keyid.c index 662806b3e..9f7b70fca 100644 --- a/g10/keyid.c +++ b/g10/keyid.c @@ -1,824 +1,819 @@ /* 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 * * 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 "gpg.h" #include "util.h" #include "main.h" #include "packet.h" #include "options.h" #include "keydb.h" #include "i18n.h" #include "rmd160.h" +#include "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); if (*name && *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); memcpy (pp[i], p, (nbits+7)/8); 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 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)) { gcry_md_write (md, pp[0], nn[0]); } else for(i=0; i < npkey; 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] = (p[0] << 24) | (p[1] <<16) | (p[2] << 8) | p[3]; + ki[0] = buf32_to_u32 (p); p += 4; - ki[1] = (p[0] << 24) | (p[1] <<16) | (p[2] << 8) | p[3]; + ki[1] = buf32_to_u32 (p); } xfree (buffer); return ki[1]; } size_t keystrlen(void) { switch(opt.keyid_format) { 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]; switch (opt.keyid_format) { case KF_SHORT: snprintf (keyid_str, sizeof keyid_str, "%08lX", (ulong)keyid[1]); break; case KF_LONG: if (keyid[0]) snprintf (keyid_str, sizeof keyid_str, "%08lX%08lX", (ulong)keyid[0], (ulong)keyid[1]); else snprintf (keyid_str, sizeof keyid_str, "%08lX", (ulong)keyid[1]); break; case KF_0xSHORT: snprintf (keyid_str, sizeof keyid_str, "0x%08lX", (ulong)keyid[1]); break; case KF_0xLONG: if(keyid[0]) snprintf (keyid_str, sizeof keyid_str, "0x%08lX%08lX", (ulong)keyid[0],(ulong)keyid[1]); else snprintf (keyid_str, sizeof keyid_str, "0x%08lX", (ulong)keyid[1]); break; default: BUG(); } return keyid_str; } const char * keystr_with_sub (u32 *main_kid, u32 *sub_kid) { static char buffer[KEYID_STR_SIZE+1+KEYID_STR_SIZE]; char *p; mem2str (buffer, keystr (main_kid), KEYID_STR_SIZE); if (sub_kid) { p = buffer + strlen (buffer); *p++ = '/'; mem2str (p, keystr (sub_kid), 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); } 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] = ((unsigned char)desc->u.fpr[12] << 24 - | (unsigned char)desc->u.fpr[13] << 16 - | (unsigned char)desc->u.fpr[14] << 8 - | (unsigned char)desc->u.fpr[15]); - keyid[1] = ((unsigned char)desc->u.fpr[16] << 24 - | (unsigned char)desc->u.fpr[17] << 16 - | (unsigned char)desc->u.fpr[18] << 8 - | (unsigned char)desc->u.fpr[19]); - + 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] = dp[12] << 24 | dp[13] << 16 | dp[14] << 8 | dp[15] ; - keyid[1] = dp[16] << 24 | dp[17] << 16 | dp[18] << 8 | dp[19] ; + 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( 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 (&pk, 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] = dp[12] << 24 | dp[13] << 16 | dp[14] << 8 | dp[15] ; - keyid[1] = dp[16] << 24 | dp[17] << 16 | dp[18] << 8 | dp[19] ; + 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); } static const char * mk_datestr (char *buffer, time_t atime) { struct tm *tp; if (IS_INVALID_TIME_T (atime)) strcpy (buffer, "????" "-??" "-??"); /* Mark this as invalid. */ else { tp = gmtime (&atime); sprintf (buffer,"%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[11+5]; time_t atime = pk->timestamp; return mk_datestr (buffer, atime); } const char * datestr_from_sig (PKT_signature *sig ) { static char buffer[11+5]; time_t atime = sig->timestamp; return mk_datestr (buffer, atime); } const char * expirestr_from_pk (PKT_public_key *pk) { static char buffer[11+5]; time_t atime; if (!pk->expiredate) return _("never "); atime = pk->expiredate; return mk_datestr (buffer, atime); } const char * expirestr_from_sig (PKT_signature *sig) { static char buffer[11+5]; time_t atime; if (!sig->expiredate) return _("never "); atime=sig->expiredate; return mk_datestr (buffer, atime); } const char * revokestr_from_pk( PKT_public_key *pk ) { static char buffer[11+5]; time_t atime; if(!pk->revoked.date) return _("never "); atime=pk->revoked.date; return mk_datestr (buffer, atime); } 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)); assert( len <= MAX_FINGERPRINT_LEN ); if (!array) array = xmalloc ( len ); memcpy (array, dp, len ); - pk->keyid[0] = dp[12] << 24 | dp[13] << 16 | dp[14] << 8 | dp[15] ; - pk->keyid[1] = dp[16] << 24 | dp[17] << 16 | dp[18] << 8 | dp[19] ; + 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. */ char * hexfingerprint (PKT_public_key *pk) { unsigned char fpr[MAX_FINGERPRINT_LEN]; size_t len; char *result; fingerprint_from_pk (pk, fpr, &len); result = xmalloc (2 * len + 1); bin2hex (fpr, len, result); return result; } /* 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 sucess 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)))" : "(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)) { log_info ("error computing keygrip\n"); memset (array, 0, 20); err = gpg_error (GPG_ERR_GENERAL); } else { if (DBG_PACKET) log_printhex ("keygrip=", array, 20); /* FIXME: Save the keygrip in PK. */ } gcry_sexp_release (s_pkey); return 0; } /* 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/misc.c b/g10/misc.c index a2b5075ed..37582afa3 100644 --- a/g10/misc.c +++ b/g10/misc.c @@ -1,1775 +1,1765 @@ /* misc.c - miscellaneous functions * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, * 2008, 2009, 2010 Free Software Foundation, Inc. * Copyright (C) 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 #if defined(__linux__) && defined(__alpha__) && __GLIBC__ < 2 #include #include #endif #ifdef HAVE_SETRLIMIT #include #include #include #endif #ifdef ENABLE_SELINUX_HACKS #include #endif #ifdef HAVE_W32_SYSTEM #include #include #ifdef HAVE_WINSOCK2_H # include #endif #include #include #ifndef CSIDL_APPDATA #define CSIDL_APPDATA 0x001a #endif #ifndef CSIDL_LOCAL_APPDATA #define CSIDL_LOCAL_APPDATA 0x001c #endif #ifndef CSIDL_FLAG_CREATE #define CSIDL_FLAG_CREATE 0x8000 #endif #endif /*HAVE_W32_SYSTEM*/ #include "gpg.h" #ifdef HAVE_W32_SYSTEM # include "status.h" #endif /*HAVE_W32_SYSTEM*/ #include "util.h" #include "main.h" #include "photoid.h" #include "options.h" #include "call-agent.h" #include "i18n.h" #include static int string_count_chr (const char *string, int c) { int count; for (count=0; *string; string++ ) if ( *string == c ) count++; return count; } #ifdef ENABLE_SELINUX_HACKS /* A object and a global variable to keep track of files marked as secured. */ struct secured_file_item { struct secured_file_item *next; ino_t ino; dev_t dev; }; static struct secured_file_item *secured_files; #endif /*ENABLE_SELINUX_HACKS*/ /* For the sake of SELinux we want to restrict access through gpg to certain files we keep under our own control. This function registers such a file and is_secured_file may then be used to check whether a file has ben registered as secured. */ void register_secured_file (const char *fname) { #ifdef ENABLE_SELINUX_HACKS struct stat buf; struct secured_file_item *sf; /* Note that we stop immediatley if something goes wrong here. */ if (stat (fname, &buf)) log_fatal (_("fstat of '%s' failed in %s: %s\n"), fname, "register_secured_file", strerror (errno)); /* log_debug ("registering '%s' i=%lu.%lu\n", fname, */ /* (unsigned long)buf.st_dev, (unsigned long)buf.st_ino); */ for (sf=secured_files; sf; sf = sf->next) { if (sf->ino == buf.st_ino && sf->dev == buf.st_dev) return; /* Already registered. */ } sf = xmalloc (sizeof *sf); sf->ino = buf.st_ino; sf->dev = buf.st_dev; sf->next = secured_files; secured_files = sf; #else /*!ENABLE_SELINUX_HACKS*/ (void)fname; #endif /*!ENABLE_SELINUX_HACKS*/ } /* Remove a file registered as secure. */ void unregister_secured_file (const char *fname) { #ifdef ENABLE_SELINUX_HACKS struct stat buf; struct secured_file_item *sf, *sfprev; if (stat (fname, &buf)) { log_error (_("fstat of '%s' failed in %s: %s\n"), fname, "unregister_secured_file", strerror (errno)); return; } /* log_debug ("unregistering '%s' i=%lu.%lu\n", fname, */ /* (unsigned long)buf.st_dev, (unsigned long)buf.st_ino); */ for (sfprev=NULL,sf=secured_files; sf; sfprev=sf, sf = sf->next) { if (sf->ino == buf.st_ino && sf->dev == buf.st_dev) { if (sfprev) sfprev->next = sf->next; else secured_files = sf->next; xfree (sf); return; } } #else /*!ENABLE_SELINUX_HACKS*/ (void)fname; #endif /*!ENABLE_SELINUX_HACKS*/ } /* Return true if FD is corresponds to a secured file. Using -1 for FS is allowed and will return false. */ int is_secured_file (int fd) { #ifdef ENABLE_SELINUX_HACKS struct stat buf; struct secured_file_item *sf; if (fd == -1) return 0; /* No file descriptor so it can't be secured either. */ /* Note that we print out a error here and claim that a file is secure if something went wrong. */ if (fstat (fd, &buf)) { log_error (_("fstat(%d) failed in %s: %s\n"), fd, "is_secured_file", strerror (errno)); return 1; } /* log_debug ("is_secured_file (%d) i=%lu.%lu\n", fd, */ /* (unsigned long)buf.st_dev, (unsigned long)buf.st_ino); */ for (sf=secured_files; sf; sf = sf->next) { if (sf->ino == buf.st_ino && sf->dev == buf.st_dev) return 1; /* Yes. */ } #else /*!ENABLE_SELINUX_HACKS*/ (void)fd; #endif /*!ENABLE_SELINUX_HACKS*/ return 0; /* No. */ } /* Return true if FNAME is corresponds to a secured file. Using NULL, "" or "-" for FS is allowed and will return false. This function is used before creating a file, thus it won't fail if the file does not exist. */ int is_secured_filename (const char *fname) { #ifdef ENABLE_SELINUX_HACKS struct stat buf; struct secured_file_item *sf; if (iobuf_is_pipe_filename (fname) || !*fname) return 0; /* Note that we print out a error here and claim that a file is secure if something went wrong. */ if (stat (fname, &buf)) { if (errno == ENOENT || errno == EPERM || errno == EACCES) return 0; log_error (_("fstat of '%s' failed in %s: %s\n"), fname, "is_secured_filename", strerror (errno)); return 1; } /* log_debug ("is_secured_filename (%s) i=%lu.%lu\n", fname, */ /* (unsigned long)buf.st_dev, (unsigned long)buf.st_ino); */ for (sf=secured_files; sf; sf = sf->next) { if (sf->ino == buf.st_ino && sf->dev == buf.st_dev) return 1; /* Yes. */ } #else /*!ENABLE_SELINUX_HACKS*/ (void)fname; #endif /*!ENABLE_SELINUX_HACKS*/ return 0; /* No. */ } u16 checksum_u16( unsigned n ) { u16 a; a = (n >> 8) & 0xff; a += n & 0xff; return a; } u16 checksum( byte *p, unsigned n ) { u16 a; for(a=0; n; n-- ) a += *p++; return a; } u16 checksum_mpi (gcry_mpi_t a) { u16 csum; byte *buffer; size_t nbytes; if ( gcry_mpi_print (GCRYMPI_FMT_PGP, NULL, 0, &nbytes, a) ) BUG (); /* Fixme: For numbers not in secure memory we should use a stack * based buffer and only allocate a larger one if mpi_print returns * an error. */ buffer = (gcry_is_secure(a)? gcry_xmalloc_secure (nbytes) : gcry_xmalloc (nbytes)); if ( gcry_mpi_print (GCRYMPI_FMT_PGP, buffer, nbytes, NULL, a) ) BUG (); csum = checksum (buffer, nbytes); xfree (buffer); return csum; } -u32 -buffer_to_u32( const byte *buffer ) -{ - unsigned long a; - a = *buffer << 24; - a |= buffer[1] << 16; - a |= buffer[2] << 8; - a |= buffer[3]; - return a; -} void print_pubkey_algo_note (pubkey_algo_t algo) { if(algo >= 100 && algo <= 110) { static int warn=0; if(!warn) { warn=1; es_fflush (es_stdout); log_info (_("WARNING: using experimental public key algorithm %s\n"), openpgp_pk_algo_name (algo)); } } else if (algo == PUBKEY_ALGO_ELGAMAL) { es_fflush (es_stdout); log_info (_("WARNING: Elgamal sign+encrypt keys are deprecated\n")); } } void print_cipher_algo_note (cipher_algo_t algo) { if(algo >= 100 && algo <= 110) { static int warn=0; if(!warn) { warn=1; es_fflush (es_stdout); log_info (_("WARNING: using experimental cipher algorithm %s\n"), openpgp_cipher_algo_name (algo)); } } } void print_digest_algo_note (digest_algo_t algo) { if(algo >= 100 && algo <= 110) { static int warn=0; if(!warn) { warn=1; es_fflush (es_stdout); log_info (_("WARNING: using experimental digest algorithm %s\n"), gcry_md_algo_name (algo)); } } else if(algo==DIGEST_ALGO_MD5) { es_fflush (es_stdout); log_info (_("WARNING: digest algorithm %s is deprecated\n"), gcry_md_algo_name (algo)); } } void print_md5_rejected_note (void) { static int shown; if (!shown) { es_fflush (es_stdout); log_info (_("Note: signatures using the %s algorithm are rejected\n"), "MD5"); shown = 1; } } /* Map OpenPGP algo numbers to those used by Libgcrypt. We need to do this for algorithms we implemented in Libgcrypt after they become part of OpenPGP. */ enum gcry_cipher_algos map_cipher_openpgp_to_gcry (cipher_algo_t algo) { switch (algo) { case CIPHER_ALGO_NONE: return GCRY_CIPHER_NONE; #ifdef GPG_USE_IDEA case CIPHER_ALGO_IDEA: return GCRY_CIPHER_IDEA; #else case CIPHER_ALGO_IDEA: return 0; #endif case CIPHER_ALGO_3DES: return GCRY_CIPHER_3DES; #ifdef GPG_USE_CAST5 case CIPHER_ALGO_CAST5: return GCRY_CIPHER_CAST5; #else case CIPHER_ALGO_CAST5: return 0; #endif #ifdef GPG_USE_BLOWFISH case CIPHER_ALGO_BLOWFISH: return GCRY_CIPHER_BLOWFISH; #else case CIPHER_ALGO_BLOWFISH: return 0; #endif #ifdef GPG_USE_AES128 case CIPHER_ALGO_AES: return GCRY_CIPHER_AES; #else case CIPHER_ALGO_AES: return 0; #endif #ifdef GPG_USE_AES192 case CIPHER_ALGO_AES192: return GCRY_CIPHER_AES192; #else case CIPHER_ALGO_AES192: return 0; #endif #ifdef GPG_USE_AES256 case CIPHER_ALGO_AES256: return GCRY_CIPHER_AES256; #else case CIPHER_ALGO_AES256: return 0; #endif #ifdef GPG_USE_TWOFISH case CIPHER_ALGO_TWOFISH: return GCRY_CIPHER_TWOFISH; #else case CIPHER_ALGO_TWOFISH: return 0; #endif #ifdef GPG_USE_CAMELLIA128 case CIPHER_ALGO_CAMELLIA128: return GCRY_CIPHER_CAMELLIA128; #else case CIPHER_ALGO_CAMELLIA128: return 0; #endif #ifdef GPG_USE_CAMELLIA192 case CIPHER_ALGO_CAMELLIA192: return GCRY_CIPHER_CAMELLIA192; #else case CIPHER_ALGO_CAMELLIA192: return 0; #endif #ifdef GPG_USE_CAMELLIA256 case CIPHER_ALGO_CAMELLIA256: return GCRY_CIPHER_CAMELLIA256; #else case CIPHER_ALGO_CAMELLIA256: return 0; #endif } return 0; } /* The inverse function of above. */ static cipher_algo_t map_cipher_gcry_to_openpgp (enum gcry_cipher_algos algo) { switch (algo) { case GCRY_CIPHER_NONE: return CIPHER_ALGO_NONE; case GCRY_CIPHER_IDEA: return CIPHER_ALGO_IDEA; case GCRY_CIPHER_3DES: return CIPHER_ALGO_3DES; case GCRY_CIPHER_CAST5: return CIPHER_ALGO_CAST5; case GCRY_CIPHER_BLOWFISH: return CIPHER_ALGO_BLOWFISH; case GCRY_CIPHER_AES: return CIPHER_ALGO_AES; case GCRY_CIPHER_AES192: return CIPHER_ALGO_AES192; case GCRY_CIPHER_AES256: return CIPHER_ALGO_AES256; case GCRY_CIPHER_TWOFISH: return CIPHER_ALGO_TWOFISH; case GCRY_CIPHER_CAMELLIA128: return CIPHER_ALGO_CAMELLIA128; case GCRY_CIPHER_CAMELLIA192: return CIPHER_ALGO_CAMELLIA192; case GCRY_CIPHER_CAMELLIA256: return CIPHER_ALGO_CAMELLIA256; default: return 0; } } /* Map Gcrypt public key algorithm numbers to those used by OpenPGP. FIXME: This mapping is used at only two places - we should get rid of it. */ pubkey_algo_t map_pk_gcry_to_openpgp (enum gcry_pk_algos algo) { switch (algo) { case GCRY_PK_ECDSA: return PUBKEY_ALGO_ECDSA; case GCRY_PK_ECDH: return PUBKEY_ALGO_ECDH; default: return algo < 110 ? algo : 0; } } /* Return the block length of an OpenPGP cipher algorithm. */ int openpgp_cipher_blocklen (cipher_algo_t algo) { /* We use the numbers from OpenPGP to be sure that we get the right block length. This is so that the packet parsing code works even for unknown algorithms (for which we assume 8 due to tradition). NOTE: If you change the the returned blocklen above 16, check the callers because they may use a fixed size buffer of that size. */ switch (algo) { case CIPHER_ALGO_AES: case CIPHER_ALGO_AES192: case CIPHER_ALGO_AES256: case CIPHER_ALGO_TWOFISH: case CIPHER_ALGO_CAMELLIA128: case CIPHER_ALGO_CAMELLIA192: case CIPHER_ALGO_CAMELLIA256: return 16; default: return 8; } } /**************** * Wrapper around the libgcrypt function with additonal checks on * the OpenPGP contraints for the algo ID. */ int openpgp_cipher_test_algo (cipher_algo_t algo) { enum gcry_cipher_algos ga; ga = map_cipher_openpgp_to_gcry (algo); if (!ga) return gpg_error (GPG_ERR_CIPHER_ALGO); return gcry_cipher_test_algo (ga); } /* Map the OpenPGP cipher algorithm whose ID is contained in ALGORITHM to a string representation of the algorithm name. For unknown algorithm IDs this function returns "?". */ const char * openpgp_cipher_algo_name (cipher_algo_t algo) { switch (algo) { case CIPHER_ALGO_NONE: break; case CIPHER_ALGO_IDEA: return "IDEA"; case CIPHER_ALGO_3DES: return "3DES"; case CIPHER_ALGO_CAST5: return "CAST5"; case CIPHER_ALGO_BLOWFISH: return "BLOWFISH"; case CIPHER_ALGO_AES: return "AES"; case CIPHER_ALGO_AES192: return "AES192"; case CIPHER_ALGO_AES256: return "AES256"; case CIPHER_ALGO_TWOFISH: return "TWOFISH"; case CIPHER_ALGO_CAMELLIA128: return "CAMELLIA128"; case CIPHER_ALGO_CAMELLIA192: return "CAMELLIA192"; case CIPHER_ALGO_CAMELLIA256: return "CAMELLIA256"; } return "?"; } /* Return 0 if ALGO is a supported OpenPGP public key algorithm. */ int openpgp_pk_test_algo (pubkey_algo_t algo) { return openpgp_pk_test_algo2 (algo, 0); } /* Return 0 if ALGO is a supported OpenPGP public key algorithm and allows the usage USE. */ int openpgp_pk_test_algo2 (pubkey_algo_t algo, unsigned int use) { enum gcry_pk_algos ga = 0; size_t use_buf = use; switch (algo) { #ifdef GPG_USE_RSA case PUBKEY_ALGO_RSA: ga = GCRY_PK_RSA; break; case PUBKEY_ALGO_RSA_E: ga = GCRY_PK_RSA_E; break; case PUBKEY_ALGO_RSA_S: ga = GCRY_PK_RSA_S; break; #else case PUBKEY_ALGO_RSA: break; case PUBKEY_ALGO_RSA_E: break; case PUBKEY_ALGO_RSA_S: break; #endif case PUBKEY_ALGO_ELGAMAL_E: ga = GCRY_PK_ELG; break; case PUBKEY_ALGO_DSA: ga = GCRY_PK_DSA; break; #ifdef GPG_USE_ECDH case PUBKEY_ALGO_ECDH: ga = GCRY_PK_ECC; break; #else case PUBKEY_ALGO_ECDH: break; #endif #ifdef GPG_USE_ECDSA case PUBKEY_ALGO_ECDSA: ga = GCRY_PK_ECC; break; #else case PUBKEY_ALGO_ECDSA: break; #endif #ifdef GPG_USE_EDDSA case PUBKEY_ALGO_EDDSA: ga = GCRY_PK_ECC; break; #else case PUBKEY_ALGO_EDDSA: break; #endif case PUBKEY_ALGO_ELGAMAL: /* Dont't allow type 20 keys unless in rfc2440 mode. */ if (RFC2440) ga = GCRY_PK_ELG; break; } if (!ga) return gpg_error (GPG_ERR_PUBKEY_ALGO); /* No check whether Libgcrypt has support for the algorithm. */ return gcry_pk_algo_info (ga, GCRYCTL_TEST_ALGO, NULL, &use_buf); } int openpgp_pk_algo_usage ( int algo ) { int use = 0; /* They are hardwired in gpg 1.0. */ switch ( algo ) { case PUBKEY_ALGO_RSA: use = (PUBKEY_USAGE_CERT | PUBKEY_USAGE_SIG | PUBKEY_USAGE_ENC | PUBKEY_USAGE_AUTH); break; case PUBKEY_ALGO_RSA_E: case PUBKEY_ALGO_ECDH: use = PUBKEY_USAGE_ENC; break; case PUBKEY_ALGO_RSA_S: use = PUBKEY_USAGE_CERT | PUBKEY_USAGE_SIG; break; case PUBKEY_ALGO_ELGAMAL: if (RFC2440) use = PUBKEY_USAGE_ENC; break; case PUBKEY_ALGO_ELGAMAL_E: use = PUBKEY_USAGE_ENC; break; case PUBKEY_ALGO_DSA: use = PUBKEY_USAGE_CERT | PUBKEY_USAGE_SIG | PUBKEY_USAGE_AUTH; break; case PUBKEY_ALGO_ECDSA: case PUBKEY_ALGO_EDDSA: use = PUBKEY_USAGE_CERT | PUBKEY_USAGE_SIG | PUBKEY_USAGE_AUTH; default: break; } return use; } /* Map the OpenPGP pubkey algorithm whose ID is contained in ALGO to a string representation of the algorithm name. For unknown algorithm IDs this function returns "?". */ const char * openpgp_pk_algo_name (pubkey_algo_t algo) { switch (algo) { case PUBKEY_ALGO_RSA: case PUBKEY_ALGO_RSA_E: case PUBKEY_ALGO_RSA_S: return "RSA"; case PUBKEY_ALGO_ELGAMAL: case PUBKEY_ALGO_ELGAMAL_E: return "ELG"; case PUBKEY_ALGO_DSA: return "DSA"; case PUBKEY_ALGO_ECDH: return "ECDH"; case PUBKEY_ALGO_ECDSA: return "ECDSA"; case PUBKEY_ALGO_EDDSA: return "EDDSA"; } return "?"; } /* Explicit mapping of OpenPGP digest algos to Libgcrypt. */ /* FIXME: We do not yes use it everywhere. */ enum gcry_md_algos map_md_openpgp_to_gcry (digest_algo_t algo) { switch (algo) { #ifdef GPG_USE_MD5 case DIGEST_ALGO_MD5: return GCRY_MD_MD5; #else case DIGEST_ALGO_MD5: return 0; #endif case DIGEST_ALGO_SHA1: return GCRY_MD_SHA1; #ifdef GPG_USE_RMD160 case DIGEST_ALGO_RMD160: return GCRY_MD_RMD160; #else case DIGEST_ALGO_RMD160: return 0; #endif #ifdef GPG_USE_SHA224 case DIGEST_ALGO_SHA224: return GCRY_MD_SHA224; #else case DIGEST_ALGO_SHA224: return 0; #endif case DIGEST_ALGO_SHA256: return GCRY_MD_SHA256; #ifdef GPG_USE_SHA384 case DIGEST_ALGO_SHA384: return GCRY_MD_SHA384; #else case DIGEST_ALGO_SHA384: return 0; #endif #ifdef GPG_USE_SHA512 case DIGEST_ALGO_SHA512: return GCRY_MD_SHA512; #else case DIGEST_ALGO_SHA512: return 0; #endif } return 0; } /* Return 0 if ALGO is suitable and implemented OpenPGP hash algorithm. */ int openpgp_md_test_algo (digest_algo_t algo) { enum gcry_md_algos ga; ga = map_md_openpgp_to_gcry (algo); if (!ga) return gpg_error (GPG_ERR_DIGEST_ALGO); return gcry_md_test_algo (ga); } /* Map the OpenPGP digest algorithm whose ID is contained in ALGO to a string representation of the algorithm name. For unknown algorithm IDs this function returns "?". */ const char * openpgp_md_algo_name (int algo) { switch (algo) { case DIGEST_ALGO_MD5: return "MD5"; case DIGEST_ALGO_SHA1: return "SHA1"; case DIGEST_ALGO_RMD160: return "RIPEMD160"; case DIGEST_ALGO_SHA256: return "SHA256"; case DIGEST_ALGO_SHA384: return "SHA384"; case DIGEST_ALGO_SHA512: return "SHA512"; case DIGEST_ALGO_SHA224: return "SHA224"; } return "?"; } static unsigned long get_signature_count (PKT_public_key *pk) { #ifdef ENABLE_CARD_SUPPORT struct agent_card_info_s info; (void)pk; if (!agent_scd_getattr ("SIG-COUNTER",&info)) return info.sig_counter; else return 0; #else (void)pk; return 0; #endif } /* Expand %-strings. Returns a string which must be xfreed. Returns NULL if the string cannot be expanded (too large). */ char * pct_expando(const char *string,struct expando_args *args) { const char *ch=string; int idx=0,maxlen=0,done=0; u32 pk_keyid[2]={0,0},sk_keyid[2]={0,0}; char *ret=NULL; if(args->pk) keyid_from_pk(args->pk,pk_keyid); if(args->pksk) keyid_from_pk (args->pksk, sk_keyid); /* This is used so that %k works in photoid command strings in --list-secret-keys (which of course has a sk, but no pk). */ if(!args->pk && args->pksk) keyid_from_pk (args->pksk, pk_keyid); while(*ch!='\0') { if(!done) { /* 8192 is way bigger than we'll need here */ if(maxlen>=8192) goto fail; maxlen+=1024; ret=xrealloc(ret,maxlen); } done=0; if(*ch=='%') { switch(*(ch+1)) { case 's': /* short key id */ if(idx+8namehash) { char *tmp = zb32_encode (args->namehash, 8*20); if (tmp) { if (idx + strlen (tmp) < maxlen) { strcpy (ret+idx, tmp); idx += strlen (tmp); } xfree (tmp); done = 1; } } break; case 'c': /* signature count from card, if any. */ if(idx+10pksk)); idx+=strlen(&ret[idx]); done=1; } break; case 'f': /* Fingerprint of key being signed */ case 'p': /* Fingerprint of the primary key making the signature. */ case 'g': /* Fingerprint of thge key making the signature. */ { byte array[MAX_FINGERPRINT_LEN]; size_t len; int i; if ((*(ch+1))=='f' && args->pk) fingerprint_from_pk (args->pk, array, &len); else if ((*(ch+1))=='p' && args->pksk) { if(args->pksk->flags.primary) fingerprint_from_pk (args->pksk, array, &len); else if (args->pksk->main_keyid[0] || args->pksk->main_keyid[1]) { /* Not the primary key: Find the fingerprint of the primary key. */ PKT_public_key *pk= xmalloc_clear(sizeof(PKT_public_key)); if (!get_pubkey_fast (pk,args->pksk->main_keyid)) fingerprint_from_pk (pk, array, &len); else memset (array, 0, (len=MAX_FINGERPRINT_LEN)); free_public_key (pk); } else /* Oops: info about the primary key missing. */ memset(array,0,(len=MAX_FINGERPRINT_LEN)); } else if((*(ch+1))=='g' && args->pksk) fingerprint_from_pk (args->pksk, array, &len); else memset(array,0,(len=MAX_FINGERPRINT_LEN)); if(idx+(len*2)validity_info && idx+1validity_info; ret[idx]='\0'; done=1; } break; /* The text string types */ case 't': case 'T': case 'V': { const char *str=NULL; switch(*(ch+1)) { case 't': /* e.g. "jpg" */ str=image_type_to_string(args->imagetype,0); break; case 'T': /* e.g. "image/jpeg" */ str=image_type_to_string(args->imagetype,2); break; case 'V': /* e.g. "full", "expired", etc. */ str=args->validity_string; break; } if(str && idx+strlen(str)" and return true if this is the case. */ int is_valid_user_id (const char *uid) { if (!uid || !*uid) return 0; return 1; } /* Similar to access(2), but uses PATH to find the file. */ int path_access(const char *file,int mode) { char *envpath; int ret=-1; envpath=getenv("PATH"); if(!envpath #ifdef HAVE_DRIVE_LETTERS || (((file[0]>='A' && file[0]<='Z') || (file[0]>='a' && file[0]<='z')) && file[1]==':') #else || file[0]=='/' #endif ) return access(file,mode); else { /* At least as large as, but most often larger than we need. */ char *buffer=xmalloc(strlen(envpath)+1+strlen(file)+1); char *split,*item,*path=xstrdup(envpath); split=path; while((item=strsep(&split,PATHSEP_S))) { strcpy(buffer,item); strcat(buffer,"/"); strcat(buffer,file); ret=access(buffer,mode); if(ret==0) break; } xfree(path); xfree(buffer); } return ret; } /* Return the number of public key parameters as used by OpenPGP. */ int pubkey_get_npkey (pubkey_algo_t algo) { switch (algo) { case PUBKEY_ALGO_RSA: case PUBKEY_ALGO_RSA_E: case PUBKEY_ALGO_RSA_S: return 2; case PUBKEY_ALGO_ELGAMAL_E: return 3; case PUBKEY_ALGO_DSA: return 4; case PUBKEY_ALGO_ECDH: return 3; case PUBKEY_ALGO_ECDSA: return 2; case PUBKEY_ALGO_ELGAMAL: return 3; case PUBKEY_ALGO_EDDSA: return 2; } return 0; } /* Return the number of secret key parameters as used by OpenPGP. */ int pubkey_get_nskey (pubkey_algo_t algo) { switch (algo) { case PUBKEY_ALGO_RSA: case PUBKEY_ALGO_RSA_E: case PUBKEY_ALGO_RSA_S: return 6; case PUBKEY_ALGO_ELGAMAL_E: return 4; case PUBKEY_ALGO_DSA: return 5; case PUBKEY_ALGO_ECDH: return 4; case PUBKEY_ALGO_ECDSA: return 3; case PUBKEY_ALGO_ELGAMAL: return 4; case PUBKEY_ALGO_EDDSA: return 3; } return 0; } /* Temporary helper. */ int pubkey_get_nsig (pubkey_algo_t algo) { switch (algo) { case PUBKEY_ALGO_RSA: case PUBKEY_ALGO_RSA_E: case PUBKEY_ALGO_RSA_S: return 1; case PUBKEY_ALGO_ELGAMAL_E: return 0; case PUBKEY_ALGO_DSA: return 2; case PUBKEY_ALGO_ECDH: return 0; case PUBKEY_ALGO_ECDSA: return 2; case PUBKEY_ALGO_ELGAMAL: return 2; case PUBKEY_ALGO_EDDSA: return 2; } return 0; } /* Temporary helper. */ int pubkey_get_nenc (pubkey_algo_t algo) { switch (algo) { case PUBKEY_ALGO_RSA: case PUBKEY_ALGO_RSA_E: case PUBKEY_ALGO_RSA_S: return 1; case PUBKEY_ALGO_ELGAMAL_E: return 2; case PUBKEY_ALGO_DSA: return 0; case PUBKEY_ALGO_ECDH: return 2; case PUBKEY_ALGO_ECDSA: return 0; case PUBKEY_ALGO_ELGAMAL: return 2; case PUBKEY_ALGO_EDDSA: return 0; } return 0; } /* Temporary helper. */ unsigned int pubkey_nbits( int algo, gcry_mpi_t *key ) { int rc, nbits; gcry_sexp_t sexp; if (algo == PUBKEY_ALGO_DSA && key[0] && key[1] && key[2] && key[3]) { rc = gcry_sexp_build (&sexp, NULL, "(public-key(dsa(p%m)(q%m)(g%m)(y%m)))", key[0], key[1], key[2], key[3] ); } else if ((algo == PUBKEY_ALGO_ELGAMAL || algo == PUBKEY_ALGO_ELGAMAL_E) && key[0] && key[1] && key[2]) { rc = gcry_sexp_build (&sexp, NULL, "(public-key(elg(p%m)(g%m)(y%m)))", key[0], key[1], key[2] ); } else if (is_RSA (algo) && key[0] && key[1]) { rc = gcry_sexp_build (&sexp, NULL, "(public-key(rsa(n%m)(e%m)))", key[0], key[1] ); } else if ((algo == PUBKEY_ALGO_ECDSA || algo == PUBKEY_ALGO_ECDH || algo == PUBKEY_ALGO_EDDSA) && key[0] && key[1]) { char *curve = openpgp_oid_to_str (key[0]); if (!curve) rc = gpg_error_from_syserror (); else { rc = gcry_sexp_build (&sexp, NULL, "(public-key(ecc(curve%s)(q%m)))", curve, key[1]); xfree (curve); } } else return 0; if (rc) BUG (); nbits = gcry_pk_get_nbits (sexp); gcry_sexp_release (sexp); return nbits; } int mpi_print (estream_t fp, gcry_mpi_t a, int mode) { int n=0; if (!a) return es_fprintf (fp, "[MPI_NULL]"); if (!mode) { unsigned int n1; n1 = gcry_mpi_get_nbits(a); n += es_fprintf (fp, "[%u bits]", n1); } else if (gcry_mpi_get_flag (a, GCRYMPI_FLAG_OPAQUE)) { unsigned int nbits; unsigned char *p = gcry_mpi_get_opaque (a, &nbits); if (!p) n += es_fprintf (fp, "[invalid opaque value]"); else { nbits = (nbits + 7)/8; for (; nbits; nbits--, p++) n += es_fprintf (fp, "%02X", *p); } } else { unsigned char *buffer; if (gcry_mpi_aprint (GCRYMPI_FMT_HEX, &buffer, NULL, a)) BUG (); es_fputs (buffer, fp); n += strlen (buffer); gcry_free (buffer); } return n; } /* pkey[1] or skey[1] is Q for ECDSA, which is an uncompressed point, i.e. 04 */ unsigned int ecdsa_qbits_from_Q (unsigned int qbits) { if ((qbits%8) > 3) { log_error (_("ECDSA public key is expected to be in SEC encoding " "multiple of 8 bits\n")); return 0; } qbits -= qbits%8; qbits /= 2; return qbits; } diff --git a/g10/parse-packet.c b/g10/parse-packet.c index 012d37368..62320865c 100644 --- a/g10/parse-packet.c +++ b/g10/parse-packet.c @@ -1,2932 +1,2931 @@ /* parse-packet.c - read packets * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, * 2007, 2009, 2010 Free Software Foundation, Inc. * Copyright (C) 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 "gpg.h" #include "util.h" #include "packet.h" #include "iobuf.h" #include "filter.h" #include "photoid.h" #include "options.h" #include "main.h" #include "i18n.h" +#include "host2net.h" /* Maximum length of packets to avoid excessive memory allocation. */ #define MAX_KEY_PACKET_LENGTH (256 * 1024) #define MAX_UID_PACKET_LENGTH ( 2 * 1024) #define MAX_COMMENT_PACKET_LENGTH ( 64 * 1024) #define MAX_ATTR_PACKET_LENGTH ( 16 * 1024*1024) static int mpi_print_mode; static int list_mode; static estream_t listfp; static int parse (IOBUF inp, PACKET * pkt, int onlykeypkts, off_t * retpos, int *skip, IOBUF out, int do_skip #ifdef DEBUG_PARSE_PACKET , const char *dbg_w, const char *dbg_f, int dbg_l #endif ); static int copy_packet (IOBUF inp, IOBUF out, int pkttype, unsigned long pktlen, int partial); static void skip_packet (IOBUF inp, int pkttype, unsigned long pktlen, int partial); static void *read_rest (IOBUF inp, size_t pktlen); static int parse_marker (IOBUF inp, int pkttype, unsigned long pktlen); static int parse_symkeyenc (IOBUF inp, int pkttype, unsigned long pktlen, PACKET * packet); static int parse_pubkeyenc (IOBUF inp, int pkttype, unsigned long pktlen, PACKET * packet); static int parse_onepass_sig (IOBUF inp, int pkttype, unsigned long pktlen, PKT_onepass_sig * ops); static int parse_key (IOBUF inp, int pkttype, unsigned long pktlen, byte * hdr, int hdrlen, PACKET * packet); static int parse_user_id (IOBUF inp, int pkttype, unsigned long pktlen, PACKET * packet); static int parse_attribute (IOBUF inp, int pkttype, unsigned long pktlen, PACKET * packet); static int parse_comment (IOBUF inp, int pkttype, unsigned long pktlen, PACKET * packet); static void parse_trust (IOBUF inp, int pkttype, unsigned long pktlen, PACKET * packet); static int parse_plaintext (IOBUF inp, int pkttype, unsigned long pktlen, PACKET * packet, int new_ctb, int partial); static int parse_compressed (IOBUF inp, int pkttype, unsigned long pktlen, PACKET * packet, int new_ctb); static int parse_encrypted (IOBUF inp, int pkttype, unsigned long pktlen, PACKET * packet, int new_ctb, int partial); static int parse_mdc (IOBUF inp, int pkttype, unsigned long pktlen, PACKET * packet, int new_ctb); static int parse_gpg_control (IOBUF inp, int pkttype, unsigned long pktlen, PACKET * packet, int partial); static unsigned short read_16 (IOBUF inp) { unsigned short a; - a = iobuf_get_noeof (inp) << 8; + a = (unsigned short)iobuf_get_noeof (inp) << 8; a |= iobuf_get_noeof (inp); return a; } static unsigned long read_32 (IOBUF inp) { unsigned long a; - a = iobuf_get_noeof (inp) << 24; + a = (unsigned long)iobuf_get_noeof (inp) << 24; a |= iobuf_get_noeof (inp) << 16; a |= iobuf_get_noeof (inp) << 8; a |= iobuf_get_noeof (inp); return a; } /* Read an external representation of an mpi and return the MPI. The * external format is a 16 bit unsigned value stored in network byte * order, giving the number of bits for the following integer. The * integer is stored with MSB first (left padded with zeroes to align * on a byte boundary). */ static gcry_mpi_t mpi_read (iobuf_t inp, unsigned int *ret_nread, int secure) { int c, c1, c2, i; unsigned int nmax = *ret_nread; unsigned int nbits, nbytes; size_t nread = 0; gcry_mpi_t a = NULL; byte *buf = NULL; byte *p; if (!nmax) goto overflow; if ((c = c1 = iobuf_get (inp)) == -1) goto leave; if (++nread == nmax) goto overflow; nbits = c << 8; if ((c = c2 = iobuf_get (inp)) == -1) goto leave; ++nread; nbits |= c; if (nbits > MAX_EXTERN_MPI_BITS) { log_error ("mpi too large (%u bits)\n", nbits); goto leave; } nbytes = (nbits + 7) / 8; buf = secure ? gcry_xmalloc_secure (nbytes + 2) : gcry_xmalloc (nbytes + 2); p = buf; p[0] = c1; p[1] = c2; for (i = 0; i < nbytes; i++) { p[i + 2] = iobuf_get (inp) & 0xff; if (nread == nmax) goto overflow; nread++; } if (gcry_mpi_scan (&a, GCRYMPI_FMT_PGP, buf, nread, &nread)) a = NULL; *ret_nread = nread; gcry_free(buf); return a; overflow: log_error ("mpi larger than indicated length (%u bits)\n", 8*nmax); leave: *ret_nread = nread; gcry_free(buf); return a; } int set_packet_list_mode (int mode) { int old = list_mode; list_mode = mode; /* FIXME(gcrypt) mpi_print_mode = DBG_MPI; */ /* We use stdout print only if invoked by the --list-packets command but switch to stderr in all other cases. This breaks the previous behaviour but that seems to be more of a bug than intentional. I don't believe that any application makes use of this long standing annoying way of printing to stdout except when doing a --list-packets. If this assumption fails, it will be easy to add an option for the listing stream. Note that we initialize it only once; mainly because some code may switch the option value later back to 1 and we want to have all output to the same stream. Using stderr is not actually very clean because it bypasses the logging code but it is a special thing anyway. I am not sure whether using log_stream() would be better. Perhaps we should enable the list mdoe only with a special option. */ if (!listfp) listfp = opt.list_packets == 2 ? es_stdout : es_stderr; return old; } static void unknown_pubkey_warning (int algo) { static byte unknown_pubkey_algos[256]; /* First check whether the algorithm is usable but not suitable for encryption/signing. */ if (pubkey_get_npkey (algo)) { if (opt.verbose) { if (!pubkey_get_nsig (algo)) log_info ("public key algorithm %s not suitable for %s\n", openpgp_pk_algo_name (algo), "signing"); if (!pubkey_get_nenc (algo)) log_info ("public key algorithm %s not suitable for %s\n", openpgp_pk_algo_name (algo), "encryption"); } } else { algo &= 0xff; if (!unknown_pubkey_algos[algo]) { if (opt.verbose) log_info (_("can't handle public key algorithm %d\n"), algo); unknown_pubkey_algos[algo] = 1; } } } /* Parse a packet and return it in packet structure. * Returns: 0 := valid packet in pkt * -1 := no more packets * >0 := error * Note: The function may return an error and a partly valid packet; * caller must free this packet. */ #ifdef DEBUG_PARSE_PACKET int dbg_parse_packet (IOBUF inp, PACKET *pkt, const char *dbg_f, int dbg_l) { int skip, rc; do { rc = parse (inp, pkt, 0, NULL, &skip, NULL, 0, "parse", dbg_f, dbg_l); } while (skip); return rc; } #else /*!DEBUG_PARSE_PACKET*/ int parse_packet (IOBUF inp, PACKET * pkt) { int skip, rc; do { rc = parse (inp, pkt, 0, NULL, &skip, NULL, 0); } while (skip); return rc; } #endif /*!DEBUG_PARSE_PACKET*/ /* * Like parse packet, but only return secret or public (sub)key * packets. */ #ifdef DEBUG_PARSE_PACKET int dbg_search_packet (IOBUF inp, PACKET * pkt, off_t * retpos, int with_uid, const char *dbg_f, int dbg_l) { int skip, rc; do { rc = parse (inp, pkt, with_uid ? 2 : 1, retpos, &skip, NULL, 0, "search", dbg_f, dbg_l); } while (skip); return rc; } #else /*!DEBUG_PARSE_PACKET*/ int search_packet (IOBUF inp, PACKET * pkt, off_t * retpos, int with_uid) { int skip, rc; do { rc = parse (inp, pkt, with_uid ? 2 : 1, retpos, &skip, NULL, 0); } while (skip); return rc; } #endif /*!DEBUG_PARSE_PACKET*/ /* * Copy all packets from INP to OUT, thereby removing unused spaces. */ #ifdef DEBUG_PARSE_PACKET int dbg_copy_all_packets (IOBUF inp, IOBUF out, const char *dbg_f, int dbg_l) { PACKET pkt; int skip, rc = 0; do { init_packet (&pkt); } while (! (rc = parse (inp, &pkt, 0, NULL, &skip, out, 0, "copy", dbg_f, dbg_l))); return rc; } #else /*!DEBUG_PARSE_PACKET*/ int copy_all_packets (IOBUF inp, IOBUF out) { PACKET pkt; int skip, rc = 0; do { init_packet (&pkt); } while (!(rc = parse (inp, &pkt, 0, NULL, &skip, out, 0))); return rc; } #endif /*!DEBUG_PARSE_PACKET*/ /* * Copy some packets from INP to OUT, thereby removing unused spaces. * Stop at offset STOPoff (i.e. don't copy packets at this or later * offsets) */ #ifdef DEBUG_PARSE_PACKET int dbg_copy_some_packets (IOBUF inp, IOBUF out, off_t stopoff, const char *dbg_f, int dbg_l) { PACKET pkt; int skip, rc = 0; do { if (iobuf_tell (inp) >= stopoff) return 0; init_packet (&pkt); } while (!(rc = parse (inp, &pkt, 0, NULL, &skip, out, 0, "some", dbg_f, dbg_l))); return rc; } #else /*!DEBUG_PARSE_PACKET*/ int copy_some_packets (IOBUF inp, IOBUF out, off_t stopoff) { PACKET pkt; int skip, rc = 0; do { if (iobuf_tell (inp) >= stopoff) return 0; init_packet (&pkt); } while (!(rc = parse (inp, &pkt, 0, NULL, &skip, out, 0))); return rc; } #endif /*!DEBUG_PARSE_PACKET*/ /* * Skip over N packets */ #ifdef DEBUG_PARSE_PACKET int dbg_skip_some_packets (IOBUF inp, unsigned n, const char *dbg_f, int dbg_l) { int skip, rc = 0; PACKET pkt; for (; n && !rc; n--) { init_packet (&pkt); rc = parse (inp, &pkt, 0, NULL, &skip, NULL, 1, "skip", dbg_f, dbg_l); } return rc; } #else /*!DEBUG_PARSE_PACKET*/ int skip_some_packets (IOBUF inp, unsigned n) { int skip, rc = 0; PACKET pkt; for (; n && !rc; n--) { init_packet (&pkt); rc = parse (inp, &pkt, 0, NULL, &skip, NULL, 1); } return rc; } #endif /*!DEBUG_PARSE_PACKET*/ /* * Parse packet. Stores 1 at SKIP 1 if the packet should be skipped; * this is the case if either ONLYKEYPKTS is set and the parsed packet * isn't a key packet or the packet-type is 0, indicating deleted * stuff. If OUT is not NULL, a special copymode is used. */ static int parse (IOBUF inp, PACKET * pkt, int onlykeypkts, off_t * retpos, int *skip, IOBUF out, int do_skip #ifdef DEBUG_PARSE_PACKET , const char *dbg_w, const char *dbg_f, int dbg_l #endif ) { int rc = 0, c, ctb, pkttype, lenbytes; unsigned long pktlen; byte hdr[8]; int hdrlen; int new_ctb = 0, partial = 0; int with_uid = (onlykeypkts == 2); off_t pos; *skip = 0; assert (!pkt->pkt.generic); if (retpos || list_mode) { pos = iobuf_tell (inp); if (retpos) *retpos = pos; } else pos = 0; /* (silence compiler warning) */ if ((ctb = iobuf_get (inp)) == -1) { rc = -1; goto leave; } hdrlen = 0; hdr[hdrlen++] = ctb; if (!(ctb & 0x80)) { log_error ("%s: invalid packet (ctb=%02x)\n", iobuf_where (inp), ctb); rc = gpg_error (GPG_ERR_INV_PACKET); goto leave; } pktlen = 0; new_ctb = !!(ctb & 0x40); if (new_ctb) { pkttype = ctb & 0x3f; if ((c = iobuf_get (inp)) == -1) { log_error ("%s: 1st length byte missing\n", iobuf_where (inp)); rc = gpg_error (GPG_ERR_INV_PACKET); goto leave; } hdr[hdrlen++] = c; if (c < 192) pktlen = c; else if (c < 224) { pktlen = (c - 192) * 256; if ((c = iobuf_get (inp)) == -1) { log_error ("%s: 2nd length byte missing\n", iobuf_where (inp)); rc = gpg_error (GPG_ERR_INV_PACKET); goto leave; } hdr[hdrlen++] = c; pktlen += c + 192; } else if (c == 255) { - pktlen = (hdr[hdrlen++] = iobuf_get_noeof (inp)) << 24; + pktlen = (unsigned long)(hdr[hdrlen++] = iobuf_get_noeof (inp)) << 24; pktlen |= (hdr[hdrlen++] = iobuf_get_noeof (inp)) << 16; pktlen |= (hdr[hdrlen++] = iobuf_get_noeof (inp)) << 8; if ((c = iobuf_get (inp)) == -1) { log_error ("%s: 4 byte length invalid\n", iobuf_where (inp)); rc = gpg_error (GPG_ERR_INV_PACKET); goto leave; } pktlen |= (hdr[hdrlen++] = c); } else /* Partial body length. */ { switch (pkttype) { case PKT_PLAINTEXT: case PKT_ENCRYPTED: case PKT_ENCRYPTED_MDC: case PKT_COMPRESSED: iobuf_set_partial_block_mode (inp, c & 0xff); pktlen = 0; /* To indicate partial length. */ partial = 1; break; default: log_error ("%s: partial length for invalid" " packet type %d\n", iobuf_where (inp), pkttype); rc = gpg_error (GPG_ERR_INV_PACKET); goto leave; } } } else { pkttype = (ctb >> 2) & 0xf; lenbytes = ((ctb & 3) == 3) ? 0 : (1 << (ctb & 3)); if (!lenbytes) { pktlen = 0; /* Don't know the value. */ /* This isn't really partial, but we can treat it the same in a "read until the end" sort of way. */ partial = 1; if (pkttype != PKT_ENCRYPTED && pkttype != PKT_PLAINTEXT && pkttype != PKT_COMPRESSED) { log_error ("%s: indeterminate length for invalid" " packet type %d\n", iobuf_where (inp), pkttype); rc = gpg_error (GPG_ERR_INV_PACKET); goto leave; } } else { for (; lenbytes; lenbytes--) { pktlen <<= 8; pktlen |= hdr[hdrlen++] = iobuf_get_noeof (inp); } } } if (pktlen == (unsigned long) (-1)) { /* With some probability this is caused by a problem in the * the uncompressing layer - in some error cases it just loops * and spits out 0xff bytes. */ log_error ("%s: garbled packet detected\n", iobuf_where (inp)); g10_exit (2); } if (out && pkttype) { rc = iobuf_write (out, hdr, hdrlen); if (!rc) rc = copy_packet (inp, out, pkttype, pktlen, partial); goto leave; } if (with_uid && pkttype == PKT_USER_ID) ; else if (do_skip || !pkttype || (onlykeypkts && pkttype != PKT_PUBLIC_SUBKEY && pkttype != PKT_PUBLIC_KEY && pkttype != PKT_SECRET_SUBKEY && pkttype != PKT_SECRET_KEY)) { iobuf_skip_rest (inp, pktlen, partial); *skip = 1; rc = 0; goto leave; } if (DBG_PACKET) { #ifdef DEBUG_PARSE_PACKET log_debug ("parse_packet(iob=%d): type=%d length=%lu%s (%s.%s.%d)\n", iobuf_id (inp), pkttype, pktlen, new_ctb ? " (new_ctb)" : "", dbg_w, dbg_f, dbg_l); #else log_debug ("parse_packet(iob=%d): type=%d length=%lu%s\n", iobuf_id (inp), pkttype, pktlen, new_ctb ? " (new_ctb)" : ""); #endif } if (list_mode) es_fprintf (listfp, "# off=%lu ctb=%02x tag=%d hlen=%d plen=%lu%s%s\n", (unsigned long)pos, ctb, pkttype, hdrlen, pktlen, partial? " partial":"", new_ctb? " new-ctb":""); pkt->pkttype = pkttype; rc = GPG_ERR_UNKNOWN_PACKET; /* default error */ switch (pkttype) { case PKT_PUBLIC_KEY: case PKT_PUBLIC_SUBKEY: case PKT_SECRET_KEY: case PKT_SECRET_SUBKEY: pkt->pkt.public_key = xmalloc_clear (sizeof *pkt->pkt.public_key); rc = parse_key (inp, pkttype, pktlen, hdr, hdrlen, pkt); break; case PKT_SYMKEY_ENC: rc = parse_symkeyenc (inp, pkttype, pktlen, pkt); break; case PKT_PUBKEY_ENC: rc = parse_pubkeyenc (inp, pkttype, pktlen, pkt); break; case PKT_SIGNATURE: pkt->pkt.signature = xmalloc_clear (sizeof *pkt->pkt.signature); rc = parse_signature (inp, pkttype, pktlen, pkt->pkt.signature); break; case PKT_ONEPASS_SIG: pkt->pkt.onepass_sig = xmalloc_clear (sizeof *pkt->pkt.onepass_sig); rc = parse_onepass_sig (inp, pkttype, pktlen, pkt->pkt.onepass_sig); break; case PKT_USER_ID: rc = parse_user_id (inp, pkttype, pktlen, pkt); break; case PKT_ATTRIBUTE: pkt->pkttype = pkttype = PKT_USER_ID; /* we store it in the userID */ rc = parse_attribute (inp, pkttype, pktlen, pkt); break; case PKT_OLD_COMMENT: case PKT_COMMENT: rc = parse_comment (inp, pkttype, pktlen, pkt); break; case PKT_RING_TRUST: parse_trust (inp, pkttype, pktlen, pkt); rc = 0; break; case PKT_PLAINTEXT: rc = parse_plaintext (inp, pkttype, pktlen, pkt, new_ctb, partial); break; case PKT_COMPRESSED: rc = parse_compressed (inp, pkttype, pktlen, pkt, new_ctb); break; case PKT_ENCRYPTED: case PKT_ENCRYPTED_MDC: rc = parse_encrypted (inp, pkttype, pktlen, pkt, new_ctb, partial); break; case PKT_MDC: rc = parse_mdc (inp, pkttype, pktlen, pkt, new_ctb); break; case PKT_GPG_CONTROL: rc = parse_gpg_control (inp, pkttype, pktlen, pkt, partial); break; case PKT_MARKER: rc = parse_marker (inp, pkttype, pktlen); break; default: skip_packet (inp, pkttype, pktlen, partial); break; } leave: /* FIXME: Do we leak in case of an error? */ if (!rc && iobuf_error (inp)) rc = GPG_ERR_INV_KEYRING; /* FIXME: We use only the error code for now to avoid problems with callers which have not been checked to always use gpg_err_code() when comparing error codes. */ return rc == -1? -1 : gpg_err_code (rc); } static void dump_hex_line (int c, int *i) { if (*i && !(*i % 8)) { if (*i && !(*i % 24)) es_fprintf (listfp, "\n%4d:", *i); else es_putc (' ', listfp); } if (c == -1) es_fprintf (listfp, " EOF"); else es_fprintf (listfp, " %02x", c); ++*i; } static int copy_packet (IOBUF inp, IOBUF out, int pkttype, unsigned long pktlen, int partial) { int rc; int n; char buf[100]; if (partial) { while ((n = iobuf_read (inp, buf, 100)) != -1) if ((rc = iobuf_write (out, buf, n))) return rc; /* write error */ } else if (!pktlen && pkttype == PKT_COMPRESSED) { log_debug ("copy_packet: compressed!\n"); /* compressed packet, copy till EOF */ while ((n = iobuf_read (inp, buf, 100)) != -1) if ((rc = iobuf_write (out, buf, n))) return rc; /* write error */ } else { for (; pktlen; pktlen -= n) { n = pktlen > 100 ? 100 : pktlen; n = iobuf_read (inp, buf, n); if (n == -1) return gpg_error (GPG_ERR_EOF); if ((rc = iobuf_write (out, buf, n))) return rc; /* write error */ } } return 0; } static void skip_packet (IOBUF inp, int pkttype, unsigned long pktlen, int partial) { if (list_mode) { es_fprintf (listfp, ":unknown packet: type %2d, length %lu\n", pkttype, pktlen); if (pkttype) { int c, i = 0; es_fputs ("dump:", listfp); if (partial) { while ((c = iobuf_get (inp)) != -1) dump_hex_line (c, &i); } else { for (; pktlen; pktlen--) { dump_hex_line ((c = iobuf_get (inp)), &i); if (c == -1) break; } } es_putc ('\n', listfp); return; } } iobuf_skip_rest (inp, pktlen, partial); } /* Read PKTLEN bytes form INP and return them in a newly allocated buffer. In case of an error NULL is returned and a error messages printed. */ static void * read_rest (IOBUF inp, size_t pktlen) { int c; byte *buf, *p; buf = xtrymalloc (pktlen); if (!buf) { gpg_error_t err = gpg_error_from_syserror (); log_error ("error reading rest of packet: %s\n", gpg_strerror (err)); return NULL; } for (p = buf; pktlen; pktlen--) { c = iobuf_get (inp); if (c == -1) { log_error ("premature eof while reading rest of packet\n"); xfree (buf); return NULL; } *p++ = c; } return buf; } /* Read a special size+body from INP. On success store an opaque MPI with it at R_DATA. On error return an error code and store NULL at R_DATA. Even in the error case store the number of read bytes at R_NREAD. The caller shall pass the remaining size of the packet in PKTLEN. */ static gpg_error_t read_size_body (iobuf_t inp, int pktlen, size_t *r_nread, gcry_mpi_t *r_data) { char buffer[256]; char *tmpbuf; int i, c, nbytes; *r_nread = 0; *r_data = NULL; if (!pktlen) return gpg_error (GPG_ERR_INV_PACKET); c = iobuf_readbyte (inp); if (c < 0) return gpg_error (GPG_ERR_INV_PACKET); pktlen--; ++*r_nread; nbytes = c; if (nbytes < 2 || nbytes > 254) return gpg_error (GPG_ERR_INV_PACKET); if (nbytes > pktlen) return gpg_error (GPG_ERR_INV_PACKET); buffer[0] = nbytes; for (i = 0; i < nbytes; i++) { c = iobuf_get (inp); if (c < 0) return gpg_error (GPG_ERR_INV_PACKET); ++*r_nread; buffer[1+i] = c; } tmpbuf = xtrymalloc (1 + nbytes); if (!tmpbuf) return gpg_error_from_syserror (); memcpy (tmpbuf, buffer, 1 + nbytes); *r_data = gcry_mpi_set_opaque (NULL, tmpbuf, 8 * (1 + nbytes)); if (!*r_data) { xfree (tmpbuf); return gpg_error_from_syserror (); } return 0; } /* Parse a marker packet. */ static int parse_marker (IOBUF inp, int pkttype, unsigned long pktlen) { (void) pkttype; if (pktlen != 3) goto fail; if (iobuf_get (inp) != 'P') { pktlen--; goto fail; } if (iobuf_get (inp) != 'G') { pktlen--; goto fail; } if (iobuf_get (inp) != 'P') { pktlen--; goto fail; } if (list_mode) es_fputs (":marker packet: PGP\n", listfp); return 0; fail: log_error ("invalid marker packet\n"); if (list_mode) es_fputs (":marker packet: [invalid]\n", listfp); iobuf_skip_rest (inp, pktlen, 0); return GPG_ERR_INV_PACKET; } static int parse_symkeyenc (IOBUF inp, int pkttype, unsigned long pktlen, PACKET * packet) { PKT_symkey_enc *k; int rc = 0; int i, version, s2kmode, cipher_algo, hash_algo, seskeylen, minlen; if (pktlen < 4) { log_error ("packet(%d) too short\n", pkttype); if (list_mode) es_fprintf (listfp, ":symkey enc packet: [too short]\n"); rc = gpg_error (GPG_ERR_INV_PACKET); goto leave; } version = iobuf_get_noeof (inp); pktlen--; if (version != 4) { log_error ("packet(%d) with unknown version %d\n", pkttype, version); if (list_mode) es_fprintf (listfp, ":symkey enc packet: [unknown version]\n"); rc = gpg_error (GPG_ERR_INV_PACKET); goto leave; } if (pktlen > 200) { /* (we encode the seskeylen in a byte) */ log_error ("packet(%d) too large\n", pkttype); if (list_mode) es_fprintf (listfp, ":symkey enc packet: [too large]\n"); rc = gpg_error (GPG_ERR_INV_PACKET); goto leave; } cipher_algo = iobuf_get_noeof (inp); pktlen--; s2kmode = iobuf_get_noeof (inp); pktlen--; hash_algo = iobuf_get_noeof (inp); pktlen--; switch (s2kmode) { case 0: /* Simple S2K. */ minlen = 0; break; case 1: /* Salted S2K. */ minlen = 8; break; case 3: /* Iterated+salted S2K. */ minlen = 9; break; default: log_error ("unknown S2K mode %d\n", s2kmode); if (list_mode) es_fprintf (listfp, ":symkey enc packet: [unknown S2K mode]\n"); goto leave; } if (minlen > pktlen) { log_error ("packet with S2K %d too short\n", s2kmode); if (list_mode) es_fprintf (listfp, ":symkey enc packet: [too short]\n"); rc = gpg_error (GPG_ERR_INV_PACKET); goto leave; } seskeylen = pktlen - minlen; k = packet->pkt.symkey_enc = xmalloc_clear (sizeof *packet->pkt.symkey_enc + seskeylen - 1); k->version = version; k->cipher_algo = cipher_algo; k->s2k.mode = s2kmode; k->s2k.hash_algo = hash_algo; if (s2kmode == 1 || s2kmode == 3) { for (i = 0; i < 8 && pktlen; i++, pktlen--) k->s2k.salt[i] = iobuf_get_noeof (inp); } if (s2kmode == 3) { k->s2k.count = iobuf_get (inp); pktlen--; } k->seskeylen = seskeylen; if (k->seskeylen) { for (i = 0; i < seskeylen && pktlen; i++, pktlen--) k->seskey[i] = iobuf_get_noeof (inp); /* What we're watching out for here is a session key decryptor with no salt. The RFC says that using salt for this is a MUST. */ if (s2kmode != 1 && s2kmode != 3) log_info (_("WARNING: potentially insecure symmetrically" " encrypted session key\n")); } assert (!pktlen); if (list_mode) { es_fprintf (listfp, ":symkey enc packet: version %d, cipher %d, s2k %d, hash %d", version, cipher_algo, s2kmode, hash_algo); if (seskeylen) es_fprintf (listfp, ", seskey %d bits", (seskeylen - 1) * 8); es_fprintf (listfp, "\n"); if (s2kmode == 1 || s2kmode == 3) { es_fprintf (listfp, "\tsalt "); es_write_hexstring (listfp, k->s2k.salt, 8, 0, NULL); if (s2kmode == 3) es_fprintf (listfp, ", count %lu (%lu)", S2K_DECODE_COUNT ((ulong) k->s2k.count), (ulong) k->s2k.count); es_fprintf (listfp, "\n"); } } leave: iobuf_skip_rest (inp, pktlen, 0); return rc; } static int parse_pubkeyenc (IOBUF inp, int pkttype, unsigned long pktlen, PACKET * packet) { int rc = 0; int i, ndata; PKT_pubkey_enc *k; k = packet->pkt.pubkey_enc = xmalloc_clear (sizeof *packet->pkt.pubkey_enc); if (pktlen < 12) { log_error ("packet(%d) too short\n", pkttype); if (list_mode) es_fputs (":pubkey enc packet: [too short]\n", listfp); rc = gpg_error (GPG_ERR_INV_PACKET); goto leave; } k->version = iobuf_get_noeof (inp); pktlen--; if (k->version != 2 && k->version != 3) { log_error ("packet(%d) with unknown version %d\n", pkttype, k->version); if (list_mode) es_fputs (":pubkey enc packet: [unknown version]\n", listfp); rc = gpg_error (GPG_ERR_INV_PACKET); goto leave; } k->keyid[0] = read_32 (inp); pktlen -= 4; k->keyid[1] = read_32 (inp); pktlen -= 4; k->pubkey_algo = iobuf_get_noeof (inp); pktlen--; k->throw_keyid = 0; /* Only used as flag for build_packet. */ if (list_mode) es_fprintf (listfp, ":pubkey enc packet: version %d, algo %d, keyid %08lX%08lX\n", k->version, k->pubkey_algo, (ulong) k->keyid[0], (ulong) k->keyid[1]); ndata = pubkey_get_nenc (k->pubkey_algo); if (!ndata) { if (list_mode) es_fprintf (listfp, "\tunsupported algorithm %d\n", k->pubkey_algo); unknown_pubkey_warning (k->pubkey_algo); k->data[0] = NULL; /* No need to store the encrypted data. */ } else { for (i = 0; i < ndata; i++) { if (k->pubkey_algo == PUBKEY_ALGO_ECDH && i == 1) { size_t n; rc = read_size_body (inp, pktlen, &n, k->data+i); pktlen -= n; } else { int n = pktlen; k->data[i] = mpi_read (inp, &n, 0); pktlen -= n; if (!k->data[i]) rc = gpg_error (GPG_ERR_INV_PACKET); } if (rc) goto leave; if (list_mode) { es_fprintf (listfp, "\tdata: "); mpi_print (listfp, k->data[i], mpi_print_mode); es_putc ('\n', listfp); } } } leave: iobuf_skip_rest (inp, pktlen, 0); return rc; } static void dump_sig_subpkt (int hashed, int type, int critical, const byte * buffer, size_t buflen, size_t length) { const char *p = NULL; int i; /* The CERT has warning out with explains how to use GNUPG to detect * the ARRs - we print our old message here when it is a faked ARR * and add an additional notice. */ if (type == SIGSUBPKT_ARR && !hashed) { es_fprintf (listfp, "\tsubpkt %d len %u (additional recipient request)\n" "WARNING: PGP versions > 5.0 and < 6.5.8 will automagically " "encrypt to this key and thereby reveal the plaintext to " "the owner of this ARR key. Detailed info follows:\n", type, (unsigned) length); } buffer++; length--; es_fprintf (listfp, "\t%s%ssubpkt %d len %u (", /*) */ critical ? "critical " : "", hashed ? "hashed " : "", type, (unsigned) length); if (length > buflen) { es_fprintf (listfp, "too short: buffer is only %u)\n", (unsigned) buflen); return; } switch (type) { case SIGSUBPKT_SIG_CREATED: if (length >= 4) es_fprintf (listfp, "sig created %s", - strtimestamp (buffer_to_u32 (buffer))); + strtimestamp (buf32_to_u32 (buffer))); break; case SIGSUBPKT_SIG_EXPIRE: if (length >= 4) { - if (buffer_to_u32 (buffer)) + if (buf32_to_u32 (buffer)) es_fprintf (listfp, "sig expires after %s", - strtimevalue (buffer_to_u32 (buffer))); + strtimevalue (buf32_to_u32 (buffer))); else es_fprintf (listfp, "sig does not expire"); } break; case SIGSUBPKT_EXPORTABLE: if (length) es_fprintf (listfp, "%sexportable", *buffer ? "" : "not "); break; case SIGSUBPKT_TRUST: if (length != 2) p = "[invalid trust subpacket]"; else es_fprintf (listfp, "trust signature of depth %d, value %d", buffer[0], buffer[1]); break; case SIGSUBPKT_REGEXP: if (!length) p = "[invalid regexp subpacket]"; else { es_fprintf (listfp, "regular expression: \""); es_write_sanitized (listfp, buffer, length, "\"", NULL); p = "\""; } break; case SIGSUBPKT_REVOCABLE: if (length) es_fprintf (listfp, "%srevocable", *buffer ? "" : "not "); break; case SIGSUBPKT_KEY_EXPIRE: if (length >= 4) { - if (buffer_to_u32 (buffer)) + if (buf32_to_u32 (buffer)) es_fprintf (listfp, "key expires after %s", - strtimevalue (buffer_to_u32 (buffer))); + strtimevalue (buf32_to_u32 (buffer))); else es_fprintf (listfp, "key does not expire"); } break; case SIGSUBPKT_PREF_SYM: es_fputs ("pref-sym-algos:", listfp); for (i = 0; i < length; i++) es_fprintf (listfp, " %d", buffer[i]); break; case SIGSUBPKT_REV_KEY: es_fputs ("revocation key: ", listfp); if (length < 22) p = "[too short]"; else { es_fprintf (listfp, "c=%02x a=%d f=", buffer[0], buffer[1]); for (i = 2; i < length; i++) es_fprintf (listfp, "%02X", buffer[i]); } break; case SIGSUBPKT_ISSUER: if (length >= 8) es_fprintf (listfp, "issuer key ID %08lX%08lX", - (ulong) buffer_to_u32 (buffer), - (ulong) buffer_to_u32 (buffer + 4)); + (ulong) buf32_to_u32 (buffer), + (ulong) buf32_to_u32 (buffer + 4)); break; case SIGSUBPKT_NOTATION: { es_fputs ("notation: ", listfp); if (length < 8) p = "[too short]"; else { const byte *s = buffer; size_t n1, n2; n1 = (s[4] << 8) | s[5]; n2 = (s[6] << 8) | s[7]; s += 8; if (8 + n1 + n2 != length) p = "[error]"; else { es_write_sanitized (listfp, s, n1, ")", NULL); es_putc ('=', listfp); if (*buffer & 0x80) es_write_sanitized (listfp, s + n1, n2, ")", NULL); else p = "[not human readable]"; } } } break; case SIGSUBPKT_PREF_HASH: es_fputs ("pref-hash-algos:", listfp); for (i = 0; i < length; i++) es_fprintf (listfp, " %d", buffer[i]); break; case SIGSUBPKT_PREF_COMPR: es_fputs ("pref-zip-algos:", listfp); for (i = 0; i < length; i++) es_fprintf (listfp, " %d", buffer[i]); break; case SIGSUBPKT_KS_FLAGS: es_fputs ("key server preferences:", listfp); for (i = 0; i < length; i++) es_fprintf (listfp, " %02X", buffer[i]); break; case SIGSUBPKT_PREF_KS: es_fputs ("preferred key server: ", listfp); es_write_sanitized (listfp, buffer, length, ")", NULL); break; case SIGSUBPKT_PRIMARY_UID: p = "primary user ID"; break; case SIGSUBPKT_POLICY: es_fputs ("policy: ", listfp); es_write_sanitized (listfp, buffer, length, ")", NULL); break; case SIGSUBPKT_KEY_FLAGS: es_fputs ("key flags:", listfp); for (i = 0; i < length; i++) es_fprintf (listfp, " %02X", buffer[i]); break; case SIGSUBPKT_SIGNERS_UID: p = "signer's user ID"; break; case SIGSUBPKT_REVOC_REASON: if (length) { es_fprintf (listfp, "revocation reason 0x%02x (", *buffer); es_write_sanitized (listfp, buffer + 1, length - 1, ")", NULL); p = ")"; } break; case SIGSUBPKT_ARR: es_fputs ("Big Brother's key (ignored): ", listfp); if (length < 22) p = "[too short]"; else { es_fprintf (listfp, "c=%02x a=%d f=", buffer[0], buffer[1]); if (length > 2) es_write_hexstring (listfp, buffer+2, length-2, 0, NULL); } break; case SIGSUBPKT_FEATURES: es_fputs ("features:", listfp); for (i = 0; i < length; i++) es_fprintf (listfp, " %02x", buffer[i]); break; case SIGSUBPKT_SIGNATURE: es_fputs ("signature: ", listfp); if (length < 17) p = "[too short]"; else es_fprintf (listfp, "v%d, class 0x%02X, algo %d, digest algo %d", buffer[0], buffer[0] == 3 ? buffer[2] : buffer[1], buffer[0] == 3 ? buffer[15] : buffer[2], buffer[0] == 3 ? buffer[16] : buffer[3]); break; default: if (type >= 100 && type <= 110) p = "experimental / private subpacket"; else p = "?"; break; } es_fprintf (listfp, "%s)\n", p ? p : ""); } /* * Returns: >= 0 use this offset into buffer * -1 explicitly reject returning this type * -2 subpacket too short */ int parse_one_sig_subpkt (const byte * buffer, size_t n, int type) { switch (type) { case SIGSUBPKT_REV_KEY: if (n < 22) break; return 0; case SIGSUBPKT_SIG_CREATED: case SIGSUBPKT_SIG_EXPIRE: case SIGSUBPKT_KEY_EXPIRE: if (n < 4) break; return 0; case SIGSUBPKT_KEY_FLAGS: case SIGSUBPKT_KS_FLAGS: case SIGSUBPKT_PREF_SYM: case SIGSUBPKT_PREF_HASH: case SIGSUBPKT_PREF_COMPR: case SIGSUBPKT_POLICY: case SIGSUBPKT_PREF_KS: case SIGSUBPKT_FEATURES: case SIGSUBPKT_REGEXP: return 0; case SIGSUBPKT_SIGNATURE: case SIGSUBPKT_EXPORTABLE: case SIGSUBPKT_REVOCABLE: case SIGSUBPKT_REVOC_REASON: if (!n) break; return 0; case SIGSUBPKT_ISSUER: /* issuer key ID */ if (n < 8) break; return 0; case SIGSUBPKT_NOTATION: /* minimum length needed, and the subpacket must be well-formed where the name length and value length all fit inside the packet. */ if (n < 8 || 8 + ((buffer[4] << 8) | buffer[5]) + ((buffer[6] << 8) | buffer[7]) != n) break; return 0; case SIGSUBPKT_PRIMARY_UID: if (n != 1) break; return 0; case SIGSUBPKT_TRUST: if (n != 2) break; return 0; default: return 0; } return -2; } /* Return true if we understand the critical notation. */ static int can_handle_critical_notation (const byte * name, size_t len) { if (len == 32 && memcmp (name, "preferred-email-encoding@pgp.com", 32) == 0) return 1; if (len == 21 && memcmp (name, "pka-address@gnupg.org", 21) == 0) return 1; return 0; } static int can_handle_critical (const byte * buffer, size_t n, int type) { switch (type) { case SIGSUBPKT_NOTATION: if (n >= 8) { size_t notation_len = ((buffer[4] << 8) | buffer[5]); if (n - 8 >= notation_len) return can_handle_critical_notation (buffer + 8, notation_len); } return 0; case SIGSUBPKT_SIGNATURE: case SIGSUBPKT_SIG_CREATED: case SIGSUBPKT_SIG_EXPIRE: case SIGSUBPKT_KEY_EXPIRE: case SIGSUBPKT_EXPORTABLE: case SIGSUBPKT_REVOCABLE: case SIGSUBPKT_REV_KEY: case SIGSUBPKT_ISSUER: /* issuer key ID */ case SIGSUBPKT_PREF_SYM: case SIGSUBPKT_PREF_HASH: case SIGSUBPKT_PREF_COMPR: case SIGSUBPKT_KEY_FLAGS: case SIGSUBPKT_PRIMARY_UID: case SIGSUBPKT_FEATURES: case SIGSUBPKT_TRUST: case SIGSUBPKT_REGEXP: /* Is it enough to show the policy or keyserver? */ case SIGSUBPKT_POLICY: case SIGSUBPKT_PREF_KS: return 1; default: return 0; } } const byte * enum_sig_subpkt (const subpktarea_t * pktbuf, sigsubpkttype_t reqtype, size_t * ret_n, int *start, int *critical) { const byte *buffer; int buflen; int type; int critical_dummy; int offset; size_t n; int seq = 0; int reqseq = start ? *start : 0; if (!critical) critical = &critical_dummy; if (!pktbuf || reqseq == -1) { static char dummy[] = "x"; /* Return a value different from NULL to indicate that * there is no critical bit we do not understand. */ return reqtype == SIGSUBPKT_TEST_CRITICAL ? dummy : NULL; } buffer = pktbuf->data; buflen = pktbuf->len; while (buflen) { n = *buffer++; buflen--; if (n == 255) /* 4 byte length header. */ { if (buflen < 4) goto too_short; - n = (buffer[0] << 24) | (buffer[1] << 16) - | (buffer[2] << 8) | buffer[3]; + n = buf32_to_size_t (buffer); buffer += 4; buflen -= 4; } else if (n >= 192) /* 4 byte special encoded length header. */ { if (buflen < 2) goto too_short; n = ((n - 192) << 8) + *buffer + 192; buffer++; buflen--; } if (buflen < n) goto too_short; type = *buffer; if (type & 0x80) { type &= 0x7f; *critical = 1; } else *critical = 0; if (!(++seq > reqseq)) ; else if (reqtype == SIGSUBPKT_TEST_CRITICAL) { if (*critical) { if (n - 1 > buflen + 1) goto too_short; if (!can_handle_critical (buffer + 1, n - 1, type)) { if (opt.verbose) log_info (_("subpacket of type %d has " "critical bit set\n"), type); if (start) *start = seq; return NULL; /* This is an error. */ } } } else if (reqtype < 0) /* List packets. */ dump_sig_subpkt (reqtype == SIGSUBPKT_LIST_HASHED, type, *critical, buffer, buflen, n); else if (type == reqtype) /* Found. */ { buffer++; n--; if (n > buflen) goto too_short; if (ret_n) *ret_n = n; offset = parse_one_sig_subpkt (buffer, n, type); switch (offset) { case -2: log_error ("subpacket of type %d too short\n", type); return NULL; case -1: return NULL; default: break; } if (start) *start = seq; return buffer + offset; } buffer += n; buflen -= n; } if (reqtype == SIGSUBPKT_TEST_CRITICAL) return buffer; /* Used as True to indicate that there is no. */ /* Critical bit we don't understand. */ if (start) *start = -1; return NULL; /* End of packets; not found. */ too_short: if (opt.verbose) log_info ("buffer shorter than subpacket\n"); if (start) *start = -1; return NULL; } const byte * parse_sig_subpkt (const subpktarea_t * buffer, sigsubpkttype_t reqtype, size_t * ret_n) { return enum_sig_subpkt (buffer, reqtype, ret_n, NULL, NULL); } const byte * parse_sig_subpkt2 (PKT_signature * sig, sigsubpkttype_t reqtype, size_t * ret_n) { const byte *p; p = parse_sig_subpkt (sig->hashed, reqtype, ret_n); if (!p) p = parse_sig_subpkt (sig->unhashed, reqtype, ret_n); return p; } /* Find all revocation keys. Look in hashed area only. */ void parse_revkeys (PKT_signature * sig) { struct revocation_key *revkey; int seq = 0; size_t len; if (sig->sig_class != 0x1F) return; while ((revkey = (struct revocation_key *) enum_sig_subpkt (sig->hashed, SIGSUBPKT_REV_KEY, &len, &seq, NULL))) { if (len == sizeof (struct revocation_key) && (revkey->class & 0x80)) /* 0x80 bit must be set. */ { sig->revkey = xrealloc (sig->revkey, sizeof (struct revocation_key *) * (sig->numrevkeys + 1)); sig->revkey[sig->numrevkeys] = revkey; sig->numrevkeys++; } } } int parse_signature (IOBUF inp, int pkttype, unsigned long pktlen, PKT_signature * sig) { int md5_len = 0; unsigned n; int is_v4 = 0; int rc = 0; int i, ndata; if (pktlen < 16) { log_error ("packet(%d) too short\n", pkttype); if (list_mode) es_fputs (":signature packet: [too short]\n", listfp); goto leave; } sig->version = iobuf_get_noeof (inp); pktlen--; if (sig->version == 4) is_v4 = 1; else if (sig->version != 2 && sig->version != 3) { log_error ("packet(%d) with unknown version %d\n", pkttype, sig->version); if (list_mode) es_fputs (":signature packet: [unknown version]\n", listfp); rc = gpg_error (GPG_ERR_INV_PACKET); goto leave; } if (!is_v4) { md5_len = iobuf_get_noeof (inp); pktlen--; } sig->sig_class = iobuf_get_noeof (inp); pktlen--; if (!is_v4) { sig->timestamp = read_32 (inp); pktlen -= 4; sig->keyid[0] = read_32 (inp); pktlen -= 4; sig->keyid[1] = read_32 (inp); pktlen -= 4; } sig->pubkey_algo = iobuf_get_noeof (inp); pktlen--; sig->digest_algo = iobuf_get_noeof (inp); pktlen--; sig->flags.exportable = 1; sig->flags.revocable = 1; if (is_v4) /* Read subpackets. */ { n = read_16 (inp); pktlen -= 2; /* Length of hashed data. */ if (n > 10000) { log_error ("signature packet: hashed data too long\n"); if (list_mode) es_fputs (":signature packet: [hashed data too long]\n", listfp); rc = GPG_ERR_INV_PACKET; goto leave; } if (n) { sig->hashed = xmalloc (sizeof (*sig->hashed) + n - 1); sig->hashed->size = n; sig->hashed->len = n; if (iobuf_read (inp, sig->hashed->data, n) != n) { log_error ("premature eof while reading " "hashed signature data\n"); if (list_mode) es_fputs (":signature packet: [premature eof]\n", listfp); rc = -1; goto leave; } pktlen -= n; } n = read_16 (inp); pktlen -= 2; /* Length of unhashed data. */ if (n > 10000) { log_error ("signature packet: unhashed data too long\n"); if (list_mode) es_fputs (":signature packet: [unhashed data too long]\n", listfp); rc = GPG_ERR_INV_PACKET; goto leave; } if (n) { sig->unhashed = xmalloc (sizeof (*sig->unhashed) + n - 1); sig->unhashed->size = n; sig->unhashed->len = n; if (iobuf_read (inp, sig->unhashed->data, n) != n) { log_error ("premature eof while reading " "unhashed signature data\n"); if (list_mode) es_fputs (":signature packet: [premature eof]\n", listfp); rc = -1; goto leave; } pktlen -= n; } } if (pktlen < 5) /* Sanity check. */ { log_error ("packet(%d) too short\n", pkttype); if (list_mode) es_fputs (":signature packet: [too short]\n", listfp); rc = GPG_ERR_INV_PACKET; goto leave; } sig->digest_start[0] = iobuf_get_noeof (inp); pktlen--; sig->digest_start[1] = iobuf_get_noeof (inp); pktlen--; if (is_v4 && sig->pubkey_algo) /* Extract required information. */ { const byte *p; size_t len; /* Set sig->flags.unknown_critical if there is a critical bit * set for packets which we do not understand. */ if (!parse_sig_subpkt (sig->hashed, SIGSUBPKT_TEST_CRITICAL, NULL) || !parse_sig_subpkt (sig->unhashed, SIGSUBPKT_TEST_CRITICAL, NULL)) sig->flags.unknown_critical = 1; p = parse_sig_subpkt (sig->hashed, SIGSUBPKT_SIG_CREATED, NULL); if (p) - sig->timestamp = buffer_to_u32 (p); + sig->timestamp = buf32_to_u32 (p); else if (!(sig->pubkey_algo >= 100 && sig->pubkey_algo <= 110) && opt.verbose) log_info ("signature packet without timestamp\n"); p = parse_sig_subpkt2 (sig, SIGSUBPKT_ISSUER, NULL); if (p) { - sig->keyid[0] = buffer_to_u32 (p); - sig->keyid[1] = buffer_to_u32 (p + 4); + sig->keyid[0] = buf32_to_u32 (p); + sig->keyid[1] = buf32_to_u32 (p + 4); } else if (!(sig->pubkey_algo >= 100 && sig->pubkey_algo <= 110) && opt.verbose) log_info ("signature packet without keyid\n"); p = parse_sig_subpkt (sig->hashed, SIGSUBPKT_SIG_EXPIRE, NULL); - if (p && buffer_to_u32 (p)) - sig->expiredate = sig->timestamp + buffer_to_u32 (p); + if (p && buf32_to_u32 (p)) + sig->expiredate = sig->timestamp + buf32_to_u32 (p); if (sig->expiredate && sig->expiredate <= make_timestamp ()) sig->flags.expired = 1; p = parse_sig_subpkt (sig->hashed, SIGSUBPKT_POLICY, NULL); if (p) sig->flags.policy_url = 1; p = parse_sig_subpkt (sig->hashed, SIGSUBPKT_PREF_KS, NULL); if (p) sig->flags.pref_ks = 1; p = parse_sig_subpkt (sig->hashed, SIGSUBPKT_NOTATION, NULL); if (p) sig->flags.notation = 1; p = parse_sig_subpkt (sig->hashed, SIGSUBPKT_REVOCABLE, NULL); if (p && *p == 0) sig->flags.revocable = 0; p = parse_sig_subpkt (sig->hashed, SIGSUBPKT_TRUST, &len); if (p && len == 2) { sig->trust_depth = p[0]; sig->trust_value = p[1]; /* Only look for a regexp if there is also a trust subpacket. */ sig->trust_regexp = parse_sig_subpkt (sig->hashed, SIGSUBPKT_REGEXP, &len); /* If the regular expression is of 0 length, there is no regular expression. */ if (len == 0) sig->trust_regexp = NULL; } /* We accept the exportable subpacket from either the hashed or unhashed areas as older versions of gpg put it in the unhashed area. In theory, anyway, we should never see this packet off of a local keyring. */ p = parse_sig_subpkt2 (sig, SIGSUBPKT_EXPORTABLE, NULL); if (p && *p == 0) sig->flags.exportable = 0; /* Find all revocation keys. */ if (sig->sig_class == 0x1F) parse_revkeys (sig); } if (list_mode) { es_fprintf (listfp, ":signature packet: algo %d, keyid %08lX%08lX\n" "\tversion %d, created %lu, md5len %d, sigclass 0x%02x\n" "\tdigest algo %d, begin of digest %02x %02x\n", sig->pubkey_algo, (ulong) sig->keyid[0], (ulong) sig->keyid[1], sig->version, (ulong) sig->timestamp, md5_len, sig->sig_class, sig->digest_algo, sig->digest_start[0], sig->digest_start[1]); if (is_v4) { parse_sig_subpkt (sig->hashed, SIGSUBPKT_LIST_HASHED, NULL); parse_sig_subpkt (sig->unhashed, SIGSUBPKT_LIST_UNHASHED, NULL); } } ndata = pubkey_get_nsig (sig->pubkey_algo); if (!ndata) { if (list_mode) es_fprintf (listfp, "\tunknown algorithm %d\n", sig->pubkey_algo); unknown_pubkey_warning (sig->pubkey_algo); /* We store the plain material in data[0], so that we are able * to write it back with build_packet(). */ if (pktlen > (5 * MAX_EXTERN_MPI_BITS / 8)) { /* We include a limit to avoid too trivial DoS attacks by having gpg allocate too much memory. */ log_error ("signature packet: too much data\n"); rc = GPG_ERR_INV_PACKET; } else { sig->data[0] = gcry_mpi_set_opaque (NULL, read_rest (inp, pktlen), pktlen * 8); pktlen = 0; } } else { for (i = 0; i < ndata; i++) { n = pktlen; sig->data[i] = mpi_read (inp, &n, 0); pktlen -= n; if (list_mode) { es_fprintf (listfp, "\tdata: "); mpi_print (listfp, sig->data[i], mpi_print_mode); es_putc ('\n', listfp); } if (!sig->data[i]) rc = GPG_ERR_INV_PACKET; } } leave: iobuf_skip_rest (inp, pktlen, 0); return rc; } static int parse_onepass_sig (IOBUF inp, int pkttype, unsigned long pktlen, PKT_onepass_sig * ops) { int version; int rc = 0; if (pktlen < 13) { log_error ("packet(%d) too short\n", pkttype); if (list_mode) es_fputs (":onepass_sig packet: [too short]\n", listfp); rc = gpg_error (GPG_ERR_INV_PACKET); goto leave; } version = iobuf_get_noeof (inp); pktlen--; if (version != 3) { log_error ("onepass_sig with unknown version %d\n", version); if (list_mode) es_fputs (":onepass_sig packet: [unknown version]\n", listfp); rc = gpg_error (GPG_ERR_INV_PACKET); goto leave; } ops->sig_class = iobuf_get_noeof (inp); pktlen--; ops->digest_algo = iobuf_get_noeof (inp); pktlen--; ops->pubkey_algo = iobuf_get_noeof (inp); pktlen--; ops->keyid[0] = read_32 (inp); pktlen -= 4; ops->keyid[1] = read_32 (inp); pktlen -= 4; ops->last = iobuf_get_noeof (inp); pktlen--; if (list_mode) es_fprintf (listfp, ":onepass_sig packet: keyid %08lX%08lX\n" "\tversion %d, sigclass 0x%02x, digest %d, pubkey %d, " "last=%d\n", (ulong) ops->keyid[0], (ulong) ops->keyid[1], version, ops->sig_class, ops->digest_algo, ops->pubkey_algo, ops->last); leave: iobuf_skip_rest (inp, pktlen, 0); return rc; } static int parse_key (IOBUF inp, int pkttype, unsigned long pktlen, byte * hdr, int hdrlen, PACKET * pkt) { gpg_error_t err = 0; int i, version, algorithm; unsigned long timestamp, expiredate, max_expiredate; int npkey, nskey; u32 keyid[2]; PKT_public_key *pk; (void) hdr; pk = pkt->pkt.public_key; /* PK has been cleared. */ version = iobuf_get_noeof (inp); pktlen--; if (pkttype == PKT_PUBLIC_SUBKEY && version == '#') { /* Early versions of G10 used the old PGP comments packets; * luckily all those comments are started by a hash. */ if (list_mode) { es_fprintf (listfp, ":rfc1991 comment packet: \""); for (; pktlen; pktlen--) { int c; c = iobuf_get (inp); if (c == -1) break; /* Ooops: shorter than indicated. */ if (c >= ' ' && c <= 'z') es_putc (c, listfp); else es_fprintf (listfp, "\\x%02x", c); } es_fprintf (listfp, "\"\n"); } iobuf_skip_rest (inp, pktlen, 0); return 0; } else if (version == 4) { /* The only supported version. Use an older gpg version (i.e. gpg 1.4) to parse v3 packets. */ } else if (version == 2 || version == 3) { if (opt.verbose > 1) log_info ("packet(%d) with obsolete version %d\n", pkttype, version); if (list_mode) es_fprintf (listfp, ":key packet: [obsolete version %d]\n", version); pk->version = version; err = gpg_error (GPG_ERR_LEGACY_KEY); goto leave; } else { log_error ("packet(%d) with unknown version %d\n", pkttype, version); if (list_mode) es_fputs (":key packet: [unknown version]\n", listfp); err = gpg_error (GPG_ERR_INV_PACKET); goto leave; } if (pktlen < 11) { log_error ("packet(%d) too short\n", pkttype); if (list_mode) es_fputs (":key packet: [too short]\n", listfp); err = gpg_error (GPG_ERR_INV_PACKET); goto leave; } else if (pktlen > MAX_KEY_PACKET_LENGTH) { log_error ("packet(%d) too large\n", pkttype); if (list_mode) es_fputs (":key packet: [too larget]\n", listfp); err = gpg_error (GPG_ERR_INV_PACKET); goto leave; } timestamp = read_32 (inp); pktlen -= 4; expiredate = 0; /* have to get it from the selfsignature */ max_expiredate = 0; algorithm = iobuf_get_noeof (inp); pktlen--; if (list_mode) es_fprintf (listfp, ":%s key packet:\n" "\tversion %d, algo %d, created %lu, expires %lu\n", pkttype == PKT_PUBLIC_KEY ? "public" : pkttype == PKT_SECRET_KEY ? "secret" : pkttype == PKT_PUBLIC_SUBKEY ? "public sub" : pkttype == PKT_SECRET_SUBKEY ? "secret sub" : "??", version, algorithm, timestamp, expiredate); pk->timestamp = timestamp; pk->expiredate = expiredate; pk->max_expiredate = max_expiredate; pk->hdrbytes = hdrlen; pk->version = version; pk->flags.primary = (pkttype == PKT_PUBLIC_KEY || pkttype == PKT_SECRET_KEY); pk->pubkey_algo = algorithm; nskey = pubkey_get_nskey (algorithm); npkey = pubkey_get_npkey (algorithm); if (!npkey) { if (list_mode) es_fprintf (listfp, "\tunknown algorithm %d\n", algorithm); unknown_pubkey_warning (algorithm); } if (!npkey) { /* Unknown algorithm - put data into an opaque MPI. */ pk->pkey[0] = gcry_mpi_set_opaque (NULL, read_rest (inp, pktlen), pktlen * 8); pktlen = 0; goto leave; } else { for (i = 0; i < npkey; i++) { if ( (algorithm == PUBKEY_ALGO_ECDSA && (i == 0)) || (algorithm == PUBKEY_ALGO_EDDSA && (i == 0)) || (algorithm == PUBKEY_ALGO_ECDH && (i == 0 || i == 2))) { /* Read the OID (i==1) or the KDF params (i==2). */ size_t n; err = read_size_body (inp, pktlen, &n, pk->pkey+i); pktlen -= n; } else { unsigned int n = pktlen; pk->pkey[i] = mpi_read (inp, &n, 0); pktlen -= n; if (!pk->pkey[i]) err = gpg_error (GPG_ERR_INV_PACKET); } if (err) goto leave; if (list_mode) { es_fprintf (listfp, "\tpkey[%d]: ", i); mpi_print (listfp, pk->pkey[i], mpi_print_mode); if ((algorithm == PUBKEY_ALGO_ECDSA || algorithm == PUBKEY_ALGO_EDDSA || algorithm == PUBKEY_ALGO_ECDH) && i==0) { char *curve = openpgp_oid_to_str (pk->pkey[0]); es_fprintf (listfp, " %s (%s)", openpgp_oid_to_curve (curve), curve); xfree (curve); } es_putc ('\n', listfp); } } } if (list_mode) keyid_from_pk (pk, keyid); if (pkttype == PKT_SECRET_KEY || pkttype == PKT_SECRET_SUBKEY) { struct seckey_info *ski; byte temp[16]; size_t snlen = 0; pk->seckey_info = ski = xtrycalloc (1, sizeof *ski); if (!pk->seckey_info) { err = gpg_error_from_syserror (); goto leave; } ski->algo = iobuf_get_noeof (inp); pktlen--; if (ski->algo) { ski->is_protected = 1; ski->s2k.count = 0; if (ski->algo == 254 || ski->algo == 255) { if (pktlen < 3) { err = gpg_error (GPG_ERR_INV_PACKET); goto leave; } ski->sha1chk = (ski->algo == 254); ski->algo = iobuf_get_noeof (inp); pktlen--; /* Note that a ski->algo > 110 is illegal, but I'm not erroring on it here as otherwise there would be no way to delete such a key. */ ski->s2k.mode = iobuf_get_noeof (inp); pktlen--; ski->s2k.hash_algo = iobuf_get_noeof (inp); pktlen--; /* Check for the special GNU extension. */ if (ski->s2k.mode == 101) { for (i = 0; i < 4 && pktlen; i++, pktlen--) temp[i] = iobuf_get_noeof (inp); if (i < 4 || memcmp (temp, "GNU", 3)) { if (list_mode) es_fprintf (listfp, "\tunknown S2K %d\n", ski->s2k.mode); err = gpg_error (GPG_ERR_INV_PACKET); goto leave; } /* Here we know that it is a GNU extension. What * follows is the GNU protection mode: All values * have special meanings and they are mapped to MODE * with a base of 1000. */ ski->s2k.mode = 1000 + temp[3]; } /* Read the salt. */ switch (ski->s2k.mode) { case 1: case 3: for (i = 0; i < 8 && pktlen; i++, pktlen--) temp[i] = iobuf_get_noeof (inp); memcpy (ski->s2k.salt, temp, 8); break; } /* Check the mode. */ switch (ski->s2k.mode) { case 0: if (list_mode) es_fprintf (listfp, "\tsimple S2K"); break; case 1: if (list_mode) es_fprintf (listfp, "\tsalted S2K"); break; case 3: if (list_mode) es_fprintf (listfp, "\titer+salt S2K"); break; case 1001: if (list_mode) es_fprintf (listfp, "\tgnu-dummy S2K"); break; case 1002: if (list_mode) es_fprintf (listfp, "\tgnu-divert-to-card S2K"); break; default: if (list_mode) es_fprintf (listfp, "\tunknown %sS2K %d\n", ski->s2k.mode < 1000 ? "" : "GNU ", ski->s2k.mode); err = gpg_error (GPG_ERR_INV_PACKET); goto leave; } /* Print some info. */ if (list_mode) { es_fprintf (listfp, ", algo: %d,%s hash: %d", ski->algo, ski->sha1chk ? " SHA1 protection," : " simple checksum,", ski->s2k.hash_algo); if (ski->s2k.mode == 1 || ski->s2k.mode == 3) { es_fprintf (listfp, ", salt: "); es_write_hexstring (listfp, ski->s2k.salt, 8, 0, NULL); } es_putc ('\n', listfp); } /* Read remaining protection parameters. */ if (ski->s2k.mode == 3) { if (pktlen < 1) { err = gpg_error (GPG_ERR_INV_PACKET); goto leave; } ski->s2k.count = iobuf_get (inp); pktlen--; if (list_mode) es_fprintf (listfp, "\tprotect count: %lu (%lu)\n", (ulong)S2K_DECODE_COUNT ((ulong)ski->s2k.count), (ulong) ski->s2k.count); } else if (ski->s2k.mode == 1002) { /* Read the serial number. */ if (pktlen < 1) { err = gpg_error (GPG_ERR_INV_PACKET); goto leave; } snlen = iobuf_get (inp); pktlen--; if (pktlen < snlen || snlen == (size_t)(-1)) { err = gpg_error (GPG_ERR_INV_PACKET); goto leave; } } } else /* Old version; no S2K, so we set mode to 0, hash MD5. */ { /* Note that a ski->algo > 110 is illegal, but I'm not erroring on it here as otherwise there would be no way to delete such a key. */ ski->s2k.mode = 0; ski->s2k.hash_algo = DIGEST_ALGO_MD5; if (list_mode) es_fprintf (listfp, "\tprotect algo: %d (hash algo: %d)\n", ski->algo, ski->s2k.hash_algo); } /* It is really ugly that we don't know the size * of the IV here in cases we are not aware of the algorithm. * so a * ski->ivlen = cipher_get_blocksize (ski->algo); * won't work. The only solution I see is to hardwire it. * NOTE: if you change the ivlen above 16, don't forget to * enlarge temp. */ ski->ivlen = openpgp_cipher_blocklen (ski->algo); assert (ski->ivlen <= sizeof (temp)); if (ski->s2k.mode == 1001) ski->ivlen = 0; else if (ski->s2k.mode == 1002) ski->ivlen = snlen < 16 ? snlen : 16; if (pktlen < ski->ivlen) { err = gpg_error (GPG_ERR_INV_PACKET); goto leave; } for (i = 0; i < ski->ivlen && pktlen; i++, pktlen--) temp[i] = iobuf_get_noeof (inp); if (list_mode) { es_fprintf (listfp, ski->s2k.mode == 1002 ? "\tserial-number: " : "\tprotect IV: "); for (i = 0; i < ski->ivlen; i++) es_fprintf (listfp, " %02x", temp[i]); es_putc ('\n', listfp); } memcpy (ski->iv, temp, ski->ivlen); } /* It does not make sense to read it into secure memory. * If the user is so careless, not to protect his secret key, * we can assume, that he operates an open system :=(. * So we put the key into secure memory when we unprotect it. */ if (ski->s2k.mode == 1001 || ski->s2k.mode == 1002) { /* Better set some dummy stuff here. */ pk->pkey[npkey] = gcry_mpi_set_opaque (NULL, xstrdup ("dummydata"), 10 * 8); pktlen = 0; } else if (ski->is_protected) { /* Ugly: The length is encrypted too, so we read all stuff * up to the end of the packet into the first SKEY * element. */ pk->pkey[npkey] = gcry_mpi_set_opaque (NULL, read_rest (inp, pktlen), pktlen * 8); /* Mark that MPI as protected - we need this information for importing a key. The OPAQUE flag can't be used because we also store public EdDSA values in opaque MPIs. */ if (pk->pkey[npkey]) gcry_mpi_set_flag (pk->pkey[npkey], GCRYMPI_FLAG_USER1); pktlen = 0; if (list_mode) es_fprintf (listfp, "\tskey[%d]: [v4 protected]\n", npkey); } else { /* Not encrypted. */ for (i = npkey; i < nskey; i++) { unsigned int n = pktlen; pk->pkey[i] = mpi_read (inp, &n, 0); pktlen -= n; if (list_mode) { es_fprintf (listfp, "\tskey[%d]: ", i); mpi_print (listfp, pk->pkey[i], mpi_print_mode); es_putc ('\n', listfp); } if (!pk->pkey[i]) err = gpg_error (GPG_ERR_INV_PACKET); } if (err) goto leave; ski->csum = read_16 (inp); pktlen -= 2; if (list_mode) es_fprintf (listfp, "\tchecksum: %04hx\n", ski->csum); } } if (list_mode) es_fprintf (listfp, "\tkeyid: %08lX%08lX\n", (ulong) keyid[0], (ulong) keyid[1]); leave: iobuf_skip_rest (inp, pktlen, 0); return err; } /* Attribute subpackets have the same format as v4 signature subpackets. This is not part of OpenPGP, but is done in several versions of PGP nevertheless. */ int parse_attribute_subpkts (PKT_user_id * uid) { size_t n; int count = 0; struct user_attribute *attribs = NULL; const byte *buffer = uid->attrib_data; int buflen = uid->attrib_len; byte type; xfree (uid->attribs); while (buflen) { n = *buffer++; buflen--; if (n == 255) /* 4 byte length header. */ { if (buflen < 4) goto too_short; - n = (buffer[0] << 24) | (buffer[1] << 16) - | (buffer[2] << 8) | buffer[3]; + n = buf32_to_size_t (buffer); buffer += 4; buflen -= 4; } else if (n >= 192) /* 2 byte special encoded length header. */ { if (buflen < 2) goto too_short; n = ((n - 192) << 8) + *buffer + 192; buffer++; buflen--; } if (buflen < n) goto too_short; if (!n) { /* Too short to encode the subpacket type. */ if (opt.verbose) log_info ("attribute subpacket too short\n"); break; } attribs = xrealloc (attribs, (count + 1) * sizeof (struct user_attribute)); memset (&attribs[count], 0, sizeof (struct user_attribute)); type = *buffer; buffer++; buflen--; n--; attribs[count].type = type; attribs[count].data = buffer; attribs[count].len = n; buffer += n; buflen -= n; count++; } uid->attribs = attribs; uid->numattribs = count; return count; too_short: if (opt.verbose) log_info ("buffer shorter than attribute subpacket\n"); uid->attribs = attribs; uid->numattribs = count; return count; } static int parse_user_id (IOBUF inp, int pkttype, unsigned long pktlen, PACKET * packet) { byte *p; /* Cap the size of a user ID at 2k: a value absurdly large enough that there is no sane user ID string (which is printable text as of RFC2440bis) that won't fit in it, but yet small enough to avoid allocation problems. A large pktlen may not be allocatable, and a very large pktlen could actually cause our allocation to wrap around in xmalloc to a small number. */ if (pktlen > MAX_UID_PACKET_LENGTH) { log_error ("packet(%d) too large\n", pkttype); if (list_mode) es_fprintf (listfp, ":user ID packet: [too large]\n"); iobuf_skip_rest (inp, pktlen, 0); return GPG_ERR_INV_PACKET; } packet->pkt.user_id = xmalloc_clear (sizeof *packet->pkt.user_id + pktlen); packet->pkt.user_id->len = pktlen; packet->pkt.user_id->ref = 1; p = packet->pkt.user_id->name; for (; pktlen; pktlen--, p++) *p = iobuf_get_noeof (inp); *p = 0; if (list_mode) { int n = packet->pkt.user_id->len; es_fprintf (listfp, ":user ID packet: \""); /* fixme: Hey why don't we replace this with es_write_sanitized?? */ for (p = packet->pkt.user_id->name; n; p++, n--) { if (*p >= ' ' && *p <= 'z') es_putc (*p, listfp); else es_fprintf (listfp, "\\x%02x", *p); } es_fprintf (listfp, "\"\n"); } return 0; } void make_attribute_uidname (PKT_user_id * uid, size_t max_namelen) { assert (max_namelen > 70); if (uid->numattribs <= 0) sprintf (uid->name, "[bad attribute packet of size %lu]", uid->attrib_len); else if (uid->numattribs > 1) sprintf (uid->name, "[%d attributes of size %lu]", uid->numattribs, uid->attrib_len); else { /* Only one attribute, so list it as the "user id" */ if (uid->attribs->type == ATTRIB_IMAGE) { u32 len; byte type; if (parse_image_header (uid->attribs, &type, &len)) sprintf (uid->name, "[%.20s image of size %lu]", image_type_to_string (type, 1), (ulong) len); else sprintf (uid->name, "[invalid image]"); } else sprintf (uid->name, "[unknown attribute of size %lu]", (ulong) uid->attribs->len); } uid->len = strlen (uid->name); } static int parse_attribute (IOBUF inp, int pkttype, unsigned long pktlen, PACKET * packet) { byte *p; (void) pkttype; /* We better cap the size of an attribute packet to make DoS not too easy. 16MB should be more then enough for one attribute packet (ie. a photo). */ if (pktlen > MAX_ATTR_PACKET_LENGTH) { log_error ("packet(%d) too large\n", pkttype); if (list_mode) es_fprintf (listfp, ":attribute packet: [too large]\n"); iobuf_skip_rest (inp, pktlen, 0); return GPG_ERR_INV_PACKET; } #define EXTRA_UID_NAME_SPACE 71 packet->pkt.user_id = xmalloc_clear (sizeof *packet->pkt.user_id + EXTRA_UID_NAME_SPACE); packet->pkt.user_id->ref = 1; packet->pkt.user_id->attrib_data = xmalloc (pktlen? pktlen:1); packet->pkt.user_id->attrib_len = pktlen; p = packet->pkt.user_id->attrib_data; for (; pktlen; pktlen--, p++) *p = iobuf_get_noeof (inp); /* Now parse out the individual attribute subpackets. This is somewhat pointless since there is only one currently defined attribute type (jpeg), but it is correct by the spec. */ parse_attribute_subpkts (packet->pkt.user_id); make_attribute_uidname (packet->pkt.user_id, EXTRA_UID_NAME_SPACE); if (list_mode) { es_fprintf (listfp, ":attribute packet: %s\n", packet->pkt.user_id->name); } return 0; } static int parse_comment (IOBUF inp, int pkttype, unsigned long pktlen, PACKET * packet) { byte *p; /* Cap comment packet at a reasonable value to avoid an integer overflow in the malloc below. Comment packets are actually not anymore define my OpenPGP and we even stopped to use our private comment packet. */ if (pktlen > MAX_COMMENT_PACKET_LENGTH) { log_error ("packet(%d) too large\n", pkttype); if (list_mode) es_fprintf (listfp, ":%scomment packet: [too large]\n", pkttype == PKT_OLD_COMMENT ? "OpenPGP draft " : ""); iobuf_skip_rest (inp, pktlen, 0); return GPG_ERR_INV_PACKET; } packet->pkt.comment = xmalloc (sizeof *packet->pkt.comment + pktlen - 1); packet->pkt.comment->len = pktlen; p = packet->pkt.comment->data; for (; pktlen; pktlen--, p++) *p = iobuf_get_noeof (inp); if (list_mode) { int n = packet->pkt.comment->len; es_fprintf (listfp, ":%scomment packet: \"", pkttype == PKT_OLD_COMMENT ? "OpenPGP draft " : ""); for (p = packet->pkt.comment->data; n; p++, n--) { if (*p >= ' ' && *p <= 'z') es_putc (*p, listfp); else es_fprintf (listfp, "\\x%02x", *p); } es_fprintf (listfp, "\"\n"); } return 0; } static void parse_trust (IOBUF inp, int pkttype, unsigned long pktlen, PACKET * pkt) { int c; (void) pkttype; pkt->pkt.ring_trust = xmalloc (sizeof *pkt->pkt.ring_trust); if (pktlen) { c = iobuf_get_noeof (inp); pktlen--; pkt->pkt.ring_trust->trustval = c; pkt->pkt.ring_trust->sigcache = 0; if (!c && pktlen == 1) { c = iobuf_get_noeof (inp); pktlen--; /* We require that bit 7 of the sigcache is 0 (easier eof handling). */ if (!(c & 0x80)) pkt->pkt.ring_trust->sigcache = c; } if (list_mode) es_fprintf (listfp, ":trust packet: flag=%02x sigcache=%02x\n", pkt->pkt.ring_trust->trustval, pkt->pkt.ring_trust->sigcache); } else { pkt->pkt.ring_trust->trustval = 0; pkt->pkt.ring_trust->sigcache = 0; if (list_mode) es_fprintf (listfp, ":trust packet: empty\n"); } iobuf_skip_rest (inp, pktlen, 0); } static int parse_plaintext (IOBUF inp, int pkttype, unsigned long pktlen, PACKET * pkt, int new_ctb, int partial) { int rc = 0; int mode, namelen; PKT_plaintext *pt; byte *p; int c, i; if (!partial && pktlen < 6) { log_error ("packet(%d) too short (%lu)\n", pkttype, (ulong) pktlen); if (list_mode) es_fputs (":literal data packet: [too short]\n", listfp); rc = gpg_error (GPG_ERR_INV_PACKET); goto leave; } mode = iobuf_get_noeof (inp); if (pktlen) pktlen--; namelen = iobuf_get_noeof (inp); if (pktlen) pktlen--; /* Note that namelen will never exceed 255 bytes. */ pt = pkt->pkt.plaintext = xmalloc (sizeof *pkt->pkt.plaintext + namelen - 1); pt->new_ctb = new_ctb; pt->mode = mode; pt->namelen = namelen; pt->is_partial = partial; if (pktlen) { for (i = 0; pktlen > 4 && i < namelen; pktlen--, i++) pt->name[i] = iobuf_get_noeof (inp); } else { for (i = 0; i < namelen; i++) if ((c = iobuf_get (inp)) == -1) break; else pt->name[i] = c; } pt->timestamp = read_32 (inp); if (pktlen) pktlen -= 4; pt->len = pktlen; pt->buf = inp; pktlen = 0; if (list_mode) { es_fprintf (listfp, ":literal data packet:\n" "\tmode %c (%X), created %lu, name=\"", mode >= ' ' && mode < 'z' ? mode : '?', mode, (ulong) pt->timestamp); for (p = pt->name, i = 0; i < namelen; p++, i++) { if (*p >= ' ' && *p <= 'z') es_putc (*p, listfp); else es_fprintf (listfp, "\\x%02x", *p); } es_fprintf (listfp, "\",\n\traw data: "); if (partial) es_fprintf (listfp, "unknown length\n"); else es_fprintf (listfp, "%lu bytes\n", (ulong) pt->len); } leave: return rc; } static int parse_compressed (IOBUF inp, int pkttype, unsigned long pktlen, PACKET * pkt, int new_ctb) { PKT_compressed *zd; /* PKTLEN is here 0, but data follows (this should be the last object in a file or the compress algorithm should know the length). */ (void) pkttype; (void) pktlen; zd = pkt->pkt.compressed = xmalloc (sizeof *pkt->pkt.compressed); zd->algorithm = iobuf_get_noeof (inp); zd->len = 0; /* not used */ zd->new_ctb = new_ctb; zd->buf = inp; if (list_mode) es_fprintf (listfp, ":compressed packet: algo=%d\n", zd->algorithm); return 0; } static int parse_encrypted (IOBUF inp, int pkttype, unsigned long pktlen, PACKET * pkt, int new_ctb, int partial) { int rc = 0; PKT_encrypted *ed; unsigned long orig_pktlen = pktlen; ed = pkt->pkt.encrypted = xmalloc (sizeof *pkt->pkt.encrypted); /* ed->len is set below. */ ed->extralen = 0; /* Unknown here; only used in build_packet. */ ed->buf = NULL; ed->new_ctb = new_ctb; ed->is_partial = partial; if (pkttype == PKT_ENCRYPTED_MDC) { /* Fixme: add some pktlen sanity checks. */ int version; version = iobuf_get_noeof (inp); if (orig_pktlen) pktlen--; if (version != 1) { log_error ("encrypted_mdc packet with unknown version %d\n", version); if (list_mode) es_fputs (":encrypted data packet: [unknown version]\n", listfp); /*skip_rest(inp, pktlen); should we really do this? */ rc = gpg_error (GPG_ERR_INV_PACKET); goto leave; } ed->mdc_method = DIGEST_ALGO_SHA1; } else ed->mdc_method = 0; /* A basic sanity check. We need at least an 8 byte IV plus the 2 detection bytes. Note that we don't known the algorithm and thus we may only check against the minimum blocksize. */ if (orig_pktlen && pktlen < 10) { /* Actually this is blocksize+2. */ log_error ("packet(%d) too short\n", pkttype); if (list_mode) es_fputs (":encrypted data packet: [too short]\n", listfp); rc = GPG_ERR_INV_PACKET; iobuf_skip_rest (inp, pktlen, partial); goto leave; } /* Store the remaining length of the encrypted data (i.e. without the MDC version number but with the IV etc.). This value is required during decryption. */ ed->len = pktlen; if (list_mode) { if (orig_pktlen) es_fprintf (listfp, ":encrypted data packet:\n\tlength: %lu\n", orig_pktlen); else es_fprintf (listfp, ":encrypted data packet:\n\tlength: unknown\n"); if (ed->mdc_method) es_fprintf (listfp, "\tmdc_method: %d\n", ed->mdc_method); } ed->buf = inp; leave: return rc; } /* Note, that this code is not anymore used in real life because the MDC checking is now done right after the decryption in decrypt_data. */ static int parse_mdc (IOBUF inp, int pkttype, unsigned long pktlen, PACKET * pkt, int new_ctb) { int rc = 0; PKT_mdc *mdc; byte *p; (void) pkttype; mdc = pkt->pkt.mdc = xmalloc (sizeof *pkt->pkt.mdc); if (list_mode) es_fprintf (listfp, ":mdc packet: length=%lu\n", pktlen); if (!new_ctb || pktlen != 20) { log_error ("mdc_packet with invalid encoding\n"); rc = gpg_error (GPG_ERR_INV_PACKET); goto leave; } p = mdc->hash; for (; pktlen; pktlen--, p++) *p = iobuf_get_noeof (inp); leave: return rc; } /* * This packet is internally generated by us (ibn armor.c) to transfer * some information to the lower layer. To make sure that this packet * is really a GPG faked one and not one comming from outside, we * first check that there is a unique tag in it. * * The format of such a control packet is: * n byte session marker * 1 byte control type CTRLPKT_xxxxx * m byte control data */ static int parse_gpg_control (IOBUF inp, int pkttype, unsigned long pktlen, PACKET * packet, int partial) { byte *p; const byte *sesmark; size_t sesmarklen; int i; (void) pkttype; if (list_mode) es_fprintf (listfp, ":packet 63: length %lu ", pktlen); sesmark = get_session_marker (&sesmarklen); if (pktlen < sesmarklen + 1) /* 1 is for the control bytes */ goto skipit; for (i = 0; i < sesmarklen; i++, pktlen--) { if (sesmark[i] != iobuf_get_noeof (inp)) goto skipit; } if (pktlen > 4096) goto skipit; /* Definitely too large. We skip it to avoid an overflow in the malloc. */ if (list_mode) puts ("- gpg control packet"); packet->pkt.gpg_control = xmalloc (sizeof *packet->pkt.gpg_control + pktlen - 1); packet->pkt.gpg_control->control = iobuf_get_noeof (inp); pktlen--; packet->pkt.gpg_control->datalen = pktlen; p = packet->pkt.gpg_control->data; for (; pktlen; pktlen--, p++) *p = iobuf_get_noeof (inp); return 0; skipit: if (list_mode) { int c; i = 0; es_fprintf (listfp, "- private (rest length %lu)\n", pktlen); if (partial) { while ((c = iobuf_get (inp)) != -1) dump_hex_line (c, &i); } else { for (; pktlen; pktlen--) { dump_hex_line ((c = iobuf_get (inp)), &i); if (c == -1) break; } } es_putc ('\n', listfp); } iobuf_skip_rest (inp, pktlen, 0); return gpg_error (GPG_ERR_INV_PACKET); } /* Create a GPG control packet to be used internally as a placeholder. */ PACKET * create_gpg_control (ctrlpkttype_t type, const byte * data, size_t datalen) { PACKET *packet; byte *p; packet = xmalloc (sizeof *packet); init_packet (packet); packet->pkttype = PKT_GPG_CONTROL; packet->pkt.gpg_control = xmalloc (sizeof *packet->pkt.gpg_control + datalen - 1); packet->pkt.gpg_control->control = type; packet->pkt.gpg_control->datalen = datalen; p = packet->pkt.gpg_control->data; for (; datalen; datalen--, p++) *p = *data++; return packet; } diff --git a/g10/pubkey-enc.c b/g10/pubkey-enc.c index fcd24f86e..957476984 100644 --- a/g10/pubkey-enc.c +++ b/g10/pubkey-enc.c @@ -1,431 +1,431 @@ /* 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 #include "gpg.h" #include "util.h" #include "packet.h" #include "keydb.h" #include "trustdb.h" #include "status.h" #include "options.h" #include "main.h" #include "i18n.h" #include "pkglue.h" #include "call-agent.h" +#include "host2net.h" static gpg_error_t get_it (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->is_revoked && !uid->is_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 (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 (sk, k->keyid))) rc = get_it (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 (;;) { free_public_key (sk); sk = xmalloc_clear (sizeof *sk); rc = enum_secret_keys (&enum_context, sk); if (rc) { 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)); rc = get_it (k, dek, sk, keyid); if (!rc) { if (!opt.quiet) log_info (_("okay, we are the anonymous recipient.\n")); break; } else if (gpg_err_code (rc) == GPG_ERR_FULLY_CANCELED) break; /* Don't try any more secret keys. */ } enum_secret_keys (&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 (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); assert (fpn == 20); } /* Decrypt. */ desc = gpg_format_keydesc (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 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_CIPHER) log_printhex ("DEK frame:", frame, nframe); 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; /* Reuse NFRAME, which size is sufficient to include the session key. */ err = gcry_mpi_print (GCRYMPI_FMT_USG, frame, nframe, &nframe, decoded); mpi_release (decoded); if (err) goto leave; /* Now the frame are the bytes decrypted but padded session key. */ /* Allow double padding for the benefit of DEK size concealment. Higher than this is wasteful. */ if (!nframe || frame[nframe-1] > 8*2 || nframe <= 8 || frame[nframe-1] > nframe) { err = gpg_error (GPG_ERR_WRONG_SECKEY); goto leave; } nframe -= frame[nframe-1]; /* Remove padding. */ assert (!n); /* (used just below) */ } else { if (padding) { if (n + 7 > nframe) { err = gpg_error (GPG_ERR_WRONG_SECKEY); goto leave; } 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 = frame[nframe - 2] << 8; - csum |= frame[nframe - 1]; + 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_CIPHER) log_printhex ("DEK is:", dek->key, dek->keylen); /* Check that the algo is in the preferences and whether it has expired. */ { PKT_public_key *pk = NULL; KBNODE pkb = get_pubkeyblock (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 k; for (k = pkb; k; k = k->next) { if (k->pkt->pkttype == PKT_PUBLIC_KEY || k->pkt->pkttype == PKT_PUBLIC_SUBKEY) { u32 aki[2]; 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 (pk, 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/g10/seckey-cert.c b/g10/seckey-cert.c index 10037567c..02dbb4859 100644 --- a/g10/seckey-cert.c +++ b/g10/seckey-cert.c @@ -1,256 +1,256 @@ /* seckey-cert.c - Not anymore used * Copyright (C) 1998, 1999, 2000, 2001, 2002, * 2006, 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 . */ #error Not anymore used - only kept for reference in the repository. #include #include #include #include #include #include "gpg.h" #include "util.h" #include "packet.h" #include "keydb.h" #include "cipher.h" #include "main.h" #include "options.h" #include "i18n.h" #include "status.h" #include "pkglue.h" static int xxxx_do_check( PKT_secret_key *sk, const char *tryagain_text, int mode, int *canceled ) { gpg_error_t err; byte *buffer; u16 csum=0; int i, res; size_t nbytes; if( sk->is_protected ) { /* remove the protection */ DEK *dek = NULL; u32 keyid[4]; /* 4! because we need two of them */ gcry_cipher_hd_t cipher_hd=NULL; PKT_secret_key *save_sk; if( sk->protect.s2k.mode == 1001 ) { log_info(_("secret key parts are not available\n")); return GPG_ERR_UNUSABLE_SECKEY; } if( sk->protect.algo == CIPHER_ALGO_NONE ) BUG(); if( openpgp_cipher_test_algo( sk->protect.algo ) ) { log_info(_("protection algorithm %d%s is not supported\n"), sk->protect.algo,sk->protect.algo==1?" (IDEA)":"" ); return GPG_ERR_CIPHER_ALGO; } if(gcry_md_test_algo (sk->protect.s2k.hash_algo)) { log_info(_("protection digest %d is not supported\n"), sk->protect.s2k.hash_algo); return GPG_ERR_DIGEST_ALGO; } keyid_from_sk( sk, keyid ); keyid[2] = keyid[3] = 0; if (!sk->flags.primary) { keyid[2] = sk->main_keyid[0]; keyid[3] = sk->main_keyid[1]; } dek = passphrase_to_dek( keyid, sk->pubkey_algo, sk->protect.algo, &sk->protect.s2k, mode, tryagain_text, canceled ); if (!dek && canceled && *canceled) return GPG_ERR_CANCELED; err = openpgp_cipher_open (&cipher_hd, sk->protect.algo, GCRY_CIPHER_MODE_CFB, (GCRY_CIPHER_SECURE | (sk->protect.algo >= 100 ? 0 : GCRY_CIPHER_ENABLE_SYNC))); if (err) log_fatal ("cipher open failed: %s\n", gpg_strerror (err) ); err = gcry_cipher_setkey (cipher_hd, dek->key, dek->keylen); if (err) log_fatal ("set key failed: %s\n", gpg_strerror (err) ); xfree(dek); save_sk = copy_secret_key( NULL, sk ); gcry_cipher_setiv ( cipher_hd, sk->protect.iv, sk->protect.ivlen ); csum = 0; if( sk->version >= 4 ) { int ndata; unsigned int ndatabits; byte *p, *data; u16 csumc = 0; i = pubkey_get_npkey(sk->pubkey_algo); assert ( gcry_mpi_get_flag (sk->skey[i], GCRYMPI_FLAG_OPAQUE )); p = gcry_mpi_get_opaque ( sk->skey[i], &ndatabits ); ndata = (ndatabits+7)/8; if ( ndata > 1 ) - csumc = p[ndata-2] << 8 | p[ndata-1]; + csumc = buf16_to_u16 (p+ndata-2); data = xmalloc_secure ( ndata ); gcry_cipher_decrypt ( cipher_hd, data, ndata, p, ndata ); gcry_mpi_release (sk->skey[i]); sk->skey[i] = NULL ; p = data; if (sk->protect.sha1chk) { /* This is the new SHA1 checksum method to detect tampering with the key as used by the Klima/Rosa attack */ sk->csum = 0; csum = 1; if( ndata < 20 ) log_error("not enough bytes for SHA-1 checksum\n"); else { gcry_md_hd_t h; if ( gcry_md_open (&h, DIGEST_ALGO_SHA1, 1)) BUG(); /* Algo not available. */ gcry_md_write (h, data, ndata - 20); gcry_md_final (h); if (!memcmp (gcry_md_read (h, DIGEST_ALGO_SHA1), data + ndata - 20, 20) ) { /* Digest does match. We have to keep the old style checksum in sk->csum, so that the test used for unprotected keys does work. This test gets used when we are adding new keys. */ sk->csum = csum = checksum (data, ndata-20); } gcry_md_close (h); } } else { if( ndata < 2 ) { log_error("not enough bytes for checksum\n"); sk->csum = 0; csum = 1; } else { csum = checksum( data, ndata-2); sk->csum = data[ndata-2] << 8 | data[ndata-1]; if ( sk->csum != csum ) { /* This is a PGP 7.0.0 workaround */ sk->csum = csumc; /* take the encrypted one */ } } } /* Must check it here otherwise the mpi_read_xx would fail because the length may have an arbitrary value */ if( sk->csum == csum ) { for( ; i < pubkey_get_nskey(sk->pubkey_algo); i++ ) { if ( gcry_mpi_scan( &sk->skey[i], GCRYMPI_FMT_PGP, p, ndata, &nbytes)) { /* Checksum was okay, but not correctly decrypted. */ sk->csum = 0; csum = 1; break; } ndata -= nbytes; p += nbytes; } /* Note: at this point ndata should be 2 for a simple checksum or 20 for the sha1 digest */ } xfree(data); } else { for(i=pubkey_get_npkey(sk->pubkey_algo); i < pubkey_get_nskey(sk->pubkey_algo); i++ ) { byte *p; size_t ndata; unsigned int ndatabits; assert (gcry_mpi_get_flag (sk->skey[i], GCRYMPI_FLAG_OPAQUE)); p = gcry_mpi_get_opaque (sk->skey[i], &ndatabits); ndata = (ndatabits+7)/8; assert (ndata >= 2); assert (ndata == ((p[0] << 8 | p[1]) + 7)/8 + 2); buffer = xmalloc_secure (ndata); 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); csum += checksum (buffer, ndata); gcry_mpi_release (sk->skey[i]); err = gcry_mpi_scan( &sk->skey[i], GCRYMPI_FMT_PGP, buffer, ndata, &ndata ); xfree (buffer); if (err) { /* Checksum was okay, but not correctly decrypted. */ sk->csum = 0; csum = 1; break; } /* csum += checksum_mpi (sk->skey[i]); */ } } gcry_cipher_close ( cipher_hd ); /* Now let's see whether we have used the correct passphrase. */ if( csum != sk->csum ) { copy_secret_key( sk, save_sk ); passphrase_clear_cache ( keyid, NULL, sk->pubkey_algo ); free_secret_key( save_sk ); return gpg_error (GPG_ERR_BAD_PASSPHRASE); } /* The checksum may fail, so we also check the key itself. */ res = pk_check_secret_key ( sk->pubkey_algo, sk->skey ); if( res ) { copy_secret_key( sk, save_sk ); passphrase_clear_cache ( keyid, NULL, sk->pubkey_algo ); free_secret_key( save_sk ); return gpg_error (GPG_ERR_BAD_PASSPHRASE); } free_secret_key( save_sk ); sk->is_protected = 0; } else { /* not protected, assume it is okay if the checksum is okay */ csum = 0; for(i=pubkey_get_npkey(sk->pubkey_algo); i < pubkey_get_nskey(sk->pubkey_algo); i++ ) { csum += checksum_mpi( sk->skey[i] ); } if( csum != sk->csum ) return GPG_ERR_CHECKSUM; } return 0; } diff --git a/g10/tdbio.c b/g10/tdbio.c index 9bb8a04e0..91ee3ab78 100644 --- a/g10/tdbio.c +++ b/g10/tdbio.c @@ -1,1553 +1,1553 @@ /* tdbio.c - trust database I/O operations * Copyright (C) 1998-2002, 2012 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 #include "gpg.h" #include "status.h" #include "iobuf.h" #include "util.h" #include "options.h" #include "main.h" #include "i18n.h" #include "trustdb.h" #include "tdbio.h" #if defined(HAVE_DOSISH_SYSTEM) && !defined(ftruncate) #define ftruncate chsize #endif #if defined(HAVE_DOSISH_SYSTEM) || defined(__CYGWIN__) #define MY_O_BINARY O_BINARY #else #define MY_O_BINARY 0 #endif /* We use ERRNO despite that the cegcc provided open/read/write functions don't set ERRNO - at least show that ERRNO does not make sense. */ #ifdef HAVE_W32CE_SYSTEM #undef strerror #define strerror(a) ("[errno not available]") #endif /**************** * Yes, this is a very simple implementation. We should really * use a page aligned buffer and read complete pages. * To implement a simple trannsaction system, this is sufficient. */ typedef struct cache_ctrl_struct *CACHE_CTRL; struct cache_ctrl_struct { CACHE_CTRL next; struct { unsigned used:1; unsigned dirty:1; } flags; ulong recno; char data[TRUST_RECORD_LEN]; }; #define MAX_CACHE_ENTRIES_SOFT 200 /* may be increased while in a */ #define MAX_CACHE_ENTRIES_HARD 10000 /* transaction to this one */ static CACHE_CTRL cache_list; static int cache_entries; static int cache_is_dirty; /* a type used to pass infomation to cmp_krec_fpr */ struct cmp_krec_fpr_struct { int pubkey_algo; const char *fpr; int fprlen; }; /* a type used to pass infomation to cmp_[s]dir */ struct cmp_xdir_struct { int pubkey_algo; u32 keyid[2]; }; static char *db_name; static dotlock_t lockhandle; static int is_locked; static int db_fd = -1; static int in_transaction; static void open_db(void); /************************************* ************* record cache ********** *************************************/ /**************** * Get the data from therecord cache and return a * pointer into that cache. Caller should copy * the return data. NULL is returned on a cache miss. */ static const char * get_record_from_cache( ulong recno ) { CACHE_CTRL r; for( r = cache_list; r; r = r->next ) { if( r->flags.used && r->recno == recno ) return r->data; } return NULL; } static int write_cache_item( CACHE_CTRL r ) { gpg_error_t err; int n; if( lseek( db_fd, r->recno * TRUST_RECORD_LEN, SEEK_SET ) == -1 ) { err = gpg_error_from_syserror (); log_error(_("trustdb rec %lu: lseek failed: %s\n"), r->recno, strerror(errno) ); return err; } n = write( db_fd, r->data, TRUST_RECORD_LEN); if( n != TRUST_RECORD_LEN ) { err = gpg_error_from_syserror (); log_error(_("trustdb rec %lu: write failed (n=%d): %s\n"), r->recno, n, strerror(errno) ); return err; } r->flags.dirty = 0; return 0; } /**************** * Put data into the cache. This function may flush the * some cache entries if there is not enough space available. */ int put_record_into_cache( ulong recno, const char *data ) { CACHE_CTRL r, unused; int dirty_count = 0; int clean_count = 0; /* see whether we already cached this one */ for( unused = NULL, r = cache_list; r; r = r->next ) { if( !r->flags.used ) { if( !unused ) unused = r; } else if( r->recno == recno ) { if( !r->flags.dirty ) { /* Hmmm: should we use a a copy and compare? */ if( memcmp(r->data, data, TRUST_RECORD_LEN ) ) { r->flags.dirty = 1; cache_is_dirty = 1; } } memcpy( r->data, data, TRUST_RECORD_LEN ); return 0; } if( r->flags.used ) { if( r->flags.dirty ) dirty_count++; else clean_count++; } } /* not in the cache: add a new entry */ if( unused ) { /* reuse this entry */ r = unused; r->flags.used = 1; r->recno = recno; memcpy( r->data, data, TRUST_RECORD_LEN ); r->flags.dirty = 1; cache_is_dirty = 1; cache_entries++; return 0; } /* see whether we reached the limit */ if( cache_entries < MAX_CACHE_ENTRIES_SOFT ) { /* no */ r = xmalloc( sizeof *r ); r->flags.used = 1; r->recno = recno; memcpy( r->data, data, TRUST_RECORD_LEN ); r->flags.dirty = 1; r->next = cache_list; cache_list = r; cache_is_dirty = 1; cache_entries++; return 0; } /* cache is full: discard some clean entries */ if( clean_count ) { int n = clean_count / 3; /* discard a third of the clean entries */ if( !n ) n = 1; for( unused = NULL, r = cache_list; r; r = r->next ) { if( r->flags.used && !r->flags.dirty ) { if( !unused ) unused = r; r->flags.used = 0; cache_entries--; if( !--n ) break; } } assert( unused ); r = unused; r->flags.used = 1; r->recno = recno; memcpy( r->data, data, TRUST_RECORD_LEN ); r->flags.dirty = 1; cache_is_dirty = 1; cache_entries++; return 0; } /* no clean entries: have to flush some dirty entries */ if( in_transaction ) { /* but we can't do this while in a transaction * we increase the cache size instead */ if( cache_entries < MAX_CACHE_ENTRIES_HARD ) { /* no */ if( opt.debug && !(cache_entries % 100) ) log_debug("increasing tdbio cache size\n"); r = xmalloc( sizeof *r ); r->flags.used = 1; r->recno = recno; memcpy( r->data, data, TRUST_RECORD_LEN ); r->flags.dirty = 1; r->next = cache_list; cache_list = r; cache_is_dirty = 1; cache_entries++; return 0; } log_info(_("trustdb transaction too large\n")); return GPG_ERR_RESOURCE_LIMIT; } if( dirty_count ) { int n = dirty_count / 5; /* discard some dirty entries */ if( !n ) n = 1; if( !is_locked ) { if( dotlock_take( lockhandle, -1 ) ) log_fatal("can't acquire lock - giving up\n"); else is_locked = 1; } for( unused = NULL, r = cache_list; r; r = r->next ) { if( r->flags.used && r->flags.dirty ) { int rc = write_cache_item( r ); if( rc ) return rc; if( !unused ) unused = r; r->flags.used = 0; cache_entries--; if( !--n ) break; } } if( !opt.lock_once ) { if( !dotlock_release( lockhandle ) ) is_locked = 0; } assert( unused ); r = unused; r->flags.used = 1; r->recno = recno; memcpy( r->data, data, TRUST_RECORD_LEN ); r->flags.dirty = 1; cache_is_dirty = 1; cache_entries++; return 0; } BUG(); } int tdbio_is_dirty() { return cache_is_dirty; } /**************** * Flush the cache. This cannot be used while in a transaction. */ int tdbio_sync() { CACHE_CTRL r; int did_lock = 0; if( db_fd == -1 ) open_db(); if( in_transaction ) log_bug("tdbio: syncing while in transaction\n"); if( !cache_is_dirty ) return 0; if( !is_locked ) { if( dotlock_take( lockhandle, -1 ) ) log_fatal("can't acquire lock - giving up\n"); else is_locked = 1; did_lock = 1; } for( r = cache_list; r; r = r->next ) { if( r->flags.used && r->flags.dirty ) { int rc = write_cache_item( r ); if( rc ) return rc; } } cache_is_dirty = 0; if( did_lock && !opt.lock_once ) { if( !dotlock_release (lockhandle) ) is_locked = 0; } return 0; } #if 0 /* The transaction code is disabled in the 1.2.x branch, as it is not yet used. It will be enabled in 1.3.x. */ /**************** * Simple transactions system: * Everything between begin_transaction and end/cancel_transaction * is not immediatly written but at the time of end_transaction. * */ int tdbio_begin_transaction() { int rc; if( in_transaction ) log_bug("tdbio: nested transactions\n"); /* flush everything out */ rc = tdbio_sync(); if( rc ) return rc; in_transaction = 1; return 0; } int tdbio_end_transaction() { int rc; if( !in_transaction ) log_bug("tdbio: no active transaction\n"); if( !is_locked ) { if( dotlock_take( lockhandle, -1 ) ) log_fatal("can't acquire lock - giving up\n"); else is_locked = 1; } block_all_signals(); in_transaction = 0; rc = tdbio_sync(); unblock_all_signals(); if( !opt.lock_once ) { if( !dotlock_release (lockhandle) ) is_locked = 0; } return rc; } int tdbio_cancel_transaction() { CACHE_CTRL r; if( !in_transaction ) log_bug("tdbio: no active transaction\n"); /* remove all dirty marked entries, so that the original ones * are read back the next time */ if( cache_is_dirty ) { for( r = cache_list; r; r = r->next ) { if( r->flags.used && r->flags.dirty ) { r->flags.used = 0; cache_entries--; } } cache_is_dirty = 0; } in_transaction = 0; return 0; } #endif /******************************************************** **************** cached I/O functions ****************** ********************************************************/ static void cleanup(void) { if( is_locked ) { if( !dotlock_release (lockhandle) ) is_locked = 0; } } /* Caller must sync */ int tdbio_update_version_record (void) { TRUSTREC rec; int rc; memset( &rec, 0, sizeof rec ); rc=tdbio_read_record( 0, &rec, RECTYPE_VER); if(rc==0) { rec.r.ver.created = make_timestamp(); rec.r.ver.marginals = opt.marginals_needed; rec.r.ver.completes = opt.completes_needed; rec.r.ver.cert_depth = opt.max_cert_depth; rec.r.ver.trust_model = opt.trust_model; rec.r.ver.min_cert_level = opt.min_cert_level; rc=tdbio_write_record(&rec); } return rc; } static int create_version_record (void) { TRUSTREC rec; int rc; memset( &rec, 0, sizeof rec ); rec.r.ver.version = 3; rec.r.ver.created = make_timestamp(); rec.r.ver.marginals = opt.marginals_needed; rec.r.ver.completes = opt.completes_needed; rec.r.ver.cert_depth = opt.max_cert_depth; if(opt.trust_model==TM_PGP || opt.trust_model==TM_CLASSIC) rec.r.ver.trust_model = opt.trust_model; else rec.r.ver.trust_model = TM_PGP; rec.r.ver.min_cert_level = opt.min_cert_level; rec.rectype = RECTYPE_VER; rec.recnum = 0; rc = tdbio_write_record( &rec ); if( !rc ) tdbio_sync(); return rc; } int tdbio_set_dbname( const char *new_dbname, int create, int *r_nofile) { char *fname; static int initialized = 0; if( !initialized ) { atexit( cleanup ); initialized = 1; } *r_nofile = 0; if(new_dbname==NULL) fname=make_filename(opt.homedir,"trustdb" EXTSEP_S GPGEXT_GPG, NULL); else if (*new_dbname != DIRSEP_C ) { if (strchr(new_dbname, DIRSEP_C) ) fname = make_filename (new_dbname, NULL); else fname = make_filename (opt.homedir, new_dbname, NULL); } else fname = xstrdup (new_dbname); if( access( fname, R_OK ) ) { #ifdef HAVE_W32CE_SYSTEM /* We know how the cegcc implementation of access works ;-). */ if (GetLastError () == ERROR_FILE_NOT_FOUND) gpg_err_set_errno (ENOENT); else gpg_err_set_errno (EIO); #endif /*HAVE_W32CE_SYSTEM*/ if( errno != ENOENT ) { log_error( _("can't access '%s': %s\n"), fname, strerror(errno) ); xfree(fname); return GPG_ERR_TRUSTDB; } if (!create) *r_nofile = 1; else { FILE *fp; TRUSTREC rec; int rc; char *p = strrchr( fname, DIRSEP_C ); mode_t oldmask; int save_slash; #if HAVE_W32_SYSTEM { /* Windows may either have a slash or a backslash. Take care of it. */ char *pp = strrchr (fname, '/'); if (!p || pp > p) p = pp; } #endif /*HAVE_W32_SYSTEM*/ assert (p); save_slash = *p; *p = 0; if( access( fname, F_OK ) ) { try_make_homedir( fname ); if (access (fname, F_OK )) log_fatal (_("%s: directory does not exist!\n"), fname); } *p = save_slash; xfree(db_name); db_name = fname; #ifdef __riscos__ if( !lockhandle ) lockhandle = dotlock_create (db_name, 0); if( !lockhandle ) log_fatal( _("can't create lock for '%s'\n"), db_name ); if( dotlock_make (lockhandle, -1) ) log_fatal( _("can't lock '%s'\n"), db_name ); #endif /* __riscos__ */ oldmask=umask(077); if (is_secured_filename (fname)) { fp = NULL; gpg_err_set_errno (EPERM); } else fp =fopen( fname, "wb" ); umask(oldmask); if( !fp ) log_fatal (_("can't create '%s': %s\n"), fname, strerror (errno)); fclose(fp); db_fd = open( db_name, O_RDWR | MY_O_BINARY ); if( db_fd == -1 ) log_fatal (_("can't open '%s': %s\n"), db_name, strerror (errno)); #ifndef __riscos__ if( !lockhandle ) lockhandle = dotlock_create (db_name, 0); if( !lockhandle ) log_fatal( _("can't create lock for '%s'\n"), db_name ); #endif /* !__riscos__ */ rc = create_version_record (); if( rc ) log_fatal( _("%s: failed to create version record: %s"), fname, gpg_strerror (rc)); /* and read again to check that we are okay */ if( tdbio_read_record( 0, &rec, RECTYPE_VER ) ) log_fatal( _("%s: invalid trustdb created\n"), db_name ); if( !opt.quiet ) log_info(_("%s: trustdb created\n"), db_name); return 0; } } xfree(db_name); db_name = fname; return 0; } const char * tdbio_get_dbname() { return db_name; } static void open_db() { TRUSTREC rec; assert( db_fd == -1 ); if (!lockhandle ) lockhandle = dotlock_create (db_name, 0); if (!lockhandle ) log_fatal( _("can't create lock for '%s'\n"), db_name ); #ifdef __riscos__ if (dotlock_take (lockhandle, -1) ) log_fatal( _("can't lock '%s'\n"), db_name ); #endif /* __riscos__ */ #ifdef HAVE_W32CE_SYSTEM { DWORD prevrc = 0; wchar_t *wname = utf8_to_wchar (db_name); if (wname) { db_fd = (int)CreateFile (wname, GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL); xfree (wname); } if (db_fd == -1) log_fatal ("can't open '%s': %d, %d\n", db_name, (int)prevrc, (int)GetLastError ()); } #else /*!HAVE_W32CE_SYSTEM*/ db_fd = open (db_name, O_RDWR | MY_O_BINARY ); if (db_fd == -1 && (errno == EACCES #ifdef EROFS || errno == EROFS #endif ) ) { /* Take care of read-only trustdbs. */ db_fd = open (db_name, O_RDONLY | MY_O_BINARY ); if (db_fd != -1 && !opt.quiet) log_info (_("Note: trustdb not writable\n")); } if ( db_fd == -1 ) log_fatal( _("can't open '%s': %s\n"), db_name, strerror(errno) ); #endif /*!HAVE_W32CE_SYSTEM*/ register_secured_file (db_name); /* Read the version record. */ if (tdbio_read_record (0, &rec, RECTYPE_VER ) ) log_fatal( _("%s: invalid trustdb\n"), db_name ); } /**************** * Make a hashtable: type 0 = trust hash */ static void create_hashtable( TRUSTREC *vr, int type ) { TRUSTREC rec; off_t offset; ulong recnum; int i, n, rc; offset = lseek( db_fd, 0, SEEK_END ); if( offset == -1 ) log_fatal("trustdb: lseek to end failed: %s\n", strerror(errno) ); recnum = offset / TRUST_RECORD_LEN; assert(recnum); /* this is will never be the first record */ if( !type ) vr->r.ver.trusthashtbl = recnum; /* Now write the records */ n = (256+ITEMS_PER_HTBL_RECORD-1) / ITEMS_PER_HTBL_RECORD; for(i=0; i < n; i++, recnum++ ) { memset( &rec, 0, sizeof rec ); rec.rectype = RECTYPE_HTBL; rec.recnum = recnum; rc = tdbio_write_record( &rec ); if( rc ) log_fatal( _("%s: failed to create hashtable: %s\n"), db_name, gpg_strerror (rc)); } /* update the version record */ rc = tdbio_write_record( vr ); if( !rc ) rc = tdbio_sync(); if( rc ) log_fatal( _("%s: error updating version record: %s\n"), db_name, gpg_strerror (rc)); } int tdbio_db_matches_options() { static int yes_no = -1; if( yes_no == -1 ) { TRUSTREC vr; int rc; rc = tdbio_read_record( 0, &vr, RECTYPE_VER ); if( rc ) log_fatal( _("%s: error reading version record: %s\n"), db_name, gpg_strerror (rc) ); yes_no = vr.r.ver.marginals == opt.marginals_needed && vr.r.ver.completes == opt.completes_needed && vr.r.ver.cert_depth == opt.max_cert_depth && vr.r.ver.trust_model == opt.trust_model && vr.r.ver.min_cert_level == opt.min_cert_level; } return yes_no; } byte tdbio_read_model(void) { TRUSTREC vr; int rc; rc = tdbio_read_record( 0, &vr, RECTYPE_VER ); if( rc ) log_fatal( _("%s: error reading version record: %s\n"), db_name, gpg_strerror (rc) ); return vr.r.ver.trust_model; } /**************** * Return the nextstamp value. */ ulong tdbio_read_nextcheck () { TRUSTREC vr; int rc; rc = tdbio_read_record( 0, &vr, RECTYPE_VER ); if( rc ) log_fatal( _("%s: error reading version record: %s\n"), db_name, gpg_strerror (rc)); return vr.r.ver.nextcheck; } /* Return true when the stamp was actually changed. */ int tdbio_write_nextcheck (ulong stamp) { TRUSTREC vr; int rc; rc = tdbio_read_record( 0, &vr, RECTYPE_VER ); if( rc ) log_fatal( _("%s: error reading version record: %s\n"), db_name, gpg_strerror (rc) ); if (vr.r.ver.nextcheck == stamp) return 0; vr.r.ver.nextcheck = stamp; rc = tdbio_write_record( &vr ); if( rc ) log_fatal( _("%s: error writing version record: %s\n"), db_name, gpg_strerror (rc) ); return 1; } /**************** * Return the record number of the trusthash tbl or create a new one. */ static ulong get_trusthashrec(void) { static ulong trusthashtbl; /* record number of the trust hashtable */ if( !trusthashtbl ) { TRUSTREC vr; int rc; rc = tdbio_read_record( 0, &vr, RECTYPE_VER ); if( rc ) log_fatal( _("%s: error reading version record: %s\n"), db_name, gpg_strerror (rc) ); if( !vr.r.ver.trusthashtbl ) create_hashtable( &vr, 0 ); trusthashtbl = vr.r.ver.trusthashtbl; } return trusthashtbl; } /**************** * Update a hashtable. * table gives the start of the table, key and keylen is the key, * newrecnum is the record number to insert. */ static int upd_hashtable( ulong table, byte *key, int keylen, ulong newrecnum ) { TRUSTREC lastrec, rec; ulong hashrec, item; int msb; int level=0; int rc, i; hashrec = table; next_level: msb = key[level]; hashrec += msb / ITEMS_PER_HTBL_RECORD; rc = tdbio_read_record( hashrec, &rec, RECTYPE_HTBL ); if( rc ) { log_error("upd_hashtable: read failed: %s\n", gpg_strerror (rc) ); return rc; } item = rec.r.htbl.item[msb % ITEMS_PER_HTBL_RECORD]; if( !item ) { /* insert a new item into the hash table */ rec.r.htbl.item[msb % ITEMS_PER_HTBL_RECORD] = newrecnum; rc = tdbio_write_record( &rec ); if( rc ) { log_error("upd_hashtable: write htbl failed: %s\n", gpg_strerror (rc) ); return rc; } } else if( item != newrecnum ) { /* must do an update */ lastrec = rec; rc = tdbio_read_record( item, &rec, 0 ); if( rc ) { log_error( "upd_hashtable: read item failed: %s\n", gpg_strerror (rc) ); return rc; } if( rec.rectype == RECTYPE_HTBL ) { hashrec = item; level++; if( level >= keylen ) { log_error( "hashtable has invalid indirections.\n"); return GPG_ERR_TRUSTDB; } goto next_level; } else if( rec.rectype == RECTYPE_HLST ) { /* extend list */ /* see whether the key is already in this list */ for(;;) { for(i=0; i < ITEMS_PER_HLST_RECORD; i++ ) { if( rec.r.hlst.rnum[i] == newrecnum ) { return 0; /* okay, already in the list */ } } if( rec.r.hlst.next ) { rc = tdbio_read_record( rec.r.hlst.next, &rec, RECTYPE_HLST); if( rc ) { log_error ("upd_hashtable: read hlst failed: %s\n", gpg_strerror (rc) ); return rc; } } else break; /* not there */ } /* find the next free entry and put it in */ for(;;) { for(i=0; i < ITEMS_PER_HLST_RECORD; i++ ) { if( !rec.r.hlst.rnum[i] ) { rec.r.hlst.rnum[i] = newrecnum; rc = tdbio_write_record( &rec ); if( rc ) log_error ("upd_hashtable: write hlst failed: %s\n", gpg_strerror (rc)); return rc; /* done */ } } if( rec.r.hlst.next ) { rc = tdbio_read_record( rec.r.hlst.next, &rec, RECTYPE_HLST ); if( rc ) { log_error ("upd_hashtable: read hlst failed: %s\n", gpg_strerror (rc)); return rc; } } else { /* add a new list record */ rec.r.hlst.next = item = tdbio_new_recnum(); rc = tdbio_write_record( &rec ); if( rc ) { log_error( "upd_hashtable: write hlst failed: %s\n", gpg_strerror (rc) ); return rc; } memset( &rec, 0, sizeof rec ); rec.rectype = RECTYPE_HLST; rec.recnum = item; rec.r.hlst.rnum[0] = newrecnum; rc = tdbio_write_record( &rec ); if( rc ) log_error( "upd_hashtable: write ext hlst failed: %s\n", gpg_strerror (rc) ); return rc; /* done */ } } /* end loop over hlst slots */ } else if( rec.rectype == RECTYPE_TRUST ) { /* insert a list record */ if( rec.recnum == newrecnum ) { return 0; } item = rec.recnum; /* save number of key record */ memset( &rec, 0, sizeof rec ); rec.rectype = RECTYPE_HLST; rec.recnum = tdbio_new_recnum(); rec.r.hlst.rnum[0] = item; /* old keyrecord */ rec.r.hlst.rnum[1] = newrecnum; /* and new one */ rc = tdbio_write_record( &rec ); if( rc ) { log_error( "upd_hashtable: write new hlst failed: %s\n", gpg_strerror (rc) ); return rc; } /* update the hashtable record */ lastrec.r.htbl.item[msb % ITEMS_PER_HTBL_RECORD] = rec.recnum; rc = tdbio_write_record( &lastrec ); if( rc ) log_error ("upd_hashtable: update htbl failed: %s\n", gpg_strerror (rc)); return rc; /* ready */ } else { log_error( "hashtbl %lu: %lu/%d points to an invalid record %lu\n", table, hashrec, (msb % ITEMS_PER_HTBL_RECORD), item); list_trustdb(NULL); return GPG_ERR_TRUSTDB; } } return 0; } /**************** * Drop an entry from a hashtable * table gives the start of the table, key and keylen is the key, */ static int drop_from_hashtable( ulong table, byte *key, int keylen, ulong recnum ) { TRUSTREC rec; ulong hashrec, item; int msb; int level=0; int rc, i; hashrec = table; next_level: msb = key[level]; hashrec += msb / ITEMS_PER_HTBL_RECORD; rc = tdbio_read_record( hashrec, &rec, RECTYPE_HTBL ); if( rc ) { log_error("drop_from_hashtable: read failed: %s\n", gpg_strerror (rc) ); return rc; } item = rec.r.htbl.item[msb % ITEMS_PER_HTBL_RECORD]; if( !item ) /* not found - forget about it */ return 0; if( item == recnum ) { /* tables points direct to the record */ rec.r.htbl.item[msb % ITEMS_PER_HTBL_RECORD] = 0; rc = tdbio_write_record( &rec ); if( rc ) log_error("drop_from_hashtable: write htbl failed: %s\n", gpg_strerror (rc) ); return rc; } rc = tdbio_read_record( item, &rec, 0 ); if( rc ) { log_error( "drop_from_hashtable: read item failed: %s\n", gpg_strerror (rc) ); return rc; } if( rec.rectype == RECTYPE_HTBL ) { hashrec = item; level++; if( level >= keylen ) { log_error( "hashtable has invalid indirections.\n"); return GPG_ERR_TRUSTDB; } goto next_level; } if( rec.rectype == RECTYPE_HLST ) { for(;;) { for(i=0; i < ITEMS_PER_HLST_RECORD; i++ ) { if( rec.r.hlst.rnum[i] == recnum ) { rec.r.hlst.rnum[i] = 0; /* drop */ rc = tdbio_write_record( &rec ); if( rc ) log_error("drop_from_hashtable: write htbl failed: %s\n", gpg_strerror (rc)); return rc; } } if( rec.r.hlst.next ) { rc = tdbio_read_record( rec.r.hlst.next, &rec, RECTYPE_HLST); if( rc ) { log_error( "drop_from_hashtable: read hlst failed: %s\n", gpg_strerror (rc) ); return rc; } } else return 0; /* key not in table */ } } log_error( "hashtbl %lu: %lu/%d points to wrong record %lu\n", table, hashrec, (msb % ITEMS_PER_HTBL_RECORD), item); return GPG_ERR_TRUSTDB; } /**************** * Lookup a record via the hashtable tablewith key/keylen and return the * result in rec. cmp() should return if the record is the desired one. * Returns -1 if not found, 0 if found or another errocode */ static int lookup_hashtable( ulong table, const byte *key, size_t keylen, int (*cmpfnc)(const void*, const TRUSTREC *), const void *cmpdata, TRUSTREC *rec ) { int rc; ulong hashrec, item; int msb; int level=0; hashrec = table; next_level: msb = key[level]; hashrec += msb / ITEMS_PER_HTBL_RECORD; rc = tdbio_read_record( hashrec, rec, RECTYPE_HTBL ); if( rc ) { log_error("lookup_hashtable failed: %s\n", gpg_strerror (rc) ); return rc; } item = rec->r.htbl.item[msb % ITEMS_PER_HTBL_RECORD]; if( !item ) return -1; /* not found */ rc = tdbio_read_record( item, rec, 0 ); if( rc ) { log_error( "hashtable read failed: %s\n", gpg_strerror (rc) ); return rc; } if( rec->rectype == RECTYPE_HTBL ) { hashrec = item; level++; if( level >= keylen ) { log_error("hashtable has invalid indirections\n"); return GPG_ERR_TRUSTDB; } goto next_level; } else if( rec->rectype == RECTYPE_HLST ) { for(;;) { int i; for(i=0; i < ITEMS_PER_HLST_RECORD; i++ ) { if( rec->r.hlst.rnum[i] ) { TRUSTREC tmp; rc = tdbio_read_record( rec->r.hlst.rnum[i], &tmp, 0 ); if( rc ) { log_error ("lookup_hashtable: read item failed: %s\n", gpg_strerror (rc)); return rc; } if( (*cmpfnc)( cmpdata, &tmp ) ) { *rec = tmp; return 0; } } } if( rec->r.hlst.next ) { rc = tdbio_read_record( rec->r.hlst.next, rec, RECTYPE_HLST ); if( rc ) { log_error ("lookup_hashtable: read hlst failed: %s\n", gpg_strerror (rc) ); return rc; } } else return -1; /* not found */ } } if( (*cmpfnc)( cmpdata, rec ) ) return 0; /* really found */ return -1; /* no: not found */ } /**************** * Update the trust hashtbl or create the table if it does not exist */ static int update_trusthashtbl( TRUSTREC *tr ) { return upd_hashtable( get_trusthashrec(), tr->r.trust.fingerprint, 20, tr->recnum ); } void tdbio_dump_record( TRUSTREC *rec, FILE *fp ) { int i; ulong rnum = rec->recnum; fprintf(fp, "rec %5lu, ", rnum ); switch( rec->rectype ) { case 0: fprintf(fp, "blank\n"); break; case RECTYPE_VER: fprintf(fp, "version, td=%lu, f=%lu, m/c/d=%d/%d/%d tm=%d mcl=%d nc=%lu (%s)\n", rec->r.ver.trusthashtbl, rec->r.ver.firstfree, rec->r.ver.marginals, rec->r.ver.completes, rec->r.ver.cert_depth, rec->r.ver.trust_model, rec->r.ver.min_cert_level, rec->r.ver.nextcheck, strtimestamp(rec->r.ver.nextcheck) ); break; case RECTYPE_FREE: fprintf(fp, "free, next=%lu\n", rec->r.free.next ); break; case RECTYPE_HTBL: fprintf(fp, "htbl,"); for(i=0; i < ITEMS_PER_HTBL_RECORD; i++ ) fprintf(fp, " %lu", rec->r.htbl.item[i] ); putc('\n', fp); break; case RECTYPE_HLST: fprintf(fp, "hlst, next=%lu,", rec->r.hlst.next ); for(i=0; i < ITEMS_PER_HLST_RECORD; i++ ) fprintf(fp, " %lu", rec->r.hlst.rnum[i] ); putc('\n', fp); break; case RECTYPE_TRUST: fprintf(fp, "trust "); for(i=0; i < 20; i++ ) fprintf(fp, "%02X", rec->r.trust.fingerprint[i] ); fprintf (fp, ", ot=%d, d=%d, vl=%lu\n", rec->r.trust.ownertrust, rec->r.trust.depth, rec->r.trust.validlist); break; case RECTYPE_VALID: fprintf(fp, "valid "); for(i=0; i < 20; i++ ) fprintf(fp, "%02X", rec->r.valid.namehash[i] ); fprintf (fp, ", v=%d, next=%lu\n", rec->r.valid.validity, rec->r.valid.next); break; default: fprintf(fp, "unknown type %d\n", rec->rectype ); break; } } /**************** * read the record with number recnum * returns: -1 on error, 0 on success */ int tdbio_read_record( ulong recnum, TRUSTREC *rec, int expected ) { byte readbuf[TRUST_RECORD_LEN]; const byte *buf, *p; gpg_error_t err = 0; int n, i; if( db_fd == -1 ) open_db(); buf = get_record_from_cache( recnum ); if( !buf ) { if( lseek( db_fd, recnum * TRUST_RECORD_LEN, SEEK_SET ) == -1 ) { err = gpg_error_from_syserror (); log_error(_("trustdb: lseek failed: %s\n"), strerror(errno) ); return err; } n = read( db_fd, readbuf, TRUST_RECORD_LEN); if( !n ) { return -1; /* eof */ } else if( n != TRUST_RECORD_LEN ) { err = gpg_error_from_syserror (); log_error(_("trustdb: read failed (n=%d): %s\n"), n, strerror(errno) ); return err; } buf = readbuf; } rec->recnum = recnum; rec->dirty = 0; p = buf; rec->rectype = *p++; if( expected && rec->rectype != expected ) { log_error("%lu: read expected rec type %d, got %d\n", recnum, expected, rec->rectype ); return gpg_error (GPG_ERR_TRUSTDB); } p++; /* skip reserved byte */ switch( rec->rectype ) { case 0: /* unused (free) record */ break; case RECTYPE_VER: /* version record */ if( memcmp(buf+1, GPGEXT_GPG, 3 ) ) { log_error( _("%s: not a trustdb file\n"), db_name ); err = gpg_error (GPG_ERR_TRUSTDB); } p += 2; /* skip "gpg" */ rec->r.ver.version = *p++; rec->r.ver.marginals = *p++; rec->r.ver.completes = *p++; rec->r.ver.cert_depth = *p++; rec->r.ver.trust_model = *p++; rec->r.ver.min_cert_level = *p++; p += 2; - rec->r.ver.created = buftoulong(p); p += 4; - rec->r.ver.nextcheck = buftoulong(p); p += 4; + rec->r.ver.created = buf32_to_ulong(p); p += 4; + rec->r.ver.nextcheck = buf32_to_ulong(p); p += 4; p += 4; p += 4; - rec->r.ver.firstfree =buftoulong(p); p += 4; + rec->r.ver.firstfree =buf32_to_ulong(p); p += 4; p += 4; - rec->r.ver.trusthashtbl =buftoulong(p); p += 4; + rec->r.ver.trusthashtbl =buf32_to_ulong(p); p += 4; if( recnum ) { log_error( _("%s: version record with recnum %lu\n"), db_name, (ulong)recnum ); err = gpg_error (GPG_ERR_TRUSTDB); } else if( rec->r.ver.version != 3 ) { log_error( _("%s: invalid file version %d\n"), db_name, rec->r.ver.version ); err = gpg_error (GPG_ERR_TRUSTDB); } break; case RECTYPE_FREE: - rec->r.free.next = buftoulong(p); p += 4; + rec->r.free.next = buf32_to_ulong(p); p += 4; break; case RECTYPE_HTBL: for(i=0; i < ITEMS_PER_HTBL_RECORD; i++ ) { - rec->r.htbl.item[i] = buftoulong(p); p += 4; + rec->r.htbl.item[i] = buf32_to_ulong(p); p += 4; } break; case RECTYPE_HLST: - rec->r.hlst.next = buftoulong(p); p += 4; + rec->r.hlst.next = buf32_to_ulong(p); p += 4; for(i=0; i < ITEMS_PER_HLST_RECORD; i++ ) { - rec->r.hlst.rnum[i] = buftoulong(p); p += 4; + rec->r.hlst.rnum[i] = buf32_to_ulong(p); p += 4; } break; case RECTYPE_TRUST: memcpy( rec->r.trust.fingerprint, p, 20); p+=20; rec->r.trust.ownertrust = *p++; rec->r.trust.depth = *p++; rec->r.trust.min_ownertrust = *p++; p++; - rec->r.trust.validlist = buftoulong(p); p += 4; + rec->r.trust.validlist = buf32_to_ulong(p); p += 4; break; case RECTYPE_VALID: memcpy( rec->r.valid.namehash, p, 20); p+=20; rec->r.valid.validity = *p++; - rec->r.valid.next = buftoulong(p); p += 4; + rec->r.valid.next = buf32_to_ulong(p); p += 4; rec->r.valid.full_count = *p++; rec->r.valid.marginal_count = *p++; break; default: log_error( "%s: invalid record type %d at recnum %lu\n", db_name, rec->rectype, (ulong)recnum ); err = gpg_error (GPG_ERR_TRUSTDB); break; } return err; } /**************** * Write the record at RECNUM */ int tdbio_write_record( TRUSTREC *rec ) { byte buf[TRUST_RECORD_LEN], *p; int rc = 0; int i; ulong recnum = rec->recnum; if( db_fd == -1 ) open_db(); memset(buf, 0, TRUST_RECORD_LEN); p = buf; *p++ = rec->rectype; p++; switch( rec->rectype ) { case 0: /* unused record */ break; case RECTYPE_VER: /* version record */ if( recnum ) BUG(); memcpy(p-1, GPGEXT_GPG, 3 ); p += 2; *p++ = rec->r.ver.version; *p++ = rec->r.ver.marginals; *p++ = rec->r.ver.completes; *p++ = rec->r.ver.cert_depth; *p++ = rec->r.ver.trust_model; *p++ = rec->r.ver.min_cert_level; p += 2; ulongtobuf(p, rec->r.ver.created); p += 4; ulongtobuf(p, rec->r.ver.nextcheck); p += 4; p += 4; p += 4; ulongtobuf(p, rec->r.ver.firstfree ); p += 4; p += 4; ulongtobuf(p, rec->r.ver.trusthashtbl ); p += 4; break; case RECTYPE_FREE: ulongtobuf(p, rec->r.free.next); p += 4; break; case RECTYPE_HTBL: for(i=0; i < ITEMS_PER_HTBL_RECORD; i++ ) { ulongtobuf( p, rec->r.htbl.item[i]); p += 4; } break; case RECTYPE_HLST: ulongtobuf( p, rec->r.hlst.next); p += 4; for(i=0; i < ITEMS_PER_HLST_RECORD; i++ ) { ulongtobuf( p, rec->r.hlst.rnum[i]); p += 4; } break; case RECTYPE_TRUST: memcpy( p, rec->r.trust.fingerprint, 20); p += 20; *p++ = rec->r.trust.ownertrust; *p++ = rec->r.trust.depth; *p++ = rec->r.trust.min_ownertrust; p++; ulongtobuf( p, rec->r.trust.validlist); p += 4; break; case RECTYPE_VALID: memcpy( p, rec->r.valid.namehash, 20); p += 20; *p++ = rec->r.valid.validity; ulongtobuf( p, rec->r.valid.next); p += 4; *p++ = rec->r.valid.full_count; *p++ = rec->r.valid.marginal_count; break; default: BUG(); } rc = put_record_into_cache( recnum, buf ); if( rc ) ; else if( rec->rectype == RECTYPE_TRUST ) rc = update_trusthashtbl( rec ); return rc; } int tdbio_delete_record( ulong recnum ) { TRUSTREC vr, rec; int rc; /* Must read the record fist, so we can drop it from the hash tables */ rc = tdbio_read_record( recnum, &rec, 0 ); if( rc ) ; else if( rec.rectype == RECTYPE_TRUST ) { rc = drop_from_hashtable( get_trusthashrec(), rec.r.trust.fingerprint, 20, rec.recnum ); } if( rc ) return rc; /* now we can chnage it to a free record */ rc = tdbio_read_record( 0, &vr, RECTYPE_VER ); if( rc ) log_fatal( _("%s: error reading version record: %s\n"), db_name, gpg_strerror (rc) ); rec.recnum = recnum; rec.rectype = RECTYPE_FREE; rec.r.free.next = vr.r.ver.firstfree; vr.r.ver.firstfree = recnum; rc = tdbio_write_record( &rec ); if( !rc ) rc = tdbio_write_record( &vr ); return rc; } /**************** * create a new record and return its record number */ ulong tdbio_new_recnum() { off_t offset; ulong recnum; TRUSTREC vr, rec; int rc; /* look for unused records */ rc = tdbio_read_record( 0, &vr, RECTYPE_VER ); if( rc ) log_fatal( _("%s: error reading version record: %s\n"), db_name, gpg_strerror (rc) ); if( vr.r.ver.firstfree ) { recnum = vr.r.ver.firstfree; rc = tdbio_read_record( recnum, &rec, RECTYPE_FREE ); if( rc ) { log_error( _("%s: error reading free record: %s\n"), db_name, gpg_strerror (rc) ); return rc; } /* update dir record */ vr.r.ver.firstfree = rec.r.free.next; rc = tdbio_write_record( &vr ); if( rc ) { log_error (_("%s: error writing dir record: %s\n"), db_name, gpg_strerror (rc)); return rc; } /*zero out the new record */ memset( &rec, 0, sizeof rec ); rec.rectype = 0; /* unused record */ rec.recnum = recnum; rc = tdbio_write_record( &rec ); if( rc ) log_fatal(_("%s: failed to zero a record: %s\n"), db_name, gpg_strerror (rc)); } else { /* not found, append a new record */ offset = lseek( db_fd, 0, SEEK_END ); if( offset == -1 ) log_fatal("trustdb: lseek to end failed: %s\n", strerror(errno) ); recnum = offset / TRUST_RECORD_LEN; assert(recnum); /* this is will never be the first record */ /* we must write a record, so that the next call to this function * returns another recnum */ memset( &rec, 0, sizeof rec ); rec.rectype = 0; /* unused record */ rec.recnum = recnum; rc = 0; if( lseek( db_fd, recnum * TRUST_RECORD_LEN, SEEK_SET ) == -1 ) { rc = gpg_error_from_syserror (); log_error(_("trustdb rec %lu: lseek failed: %s\n"), recnum, strerror(errno) ); } else { int n = write( db_fd, &rec, TRUST_RECORD_LEN); if( n != TRUST_RECORD_LEN ) { rc = gpg_error_from_syserror (); log_error(_("trustdb rec %lu: write failed (n=%d): %s\n"), recnum, n, strerror(errno) ); } } if( rc ) log_fatal(_("%s: failed to append a record: %s\n"), db_name, gpg_strerror (rc)); } return recnum ; } static int cmp_trec_fpr ( const void *fpr, const TRUSTREC *rec ) { return (rec->rectype == RECTYPE_TRUST && !memcmp (rec->r.trust.fingerprint, fpr, 20)); } int tdbio_search_trust_byfpr( const byte *fingerprint, TRUSTREC *rec ) { int rc; /* locate the trust record using the hash table */ rc = lookup_hashtable( get_trusthashrec(), fingerprint, 20, cmp_trec_fpr, fingerprint, rec ); return rc; } int tdbio_search_trust_bypk (PKT_public_key *pk, TRUSTREC *rec) { byte fingerprint[MAX_FINGERPRINT_LEN]; size_t fingerlen; fingerprint_from_pk( pk, fingerprint, &fingerlen ); for (; fingerlen < 20; fingerlen++ ) fingerprint[fingerlen] = 0; return tdbio_search_trust_byfpr (fingerprint, rec); } void tdbio_invalid(void) { log_error (_("Error: The trustdb is corrupted.\n")); how_to_fix_the_trustdb (); g10_exit (2); } diff --git a/g10/trust.c b/g10/trust.c index 796694d59..316fe2fe1 100644 --- a/g10/trust.c +++ b/g10/trust.c @@ -1,740 +1,741 @@ /* trust.c - High level trust functions * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, * 2008, 2012 Free Software Foundation, Inc. * Copyright (C) 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 "gpg.h" #include "keydb.h" #include "util.h" #include "options.h" #include "packet.h" #include "main.h" #include "i18n.h" #include "trustdb.h" +#include "host2net.h" /* Return true if key is disabled. Note that this is usually used via the pk_is_disabled macro. */ int cache_disabled_value (PKT_public_key *pk) { #ifdef NO_TRUST_MODELS (void)pk; return 0; #else return tdb_cache_disabled_value (pk); #endif } void register_trusted_keyid (u32 *keyid) { #ifdef NO_TRUST_MODELS (void)keyid; #else tdb_register_trusted_keyid (keyid); #endif } void register_trusted_key (const char *string) { #ifdef NO_TRUST_MODELS (void)string; #else tdb_register_trusted_key (string); #endif } /* * This function returns a letter for a trust value. Trust flags * are ignored. */ static int trust_letter (unsigned int value) { switch( (value & TRUST_MASK) ) { case TRUST_UNKNOWN: return '-'; case TRUST_EXPIRED: return 'e'; case TRUST_UNDEFINED: return 'q'; case TRUST_NEVER: return 'n'; case TRUST_MARGINAL: return 'm'; case TRUST_FULLY: return 'f'; case TRUST_ULTIMATE: return 'u'; default: return '?'; } } /* The strings here are similar to those in pkclist.c:do_edit_ownertrust() */ const char * trust_value_to_string (unsigned int value) { switch ((value & TRUST_MASK)) { case TRUST_UNKNOWN: return _("unknown"); case TRUST_EXPIRED: return _("expired"); case TRUST_UNDEFINED: return _("undefined"); case TRUST_NEVER: return _("never"); case TRUST_MARGINAL: return _("marginal"); case TRUST_FULLY: return _("full"); case TRUST_ULTIMATE: return _("ultimate"); default: return "err"; } } int string_to_trust_value (const char *str) { if (!ascii_strcasecmp (str, "undefined")) return TRUST_UNDEFINED; else if (!ascii_strcasecmp (str, "never")) return TRUST_NEVER; else if (!ascii_strcasecmp (str, "marginal")) return TRUST_MARGINAL; else if (!ascii_strcasecmp (str, "full")) return TRUST_FULLY; else if (!ascii_strcasecmp(str, "ultimate")) return TRUST_ULTIMATE; else return -1; } const char * uid_trust_string_fixed (PKT_public_key *key, PKT_user_id *uid) { if (!key && !uid) { /* TRANSLATORS: these strings are similar to those in trust_value_to_string(), but are a fixed length. This is needed to make attractive information listings where columns line up properly. The value "10" should be the length of the strings you choose to translate to. This is the length in printable columns. It gets passed to atoi() so everything after the number is essentially a comment and need not be translated. Either key and uid are both NULL, or neither are NULL. */ return _("10 translator see trust.c:uid_trust_string_fixed"); } else if(uid->is_revoked || (key && key->flags.revoked)) return _("[ revoked]"); else if(uid->is_expired) return _("[ expired]"); else if(key) { switch (get_validity(key,uid)&TRUST_MASK) { case TRUST_UNKNOWN: return _("[ unknown]"); case TRUST_EXPIRED: return _("[ expired]"); case TRUST_UNDEFINED: return _("[ undef ]"); case TRUST_MARGINAL: return _("[marginal]"); case TRUST_FULLY: return _("[ full ]"); case TRUST_ULTIMATE: return _("[ultimate]"); } } return "err"; } /* * Return the assigned ownertrust value for the given public key. * The key should be the primary key. */ unsigned int get_ownertrust (PKT_public_key *pk) { #ifdef NO_TRUST_MODELS (void)pk; return TRUST_UNKNOWN; #else return tdb_get_ownertrust (pk); #endif } /* * Same as get_ownertrust but this takes the minimum ownertrust value * into into account, and will bump up the value as needed. */ static int get_ownertrust_with_min (PKT_public_key *pk) { #ifdef NO_TRUST_MODELS (void)pk; return TRUST_UNKNOWN; #else unsigned int otrust, otrust_min; otrust = (tdb_get_ownertrust (pk) & TRUST_MASK); otrust_min = tdb_get_min_ownertrust (pk); if (otrust < otrust_min) { /* If the trust that the user has set is less than the trust that was calculated from a trust signature chain, use the higher of the two. We do this here and not in get_ownertrust since the underlying ownertrust should not really be set - just the appearance of the ownertrust. */ otrust = otrust_min; } return otrust; #endif } /* * Same as get_ownertrust but return a trust letter instead of an * value. This takes the minimum ownertrust value into account. */ int get_ownertrust_info (PKT_public_key *pk) { return trust_letter (get_ownertrust_with_min (pk)); } /* * Same as get_ownertrust but return a trust string instead of an * value. This takes the minimum ownertrust value into account. */ const char * get_ownertrust_string (PKT_public_key *pk) { return trust_value_to_string (get_ownertrust_with_min (pk)); } /* * Set the trust value of the given public key to the new value. * The key should be a primary one. */ void update_ownertrust (PKT_public_key *pk, unsigned int new_trust) { #ifdef NO_TRUST_MODELS (void)pk; (void)new_trust; #else tdb_update_ownertrust (pk, new_trust); #endif } int clear_ownertrusts (PKT_public_key *pk) { #ifdef NO_TRUST_MODELS (void)pk; return 0; #else return tdb_clear_ownertrusts (pk); #endif } void revalidation_mark (void) { #ifndef NO_TRUST_MODELS tdb_revalidation_mark (); #endif } void check_trustdb_stale (void) { #ifndef NO_TRUST_MODELS tdb_check_trustdb_stale (); #endif } void check_or_update_trustdb (void) { #ifndef NO_TRUST_MODELS tdb_check_or_update (); #endif } /* * Return the validity information for PK. If the namehash is not * NULL, the validity of the corresponsing user ID is returned, * otherwise, a reasonable value for the entire key is returned. */ unsigned int get_validity (PKT_public_key *pk, PKT_user_id *uid) { int rc; unsigned int validity; u32 kid[2]; PKT_public_key *main_pk; if (uid) namehash_from_uid (uid); keyid_from_pk (pk, kid); if (pk->main_keyid[0] != kid[0] || pk->main_keyid[1] != kid[1]) { /* This is a subkey - get the mainkey. */ main_pk = xmalloc_clear (sizeof *main_pk); rc = get_pubkey (main_pk, pk->main_keyid); if (rc) { char *tempkeystr = xstrdup (keystr (pk->main_keyid)); log_error ("error getting main key %s of subkey %s: %s\n", tempkeystr, keystr (kid), gpg_strerror (rc)); xfree (tempkeystr); validity = TRUST_UNKNOWN; goto leave; } } else main_pk = pk; #ifdef NO_TRUST_MODELS validity = TRUST_UNKNOWN; #else validity = tdb_get_validity_core (pk, uid, main_pk); #endif leave: /* Set some flags direct from the key */ if (main_pk->flags.revoked) validity |= TRUST_FLAG_REVOKED; if (main_pk != pk && pk->flags.revoked) validity |= TRUST_FLAG_SUB_REVOKED; /* Note: expiration is a trust value and not a flag - don't know why * I initially designed it that way. */ if (main_pk->has_expired || pk->has_expired) validity = ((validity & (~TRUST_MASK | TRUST_FLAG_PENDING_CHECK)) | TRUST_EXPIRED); if (main_pk != pk) free_public_key (main_pk); return validity; } int get_validity_info (PKT_public_key *pk, PKT_user_id *uid) { int trustlevel; if (!pk) return '?'; /* Just in case a NULL PK is passed. */ trustlevel = get_validity (pk, uid); if ((trustlevel & TRUST_FLAG_REVOKED)) return 'r'; return trust_letter (trustlevel); } const char * get_validity_string (PKT_public_key *pk, PKT_user_id *uid) { int trustlevel; if (!pk) return "err"; /* Just in case a NULL PK is passed. */ trustlevel = get_validity (pk, uid); if ((trustlevel & TRUST_FLAG_REVOKED)) return _("revoked"); return trust_value_to_string (trustlevel); } /* * Mark the signature of the given UID which are used to certify it. * To do this, we first revmove all signatures which are not valid and * from the remain ones we look for the latest one. If this is not a * certification revocation signature we mark the signature by setting * node flag bit 8. Revocations are marked with flag 11, and sigs * from unavailable keys are marked with flag 12. Note that flag bits * 9 and 10 are used for internal purposes. */ void mark_usable_uid_certs (kbnode_t keyblock, kbnode_t uidnode, u32 *main_kid, struct key_item *klist, u32 curtime, u32 *next_expire) { kbnode_t node; PKT_signature *sig; /* First check all signatures. */ for (node=uidnode->next; node; node = node->next) { int rc; node->flag &= ~(1<<8 | 1<<9 | 1<<10 | 1<<11 | 1<<12); if (node->pkt->pkttype == PKT_USER_ID || node->pkt->pkttype == PKT_PUBLIC_SUBKEY) break; /* ready */ if (node->pkt->pkttype != PKT_SIGNATURE) continue; sig = node->pkt->pkt.signature; if (main_kid && sig->keyid[0] == main_kid[0] && sig->keyid[1] == main_kid[1]) continue; /* ignore self-signatures if we pass in a main_kid */ if (!IS_UID_SIG(sig) && !IS_UID_REV(sig)) continue; /* we only look at these signature classes */ if(sig->sig_class>=0x11 && sig->sig_class<=0x13 && sig->sig_class-0x10flag |= 1<<12; continue; } node->flag |= 1<<9; } /* Reset the remaining flags. */ for (; node; node = node->next) node->flag &= ~(1<<8 | 1<<9 | 1<<10 | 1<<11 | 1<<12); /* kbnode flag usage: bit 9 is here set for signatures to consider, * bit 10 will be set by the loop to keep track of keyIDs already * processed, bit 8 will be set for the usable signatures, and bit * 11 will be set for usable revocations. */ /* For each cert figure out the latest valid one. */ for (node=uidnode->next; node; node = node->next) { KBNODE n, signode; u32 kid[2]; u32 sigdate; if (node->pkt->pkttype == PKT_PUBLIC_SUBKEY) break; if ( !(node->flag & (1<<9)) ) continue; /* not a node to look at */ if ( (node->flag & (1<<10)) ) continue; /* signature with a keyID already processed */ node->flag |= (1<<10); /* mark this node as processed */ sig = node->pkt->pkt.signature; signode = node; sigdate = sig->timestamp; kid[0] = sig->keyid[0]; kid[1] = sig->keyid[1]; /* Now find the latest and greatest signature */ for (n=uidnode->next; n; n = n->next) { if (n->pkt->pkttype == PKT_PUBLIC_SUBKEY) break; if ( !(n->flag & (1<<9)) ) continue; if ( (n->flag & (1<<10)) ) continue; /* shortcut already processed signatures */ sig = n->pkt->pkt.signature; if (kid[0] != sig->keyid[0] || kid[1] != sig->keyid[1]) continue; n->flag |= (1<<10); /* mark this node as processed */ /* If signode is nonrevocable and unexpired and n isn't, then take signode (skip). It doesn't matter which is older: if signode was older then we don't want to take n as signode is nonrevocable. If n was older then we're automatically fine. */ if(((IS_UID_SIG(signode->pkt->pkt.signature) && !signode->pkt->pkt.signature->flags.revocable && (signode->pkt->pkt.signature->expiredate==0 || signode->pkt->pkt.signature->expiredate>curtime))) && (!(IS_UID_SIG(n->pkt->pkt.signature) && !n->pkt->pkt.signature->flags.revocable && (n->pkt->pkt.signature->expiredate==0 || n->pkt->pkt.signature->expiredate>curtime)))) continue; /* If n is nonrevocable and unexpired and signode isn't, then take n. Again, it doesn't matter which is older: if n was older then we don't want to take signode as n is nonrevocable. If signode was older then we're automatically fine. */ if((!(IS_UID_SIG(signode->pkt->pkt.signature) && !signode->pkt->pkt.signature->flags.revocable && (signode->pkt->pkt.signature->expiredate==0 || signode->pkt->pkt.signature->expiredate>curtime))) && ((IS_UID_SIG(n->pkt->pkt.signature) && !n->pkt->pkt.signature->flags.revocable && (n->pkt->pkt.signature->expiredate==0 || n->pkt->pkt.signature->expiredate>curtime)))) { signode = n; sigdate = sig->timestamp; continue; } /* At this point, if it's newer, it goes in as the only remaining possibilities are signode and n are both either revocable or expired or both nonrevocable and unexpired. If the timestamps are equal take the later ordered packet, presuming that the key packets are hopefully in their original order. */ if (sig->timestamp >= sigdate) { signode = n; sigdate = sig->timestamp; } } sig = signode->pkt->pkt.signature; if (IS_UID_SIG (sig)) { /* this seems to be a usable one which is not revoked. * Just need to check whether there is an expiration time, * We do the expired certification after finding a suitable * certification, the assumption is that a signator does not * want that after the expiration of his certificate the * system falls back to an older certification which has a * different expiration time */ const byte *p; u32 expire; p = parse_sig_subpkt (sig->hashed, SIGSUBPKT_SIG_EXPIRE, NULL ); - expire = p? sig->timestamp + buffer_to_u32(p) : 0; + expire = p? sig->timestamp + buf32_to_u32(p) : 0; if (expire==0 || expire > curtime ) { signode->flag |= (1<<8); /* yeah, found a good cert */ if (next_expire && expire && expire < *next_expire) *next_expire = expire; } } else signode->flag |= (1<<11); } } static int clean_sigs_from_uid (kbnode_t keyblock, kbnode_t uidnode, int noisy, int self_only) { int deleted = 0; kbnode_t node; u32 keyid[2]; assert (keyblock->pkt->pkttype==PKT_PUBLIC_KEY); keyid_from_pk (keyblock->pkt->pkt.public_key, keyid); /* Passing in a 0 for current time here means that we'll never weed out an expired sig. This is correct behavior since we want to keep the most recent expired sig in a series. */ mark_usable_uid_certs (keyblock, uidnode, NULL, NULL, 0, NULL); /* What we want to do here is remove signatures that are not considered as part of the trust calculations. Thus, all invalid signatures are out, as are any signatures that aren't the last of a series of uid sigs or revocations It breaks down like this: coming out of mark_usable_uid_certs, if a sig is unflagged, it is not even a candidate. If a sig has flag 9 or 10, that means it was selected as a candidate and vetted. If a sig has flag 8 it is a usable signature. If a sig has flag 11 it is a usable revocation. If a sig has flag 12 it was issued by an unavailable key. "Usable" here means the most recent valid signature/revocation in a series from a particular signer. Delete everything that isn't a usable uid sig (which might be expired), a usable revocation, or a sig from an unavailable key. */ for (node=uidnode->next; node && node->pkt->pkttype==PKT_SIGNATURE; node=node->next) { int keep; keep = self_only? (node->pkt->pkt.signature->keyid[0] == keyid[0] && node->pkt->pkt.signature->keyid[1] == keyid[1]) : 1; /* Keep usable uid sigs ... */ if ((node->flag & (1<<8)) && keep) continue; /* ... and usable revocations... */ if ((node->flag & (1<<11)) && keep) continue; /* ... and sigs from unavailable keys. */ /* disabled for now since more people seem to want sigs from unavailable keys removed altogether. */ /* if(node->flag & (1<<12)) continue; */ /* Everything else we delete */ /* At this point, if 12 is set, the signing key was unavailable. If 9 or 10 is set, it's superseded. Otherwise, it's invalid. */ if (noisy) log_info ("removing signature from key %s on user ID \"%s\": %s\n", keystr (node->pkt->pkt.signature->keyid), uidnode->pkt->pkt.user_id->name, node->flag&(1<<12)? "key unavailable": node->flag&(1<<9)? "signature superseded" /* */ :"invalid signature" ); delete_kbnode (node); deleted++; } return deleted; } /* This is substantially easier than clean_sigs_from_uid since we just have to establish if the uid has a valid self-sig, is not revoked, and is not expired. Note that this does not take into account whether the uid has a trust path to it - just whether the keyholder themselves has certified the uid. Returns true if the uid was compacted. To "compact" a user ID, we simply remove ALL signatures except the self-sig that caused the user ID to be remove-worthy. We don't actually remove the user ID packet itself since it might be ressurected in a later merge. Note that this function requires that the caller has already done a merge_keys_and_selfsig(). TODO: change the import code to allow importing a uid with only a revocation if the uid already exists on the keyring. */ static int clean_uid_from_key (kbnode_t keyblock, kbnode_t uidnode, int noisy) { kbnode_t node; PKT_user_id *uid = uidnode->pkt->pkt.user_id; int deleted = 0; assert (keyblock->pkt->pkttype==PKT_PUBLIC_KEY); assert (uidnode->pkt->pkttype==PKT_USER_ID); /* Skip valid user IDs, compacted user IDs, and non-self-signed user IDs if --allow-non-selfsigned-uid is set. */ if (uid->created || uid->flags.compacted || (!uid->is_expired && !uid->is_revoked && opt.allow_non_selfsigned_uid)) return 0; for (node=uidnode->next; node && node->pkt->pkttype == PKT_SIGNATURE; node=node->next) { if (!node->pkt->pkt.signature->flags.chosen_selfsig) { delete_kbnode (node); deleted = 1; uidnode->pkt->pkt.user_id->flags.compacted = 1; } } if (noisy) { const char *reason; char *user = utf8_to_native (uid->name, uid->len, 0); if (uid->is_revoked) reason = _("revoked"); else if (uid->is_expired) reason = _("expired"); else reason = _("invalid"); log_info ("compacting user ID \"%s\" on key %s: %s\n", user, keystr_from_pk (keyblock->pkt->pkt.public_key), reason); xfree (user); } return deleted; } /* Needs to be called after a merge_keys_and_selfsig() */ void clean_one_uid (kbnode_t keyblock, kbnode_t uidnode, int noisy, int self_only, int *uids_cleaned, int *sigs_cleaned) { int dummy; assert (keyblock->pkt->pkttype==PKT_PUBLIC_KEY); assert (uidnode->pkt->pkttype==PKT_USER_ID); if (!uids_cleaned) uids_cleaned = &dummy; if (!sigs_cleaned) sigs_cleaned = &dummy; /* Do clean_uid_from_key first since if it fires off, we don't have to bother with the other. */ *uids_cleaned += clean_uid_from_key (keyblock, uidnode, noisy); if (!uidnode->pkt->pkt.user_id->flags.compacted) *sigs_cleaned += clean_sigs_from_uid (keyblock, uidnode, noisy, self_only); } void clean_key (kbnode_t keyblock, int noisy, int self_only, int *uids_cleaned, int *sigs_cleaned) { kbnode_t uidnode; merge_keys_and_selfsig (keyblock); for (uidnode = keyblock->next; uidnode && uidnode->pkt->pkttype != PKT_PUBLIC_SUBKEY; uidnode = uidnode->next) { if (uidnode->pkt->pkttype == PKT_USER_ID) clean_one_uid (keyblock, uidnode,noisy, self_only, uids_cleaned, sigs_cleaned); } } diff --git a/g13/mount.c b/g13/mount.c index a9203d11f..8d1c0150f 100644 --- a/g13/mount.c +++ b/g13/mount.c @@ -1,417 +1,416 @@ /* mount.c - Mount a crypto container * Copyright (C) 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 "g13.h" #include "i18n.h" #include "mount.h" #include "keyblob.h" #include "backend.h" #include "utils.h" #include "../common/sysutils.h" #include "call-gpg.h" #include "mountinfo.h" #include "runner.h" +#include "host2net.h" /* Parse the header prefix and return the length of the entire header. */ static gpg_error_t parse_header (const char *filename, const unsigned char *packet, size_t packetlen, size_t *r_headerlen) { unsigned int len; if (packetlen != 32) return gpg_error (GPG_ERR_BUG); - len = ((packet[2] << 24) | (packet[3] << 16) - | (packet[4] << 8) | packet[5]); + len = buf32_to_uint (packet+2); if (packet[0] != (0xc0|61) || len < 26 || memcmp (packet+6, "GnuPG/G13", 10)) { log_error ("file '%s' is not valid container\n", filename); return gpg_error (GPG_ERR_INV_OBJ); } if (packet[16] != 1) { log_error ("unknown version %u of container '%s'\n", (unsigned int)packet[16], filename); return gpg_error (GPG_ERR_INV_OBJ); } if (packet[17] || packet[18] || packet[26] || packet[27] || packet[28] || packet[29] || packet[30] || packet[31]) log_info ("WARNING: unknown meta information in '%s'\n", filename); if (packet[19]) log_info ("WARNING: OS flag is not supported in '%s'\n", filename); if (packet[24] != 1 || packet[25] != 0) { log_error ("meta data copies in '%s' are not supported\n", filename); return gpg_error (GPG_ERR_NOT_IMPLEMENTED); } - len = ((packet[20] << 24) | (packet[21] << 16) - | (packet[22] << 8) | packet[23]); + len = buf32_to_uint (packet+20); /* Do a basic sanity check on the length. */ if (len < 32 || len > 1024*1024) { log_error ("bad length given in container '%s'\n", filename); return gpg_error (GPG_ERR_INV_OBJ); } *r_headerlen = len; return 0; } /* Read the prefix of the keyblob and do some basic parsing. On success returns an open estream file at R_FP and the length of the header at R_HEADERLEN. */ static gpg_error_t read_keyblob_prefix (const char *filename, estream_t *r_fp, size_t *r_headerlen) { gpg_error_t err; estream_t fp; unsigned char packet[32]; *r_fp = NULL; fp = es_fopen (filename, "rb"); if (!fp) { err = gpg_error_from_syserror (); log_error ("error reading '%s': %s\n", filename, gpg_strerror (err)); return err; } /* Read the header. It is defined as 32 bytes thus we read it in one go. */ if (es_fread (packet, 32, 1, fp) != 1) { err = gpg_error_from_syserror (); log_error ("error reading the header of '%s': %s\n", filename, gpg_strerror (err)); es_fclose (fp); return err; } err = parse_header (filename, packet, 32, r_headerlen); if (err) es_fclose (fp); else *r_fp = fp; return err; } /* Read the keyblob at FILENAME. The caller should have acquired a lockfile and checked that the file exists. */ static gpg_error_t read_keyblob (const char *filename, void **r_enckeyblob, size_t *r_enckeybloblen) { gpg_error_t err; estream_t fp = NULL; size_t headerlen = 0; size_t msglen; void *msg = NULL; *r_enckeyblob = NULL; *r_enckeybloblen = 0; err = read_keyblob_prefix (filename, &fp, &headerlen); if (err) goto leave; if (opt.verbose) log_info ("header length of '%s' is %zu\n", filename, headerlen); /* Read everything including the padding. We should eventually do a regular OpenPGP parsing to detect the padding packet and pass only the actual used OpenPGP data to the engine. This is in particular required when supporting CMS which will be encapsulated in an OpenPGP packet. */ assert (headerlen >= 32); msglen = headerlen - 32; if (!msglen) { err = gpg_error (GPG_ERR_NO_DATA); goto leave; } msg = xtrymalloc (msglen); if (!msglen) { err = gpg_error_from_syserror (); goto leave; } if (es_fread (msg, msglen, 1, fp) != 1) { err = gpg_error_from_syserror (); log_error ("error reading keyblob of '%s': %s\n", filename, gpg_strerror (err)); goto leave; } *r_enckeyblob = msg; msg = NULL; *r_enckeybloblen = msglen; leave: xfree (msg); es_fclose (fp); return err; } /* Decrypt the keyblob (ENCKEYBLOB,ENCKEYBLOBLEN) and store the result at (R_KEYBLOB, R_KEYBLOBLEN). Returns 0 on success or an error code. On error R_KEYBLOB is set to NULL. */ static gpg_error_t decrypt_keyblob (ctrl_t ctrl, const void *enckeyblob, size_t enckeybloblen, void **r_keyblob, size_t *r_keybloblen) { gpg_error_t err; /* FIXME: For now we only implement OpenPGP. */ err = gpg_decrypt_blob (ctrl, enckeyblob, enckeybloblen, r_keyblob, r_keybloblen); return err; } static void dump_keyblob (tupledesc_t tuples) { size_t n; unsigned int tag; const void *value; 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 (tag == KEYBLOB_TAG_ENCKEY || tag == KEYBLOB_TAG_MACKEY) log_printf ("[confidential]\n"); else if (!n) log_printf ("[none]\n"); else log_printhex ("", value, n); value = next_tuple (tuples, &tag, &n); } } /* Mount the container with name FILENAME at MOUNTPOINT. */ gpg_error_t g13_mount_container (ctrl_t ctrl, const char *filename, const char *mountpoint) { gpg_error_t err; dotlock_t lock; void *enckeyblob = NULL; size_t enckeybloblen; void *keyblob = NULL; size_t keybloblen; tupledesc_t tuples = NULL; size_t n; const unsigned char *value; int conttype; unsigned int rid; char *mountpoint_buffer = NULL; /* A quick check to see whether the container exists. */ if (access (filename, R_OK)) return gpg_error_from_syserror (); if (!mountpoint) { mountpoint_buffer = xtrystrdup ("/tmp/g13-XXXXXX"); if (!mountpoint_buffer) return gpg_error_from_syserror (); if (!gnupg_mkdtemp (mountpoint_buffer)) { err = gpg_error_from_syserror (); log_error (_("can't create directory '%s': %s\n"), "/tmp/g13-XXXXXX", gpg_strerror (err)); xfree (mountpoint_buffer); return err; } mountpoint = mountpoint_buffer; } /* Try to take a lock. */ lock = dotlock_create (filename, 0); if (!lock) { xfree (mountpoint_buffer); return gpg_error_from_syserror (); } if (dotlock_take (lock, 0)) { err = gpg_error_from_syserror (); goto leave; } else err = 0; /* Check again that the file exists. */ { struct stat sb; if (stat (filename, &sb)) { err = gpg_error_from_syserror (); goto leave; } } /* Read the encrypted keyblob. */ err = read_keyblob (filename, &enckeyblob, &enckeybloblen); if (err) goto leave; /* Decrypt that keyblob and store it in a tuple descriptor. */ err = decrypt_keyblob (ctrl, enckeyblob, enckeybloblen, &keyblob, &keybloblen); if (err) goto leave; xfree (enckeyblob); enckeyblob = NULL; err = create_tupledesc (&tuples, keyblob, keybloblen); if (!err) keyblob = NULL; else { if (gpg_err_code (err) == GPG_ERR_NOT_SUPPORTED) log_error ("unknown keyblob version\n"); goto leave; } if (opt.verbose) dump_keyblob (tuples); value = find_tuple (tuples, KEYBLOB_TAG_CONTTYPE, &n); if (!value || n != 2) conttype = 0; else conttype = (value[0] << 8 | value[1]); if (!be_is_supported_conttype (conttype)) { log_error ("content type %d is not supported\n", conttype); err = gpg_error (GPG_ERR_NOT_SUPPORTED); goto leave; } err = be_mount_container (ctrl, conttype, filename, mountpoint, tuples, &rid); if (!err) { err = mountinfo_add_mount (filename, mountpoint, conttype, rid, !!mountpoint_buffer); /* Fixme: What shall we do if this fails? Add a provisional mountinfo entry first and remove it on error? */ if (!err) { char *tmp = percent_plus_escape (mountpoint); if (!tmp) err = gpg_error_from_syserror (); else { g13_status (ctrl, STATUS_MOUNTPOINT, tmp, NULL); xfree (tmp); } } } leave: destroy_tupledesc (tuples); xfree (keyblob); xfree (enckeyblob); dotlock_destroy (lock); xfree (mountpoint_buffer); return err; } /* Unmount the container with name FILENAME or the one mounted at MOUNTPOINT. If both are given the FILENAME takes precedence. */ gpg_error_t g13_umount_container (ctrl_t ctrl, const char *filename, const char *mountpoint) { gpg_error_t err; unsigned int rid; runner_t runner; (void)ctrl; if (!filename && !mountpoint) return gpg_error (GPG_ERR_ENOENT); err = mountinfo_find_mount (filename, mountpoint, &rid); if (err) return err; runner = runner_find_by_rid (rid); if (!runner) { log_error ("runner %u not found\n", rid); return gpg_error (GPG_ERR_NOT_FOUND); } runner_cancel (runner); runner_release (runner); return 0; } /* Test whether the container with name FILENAME is a suitable G13 container. This function may even be called on a mounted container. */ gpg_error_t g13_is_container (ctrl_t ctrl, const char *filename) { gpg_error_t err; estream_t fp = NULL; size_t dummy; (void)ctrl; /* Read just the prefix of the header. */ err = read_keyblob_prefix (filename, &fp, &dummy); if (!err) es_fclose (fp); return err; } diff --git a/kbx/keybox-dump.c b/kbx/keybox-dump.c index 5315e8444..8815a6f7f 100644 --- a/kbx/keybox-dump.c +++ b/kbx/keybox-dump.c @@ -1,821 +1,805 @@ /* keybox-dump.c - Debug helpers * 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 "keybox-defs.h" #include +#include "host2net.h" /* Argg, we can't include ../common/util.h */ char *bin2hexcolon (const void *buffer, size_t length, char *stringbuf); +#define get32(a) buf32_to_ulong ((a)) +#define get16(a) buf16_to_ulong ((a)) -static ulong -get32 (const byte *buffer) -{ - ulong a; - a = *buffer << 24; - a |= buffer[1] << 16; - a |= buffer[2] << 8; - a |= buffer[3]; - return a; -} - -static ulong -get16 (const byte *buffer) -{ - ulong a; - a = *buffer << 8; - a |= buffer[1]; - return a; -} void print_string (FILE *fp, const byte *p, size_t n, int delim) { for ( ; n; n--, p++ ) { if (*p < 0x20 || (*p >= 0x7f && *p < 0xa0) || *p == delim) { putc('\\', fp); if( *p == '\n' ) putc('n', fp); else if( *p == '\r' ) putc('r', fp); else if( *p == '\f' ) putc('f', fp); else if( *p == '\v' ) putc('v', fp); else if( *p == '\b' ) putc('b', fp); else if( !*p ) putc('0', fp); else fprintf(fp, "x%02x", *p ); } else putc(*p, fp); } } static int print_checksum (const byte *buffer, size_t length, size_t unhashed, FILE *fp) { const byte *p; int i; int hashlen; unsigned char digest[20]; fprintf (fp, "Checksum: "); if (unhashed && unhashed < 20) { fputs ("[specified unhashed sized too short]\n", fp); return 0; } if (!unhashed) { unhashed = 16; hashlen = 16; } else hashlen = 20; if (length < 5+unhashed) { fputs ("[blob too short for a checksum]\n", fp); return 0; } p = buffer + length - hashlen; for (i=0; i < hashlen; p++, i++) fprintf (fp, "%02x", *p); if (hashlen == 16) /* Compatibility method. */ { gcry_md_hash_buffer (GCRY_MD_MD5, digest, buffer, length - 16); if (!memcmp (buffer + length - 16, digest, 16)) fputs (" [valid]\n", fp); else fputs (" [bad]\n", fp); } else { gcry_md_hash_buffer (GCRY_MD_SHA1, digest, buffer, length - unhashed); if (!memcmp (buffer + length - hashlen, digest, hashlen)) fputs (" [valid]\n", fp); else fputs (" [bad]\n", fp); } return 0; } static int dump_header_blob (const byte *buffer, size_t length, FILE *fp) { unsigned long n; if (length < 32) { fprintf (fp, "[blob too short]\n"); return -1; } fprintf (fp, "Version: %d\n", buffer[5]); n = get16 (buffer + 6); fprintf( fp, "Flags: %04lX", n); if (n) { int any = 0; fputs (" (", fp); if ((n & 2)) { if (any) putc (',', fp); fputs ("openpgp", fp); any++; } putc (')', fp); } putc ('\n', fp); if ( memcmp (buffer+8, "KBXf", 4)) fprintf (fp, "[Error: invalid magic number]\n"); n = get32 (buffer+16); fprintf( fp, "created-at: %lu\n", n ); n = get32 (buffer+20); fprintf( fp, "last-maint: %lu\n", n ); return 0; } /* Dump one block to FP */ int _keybox_dump_blob (KEYBOXBLOB blob, FILE *fp) { const byte *buffer; size_t length; int type, i; ulong n, nkeys, keyinfolen; ulong nuids, uidinfolen; ulong nsigs, siginfolen; ulong rawdata_off, rawdata_len; ulong nserial; ulong unhashed; const byte *p; buffer = _keybox_get_blob_image (blob, &length); if (length < 32) { fprintf (fp, "[blob too short]\n"); return -1; } n = get32( buffer ); if (n > length) fprintf (fp, "[blob larger than length - output truncated]\n"); else length = n; /* ignore the rest */ fprintf (fp, "Length: %lu\n", n ); type = buffer[4]; switch (type) { case KEYBOX_BLOBTYPE_EMPTY: fprintf (fp, "Type: Empty\n"); return 0; case KEYBOX_BLOBTYPE_HEADER: fprintf (fp, "Type: Header\n"); return dump_header_blob (buffer, length, fp); case KEYBOX_BLOBTYPE_PGP: fprintf (fp, "Type: OpenPGP\n"); break; case KEYBOX_BLOBTYPE_X509: fprintf (fp, "Type: X.509\n"); break; default: fprintf (fp, "Type: %d\n", type); fprintf (fp, "[can't dump this blob type]\n"); return 0; } fprintf (fp, "Version: %d\n", buffer[5]); if (length < 40) { fprintf (fp, "[blob too short]\n"); return -1; } n = get16 (buffer + 6); fprintf( fp, "Blob-Flags: %04lX", n); if (n) { int any = 0; fputs (" (", fp); if ((n & 1)) { fputs ("secret", fp); any++; } if ((n & 2)) { if (any) putc (',', fp); fputs ("ephemeral", fp); any++; } putc (')', fp); } putc ('\n', fp); rawdata_off = get32 (buffer + 8); rawdata_len = get32 (buffer + 12); fprintf( fp, "Data-Offset: %lu\n", rawdata_off ); fprintf( fp, "Data-Length: %lu\n", rawdata_len ); if (rawdata_off > length || rawdata_len > length || rawdata_off+rawdata_len > length || rawdata_len + 4 > length || rawdata_off+rawdata_len + 4 > length) fprintf (fp, "[Error: raw data larger than blob]\n"); unhashed = length - rawdata_off - rawdata_len; fprintf (fp, "Unhashed: %lu\n", unhashed); nkeys = get16 (buffer + 16); fprintf (fp, "Key-Count: %lu\n", nkeys ); if (!nkeys) fprintf (fp, "[Error: no keys]\n"); if (nkeys > 1 && type == KEYBOX_BLOBTYPE_X509) fprintf (fp, "[Error: only one key allowed for X509]\n"); keyinfolen = get16 (buffer + 18 ); fprintf (fp, "Key-Info-Length: %lu\n", keyinfolen); /* fixme: check bounds */ p = buffer + 20; for (n=0; n < nkeys; n++, p += keyinfolen) { ulong kidoff, kflags; fprintf (fp, "Key-Fpr[%lu]: ", n ); for (i=0; i < 20; i++ ) fprintf (fp, "%02X", p[i]); kidoff = get32 (p + 20); fprintf (fp, "\nKey-Kid-Off[%lu]: %lu\n", n, kidoff ); fprintf (fp, "Key-Kid[%lu]: ", n ); /* fixme: check bounds */ for (i=0; i < 8; i++ ) fprintf (fp, "%02X", buffer[kidoff+i] ); kflags = get16 (p + 24 ); fprintf( fp, "\nKey-Flags[%lu]: %04lX\n", n, kflags); } /* serial number */ fputs ("Serial-No: ", fp); nserial = get16 (p); p += 2; if (!nserial) fputs ("none", fp); else { for (; nserial; nserial--, p++) fprintf (fp, "%02X", *p); } putc ('\n', fp); /* user IDs */ nuids = get16 (p); fprintf (fp, "Uid-Count: %lu\n", nuids ); uidinfolen = get16 (p + 2); fprintf (fp, "Uid-Info-Length: %lu\n", uidinfolen); /* fixme: check bounds */ p += 4; for (n=0; n < nuids; n++, p += uidinfolen) { ulong uidoff, uidlen, uflags; uidoff = get32( p ); uidlen = get32( p+4 ); if (type == KEYBOX_BLOBTYPE_X509 && !n) { fprintf (fp, "Issuer-Off: %lu\n", uidoff ); fprintf (fp, "Issuer-Len: %lu\n", uidlen ); fprintf (fp, "Issuer: \""); } else if (type == KEYBOX_BLOBTYPE_X509 && n == 1) { fprintf (fp, "Subject-Off: %lu\n", uidoff ); fprintf (fp, "Subject-Len: %lu\n", uidlen ); fprintf (fp, "Subject: \""); } else { fprintf (fp, "Uid-Off[%lu]: %lu\n", n, uidoff ); fprintf (fp, "Uid-Len[%lu]: %lu\n", n, uidlen ); fprintf (fp, "Uid[%lu]: \"", n ); } print_string (fp, buffer+uidoff, uidlen, '\"'); fputs ("\"\n", fp); uflags = get16 (p + 8); if (type == KEYBOX_BLOBTYPE_X509 && !n) { fprintf (fp, "Issuer-Flags: %04lX\n", uflags ); fprintf (fp, "Issuer-Validity: %d\n", p[10] ); } else if (type == KEYBOX_BLOBTYPE_X509 && n == 1) { fprintf (fp, "Subject-Flags: %04lX\n", uflags ); fprintf (fp, "Subject-Validity: %d\n", p[10] ); } else { fprintf (fp, "Uid-Flags[%lu]: %04lX\n", n, uflags ); fprintf (fp, "Uid-Validity[%lu]: %d\n", n, p[10] ); } } nsigs = get16 (p); fprintf (fp, "Sig-Count: %lu\n", nsigs ); siginfolen = get16 (p + 2); fprintf (fp, "Sig-Info-Length: %lu\n", siginfolen ); /* fixme: check bounds */ p += 4; { int in_range = 0; ulong first = 0; for (n=0; n < nsigs; n++, p += siginfolen) { ulong sflags; sflags = get32 (p); if (!in_range && !sflags) { in_range = 1; first = n; continue; } if (in_range && !sflags) continue; if (in_range) { fprintf (fp, "Sig-Expire[%lu-%lu]: [not checked]\n", first, n-1); in_range = 0; } fprintf (fp, "Sig-Expire[%lu]: ", n ); if (!sflags) fputs ("[not checked]", fp); else if (sflags == 1 ) fputs ("[missing key]", fp); else if (sflags == 2 ) fputs ("[bad signature]", fp); else if (sflags < 0x10000000) fprintf (fp, "[bad flag %0lx]", sflags); else if (sflags == (ulong)(-1)) fputs ("[good - does not expire]", fp ); else fprintf (fp, "[good - expires at %lu]", sflags); putc ('\n', fp ); } if (in_range) { fprintf (fp, "Sig-Expire[%lu-%lu]: [not checked]\n", first, n-1); in_range = 0; } } fprintf (fp, "Ownertrust: %d\n", p[0] ); fprintf (fp, "All-Validity: %d\n", p[1] ); p += 4; n = get32 (p); p += 4; fprintf (fp, "Recheck-After: %lu\n", n ); n = get32 (p ); p += 4; fprintf( fp, "Latest-Timestamp: %lu\n", n ); n = get32 (p ); p += 4; fprintf (fp, "Created-At: %lu\n", n ); n = get32 (p ); p += 4; fprintf (fp, "Reserved-Space: %lu\n", n ); if (n >= 4 && unhashed >= 24) { n = get32 ( buffer + length - unhashed); fprintf (fp, "Storage-Flags: %08lx\n", n ); } print_checksum (buffer, length, unhashed, fp); return 0; } /* Compute the SHA-1 checksum of the rawdata in BLOB and put it into DIGEST. */ static int hash_blob_rawdata (KEYBOXBLOB blob, unsigned char *digest) { const unsigned char *buffer; size_t n, length; int type; ulong rawdata_off, rawdata_len; buffer = _keybox_get_blob_image (blob, &length); if (length < 32) return -1; n = get32 (buffer); if (n < length) length = n; /* Blob larger than length in header - ignore the rest. */ type = buffer[4]; switch (type) { case KEYBOX_BLOBTYPE_PGP: case KEYBOX_BLOBTYPE_X509: break; case KEYBOX_BLOBTYPE_EMPTY: case KEYBOX_BLOBTYPE_HEADER: default: memset (digest, 0, 20); return 0; } if (length < 40) return -1; rawdata_off = get32 (buffer + 8); rawdata_len = get32 (buffer + 12); if (rawdata_off > length || rawdata_len > length || rawdata_off+rawdata_off > length) return -1; /* Out of bounds. */ gcry_md_hash_buffer (GCRY_MD_SHA1, digest, buffer+rawdata_off, rawdata_len); return 0; } struct file_stats_s { unsigned long too_short_blobs; unsigned long too_large_blobs; unsigned long total_blob_count; unsigned long empty_blob_count; unsigned long header_blob_count; unsigned long pgp_blob_count; unsigned long x509_blob_count; unsigned long unknown_blob_count; unsigned long non_flagged; unsigned long secret_flagged; unsigned long ephemeral_flagged; unsigned long skipped_long_blobs; }; static int update_stats (KEYBOXBLOB blob, struct file_stats_s *s) { const unsigned char *buffer; size_t length; int type; unsigned long n; buffer = _keybox_get_blob_image (blob, &length); if (length < 32) { s->too_short_blobs++; return -1; } n = get32( buffer ); if (n > length) s->too_large_blobs++; else length = n; /* ignore the rest */ s->total_blob_count++; type = buffer[4]; switch (type) { case KEYBOX_BLOBTYPE_EMPTY: s->empty_blob_count++; return 0; case KEYBOX_BLOBTYPE_HEADER: s->header_blob_count++; return 0; case KEYBOX_BLOBTYPE_PGP: s->pgp_blob_count++; break; case KEYBOX_BLOBTYPE_X509: s->x509_blob_count++; break; default: s->unknown_blob_count++; return 0; } if (length < 40) { s->too_short_blobs++; return -1; } n = get16 (buffer + 6); if (n) { if ((n & 1)) s->secret_flagged++; if ((n & 2)) s->ephemeral_flagged++; } else s->non_flagged++; return 0; } static FILE * open_file (const char **filename, FILE *outfp) { FILE *fp; if (!*filename) { *filename = "-"; fp = stdin; } else fp = fopen (*filename, "rb"); if (!fp) { int save_errno = errno; fprintf (outfp, "can't open '%s': %s\n", *filename, strerror(errno)); gpg_err_set_errno (save_errno); } return fp; } int _keybox_dump_file (const char *filename, int stats_only, FILE *outfp) { FILE *fp; KEYBOXBLOB blob; int rc; unsigned long count = 0; struct file_stats_s stats; memset (&stats, 0, sizeof stats); if (!(fp = open_file (&filename, outfp))) return gpg_error_from_syserror (); for (;;) { rc = _keybox_read_blob (&blob, fp); if (gpg_err_code (rc) == GPG_ERR_TOO_LARGE && gpg_err_source (rc) == GPG_ERR_SOURCE_KEYBOX) { if (stats_only) stats.skipped_long_blobs++; else { fprintf (outfp, "BEGIN-RECORD: %lu\n", count ); fprintf (outfp, "# Record too large\nEND-RECORD\n"); } count++; continue; } if (rc) break; if (stats_only) { update_stats (blob, &stats); } else { fprintf (outfp, "BEGIN-RECORD: %lu\n", count ); _keybox_dump_blob (blob, outfp); fprintf (outfp, "END-RECORD\n"); } _keybox_release_blob (blob); count++; } if (rc == -1) rc = 0; if (rc) fprintf (outfp, "# error reading '%s': %s\n", filename, gpg_strerror (rc)); if (fp != stdin) fclose (fp); if (stats_only) { fprintf (outfp, "Total number of blobs: %8lu\n" " header: %8lu\n" " empty: %8lu\n" " openpgp: %8lu\n" " x509: %8lu\n" " non flagged: %8lu\n" " secret flagged: %8lu\n" " ephemeral flagged: %8lu\n", stats.total_blob_count, stats.header_blob_count, stats.empty_blob_count, stats.pgp_blob_count, stats.x509_blob_count, stats.non_flagged, stats.secret_flagged, stats.ephemeral_flagged); if (stats.skipped_long_blobs) fprintf (outfp, " skipped long blobs: %8lu\n", stats.skipped_long_blobs); if (stats.unknown_blob_count) fprintf (outfp, " unknown blob types: %8lu\n", stats.unknown_blob_count); if (stats.too_short_blobs) fprintf (outfp, " too short blobs: %8lu (error)\n", stats.too_short_blobs); if (stats.too_large_blobs) fprintf (outfp, " too large blobs: %8lu (error)\n", stats.too_large_blobs); } return rc; } struct dupitem_s { unsigned long recno; unsigned char digest[20]; }; static int cmp_dupitems (const void *arg_a, const void *arg_b) { struct dupitem_s *a = (struct dupitem_s *)arg_a; struct dupitem_s *b = (struct dupitem_s *)arg_b; return memcmp (a->digest, b->digest, 20); } int _keybox_dump_find_dups (const char *filename, int print_them, FILE *outfp) { FILE *fp; KEYBOXBLOB blob; int rc; unsigned long recno = 0; unsigned char zerodigest[20]; struct dupitem_s *dupitems; size_t dupitems_size, dupitems_count, lastn, n; char fprbuf[3*20+1]; (void)print_them; memset (zerodigest, 0, sizeof zerodigest); if (!(fp = open_file (&filename, outfp))) return gpg_error_from_syserror (); dupitems_size = 1000; dupitems = malloc (dupitems_size * sizeof *dupitems); if (!dupitems) { gpg_error_t tmperr = gpg_error_from_syserror (); fprintf (outfp, "error allocating array for '%s': %s\n", filename, strerror(errno)); return tmperr; } dupitems_count = 0; while ( !(rc = _keybox_read_blob (&blob, fp)) ) { unsigned char digest[20]; if (hash_blob_rawdata (blob, digest)) fprintf (outfp, "error in blob %ld of '%s'\n", recno, filename); else if (memcmp (digest, zerodigest, 20)) { if (dupitems_count >= dupitems_size) { struct dupitem_s *tmp; dupitems_size += 1000; tmp = realloc (dupitems, dupitems_size * sizeof *dupitems); if (!tmp) { gpg_error_t tmperr = gpg_error_from_syserror (); fprintf (outfp, "error reallocating array for '%s': %s\n", filename, strerror(errno)); free (dupitems); return tmperr; } dupitems = tmp; } dupitems[dupitems_count].recno = recno; memcpy (dupitems[dupitems_count].digest, digest, 20); dupitems_count++; } _keybox_release_blob (blob); recno++; } if (rc == -1) rc = 0; if (rc) fprintf (outfp, "error reading '%s': %s\n", filename, gpg_strerror (rc)); if (fp != stdin) fclose (fp); qsort (dupitems, dupitems_count, sizeof *dupitems, cmp_dupitems); for (lastn=0, n=1; n < dupitems_count; lastn=n, n++) { if (!memcmp (dupitems[lastn].digest, dupitems[n].digest, 20)) { bin2hexcolon (dupitems[lastn].digest, 20, fprbuf); fprintf (outfp, "fpr=%s recno=%lu", fprbuf, dupitems[lastn].recno); do fprintf (outfp, " %lu", dupitems[n].recno); while (++n < dupitems_count && !memcmp (dupitems[lastn].digest, dupitems[n].digest, 20)); putc ('\n', outfp); n--; } } free (dupitems); return rc; } /* Print records with record numbers FROM to TO to OUTFP. */ int _keybox_dump_cut_records (const char *filename, unsigned long from, unsigned long to, FILE *outfp) { FILE *fp; KEYBOXBLOB blob; int rc; unsigned long recno = 0; if (!(fp = open_file (&filename, stderr))) return gpg_error_from_syserror (); while ( !(rc = _keybox_read_blob (&blob, fp)) ) { if (recno > to) break; /* Ready. */ if (recno >= from) { if ((rc = _keybox_write_blob (blob, outfp))) { fprintf (stderr, "error writing output: %s\n", gpg_strerror (rc)); goto leave; } } _keybox_release_blob (blob); recno++; } if (rc == -1) rc = 0; if (rc) fprintf (stderr, "error reading '%s': %s\n", filename, gpg_strerror (rc)); leave: if (fp != stdin) fclose (fp); return rc; } diff --git a/kbx/keybox-openpgp.c b/kbx/keybox-openpgp.c index 6ae6c44ce..2cac242e9 100644 --- a/kbx/keybox-openpgp.c +++ b/kbx/keybox-openpgp.c @@ -1,518 +1,516 @@ /* keybox-openpgp.c - OpenPGP key parsing * Copyright (C) 2001, 2003, 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 . */ /* This is a simple OpenPGP parser suitable for all OpenPGP key material. It just provides the functionality required to build and parse an KBX OpenPGP key blob. Thus it is not a complete parser. However it is self-contained and optimized for fast in-memory parsing. Note that we don't support old ElGamal v3 keys anymore. */ #include #include #include #include #include #include #include "keybox-defs.h" #include #include "../common/openpgpdefs.h" - +#include "host2net.h" /* Assume a valid OpenPGP packet at the address pointed to by BUFBTR which has a maximum length as stored at BUFLEN. Return the header information of that packet and advance the pointer stored at BUFPTR to the next packet; also adjust the length stored at BUFLEN to match the remaining bytes. If there are no more packets, store NULL at BUFPTR. Return an non-zero error code on failure or the following data on success: R_DATAPKT = Pointer to the begin of the packet data. R_DATALEN = Length of this data. This has already been checked to fit into the buffer. R_PKTTYPE = The packet type. R_NTOTAL = The total number of bytes of this packet Note that these values are only updated on success. */ static gpg_error_t next_packet (unsigned char const **bufptr, size_t *buflen, unsigned char const **r_data, size_t *r_datalen, int *r_pkttype, size_t *r_ntotal) { const unsigned char *buf = *bufptr; size_t len = *buflen; int c, ctb, pkttype; unsigned long pktlen; if (!len) return gpg_error (GPG_ERR_NO_DATA); ctb = *buf++; len--; if ( !(ctb & 0x80) ) return gpg_error (GPG_ERR_INV_PACKET); /* Invalid CTB. */ pktlen = 0; if ((ctb & 0x40)) /* New style (OpenPGP) CTB. */ { pkttype = (ctb & 0x3f); if (!len) return gpg_error (GPG_ERR_INV_PACKET); /* No 1st length byte. */ c = *buf++; len--; if (pkttype == PKT_COMPRESSED) return gpg_error (GPG_ERR_UNEXPECTED); /* ... packet in a keyblock. */ if ( c < 192 ) pktlen = c; else if ( c < 224 ) { pktlen = (c - 192) * 256; if (!len) return gpg_error (GPG_ERR_INV_PACKET); /* No 2nd length byte. */ c = *buf++; len--; pktlen += c + 192; } else if (c == 255) { if (len <4 ) return gpg_error (GPG_ERR_INV_PACKET); /* No length bytes. */ - pktlen = (*buf++) << 24; - pktlen |= (*buf++) << 16; - pktlen |= (*buf++) << 8; - pktlen |= (*buf++); + pktlen = buf32_to_ulong (buf); + buf += 4; len -= 4; } else /* Partial length encoding is not allowed for key packets. */ return gpg_error (GPG_ERR_UNEXPECTED); } else /* Old style CTB. */ { int lenbytes; pktlen = 0; pkttype = (ctb>>2)&0xf; lenbytes = ((ctb&3)==3)? 0 : (1<<(ctb & 3)); if (!lenbytes) /* Not allowed in key packets. */ return gpg_error (GPG_ERR_UNEXPECTED); if (len < lenbytes) return gpg_error (GPG_ERR_INV_PACKET); /* Not enough length bytes. */ for (; lenbytes; lenbytes--) { pktlen <<= 8; pktlen |= *buf++; len--; } } /* Do some basic sanity check. */ switch (pkttype) { case PKT_SIGNATURE: case PKT_SECRET_KEY: case PKT_PUBLIC_KEY: case PKT_SECRET_SUBKEY: case PKT_MARKER: case PKT_RING_TRUST: case PKT_USER_ID: case PKT_PUBLIC_SUBKEY: case PKT_OLD_COMMENT: case PKT_ATTRIBUTE: case PKT_COMMENT: case PKT_GPG_CONTROL: break; /* Okay these are allowed packets. */ default: return gpg_error (GPG_ERR_UNEXPECTED); } if (pktlen == (unsigned long)(-1)) return gpg_error (GPG_ERR_INV_PACKET); if (pktlen > len) return gpg_error (GPG_ERR_INV_PACKET); /* Packet length header too long. */ *r_data = buf; *r_datalen = pktlen; *r_pkttype = pkttype; *r_ntotal = (buf - *bufptr) + pktlen; *bufptr = buf + pktlen; *buflen = len - pktlen; if (!*buflen) *bufptr = NULL; return 0; } /* Parse a key packet and store the information in KI. */ static gpg_error_t parse_key (const unsigned char *data, size_t datalen, struct _keybox_openpgp_key_info *ki) { gpg_error_t err; const unsigned char *data_start = data; int i, version, algorithm; size_t n; int npkey; unsigned char hashbuffer[768]; const unsigned char *mpi_n = NULL; size_t mpi_n_len = 0, mpi_e_len = 0; gcry_md_hd_t md; int is_ecc = 0; if (datalen < 5) return gpg_error (GPG_ERR_INV_PACKET); version = *data++; datalen--; if (version < 2 || version > 4 ) return gpg_error (GPG_ERR_INV_PACKET); /* Invalid version. */ /*timestamp = ((data[0]<<24)|(data[1]<<16)|(data[2]<<8)|(data[3]));*/ data +=4; datalen -=4; if (version < 4) { if (datalen < 2) return gpg_error (GPG_ERR_INV_PACKET); data +=2; datalen -= 2; } if (!datalen) return gpg_error (GPG_ERR_INV_PACKET); algorithm = *data++; datalen--; switch (algorithm) { case PUBKEY_ALGO_RSA: case PUBKEY_ALGO_RSA_E: case PUBKEY_ALGO_RSA_S: npkey = 2; break; case PUBKEY_ALGO_ELGAMAL_E: case PUBKEY_ALGO_ELGAMAL: npkey = 3; break; case PUBKEY_ALGO_DSA: npkey = 4; break; case PUBKEY_ALGO_ECDH: npkey = 3; is_ecc = 1; break; case PUBKEY_ALGO_ECDSA: case PUBKEY_ALGO_EDDSA: npkey = 2; is_ecc = 1; break; default: /* Unknown algorithm. */ return gpg_error (GPG_ERR_UNKNOWN_ALGORITHM); } ki->algo = algorithm; for (i=0; i < npkey; i++ ) { unsigned int nbits, nbytes; if (datalen < 2) return gpg_error (GPG_ERR_INV_PACKET); if (is_ecc && (i == 0 || i == 2)) { nbytes = data[0]; if (nbytes < 2 || nbytes > 254) return gpg_error (GPG_ERR_INV_PACKET); nbytes++; /* The size byte itself. */ if (datalen < nbytes) return gpg_error (GPG_ERR_INV_PACKET); } else { nbits = ((data[0]<<8)|(data[1])); data += 2; datalen -= 2; nbytes = (nbits+7) / 8; if (datalen < nbytes) return gpg_error (GPG_ERR_INV_PACKET); /* For use by v3 fingerprint calculation we need to know the RSA modulus and exponent. */ if (i==0) { mpi_n = data; mpi_n_len = nbytes; } else if (i==1) mpi_e_len = nbytes; } data += nbytes; datalen -= nbytes; } n = data - data_start; if (version < 4) { /* We do not support any other algorithm than RSA in v3 packets. */ if (algorithm < 1 || algorithm > 3) return gpg_error (GPG_ERR_UNSUPPORTED_ALGORITHM); err = gcry_md_open (&md, GCRY_MD_MD5, 0); if (err) return err; /* Oops */ gcry_md_write (md, mpi_n, mpi_n_len); gcry_md_write (md, mpi_n+mpi_n_len+2, mpi_e_len); memcpy (ki->fpr, gcry_md_read (md, 0), 16); gcry_md_close (md); ki->fprlen = 16; if (mpi_n_len < 8) { /* Moduli less than 64 bit are out of the specs scope. Zero them out because this is what gpg does too. */ memset (ki->keyid, 0, 8); } else memcpy (ki->keyid, mpi_n + mpi_n_len - 8, 8); } else { /* Its a pitty that we need to prefix the buffer with the tag and a length header: We can't simply pass it to the fast hashing function for that reason. It might be a good idea to have a scatter-gather enabled hash function. What we do here is to use a static buffer if this one is large enough and only use the regular hash functions if this buffer is not large enough. */ if ( 3 + n < sizeof hashbuffer ) { hashbuffer[0] = 0x99; /* CTB */ hashbuffer[1] = (n >> 8); /* 2 byte length header. */ hashbuffer[2] = n; memcpy (hashbuffer + 3, data_start, n); gcry_md_hash_buffer (GCRY_MD_SHA1, ki->fpr, hashbuffer, 3 + n); } else { err = gcry_md_open (&md, GCRY_MD_SHA1, 0); if (err) return err; /* Oops */ gcry_md_putc (md, 0x99 ); /* CTB */ gcry_md_putc (md, (n >> 8) ); /* 2 byte length header. */ gcry_md_putc (md, n ); gcry_md_write (md, data_start, n); memcpy (ki->fpr, gcry_md_read (md, 0), 20); gcry_md_close (md); } ki->fprlen = 20; memcpy (ki->keyid, ki->fpr+12, 8); } return 0; } /* The caller must pass the address of an INFO structure which will get filled on success with information pertaining to the OpenPGP keyblock IMAGE of length IMAGELEN. Note that a caller does only need to release this INFO structure if the function returns success. If NPARSED is not NULL the actual number of bytes parsed will be stored at this address. */ gpg_error_t _keybox_parse_openpgp (const unsigned char *image, size_t imagelen, size_t *nparsed, keybox_openpgp_info_t info) { gpg_error_t err = 0; const unsigned char *image_start, *data; size_t n, datalen; int pkttype; int first = 1; int read_error = 0; struct _keybox_openpgp_key_info *k, **ktail = NULL; struct _keybox_openpgp_uid_info *u, **utail = NULL; memset (info, 0, sizeof *info); if (nparsed) *nparsed = 0; image_start = image; while (image) { err = next_packet (&image, &imagelen, &data, &datalen, &pkttype, &n); if (err) { read_error = 1; break; } if (first) { if (pkttype == PKT_PUBLIC_KEY) ; else if (pkttype == PKT_SECRET_KEY) info->is_secret = 1; else { err = gpg_error (GPG_ERR_UNEXPECTED); if (nparsed) *nparsed += n; break; } first = 0; } else if (pkttype == PKT_PUBLIC_KEY || pkttype == PKT_SECRET_KEY) break; /* Next keyblock encountered - ready. */ if (nparsed) *nparsed += n; if (pkttype == PKT_SIGNATURE) { /* For now we only count the total number of signatures. */ info->nsigs++; } else if (pkttype == PKT_USER_ID) { info->nuids++; if (info->nuids == 1) { info->uids.off = data - image_start; info->uids.len = datalen; utail = &info->uids.next; } else { u = xtrycalloc (1, sizeof *u); if (!u) { err = gpg_error_from_syserror (); break; } u->off = data - image_start; u->len = datalen; *utail = u; utail = &u->next; } } else if (pkttype == PKT_PUBLIC_KEY || pkttype == PKT_SECRET_KEY) { err = parse_key (data, datalen, &info->primary); if (err) break; } else if( pkttype == PKT_PUBLIC_SUBKEY && datalen && *data == '#' ) { /* Early versions of GnuPG used old PGP comment packets; * luckily all those comments are prefixed by a hash * sign - ignore these packets. */ } else if (pkttype == PKT_PUBLIC_SUBKEY || pkttype == PKT_SECRET_SUBKEY) { info->nsubkeys++; if (info->nsubkeys == 1) { err = parse_key (data, datalen, &info->subkeys); if (err) { info->nsubkeys--; /* We ignore subkeys with unknown algorithms. */ if (gpg_err_code (err) == GPG_ERR_UNKNOWN_ALGORITHM || gpg_err_code (err) == GPG_ERR_UNSUPPORTED_ALGORITHM) err = 0; if (err) break; } else ktail = &info->subkeys.next; } else { k = xtrycalloc (1, sizeof *k); if (!k) { err = gpg_error_from_syserror (); break; } err = parse_key (data, datalen, k); if (err) { xfree (k); info->nsubkeys--; /* We ignore subkeys with unknown algorithms. */ if (gpg_err_code (err) == GPG_ERR_UNKNOWN_ALGORITHM || gpg_err_code (err) == GPG_ERR_UNSUPPORTED_ALGORITHM) err = 0; if (err) break; } else { *ktail = k; ktail = &k->next; } } } } if (err) { _keybox_destroy_openpgp_info (info); if (!read_error) { /* Packet parsing worked, thus we should be able to skip the rest of the keyblock. */ while (image) { if (next_packet (&image, &imagelen, &data, &datalen, &pkttype, &n) ) break; /* Another error - stop here. */ if (pkttype == PKT_PUBLIC_KEY || pkttype == PKT_SECRET_KEY) break; /* Next keyblock encountered - ready. */ if (nparsed) *nparsed += n; } } } return err; } /* Release any malloced data in INFO but not INFO itself! */ void _keybox_destroy_openpgp_info (keybox_openpgp_info_t info) { struct _keybox_openpgp_key_info *k, *k2; struct _keybox_openpgp_uid_info *u, *u2; assert (!info->primary.next); for (k=info->subkeys.next; k; k = k2) { k2 = k->next; xfree (k); } for (u=info->uids.next; u; u = u2) { u2 = u->next; xfree (u); } } diff --git a/kbx/keybox-search.c b/kbx/keybox-search.c index 0a3ed43d0..d22ef1921 100644 --- a/kbx/keybox-search.c +++ b/kbx/keybox-search.c @@ -1,1177 +1,1158 @@ /* keybox-search.c - Search operations * Copyright (C) 2001, 2002, 2003, 2004, 2012, * 2013 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 "../common/stringhelp.h" /* ascii_xxxx() */ #include "keybox-defs.h" #include - +#include "host2net.h" #define xtoi_1(p) (*(p) <= '9'? (*(p)- '0'): \ *(p) <= 'F'? (*(p)-'A'+10):(*(p)-'a'+10)) #define xtoi_2(p) ((xtoi_1(p) * 16) + xtoi_1((p)+1)) struct sn_array_s { int snlen; unsigned char *sn; }; - -static inline ulong -get32 (const byte *buffer) -{ - ulong a; - a = *buffer << 24; - a |= buffer[1] << 16; - a |= buffer[2] << 8; - a |= buffer[3]; - return a; -} - -static inline ulong -get16 (const byte *buffer) -{ - ulong a; - a = *buffer << 8; - a |= buffer[1]; - return a; -} - +#define get32(a) buf32_to_ulong ((a)) +#define get16(a) buf16_to_ulong ((a)) static inline unsigned int blob_get_blob_flags (KEYBOXBLOB blob) { const unsigned char *buffer; size_t length; buffer = _keybox_get_blob_image (blob, &length); if (length < 8) return 0; /* oops */ return get16 (buffer + 6); } /* Return the first keyid from the blob. Returns true if available. */ static int blob_get_first_keyid (KEYBOXBLOB blob, u32 *kid) { const unsigned char *buffer; size_t length, nkeys, keyinfolen; buffer = _keybox_get_blob_image (blob, &length); if (length < 48) return 0; /* blob too short */ nkeys = get16 (buffer + 16); keyinfolen = get16 (buffer + 18); if (!nkeys || keyinfolen < 28) return 0; /* invalid blob */ kid[0] = get32 (buffer + 32); kid[1] = get32 (buffer + 36); return 1; } /* Return information on the flag WHAT within the blob BUFFER,LENGTH. Return the offset and the length (in bytes) of the flag in FLAGOFF,FLAG_SIZE. */ gpg_err_code_t _keybox_get_flag_location (const unsigned char *buffer, size_t length, int what, size_t *flag_off, size_t *flag_size) { size_t pos; size_t nkeys, keyinfolen; size_t nuids, uidinfolen; size_t nserial; size_t nsigs, siginfolen, siginfooff; switch (what) { case KEYBOX_FLAG_BLOB: if (length < 8) return GPG_ERR_INV_OBJ; *flag_off = 6; *flag_size = 2; break; case KEYBOX_FLAG_OWNERTRUST: case KEYBOX_FLAG_VALIDITY: case KEYBOX_FLAG_CREATED_AT: case KEYBOX_FLAG_SIG_INFO: if (length < 20) return GPG_ERR_INV_OBJ; /* Key info. */ nkeys = get16 (buffer + 16); keyinfolen = get16 (buffer + 18 ); if (keyinfolen < 28) return GPG_ERR_INV_OBJ; pos = 20 + keyinfolen*nkeys; if (pos+2 > length) return GPG_ERR_INV_OBJ; /* Out of bounds. */ /* Serial number. */ nserial = get16 (buffer+pos); pos += 2 + nserial; if (pos+4 > length) return GPG_ERR_INV_OBJ; /* Out of bounds. */ /* User IDs. */ nuids = get16 (buffer + pos); pos += 2; uidinfolen = get16 (buffer + pos); pos += 2; if (uidinfolen < 12 ) return GPG_ERR_INV_OBJ; pos += uidinfolen*nuids; if (pos+4 > length) return GPG_ERR_INV_OBJ ; /* Out of bounds. */ /* Signature info. */ siginfooff = pos; nsigs = get16 (buffer + pos); pos += 2; siginfolen = get16 (buffer + pos); pos += 2; if (siginfolen < 4 ) return GPG_ERR_INV_OBJ; pos += siginfolen*nsigs; if (pos+1+1+2+4+4+4+4 > length) return GPG_ERR_INV_OBJ ; /* Out of bounds. */ *flag_size = 1; *flag_off = pos; switch (what) { case KEYBOX_FLAG_VALIDITY: *flag_off += 1; break; case KEYBOX_FLAG_CREATED_AT: *flag_size = 4; *flag_off += 1+2+4+4+4; break; case KEYBOX_FLAG_SIG_INFO: *flag_size = siginfolen * nsigs; *flag_off = siginfooff; break; default: break; } break; default: return GPG_ERR_INV_FLAG; } return 0; } /* Return one of the flags WHAT in VALUE from teh blob BUFFER of LENGTH bytes. Return 0 on success or an raw error code. */ static gpg_err_code_t get_flag_from_image (const unsigned char *buffer, size_t length, int what, unsigned int *value) { gpg_err_code_t ec; size_t pos, size; *value = 0; ec = _keybox_get_flag_location (buffer, length, what, &pos, &size); if (!ec) switch (size) { case 1: *value = buffer[pos]; break; case 2: *value = get16 (buffer + pos); break; case 4: *value = get32 (buffer + pos); break; default: ec = GPG_ERR_BUG; break; } return ec; } static int blob_cmp_sn (KEYBOXBLOB blob, const unsigned char *sn, int snlen) { const unsigned char *buffer; size_t length; size_t pos, off; size_t nkeys, keyinfolen; size_t nserial; buffer = _keybox_get_blob_image (blob, &length); if (length < 40) return 0; /* blob too short */ /*keys*/ nkeys = get16 (buffer + 16); keyinfolen = get16 (buffer + 18 ); if (keyinfolen < 28) return 0; /* invalid blob */ pos = 20 + keyinfolen*nkeys; if (pos+2 > length) return 0; /* out of bounds */ /*serial*/ nserial = get16 (buffer+pos); off = pos + 2; if (off+nserial > length) return 0; /* out of bounds */ return nserial == snlen && !memcmp (buffer+off, sn, snlen); } /* Returns 0 if not found or the number of the key which was found. For X.509 this is always 1, for OpenPGP this is 1 for the primary key and 2 and more for the subkeys. */ static int blob_cmp_fpr (KEYBOXBLOB blob, const unsigned char *fpr) { const unsigned char *buffer; size_t length; size_t pos, off; size_t nkeys, keyinfolen; int idx; buffer = _keybox_get_blob_image (blob, &length); if (length < 40) return 0; /* blob too short */ /*keys*/ nkeys = get16 (buffer + 16); keyinfolen = get16 (buffer + 18 ); if (keyinfolen < 28) return 0; /* invalid blob */ pos = 20; if (pos + keyinfolen*nkeys > length) return 0; /* out of bounds */ for (idx=0; idx < nkeys; idx++) { off = pos + idx*keyinfolen; if (!memcmp (buffer + off, fpr, 20)) return idx+1; /* found */ } return 0; /* not found */ } static int blob_cmp_fpr_part (KEYBOXBLOB blob, const unsigned char *fpr, int fproff, int fprlen) { const unsigned char *buffer; size_t length; size_t pos, off; size_t nkeys, keyinfolen; int idx; buffer = _keybox_get_blob_image (blob, &length); if (length < 40) return 0; /* blob too short */ /*keys*/ nkeys = get16 (buffer + 16); keyinfolen = get16 (buffer + 18 ); if (keyinfolen < 28) return 0; /* invalid blob */ pos = 20; if (pos + keyinfolen*nkeys > length) return 0; /* out of bounds */ for (idx=0; idx < nkeys; idx++) { off = pos + idx*keyinfolen; if (!memcmp (buffer + off + fproff, fpr, fprlen)) return idx+1; /* found */ } return 0; /* not found */ } static int blob_cmp_name (KEYBOXBLOB blob, int idx, const char *name, size_t namelen, int substr, int x509) { const unsigned char *buffer; size_t length; size_t pos, off, len; size_t nkeys, keyinfolen; size_t nuids, uidinfolen; size_t nserial; buffer = _keybox_get_blob_image (blob, &length); if (length < 40) return 0; /* blob too short */ /*keys*/ nkeys = get16 (buffer + 16); keyinfolen = get16 (buffer + 18 ); if (keyinfolen < 28) return 0; /* invalid blob */ pos = 20 + keyinfolen*nkeys; if (pos+2 > length) return 0; /* out of bounds */ /*serial*/ nserial = get16 (buffer+pos); pos += 2 + nserial; if (pos+4 > length) return 0; /* out of bounds */ /* user ids*/ nuids = get16 (buffer + pos); pos += 2; uidinfolen = get16 (buffer + pos); pos += 2; if (uidinfolen < 12 /* should add a: || nuidinfolen > MAX_UIDINFOLEN */) return 0; /* invalid blob */ if (pos + uidinfolen*nuids > length) return 0; /* out of bounds */ if (idx < 0) { /* Compare all names. Note that for X.509 we start with index 1 so to skip the issuer at index 0. */ for (idx = !!x509; idx < nuids; idx++) { size_t mypos = pos; mypos += idx*uidinfolen; off = get32 (buffer+mypos); len = get32 (buffer+mypos+4); if (off+len > length) return 0; /* error: better stop here out of bounds */ if (len < 1) continue; /* empty name */ if (substr) { if (ascii_memcasemem (buffer+off, len, name, namelen)) return idx+1; /* found */ } else { if (len == namelen && !memcmp (buffer+off, name, len)) return idx+1; /* found */ } } } else { if (idx > nuids) return 0; /* no user ID with that idx */ pos += idx*uidinfolen; off = get32 (buffer+pos); len = get32 (buffer+pos+4); if (off+len > length) return 0; /* out of bounds */ if (len < 1) return 0; /* empty name */ if (substr) { if (ascii_memcasemem (buffer+off, len, name, namelen)) return idx+1; /* found */ } else { if (len == namelen && !memcmp (buffer+off, name, len)) return idx+1; /* found */ } } return 0; /* not found */ } /* Compare all email addresses of the subject. With SUBSTR given as True a substring search is done in the mail address. If X509 states whether thr search is done on an X.509 blob. */ static int blob_cmp_mail (KEYBOXBLOB blob, const char *name, size_t namelen, int substr, int x509) { const unsigned char *buffer; size_t length; size_t pos, off, len; size_t nkeys, keyinfolen; size_t nuids, uidinfolen; size_t nserial; int idx; /* fixme: this code is common to blob_cmp_mail */ buffer = _keybox_get_blob_image (blob, &length); if (length < 40) return 0; /* blob too short */ /*keys*/ nkeys = get16 (buffer + 16); keyinfolen = get16 (buffer + 18 ); if (keyinfolen < 28) return 0; /* invalid blob */ pos = 20 + keyinfolen*nkeys; if (pos+2 > length) return 0; /* out of bounds */ /*serial*/ nserial = get16 (buffer+pos); pos += 2 + nserial; if (pos+4 > length) return 0; /* out of bounds */ /* user ids*/ nuids = get16 (buffer + pos); pos += 2; uidinfolen = get16 (buffer + pos); pos += 2; if (uidinfolen < 12 /* should add a: || nuidinfolen > MAX_UIDINFOLEN */) return 0; /* invalid blob */ if (pos + uidinfolen*nuids > length) return 0; /* out of bounds */ if (namelen < 1) return 0; /* Note that for X.509 we start at index 1 becuase index 0 is used for the issuer name. */ for (idx=!!x509 ;idx < nuids; idx++) { size_t mypos = pos; mypos += idx*uidinfolen; off = get32 (buffer+mypos); len = get32 (buffer+mypos+4); if (off+len > length) return 0; /* error: better stop here out of bounds */ if (!x509) { /* For OpenPGP we need to forward to the mailbox part. */ for ( ;len && buffer[off] != '<'; len--, off++) ; } if (len < 2 || buffer[off] != '<') continue; /* empty name or trailing 0 not stored */ len--; /* one back */ if ( len < 3 || buffer[off+len] != '>') continue; /* not a proper email address */ len--; if (substr) { if (ascii_memcasemem (buffer+off+1, len, name, namelen)) return idx+1; /* found */ } else { if (len == namelen && !ascii_memcasecmp (buffer+off+1, name, len)) return idx+1; /* found */ } } return 0; /* not found */ } #ifdef KEYBOX_WITH_X509 /* Return true if the key in BLOB matches the 20 bytes keygrip GRIP. We don't have the keygrips as meta data, thus we need to parse the certificate. Fixme: We might want to return proper error codes instead of failing a search for invalid certificates etc. */ static int blob_x509_has_grip (KEYBOXBLOB blob, const unsigned char *grip) { int rc; const unsigned char *buffer; size_t length; size_t cert_off, cert_len; ksba_reader_t reader = NULL; ksba_cert_t cert = NULL; ksba_sexp_t p = NULL; gcry_sexp_t s_pkey; unsigned char array[20]; unsigned char *rcp; size_t n; buffer = _keybox_get_blob_image (blob, &length); if (length < 40) return 0; /* Too short. */ cert_off = get32 (buffer+8); cert_len = get32 (buffer+12); if (cert_off+cert_len > length) return 0; /* Too short. */ rc = ksba_reader_new (&reader); if (rc) return 0; /* Problem with ksba. */ rc = ksba_reader_set_mem (reader, buffer+cert_off, cert_len); if (rc) goto failed; rc = ksba_cert_new (&cert); if (rc) goto failed; rc = ksba_cert_read_der (cert, reader); if (rc) goto failed; p = ksba_cert_get_public_key (cert); if (!p) goto failed; n = gcry_sexp_canon_len (p, 0, NULL, NULL); if (!n) goto failed; rc = gcry_sexp_sscan (&s_pkey, NULL, (char*)p, n); if (rc) { gcry_sexp_release (s_pkey); goto failed; } rcp = gcry_pk_get_keygrip (s_pkey, array); gcry_sexp_release (s_pkey); if (!rcp) goto failed; /* Can't calculate keygrip. */ xfree (p); ksba_cert_release (cert); ksba_reader_release (reader); return !memcmp (array, grip, 20); failed: xfree (p); ksba_cert_release (cert); ksba_reader_release (reader); return 0; } #endif /*KEYBOX_WITH_X509*/ /* The has_foo functions are used as helpers for search */ static inline int has_short_kid (KEYBOXBLOB blob, u32 lkid) { unsigned char buf[4]; buf[0] = lkid >> 24; buf[1] = lkid >> 16; buf[2] = lkid >> 8; buf[3] = lkid; return blob_cmp_fpr_part (blob, buf, 16, 4); } static inline int has_long_kid (KEYBOXBLOB blob, u32 mkid, u32 lkid) { unsigned char buf[8]; buf[0] = mkid >> 24; buf[1] = mkid >> 16; buf[2] = mkid >> 8; buf[3] = mkid; buf[4] = lkid >> 24; buf[5] = lkid >> 16; buf[6] = lkid >> 8; buf[7] = lkid; return blob_cmp_fpr_part (blob, buf, 12, 8); } static inline int has_fingerprint (KEYBOXBLOB blob, const unsigned char *fpr) { return blob_cmp_fpr (blob, fpr); } static inline int has_keygrip (KEYBOXBLOB blob, const unsigned char *grip) { #ifdef KEYBOX_WITH_X509 if (blob_get_type (blob) == KEYBOX_BLOBTYPE_X509) return blob_x509_has_grip (blob, grip); #endif return 0; } static inline int has_issuer (KEYBOXBLOB blob, const char *name) { size_t namelen; return_val_if_fail (name, 0); if (blob_get_type (blob) != KEYBOX_BLOBTYPE_X509) return 0; namelen = strlen (name); return blob_cmp_name (blob, 0 /* issuer */, name, namelen, 0, 1); } static inline int has_issuer_sn (KEYBOXBLOB blob, const char *name, const unsigned char *sn, int snlen) { size_t namelen; return_val_if_fail (name, 0); return_val_if_fail (sn, 0); if (blob_get_type (blob) != KEYBOX_BLOBTYPE_X509) return 0; namelen = strlen (name); return (blob_cmp_sn (blob, sn, snlen) && blob_cmp_name (blob, 0 /* issuer */, name, namelen, 0, 1)); } static inline int has_sn (KEYBOXBLOB blob, const unsigned char *sn, int snlen) { return_val_if_fail (sn, 0); if (blob_get_type (blob) != KEYBOX_BLOBTYPE_X509) return 0; return blob_cmp_sn (blob, sn, snlen); } static inline int has_subject (KEYBOXBLOB blob, const char *name) { size_t namelen; return_val_if_fail (name, 0); if (blob_get_type (blob) != KEYBOX_BLOBTYPE_X509) return 0; namelen = strlen (name); return blob_cmp_name (blob, 1 /* subject */, name, namelen, 0, 1); } static inline int has_username (KEYBOXBLOB blob, const char *name, int substr) { size_t namelen; int btype; return_val_if_fail (name, 0); btype = blob_get_type (blob); if (btype != KEYBOX_BLOBTYPE_PGP && btype != KEYBOX_BLOBTYPE_X509) return 0; namelen = strlen (name); return blob_cmp_name (blob, -1 /* all subject/user names */, name, namelen, substr, (btype == KEYBOX_BLOBTYPE_X509)); } static inline int has_mail (KEYBOXBLOB blob, const char *name, int substr) { size_t namelen; int btype; return_val_if_fail (name, 0); btype = blob_get_type (blob); if (btype != KEYBOX_BLOBTYPE_PGP && btype != KEYBOX_BLOBTYPE_X509) return 0; if (btype == KEYBOX_BLOBTYPE_PGP && *name == '<') name++; /* Hack to remove the leading '<' for gpg. */ namelen = strlen (name); if (namelen && name[namelen-1] == '>') namelen--; return blob_cmp_mail (blob, name, namelen, substr, (btype == KEYBOX_BLOBTYPE_X509)); } static void release_sn_array (struct sn_array_s *array, size_t size) { size_t n; for (n=0; n < size; n++) xfree (array[n].sn); xfree (array); } /* The search API */ int keybox_search_reset (KEYBOX_HANDLE hd) { if (!hd) return gpg_error (GPG_ERR_INV_VALUE); if (hd->found.blob) { _keybox_release_blob (hd->found.blob); hd->found.blob = NULL; } if (hd->fp) { fclose (hd->fp); hd->fp = NULL; } hd->error = 0; hd->eof = 0; return 0; } /* Note: When in ephemeral mode the search function does visit all blobs but in standard mode, blobs flagged as ephemeral are ignored. If WANT_BLOBTYPE is not 0 only blobs of this type are considered. The value at R_SKIPPED is updated by the number of skipped long records (counts PGP and X.509). */ int keybox_search (KEYBOX_HANDLE hd, KEYBOX_SEARCH_DESC *desc, size_t ndesc, keybox_blobtype_t want_blobtype, size_t *r_descindex, unsigned long *r_skipped) { int rc; size_t n; int need_words, any_skip; KEYBOXBLOB blob = NULL; struct sn_array_s *sn_array = NULL; int pk_no, uid_no; if (!hd) return gpg_error (GPG_ERR_INV_VALUE); /* clear last found result */ if (hd->found.blob) { _keybox_release_blob (hd->found.blob); hd->found.blob = NULL; } if (hd->error) return hd->error; /* still in error state */ if (hd->eof) return -1; /* still EOF */ /* figure out what information we need */ need_words = any_skip = 0; for (n=0; n < ndesc; n++) { switch (desc[n].mode) { case KEYDB_SEARCH_MODE_WORDS: need_words = 1; break; case KEYDB_SEARCH_MODE_FIRST: /* always restart the search in this mode */ keybox_search_reset (hd); break; default: break; } if (desc[n].skipfnc) any_skip = 1; if (desc[n].snlen == -1 && !sn_array) { sn_array = xtrycalloc (ndesc, sizeof *sn_array); if (!sn_array) return (hd->error = gpg_error_from_syserror ()); } } (void)need_words; /* Not yet implemented. */ if (!hd->fp) { hd->fp = fopen (hd->kb->fname, "rb"); if (!hd->fp) { hd->error = gpg_error_from_syserror (); xfree (sn_array); return hd->error; } } /* Kludge: We need to convert an SN given as hexstring to its binary representation - in some cases we are not able to store it in the search descriptor, because due to the way we use it, it is not possible to free allocated memory. */ if (sn_array) { const unsigned char *s; int i, odd; size_t snlen; for (n=0; n < ndesc; n++) { if (!desc[n].sn) ; else if (desc[n].snlen == -1) { unsigned char *sn; s = desc[n].sn; for (i=0; *s && *s != '/'; s++, i++) ; odd = (i & 1); snlen = (i+1)/2; sn_array[n].sn = xtrymalloc (snlen); if (!sn_array[n].sn) { hd->error = gpg_error_from_syserror (); release_sn_array (sn_array, n); return hd->error; } sn_array[n].snlen = snlen; sn = sn_array[n].sn; s = desc[n].sn; if (odd) { *sn++ = xtoi_1 (s); s++; } for (; *s && *s != '/'; s += 2) *sn++ = xtoi_2 (s); } else { const unsigned char *sn; sn = desc[n].sn; snlen = desc[n].snlen; sn_array[n].sn = xtrymalloc (snlen); if (!sn_array[n].sn) { hd->error = gpg_error_from_syserror (); release_sn_array (sn_array, n); return hd->error; } sn_array[n].snlen = snlen; memcpy (sn_array[n].sn, sn, snlen); } } } pk_no = uid_no = 0; for (;;) { unsigned int blobflags; int blobtype; _keybox_release_blob (blob); blob = NULL; rc = _keybox_read_blob (&blob, hd->fp); if (gpg_err_code (rc) == GPG_ERR_TOO_LARGE && gpg_err_source (rc) == GPG_ERR_SOURCE_KEYBOX) { ++*r_skipped; continue; /* Skip too large records. */ } if (rc) break; blobtype = blob_get_type (blob); if (blobtype == KEYBOX_BLOBTYPE_HEADER) continue; if (want_blobtype && blobtype != want_blobtype) continue; blobflags = blob_get_blob_flags (blob); if (!hd->ephemeral && (blobflags & 2)) continue; /* Not in ephemeral mode but blob is flagged ephemeral. */ for (n=0; n < ndesc; n++) { switch (desc[n].mode) { case KEYDB_SEARCH_MODE_NONE: never_reached (); break; case KEYDB_SEARCH_MODE_EXACT: uid_no = has_username (blob, desc[n].u.name, 0); if (uid_no) goto found; break; case KEYDB_SEARCH_MODE_MAIL: uid_no = has_mail (blob, desc[n].u.name, 0); if (uid_no) goto found; break; case KEYDB_SEARCH_MODE_MAILSUB: uid_no = has_mail (blob, desc[n].u.name, 1); if (uid_no) goto found; break; case KEYDB_SEARCH_MODE_SUBSTR: uid_no = has_username (blob, desc[n].u.name, 1); if (uid_no) goto found; break; case KEYDB_SEARCH_MODE_MAILEND: case KEYDB_SEARCH_MODE_WORDS: /* not yet implemented */ break; case KEYDB_SEARCH_MODE_ISSUER: if (has_issuer (blob, desc[n].u.name)) goto found; break; case KEYDB_SEARCH_MODE_ISSUER_SN: if (has_issuer_sn (blob, desc[n].u.name, sn_array? sn_array[n].sn : desc[n].sn, sn_array? sn_array[n].snlen : desc[n].snlen)) goto found; break; case KEYDB_SEARCH_MODE_SN: if (has_sn (blob, sn_array? sn_array[n].sn : desc[n].sn, sn_array? sn_array[n].snlen : desc[n].snlen)) goto found; break; case KEYDB_SEARCH_MODE_SUBJECT: if (has_subject (blob, desc[n].u.name)) goto found; break; case KEYDB_SEARCH_MODE_SHORT_KID: pk_no = has_short_kid (blob, desc[n].u.kid[1]); if (pk_no) goto found; break; case KEYDB_SEARCH_MODE_LONG_KID: pk_no = has_long_kid (blob, desc[n].u.kid[0], desc[n].u.kid[1]); if (pk_no) goto found; break; case KEYDB_SEARCH_MODE_FPR: case KEYDB_SEARCH_MODE_FPR20: pk_no = has_fingerprint (blob, desc[n].u.fpr); if (pk_no) goto found; break; case KEYDB_SEARCH_MODE_KEYGRIP: if (has_keygrip (blob, desc[n].u.grip)) goto found; break; case KEYDB_SEARCH_MODE_FIRST: goto found; break; case KEYDB_SEARCH_MODE_NEXT: goto found; break; default: rc = gpg_error (GPG_ERR_INV_VALUE); goto found; } } continue; found: /* Record which DESC we matched on. Note this value is only meaningful if this function returns with no errors. */ if(r_descindex) *r_descindex = n; for (n=any_skip?0:ndesc; n < ndesc; n++) { u32 kid[2]; if (desc[n].skipfnc && blob_get_first_keyid (blob, kid) && desc[n].skipfnc (desc[n].skipfncvalue, kid, NULL)) break; } if (n == ndesc) break; /* got it */ } if (!rc) { hd->found.blob = blob; hd->found.pk_no = pk_no; hd->found.uid_no = uid_no; } else if (rc == -1) { _keybox_release_blob (blob); hd->eof = 1; } else { _keybox_release_blob (blob); hd->error = rc; } if (sn_array) release_sn_array (sn_array, ndesc); return rc; } /* Functions to return a certificate or a keyblock. To be used after a successful search operation. */ /* Return the last found keyblock. Returns 0 on success and stores a new iobuf at R_IOBUF and a signature status vector at R_SIGSTATUS in that case. R_UID_NO and R_PK_NO are used to retun the number of the key or user id which was matched the search criteria; if not known they are set to 0. */ gpg_error_t keybox_get_keyblock (KEYBOX_HANDLE hd, iobuf_t *r_iobuf, int *r_pk_no, int *r_uid_no, u32 **r_sigstatus) { gpg_error_t err; const unsigned char *buffer, *p; size_t length; size_t image_off, image_len; size_t siginfo_off, siginfo_len; u32 *sigstatus, n, n_sigs, sigilen; *r_iobuf = NULL; *r_sigstatus = NULL; if (!hd) return gpg_error (GPG_ERR_INV_VALUE); if (!hd->found.blob) return gpg_error (GPG_ERR_NOTHING_FOUND); if (blob_get_type (hd->found.blob) != KEYBOX_BLOBTYPE_PGP) return gpg_error (GPG_ERR_WRONG_BLOB_TYPE); buffer = _keybox_get_blob_image (hd->found.blob, &length); if (length < 40) return gpg_error (GPG_ERR_TOO_SHORT); image_off = get32 (buffer+8); image_len = get32 (buffer+12); if (image_off+image_len > length) return gpg_error (GPG_ERR_TOO_SHORT); err = _keybox_get_flag_location (buffer, length, KEYBOX_FLAG_SIG_INFO, &siginfo_off, &siginfo_len); if (err) return err; n_sigs = get16 (buffer + siginfo_off); sigilen = get16 (buffer + siginfo_off + 2); p = buffer + siginfo_off + 4; sigstatus = xtrymalloc ((1+n_sigs) * sizeof *sigstatus); if (!sigstatus) return gpg_error_from_syserror (); sigstatus[0] = n_sigs; for (n=1; n <= n_sigs; n++, p += sigilen) sigstatus[n] = get32 (p); *r_pk_no = hd->found.pk_no; *r_uid_no = hd->found.uid_no; *r_sigstatus = sigstatus; *r_iobuf = iobuf_temp_with_content (buffer+image_off, image_len); return 0; } #ifdef KEYBOX_WITH_X509 /* Return the last found cert. Caller must free it. */ int keybox_get_cert (KEYBOX_HANDLE hd, ksba_cert_t *r_cert) { const unsigned char *buffer; size_t length; size_t cert_off, cert_len; ksba_reader_t reader = NULL; ksba_cert_t cert = NULL; int rc; if (!hd) return gpg_error (GPG_ERR_INV_VALUE); if (!hd->found.blob) return gpg_error (GPG_ERR_NOTHING_FOUND); if (blob_get_type (hd->found.blob) != KEYBOX_BLOBTYPE_X509) return gpg_error (GPG_ERR_WRONG_BLOB_TYPE); buffer = _keybox_get_blob_image (hd->found.blob, &length); if (length < 40) return gpg_error (GPG_ERR_TOO_SHORT); cert_off = get32 (buffer+8); cert_len = get32 (buffer+12); if (cert_off+cert_len > length) return gpg_error (GPG_ERR_TOO_SHORT); rc = ksba_reader_new (&reader); if (rc) return rc; rc = ksba_reader_set_mem (reader, buffer+cert_off, cert_len); if (rc) { ksba_reader_release (reader); /* fixme: need to map the error codes */ return gpg_error (GPG_ERR_GENERAL); } rc = ksba_cert_new (&cert); if (rc) { ksba_reader_release (reader); return rc; } rc = ksba_cert_read_der (cert, reader); if (rc) { ksba_cert_release (cert); ksba_reader_release (reader); /* fixme: need to map the error codes */ return gpg_error (GPG_ERR_GENERAL); } *r_cert = cert; ksba_reader_release (reader); return 0; } #endif /*KEYBOX_WITH_X509*/ /* Return the flags named WHAT at the address of VALUE. IDX is used only for certain flags and should be 0 if not required. */ int keybox_get_flags (KEYBOX_HANDLE hd, int what, int idx, unsigned int *value) { const unsigned char *buffer; size_t length; gpg_err_code_t ec; (void)idx; /* Not yet used. */ if (!hd) return gpg_error (GPG_ERR_INV_VALUE); if (!hd->found.blob) return gpg_error (GPG_ERR_NOTHING_FOUND); buffer = _keybox_get_blob_image (hd->found.blob, &length); ec = get_flag_from_image (buffer, length, what, value); return ec? gpg_error (ec):0; } diff --git a/kbx/keybox-update.c b/kbx/keybox-update.c index 7b207a520..4b14b2f23 100644 --- a/kbx/keybox-update.c +++ b/kbx/keybox-update.c @@ -1,860 +1,859 @@ /* keybox-update.c - keybox update operations * Copyright (C) 2001, 2003, 2004, 2012 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 "keybox-defs.h" #include "../common/sysutils.h" +#include "../common/host2net.h" #define EXTSEP_S "." #define FILECOPY_INSERT 1 #define FILECOPY_DELETE 2 #define FILECOPY_UPDATE 3 #if !defined(HAVE_FSEEKO) && !defined(fseeko) #ifdef HAVE_LIMITS_H # include #endif #ifndef LONG_MAX # define LONG_MAX ((long) ((unsigned long) -1 >> 1)) #endif #ifndef LONG_MIN # define LONG_MIN (-1 - LONG_MAX) #endif /**************** * A substitute for fseeko, for hosts that don't have it. */ static int fseeko (FILE * stream, off_t newpos, int whence) { while (newpos != (long) newpos) { long pos = newpos < 0 ? LONG_MIN : LONG_MAX; if (fseek (stream, pos, whence) != 0) return -1; newpos -= pos; whence = SEEK_CUR; } return fseek (stream, (long) newpos, whence); } #endif /* !defined(HAVE_FSEEKO) && !defined(fseeko) */ static int create_tmp_file (const char *template, char **r_bakfname, char **r_tmpfname, FILE **r_fp) { char *bakfname, *tmpfname; *r_bakfname = NULL; *r_tmpfname = NULL; # ifdef USE_ONLY_8DOT3 /* Here is another Windoze bug?: * you cant rename("pubring.kbx.tmp", "pubring.kbx"); * but rename("pubring.kbx.tmp", "pubring.aaa"); * works. So we replace ".kbx" by ".kb_" or ".k__". Note that we * can't use ".bak" and ".tmp", because these suffixes are used by * gpg and would lead to a sharing violation or data corruption. */ if (strlen (template) > 4 && !strcmp (template+strlen(template)-4, EXTSEP_S "kbx") ) { bakfname = xtrymalloc (strlen (template) + 1); if (!bakfname) return gpg_error_from_syserror (); strcpy (bakfname, template); strcpy (bakfname+strlen(template)-4, EXTSEP_S "kb_"); tmpfname = xtrymalloc (strlen (template) + 1); if (!tmpfname) { gpg_error_t tmperr = gpg_error_from_syserror (); xfree (bakfname); return tmperr; } strcpy (tmpfname,template); strcpy (tmpfname + strlen (template)-4, EXTSEP_S "k__"); } else { /* File does not end with kbx, thus we hope we are working on a modern file system and appending a suffix works. */ bakfname = xtrymalloc ( strlen (template) + 5); if (!bakfname) return gpg_error_from_syserror (); strcpy (stpcpy (bakfname, template), EXTSEP_S "kb_"); tmpfname = xtrymalloc ( strlen (template) + 5); if (!tmpfname) { gpg_error_t tmperr = gpg_error_from_syserror (); xfree (bakfname); return tmperr; } strcpy (stpcpy (tmpfname, template), EXTSEP_S "k__"); } # else /* Posix file names */ bakfname = xtrymalloc (strlen (template) + 2); if (!bakfname) return gpg_error_from_syserror (); strcpy (stpcpy (bakfname,template),"~"); tmpfname = xtrymalloc ( strlen (template) + 5); if (!tmpfname) { gpg_error_t tmperr = gpg_error_from_syserror (); xfree (bakfname); return tmperr; } strcpy (stpcpy (tmpfname,template), EXTSEP_S "tmp"); # endif /* Posix filename */ *r_fp = fopen (tmpfname, "wb"); if (!*r_fp) { gpg_error_t tmperr = gpg_error_from_syserror (); xfree (tmpfname); xfree (bakfname); return tmperr; } *r_bakfname = bakfname; *r_tmpfname = tmpfname; return 0; } static int rename_tmp_file (const char *bakfname, const char *tmpfname, const char *fname, int secret ) { int rc=0; /* restrict the permissions for secret keyboxs */ #ifndef HAVE_DOSISH_SYSTEM /* if (secret && !opt.preserve_permissions) */ /* { */ /* if (chmod (tmpfname, S_IRUSR | S_IWUSR) ) */ /* { */ /* log_debug ("chmod of '%s' failed: %s\n", */ /* tmpfname, strerror(errno) ); */ /* return KEYBOX_Write_File; */ /* } */ /* } */ #endif /* fixme: invalidate close caches (not used with stdio)*/ /* iobuf_ioctl (NULL, IOBUF_IOCTL_INVALIDATE_CACHE, 0, (char*)tmpfname ); */ /* iobuf_ioctl (NULL, IOBUF_IOCTL_INVALIDATE_CACHE, 0, (char*)bakfname ); */ /* iobuf_ioctl (NULL, IOBUF_IOCTL_INVALIDATE_CACHE, 0, (char*)fname ); */ /* First make a backup file except for secret keyboxes. */ if (!secret) { #if defined(HAVE_DOSISH_SYSTEM) || defined(__riscos__) gnupg_remove (bakfname); #endif if (rename (fname, bakfname) ) { return gpg_error_from_syserror (); } } /* Then rename the file. */ #if defined(HAVE_DOSISH_SYSTEM) || defined(__riscos__) gnupg_remove (fname); #endif if (rename (tmpfname, fname) ) { rc = gpg_error_from_syserror (); if (secret) { /* log_info ("WARNING: 2 files with confidential" */ /* " information exists.\n"); */ /* log_info ("%s is the unchanged one\n", fname ); */ /* log_info ("%s is the new one\n", tmpfname ); */ /* log_info ("Please fix this possible security flaw\n"); */ } return rc; } return 0; } /* Perform insert/delete/update operation. MODE is one of FILECOPY_INSERT, FILECOPY_DELETE, FILECOPY_UPDATE. FOR_OPENPGP indicates that this is called due to an OpenPGP keyblock change. */ static int blob_filecopy (int mode, const char *fname, KEYBOXBLOB blob, int secret, int for_openpgp, off_t start_offset) { FILE *fp, *newfp; int rc=0; char *bakfname = NULL; char *tmpfname = NULL; char buffer[4096]; /* (Must be at least 32 bytes) */ int nread, nbytes; /* Open the source file. Because we do a rename, we have to check the permissions of the file */ if (access (fname, W_OK)) return gpg_error_from_syserror (); fp = fopen (fname, "rb"); if (mode == FILECOPY_INSERT && !fp && errno == ENOENT) { /* Insert mode but file does not exist: Create a new keybox file. */ newfp = fopen (fname, "wb"); if (!newfp ) return gpg_error_from_syserror (); rc = _keybox_write_header_blob (newfp, for_openpgp); if (rc) { fclose (newfp); return rc; } rc = _keybox_write_blob (blob, newfp); if (rc) { fclose (newfp); return rc; } if ( fclose (newfp) ) return gpg_error_from_syserror (); /* if (chmod( fname, S_IRUSR | S_IWUSR )) */ /* { */ /* log_debug ("%s: chmod failed: %s\n", fname, strerror(errno) ); */ /* return KEYBOX_File_Error; */ /* } */ return 0; /* Ready. */ } if (!fp) { rc = gpg_error_from_syserror (); goto leave; } /* Create the new file. */ rc = create_tmp_file (fname, &bakfname, &tmpfname, &newfp); if (rc) { fclose (fp); fclose (newfp); goto leave; } /* prepare for insert */ if (mode == FILECOPY_INSERT) { int first_record = 1; /* Copy everything to the new file. If this is for OpenPGP, we make sure that the openpgp flag is set in the header. (We failsafe the blob type.) */ while ( (nread = fread (buffer, 1, DIM(buffer), fp)) > 0 ) { if (first_record && for_openpgp && buffer[4] == KEYBOX_BLOBTYPE_HEADER) { first_record = 0; buffer[7] |= 0x02; /* OpenPGP data may be available. */ } if (fwrite (buffer, nread, 1, newfp) != 1) { rc = gpg_error_from_syserror (); fclose (fp); fclose (newfp); goto leave; } } if (ferror (fp)) { rc = gpg_error_from_syserror (); fclose (fp); fclose (newfp); goto leave; } } /* Prepare for delete or update. */ if ( mode == FILECOPY_DELETE || mode == FILECOPY_UPDATE ) { off_t current = 0; /* Copy first part to the new file. */ while ( current < start_offset ) { nbytes = DIM(buffer); if (current + nbytes > start_offset) nbytes = start_offset - current; nread = fread (buffer, 1, nbytes, fp); if (!nread) break; current += nread; if (fwrite (buffer, nread, 1, newfp) != 1) { rc = gpg_error_from_syserror (); fclose (fp); fclose (newfp); goto leave; } } if (ferror (fp)) { rc = gpg_error_from_syserror (); fclose (fp); fclose (newfp); goto leave; } /* Skip this blob. */ rc = _keybox_read_blob (NULL, fp); if (rc) { fclose (fp); fclose (newfp); return rc; } } /* Do an insert or update. */ if ( mode == FILECOPY_INSERT || mode == FILECOPY_UPDATE ) { rc = _keybox_write_blob (blob, newfp); if (rc) { fclose (fp); fclose (newfp); return rc; } } /* Copy the rest of the packet for an delete or update. */ if (mode == FILECOPY_DELETE || mode == FILECOPY_UPDATE) { while ( (nread = fread (buffer, 1, DIM(buffer), fp)) > 0 ) { if (fwrite (buffer, nread, 1, newfp) != 1) { rc = gpg_error_from_syserror (); fclose (fp); fclose (newfp); goto leave; } } if (ferror (fp)) { rc = gpg_error_from_syserror (); fclose (fp); fclose (newfp); goto leave; } } /* Close both files. */ if (fclose(fp)) { rc = gpg_error_from_syserror (); fclose (newfp); goto leave; } if (fclose(newfp)) { rc = gpg_error_from_syserror (); goto leave; } rc = rename_tmp_file (bakfname, tmpfname, fname, secret); leave: xfree(bakfname); xfree(tmpfname); return rc; } /* Insert the OpenPGP keyblock {IMAGE,IMAGELEN} into HD. SIGSTATUS is a vector describing the status of the signatures; its first element gives the number of following elements. */ gpg_error_t keybox_insert_keyblock (KEYBOX_HANDLE hd, const void *image, size_t imagelen, u32 *sigstatus) { gpg_error_t err; const char *fname; KEYBOXBLOB blob; size_t nparsed; struct _keybox_openpgp_info info; if (!hd) return gpg_error (GPG_ERR_INV_HANDLE); if (!hd->kb) return gpg_error (GPG_ERR_INV_HANDLE); fname = hd->kb->fname; if (!fname) return gpg_error (GPG_ERR_INV_HANDLE); /* Close this one otherwise we will mess up the position for a next search. Fixme: it would be better to adjust the position after the write operation. */ _keybox_close_file (hd); err = _keybox_parse_openpgp (image, imagelen, &nparsed, &info); if (err) return err; assert (nparsed <= imagelen); err = _keybox_create_openpgp_blob (&blob, &info, image, imagelen, sigstatus, hd->ephemeral); _keybox_destroy_openpgp_info (&info); if (!err) { err = blob_filecopy (FILECOPY_INSERT, fname, blob, hd->secret, 1, 0); _keybox_release_blob (blob); /* if (!rc && !hd->secret && kb_offtbl) */ /* { */ /* update_offset_hash_table_from_kb (kb_offtbl, kb, 0); */ /* } */ } return err; } /* Update the current key at HD with the given OpenPGP keyblock in {IMAGE,IMAGELEN}. */ gpg_error_t keybox_update_keyblock (KEYBOX_HANDLE hd, const void *image, size_t imagelen) { gpg_error_t err; const char *fname; off_t off; KEYBOXBLOB blob; size_t nparsed; struct _keybox_openpgp_info info; if (!hd || !image || !imagelen) return gpg_error (GPG_ERR_INV_VALUE); if (!hd->found.blob) return gpg_error (GPG_ERR_NOTHING_FOUND); if (blob_get_type (hd->found.blob) != KEYBOX_BLOBTYPE_PGP) return gpg_error (GPG_ERR_WRONG_BLOB_TYPE); fname = hd->kb->fname; if (!fname) return gpg_error (GPG_ERR_INV_HANDLE); off = _keybox_get_blob_fileoffset (hd->found.blob); if (off == (off_t)-1) return gpg_error (GPG_ERR_GENERAL); /* Close this the file so that we do no mess up the position for a next search. */ _keybox_close_file (hd); /* Build a new blob. */ err = _keybox_parse_openpgp (image, imagelen, &nparsed, &info); if (err) return err; assert (nparsed <= imagelen); err = _keybox_create_openpgp_blob (&blob, &info, image, imagelen, NULL, hd->ephemeral); _keybox_destroy_openpgp_info (&info); /* Update the keyblock. */ if (!err) { err = blob_filecopy (FILECOPY_UPDATE, fname, blob, hd->secret, 1, off); _keybox_release_blob (blob); } return err; } #ifdef KEYBOX_WITH_X509 int keybox_insert_cert (KEYBOX_HANDLE hd, ksba_cert_t cert, unsigned char *sha1_digest) { int rc; const char *fname; KEYBOXBLOB blob; if (!hd) return gpg_error (GPG_ERR_INV_HANDLE); if (!hd->kb) return gpg_error (GPG_ERR_INV_HANDLE); fname = hd->kb->fname; if (!fname) return gpg_error (GPG_ERR_INV_HANDLE); /* Close this one otherwise we will mess up the position for a next search. Fixme: it would be better to adjust the position after the write operation. */ _keybox_close_file (hd); rc = _keybox_create_x509_blob (&blob, cert, sha1_digest, hd->ephemeral); if (!rc) { rc = blob_filecopy (FILECOPY_INSERT, fname, blob, hd->secret, 0, 0); _keybox_release_blob (blob); /* if (!rc && !hd->secret && kb_offtbl) */ /* { */ /* update_offset_hash_table_from_kb (kb_offtbl, kb, 0); */ /* } */ } return rc; } int keybox_update_cert (KEYBOX_HANDLE hd, ksba_cert_t cert, unsigned char *sha1_digest) { (void)hd; (void)cert; (void)sha1_digest; return -1; } #endif /*KEYBOX_WITH_X509*/ /* Note: We assume that the keybox has been locked before the current search was executed. This is needed so that we can depend on the offset information of the flags. */ int keybox_set_flags (KEYBOX_HANDLE hd, int what, int idx, unsigned int value) { off_t off; const char *fname; FILE *fp; gpg_err_code_t ec; size_t flag_pos, flag_size; const unsigned char *buffer; size_t length; (void)idx; /* Not yet used. */ if (!hd) return gpg_error (GPG_ERR_INV_VALUE); if (!hd->found.blob) return gpg_error (GPG_ERR_NOTHING_FOUND); if (!hd->kb) return gpg_error (GPG_ERR_INV_HANDLE); if (!hd->found.blob) return gpg_error (GPG_ERR_NOTHING_FOUND); fname = hd->kb->fname; if (!fname) return gpg_error (GPG_ERR_INV_HANDLE); off = _keybox_get_blob_fileoffset (hd->found.blob); if (off == (off_t)-1) return gpg_error (GPG_ERR_GENERAL); buffer = _keybox_get_blob_image (hd->found.blob, &length); ec = _keybox_get_flag_location (buffer, length, what, &flag_pos, &flag_size); if (ec) return gpg_error (ec); off += flag_pos; _keybox_close_file (hd); fp = fopen (hd->kb->fname, "r+b"); if (!fp) return gpg_error_from_syserror (); ec = 0; if (fseeko (fp, off, SEEK_SET)) ec = gpg_error_from_syserror (); else { unsigned char tmp[4]; tmp[0] = value >> 24; tmp[1] = value >> 16; tmp[2] = value >> 8; tmp[3] = value; switch (flag_size) { case 1: case 2: case 4: if (fwrite (tmp+4-flag_size, flag_size, 1, fp) != 1) ec = gpg_err_code_from_syserror (); break; default: ec = GPG_ERR_BUG; break; } } if (fclose (fp)) { if (!ec) ec = gpg_err_code_from_syserror (); } return gpg_error (ec); } int keybox_delete (KEYBOX_HANDLE hd) { off_t off; const char *fname; FILE *fp; int rc; if (!hd) return gpg_error (GPG_ERR_INV_VALUE); if (!hd->found.blob) return gpg_error (GPG_ERR_NOTHING_FOUND); if (!hd->kb) return gpg_error (GPG_ERR_INV_HANDLE); fname = hd->kb->fname; if (!fname) return gpg_error (GPG_ERR_INV_HANDLE); off = _keybox_get_blob_fileoffset (hd->found.blob); if (off == (off_t)-1) return gpg_error (GPG_ERR_GENERAL); off += 4; _keybox_close_file (hd); fp = fopen (hd->kb->fname, "r+b"); if (!fp) return gpg_error_from_syserror (); if (fseeko (fp, off, SEEK_SET)) rc = gpg_error_from_syserror (); else if (putc (0, fp) == EOF) rc = gpg_error_from_syserror (); else rc = 0; if (fclose (fp)) { if (!rc) rc = gpg_error_from_syserror (); } return rc; } /* Compress the keybox file. This should be run with the file locked. */ int keybox_compress (KEYBOX_HANDLE hd) { int read_rc, rc; const char *fname; FILE *fp, *newfp; char *bakfname = NULL; char *tmpfname = NULL; int first_blob; KEYBOXBLOB blob = NULL; u32 cut_time; int any_changes = 0; int skipped_deleted; if (!hd) return gpg_error (GPG_ERR_INV_HANDLE); if (!hd->kb) return gpg_error (GPG_ERR_INV_HANDLE); if (hd->secret) return gpg_error (GPG_ERR_NOT_IMPLEMENTED); fname = hd->kb->fname; if (!fname) return gpg_error (GPG_ERR_INV_HANDLE); _keybox_close_file (hd); /* Open the source file. Because we do a rename, we have to check the permissions of the file */ if (access (fname, W_OK)) return gpg_error_from_syserror (); fp = fopen (fname, "rb"); if (!fp && errno == ENOENT) return 0; /* Ready. File has been deleted right after the access above. */ if (!fp) { rc = gpg_error_from_syserror (); return rc; } /* A quick test to see if we need to compress the file at all. We schedule a compress run after 3 hours. */ if ( !_keybox_read_blob (&blob, fp) ) { const unsigned char *buffer; size_t length; buffer = _keybox_get_blob_image (blob, &length); if (length > 4 && buffer[4] == KEYBOX_BLOBTYPE_HEADER) { - u32 last_maint = ((buffer[20] << 24) | (buffer[20+1] << 16) - | (buffer[20+2] << 8) | (buffer[20+3])); + u32 last_maint = buf32_to_u32 (buffer+20); if ( (last_maint + 3*3600) > time (NULL) ) { fclose (fp); _keybox_release_blob (blob); return 0; /* Compress run not yet needed. */ } } _keybox_release_blob (blob); fseek (fp, 0, SEEK_SET); clearerr (fp); } /* Create the new file. */ rc = create_tmp_file (fname, &bakfname, &tmpfname, &newfp); if (rc) { fclose (fp); return rc;; } /* Processing loop. By reading using _keybox_read_blob we automagically skip any blobs flagged as deleted. Thus what we only have to do is to check all ephemeral flagged blocks whether their time has come and write out all other blobs. */ cut_time = time(NULL) - 86400; first_blob = 1; skipped_deleted = 0; for (rc=0; !(read_rc = _keybox_read_blob2 (&blob, fp, &skipped_deleted)); _keybox_release_blob (blob), blob = NULL ) { unsigned int blobflags; const unsigned char *buffer; size_t length, pos, size; u32 created_at; if (skipped_deleted) any_changes = 1; buffer = _keybox_get_blob_image (blob, &length); if (first_blob) { first_blob = 0; if (length > 4 && buffer[4] == KEYBOX_BLOBTYPE_HEADER) { /* Write out the blob with an updated maintenance time stamp and if needed (ie. used by gpg) set the openpgp flag. */ _keybox_update_header_blob (blob, hd->for_openpgp); rc = _keybox_write_blob (blob, newfp); if (rc) break; continue; } /* The header blob is missing. Insert it. */ rc = _keybox_write_header_blob (newfp, hd->for_openpgp); if (rc) break; any_changes = 1; } else if (length > 4 && buffer[4] == KEYBOX_BLOBTYPE_HEADER) { /* Oops: There is another header record - remove it. */ any_changes = 1; continue; } if (_keybox_get_flag_location (buffer, length, KEYBOX_FLAG_BLOB, &pos, &size) || size != 2) { rc = gpg_error (GPG_ERR_BUG); break; } - blobflags = ((buffer[pos] << 8) | (buffer[pos+1])); + blobflags = buf16_to_uint (buffer+pos); if ((blobflags & KEYBOX_FLAG_BLOB_EPHEMERAL)) { /* This is an ephemeral blob. */ if (_keybox_get_flag_location (buffer, length, KEYBOX_FLAG_CREATED_AT, &pos, &size) || size != 4) created_at = 0; /* oops. */ else - created_at = ((buffer[pos] << 24) | (buffer[pos+1] << 16) - | (buffer[pos+2] << 8) | (buffer[pos+3])); + created_at = buf32_to_u32 (buffer+pos); if (created_at && created_at < cut_time) { any_changes = 1; continue; /* Skip this blob. */ } } rc = _keybox_write_blob (blob, newfp); if (rc) break; } if (skipped_deleted) any_changes = 1; _keybox_release_blob (blob); blob = NULL; if (!rc && read_rc == -1) rc = 0; else if (!rc) rc = read_rc; /* Close both files. */ if (fclose(fp) && !rc) rc = gpg_error_from_syserror (); if (fclose(newfp) && !rc) rc = gpg_error_from_syserror (); /* Rename or remove the temporary file. */ if (rc || !any_changes) gnupg_remove (tmpfname); else rc = rename_tmp_file (bakfname, tmpfname, fname, hd->secret); xfree(bakfname); xfree(tmpfname); return rc; } diff --git a/scd/apdu.c b/scd/apdu.c index 4ec6b4d00..e5db4f096 100644 --- a/scd/apdu.c +++ b/scd/apdu.c @@ -1,4308 +1,4302 @@ /* 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 "options.h" #include "errors.h" #include "memory.h" #include "util.h" #include "i18n.h" #include "dynload.h" #include "cardglue.h" #else /* GNUPG_MAJOR_VERSION != 1 */ #include "scdaemon.h" #include "exechelp.h" #endif /* GNUPG_MAJOR_VERSION != 1 */ +#include "host2net.h" #include "iso7816.h" #include "apdu.h" #define CCID_DRIVER_INCLUDE_USB_IDS 1 #include "ccid-driver.h" /* Due to conflicting use of threading libraries we usually can't link against libpcsclite if we are using Pth. Instead we use a wrapper program. Note that with nPth there is no need for a wrapper. */ #ifdef USE_PTH /* Right, plain old Pth. */ #if !defined(HAVE_W32_SYSTEM) && !defined(__CYGWIN__) #define NEED_PCSC_WRAPPER 1 #endif #endif #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 intialized to the various backends. */ int (*connect_card)(int); int (*disconnect_card)(int); int (*close_reader)(int); int (*shutdown_reader)(int); int (*reset_reader)(int); int (*get_status_reader)(int, unsigned 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 (*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; #ifdef NEED_PCSC_WRAPPER int req_fd; int rsp_fd; pid_t pid; #endif /*NEED_PCSC_WRAPPER*/ } 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. */ int any_status; /* True if we have seen any status. */ int last_status; int status; int is_t0; /* True if we know that we are running T=0. */ int is_spr532; /* True if we know that the reader is a SPR532. */ int pinpad_varlen_supported; /* True if we know that the reader supports variable length pinpad input. */ 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. */ unsigned int change_counter; #ifdef USE_NPTH int lock_initialized; 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]; /* ct API function pointer. */ static char (* DLSTDCALL CT_init) (unsigned short ctn, unsigned short Pn); static char (* DLSTDCALL CT_data) (unsigned short ctn, unsigned char *dad, unsigned char *sad, unsigned short lc, unsigned char *cmd, unsigned short *lr, unsigned char *rsp); static char (* DLSTDCALL CT_close) (unsigned short ctn); /* 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_W_REMOVED_CARD 0x80100069 /* Fix pcsc-lite ABI incompatibilty. */ #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); static int reset_pcsc_reader (int slot); static int apdu_get_status_internal (int slot, int hang, int no_atr_reset, unsigned int *status, unsigned int *changed); 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; int err; for (i=0; i < MAX_READER; i++) { if (!reader_table[i].used && reader == -1) reader = i; } if (reader == -1) { log_error ("new_reader_slot: out of slots\n"); return -1; } #ifdef USE_NPTH if (!reader_table[reader].lock_initialized) { err = npth_mutex_init (&reader_table[reader].lock, NULL); if (err) { log_error ("error initializing mutex: %s\n", strerror (err)); return -1; } reader_table[reader].lock_initialized = 1; } #endif /*USE_NPTH*/ if (lock_slot (reader)) { log_error ("error locking mutex: %s\n", strerror (errno)); return -1; } reader_table[reader].connect_card = NULL; reader_table[reader].disconnect_card = NULL; reader_table[reader].close_reader = NULL; reader_table[reader].shutdown_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].pinpad_verify = pcsc_pinpad_verify; reader_table[reader].pinpad_modify = pcsc_pinpad_modify; reader_table[reader].used = 1; reader_table[reader].any_status = 0; reader_table[reader].last_status = 0; reader_table[reader].is_t0 = 1; reader_table[reader].is_spr532 = 0; reader_table[reader].pinpad_varlen_supported = 0; #ifdef NEED_PCSC_WRAPPER reader_table[reader].pcsc.req_fd = -1; reader_table[reader].pcsc.rsp_fd = -1; reader_table[reader].pcsc.pid = (pid_t)(-1); #endif 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; 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].status != -1 && reader_table[slot].atrlen) { log_info ("slot %d: ATR=", slot); 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"; 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"; } } /* ct API Interface */ static const char * ct_error_string (long err) { switch (err) { case 0: return "okay"; case -1: return "invalid data"; case -8: return "ct error"; case -10: return "transmission error"; case -11: return "memory allocation error"; case -128: return "HTSI error"; default: return "unknown CT-API error"; } } static void ct_dump_reader_status (int slot) { log_info ("reader slot %d: %s\n", slot, reader_table[slot].status == 1? "Processor ICC present" : reader_table[slot].status == 0? "Memory ICC present" : "ICC not present" ); } /* Wait for the card in SLOT and activate it. Return a status word error or 0 on success. */ static int ct_activate_card (int slot) { int rc; unsigned char dad[1], sad[1], cmd[11], buf[256]; unsigned short buflen; /* Check whether card has been inserted. */ dad[0] = 1; /* Destination address: CT. */ sad[0] = 2; /* Source address: Host. */ cmd[0] = 0x20; /* Class byte. */ cmd[1] = 0x13; /* Request status. */ cmd[2] = 0x00; /* From kernel. */ cmd[3] = 0x80; /* Return card's DO. */ cmd[4] = 0x00; buflen = DIM(buf); rc = CT_data (slot, dad, sad, 5, cmd, &buflen, buf); if (rc || buflen < 2 || buf[buflen-2] != 0x90) { log_error ("ct_activate_card: can't get status of reader %d: %s\n", slot, ct_error_string (rc)); return SW_HOST_CARD_IO_ERROR; } /* Connected, now activate the card. */ dad[0] = 1; /* Destination address: CT. */ sad[0] = 2; /* Source address: Host. */ cmd[0] = 0x20; /* Class byte. */ cmd[1] = 0x12; /* Request ICC. */ cmd[2] = 0x01; /* From first interface. */ cmd[3] = 0x01; /* Return card's ATR. */ cmd[4] = 0x00; buflen = DIM(buf); rc = CT_data (slot, dad, sad, 5, cmd, &buflen, buf); if (rc || buflen < 2 || buf[buflen-2] != 0x90) { log_error ("ct_activate_card(%d): activation failed: %s\n", slot, ct_error_string (rc)); if (!rc) log_printhex (" received data:", buf, buflen); return SW_HOST_CARD_IO_ERROR; } /* Store the type and the ATR. */ if (buflen - 2 > DIM (reader_table[0].atr)) { log_error ("ct_activate_card(%d): ATR too long\n", slot); return SW_HOST_CARD_IO_ERROR; } reader_table[slot].status = buf[buflen - 1]; memcpy (reader_table[slot].atr, buf, buflen - 2); reader_table[slot].atrlen = buflen - 2; return 0; } static int close_ct_reader (int slot) { CT_close (slot); reader_table[slot].used = 0; return 0; } static int reset_ct_reader (int slot) { /* FIXME: Check is this is sufficient do do a reset. */ return ct_activate_card (slot); } static int ct_get_status (int slot, unsigned int *status) { (void)slot; /* The status we returned is wrong but we don't care becuase ctAPI is not anymore required. */ *status = APDU_CARD_USABLE|APDU_CARD_PRESENT|APDU_CARD_ACTIVE; return 0; } /* Actually send the APDU of length APDULEN to SLOT and return a maximum of *BUFLEN data in BUFFER, the actual retruned size will be set to BUFLEN. Returns: CT API error code. */ static int ct_send_apdu (int slot, unsigned char *apdu, size_t apdulen, unsigned char *buffer, size_t *buflen, pininfo_t *pininfo) { int rc; unsigned char dad[1], sad[1]; unsigned short ctbuflen; (void)pininfo; /* If we don't have an ATR, we need to reset the reader first. */ if (!reader_table[slot].atrlen && (rc = reset_ct_reader (slot))) return rc; dad[0] = 0; /* Destination address: Card. */ sad[0] = 2; /* Source address: Host. */ ctbuflen = *buflen; if (DBG_CARD_IO) log_printhex (" CT_data:", apdu, apdulen); rc = CT_data (slot, dad, sad, apdulen, apdu, &ctbuflen, buffer); *buflen = ctbuflen; return rc? SW_HOST_CARD_IO_ERROR: 0; } /* Open a reader and return an internal handle for it. PORT is a non-negative value with the port number of the reader. USB readers do have port numbers starting at 32769. */ static int open_ct_reader (int port) { int rc, reader; if (port < 0 || port > 0xffff) { log_error ("open_ct_reader: invalid port %d requested\n", port); return -1; } reader = new_reader_slot (); if (reader == -1) return reader; reader_table[reader].port = port; rc = CT_init (reader, (unsigned short)port); if (rc) { log_error ("apdu_open_ct_reader failed on port %d: %s\n", port, ct_error_string (rc)); reader_table[reader].used = 0; unlock_slot (reader); return -1; } /* Only try to activate the card. */ rc = ct_activate_card (reader); if (rc) { reader_table[reader].atrlen = 0; rc = 0; } reader_table[reader].close_reader = close_ct_reader; reader_table[reader].reset_reader = reset_ct_reader; reader_table[reader].get_status_reader = ct_get_status; reader_table[reader].send_apdu_reader = ct_send_apdu; reader_table[reader].check_pinpad = NULL; reader_table[reader].dump_status_reader = ct_dump_reader_status; reader_table[reader].pinpad_verify = NULL; reader_table[reader].pinpad_modify = NULL; dump_reader_status (reader); unlock_slot (reader); return reader; } /* PC/SC Interface */ #ifdef NEED_PCSC_WRAPPER static int writen (int fd, const void *buf, size_t nbytes) { size_t nleft = nbytes; int nwritten; /* log_printhex (" writen:", buf, nbytes); */ while (nleft > 0) { #ifdef USE_NPTH nwritten = npth_write (fd, buf, nleft); #else nwritten = write (fd, buf, nleft); #endif if (nwritten < 0 && errno == EINTR) continue; if (nwritten < 0) return -1; nleft -= nwritten; buf = (const char*)buf + nwritten; } return 0; } /* Read up to BUFLEN bytes from FD and return the number of bytes actually read in NREAD. Returns -1 on error or 0 on success. */ static int readn (int fd, void *buf, size_t buflen, size_t *nread) { size_t nleft = buflen; int n; /* void *orig_buf = buf; */ while (nleft > 0) { #ifdef USE_NPTH # ifdef HAVE_W32_SYSTEM # error Cannot use npth_read here because it expects a system HANDLE. # endif n = npth_read (fd, buf, nleft); #else n = read (fd, buf, nleft); #endif if (n < 0 && errno == EINTR) continue; if (n < 0) return -1; /* read error. */ if (!n) break; /* EOF */ nleft -= n; buf = (char*)buf + n; } if (nread) *nread = buflen - nleft; /* log_printhex (" readn:", orig_buf, *nread); */ return 0; } #endif /*NEED_PCSC_WRAPPER*/ 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_ABORTED; 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_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); } #ifndef NEED_PCSC_WRAPPER static int pcsc_get_status_direct (int slot, unsigned int *status) { long err; struct pcsc_readerstate_s rdrstates[1]; memset (rdrstates, 0, sizeof *rdrstates); rdrstates[0].reader = reader_table[slot].rdrname; rdrstates[0].current_state = PCSC_STATE_UNAWARE; err = pcsc_get_status_change (reader_table[slot].pcsc.context, 0, rdrstates, 1); if (err == PCSC_E_TIMEOUT) err = 0; /* Timeout is no error 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); } /* 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)? " unuse":"", */ /* (rdrstates[0].event_state & PCSC_STATE_MUTE)? " mute":"" ); */ *status = 0; if ( (rdrstates[0].event_state & PCSC_STATE_PRESENT) ) { *status |= APDU_CARD_PRESENT; if ( !(rdrstates[0].event_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) && !(rdrstates[0].event_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 return 0; } #endif /*!NEED_PCSC_WRAPPER*/ #ifdef NEED_PCSC_WRAPPER static int pcsc_get_status_wrapped (int slot, unsigned int *status) { long err; reader_table_t slotp; size_t len, full_len; int i, n; unsigned char msgbuf[9]; unsigned char buffer[16]; int sw = SW_HOST_CARD_IO_ERROR; slotp = reader_table + slot; if (slotp->pcsc.req_fd == -1 || slotp->pcsc.rsp_fd == -1 || slotp->pcsc.pid == (pid_t)(-1) ) { log_error ("pcsc_get_status: pcsc-wrapper not running\n"); return sw; } msgbuf[0] = 0x04; /* STATUS command. */ len = 0; msgbuf[1] = (len >> 24); msgbuf[2] = (len >> 16); msgbuf[3] = (len >> 8); msgbuf[4] = (len ); if ( writen (slotp->pcsc.req_fd, msgbuf, 5) ) { log_error ("error sending PC/SC STATUS request: %s\n", strerror (errno)); goto command_failed; } /* Read the response. */ if ((i=readn (slotp->pcsc.rsp_fd, msgbuf, 9, &len)) || len != 9) { log_error ("error receiving PC/SC STATUS response: %s\n", i? strerror (errno) : "premature EOF"); goto command_failed; } - len = (msgbuf[1] << 24) | (msgbuf[2] << 16) | (msgbuf[3] << 8 ) | msgbuf[4]; + len = buf_to_size_t (msgbuf+1); if (msgbuf[0] != 0x81 || len < 4) { log_error ("invalid response header from PC/SC received\n"); goto command_failed; } len -= 4; /* Already read the error code. */ - err = PCSC_ERR_MASK ((msgbuf[5] << 24) | (msgbuf[6] << 16) - | (msgbuf[7] << 8 ) | msgbuf[8]); + err = PCSC_ERR_MASK (buf32_to_ulong (msgbuf+5)); if (err) { log_error ("pcsc_status failed: %s (0x%lx)\n", pcsc_error_string (err), err); /* This is a proper error code, so return immediately. */ return pcsc_error_to_sw (err); } full_len = len; /* The current version returns 3 words but we allow also for old versions returning only 2 words. */ n = 12 < len ? 12 : len; if ((i=readn (slotp->pcsc.rsp_fd, buffer, n, &len)) || (len != 8 && len != 12)) { log_error ("error receiving PC/SC STATUS response: %s\n", i? strerror (errno) : "premature EOF"); goto command_failed; } slotp->is_t0 = (len == 12 && !!(buffer[11] & PCSC_PROTOCOL_T0)); full_len -= len; /* Newer versions of the wrapper might send more status bytes. Read them. */ while (full_len) { unsigned char dummybuf[128]; n = full_len < DIM (dummybuf) ? full_len : DIM (dummybuf); if ((i=readn (slotp->pcsc.rsp_fd, dummybuf, n, &len)) || len != n) { log_error ("error receiving PC/SC TRANSMIT response: %s\n", i? strerror (errno) : "premature EOF"); goto command_failed; } full_len -= n; } /* We are lucky: The wrapper already returns the data in the required format. */ *status = buffer[3]; return 0; command_failed: close (slotp->pcsc.req_fd); close (slotp->pcsc.rsp_fd); slotp->pcsc.req_fd = -1; slotp->pcsc.rsp_fd = -1; if (slotp->pcsc.pid != -1) kill (slotp->pcsc.pid, SIGTERM); slotp->pcsc.pid = (pid_t)(-1); slotp->used = 0; return sw; } #endif /*NEED_PCSC_WRAPPER*/ static int pcsc_get_status (int slot, unsigned int *status) { #ifdef NEED_PCSC_WRAPPER return pcsc_get_status_wrapped (slot, status); #else return pcsc_get_status_direct (slot, status); #endif } #ifndef NEED_PCSC_WRAPPER static int pcsc_send_apdu_direct (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); 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); return pcsc_error_to_sw (err); } #endif /*!NEED_PCSC_WRAPPER*/ #ifdef NEED_PCSC_WRAPPER static int pcsc_send_apdu_wrapped (int slot, unsigned char *apdu, size_t apdulen, unsigned char *buffer, size_t *buflen, pininfo_t *pininfo) { long err; reader_table_t slotp; size_t len, full_len; int i, n; unsigned char msgbuf[9]; int sw = SW_HOST_CARD_IO_ERROR; (void)pininfo; if (!reader_table[slot].atrlen && (err = reset_pcsc_reader (slot))) return err; if (DBG_CARD_IO) log_printhex (" PCSC_data:", apdu, apdulen); slotp = reader_table + slot; if (slotp->pcsc.req_fd == -1 || slotp->pcsc.rsp_fd == -1 || slotp->pcsc.pid == (pid_t)(-1) ) { log_error ("pcsc_send_apdu: pcsc-wrapper not running\n"); return sw; } msgbuf[0] = 0x03; /* TRANSMIT command. */ len = apdulen; msgbuf[1] = (len >> 24); msgbuf[2] = (len >> 16); msgbuf[3] = (len >> 8); msgbuf[4] = (len ); if ( writen (slotp->pcsc.req_fd, msgbuf, 5) || writen (slotp->pcsc.req_fd, apdu, len)) { log_error ("error sending PC/SC TRANSMIT request: %s\n", strerror (errno)); goto command_failed; } /* Read the response. */ if ((i=readn (slotp->pcsc.rsp_fd, msgbuf, 9, &len)) || len != 9) { log_error ("error receiving PC/SC TRANSMIT response: %s\n", i? strerror (errno) : "premature EOF"); goto command_failed; } - len = (msgbuf[1] << 24) | (msgbuf[2] << 16) | (msgbuf[3] << 8 ) | msgbuf[4]; + len = buf_to_size_t (msgbuf+1); if (msgbuf[0] != 0x81 || len < 4) { log_error ("invalid response header from PC/SC received\n"); goto command_failed; } len -= 4; /* Already read the error code. */ - err = PCSC_ERR_MASK ((msgbuf[5] << 24) | (msgbuf[6] << 16) - | (msgbuf[7] << 8 ) | msgbuf[8]); + err = PCSC_ERR_MASK (buf32_to_ulong (msgbuf+5)); if (err) { log_error ("pcsc_transmit failed: %s (0x%lx)\n", pcsc_error_string (err), err); return pcsc_error_to_sw (err); } full_len = len; n = *buflen < len ? *buflen : len; if ((i=readn (slotp->pcsc.rsp_fd, buffer, n, &len)) || len != n) { log_error ("error receiving PC/SC TRANSMIT response: %s\n", i? strerror (errno) : "premature EOF"); goto command_failed; } *buflen = n; full_len -= len; if (full_len) { log_error ("pcsc_send_apdu: provided buffer too short - truncated\n"); err = SW_HOST_INV_VALUE; } /* We need to read any rest of the response, to keep the protocol running. */ while (full_len) { unsigned char dummybuf[128]; n = full_len < DIM (dummybuf) ? full_len : DIM (dummybuf); if ((i=readn (slotp->pcsc.rsp_fd, dummybuf, n, &len)) || len != n) { log_error ("error receiving PC/SC TRANSMIT response: %s\n", i? strerror (errno) : "premature EOF"); goto command_failed; } full_len -= n; } return err; command_failed: close (slotp->pcsc.req_fd); close (slotp->pcsc.rsp_fd); slotp->pcsc.req_fd = -1; slotp->pcsc.rsp_fd = -1; if (slotp->pcsc.pid != -1) kill (slotp->pcsc.pid, SIGTERM); slotp->pcsc.pid = (pid_t)(-1); slotp->used = 0; return sw; } #endif /*NEED_PCSC_WRAPPER*/ /* 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) { #ifdef NEED_PCSC_WRAPPER return pcsc_send_apdu_wrapped (slot, apdu, apdulen, buffer, buflen, pininfo); #else return pcsc_send_apdu_direct (slot, apdu, apdulen, buffer, buflen, pininfo); #endif } #ifndef NEED_PCSC_WRAPPER static int control_pcsc_direct (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); if (err) { log_error ("pcsc_control failed: %s (0x%lx)\n", pcsc_error_string (err), err); return pcsc_error_to_sw (err); } return 0; } #endif /*!NEED_PCSC_WRAPPER*/ #ifdef NEED_PCSC_WRAPPER static int control_pcsc_wrapped (int slot, pcsc_dword_t ioctl_code, const unsigned char *cntlbuf, size_t len, unsigned char *buffer, pcsc_dword_t *buflen) { long err = PCSC_E_NOT_TRANSACTED; reader_table_t slotp; unsigned char msgbuf[9]; int i, n; size_t full_len; slotp = reader_table + slot; msgbuf[0] = 0x06; /* CONTROL command. */ msgbuf[1] = ((len + 4) >> 24); msgbuf[2] = ((len + 4) >> 16); msgbuf[3] = ((len + 4) >> 8); msgbuf[4] = ((len + 4) ); msgbuf[5] = (ioctl_code >> 24); msgbuf[6] = (ioctl_code >> 16); msgbuf[7] = (ioctl_code >> 8); msgbuf[8] = (ioctl_code ); if ( writen (slotp->pcsc.req_fd, msgbuf, 9) || writen (slotp->pcsc.req_fd, cntlbuf, len)) { log_error ("error sending PC/SC CONTROL request: %s\n", strerror (errno)); goto command_failed; } /* Read the response. */ if ((i=readn (slotp->pcsc.rsp_fd, msgbuf, 9, &len)) || len != 9) { log_error ("error receiving PC/SC CONTROL response: %s\n", i? strerror (errno) : "premature EOF"); goto command_failed; } - len = (msgbuf[1] << 24) | (msgbuf[2] << 16) | (msgbuf[3] << 8 ) | msgbuf[4]; + len = buf32_to_size_t (msgbuf+1); if (msgbuf[0] != 0x81 || len < 4) { log_error ("invalid response header from PC/SC received\n"); goto command_failed; } len -= 4; /* Already read the error code. */ - err = PCSC_ERR_MASK ((msgbuf[5] << 24) | (msgbuf[6] << 16) - | (msgbuf[7] << 8 ) | msgbuf[8]); + err = PCSC_ERR_MASK (buf32_to_ulong (msgbuf+5)); if (err) { log_error ("pcsc_control failed: %s (0x%lx)\n", pcsc_error_string (err), err); return pcsc_error_to_sw (err); } full_len = len; n = *buflen < len ? *buflen : len; if ((i=readn (slotp->pcsc.rsp_fd, buffer, n, &len)) || len != n) { log_error ("error receiving PC/SC CONTROL response: %s\n", i? strerror (errno) : "premature EOF"); goto command_failed; } *buflen = n; full_len -= len; if (full_len) { log_error ("pcsc_send_apdu: provided buffer too short - truncated\n"); err = PCSC_E_INVALID_VALUE; } /* We need to read any rest of the response, to keep the protocol running. */ while (full_len) { unsigned char dummybuf[128]; n = full_len < DIM (dummybuf) ? full_len : DIM (dummybuf); if ((i=readn (slotp->pcsc.rsp_fd, dummybuf, n, &len)) || len != n) { log_error ("error receiving PC/SC CONTROL response: %s\n", i? strerror (errno) : "premature EOF"); goto command_failed; } full_len -= n; } if (!err) return 0; command_failed: close (slotp->pcsc.req_fd); close (slotp->pcsc.rsp_fd); slotp->pcsc.req_fd = -1; slotp->pcsc.rsp_fd = -1; if (slotp->pcsc.pid != -1) kill (slotp->pcsc.pid, SIGTERM); slotp->pcsc.pid = (pid_t)(-1); slotp->used = 0; return pcsc_error_to_sw (err); } #endif /*NEED_PCSC_WRAPPER*/ /* 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) { #ifdef NEED_PCSC_WRAPPER return control_pcsc_wrapped (slot, ioctl_code, cntlbuf, len, buffer, buflen); #else return control_pcsc_direct (slot, ioctl_code, cntlbuf, len, buffer, buflen); #endif } #ifndef NEED_PCSC_WRAPPER static int close_pcsc_reader_direct (int slot) { pcsc_release_context (reader_table[slot].pcsc.context); xfree (reader_table[slot].rdrname); reader_table[slot].rdrname = NULL; reader_table[slot].used = 0; return 0; } #endif /*!NEED_PCSC_WRAPPER*/ #ifdef NEED_PCSC_WRAPPER static int close_pcsc_reader_wrapped (int slot) { long err; reader_table_t slotp; size_t len; int i; unsigned char msgbuf[9]; slotp = reader_table + slot; if (slotp->pcsc.req_fd == -1 || slotp->pcsc.rsp_fd == -1 || slotp->pcsc.pid == (pid_t)(-1) ) { log_error ("close_pcsc_reader: pcsc-wrapper not running\n"); return 0; } msgbuf[0] = 0x02; /* CLOSE command. */ len = 0; msgbuf[1] = (len >> 24); msgbuf[2] = (len >> 16); msgbuf[3] = (len >> 8); msgbuf[4] = (len ); if ( writen (slotp->pcsc.req_fd, msgbuf, 5) ) { log_error ("error sending PC/SC CLOSE request: %s\n", strerror (errno)); goto command_failed; } /* Read the response. */ if ((i=readn (slotp->pcsc.rsp_fd, msgbuf, 9, &len)) || len != 9) { log_error ("error receiving PC/SC CLOSE response: %s\n", i? strerror (errno) : "premature EOF"); goto command_failed; } - len = (msgbuf[1] << 24) | (msgbuf[2] << 16) | (msgbuf[3] << 8 ) | msgbuf[4]; + len = buf32_to_size_t (msgbuf+1); if (msgbuf[0] != 0x81 || len < 4) { log_error ("invalid response header from PC/SC received\n"); goto command_failed; } len -= 4; /* Already read the error code. */ - err = PCSC_ERR_MASK ((msgbuf[5] << 24) | (msgbuf[6] << 16) - | (msgbuf[7] << 8 ) | msgbuf[8]); + err = PCSC_ERR_MASK (buf32_to_ulong (msgbuf+5)); if (err) log_error ("pcsc_close failed: %s (0x%lx)\n", pcsc_error_string (err), err); /* We will close the wrapper in any case - errors are merely informational. */ command_failed: close (slotp->pcsc.req_fd); close (slotp->pcsc.rsp_fd); slotp->pcsc.req_fd = -1; slotp->pcsc.rsp_fd = -1; if (slotp->pcsc.pid != -1) kill (slotp->pcsc.pid, SIGTERM); slotp->pcsc.pid = (pid_t)(-1); slotp->used = 0; return 0; } #endif /*NEED_PCSC_WRAPPER*/ static int close_pcsc_reader (int slot) { #ifdef NEED_PCSC_WRAPPER return close_pcsc_reader_wrapped (slot); #else return close_pcsc_reader_direct (slot); #endif } /* Connect a PC/SC card. */ #ifndef NEED_PCSC_WRAPPER 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].last_status = 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; /* If we got to here we know that a card is present and usable. Remember this. */ reader_table[slot].last_status = ( APDU_CARD_USABLE | APDU_CARD_PRESENT | APDU_CARD_ACTIVE); reader_table[slot].is_t0 = !!(card_protocol & PCSC_PROTOCOL_T0); } } dump_reader_status (slot); return pcsc_error_to_sw (err); } #endif /*!NEED_PCSC_WRAPPER*/ /* Disconnect a PC/SC card. Note that this succeeds even if the card is not connected. */ #ifndef NEED_PCSC_WRAPPER 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; } #endif /*!NEED_PCSC_WRAPPER*/ #ifndef NEED_PCSC_WRAPPER static int reset_pcsc_reader_direct (int slot) { int sw; sw = disconnect_pcsc_card (slot); if (!sw) sw = connect_pcsc_card (slot); return sw; } #endif /*NEED_PCSC_WRAPPER*/ #ifdef NEED_PCSC_WRAPPER static int reset_pcsc_reader_wrapped (int slot) { long err; reader_table_t slotp; size_t len; int i, n; unsigned char msgbuf[9]; unsigned int dummy_status; int sw = SW_HOST_CARD_IO_ERROR; slotp = reader_table + slot; if (slotp->pcsc.req_fd == -1 || slotp->pcsc.rsp_fd == -1 || slotp->pcsc.pid == (pid_t)(-1) ) { log_error ("pcsc_get_status: pcsc-wrapper not running\n"); return sw; } msgbuf[0] = 0x05; /* RESET command. */ len = 0; msgbuf[1] = (len >> 24); msgbuf[2] = (len >> 16); msgbuf[3] = (len >> 8); msgbuf[4] = (len ); if ( writen (slotp->pcsc.req_fd, msgbuf, 5) ) { log_error ("error sending PC/SC RESET request: %s\n", strerror (errno)); goto command_failed; } /* Read the response. */ if ((i=readn (slotp->pcsc.rsp_fd, msgbuf, 9, &len)) || len != 9) { log_error ("error receiving PC/SC RESET response: %s\n", i? strerror (errno) : "premature EOF"); goto command_failed; } - len = (msgbuf[1] << 24) | (msgbuf[2] << 16) | (msgbuf[3] << 8 ) | msgbuf[4]; + len = buf32_to_size_t (msgbuf+1); if (msgbuf[0] != 0x81 || len < 4) { log_error ("invalid response header from PC/SC received\n"); goto command_failed; } len -= 4; /* Already read the error code. */ if (len > DIM (slotp->atr)) { log_error ("PC/SC returned a too large ATR (len=%lx)\n", (unsigned long)len); sw = SW_HOST_GENERAL_ERROR; goto command_failed; } - err = PCSC_ERR_MASK ((msgbuf[5] << 24) | (msgbuf[6] << 16) - | (msgbuf[7] << 8 ) | msgbuf[8]); + err = PCSC_ERR_MASK (buf32_to_ulong (msgbuf+5)); if (err) { log_error ("PC/SC RESET failed: %s (0x%lx)\n", pcsc_error_string (err), err); /* If the error code is no smart card, we should not considere this a major error and close the wrapper. */ sw = pcsc_error_to_sw (err); if (err == PCSC_E_NO_SMARTCARD) return sw; goto command_failed; } /* The open function may return a zero for the ATR length to indicate that no card is present. */ n = len; if (n) { if ((i=readn (slotp->pcsc.rsp_fd, slotp->atr, n, &len)) || len != n) { log_error ("error receiving PC/SC RESET response: %s\n", i? strerror (errno) : "premature EOF"); goto command_failed; } } slotp->atrlen = len; /* Read the status so that IS_T0 will be set. */ pcsc_get_status (slot, &dummy_status); return 0; command_failed: close (slotp->pcsc.req_fd); close (slotp->pcsc.rsp_fd); slotp->pcsc.req_fd = -1; slotp->pcsc.rsp_fd = -1; if (slotp->pcsc.pid != -1) kill (slotp->pcsc.pid, SIGTERM); slotp->pcsc.pid = (pid_t)(-1); slotp->used = 0; return sw; } #endif /* !NEED_PCSC_WRAPPER */ /* Send an PC/SC reset command and return a status word on error or 0 on success. */ static int reset_pcsc_reader (int slot) { #ifdef NEED_PCSC_WRAPPER return reset_pcsc_reader_wrapped (slot); #else return reset_pcsc_reader_direct (slot); #endif } /* 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 = ((p[0] << 8) | p[1]); + v = buf16_to_uint (p); else if (l == 4) - v = ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]); + 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") || strstr (reader_table[slot].rdrname, "cyberJack") || strstr (reader_table[slot].rdrname, "DIGIPASS") || strstr (reader_table[slot].rdrname, "Gnuk") || strstr (reader_table[slot].rdrname, "KAAN")) 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 = ((p[1] << 8) | p[0]); + v = buf16_to_uint (p); else if (l == 4) - v = ((p[3] << 24) | (p[2] << 16) | (p[1] << 8) | p[0]); + v = buf32_to_uint (p); 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 && product == 0x003e) /* Cherry ST-2xxx */ || 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??? */) 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. */ #ifndef NEED_PCSC_WRAPPER static int open_pcsc_reader_direct (const char *portstr) { long err; int slot; char *list = 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; if (*p) log_info ("detected reader '%s'\n", p); if (nreader < (strlen (p)+1)) { log_error ("invalid response from pcsc_list_readers\n"); break; } nreader -= strlen (p)+1; p += strlen (p) + 1; } reader_table[slot].rdrname = xtrymalloc (strlen (portstr? portstr : list)+1); 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; } strcpy (reader_table[slot].rdrname, portstr? portstr : list); xfree (list); list = NULL; reader_table[slot].pcsc.card = 0; reader_table[slot].atrlen = 0; reader_table[slot].last_status = 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; } #endif /*!NEED_PCSC_WRAPPER */ /* Open the PC/SC reader using the pcsc_wrapper program. This is needed to cope with different thread models and other peculiarities of libpcsclite. */ #ifdef NEED_PCSC_WRAPPER static int open_pcsc_reader_wrapped (const char *portstr) { int slot; reader_table_t slotp; int fd, rp[2], wp[2]; int n, i; pid_t pid; size_t len; unsigned char msgbuf[9]; int err; unsigned int dummy_status; /* Note that we use the constant and not the fucntion because this code won't be be used under Windows. */ const char *wrapperpgm = GNUPG_LIBEXECDIR "/gnupg-pcsc-wrapper"; if (access (wrapperpgm, X_OK)) { log_error ("can't run PC/SC access module '%s': %s\n", wrapperpgm, strerror (errno)); return -1; } slot = new_reader_slot (); if (slot == -1) return -1; slotp = reader_table + slot; /* Fire up the PC/SCc wrapper. We don't use any fork/exec code from the common directy but implement it directly so that this file may still be source copied. */ if (pipe (rp) == -1) { log_error ("error creating a pipe: %s\n", strerror (errno)); slotp->used = 0; unlock_slot (slot); return -1; } if (pipe (wp) == -1) { log_error ("error creating a pipe: %s\n", strerror (errno)); close (rp[0]); close (rp[1]); slotp->used = 0; unlock_slot (slot); return -1; } pid = fork (); if (pid == -1) { log_error ("error forking process: %s\n", strerror (errno)); close (rp[0]); close (rp[1]); close (wp[0]); close (wp[1]); slotp->used = 0; unlock_slot (slot); return -1; } slotp->pcsc.pid = pid; if (!pid) { /* === Child === */ /* Double fork. */ pid = fork (); if (pid == -1) _exit (31); if (pid) _exit (0); /* Immediate exit this parent, so that the child gets cleaned up by the init process. */ /* Connect our pipes. */ if (wp[0] != 0 && dup2 (wp[0], 0) == -1) log_fatal ("dup2 stdin failed: %s\n", strerror (errno)); if (rp[1] != 1 && dup2 (rp[1], 1) == -1) log_fatal ("dup2 stdout failed: %s\n", strerror (errno)); /* Send stderr to the bit bucket. */ fd = open ("/dev/null", O_WRONLY); if (fd == -1) log_fatal ("can't open '/dev/null': %s", strerror (errno)); if (fd != 2 && dup2 (fd, 2) == -1) log_fatal ("dup2 stderr failed: %s\n", strerror (errno)); /* Close all other files. */ close_all_fds (3, NULL); execl (wrapperpgm, "pcsc-wrapper", "--", "1", /* API version */ opt.pcsc_driver, /* Name of the PC/SC library. */ NULL); _exit (31); } /* === Parent === */ close (wp[0]); close (rp[1]); slotp->pcsc.req_fd = wp[1]; slotp->pcsc.rsp_fd = rp[0]; /* Wait for the intermediate child to terminate. */ #ifdef USE_NPTH #define WAIT npth_waitpid #else #define WAIT waitpid #endif while ( (i=WAIT (pid, NULL, 0)) == -1 && errno == EINTR) ; #undef WAIT /* Now send the open request. */ msgbuf[0] = 0x01; /* OPEN command. */ len = portstr? strlen (portstr):0; msgbuf[1] = (len >> 24); msgbuf[2] = (len >> 16); msgbuf[3] = (len >> 8); msgbuf[4] = (len ); if ( writen (slotp->pcsc.req_fd, msgbuf, 5) || (portstr && writen (slotp->pcsc.req_fd, portstr, len))) { log_error ("error sending PC/SC OPEN request: %s\n", strerror (errno)); goto command_failed; } /* Read the response. */ if ((i=readn (slotp->pcsc.rsp_fd, msgbuf, 9, &len)) || len != 9) { log_error ("error receiving PC/SC OPEN response: %s\n", i? strerror (errno) : "premature EOF"); goto command_failed; } - len = (msgbuf[1] << 24) | (msgbuf[2] << 16) | (msgbuf[3] << 8 ) | msgbuf[4]; + len = buf32_to_size_t (msgbuf+1); if (msgbuf[0] != 0x81 || len < 4) { log_error ("invalid response header from PC/SC received\n"); goto command_failed; } len -= 4; /* Already read the error code. */ if (len > DIM (slotp->atr)) { log_error ("PC/SC returned a too large ATR (len=%lx)\n", (unsigned long)len); goto command_failed; } - err = PCSC_ERR_MASK ((msgbuf[5] << 24) | (msgbuf[6] << 16) - | (msgbuf[7] << 8 ) | msgbuf[8]); - + err = PCSC_ERR_MASK (buf32_to_ulong (msgbuf+5)); if (err) { log_error ("PC/SC OPEN failed: %s\n", pcsc_error_string (err)); goto command_failed; } slotp->last_status = 0; /* The open request may return a zero for the ATR length to indicate that no card is present. */ n = len; if (n) { if ((i=readn (slotp->pcsc.rsp_fd, slotp->atr, n, &len)) || len != n) { log_error ("error receiving PC/SC OPEN response: %s\n", i? strerror (errno) : "premature EOF"); goto command_failed; } /* If we got to here we know that a card is present and usable. Thus remember this. */ slotp->last_status = ( APDU_CARD_USABLE | APDU_CARD_PRESENT | APDU_CARD_ACTIVE); } slotp->atrlen = len; 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; pcsc_vendor_specific_init (slot); /* Read the status so that IS_T0 will be set. */ pcsc_get_status (slot, &dummy_status); dump_reader_status (slot); unlock_slot (slot); return slot; command_failed: close (slotp->pcsc.req_fd); close (slotp->pcsc.rsp_fd); slotp->pcsc.req_fd = -1; slotp->pcsc.rsp_fd = -1; if (slotp->pcsc.pid != -1) kill (slotp->pcsc.pid, SIGTERM); slotp->pcsc.pid = (pid_t)(-1); slotp->used = 0; unlock_slot (slot); /* There is no way to return SW. */ return -1; } #endif /*NEED_PCSC_WRAPPER*/ static int open_pcsc_reader (const char *portstr) { #ifdef NEED_PCSC_WRAPPER return open_pcsc_reader_wrapped (portstr); #else return open_pcsc_reader_direct (portstr); #endif } /* 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; unsigned char result[2]; pcsc_dword_t resultlen = 2; 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[2]; pcsc_dword_t resultlen = 2; 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); reader_table[slot].used = 0; return 0; } static int shutdown_ccid_reader (int slot) { ccid_shutdown_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 get_status_ccid (int slot, unsigned int *status) { int rc; int bits; rc = ccid_slot_status (reader_table[slot].ccid.handle, &bits); 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); 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 (const char *portstr) { int err; int slot; reader_table_t slotp; slot = new_reader_slot (); if (slot == -1) return -1; slotp = reader_table + slot; err = ccid_open_reader (&slotp->ccid.handle, portstr); if (err) { slotp->used = 0; unlock_slot (slot); return -1; } err = ccid_get_atr (slotp->ccid.handle, slotp->atr, sizeof slotp->atr, &slotp->atrlen); if (err) { slotp->atrlen = 0; err = 0; } else { /* If we got to here we know that a card is present and usable. Thus remember this. */ reader_table[slot].last_status = (APDU_CARD_USABLE | APDU_CARD_PRESENT | APDU_CARD_ACTIVE); } reader_table[slot].close_reader = close_ccid_reader; reader_table[slot].shutdown_reader = shutdown_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].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; 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); reader_table[slot].used = 0; 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 err; reader_table_t slotp; rapdu_msg_t msg = NULL; int oldslot; 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); 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 */ /* 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). */ int apdu_open_reader (const char *portstr) { static int pcsc_api_loaded, ct_api_loaded; int slot; if (DBG_READER) log_debug ("enter: apdu_open_reader: portstr=%s\n", portstr); #ifdef HAVE_LIBUSB if (!opt.disable_ccid) { static int once_available; int i; const char *s; slot = open_ccid_reader (portstr); if (slot != -1) { once_available = 1; if (DBG_READER) log_debug ("leave: apdu_open_reader => slot=%d [ccid]\n", slot); return slot; /* got one */ } /* If we ever loaded successfully loaded a CCID reader we never want to fallback to another driver. This solves a problem where ccid was used, the card unplugged and then scdaemon tries to find a new reader and will eventually try PC/SC over and over again. To reset this flag "gpgconf --kill scdaemon" can be used. */ if (once_available) { if (DBG_READER) log_debug ("leave: apdu_open_reader => slot=-1 (once_avail)\n"); return -1; } /* If a CCID reader specification has been given, the user does not want a fallback to other drivers. */ if (portstr) for (s=portstr, i=0; *s; s++) if (*s == ':' && (++i == 3)) { if (DBG_READER) log_debug ("leave: apdu_open_reader => slot=-1 (no ccid)\n"); return -1; } } #endif /* HAVE_LIBUSB */ if (opt.ctapi_driver && *opt.ctapi_driver) { int port = portstr? atoi (portstr) : 32768; if (!ct_api_loaded) { void *handle; handle = dlopen (opt.ctapi_driver, RTLD_LAZY); if (!handle) { log_error ("apdu_open_reader: failed to open driver: %s\n", dlerror ()); return -1; } CT_init = dlsym (handle, "CT_init"); CT_data = dlsym (handle, "CT_data"); CT_close = dlsym (handle, "CT_close"); if (!CT_init || !CT_data || !CT_close) { log_error ("apdu_open_reader: invalid CT-API driver\n"); dlclose (handle); return -1; } ct_api_loaded = 1; } return open_ct_reader (port); } /* No ctAPI configured, so lets try the PC/SC API */ if (!pcsc_api_loaded) { #ifndef NEED_PCSC_WRAPPER 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; } #endif /*!NEED_PCSC_WRAPPER*/ 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; } /* 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 availabe 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) { if (DBG_READER) log_debug ("leave: apdu_close_reader => 0x%x (apdu_disconnect)\n", sw); return sw; } if (reader_table[slot].close_reader) { sw = reader_table[slot].close_reader (slot); if (DBG_READER) log_debug ("leave: apdu_close_reader => 0x%x (close_reader)\n", sw); return sw; } 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; 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); reader_table[slot].used = 0; } sentinel = 0; } } /* Shutdown a reader; that is basically the same as a close but keeps the handle ready for later use. A apdu_reset_reader or apdu_connect should be used to get it active again. */ int apdu_shutdown_reader (int slot) { int sw; if (DBG_READER) log_debug ("enter: apdu_shutdown_reader: slot=%d\n", slot); if (slot < 0 || slot >= MAX_READER || !reader_table[slot].used ) { if (DBG_READER) log_debug ("leave: apdu_shutdown_reader => SW_HOST_NO_DRIVER\n"); return SW_HOST_NO_DRIVER; } sw = apdu_disconnect (slot); if (sw) { if (DBG_READER) log_debug ("leave: apdu_shutdown_reader => 0x%x (apdu_disconnect)\n", sw); return sw; } if (reader_table[slot].shutdown_reader) { sw = reader_table[slot].shutdown_reader (slot); if (DBG_READER) log_debug ("leave: apdu_shutdown_reader => 0x%x (close_reader)\n", sw); return sw; } if (DBG_READER) log_debug ("leave: apdu_shutdown_reader => SW_HOST_NOT_SUPPORTED\n"); return SW_HOST_NOT_SUPPORTED; } /* 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. */ int apdu_connect (int slot) { int sw; unsigned int status; 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 SW_HOST_NO_DRIVER; } /* 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); } } else sw = 0; /* 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. */ apdu_get_status_internal (slot, 1, 1, &status, NULL); 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 (DBG_READER) log_debug ("leave: apdu_connect => sw=0x%x\n", sw); return sw; } 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; } /* 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; } reader_table[slot].last_status = 0; if (reader_table[slot].reset_reader) sw = reader_table[slot].reset_reader (slot); if (!sw) { /* If we got to here we know that a card is present and usable. Thus remember this. */ reader_table[slot].last_status = (APDU_CARD_USABLE | APDU_CARD_PRESENT | APDU_CARD_ACTIVE); } 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 must applications, testing bit 0 is sufficient. CHANGED will receive the value of the counter tracking the number of card insertions. This value may be used to detect a card change. */ static int apdu_get_status_internal (int slot, int hang, int no_atr_reset, unsigned int *status, unsigned int *changed) { int sw; unsigned int s; 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); unlock_slot (slot); if (sw) { reader_table[slot].last_status = 0; return sw; } /* Keep track of changes. */ if (s != reader_table[slot].last_status || !reader_table[slot].any_status ) { reader_table[slot].change_counter++; /* Make sure that the ATR is invalid so that a reset will be triggered by apdu_activate. */ if (!no_atr_reset) reader_table[slot].atrlen = 0; } reader_table[slot].any_status = 1; reader_table[slot].last_status = s; if (status) *status = s; if (changed) *changed = reader_table[slot].change_counter; return 0; } /* See above for a description. */ int apdu_get_status (int slot, int hang, unsigned int *status, unsigned int *changed) { 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, 0, status, changed); if (DBG_READER) { if (status && changed) log_debug ("leave: apdu_get_status => sw=0x%x status=%u changecnt=%u\n", sw, *status, *changed); else if (status) log_debug ("leave: apdu_get_status => sw=0x%x status=%u\n", sw, *status); else if (changed) log_debug ("leave: apdu_get_status => sw=0x%x changed=%u\n", sw, *changed); 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)) { result_buffer_size = le < 0? 4096 : le; 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 = 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; apdu[apdulen++] = 0; /* Z byte: Extended length marker. */ if (lc >= 0) { apdu[apdulen++] = ((lc >> 8) & 0xff); apdu[apdulen++] = (lc & 0xff); memcpy (apdu+apdulen, data, lc); data += lc; apdulen += lc; } if (le != -1) { 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; apdu_buffer_size = 0; } /* 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); } 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); } 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); 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 reponsible 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 reponsible 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. */ int apdu_send_direct (int slot, size_t extended_length, const unsigned char *apdudata, size_t apdudatalen, int handle_more, 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); } 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); } 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 (DBG_CARD_IO && retbuf) log_printhex (" dump: ", *retbuf, *retbuflen); return 0; } diff --git a/scd/app-nks.c b/scd/app-nks.c index 19a33ed12..d0b96a906 100644 --- a/scd/app-nks.c +++ b/scd/app-nks.c @@ -1,1424 +1,1425 @@ /* app-nks.c - The Telesec NKS card application. * Copyright (C) 2004, 2007, 2008, 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 . */ /* Notes: - We are now targeting TCOS 3 cards and it may happen that there is a regression towards TCOS 2 cards. Please report. - The TKS3 AUT key is not used. It seems that it is only useful for the internal authentication command and not accessible by other applications. The key itself is in the encryption class but the corresponding certificate has only the digitalSignature capability. - If required, we automagically switch between the NKS application and the SigG application. This avoids to use the DINSIG application which is somewhat limited, has no support for Secure Messaging as required by TCOS 3 and has no way to change the PIN or even set the NullPIN. - We use the prefix NKS-DF01 for TCOS 2 cards and NKS-NKS3 for newer cards. This is because the NKS application has moved to DF02 with TCOS 3 and thus we better use a DF independent tag. - We use only the global PINs for the NKS application. */ #include #include #include #include #include #include #include #include "scdaemon.h" #include "i18n.h" #include "iso7816.h" #include "app-common.h" #include "tlv.h" #include "apdu.h" +#include "host2net.h" static char const aid_nks[] = { 0xD2, 0x76, 0x00, 0x00, 0x03, 0x01, 0x02 }; static char const aid_sigg[] = { 0xD2, 0x76, 0x00, 0x00, 0x66, 0x01 }; static struct { int is_sigg; /* Valid for SigG application. */ int fid; /* File ID. */ int nks_ver; /* 0 for NKS version 2, 3 for version 3. */ int certtype; /* Type of certificate or 0 if it is not a certificate. */ int iskeypair; /* If true has the FID of the corresponding certificate. */ int issignkey; /* True if file is a key usable for signing. */ int isenckey; /* True if file is a key usable for decryption. */ unsigned char kid; /* Corresponding key references. */ } filelist[] = { { 0, 0x4531, 0, 0, 0xC000, 1, 0, 0x80 }, /* EF_PK.NKS.SIG */ { 0, 0xC000, 0, 101 }, /* EF_C.NKS.SIG */ { 0, 0x4331, 0, 100 }, { 0, 0x4332, 0, 100 }, { 0, 0xB000, 0, 110 }, /* EF_PK.RCA.NKS */ { 0, 0x45B1, 0, 0, 0xC200, 0, 1, 0x81 }, /* EF_PK.NKS.ENC */ { 0, 0xC200, 0, 101 }, /* EF_C.NKS.ENC */ { 0, 0x43B1, 0, 100 }, { 0, 0x43B2, 0, 100 }, /* The authentication key is not used. */ /* { 0, 0x4571, 3, 0, 0xC500, 0, 0, 0x82 }, /\* EF_PK.NKS.AUT *\/ */ /* { 0, 0xC500, 3, 101 }, /\* EF_C.NKS.AUT *\/ */ { 0, 0x45B2, 3, 0, 0xC201, 0, 1, 0x83 }, /* EF_PK.NKS.ENC1024 */ { 0, 0xC201, 3, 101 }, /* EF_C.NKS.ENC1024 */ { 1, 0x4531, 3, 0, 0xC000, 1, 1, 0x84 }, /* EF_PK.CH.SIG */ { 1, 0xC000, 0, 101 }, /* EF_C.CH.SIG */ { 1, 0xC008, 3, 101 }, /* EF_C.CA.SIG */ { 1, 0xC00E, 3, 111 }, /* EF_C.RCA.SIG */ { 0, 0 } }; /* Object with application (i.e. NKS) specific data. */ struct app_local_s { int nks_version; /* NKS version. */ int sigg_active; /* True if switched to the SigG application. */ int sigg_msig_checked;/* True if we checked for a mass signature card. */ int sigg_is_msig; /* True if this is a mass signature card. */ int need_app_select; /* Need to re-select the application. */ }; static gpg_error_t switch_application (app_t app, int enable_sigg); /* Release local data. */ static void do_deinit (app_t app) { if (app && app->app_local) { xfree (app->app_local); app->app_local = NULL; } } static int all_zero_p (void *buffer, size_t length) { char *p; for (p=buffer; length; length--, p++) if (*p) return 0; return 1; } /* Read the file with FID, assume it contains a public key and return its keygrip in the caller provided 41 byte buffer R_GRIPSTR. */ static gpg_error_t keygripstr_from_pk_file (app_t app, int fid, char *r_gripstr) { gpg_error_t err; unsigned char grip[20]; unsigned char *buffer[2]; size_t buflen[2]; gcry_sexp_t sexp; int i; int offset[2] = { 0, 0 }; err = iso7816_select_file (app->slot, fid, 0, NULL, NULL); if (err) return err; err = iso7816_read_record (app->slot, 1, 1, 0, &buffer[0], &buflen[0]); if (err) return err; err = iso7816_read_record (app->slot, 2, 1, 0, &buffer[1], &buflen[1]); if (err) { xfree (buffer[0]); return err; } if (app->app_local->nks_version < 3) { /* Old versions of NKS store the values in a TLV encoded format. We need to do some checks. */ for (i=0; i < 2; i++) { /* Check that the value appears like an integer encoded as Simple-TLV. We don't check the tag because the tests cards I have use 1 for both, the modulus and the exponent - the example in the documentation gives 2 for the exponent. */ if (buflen[i] < 3) err = gpg_error (GPG_ERR_TOO_SHORT); else if (buffer[i][1] != buflen[i]-2 ) err = gpg_error (GPG_ERR_INV_OBJ); else offset[i] = 2; } } else { /* Remove leading zeroes to get a correct keygrip. Take care of negative numbers. We should also fix it the same way in libgcrypt but we can't yet rely on it yet. */ for (i=0; i < 2; i++) { while (buflen[i]-offset[i] > 1 && !buffer[i][offset[i]] && !(buffer[i][offset[i]+1] & 0x80)) offset[i]++; } } /* Check whether negative values are not prefixed with a zero and fix that. */ for (i=0; i < 2; i++) { if ((buflen[i]-offset[i]) && (buffer[i][offset[i]] & 0x80)) { unsigned char *newbuf; size_t newlen; newlen = 1 + buflen[i] - offset[i]; newbuf = xtrymalloc (newlen); if (!newlen) { xfree (buffer[0]); xfree (buffer[1]); return gpg_error_from_syserror (); } newbuf[0] = 0; memcpy (newbuf+1, buffer[i]+offset[i], buflen[i] - offset[i]); xfree (buffer[i]); buffer[i] = newbuf; buflen[i] = newlen; offset[i] = 0; } } if (!err) err = gcry_sexp_build (&sexp, NULL, "(public-key (rsa (n %b) (e %b)))", (int)buflen[0]-offset[0], buffer[0]+offset[0], (int)buflen[1]-offset[1], buffer[1]+offset[1]); xfree (buffer[0]); xfree (buffer[1]); if (err) return err; if (!gcry_pk_get_keygrip (sexp, grip)) { err = gpg_error (GPG_ERR_INTERNAL); /* i.e. RSA not supported by libgcrypt. */ } else { bin2hex (grip, 20, r_gripstr); } gcry_sexp_release (sexp); return err; } /* TCOS responds to a verify with empty data (i.e. without the Lc byte) with the status of the PIN. PWID is the PIN ID, If SIGG is true, the application is switched into SigG mode. Returns: -1 = Error retrieving the data, -2 = No such PIN, -3 = PIN blocked, -4 = NullPIN activ, n >= 0 = Number of verification attempts left. */ static int get_chv_status (app_t app, int sigg, int pwid) { unsigned char *result = NULL; size_t resultlen; char command[4]; int rc; if (switch_application (app, sigg)) return sigg? -2 : -1; /* No such PIN / General error. */ command[0] = 0x00; command[1] = 0x20; command[2] = 0x00; command[3] = pwid; if (apdu_send_direct (app->slot, 0, (unsigned char *)command, 4, 0, &result, &resultlen)) rc = -1; /* Error. */ else if (resultlen < 2) rc = -1; /* Error. */ else { - unsigned int sw = ((result[resultlen-2] << 8) | result[resultlen-1]); + unsigned int sw = buf16_to_uint (result+resultlen-2); if (sw == 0x6a88) rc = -2; /* No such PIN. */ else if (sw == 0x6983) rc = -3; /* PIN is blocked. */ else if (sw == 0x6985) rc = -4; /* NullPIN is activ. */ else if ((sw & 0xfff0) == 0x63C0) rc = (sw & 0x000f); /* PIN has N tries left. */ else rc = -1; /* Other error. */ } xfree (result); return rc; } /* Implement the GETATTR command. This is similar to the LEARN command but returns just one value via the status interface. */ static gpg_error_t do_getattr (app_t app, ctrl_t ctrl, const char *name) { static struct { const char *name; int special; } table[] = { { "$AUTHKEYID", 1 }, { "NKS-VERSION", 2 }, { "CHV-STATUS", 3 }, { NULL, 0 } }; gpg_error_t err = 0; int idx; char buffer[100]; err = switch_application (app, 0); if (err) return err; for (idx=0; table[idx].name && strcmp (table[idx].name, name); idx++) ; if (!table[idx].name) return gpg_error (GPG_ERR_INV_NAME); switch (table[idx].special) { case 1: /* $AUTHKEYID */ { /* NetKey 3.0 cards define an authentication key but according to the specs this key is only usable for encryption and not signing. it might work anyway but it has not yet been tested - fixme. Thus for now we use the NKS signature key for authentication. */ char const tmp[] = "NKS-NKS3.4531"; send_status_info (ctrl, table[idx].name, tmp, strlen (tmp), NULL, 0); } break; case 2: /* NKS-VERSION */ snprintf (buffer, sizeof buffer, "%d", app->app_local->nks_version); send_status_info (ctrl, table[idx].name, buffer, strlen (buffer), NULL, 0); break; case 3: /* CHV-STATUS */ { /* Returns: PW1.CH PW2.CH PW1.CH.SIG PW2.CH.SIG That are the two global passwords followed by the two SigG passwords. For the values, see the function get_chv_status. */ int tmp[4]; /* We use a helper array so that we can control that there is no superfluous application switch. Note that PW2.CH.SIG really has the identifier 0x83 and not 0x82 as one would expect. */ tmp[0] = get_chv_status (app, 0, 0x00); tmp[1] = get_chv_status (app, 0, 0x01); tmp[2] = get_chv_status (app, 1, 0x81); tmp[3] = get_chv_status (app, 1, 0x83); snprintf (buffer, sizeof buffer, "%d %d %d %d", tmp[0], tmp[1], tmp[2], tmp[3]); send_status_info (ctrl, table[idx].name, buffer, strlen (buffer), NULL, 0); } break; default: err = gpg_error (GPG_ERR_NOT_IMPLEMENTED); break; } return err; } static void do_learn_status_core (app_t app, ctrl_t ctrl, unsigned int flags, int is_sigg) { gpg_error_t err; char ct_buf[100], id_buf[100]; int i; const char *tag; if (is_sigg) tag = "SIGG"; else if (app->app_local->nks_version < 3) tag = "DF01"; else tag = "NKS3"; /* Output information about all useful objects in the NKS application. */ for (i=0; filelist[i].fid; i++) { if (filelist[i].nks_ver > app->app_local->nks_version) continue; if (!!filelist[i].is_sigg != !!is_sigg) continue; if (filelist[i].certtype && !(flags &1)) { size_t len; len = app_help_read_length_of_cert (app->slot, filelist[i].fid, NULL); if (len) { /* FIXME: We should store the length in the application's context so that a following readcert does only need to read that many bytes. */ snprintf (ct_buf, sizeof ct_buf, "%d", filelist[i].certtype); snprintf (id_buf, sizeof id_buf, "NKS-%s.%04X", tag, filelist[i].fid); send_status_info (ctrl, "CERTINFO", ct_buf, strlen (ct_buf), id_buf, strlen (id_buf), NULL, (size_t)0); } } else if (filelist[i].iskeypair) { char gripstr[40+1]; err = keygripstr_from_pk_file (app, filelist[i].fid, gripstr); if (err) log_error ("can't get keygrip from FID 0x%04X: %s\n", filelist[i].fid, gpg_strerror (err)); else { snprintf (id_buf, sizeof id_buf, "NKS-%s.%04X", tag, filelist[i].fid); send_status_info (ctrl, "KEYPAIRINFO", gripstr, 40, id_buf, strlen (id_buf), NULL, (size_t)0); } } } } static gpg_error_t do_learn_status (app_t app, ctrl_t ctrl, unsigned int flags) { gpg_error_t err; err = switch_application (app, 0); if (err) return err; do_learn_status_core (app, ctrl, flags, 0); err = switch_application (app, 1); if (err) return 0; /* Silently ignore if we can't switch to SigG. */ do_learn_status_core (app, ctrl, flags, 1); return 0; } /* Read the certificate with id CERTID (as returned by learn_status in the CERTINFO status lines) and return it in the freshly allocated buffer put into CERT and the length of the certificate put into CERTLEN. */ static gpg_error_t do_readcert (app_t app, const char *certid, unsigned char **cert, size_t *certlen) { int i, fid; gpg_error_t err; unsigned char *buffer; const unsigned char *p; size_t buflen, n; int class, tag, constructed, ndef; size_t totobjlen, objlen, hdrlen; int rootca = 0; int is_sigg = 0; *cert = NULL; *certlen = 0; if (!strncmp (certid, "NKS-NKS3.", 9)) ; else if (!strncmp (certid, "NKS-DF01.", 9)) ; else if (!strncmp (certid, "NKS-SIGG.", 9)) is_sigg = 1; else return gpg_error (GPG_ERR_INV_ID); err = switch_application (app, is_sigg); if (err) return err; certid += 9; if (!hexdigitp (certid) || !hexdigitp (certid+1) || !hexdigitp (certid+2) || !hexdigitp (certid+3) || certid[4]) return gpg_error (GPG_ERR_INV_ID); fid = xtoi_4 (certid); for (i=0; filelist[i].fid; i++) if ((filelist[i].certtype || filelist[i].iskeypair) && filelist[i].fid == fid) break; if (!filelist[i].fid) return gpg_error (GPG_ERR_NOT_FOUND); /* If the requested objects is a plain public key, redirect it to the corresponding certificate. The whole system is a bit messy because we sometime use the key directly or let the caller retrieve the key from the certificate. The rationale for that is to support not-yet stored certificates. */ if (filelist[i].iskeypair) fid = filelist[i].iskeypair; /* Read the entire file. fixme: This could be optimized by first reading the header to figure out how long the certificate actually is. */ err = iso7816_select_file (app->slot, fid, 0, NULL, NULL); if (err) { log_error ("error selecting FID 0x%04X: %s\n", fid, gpg_strerror (err)); return err; } err = iso7816_read_binary (app->slot, 0, 0, &buffer, &buflen); if (err) { log_error ("error reading certificate from FID 0x%04X: %s\n", fid, gpg_strerror (err)); return err; } if (!buflen || *buffer == 0xff) { log_info ("no certificate contained in FID 0x%04X\n", fid); err = gpg_error (GPG_ERR_NOT_FOUND); goto leave; } /* Now figure something out about the object. */ 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 ) ; else if ( class == CLASS_UNIVERSAL && tag == TAG_SET && constructed ) rootca = 1; else return gpg_error (GPG_ERR_INV_OBJ); totobjlen = objlen + hdrlen; assert (totobjlen <= buflen); err = parse_ber_header (&p, &n, &class, &tag, &constructed, &ndef, &objlen, &hdrlen); if (err) goto leave; if (rootca) ; else if (class == CLASS_UNIVERSAL && tag == TAG_OBJECT_ID && !constructed) { const unsigned char *save_p; /* 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) ) return gpg_error (GPG_ERR_INV_OBJ); totobjlen = objlen + hdrlen; assert (save_p + totobjlen <= buffer + buflen); memmove (buffer, save_p, totobjlen); } *cert = buffer; buffer = NULL; *certlen = totobjlen; leave: xfree (buffer); return err; } /* Handle the READKEY command. On success a canonical encoded S-expression with the public key will get stored at PK and its length at PKLEN; the caller must release that buffer. On error PK and PKLEN are not changed and an error code is returned. As of now this function is only useful for the internal authentication key. Other keys are automagically retrieved via by means of the certificate parsing code in commands.c:cmd_readkey. For internal use PK and PKLEN may be NULL to just check for an existing key. */ static gpg_error_t do_readkey (app_t app, const char *keyid, unsigned char **pk, size_t *pklen) { gpg_error_t err; unsigned char *buffer[2]; size_t buflen[2]; unsigned short path[1] = { 0x4500 }; /* We use a generic name to retrieve PK.AUT.IFD-SPK. */ if (!strcmp (keyid, "$IFDAUTHKEY") && app->app_local->nks_version >= 3) ; else /* Return the error code expected by cmd_readkey. */ return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION); /* Access the KEYD file which is always in the master directory. */ err = iso7816_select_path (app->slot, path, DIM (path), NULL, NULL); if (err) return err; /* Due to the above select we need to re-select our application. */ app->app_local->need_app_select = 1; /* Get the two records. */ err = iso7816_read_record (app->slot, 5, 1, 0, &buffer[0], &buflen[0]); if (err) return err; if (all_zero_p (buffer[0], buflen[0])) { xfree (buffer[0]); return gpg_error (GPG_ERR_NOT_FOUND); } err = iso7816_read_record (app->slot, 6, 1, 0, &buffer[1], &buflen[1]); if (err) { xfree (buffer[0]); return err; } if (pk && pklen) { *pk = make_canon_sexp_from_rsa_pk (buffer[0], buflen[0], buffer[1], buflen[1], pklen); if (!*pk) err = gpg_error_from_syserror (); } xfree (buffer[0]); xfree (buffer[1]); return err; } /* Handle the WRITEKEY command for NKS. This function expects a canonical encoded S-expression with the public key in KEYDATA and its length in KEYDATALEN. The only supported KEYID is "$IFDAUTHKEY" to store the terminal key on the card. 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); const unsigned char *rsa_n = NULL; const unsigned char *rsa_e = NULL; size_t rsa_n_len, rsa_e_len; unsigned int nbits; (void)ctrl; (void)pincb; (void)pincb_arg; if (!strcmp (keyid, "$IFDAUTHKEY") && app->app_local->nks_version >= 3) ; else return gpg_error (GPG_ERR_INV_ID); if (!force && !do_readkey (app, keyid, NULL, NULL)) return gpg_error (GPG_ERR_EEXIST); /* Parse the S-expression. */ err = get_rsa_pk_from_canon_sexp (keydata, keydatalen, &rsa_n, &rsa_n_len, &rsa_e, &rsa_e_len); if (err) goto leave; /* Check that the parameters match the requirements. */ nbits = app_help_count_bits (rsa_n, rsa_n_len); if (nbits != 1024) { log_error (_("RSA modulus missing or not of size %d bits\n"), 1024); err = gpg_error (GPG_ERR_BAD_PUBKEY); goto leave; } nbits = app_help_count_bits (rsa_e, rsa_e_len); if (nbits < 2 || nbits > 32) { log_error (_("RSA public exponent missing or larger than %d bits\n"), 32); err = gpg_error (GPG_ERR_BAD_PUBKEY); goto leave; } /* /\* Store them. *\/ */ /* err = verify_pin (app, 0, NULL, pincb, pincb_arg); */ /* if (err) */ /* goto leave; */ /* Send the MSE:Store_Public_Key. */ err = gpg_error (GPG_ERR_NOT_IMPLEMENTED); /* mse = xtrymalloc (1000); */ /* mse[0] = 0x80; /\* Algorithm reference. *\/ */ /* mse[1] = 1; */ /* mse[2] = 0x17; */ /* mse[3] = 0x84; /\* Private key reference. *\/ */ /* mse[4] = 1; */ /* mse[5] = 0x77; */ /* mse[6] = 0x7F; /\* Public key parameter. *\/ */ /* mse[7] = 0x49; */ /* mse[8] = 0x81; */ /* mse[9] = 3 + 0x80 + 2 + rsa_e_len; */ /* mse[10] = 0x81; /\* RSA modulus of 128 byte. *\/ */ /* mse[11] = 0x81; */ /* mse[12] = rsa_n_len; */ /* memcpy (mse+12, rsa_n, rsa_n_len); */ /* mse[10] = 0x82; /\* RSA public exponent of up to 4 bytes. *\/ */ /* mse[12] = rsa_e_len; */ /* memcpy (mse+12, rsa_e, rsa_e_len); */ /* err = iso7816_manage_security_env (app->slot, 0x81, 0xB6, */ /* mse, sizeof mse); */ leave: return err; } static gpg_error_t basic_pin_checks (const char *pinvalue, int minlen, int maxlen) { if (strlen (pinvalue) < minlen) { log_error ("PIN is too short; minimum length is %d\n", minlen); return gpg_error (GPG_ERR_BAD_PIN); } if (strlen (pinvalue) > maxlen) { log_error ("PIN is too large; maximum length is %d\n", maxlen); return gpg_error (GPG_ERR_BAD_PIN); } return 0; } /* Verify the PIN if required. */ static gpg_error_t verify_pin (app_t app, int pwid, const char *desc, gpg_error_t (*pincb)(void*, const char *, char **), void *pincb_arg) { pininfo_t pininfo; int rc; if (!desc) desc = "PIN"; memset (&pininfo, 0, sizeof pininfo); pininfo.fixedlen = -1; pininfo.minlen = 6; pininfo.maxlen = 16; if (!opt.disable_pinpad && !iso7816_check_pinpad (app->slot, ISO7816_VERIFY, &pininfo) ) { rc = pincb (pincb_arg, desc, NULL); if (rc) { log_info (_("PIN callback returned error: %s\n"), gpg_strerror (rc)); return rc; } rc = iso7816_verify_kp (app->slot, pwid, &pininfo); pincb (pincb_arg, NULL, NULL); /* Dismiss the prompt. */ } else { char *pinvalue; rc = pincb (pincb_arg, desc, &pinvalue); if (rc) { log_info ("PIN callback returned error: %s\n", gpg_strerror (rc)); return rc; } rc = basic_pin_checks (pinvalue, pininfo.minlen, pininfo.maxlen); if (rc) { xfree (pinvalue); return rc; } rc = iso7816_verify (app->slot, pwid, pinvalue, strlen (pinvalue)); xfree (pinvalue); } if (rc) { if ( gpg_err_code (rc) == GPG_ERR_USE_CONDITIONS ) log_error (_("the NullPIN has not yet been changed\n")); else log_error ("verify PIN failed\n"); return rc; } return 0; } /* 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 in 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 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 }; int rc, i; int is_sigg = 0; int fid; unsigned char kid; unsigned char data[83]; /* Must be large enough for a SHA-1 digest + the largest OID prefix. */ size_t datalen; if (!keyidstr || !*keyidstr) return gpg_error (GPG_ERR_INV_VALUE); switch (indatalen) { case 16: case 20: case 35: case 47: case 51: case 67: case 83: break; default: return gpg_error (GPG_ERR_INV_VALUE); } /* Check that the provided ID is valid. This is not really needed but we do it to enforce correct usage by the caller. */ if (!strncmp (keyidstr, "NKS-NKS3.", 9) ) ; else if (!strncmp (keyidstr, "NKS-DF01.", 9) ) ; else if (!strncmp (keyidstr, "NKS-SIGG.", 9) ) is_sigg = 1; else return gpg_error (GPG_ERR_INV_ID); keyidstr += 9; rc = switch_application (app, is_sigg); if (rc) return rc; if (is_sigg && app->app_local->sigg_is_msig) { log_info ("mass signature cards are not allowed\n"); return gpg_error (GPG_ERR_NOT_SUPPORTED); } if (!hexdigitp (keyidstr) || !hexdigitp (keyidstr+1) || !hexdigitp (keyidstr+2) || !hexdigitp (keyidstr+3) || keyidstr[4]) return gpg_error (GPG_ERR_INV_ID); fid = xtoi_4 (keyidstr); for (i=0; filelist[i].fid; i++) if (filelist[i].iskeypair && filelist[i].fid == fid) break; if (!filelist[i].fid) return gpg_error (GPG_ERR_NOT_FOUND); if (!filelist[i].issignkey) return gpg_error (GPG_ERR_INV_ID); kid = filelist[i].kid; /* Prepare the DER object from INDATA. */ if (app->app_local->nks_version > 2 && (indatalen == 35 || indatalen == 47 || indatalen == 51 || indatalen == 67 || indatalen == 83)) { /* The caller send data matching the length of the ASN.1 encoded hash for SHA-{1,224,256,384,512}. Assume that is okay. */ assert (indatalen <= sizeof data); memcpy (data, indata, indatalen); datalen = indatalen; } else if (indatalen == 35) { /* Alright, the caller was so kind to send us an already prepared DER object. This is for TCOS 2. */ 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; } else if (indatalen == 20) { if (hashalgo == GCRY_MD_SHA1) memcpy (data, sha1_prefix, 15); else if (hashalgo == GCRY_MD_RMD160) memcpy (data, rmd160_prefix, 15); else return gpg_error (GPG_ERR_UNSUPPORTED_ALGORITHM); memcpy (data+15, indata, indatalen); datalen = 35; } else return gpg_error (GPG_ERR_INV_VALUE); /* Send an MSE for PSO:Computer_Signature. */ if (app->app_local->nks_version > 2) { unsigned char mse[6]; mse[0] = 0x80; /* Algorithm reference. */ mse[1] = 1; mse[2] = 2; /* RSA, card does pkcs#1 v1.5 padding, no ASN.1 check. */ mse[3] = 0x84; /* Private key reference. */ mse[4] = 1; mse[5] = kid; rc = iso7816_manage_security_env (app->slot, 0x41, 0xB6, mse, sizeof mse); } /* Verify using PW1.CH. */ if (!rc) rc = verify_pin (app, 0, NULL, pincb, pincb_arg); /* Compute the signature. */ if (!rc) rc = iso7816_compute_ds (app->slot, 0, data, datalen, 0, outdata, outdatalen); return rc; } /* 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) { int rc, i; int is_sigg = 0; int fid; int kid; (void)r_info; if (!keyidstr || !*keyidstr || !indatalen) return gpg_error (GPG_ERR_INV_VALUE); /* Check that the provided ID is valid. This is not really needed but we do it to to enforce correct usage by the caller. */ if (!strncmp (keyidstr, "NKS-NKS3.", 9) ) ; else if (!strncmp (keyidstr, "NKS-DF01.", 9) ) ; else if (!strncmp (keyidstr, "NKS-SIGG.", 9) ) is_sigg = 1; else return gpg_error (GPG_ERR_INV_ID); keyidstr += 9; rc = switch_application (app, is_sigg); if (rc) return rc; if (!hexdigitp (keyidstr) || !hexdigitp (keyidstr+1) || !hexdigitp (keyidstr+2) || !hexdigitp (keyidstr+3) || keyidstr[4]) return gpg_error (GPG_ERR_INV_ID); fid = xtoi_4 (keyidstr); for (i=0; filelist[i].fid; i++) if (filelist[i].iskeypair && filelist[i].fid == fid) break; if (!filelist[i].fid) return gpg_error (GPG_ERR_NOT_FOUND); if (!filelist[i].isenckey) return gpg_error (GPG_ERR_INV_ID); kid = filelist[i].kid; if (app->app_local->nks_version > 2) { unsigned char mse[6]; mse[0] = 0x80; /* Algorithm reference. */ mse[1] = 1; mse[2] = 0x0a; /* RSA no padding. (0x1A is pkcs#1.5 padding.) */ mse[3] = 0x84; /* Private key reference. */ mse[4] = 1; mse[5] = kid; rc = iso7816_manage_security_env (app->slot, 0x41, 0xB8, mse, sizeof mse); } else { static const unsigned char mse[] = { 0x80, 1, 0x10, /* Select algorithm RSA. */ 0x84, 1, 0x81 /* Select local secret key 1 for decryption. */ }; rc = iso7816_manage_security_env (app->slot, 0xC1, 0xB8, mse, sizeof mse); } if (!rc) rc = verify_pin (app, 0, NULL, pincb, pincb_arg); /* Note that we need to use extended length APDUs for TCOS 3 cards. Command chaining does not work. */ if (!rc) rc = iso7816_decipher (app->slot, app->app_local->nks_version > 2? 1:0, indata, indatalen, 0, 0x81, outdata, outdatalen); return rc; } /* Parse a password ID string. Returns NULL on error or a string suitable as passpahrse prompt on success. On success stores the reference value for the password at R_PWID and a flag indicating that the SigG application is to be used at R_SIGG. If NEW_MODE is true, the returned description is suitable for a new Password. Supported values for PWIDSTR are: PW1.CH - Global password 1 PW2.CH - Global password 2 PW1.CH.SIG - SigG password 1 PW2.CH.SIG - SigG password 2 */ static const char * parse_pwidstr (const char *pwidstr, int new_mode, int *r_sigg, int *r_pwid) { const char *desc; if (!pwidstr) desc = NULL; else if (!strcmp (pwidstr, "PW1.CH")) { *r_sigg = 0; *r_pwid = 0x00; /* TRANSLATORS: Do not translate the "|*|" prefixes but keep them verbatim at the start of the string. */ desc = (new_mode ? _("|N|Please enter a new PIN for the standard keys.") : _("||Please enter the PIN for the standard keys.")); } else if (!strcmp (pwidstr, "PW2.CH")) { *r_pwid = 0x01; desc = (new_mode ? _("|NP|Please enter a new PIN Unblocking Code (PUK) " "for the standard keys.") : _("|P|Please enter the PIN Unblocking Code (PUK) " "for the standard keys.")); } else if (!strcmp (pwidstr, "PW1.CH.SIG")) { *r_pwid = 0x81; *r_sigg = 1; desc = (new_mode ? _("|N|Please enter a new PIN for the key to create " "qualified signatures.") : _("||Please enter the PIN for the key to create " "qualified signatures.")); } else if (!strcmp (pwidstr, "PW2.CH.SIG")) { *r_pwid = 0x83; /* Yes, that is 83 and not 82. */ *r_sigg = 1; desc = (new_mode ? _("|NP|Please enter a new PIN Unblocking Code (PUK) " "for the key to create qualified signatures.") : _("|P|Please enter the PIN Unblocking Code (PUK) " "for the key to create qualified signatures.")); } else { *r_pwid = 0; /* Only to avoid gcc warning in calling function. */ desc = NULL; /* Error. */ } return desc; } /* Handle the PASSWD command. See parse_pwidstr() for allowed values for CHVNOSTR. */ static gpg_error_t do_change_pin (app_t app, ctrl_t ctrl, const char *pwidstr, unsigned int flags, gpg_error_t (*pincb)(void*, const char *, char **), void *pincb_arg) { gpg_error_t err; char *newpin = NULL; char *oldpin = NULL; size_t newpinlen; size_t oldpinlen; int is_sigg; const char *newdesc; int pwid; pininfo_t pininfo; (void)ctrl; /* The minimum length is enforced by TCOS, the maximum length is just a reasonable value. */ memset (&pininfo, 0, sizeof pininfo); pininfo.minlen = 6; pininfo.maxlen = 16; newdesc = parse_pwidstr (pwidstr, 1, &is_sigg, &pwid); if (!newdesc) return gpg_error (GPG_ERR_INV_ID); err = switch_application (app, is_sigg); if (err) return err; if ((flags & APP_CHANGE_FLAG_NULLPIN)) { /* With the nullpin flag, we do not verify the PIN - it would fail if the Nullpin is still set. */ oldpin = xtrycalloc (1, 6); if (!oldpin) { err = gpg_error_from_syserror (); goto leave; } oldpinlen = 6; } else { const char *desc; int dummy1, dummy2; if ((flags & APP_CHANGE_FLAG_RESET)) { /* Reset mode: Ask for the alternate PIN. */ const char *altpwidstr; if (!strcmp (pwidstr, "PW1.CH")) altpwidstr = "PW2.CH"; else if (!strcmp (pwidstr, "PW2.CH")) altpwidstr = "PW1.CH"; else if (!strcmp (pwidstr, "PW1.CH.SIG")) altpwidstr = "PW2.CH.SIG"; else if (!strcmp (pwidstr, "PW2.CH.SIG")) altpwidstr = "PW1.CH.SIG"; else { err = gpg_error (GPG_ERR_BUG); goto leave; } desc = parse_pwidstr (altpwidstr, 0, &dummy1, &dummy2); } else { /* Regular change mode: Ask for the old PIN. */ desc = parse_pwidstr (pwidstr, 0, &dummy1, &dummy2); } err = pincb (pincb_arg, desc, &oldpin); if (err) { log_error ("error getting old PIN: %s\n", gpg_strerror (err)); goto leave; } oldpinlen = strlen (oldpin); err = basic_pin_checks (oldpin, pininfo.minlen, pininfo.maxlen); if (err) goto leave; } err = pincb (pincb_arg, newdesc, &newpin); if (err) { log_error (_("error getting new PIN: %s\n"), gpg_strerror (err)); goto leave; } newpinlen = strlen (newpin); err = basic_pin_checks (newpin, pininfo.minlen, pininfo.maxlen); if (err) goto leave; if ((flags & APP_CHANGE_FLAG_RESET)) { char *data; size_t datalen = oldpinlen + newpinlen; data = xtrymalloc (datalen); if (!data) { err = gpg_error_from_syserror (); goto leave; } memcpy (data, oldpin, oldpinlen); memcpy (data+oldpinlen, newpin, newpinlen); err = iso7816_reset_retry_counter_with_rc (app->slot, pwid, data, datalen); wipememory (data, datalen); xfree (data); } else err = iso7816_change_reference_data (app->slot, pwid, oldpin, oldpinlen, newpin, newpinlen); leave: xfree (oldpin); xfree (newpin); return err; } /* Perform a simple verify operation. KEYIDSTR should be NULL or empty. */ static gpg_error_t do_check_pin (app_t app, const char *pwidstr, gpg_error_t (*pincb)(void*, const char *, char **), void *pincb_arg) { gpg_error_t err; int pwid; int is_sigg; const char *desc; desc = parse_pwidstr (pwidstr, 0, &is_sigg, &pwid); if (!desc) return gpg_error (GPG_ERR_INV_ID); err = switch_application (app, is_sigg); if (err) return err; return verify_pin (app, pwid, desc, pincb, pincb_arg); } /* Return the version of the NKS application. */ static int get_nks_version (int slot) { unsigned char *result = NULL; size_t resultlen; int type; if (iso7816_apdu_direct (slot, "\x80\xaa\x06\x00\x00", 5, 0, &result, &resultlen)) return 2; /* NKS 2 does not support this command. */ /* Example value: 04 11 19 22 21 6A 20 80 03 03 01 01 01 00 00 00 vv tt ccccccccccccccccc aa bb cc vvvvvvvvvvv xx vendor (Philips) -+ | | | | | | | chip type -----------+ | | | | | | chip id ----------------+ | | | | | card type (3 - tcos 3) -------------------+ | | | | OS version of card type ---------------------+ | | | OS release of card type ------------------------+ | | OS vendor internal version ------------------------+ | RFU -----------------------------------------------------------+ */ if (resultlen < 16) type = 0; /* Invalid data returned. */ else type = result[8]; xfree (result); return type; } /* If ENABLE_SIGG is true switch to the SigG application if not yet active. If false switch to the NKS application if not yet active. Returns 0 on success. */ static gpg_error_t switch_application (app_t app, int enable_sigg) { gpg_error_t err; if (((app->app_local->sigg_active && enable_sigg) || (!app->app_local->sigg_active && !enable_sigg)) && !app->app_local->need_app_select) return 0; /* Already switched. */ log_info ("app-nks: switching to %s\n", enable_sigg? "SigG":"NKS"); if (enable_sigg) err = iso7816_select_application (app->slot, aid_sigg, sizeof aid_sigg, 0); else err = iso7816_select_application (app->slot, aid_nks, sizeof aid_nks, 0); if (!err && enable_sigg && app->app_local->nks_version >= 3 && !app->app_local->sigg_msig_checked) { /* Check whether this card is a mass signature card. */ unsigned char *buffer; size_t buflen; const unsigned char *tmpl; size_t tmpllen; app->app_local->sigg_msig_checked = 1; app->app_local->sigg_is_msig = 1; err = iso7816_select_file (app->slot, 0x5349, 0, NULL, NULL); if (!err) err = iso7816_read_record (app->slot, 1, 1, 0, &buffer, &buflen); if (!err) { tmpl = find_tlv (buffer, buflen, 0x7a, &tmpllen); if (tmpl && tmpllen == 12 && !memcmp (tmpl, "\x93\x02\x00\x01\xA4\x06\x83\x01\x81\x83\x01\x83", 12)) app->app_local->sigg_is_msig = 0; xfree (buffer); } if (app->app_local->sigg_is_msig) log_info ("This is a mass signature card\n"); } if (!err) { app->app_local->need_app_select = 0; app->app_local->sigg_active = enable_sigg; } else log_error ("app-nks: error switching to %s: %s\n", enable_sigg? "SigG":"NKS", gpg_strerror (err)); return err; } /* Select the NKS application. */ gpg_error_t app_select_nks (app_t app) { int slot = app->slot; int rc; rc = iso7816_select_application (slot, aid_nks, sizeof aid_nks, 0); if (!rc) { app->apptype = "NKS"; 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->nks_version = get_nks_version (slot); if (opt.verbose) log_info ("Detected NKS version: %d\n", app->app_local->nks_version); 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 = NULL; app->fnc.writekey = do_writekey; app->fnc.genkey = NULL; app->fnc.sign = do_sign; app->fnc.auth = NULL; 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-openpgp.c b/scd/app-openpgp.c index f68813bfd..6583fb278 100644 --- a/scd/app-openpgp.c +++ b/scd/app-openpgp.c @@ -1,4602 +1,4603 @@ /* app-openpgp.c - The OpenPGP card application. * Copyright (C) 2003, 2004, 2005, 2007, 2008, * 2009, 2013, 2014 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 "util.h" #include "cardglue.h" #else /* GNUPG_MAJOR_VERSION != 1 */ #include "scdaemon.h" #endif /* GNUPG_MAJOR_VERSION != 1 */ #include "i18n.h" #include "iso7816.h" #include "app-common.h" #include "tlv.h" +#include "host2net.h" /* 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. */ int binary:1; int dont_cache:1; int flush_on_error:1; 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. */ int try_extlen:1; /* Large object; try to use an extended length APDU. */ char *desc; } data_objects[] = { { 0x005E, 0, 0, 1, 0, 0, 0, 0, "Login Data" }, { 0x5F50, 0, 0, 0, 0, 0, 0, 0, "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, "Sex" }, { 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, 0, "Private DO 1"}, { 0x0102, 0, 0, 0, 0, 0, 0, 0, "Private DO 2"}, { 0x0103, 0, 0, 0, 0, 0, 0, 0, "Private DO 3"}, { 0x0104, 0, 0, 0, 0, 0, 0, 0, "Private DO 4"}, { 0x7F21, 1, 0, 1, 0, 0, 0, 1, "Cardholder certificate"}, { 0 } }; /* Type of keys. */ typedef enum { KEY_TYPE_ECC, KEY_TYPE_EDDSA, 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; /* Elliptic Curves. */ enum { CURVE_NIST_P256, CURVE_NIST_P384, CURVE_NIST_P521, CURVE_SEC_P256K1, CURVE_ED25519, CURVE_UNKNOWN, }; /* 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; /* This is a v2.0 compatible card. */ 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 sm_supported:1; /* Secure Messaging is supported. */ unsigned int sm_aes128:1; /* Use AES-128 for SM. */ unsigned int max_certlen_3:16; unsigned int max_get_challenge:16; /* Maximum size for get_challenge. */ unsigned int max_cmd_data:16; /* Maximum data size for a command. */ unsigned int max_rsp_data:16; /* Maximum size of a response. */ } 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 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 { int curve; } ecc; struct { int curve; } eddsa; }; } keyattr[3]; }; /***** 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); /* 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) exmode = app->app_local->extcap.max_rsp_data; else exmode = 0; err = iso7816_get_data (app->slot, exmode, tag, &p, &len); if (err) return err; *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) { memcpy (c->data, p, len); 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) { if (data_objects[i].try_extlen && app->app_local->cardcap.ext_lc_le) exmode = app->app_local->extcap.max_rsp_data; else 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); } 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); } 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 syncronized Bit 1 = CHV2 has been 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); } static unsigned char get_algo_byte (int keynumber, key_type_t key_type) { if (key_type == KEY_TYPE_ECC && keynumber != 1) return 19; else if (key_type == KEY_TYPE_ECC && keynumber == 1) return 18; else if (key_type == KEY_TYPE_EDDSA) return 22; else return 1; /* RSA */ } #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, key_type_t key_type, ...) { 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 (keynumber == 1 && key_type == KEY_TYPE_ECC) argc = 3; else argc = 2; va_start (ap, key_type); for (i = 0; i < argc; i++) { m[i] = va_arg (ap, const unsigned char *); mlen[i] = va_arg (ap, size_t); if (key_type != KEY_TYPE_EDDSA) /* strip off leading zeroes */ for (; mlen[i] && !*m[i]; mlen[i]--, m[i]++) ; if (key_type == KEY_TYPE_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++ = get_algo_byte (keynumber, key_type); for (i = 0; i < argc; i++) { if (key_type == KEY_TYPE_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 = (stamp[0] << 24) | (stamp[1]<<16) | (stamp[2]<<8) | stamp[3]; + 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 get_ecc_key_parameters (int curve, int *r_n_bits, const char **r_curve_oid) { if (curve == CURVE_NIST_P256) { *r_n_bits = 256; *r_curve_oid = "1.2.840.10045.3.1.7"; } else if (curve == CURVE_NIST_P384) { *r_n_bits = 384; *r_curve_oid = "1.3.132.0.34"; } else if (curve == CURVE_NIST_P521) { *r_n_bits = 521; *r_curve_oid = "1.3.132.0.35"; } else if (curve == CURVE_SEC_P256K1) { *r_n_bits = 256; *r_curve_oid = "1.3.132.0.10"; } else if (curve == CURVE_ED25519) { *r_n_bits = 255; *r_curve_oid = "1.3.6.1.4.1.11591.15.1"; } else { *r_n_bits = 0; *r_curve_oid = "1.3.6.1.4.1.11591.2.12242973"; /* gnu.gnupg.badoid */ } } static void send_key_attr (ctrl_t ctrl, app_t app, const char *keyword, int number) { char buffer[200]; int n_bits; const char *curve_oid; assert (number >=0 && number < DIM(app->app_local->keyattr)); if (app->app_local->keyattr[number].key_type == KEY_TYPE_RSA) snprintf (buffer, sizeof buffer, "%d 1 %u %u %d", number+1, app->app_local->keyattr[number].rsa.n_bits, app->app_local->keyattr[number].rsa.e_bits, app->app_local->keyattr[number].rsa.format); else if (app->app_local->keyattr[number].key_type == KEY_TYPE_ECC) { get_ecc_key_parameters (app->app_local->keyattr[number].ecc.curve, &n_bits, &curve_oid); snprintf (buffer, sizeof buffer, "%d %d %u %s", number+1, number==1? 18: 19, n_bits, curve_oid); } else if (app->app_local->keyattr[number].key_type == KEY_TYPE_EDDSA) { get_ecc_key_parameters (app->app_local->keyattr[number].eddsa.curve, &n_bits, &curve_oid); snprintf (buffer, sizeof buffer, "%d 22 %u %s", number+1, n_bits, curve_oid); } else snprintf (buffer, sizeof buffer, "0 0 UNKNOWN"); send_status_direct (ctrl, keyword, buffer); } /* 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 }, { "$DISPSERIALNO",0x0000, -4 }, { 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, but we have it already in the app context and the stamp argument is required anyway which we can't by other means. The AID DO is available anyway but not hex formatted. */ char *serial; time_t stamp; char tmp[50]; if (!app_get_serial_and_stamp (app, &serial, &stamp)) { sprintf (tmp, "%lu", (unsigned long)stamp); send_status_info (ctrl, "SERIALNO", serial, strlen (serial), tmp, strlen (tmp), NULL, 0); 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", 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_aes128? 7 : 2) : 0), app->app_local->status_indicator); 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; time_t stamp; if (!app_get_serial_and_stamp (app, &serial, &stamp)) { 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; } 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 send_status_info (ctrl, table[idx].name, value, valuelen, NULL, 0); xfree (relptr); } return rc; } /* 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. */ i = 0; /* Avoid erroneous compiler warning. */ 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 const char * get_curve_name (int curve) { if (curve == CURVE_NIST_P256) return "NIST P-256"; else if (curve == CURVE_NIST_P384) return "NIST P-384"; else if (curve == CURVE_NIST_P521) return "NIST P-521"; else if (curve == CURVE_SEC_P256K1) return "secp256k1"; else if (curve == CURVE_ED25519) return "Ed25519"; else return "unknown"; } /* Get the public key for KEYNO and store it as an S-expresion 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 soley indicated by the presence of the app->app_local->pk[KEYNO-1].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-expresion 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 *keydata, *m, *e; size_t buflen, keydatalen; size_t mlen = 0; size_t elen = 0; unsigned char *mbuf = NULL; unsigned char *ebuf = NULL; char *keybuf = NULL; gcry_sexp_t s_pkey; size_t len; if (keyno < 1 || keyno > 3) return gpg_error (GPG_ERR_INV_ID); keyno--; /* 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) { exmode = 1; /* Use extended length. */ le_value = app->app_local->extcap.max_rsp_data; } else { exmode = 0; le_value = 256; /* Use legacy value. */ } err = iso7816_read_public_key (app->slot, exmode, (const unsigned char*)(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; } 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; } if (app->app_local->keyattr[keyno].key_type == KEY_TYPE_RSA) { m = find_tlv (keydata, keydatalen, 0x0081, &mlen); if (!m) { err = gpg_error (GPG_ERR_CARD); log_error (_("response does not contain the RSA modulus\n")); goto leave; } e = find_tlv (keydata, keydatalen, 0x0082, &elen); if (!e) { err = gpg_error (GPG_ERR_CARD); log_error (_("response does not contain the RSA public exponent\n")); goto leave; } } else { m = find_tlv (keydata, keydatalen, 0x0086, &mlen); if (!m) { err = gpg_error (GPG_ERR_CARD); log_error (_("response does not contain the EC public point\n")); goto leave; } } } 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; } } mbuf = xtrymalloc ( mlen + 1); if (!mbuf) { err = gpg_error_from_syserror (); goto leave; } /* Prepend numbers with a 0 if needed. */ if (app->app_local->keyattr[keyno].key_type != KEY_TYPE_EDDSA && 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); if (app->app_local->keyattr[keyno].key_type == KEY_TYPE_RSA) { err = gcry_sexp_build (&s_pkey, NULL, "(public-key(rsa(n%b)(e%b)))", mlen, mbuf, elen, ebuf); if (err) goto leave; len = gcry_sexp_sprint (s_pkey, GCRYSEXP_FMT_CANON, NULL, 0); keybuf = xtrymalloc (len); if (!keybuf) { gcry_sexp_release (s_pkey); err = gpg_error_from_syserror (); goto leave; } gcry_sexp_sprint (s_pkey, GCRYSEXP_FMT_CANON, keybuf, len); gcry_sexp_release (s_pkey); } else if (app->app_local->keyattr[keyno].key_type == KEY_TYPE_ECC) { const char *curve_name = get_curve_name (app->app_local->keyattr[keyno].ecc.curve); err = gcry_sexp_build (&s_pkey, NULL, "(public-key(ecc(curve%s)(q%b)))", curve_name, mlen, mbuf); if (err) goto leave; len = gcry_sexp_sprint (s_pkey, GCRYSEXP_FMT_CANON, NULL, 0); keybuf = xtrymalloc (len); if (!keybuf) { gcry_sexp_release (s_pkey); err = gpg_error_from_syserror (); goto leave; } gcry_sexp_sprint (s_pkey, GCRYSEXP_FMT_CANON, keybuf, len); gcry_sexp_release (s_pkey); } else if (app->app_local->keyattr[keyno].key_type == KEY_TYPE_EDDSA) { const char *curve_name = get_curve_name (app->app_local->keyattr[keyno].eddsa.curve); err = gcry_sexp_build (&s_pkey, NULL, "(public-key(ecc(curve%s)(flags eddsa)(q%b)))", curve_name, mlen, mbuf); if (err) goto leave; len = gcry_sexp_sprint (s_pkey, GCRYSEXP_FMT_CANON, NULL, 0); keybuf = xtrymalloc (len); if (!keybuf) { gcry_sexp_release (s_pkey); err = gpg_error_from_syserror (); goto leave; } gcry_sexp_sprint (s_pkey, GCRYSEXP_FMT_CANON, keybuf, len); gcry_sexp_release (s_pkey); } else { err = gpg_error (GPG_ERR_NOT_IMPLEMENTED); goto leave; } app->app_local->pk[keyno].key = (unsigned char*)keybuf; app->app_local->pk[keyno].keylen = len - 1; /* Decrement for trailing '\0' */ leave: /* Set a flag to indicate that we tried to read the key. */ app->app_local->pk[keyno].read_done = 1; xfree (buffer); xfree (mbuf); xfree (ebuf); return 0; } #endif /* GNUPG_MAJOR_VERSION > 1 */ /* Send the KEYPAIRINFO back. KEYNO 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 keyno) { 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]; err = get_public_key (app, keyno); if (err) goto leave; assert (keyno >= 1 && keyno <= 3); if (!app->app_local->pk[keyno-1].key) goto leave; /* No such key - ignore. */ err = keygrip_from_canon_sexp (app->app_local->pk[keyno-1].key, app->app_local->pk[keyno-1].keylen, grip); if (err) goto leave; bin2hex (grip, 20, gripstr); sprintf (idbuf, "OPENPGP.%d", keyno); send_status_info (ctrl, "KEYPAIRINFO", gripstr, 40, idbuf, strlen (idbuf), 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, "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.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 relativly 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, 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 = 1; else if (!strcmp (keyid, "OPENPGP.2")) keyno = 2; else if (!strcmp (keyid, "OPENPGP.3")) keyno = 3; else return gpg_error (GPG_ERR_INV_ID); err = get_public_key (app, keyno); if (err) return err; buf = app->app_local->pk[keyno-1].key; if (!buf) return gpg_error (GPG_ERR_NO_PUBKEY); *pklen = app->app_local->pk[keyno-1].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.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; } /* Verify a CHV either using using the pinentry or if possibile 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 rc = 0; char *prompt_buffer = NULL; const char *prompt; pininfo_t pininfo; int minlen = 6; assert (chvno == 1 || chvno == 2); *pinvalue = NULL; 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; if (chvno == 1) { #define PROMPTSTRING _("||Please enter the PIN%%0A[sigs done: %lu]") size_t promptsize = strlen (PROMPTSTRING) + 50; prompt_buffer = xtrymalloc (promptsize); if (!prompt_buffer) return gpg_error_from_syserror (); snprintf (prompt_buffer, promptsize-1, PROMPTSTRING, sigcount); prompt = prompt_buffer; #undef PROMPTSTRING } else prompt = _("||Please enter the PIN"); if (!opt.disable_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); 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 = iso7816_verify (app->slot, 0x80+chvno, *pinvalue, strlen (*pinvalue)); } 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; if (app->did_chv2) return 0; /* We already verified CHV2. */ rc = verify_a_chv (app, pincb, pincb_arg, 2, 0, &pinvalue); 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, strlen (pinvalue)); 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) { void *relptr; unsigned char *value; size_t valuelen; int remaining; char *prompt; *r_prompt = NULL; 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); } if (value[6] == 0) { log_info (_("card is permanently locked!\n")); xfree (relptr); return gpg_error (GPG_ERR_BAD_PIN); } remaining = value[6]; xfree (relptr); log_info(_("%d Admin PIN attempts remaining before card" " is permanently locked\n"), remaining); if (remaining < 3) { /* TRANSLATORS: Do not translate the "|A|" prefix but keep it at the start of the string. Use %%0A to force a linefeed. */ prompt = xtryasprintf (_("|A|Please enter the Admin PIN%%0A" "[remaining attempts: %d]"), remaining); } else prompt = xtrystrdup (_("|A|Please enter the Admin PIN")); 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; 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 = iso7816_verify (app->slot, 0x83, pinvalue, strlen (pinvalue)); 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 need_chv; int special; unsigned int need_v2:1; } table[] = { { "DISP-NAME", 0x005B, 3 }, { "LOGIN-DATA", 0x005E, 3, 2 }, { "DISP-LANG", 0x5F2D, 3 }, { "DISP-SEX", 0x5F35, 3 }, { "PUBKEY-URL", 0x5F50, 3 }, { "CHV-STATUS-1", 0x00C4, 3, 1 }, { "CA-FPR-1", 0x00CA, 3 }, { "CA-FPR-2", 0x00CB, 3 }, { "CA-FPR-3", 0x00CC, 3 }, { "PRIVATE-DO-1", 0x0101, 2 }, { "PRIVATE-DO-2", 0x0102, 3 }, { "PRIVATE-DO-3", 0x0103, 2 }, { "PRIVATE-DO-4", 0x0104, 3 }, { "CERT-3", 0x7F21, 3, 0, 1 }, { "SM-KEY-ENC", 0x00D1, 3, 0, 1 }, { "SM-KEY-MAC", 0x00D2, 3, 0, 1 }, { "KEY-ATTR", 0, 0, 3, 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].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); 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. */ 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 = atoi (chvnostr); 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; (void)ctrl; memset (&pininfo, 0, sizeof pininfo); pininfo.fixedlen = -1; pininfo.minlen = minlen; 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) { 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 (stpcpy (buffer, resetcode), pinvalue); rc = iso7816_reset_retry_counter_with_rc (app->slot, 0x81, buffer, strlen (buffer)); wipememory (buffer, strlen (buffer)); 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 = iso7816_put_data (app->slot, 0, 0xD3, pinvalue, strlen (pinvalue)); } else if (reset_mode) { rc = iso7816_reset_retry_counter (app->slot, 0x81, pinvalue, strlen (pinvalue)); if (!rc && !app->app_local->extcap.is_v2) rc = iso7816_reset_retry_counter (app->slot, 0x82, pinvalue, strlen (pinvalue)); } 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 = iso7816_change_reference_data (app->slot, 0x80 + chvno, oldpinvalue, strlen (oldpinvalue), pinvalue, strlen (pinvalue)); } if (pinvalue) { wipememory (pinvalue, strlen (pinvalue)); xfree (pinvalue); } if (rc) flush_cache_after_error (app); leave: if (resetcode) { wipememory (resetcode, strlen (resetcode)); xfree (resetcode); } if (oldpinvalue) { wipememory (oldpinvalue, strlen (oldpinvalue)); 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, unsigned char **result, size_t *resultlen) { unsigned char privkey[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; (void)app; *result = NULL; *resultlen = 0; /* Build the 7f48 cardholder private key template. */ datalen = 0; tp = privkey; tp += add_tlv (tp, 0x91, ecc_d_len); /* Tag 0x91??? */ datalen += ecc_d_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); 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; 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, unsigned int nbits, gpg_error_t (*pincb)(void*, const char *, char **), void *pincb_arg) { gpg_error_t err; unsigned char *buffer; size_t buflen; void *relptr; assert (keyno >=0 && keyno <= 2); if (nbits > 4096) return gpg_error (GPG_ERR_TOO_LARGE); /* Read the current attributes into a buffer. */ relptr = get_one_do (app, 0xC1+keyno, &buffer, &buflen, NULL); if (!relptr) return gpg_error (GPG_ERR_CARD); if (buflen < 6 || buffer[0] != 1) { /* Attriutes too short or not an RSA key. */ xfree (relptr); return gpg_error (GPG_ERR_CARD); } /* 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; buffer[1] = (nbits >> 8); buffer[2] = nbits; /* Prepare for storing the key. */ err = verify_chv3 (app, pincb, pincb_arg); if (err) { xfree (relptr); return err; } /* Change the attribute. */ err = iso7816_put_data (app->slot, 0, 0xC1+keyno, buffer, buflen); xfree (relptr); if (err) log_error ("error changing size of key %d to %u bits\n", keyno+1, nbits); else log_info ("size of key %d changed to %u bits\n", keyno+1, nbits); flush_cache (app); parse_algorithm_attribute (app, keyno); app->did_chv1 = 0; app->did_chv2 = 0; app->did_chv3 = 0; return err; } /* Helper to process an setattr command for name KEY-ATTR. It expects a string "--force " in (VALUE,VALUELEN). */ 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; char *string; int keyno, algo; unsigned int nbits; /* 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. */ if (sscanf (string, " --force %d %d %u", &keyno, &algo, &nbits) != 3) err = gpg_error (GPG_ERR_INV_DATA); else if (keyno < 1 || keyno > 3) err = gpg_error (GPG_ERR_INV_ID); else if (algo != 1) err = gpg_error (GPG_ERR_PUBKEY_ALGO); /* Not RSA. */ else if (nbits < 1024) err = gpg_error (GPG_ERR_TOO_SHORT); else err = change_keyattr (app, keyno-1, nbits, pincb, pincb_arg); 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; 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 (%u bytes)\n", nbits, (unsigned int)rsa_n_len); if (nbits && nbits != maxbits && app->app_local->extcap.algo_attr_change) { /* Try to switch the key to a new length. */ err = change_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, KEY_TYPE_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; unsigned char *template = NULL; size_t template_len; unsigned char fprbuf[20]; u32 created_at = 0; int curve = CURVE_UNKNOWN; /* (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)) { if ((err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen))) goto leave; if (tok && toklen == 10 && !memcmp (tok, "NIST P-256", 10)) curve = CURVE_NIST_P256; else if (tok && toklen == 9 && !memcmp (tok, "secp256k1", 9)) curve = CURVE_SEC_P256K1; else if (tok && toklen == 7 && !memcmp (tok, "Ed25519", 7)) curve = CURVE_ED25519; else { log_error (_("unsupported curve\n")); err = gpg_error (GPG_ERR_INV_VALUE); goto leave; } } else if (tok && toklen == 1) { const unsigned char **buf2; size_t *buf2len; switch (*tok) { case 'q': buf2 = &ecc_q; buf2len = &ecc_q_len; break; case 'd': buf2 = &ecc_d; buf2len = &ecc_d_len; 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 && curve != CURVE_ED25519) /* It's MPI. 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 (!created_at) { log_error (_("creation timestamp missing\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. */ int exmode; err = build_ecc_privkey_template (app, keyno, ecc_d, ecc_d_len, &template, &template_len); 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 return 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, curve == CURVE_ED25519 ? KEY_TYPE_EDDSA : KEY_TYPE_ECC, curve == CURVE_ED25519 ? "\x09\x2b\x06\x01\x04\x01\xda\x47\x0f\x01" : curve == CURVE_NIST_P256 ? "\x08\x2a\x86\x48\xce\x3d\x03\x01\x07" : "\x05\x2b\x81\x04\x00\x0a", (size_t)(curve == CURVE_ED25519 ? 10 : curve == CURVE_NIST_P256? 9 : 6), ecc_q, ecc_q_len, "\x03\x01\x08\x07", (size_t)4); if (err) goto leave; leave: xfree (template); 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) || (toklen == 4 && memcmp ("ecdh", tok, toklen) == 0) || (toklen == 5 && memcmp ("ecdsa", 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, unsigned int flags, time_t createtime, gpg_error_t (*pincb)(void*, const char *, char **), void *pincb_arg) { int rc; char numbuf[30]; unsigned char fprbuf[20]; const unsigned char *keydata, *m, *e; unsigned char *buffer = NULL; size_t buflen, keydatalen, mlen, elen; time_t created_at; int keyno = atoi (keynostr); int force = (flags & 1); time_t start_at; int exmode; int le_value; unsigned int keybits; if (keyno < 1 || keyno > 3) return gpg_error (GPG_ERR_INV_ID); keyno--; /* 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. */ rc = does_key_exist (app, keyno, 1, force); if (rc) return rc; /* 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. */ keybits = app->app_local->keyattr[keyno].rsa.n_bits; if (keybits > 4096) return gpg_error (GPG_ERR_TOO_LARGE); /* Prepare for key generation by verifying the Admin PIN. */ rc = verify_chv3 (app, pincb, pincb_arg); if (rc) goto leave; /* Test whether we will need extended length mode. (1900 is an arbitrary length which for sure fits into a short apdu.) */ if (app->app_local->cardcap.ext_lc_le && keybits > 1900) { exmode = 1; /* Use extended length w/o a limit. */ le_value = app->app_local->extcap.max_rsp_data; /* 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. */ } else { exmode = 0; le_value = 256; /* Use legacy value. */ } log_info (_("please wait while key is being generated ...\n")); start_at = time (NULL); rc = iso7816_generate_keypair /* # warning key generation temporary replaced by reading an existing key. */ /* rc = iso7816_read_public_key */ (app->slot, exmode, (const unsigned char*)(keyno == 0? "\xB6" : keyno == 1? "\xB8" : "\xA4"), 2, le_value, &buffer, &buflen); if (rc) { rc = gpg_error (GPG_ERR_CARD); log_error (_("generating key failed\n")); goto leave; } log_info (_("key generation completed (%d seconds)\n"), (int)(time (NULL) - start_at)); keydata = find_tlv (buffer, buflen, 0x7F49, &keydatalen); if (!keydata) { rc = gpg_error (GPG_ERR_CARD); log_error (_("response does not contain the public key data\n")); goto leave; } m = find_tlv (keydata, keydatalen, 0x0081, &mlen); if (!m) { rc = gpg_error (GPG_ERR_CARD); log_error (_("response does not contain the RSA modulus\n")); goto leave; } /* log_printhex ("RSA n:", m, mlen); */ send_key_data (ctrl, "n", m, mlen); e = find_tlv (keydata, keydatalen, 0x0082, &elen); if (!e) { rc = gpg_error (GPG_ERR_CARD); log_error (_("response does not contain the RSA public exponent\n")); goto leave; } /* log_printhex ("RSA e:", e, elen); */ send_key_data (ctrl, "e", e, elen); created_at = createtime? createtime : gnupg_get_time (); sprintf (numbuf, "%lu", (unsigned long)created_at); send_status_info (ctrl, "KEY-CREATED-AT", numbuf, (size_t)strlen(numbuf), NULL, 0); rc = store_fpr (app, keyno, (u32)created_at, fprbuf, KEY_TYPE_RSA, m, mlen, e, elen); if (rc) goto leave; send_fpr_if_not_null (ctrl, "KEY-FPR", -1, fprbuf); leave: xfree (buffer); return rc; } 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 >= 1 && keyno <= 3); 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-1)*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 keyno) { 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, keyno, 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; rc = verify_a_chv (app, pincb, pincb_arg, 1, sigcount, &pinvalue); 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, strlen (pinvalue)); 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) { exmode = 1; /* Use extended length. */ le_value = app->app_local->extcap.max_rsp_data; } else { exmode = 0; le_value = 0; } rc = iso7816_compute_ds (app->slot, exmode, data, datalen, le_value, outdata, outdatalen); 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 && (indatalen == 51 || indatalen == 67 || indatalen == 83)) { const char *p = (const char *)indata + 19; indata = p; indatalen -= 19; } else if (app->app_local->keyattr[2].key_type == KEY_TYPE_EDDSA) { 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) { exmode = 1; /* Use extended length. */ le_value = app->app_local->extcap.max_rsp_data; } 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; 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 (app->app_local->keyattr[1].key_type == KEY_TYPE_RSA) { int fixuplen; /* 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) padind = -1; else return gpg_error (GPG_ERR_INV_VALUE); if (app->app_local->cardcap.ext_lc_le && indatalen > 254 ) { exmode = 1; /* Extended length w/o a limit. */ le_value = app->app_local->extcap.max_rsp_data; } 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 (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 ("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 ("SM-Support .....: %s", s->extcap.sm_supported? "yes":"no"); if (s->extcap.sm_supported) log_printf (" (%s)", s->extcap.sm_aes128? "AES-128":"3DES"); log_info ("Max-Cert3-Len ..: %u\n", s->extcap.max_certlen_3); log_info ("Max-Cmd-Data ...: %u\n", s->extcap.max_cmd_data); log_info ("Max-Rsp-Data ...: %u\n", s->extcap.max_rsp_data); 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; } } static int parse_ecc_curve (const unsigned char *buffer, size_t buflen) { int curve; if (buflen == 5 && buffer[5] == 0x22) curve = CURVE_NIST_P384; else if (buflen == 5 && buffer[5] == 0x23) curve = CURVE_NIST_P521; else if (buflen == 8) curve = CURVE_NIST_P256; else if (buflen == 5 && buffer[5] == 0x0a) curve = CURVE_SEC_P256K1; else if (buflen == 9) curve = CURVE_ED25519; else curve = CURVE_UNKNOWN; return curve; } /* 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 == 1 && (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 == 18 || *buffer == 19) /* ECDH or ECDSA */ { app->app_local->keyattr[keyno].key_type = KEY_TYPE_ECC; app->app_local->keyattr[keyno].ecc.curve = parse_ecc_curve (buffer + 1, buflen - 1); if (opt.verbose) log_printf ("ECC, curve=%s\n", get_curve_name (app->app_local->keyattr[keyno].ecc.curve)); } else if (*buffer == 22) /* EdDSA */ { app->app_local->keyattr[keyno].key_type = KEY_TYPE_EDDSA; app->app_local->keyattr[keyno].eddsa.curve = parse_ecc_curve (buffer + 1, buflen - 1); if (opt.verbose) log_printf ("EdDSA, curve=%s\n", get_curve_name (app->app_local->keyattr[keyno].eddsa.curve)); } else if (opt.verbose) 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); } 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; /* 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); } 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); } if (buflen >= 10) { /* Available with v2 cards. */ app->app_local->extcap.sm_aes128 = (buffer[1] == 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]); app->app_local->extcap.max_cmd_data = (buffer[6] << 8 | buffer[7]); app->app_local->extcap.max_rsp_data = (buffer[8] << 8 | buffer[9]); } xfree (relptr); /* Some of the first cards accidently 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; 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/ccid-driver.c b/scd/ccid-driver.c index fdfe1f510..1926f7115 100644 --- a/scd/ccid-driver.c +++ b/scd/ccid-driver.c @@ -1,3838 +1,3838 @@ /* ccid-driver.c - USB ChipCardInterfaceDevices driver * Copyright (C) 2003, 2004, 2005, 2006, 2007 * 2008, 2009, 2013 Free Software Foundation, Inc. * Written by 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 . * * ALTERNATIVELY, this file may be distributed under the terms of the * following license, in which case the provisions of this license are * required INSTEAD OF the GNU General Public License. If you wish to * allow use of your version of this file only under the terms of the * GNU General Public License, and not to allow others to use your * version of this file under the terms of the following license, * indicate your decision by deleting this paragraph and the license * below. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, and the entire permission notice in its entirety, * including the disclaimer of warranties. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote * products derived from this software without specific prior * written permission. * * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED * OF THE POSSIBILITY OF SUCH DAMAGE. */ /* CCID (ChipCardInterfaceDevices) is a specification for accessing smartcard via a reader connected to the USB. This is a limited driver allowing to use some CCID drivers directly without any other specila drivers. This is a fallback driver to be used when nothing else works or the system should be kept minimal for security reasons. It makes use of the libusb library to gain portable access to USB. This driver has been tested with the SCM SCR335 and SPR532 smartcard readers and requires that a reader implements APDU or TPDU level exchange and does fully automatic initialization. */ #ifdef HAVE_CONFIG_H # include #endif #if defined(HAVE_LIBUSB) || defined(TEST) #include #include #include #include #include #include #include #include #include #ifdef HAVE_NPTH # include #endif /*HAVE_NPTH*/ #include #include "scdaemon.h" #include "iso7816.h" #define CCID_DRIVER_INCLUDE_USB_IDS 1 #include "ccid-driver.h" #define DRVNAME "ccid-driver: " /* Depending on how this source is used we either define our error output to go to stderr or to the jnlib based logging functions. We use the latter when GNUPG_MAJOR_VERSION is defines or when both, GNUPG_SCD_MAIN_HEADER and HAVE_JNLIB_LOGGING are defined. */ #if defined(GNUPG_MAJOR_VERSION) \ || (defined(GNUPG_SCD_MAIN_HEADER) && defined(HAVE_JNLIB_LOGGING)) #if defined(GNUPG_SCD_MAIN_HEADER) # include GNUPG_SCD_MAIN_HEADER #elif GNUPG_MAJOR_VERSION == 1 /* GnuPG Version is < 1.9. */ # include "options.h" # include "util.h" # include "memory.h" # include "cardglue.h" # else /* This is the modularized GnuPG 1.9 or later. */ # include "scdaemon.h" #endif # define DEBUGOUT(t) do { if (debug_level) \ log_debug (DRVNAME t); } while (0) # define DEBUGOUT_1(t,a) do { if (debug_level) \ log_debug (DRVNAME t,(a)); } while (0) # define DEBUGOUT_2(t,a,b) do { if (debug_level) \ log_debug (DRVNAME t,(a),(b)); } while (0) # define DEBUGOUT_3(t,a,b,c) do { if (debug_level) \ log_debug (DRVNAME t,(a),(b),(c));} while (0) # define DEBUGOUT_4(t,a,b,c,d) do { if (debug_level) \ log_debug (DRVNAME t,(a),(b),(c),(d));} while (0) # define DEBUGOUT_CONT(t) do { if (debug_level) \ log_printf (t); } while (0) # define DEBUGOUT_CONT_1(t,a) do { if (debug_level) \ log_printf (t,(a)); } while (0) # define DEBUGOUT_CONT_2(t,a,b) do { if (debug_level) \ log_printf (t,(a),(b)); } while (0) # define DEBUGOUT_CONT_3(t,a,b,c) do { if (debug_level) \ log_printf (t,(a),(b),(c)); } while (0) # define DEBUGOUT_LF() do { if (debug_level) \ log_printf ("\n"); } while (0) #else /* Other usage of this source - don't use gnupg specifics. */ # define DEBUGOUT(t) do { if (debug_level) \ fprintf (stderr, DRVNAME t); } while (0) # define DEBUGOUT_1(t,a) do { if (debug_level) \ fprintf (stderr, DRVNAME t, (a)); } while (0) # define DEBUGOUT_2(t,a,b) do { if (debug_level) \ fprintf (stderr, DRVNAME t, (a), (b)); } while (0) # define DEBUGOUT_3(t,a,b,c) do { if (debug_level) \ fprintf (stderr, DRVNAME t, (a), (b), (c)); } while (0) # define DEBUGOUT_4(t,a,b,c,d) do { if (debug_level) \ fprintf (stderr, DRVNAME t, (a), (b), (c), (d));} while(0) # define DEBUGOUT_CONT(t) do { if (debug_level) \ fprintf (stderr, t); } while (0) # define DEBUGOUT_CONT_1(t,a) do { if (debug_level) \ fprintf (stderr, t, (a)); } while (0) # define DEBUGOUT_CONT_2(t,a,b) do { if (debug_level) \ fprintf (stderr, t, (a), (b)); } while (0) # define DEBUGOUT_CONT_3(t,a,b,c) do { if (debug_level) \ fprintf (stderr, t, (a), (b), (c)); } while (0) # define DEBUGOUT_LF() do { if (debug_level) \ putc ('\n', stderr); } while (0) #endif /* This source not used by scdaemon. */ #ifndef EAGAIN #define EAGAIN EWOULDBLOCK #endif enum { RDR_to_PC_NotifySlotChange= 0x50, RDR_to_PC_HardwareError = 0x51, PC_to_RDR_SetParameters = 0x61, PC_to_RDR_IccPowerOn = 0x62, PC_to_RDR_IccPowerOff = 0x63, PC_to_RDR_GetSlotStatus = 0x65, PC_to_RDR_Secure = 0x69, PC_to_RDR_T0APDU = 0x6a, PC_to_RDR_Escape = 0x6b, PC_to_RDR_GetParameters = 0x6c, PC_to_RDR_ResetParameters = 0x6d, PC_to_RDR_IccClock = 0x6e, PC_to_RDR_XfrBlock = 0x6f, PC_to_RDR_Mechanical = 0x71, PC_to_RDR_Abort = 0x72, PC_to_RDR_SetDataRate = 0x73, RDR_to_PC_DataBlock = 0x80, RDR_to_PC_SlotStatus = 0x81, RDR_to_PC_Parameters = 0x82, RDR_to_PC_Escape = 0x83, RDR_to_PC_DataRate = 0x84 }; /* Two macro to detect whether a CCID command has failed and to get the error code. These macros assume that we can access the mandatory first 10 bytes of a CCID message in BUF. */ #define CCID_COMMAND_FAILED(buf) ((buf)[7] & 0x40) #define CCID_ERROR_CODE(buf) (((unsigned char *)(buf))[8]) /* A list and a table with special transport descriptions. */ enum { TRANSPORT_USB = 0, /* Standard USB transport. */ TRANSPORT_CM4040 = 1 /* As used by the Cardman 4040. */ }; static struct { char *name; /* Device name. */ int type; } transports[] = { { "/dev/cmx0", TRANSPORT_CM4040 }, { "/dev/cmx1", TRANSPORT_CM4040 }, { NULL }, }; /* Store information on the driver's state. A pointer to such a structure is used as handle for most functions. */ struct ccid_driver_s { usb_dev_handle *idev; char *rid; int dev_fd; /* -1 for USB transport or file descriptor of the transport device. */ unsigned short id_vendor; unsigned short id_product; unsigned short bcd_device; int ifc_no; int ep_bulk_out; int ep_bulk_in; int ep_intr; int seqno; unsigned char t1_ns; unsigned char t1_nr; unsigned char nonnull_nad; int max_ifsd; int ifsd; int ifsc; unsigned char apdu_level:2; /* Reader supports short APDU level exchange. With a value of 2 short and extended level is supported.*/ unsigned int auto_voltage:1; unsigned int auto_param:1; unsigned int auto_pps:1; unsigned int auto_ifsd:1; unsigned int powered_off:1; unsigned int has_pinpad:2; unsigned int enodev_seen:1; time_t last_progress; /* Last time we sent progress line. */ /* The progress callback and its first arg as supplied to ccid_set_progress_cb. */ void (*progress_cb)(void *, const char *, int, int, int); void *progress_cb_arg; }; static int initialized_usb; /* Tracks whether USB has been initialized. */ static int debug_level; /* Flag to control the debug output. 0 = No debugging 1 = USB I/O info 2 = Level 1 + T=1 protocol tracing 3 = Level 2 + USB/I/O tracing of SlotStatus. */ static unsigned int compute_edc (const unsigned char *data, size_t datalen, int use_crc); static int bulk_out (ccid_driver_t handle, unsigned char *msg, size_t msglen, int no_debug); static int bulk_in (ccid_driver_t handle, unsigned char *buffer, size_t length, size_t *nread, int expected_type, int seqno, int timeout, int no_debug); static int abort_cmd (ccid_driver_t handle, int seqno); static int send_escape_cmd (ccid_driver_t handle, const unsigned char *data, size_t datalen, unsigned char *result, size_t resultmax, size_t *resultlen); /* Convert a little endian stored 4 byte value into an unsigned integer. */ static unsigned int convert_le_u32 (const unsigned char *buf) { - return buf[0] | (buf[1] << 8) | (buf[2] << 16) | (buf[3] << 24); + return buf[0] | (buf[1] << 8) | (buf[2] << 16) | ((unsigned int)buf[3] << 24); } /* Convert a little endian stored 2 byte value into an unsigned integer. */ static unsigned int convert_le_u16 (const unsigned char *buf) { return buf[0] | (buf[1] << 8); } static void set_msg_len (unsigned char *msg, unsigned int length) { msg[1] = length; msg[2] = length >> 8; msg[3] = length >> 16; msg[4] = length >> 24; } static void my_sleep (int seconds) { #ifdef USE_NPTH npth_sleep (seconds); #else # ifdef HAVE_W32_SYSTEM Sleep (seconds*1000); # else sleep (seconds); # endif #endif } static void print_progress (ccid_driver_t handle) { time_t ct = time (NULL); /* We don't want to print progress lines too often. */ if (ct == handle->last_progress) return; if (handle->progress_cb) handle->progress_cb (handle->progress_cb_arg, "card_busy", 'w', 0, 0); handle->last_progress = ct; } /* Pint an error message for a failed CCID command including a textual error code. MSG shall be the CCID message at a minimum of 10 bytes. */ static void print_command_failed (const unsigned char *msg) { const char *t; char buffer[100]; int ec; if (!debug_level) return; ec = CCID_ERROR_CODE (msg); switch (ec) { case 0x00: t = "Command not supported"; break; case 0xE0: t = "Slot busy"; break; case 0xEF: t = "PIN cancelled"; break; case 0xF0: t = "PIN timeout"; break; case 0xF2: t = "Automatic sequence ongoing"; break; case 0xF3: t = "Deactivated Protocol"; break; case 0xF4: t = "Procedure byte conflict"; break; case 0xF5: t = "ICC class not supported"; break; case 0xF6: t = "ICC protocol not supported"; break; case 0xF7: t = "Bad checksum in ATR"; break; case 0xF8: t = "Bad TS in ATR"; break; case 0xFB: t = "An all inclusive hardware error occurred"; break; case 0xFC: t = "Overrun error while talking to the ICC"; break; case 0xFD: t = "Parity error while talking to the ICC"; break; case 0xFE: t = "CCID timed out while talking to the ICC"; break; case 0xFF: t = "Host aborted the current activity"; break; default: if (ec > 0 && ec < 128) sprintf (buffer, "Parameter error at offset %d", ec); else sprintf (buffer, "Error code %02X", ec); t = buffer; break; } DEBUGOUT_1 ("CCID command failed: %s\n", t); } static void print_pr_data (const unsigned char *data, size_t datalen, size_t off) { int any = 0; for (; off < datalen; off++) { if (!any || !(off % 16)) { if (any) DEBUGOUT_LF (); DEBUGOUT_1 (" [%04lu] ", (unsigned long) off); } DEBUGOUT_CONT_1 (" %02X", data[off]); any = 1; } if (any && (off % 16)) DEBUGOUT_LF (); } static void print_p2r_header (const char *name, const unsigned char *msg, size_t msglen) { DEBUGOUT_1 ("%s:\n", name); if (msglen < 7) return; DEBUGOUT_1 (" dwLength ..........: %u\n", convert_le_u32 (msg+1)); DEBUGOUT_1 (" bSlot .............: %u\n", msg[5]); DEBUGOUT_1 (" bSeq ..............: %u\n", msg[6]); } static void print_p2r_iccpoweron (const unsigned char *msg, size_t msglen) { print_p2r_header ("PC_to_RDR_IccPowerOn", msg, msglen); if (msglen < 10) return; DEBUGOUT_2 (" bPowerSelect ......: 0x%02x (%s)\n", msg[7], msg[7] == 0? "auto": msg[7] == 1? "5.0 V": msg[7] == 2? "3.0 V": msg[7] == 3? "1.8 V":""); print_pr_data (msg, msglen, 8); } static void print_p2r_iccpoweroff (const unsigned char *msg, size_t msglen) { print_p2r_header ("PC_to_RDR_IccPowerOff", msg, msglen); print_pr_data (msg, msglen, 7); } static void print_p2r_getslotstatus (const unsigned char *msg, size_t msglen) { print_p2r_header ("PC_to_RDR_GetSlotStatus", msg, msglen); print_pr_data (msg, msglen, 7); } static void print_p2r_xfrblock (const unsigned char *msg, size_t msglen) { unsigned int val; print_p2r_header ("PC_to_RDR_XfrBlock", msg, msglen); if (msglen < 10) return; DEBUGOUT_1 (" bBWI ..............: 0x%02x\n", msg[7]); val = convert_le_u16 (msg+8); DEBUGOUT_2 (" wLevelParameter ...: 0x%04x%s\n", val, val == 1? " (continued)": val == 2? " (continues+ends)": val == 3? " (continues+continued)": val == 16? " (DataBlock-expected)":""); print_pr_data (msg, msglen, 10); } static void print_p2r_getparameters (const unsigned char *msg, size_t msglen) { print_p2r_header ("PC_to_RDR_GetParameters", msg, msglen); print_pr_data (msg, msglen, 7); } static void print_p2r_resetparameters (const unsigned char *msg, size_t msglen) { print_p2r_header ("PC_to_RDR_ResetParameters", msg, msglen); print_pr_data (msg, msglen, 7); } static void print_p2r_setparameters (const unsigned char *msg, size_t msglen) { print_p2r_header ("PC_to_RDR_SetParameters", msg, msglen); if (msglen < 10) return; DEBUGOUT_1 (" bProtocolNum ......: 0x%02x\n", msg[7]); print_pr_data (msg, msglen, 8); } static void print_p2r_escape (const unsigned char *msg, size_t msglen) { print_p2r_header ("PC_to_RDR_Escape", msg, msglen); print_pr_data (msg, msglen, 7); } static void print_p2r_iccclock (const unsigned char *msg, size_t msglen) { print_p2r_header ("PC_to_RDR_IccClock", msg, msglen); if (msglen < 10) return; DEBUGOUT_1 (" bClockCommand .....: 0x%02x\n", msg[7]); print_pr_data (msg, msglen, 8); } static void print_p2r_to0apdu (const unsigned char *msg, size_t msglen) { print_p2r_header ("PC_to_RDR_T0APDU", msg, msglen); if (msglen < 10) return; DEBUGOUT_1 (" bmChanges .........: 0x%02x\n", msg[7]); DEBUGOUT_1 (" bClassGetResponse .: 0x%02x\n", msg[8]); DEBUGOUT_1 (" bClassEnvelope ....: 0x%02x\n", msg[9]); print_pr_data (msg, msglen, 10); } static void print_p2r_secure (const unsigned char *msg, size_t msglen) { unsigned int val; print_p2r_header ("PC_to_RDR_Secure", msg, msglen); if (msglen < 10) return; DEBUGOUT_1 (" bBMI ..............: 0x%02x\n", msg[7]); val = convert_le_u16 (msg+8); DEBUGOUT_2 (" wLevelParameter ...: 0x%04x%s\n", val, val == 1? " (continued)": val == 2? " (continues+ends)": val == 3? " (continues+continued)": val == 16? " (DataBlock-expected)":""); print_pr_data (msg, msglen, 10); } static void print_p2r_mechanical (const unsigned char *msg, size_t msglen) { print_p2r_header ("PC_to_RDR_Mechanical", msg, msglen); if (msglen < 10) return; DEBUGOUT_1 (" bFunction .........: 0x%02x\n", msg[7]); print_pr_data (msg, msglen, 8); } static void print_p2r_abort (const unsigned char *msg, size_t msglen) { print_p2r_header ("PC_to_RDR_Abort", msg, msglen); print_pr_data (msg, msglen, 7); } static void print_p2r_setdatarate (const unsigned char *msg, size_t msglen) { print_p2r_header ("PC_to_RDR_SetDataRate", msg, msglen); if (msglen < 10) return; print_pr_data (msg, msglen, 7); } static void print_p2r_unknown (const unsigned char *msg, size_t msglen) { print_p2r_header ("Unknown PC_to_RDR command", msg, msglen); if (msglen < 10) return; print_pr_data (msg, msglen, 0); } static void print_r2p_header (const char *name, const unsigned char *msg, size_t msglen) { DEBUGOUT_1 ("%s:\n", name); if (msglen < 9) return; DEBUGOUT_1 (" dwLength ..........: %u\n", convert_le_u32 (msg+1)); DEBUGOUT_1 (" bSlot .............: %u\n", msg[5]); DEBUGOUT_1 (" bSeq ..............: %u\n", msg[6]); DEBUGOUT_1 (" bStatus ...........: %u\n", msg[7]); if (msg[8]) DEBUGOUT_1 (" bError ............: %u\n", msg[8]); } static void print_r2p_datablock (const unsigned char *msg, size_t msglen) { print_r2p_header ("RDR_to_PC_DataBlock", msg, msglen); if (msglen < 10) return; if (msg[9]) DEBUGOUT_2 (" bChainParameter ...: 0x%02x%s\n", msg[9], msg[9] == 1? " (continued)": msg[9] == 2? " (continues+ends)": msg[9] == 3? " (continues+continued)": msg[9] == 16? " (XferBlock-expected)":""); print_pr_data (msg, msglen, 10); } static void print_r2p_slotstatus (const unsigned char *msg, size_t msglen) { print_r2p_header ("RDR_to_PC_SlotStatus", msg, msglen); if (msglen < 10) return; DEBUGOUT_2 (" bClockStatus ......: 0x%02x%s\n", msg[9], msg[9] == 0? " (running)": msg[9] == 1? " (stopped-L)": msg[9] == 2? " (stopped-H)": msg[9] == 3? " (stopped)":""); print_pr_data (msg, msglen, 10); } static void print_r2p_parameters (const unsigned char *msg, size_t msglen) { print_r2p_header ("RDR_to_PC_Parameters", msg, msglen); if (msglen < 10) return; DEBUGOUT_1 (" protocol ..........: T=%d\n", msg[9]); if (msglen == 17 && msg[9] == 1) { /* Protocol T=1. */ DEBUGOUT_1 (" bmFindexDindex ....: %02X\n", msg[10]); DEBUGOUT_1 (" bmTCCKST1 .........: %02X\n", msg[11]); DEBUGOUT_1 (" bGuardTimeT1 ......: %02X\n", msg[12]); DEBUGOUT_1 (" bmWaitingIntegersT1: %02X\n", msg[13]); DEBUGOUT_1 (" bClockStop ........: %02X\n", msg[14]); DEBUGOUT_1 (" bIFSC .............: %d\n", msg[15]); DEBUGOUT_1 (" bNadValue .........: %d\n", msg[16]); } else print_pr_data (msg, msglen, 10); } static void print_r2p_escape (const unsigned char *msg, size_t msglen) { print_r2p_header ("RDR_to_PC_Escape", msg, msglen); if (msglen < 10) return; DEBUGOUT_1 (" buffer[9] .........: %02X\n", msg[9]); print_pr_data (msg, msglen, 10); } static void print_r2p_datarate (const unsigned char *msg, size_t msglen) { print_r2p_header ("RDR_to_PC_DataRate", msg, msglen); if (msglen < 10) return; if (msglen >= 18) { DEBUGOUT_1 (" dwClockFrequency ..: %u\n", convert_le_u32 (msg+10)); DEBUGOUT_1 (" dwDataRate ..... ..: %u\n", convert_le_u32 (msg+14)); print_pr_data (msg, msglen, 18); } else print_pr_data (msg, msglen, 10); } static void print_r2p_unknown (const unsigned char *msg, size_t msglen) { print_r2p_header ("Unknown RDR_to_PC command", msg, msglen); if (msglen < 10) return; DEBUGOUT_1 (" bMessageType ......: %02X\n", msg[0]); DEBUGOUT_1 (" buffer[9] .........: %02X\n", msg[9]); print_pr_data (msg, msglen, 10); } /* Given a handle used for special transport prepare it for use. In particular setup all information in way that resembles what parse_cccid_descriptor does. */ static void prepare_special_transport (ccid_driver_t handle) { assert (!handle->id_vendor); handle->nonnull_nad = 0; handle->auto_ifsd = 0; handle->max_ifsd = 32; handle->ifsd = 0; handle->has_pinpad = 0; handle->apdu_level = 0; switch (handle->id_product) { case TRANSPORT_CM4040: DEBUGOUT ("setting up transport for CardMan 4040\n"); handle->apdu_level = 1; break; default: assert (!"transport not defined"); } } /* Parse a CCID descriptor, optionally print all available features and test whether this reader is usable by this driver. Returns 0 if it is usable. Note, that this code is based on the one in lsusb.c of the usb-utils package, I wrote on 2003-09-01. -wk. */ static int parse_ccid_descriptor (ccid_driver_t handle, const unsigned char *buf, size_t buflen) { unsigned int i; unsigned int us; int have_t1 = 0, have_tpdu=0; handle->nonnull_nad = 0; handle->auto_ifsd = 0; handle->max_ifsd = 32; handle->ifsd = 0; handle->has_pinpad = 0; handle->apdu_level = 0; handle->auto_voltage = 0; handle->auto_param = 0; handle->auto_pps = 0; DEBUGOUT_3 ("idVendor: %04X idProduct: %04X bcdDevice: %04X\n", handle->id_vendor, handle->id_product, handle->bcd_device); if (buflen < 54 || buf[0] < 54) { DEBUGOUT ("CCID device descriptor is too short\n"); return -1; } DEBUGOUT ("ChipCard Interface Descriptor:\n"); DEBUGOUT_1 (" bLength %5u\n", buf[0]); DEBUGOUT_1 (" bDescriptorType %5u\n", buf[1]); DEBUGOUT_2 (" bcdCCID %2x.%02x", buf[3], buf[2]); if (buf[3] != 1 || buf[2] != 0) DEBUGOUT_CONT(" (Warning: Only accurate for version 1.0)"); DEBUGOUT_LF (); DEBUGOUT_1 (" nMaxSlotIndex %5u\n", buf[4]); DEBUGOUT_2 (" bVoltageSupport %5u %s\n", buf[5], (buf[5] == 1? "5.0V" : buf[5] == 2? "3.0V" : buf[5] == 3? "1.8V":"?")); us = convert_le_u32 (buf+6); DEBUGOUT_1 (" dwProtocols %5u ", us); if ((us & 1)) DEBUGOUT_CONT (" T=0"); if ((us & 2)) { DEBUGOUT_CONT (" T=1"); have_t1 = 1; } if ((us & ~3)) DEBUGOUT_CONT (" (Invalid values detected)"); DEBUGOUT_LF (); us = convert_le_u32(buf+10); DEBUGOUT_1 (" dwDefaultClock %5u\n", us); us = convert_le_u32(buf+14); DEBUGOUT_1 (" dwMaxiumumClock %5u\n", us); DEBUGOUT_1 (" bNumClockSupported %5u\n", buf[18]); us = convert_le_u32(buf+19); DEBUGOUT_1 (" dwDataRate %7u bps\n", us); us = convert_le_u32(buf+23); DEBUGOUT_1 (" dwMaxDataRate %7u bps\n", us); DEBUGOUT_1 (" bNumDataRatesSupp. %5u\n", buf[27]); us = convert_le_u32(buf+28); DEBUGOUT_1 (" dwMaxIFSD %5u\n", us); handle->max_ifsd = us; us = convert_le_u32(buf+32); DEBUGOUT_1 (" dwSyncProtocols %08X ", us); if ((us&1)) DEBUGOUT_CONT ( " 2-wire"); if ((us&2)) DEBUGOUT_CONT ( " 3-wire"); if ((us&4)) DEBUGOUT_CONT ( " I2C"); DEBUGOUT_LF (); us = convert_le_u32(buf+36); DEBUGOUT_1 (" dwMechanical %08X ", us); if ((us & 1)) DEBUGOUT_CONT (" accept"); if ((us & 2)) DEBUGOUT_CONT (" eject"); if ((us & 4)) DEBUGOUT_CONT (" capture"); if ((us & 8)) DEBUGOUT_CONT (" lock"); DEBUGOUT_LF (); us = convert_le_u32(buf+40); DEBUGOUT_1 (" dwFeatures %08X\n", us); if ((us & 0x0002)) { DEBUGOUT (" Auto configuration based on ATR (assumes auto voltage)\n"); handle->auto_voltage = 1; } if ((us & 0x0004)) DEBUGOUT (" Auto activation on insert\n"); if ((us & 0x0008)) { DEBUGOUT (" Auto voltage selection\n"); handle->auto_voltage = 1; } if ((us & 0x0010)) DEBUGOUT (" Auto clock change\n"); if ((us & 0x0020)) DEBUGOUT (" Auto baud rate change\n"); if ((us & 0x0040)) { DEBUGOUT (" Auto parameter negotiation made by CCID\n"); handle->auto_param = 1; } else if ((us & 0x0080)) { DEBUGOUT (" Auto PPS made by CCID\n"); handle->auto_pps = 1; } if ((us & (0x0040 | 0x0080)) == (0x0040 | 0x0080)) DEBUGOUT (" WARNING: conflicting negotiation features\n"); if ((us & 0x0100)) DEBUGOUT (" CCID can set ICC in clock stop mode\n"); if ((us & 0x0200)) { DEBUGOUT (" NAD value other than 0x00 accepted\n"); handle->nonnull_nad = 1; } if ((us & 0x0400)) { DEBUGOUT (" Auto IFSD exchange\n"); handle->auto_ifsd = 1; } if ((us & 0x00010000)) { DEBUGOUT (" TPDU level exchange\n"); have_tpdu = 1; } else if ((us & 0x00020000)) { DEBUGOUT (" Short APDU level exchange\n"); handle->apdu_level = 1; } else if ((us & 0x00040000)) { DEBUGOUT (" Short and extended APDU level exchange\n"); handle->apdu_level = 2; } else if ((us & 0x00070000)) DEBUGOUT (" WARNING: conflicting exchange levels\n"); us = convert_le_u32(buf+44); DEBUGOUT_1 (" dwMaxCCIDMsgLen %5u\n", us); DEBUGOUT ( " bClassGetResponse "); if (buf[48] == 0xff) DEBUGOUT_CONT ("echo\n"); else DEBUGOUT_CONT_1 (" %02X\n", buf[48]); DEBUGOUT ( " bClassEnvelope "); if (buf[49] == 0xff) DEBUGOUT_CONT ("echo\n"); else DEBUGOUT_CONT_1 (" %02X\n", buf[48]); DEBUGOUT ( " wlcdLayout "); if (!buf[50] && !buf[51]) DEBUGOUT_CONT ("none\n"); else DEBUGOUT_CONT_2 ("%u cols %u lines\n", buf[50], buf[51]); DEBUGOUT_1 (" bPINSupport %5u ", buf[52]); if ((buf[52] & 1)) { DEBUGOUT_CONT ( " verification"); handle->has_pinpad |= 1; } if ((buf[52] & 2)) { DEBUGOUT_CONT ( " modification"); handle->has_pinpad |= 2; } DEBUGOUT_LF (); DEBUGOUT_1 (" bMaxCCIDBusySlots %5u\n", buf[53]); if (buf[0] > 54) { DEBUGOUT (" junk "); for (i=54; i < buf[0]-54; i++) DEBUGOUT_CONT_1 (" %02X", buf[i]); DEBUGOUT_LF (); } if (!have_t1 || !(have_tpdu || handle->apdu_level)) { DEBUGOUT ("this drivers requires that the reader supports T=1, " "TPDU or APDU level exchange - this is not available\n"); return -1; } /* SCM drivers get stuck in their internal USB stack if they try to send a frame of n*wMaxPacketSize back to us. Given that wMaxPacketSize is 64 for these readers we set the IFSD to a value lower than that: 64 - 10 CCID header - 4 T1frame - 2 reserved = 48 Product Ids: 0xe001 - SCR 331 0x5111 - SCR 331-DI 0x5115 - SCR 335 0xe003 - SPR 532 The 0x5117 - SCR 3320 USB ID-000 reader seems to be very slow but enabling this workaround boosts the performance to a a more or less acceptable level (tested by David). */ if (handle->id_vendor == VENDOR_SCM && handle->max_ifsd > 48 && ( (handle->id_product == SCM_SCR331 && handle->bcd_device < 0x0516) ||(handle->id_product == SCM_SCR331DI && handle->bcd_device < 0x0620) ||(handle->id_product == SCM_SCR335 && handle->bcd_device < 0x0514) ||(handle->id_product == SCM_SPR532 && handle->bcd_device < 0x0504) ||(handle->id_product == SCM_SCR3320 && handle->bcd_device < 0x0522) )) { DEBUGOUT ("enabling workaround for buggy SCM readers\n"); handle->max_ifsd = 48; } if (handle->id_vendor == VENDOR_GEMPC && handle->id_product == GEMPC_CT30) { DEBUGOUT ("enabling product quirk: disable non-null NAD\n"); handle->nonnull_nad = 0; } return 0; } static char * get_escaped_usb_string (usb_dev_handle *idev, int idx, const char *prefix, const char *suffix) { int rc; unsigned char buf[280]; unsigned char *s; unsigned int langid; size_t i, n, len; char *result; if (!idx) return NULL; /* Fixme: The next line is for the current Valgrid without support for USB IOCTLs. */ memset (buf, 0, sizeof buf); /* First get the list of supported languages and use the first one. If we do don't find it we try to use English. Note that this is all in a 2 bute Unicode encoding using little endian. */ rc = usb_control_msg (idev, USB_ENDPOINT_IN, USB_REQ_GET_DESCRIPTOR, (USB_DT_STRING << 8), 0, (char*)buf, sizeof buf, 1000 /* ms timeout */); if (rc < 4) langid = 0x0409; /* English. */ else langid = (buf[3] << 8) | buf[2]; rc = usb_control_msg (idev, USB_ENDPOINT_IN, USB_REQ_GET_DESCRIPTOR, (USB_DT_STRING << 8) + idx, langid, (char*)buf, sizeof buf, 1000 /* ms timeout */); if (rc < 2 || buf[1] != USB_DT_STRING) return NULL; /* Error or not a string. */ len = buf[0]; if (len > rc) return NULL; /* Larger than our buffer. */ for (s=buf+2, i=2, n=0; i+1 < len; i += 2, s += 2) { if (s[1]) n++; /* High byte set. */ else if (*s <= 0x20 || *s >= 0x7f || *s == '%' || *s == ':') n += 3 ; else n++; } result = malloc (strlen (prefix) + n + strlen (suffix) + 1); if (!result) return NULL; strcpy (result, prefix); n = strlen (prefix); for (s=buf+2, i=2; i+1 < len; i += 2, s += 2) { if (s[1]) result[n++] = '\xff'; /* High byte set. */ else if (*s <= 0x20 || *s >= 0x7f || *s == '%' || *s == ':') { sprintf (result+n, "%%%02X", *s); n += 3; } else result[n++] = *s; } strcpy (result+n, suffix); return result; } /* This function creates an reader id to be used to find the same physical reader after a reset. It returns an allocated and possibly percent escaped string or NULL if not enough memory is available. */ static char * make_reader_id (usb_dev_handle *idev, unsigned int vendor, unsigned int product, unsigned char serialno_index) { char *rid; char prefix[20]; sprintf (prefix, "%04X:%04X:", (vendor & 0xffff), (product & 0xffff)); rid = get_escaped_usb_string (idev, serialno_index, prefix, ":0"); if (!rid) { rid = malloc (strlen (prefix) + 3 + 1); if (!rid) return NULL; strcpy (rid, prefix); strcat (rid, "X:0"); } return rid; } /* Helper to find the endpoint from an interface descriptor. */ static int find_endpoint (struct usb_interface_descriptor *ifcdesc, int mode) { int no; int want_bulk_in = 0; if (mode == 1) want_bulk_in = 0x80; for (no=0; no < ifcdesc->bNumEndpoints; no++) { struct usb_endpoint_descriptor *ep = ifcdesc->endpoint + no; if (ep->bDescriptorType != USB_DT_ENDPOINT) ; else if (mode == 2 && ((ep->bmAttributes & USB_ENDPOINT_TYPE_MASK) == USB_ENDPOINT_TYPE_INTERRUPT) && (ep->bEndpointAddress & 0x80)) return (ep->bEndpointAddress & 0x0f); else if (((ep->bmAttributes & USB_ENDPOINT_TYPE_MASK) == USB_ENDPOINT_TYPE_BULK) && (ep->bEndpointAddress & 0x80) == want_bulk_in) return (ep->bEndpointAddress & 0x0f); } /* Should never happen. */ return mode == 2? 0x83 : mode == 1? 0x82 :1; } /* Helper for scan_or_find_devices. This function returns true if a requested device has been found or the caller should stop scanning for other reasons. */ static int scan_or_find_usb_device (int scan_mode, int *readerno, int *count, char **rid_list, const char *readerid, struct usb_device *dev, char **r_rid, struct usb_device **r_dev, usb_dev_handle **r_idev, unsigned char **ifcdesc_extra, size_t *ifcdesc_extra_len, int *interface_number, int *ep_bulk_out, int *ep_bulk_in, int *ep_intr) { int cfg_no; int ifc_no; int set_no; struct usb_config_descriptor *config; struct usb_interface *interface; struct usb_interface_descriptor *ifcdesc; char *rid; usb_dev_handle *idev; *r_idev = NULL; for (cfg_no=0; cfg_no < dev->descriptor.bNumConfigurations; cfg_no++) { config = dev->config + cfg_no; if(!config) continue; for (ifc_no=0; ifc_no < config->bNumInterfaces; ifc_no++) { interface = config->interface + ifc_no; if (!interface) continue; for (set_no=0; set_no < interface->num_altsetting; set_no++) { ifcdesc = (interface->altsetting + set_no); /* The second condition is for older SCM SPR 532 who did not know about the assigned CCID class. The third condition does the same for a Cherry SmartTerminal ST-2000. Instead of trying to interpret the strings we simply check the product ID. */ if (ifcdesc && ifcdesc->extra && ((ifcdesc->bInterfaceClass == 11 && ifcdesc->bInterfaceSubClass == 0 && ifcdesc->bInterfaceProtocol == 0) || (ifcdesc->bInterfaceClass == 255 && dev->descriptor.idVendor == VENDOR_SCM && dev->descriptor.idProduct == SCM_SPR532) || (ifcdesc->bInterfaceClass == 255 && dev->descriptor.idVendor == VENDOR_CHERRY && dev->descriptor.idProduct == CHERRY_ST2000))) { idev = usb_open (dev); if (!idev) { DEBUGOUT_1 ("usb_open failed: %s\n", strerror (errno)); continue; /* with next setting. */ } rid = make_reader_id (idev, dev->descriptor.idVendor, dev->descriptor.idProduct, dev->descriptor.iSerialNumber); if (rid) { if (scan_mode) { char *p; /* We are collecting infos about all available CCID readers. Store them and continue. */ DEBUGOUT_2 ("found CCID reader %d (ID=%s)\n", *count, rid ); p = malloc ((*rid_list? strlen (*rid_list):0) + 1 + strlen (rid) + 1); if (p) { *p = 0; if (*rid_list) { strcat (p, *rid_list); free (*rid_list); } strcat (p, rid); strcat (p, "\n"); *rid_list = p; } else /* Out of memory. */ free (rid); rid = NULL; ++*count; } else if (!*readerno || (*readerno < 0 && readerid && !strcmp (readerid, rid))) { /* We found the requested reader. */ if (ifcdesc_extra && ifcdesc_extra_len) { *ifcdesc_extra = malloc (ifcdesc ->extralen); if (!*ifcdesc_extra) { usb_close (idev); free (rid); return 1; /* Out of core. */ } memcpy (*ifcdesc_extra, ifcdesc->extra, ifcdesc->extralen); *ifcdesc_extra_len = ifcdesc->extralen; } if (interface_number) *interface_number = (ifcdesc->bInterfaceNumber); if (ep_bulk_out) *ep_bulk_out = find_endpoint (ifcdesc, 0); if (ep_bulk_in) *ep_bulk_in = find_endpoint (ifcdesc, 1); if (ep_intr) *ep_intr = find_endpoint (ifcdesc, 2); if (r_dev) *r_dev = dev; if (r_rid) { *r_rid = rid; rid = NULL; } else free (rid); *r_idev = idev; return 1; /* Found requested device. */ } else { /* This is not yet the reader we want. fixme: We should avoid the extra usb_open in this case. */ if (*readerno >= 0) --*readerno; } free (rid); } usb_close (idev); idev = NULL; return 0; } } } } return 0; } /* Combination function to either scan all CCID devices or to find and open one specific device. The function returns 0 if a reader has been found or when a scan returned without error. With READERNO = -1 and READERID is NULL, scan mode is used and R_RID should be the address where to store the list of reader_ids we found. If on return this list is empty, no CCID device has been found; otherwise it points to an allocated linked list of reader IDs. Note that in this mode the function always returns NULL. With READERNO >= 0 or READERID is not NULL find mode is used. This uses the same algorithm as the scan mode but stops and returns at the entry number READERNO and return the handle for the the opened USB device. If R_RID is not NULL it will receive the reader ID of that device. If R_DEV is not NULL it will the device pointer of that device. If IFCDESC_EXTRA is NOT NULL it will receive a malloced copy of the interfaces "extra: data filed; IFCDESC_EXTRA_LEN receive the length of this field. If there is no reader with number READERNO or that reader is not usable by our implementation NULL will be returned. The caller must close a returned USB device handle and free (if not passed as NULL) the returned reader ID info as well as the IFCDESC_EXTRA. On error NULL will get stored at R_RID, R_DEV, IFCDESC_EXTRA and IFCDESC_EXTRA_LEN. With READERID being -1 the function stops if the READERID was found. If R_FD is not -1 on return the device is not using USB for transport but the device associated with that file descriptor. In this case INTERFACE will receive the transport type and the other USB specific return values are not used; the return value is (void*)(1). Note that the first entry of the returned reader ID list in scan mode corresponds with a READERNO of 0 in find mode. */ static int scan_or_find_devices (int readerno, const char *readerid, char **r_rid, struct usb_device **r_dev, unsigned char **ifcdesc_extra, size_t *ifcdesc_extra_len, int *interface_number, int *ep_bulk_out, int *ep_bulk_in, int *ep_intr, usb_dev_handle **r_idev, int *r_fd) { char *rid_list = NULL; int count = 0; struct usb_bus *busses, *bus; struct usb_device *dev = NULL; usb_dev_handle *idev = NULL; int scan_mode = (readerno == -1 && !readerid); int i; /* Set return values to a default. */ if (r_rid) *r_rid = NULL; if (r_dev) *r_dev = NULL; if (ifcdesc_extra) *ifcdesc_extra = NULL; if (ifcdesc_extra_len) *ifcdesc_extra_len = 0; if (interface_number) *interface_number = 0; if (r_idev) *r_idev = NULL; if (r_fd) *r_fd = -1; /* See whether we want scan or find mode. */ if (scan_mode) { assert (r_rid); } usb_find_busses(); usb_find_devices(); #ifdef HAVE_USB_GET_BUSSES busses = usb_get_busses(); #else busses = usb_busses; #endif for (bus = busses; bus; bus = bus->next) { for (dev = bus->devices; dev; dev = dev->next) { if (scan_or_find_usb_device (scan_mode, &readerno, &count, &rid_list, readerid, dev, r_rid, r_dev, &idev, ifcdesc_extra, ifcdesc_extra_len, interface_number, ep_bulk_out, ep_bulk_in, ep_intr)) { /* Found requested device or out of core. */ if (!idev) { free (rid_list); return -1; /* error */ } *r_idev = idev; return 0; } } } /* Now check whether there are any devices with special transport types. */ for (i=0; transports[i].name; i++) { int fd; char *rid, *p; fd = open (transports[i].name, O_RDWR); if (fd == -1 && scan_mode && errno == EBUSY) { /* Ignore this error in scan mode because it indicates that the device exists but is already open (most likely by us) and thus in general suitable as a reader. */ } else if (fd == -1) { DEBUGOUT_2 ("failed to open '%s': %s\n", transports[i].name, strerror (errno)); continue; } rid = malloc (strlen (transports[i].name) + 30 + 10); if (!rid) { if (fd != -1) close (fd); free (rid_list); return -1; /* Error. */ } sprintf (rid, "0000:%04X:%s:0", transports[i].type, transports[i].name); if (scan_mode) { DEBUGOUT_2 ("found CCID reader %d (ID=%s)\n", count, rid); p = malloc ((rid_list? strlen (rid_list):0) + 1 + strlen (rid) + 1); if (!p) { if (fd != -1) close (fd); free (rid_list); free (rid); return -1; /* Error. */ } *p = 0; if (rid_list) { strcat (p, rid_list); free (rid_list); } strcat (p, rid); strcat (p, "\n"); rid_list = p; ++count; } else if (!readerno || (readerno < 0 && readerid && !strcmp (readerid, rid))) { /* Found requested device. */ if (interface_number) *interface_number = transports[i].type; if (r_rid) *r_rid = rid; else free (rid); if (r_fd) *r_fd = fd; return 0; /* Okay, found device */ } else /* This is not yet the reader we want. */ { if (readerno >= 0) --readerno; } free (rid); if (fd != -1) close (fd); } if (scan_mode) { *r_rid = rid_list; return 0; } else return -1; } /* Set the level of debugging to LEVEL and return the old level. -1 just returns the old level. A level of 0 disables debugging, 1 enables debugging, 2 enables additional tracing of the T=1 protocol, 3 additionally enables debugging for GetSlotStatus, other values are not yet defined. Note that libusb may provide its own debugging feature which is enabled by setting the envvar USB_DEBUG. */ int ccid_set_debug_level (int level) { int old = debug_level; if (level != -1) debug_level = level; return old; } char * ccid_get_reader_list (void) { char *reader_list; if (!initialized_usb) { usb_init (); initialized_usb = 1; } if (scan_or_find_devices (-1, NULL, &reader_list, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL)) return NULL; /* Error. */ return reader_list; } /* Vendor specific custom initialization. */ static int ccid_vendor_specific_init (ccid_driver_t handle) { if (handle->id_vendor == VENDOR_VEGA && handle->id_product == VEGA_ALPHA) { int r; /* * Vega alpha has a feature to show retry counter on the pinpad * display. But it assumes that the card returns the value of * retry counter by VERIFY with empty data (return code of * 63Cx). Unfortunately, existing OpenPGP cards don't support * VERIFY command with empty data. This vendor specific command * sequence is to disable the feature. */ const unsigned char cmd[] = { '\xb5', '\x01', '\x00', '\x03', '\x00' }; r = send_escape_cmd (handle, cmd, sizeof (cmd), NULL, 0, NULL); if (r != 0 && r != CCID_DRIVER_ERR_CARD_INACTIVE && r != CCID_DRIVER_ERR_NO_CARD) return r; } return 0; } /* Open the reader with the internal number READERNO and return a pointer to be used as handle in HANDLE. Returns 0 on success. */ int ccid_open_reader (ccid_driver_t *handle, const char *readerid) { int rc = 0; struct usb_device *dev = NULL; usb_dev_handle *idev = NULL; int dev_fd = -1; char *rid = NULL; unsigned char *ifcdesc_extra = NULL; size_t ifcdesc_extra_len; int readerno; int ifc_no, ep_bulk_out, ep_bulk_in, ep_intr; *handle = NULL; if (!initialized_usb) { usb_init (); initialized_usb = 1; } /* 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 (readerid && strchr (readerid, ':')) readerno = -1; /* We want to use the readerid. */ else if (readerid) { readerno = atoi (readerid); if (readerno < 0) { DEBUGOUT ("no CCID readers found\n"); rc = CCID_DRIVER_ERR_NO_READER; goto leave; } } else readerno = 0; /* Default. */ if (scan_or_find_devices (readerno, readerid, &rid, &dev, &ifcdesc_extra, &ifcdesc_extra_len, &ifc_no, &ep_bulk_out, &ep_bulk_in, &ep_intr, &idev, &dev_fd) ) { if (readerno == -1) DEBUGOUT_1 ("no CCID reader with ID %s\n", readerid ); else DEBUGOUT_1 ("no CCID reader with number %d\n", readerno ); rc = CCID_DRIVER_ERR_NO_READER; goto leave; } /* Okay, this is a CCID reader. */ *handle = calloc (1, sizeof **handle); if (!*handle) { DEBUGOUT ("out of memory\n"); rc = CCID_DRIVER_ERR_OUT_OF_CORE; goto leave; } (*handle)->rid = rid; if (idev) /* Regular USB transport. */ { (*handle)->idev = idev; (*handle)->dev_fd = -1; (*handle)->id_vendor = dev->descriptor.idVendor; (*handle)->id_product = dev->descriptor.idProduct; (*handle)->bcd_device = dev->descriptor.bcdDevice; (*handle)->ifc_no = ifc_no; (*handle)->ep_bulk_out = ep_bulk_out; (*handle)->ep_bulk_in = ep_bulk_in; (*handle)->ep_intr = ep_intr; } else if (dev_fd != -1) /* Device transport. */ { (*handle)->idev = NULL; (*handle)->dev_fd = dev_fd; (*handle)->id_vendor = 0; /* Magic vendor for special transport. */ (*handle)->id_product = ifc_no; /* Transport type */ prepare_special_transport (*handle); } else { assert (!"no transport"); /* Bug. */ } DEBUGOUT_2 ("using CCID reader %d (ID=%s)\n", readerno, rid ); if (idev) { if (parse_ccid_descriptor (*handle, ifcdesc_extra, ifcdesc_extra_len)) { DEBUGOUT ("device not supported\n"); rc = CCID_DRIVER_ERR_NO_READER; goto leave; } rc = usb_claim_interface (idev, ifc_no); if (rc) { DEBUGOUT_1 ("usb_claim_interface failed: %d\n", rc); rc = CCID_DRIVER_ERR_CARD_IO_ERROR; goto leave; } } rc = ccid_vendor_specific_init (*handle); leave: free (ifcdesc_extra); if (rc) { free (rid); if (idev) usb_close (idev); if (dev_fd != -1) close (dev_fd); free (*handle); *handle = NULL; } return rc; } static void do_close_reader (ccid_driver_t handle) { int rc; unsigned char msg[100]; size_t msglen; unsigned char seqno; if (!handle->powered_off) { msg[0] = PC_to_RDR_IccPowerOff; msg[5] = 0; /* slot */ msg[6] = seqno = handle->seqno++; msg[7] = 0; /* RFU */ msg[8] = 0; /* RFU */ msg[9] = 0; /* RFU */ set_msg_len (msg, 0); msglen = 10; rc = bulk_out (handle, msg, msglen, 0); if (!rc) bulk_in (handle, msg, sizeof msg, &msglen, RDR_to_PC_SlotStatus, seqno, 2000, 0); handle->powered_off = 1; } if (handle->idev) { usb_release_interface (handle->idev, handle->ifc_no); usb_close (handle->idev); handle->idev = NULL; } if (handle->dev_fd != -1) { close (handle->dev_fd); handle->dev_fd = -1; } } /* Reset a reader on HANDLE. This is useful in case a reader has been plugged of and inserted at a different port. By resetting the handle, the same reader will be get used. Note, that on error the handle won't get released. This does not return an ATR, so ccid_get_atr should be called right after this one. */ int ccid_shutdown_reader (ccid_driver_t handle) { int rc = 0; struct usb_device *dev = NULL; usb_dev_handle *idev = NULL; unsigned char *ifcdesc_extra = NULL; size_t ifcdesc_extra_len; int ifc_no, ep_bulk_out, ep_bulk_in, ep_intr; if (!handle || !handle->rid) return CCID_DRIVER_ERR_INV_VALUE; do_close_reader (handle); if (scan_or_find_devices (-1, handle->rid, NULL, &dev, &ifcdesc_extra, &ifcdesc_extra_len, &ifc_no, &ep_bulk_out, &ep_bulk_in, &ep_intr, &idev, NULL) || !idev) { DEBUGOUT_1 ("no CCID reader with ID %s\n", handle->rid); return CCID_DRIVER_ERR_NO_READER; } if (idev) { handle->idev = idev; handle->ifc_no = ifc_no; handle->ep_bulk_out = ep_bulk_out; handle->ep_bulk_in = ep_bulk_in; handle->ep_intr = ep_intr; if (parse_ccid_descriptor (handle, ifcdesc_extra, ifcdesc_extra_len)) { DEBUGOUT ("device not supported\n"); rc = CCID_DRIVER_ERR_NO_READER; goto leave; } rc = usb_claim_interface (idev, ifc_no); if (rc) { DEBUGOUT_1 ("usb_claim_interface failed: %d\n", rc); rc = CCID_DRIVER_ERR_CARD_IO_ERROR; goto leave; } } leave: free (ifcdesc_extra); if (rc) { if (handle->idev) usb_close (handle->idev); handle->idev = NULL; if (handle->dev_fd != -1) close (handle->dev_fd); handle->dev_fd = -1; } return rc; } int ccid_set_progress_cb (ccid_driver_t handle, void (*cb)(void *, const char *, int, int, int), void *cb_arg) { if (!handle || !handle->rid) return CCID_DRIVER_ERR_INV_VALUE; handle->progress_cb = cb; handle->progress_cb_arg = cb_arg; return 0; } /* Close the reader HANDLE. */ int ccid_close_reader (ccid_driver_t handle) { if (!handle || (!handle->idev && handle->dev_fd == -1)) return 0; do_close_reader (handle); free (handle->rid); free (handle); return 0; } /* Return False if a card is present and powered. */ int ccid_check_card_presence (ccid_driver_t handle) { (void)handle; /* Not yet implemented. */ return -1; } /* Write NBYTES of BUF to file descriptor FD. */ static int writen (int fd, const void *buf, size_t nbytes) { size_t nleft = nbytes; int nwritten; while (nleft > 0) { nwritten = write (fd, buf, nleft); if (nwritten < 0) { if (errno == EINTR) nwritten = 0; else return -1; } nleft -= nwritten; buf = (const char*)buf + nwritten; } return 0; } /* Write a MSG of length MSGLEN to the designated bulk out endpoint. Returns 0 on success. */ static int bulk_out (ccid_driver_t handle, unsigned char *msg, size_t msglen, int no_debug) { int rc; /* No need to continue and clutter the log with USB write error messages after we got the first ENODEV. */ if (handle->enodev_seen) return CCID_DRIVER_ERR_NO_READER; if (debug_level && (!no_debug || debug_level >= 3)) { switch (msglen? msg[0]:0) { case PC_to_RDR_IccPowerOn: print_p2r_iccpoweron (msg, msglen); break; case PC_to_RDR_IccPowerOff: print_p2r_iccpoweroff (msg, msglen); break; case PC_to_RDR_GetSlotStatus: print_p2r_getslotstatus (msg, msglen); break; case PC_to_RDR_XfrBlock: print_p2r_xfrblock (msg, msglen); break; case PC_to_RDR_GetParameters: print_p2r_getparameters (msg, msglen); break; case PC_to_RDR_ResetParameters: print_p2r_resetparameters (msg, msglen); break; case PC_to_RDR_SetParameters: print_p2r_setparameters (msg, msglen); break; case PC_to_RDR_Escape: print_p2r_escape (msg, msglen); break; case PC_to_RDR_IccClock: print_p2r_iccclock (msg, msglen); break; case PC_to_RDR_T0APDU: print_p2r_to0apdu (msg, msglen); break; case PC_to_RDR_Secure: print_p2r_secure (msg, msglen); break; case PC_to_RDR_Mechanical: print_p2r_mechanical (msg, msglen); break; case PC_to_RDR_Abort: print_p2r_abort (msg, msglen); break; case PC_to_RDR_SetDataRate: print_p2r_setdatarate (msg, msglen); break; default: print_p2r_unknown (msg, msglen); break; } } if (handle->idev) { rc = usb_bulk_write (handle->idev, handle->ep_bulk_out, (char*)msg, msglen, 5000 /* ms timeout */); if (rc == msglen) return 0; #ifdef ENODEV if (rc == -(ENODEV)) { /* The Linux libusb returns a negative error value. Catch the most important one. */ errno = ENODEV; rc = -1; } #endif /*ENODEV*/ if (rc == -1) { DEBUGOUT_1 ("usb_bulk_write error: %s\n", strerror (errno)); #ifdef ENODEV if (errno == ENODEV) { handle->enodev_seen = 1; return CCID_DRIVER_ERR_NO_READER; } #endif /*ENODEV*/ } else DEBUGOUT_1 ("usb_bulk_write failed: %d\n", rc); } else { rc = writen (handle->dev_fd, msg, msglen); if (!rc) return 0; DEBUGOUT_2 ("writen to %d failed: %s\n", handle->dev_fd, strerror (errno)); } return CCID_DRIVER_ERR_CARD_IO_ERROR; } /* Read a maximum of LENGTH bytes from the bulk in endpoint into BUFFER and return the actual read number if bytes in NREAD. SEQNO is the sequence number used to send the request and EXPECTED_TYPE the type of message we expect. Does checks on the ccid header. TIMEOUT is the timeout value in ms. NO_DEBUG may be set to avoid debug messages in case of no error; this can be overriden with a glibal debug level of at least 3. Returns 0 on success. */ static int bulk_in (ccid_driver_t handle, unsigned char *buffer, size_t length, size_t *nread, int expected_type, int seqno, int timeout, int no_debug) { int rc; size_t msglen; int eagain_retries = 0; /* Fixme: The next line for the current Valgrind without support for USB IOCTLs. */ memset (buffer, 0, length); retry: if (handle->idev) { rc = usb_bulk_read (handle->idev, handle->ep_bulk_in, (char*)buffer, length, timeout); if (rc < 0) { rc = errno; DEBUGOUT_1 ("usb_bulk_read error: %s\n", strerror (rc)); if (rc == EAGAIN && eagain_retries++ < 3) { my_sleep (1); goto retry; } return CCID_DRIVER_ERR_CARD_IO_ERROR; } *nread = msglen = rc; } else { rc = read (handle->dev_fd, buffer, length); if (rc < 0) { rc = errno; DEBUGOUT_2 ("read from %d failed: %s\n", handle->dev_fd, strerror (rc)); if (rc == EAGAIN && eagain_retries++ < 5) { my_sleep (1); goto retry; } return CCID_DRIVER_ERR_CARD_IO_ERROR; } *nread = msglen = rc; } eagain_retries = 0; if (msglen < 10) { DEBUGOUT_1 ("bulk-in msg too short (%u)\n", (unsigned int)msglen); abort_cmd (handle, seqno); return CCID_DRIVER_ERR_INV_VALUE; } if (buffer[5] != 0) { DEBUGOUT_1 ("unexpected bulk-in slot (%d)\n", buffer[5]); return CCID_DRIVER_ERR_INV_VALUE; } if (buffer[6] != seqno) { DEBUGOUT_2 ("bulk-in seqno does not match (%d/%d)\n", seqno, buffer[6]); /* Retry until we are synced again. */ goto retry; } /* We need to handle the time extension request before we check that we got the expected message type. This is in particular required for the Cherry keyboard which sends a time extension request for each key hit. */ if ( !(buffer[7] & 0x03) && (buffer[7] & 0xC0) == 0x80) { /* Card present and active, time extension requested. */ DEBUGOUT_2 ("time extension requested (%02X,%02X)\n", buffer[7], buffer[8]); goto retry; } if (buffer[0] != expected_type) { DEBUGOUT_1 ("unexpected bulk-in msg type (%02x)\n", buffer[0]); abort_cmd (handle, seqno); return CCID_DRIVER_ERR_INV_VALUE; } if (debug_level && (!no_debug || debug_level >= 3)) { switch (buffer[0]) { case RDR_to_PC_DataBlock: print_r2p_datablock (buffer, msglen); break; case RDR_to_PC_SlotStatus: print_r2p_slotstatus (buffer, msglen); break; case RDR_to_PC_Parameters: print_r2p_parameters (buffer, msglen); break; case RDR_to_PC_Escape: print_r2p_escape (buffer, msglen); break; case RDR_to_PC_DataRate: print_r2p_datarate (buffer, msglen); break; default: print_r2p_unknown (buffer, msglen); break; } } if (CCID_COMMAND_FAILED (buffer)) print_command_failed (buffer); /* Check whether a card is at all available. Note: If you add new error codes here, check whether they need to be ignored in send_escape_cmd. */ switch ((buffer[7] & 0x03)) { case 0: /* no error */ break; case 1: return CCID_DRIVER_ERR_CARD_INACTIVE; case 2: return CCID_DRIVER_ERR_NO_CARD; case 3: /* RFU */ break; } return 0; } /* Send an abort sequence and wait until everything settled. */ static int abort_cmd (ccid_driver_t handle, int seqno) { int rc; char dummybuf[8]; unsigned char msg[100]; size_t msglen; if (!handle->idev) { /* I don't know how to send an abort to non-USB devices. */ rc = CCID_DRIVER_ERR_NOT_SUPPORTED; } seqno &= 0xff; DEBUGOUT_1 ("sending abort sequence for seqno %d\n", seqno); /* Send the abort command to the control pipe. Note that we don't need to keep track of sent abort commands because there should never be another thread using the same slot concurrently. */ rc = usb_control_msg (handle->idev, 0x21,/* bmRequestType: host-to-device, class specific, to interface. */ 1, /* ABORT */ (seqno << 8 | 0 /* slot */), handle->ifc_no, dummybuf, 0, 1000 /* ms timeout */); if (rc < 0) { DEBUGOUT_1 ("usb_control_msg error: %s\n", strerror (errno)); return CCID_DRIVER_ERR_CARD_IO_ERROR; } /* Now send the abort command to the bulk out pipe using the same SEQNO and SLOT. Do this in a loop to so that all seqno are tried. */ seqno--; /* Adjust for next increment. */ do { seqno++; msg[0] = PC_to_RDR_Abort; msg[5] = 0; /* slot */ msg[6] = seqno; msg[7] = 0; /* RFU */ msg[8] = 0; /* RFU */ msg[9] = 0; /* RFU */ msglen = 10; set_msg_len (msg, 0); rc = usb_bulk_write (handle->idev, handle->ep_bulk_out, (char*)msg, msglen, 5000 /* ms timeout */); if (rc == msglen) rc = 0; else if (rc == -1) DEBUGOUT_1 ("usb_bulk_write error in abort_cmd: %s\n", strerror (errno)); else DEBUGOUT_1 ("usb_bulk_write failed in abort_cmd: %d\n", rc); if (rc) return rc; rc = usb_bulk_read (handle->idev, handle->ep_bulk_in, (char*)msg, sizeof msg, 5000 /*ms timeout*/); if (rc < 0) { DEBUGOUT_1 ("usb_bulk_read error in abort_cmd: %s\n", strerror (errno)); return CCID_DRIVER_ERR_CARD_IO_ERROR; } msglen = rc; if (msglen < 10) { DEBUGOUT_1 ("bulk-in msg in abort_cmd too short (%u)\n", (unsigned int)msglen); return CCID_DRIVER_ERR_INV_VALUE; } if (msg[5] != 0) { DEBUGOUT_1 ("unexpected bulk-in slot (%d) in abort_cmd\n", msg[5]); return CCID_DRIVER_ERR_INV_VALUE; } DEBUGOUT_3 ("status: %02X error: %02X octet[9]: %02X\n", msg[7], msg[8], msg[9]); if (CCID_COMMAND_FAILED (msg)) print_command_failed (msg); } while (msg[0] != RDR_to_PC_SlotStatus && msg[5] != 0 && msg[6] != seqno); handle->seqno = ((seqno + 1) & 0xff); DEBUGOUT ("sending abort sequence succeeded\n"); return 0; } /* Note that this function won't return the error codes NO_CARD or CARD_INACTIVE. IF RESULT is not NULL, the result from the operation will get returned in RESULT and its length in RESULTLEN. If the response is larger than RESULTMAX, an error is returned and the required buffer length returned in RESULTLEN. */ static int send_escape_cmd (ccid_driver_t handle, const unsigned char *data, size_t datalen, unsigned char *result, size_t resultmax, size_t *resultlen) { int rc; unsigned char msg[100]; size_t msglen; unsigned char seqno; if (resultlen) *resultlen = 0; if (datalen > sizeof msg - 10) return CCID_DRIVER_ERR_INV_VALUE; /* Escape data too large. */ msg[0] = PC_to_RDR_Escape; msg[5] = 0; /* slot */ msg[6] = seqno = handle->seqno++; msg[7] = 0; /* RFU */ msg[8] = 0; /* RFU */ msg[9] = 0; /* RFU */ memcpy (msg+10, data, datalen); msglen = 10 + datalen; set_msg_len (msg, datalen); rc = bulk_out (handle, msg, msglen, 0); if (rc) return rc; rc = bulk_in (handle, msg, sizeof msg, &msglen, RDR_to_PC_Escape, seqno, 5000, 0); if (result) switch (rc) { /* We need to ignore certain errorcode here. */ case 0: case CCID_DRIVER_ERR_CARD_INACTIVE: case CCID_DRIVER_ERR_NO_CARD: { if (msglen > resultmax) rc = CCID_DRIVER_ERR_INV_VALUE; /* Response too large. */ else { memcpy (result, msg, msglen); *resultlen = msglen; rc = 0; } } break; default: break; } return rc; } int ccid_transceive_escape (ccid_driver_t handle, const unsigned char *data, size_t datalen, unsigned char *resp, size_t maxresplen, size_t *nresp) { return send_escape_cmd (handle, data, datalen, resp, maxresplen, nresp); } /* experimental */ int ccid_poll (ccid_driver_t handle) { int rc; unsigned char msg[10]; size_t msglen; int i, j; if (handle->idev) { rc = usb_bulk_read (handle->idev, handle->ep_intr, (char*)msg, sizeof msg, 0 /* ms timeout */ ); if (rc < 0 && errno == ETIMEDOUT) return 0; } else return 0; if (rc < 0) { DEBUGOUT_1 ("usb_intr_read error: %s\n", strerror (errno)); return CCID_DRIVER_ERR_CARD_IO_ERROR; } msglen = rc; rc = 0; if (msglen < 1) { DEBUGOUT ("intr-in msg too short\n"); return CCID_DRIVER_ERR_INV_VALUE; } if (msg[0] == RDR_to_PC_NotifySlotChange) { DEBUGOUT ("notify slot change:"); for (i=1; i < msglen; i++) for (j=0; j < 4; j++) DEBUGOUT_CONT_3 (" %d:%c%c", (i-1)*4+j, (msg[i] & (1<<(j*2)))? 'p':'-', (msg[i] & (2<<(j*2)))? '*':' '); DEBUGOUT_LF (); } else if (msg[0] == RDR_to_PC_HardwareError) { DEBUGOUT ("hardware error occured\n"); } else { DEBUGOUT_1 ("unknown intr-in msg of type %02X\n", msg[0]); } return 0; } /* Note that this function won't return the error codes NO_CARD or CARD_INACTIVE */ int ccid_slot_status (ccid_driver_t handle, int *statusbits) { int rc; unsigned char msg[100]; size_t msglen; unsigned char seqno; int retries = 0; retry: msg[0] = PC_to_RDR_GetSlotStatus; msg[5] = 0; /* slot */ msg[6] = seqno = handle->seqno++; msg[7] = 0; /* RFU */ msg[8] = 0; /* RFU */ msg[9] = 0; /* RFU */ set_msg_len (msg, 0); rc = bulk_out (handle, msg, 10, 1); if (rc) return rc; /* Note that we set the NO_DEBUG flag here, so that the logs won't get cluttered up by a ticker function checking for the slot status and debugging enabled. */ rc = bulk_in (handle, msg, sizeof msg, &msglen, RDR_to_PC_SlotStatus, seqno, retries? 1000 : 200, 1); if (rc == CCID_DRIVER_ERR_CARD_IO_ERROR && retries < 3) { if (!retries) { DEBUGOUT ("USB: CALLING USB_CLEAR_HALT\n"); usb_clear_halt (handle->idev, handle->ep_bulk_in); usb_clear_halt (handle->idev, handle->ep_bulk_out); } else DEBUGOUT ("USB: RETRYING bulk_in AGAIN\n"); retries++; goto retry; } if (rc && rc != CCID_DRIVER_ERR_NO_CARD && rc != CCID_DRIVER_ERR_CARD_INACTIVE) return rc; *statusbits = (msg[7] & 3); return 0; } /* Parse ATR string (of ATRLEN) and update parameters at PARAM. Calling this routine, it should prepare default values at PARAM beforehand. This routine assumes that card is accessed by T=1 protocol. It doesn't analyze historical bytes at all. Returns < 0 value on error: -1 for parse error or integrity check error -2 for card doesn't support T=1 protocol -3 for parameters are nod explicitly defined by ATR -4 for this driver doesn't support CRC Returns >= 0 on success: 0 for card is negotiable mode 1 for card is specific mode (and not negotiable) */ static int update_param_by_atr (unsigned char *param, unsigned char *atr, size_t atrlen) { int i = -1; int t, y, chk; int historical_bytes_num, negotiable = 1; #define NEXTBYTE() do { i++; if (atrlen <= i) return -1; } while (0) NEXTBYTE (); if (atr[i] == 0x3F) param[1] |= 0x02; /* Convention is inverse. */ NEXTBYTE (); y = (atr[i] >> 4); historical_bytes_num = atr[i] & 0x0f; NEXTBYTE (); if ((y & 1)) { param[0] = atr[i]; /* TA1 - Fi & Di */ NEXTBYTE (); } if ((y & 2)) NEXTBYTE (); /* TB1 - ignore */ if ((y & 4)) { param[2] = atr[i]; /* TC1 - Guard Time */ NEXTBYTE (); } if ((y & 8)) { y = (atr[i] >> 4); /* TD1 */ t = atr[i] & 0x0f; NEXTBYTE (); if ((y & 1)) { /* TA2 - PPS mode */ if ((atr[i] & 0x0f) != 1) return -2; /* Wrong card protocol (!= 1). */ if ((atr[i] & 0x10) != 0x10) return -3; /* Transmission parameters are implicitly defined. */ negotiable = 0; /* TA2 means specific mode. */ NEXTBYTE (); } if ((y & 2)) NEXTBYTE (); /* TB2 - ignore */ if ((y & 4)) NEXTBYTE (); /* TC2 - ignore */ if ((y & 8)) { y = (atr[i] >> 4); /* TD2 */ t = atr[i] & 0x0f; NEXTBYTE (); } else y = 0; while (y) { if ((y & 1)) { /* TAx */ if (t == 1) param[5] = atr[i]; /* IFSC */ else if (t == 15) /* XXX: check voltage? */ param[4] = (atr[i] >> 6); /* ClockStop */ NEXTBYTE (); } if ((y & 2)) { if (t == 1) param[3] = atr[i]; /* TBx - BWI & CWI */ NEXTBYTE (); } if ((y & 4)) { if (t == 1) param[1] |= (atr[i] & 0x01); /* TCx - LRC/CRC */ NEXTBYTE (); if (param[1] & 0x01) return -4; /* CRC not supported yet. */ } if ((y & 8)) { y = (atr[i] >> 4); /* TDx */ t = atr[i] & 0x0f; NEXTBYTE (); } else y = 0; } } i += historical_bytes_num - 1; NEXTBYTE (); if (atrlen != i+1) return -1; #undef NEXTBYTE chk = 0; do { chk ^= atr[i]; i--; } while (i > 0); if (chk != 0) return -1; return negotiable; } /* Return the ATR of the card. This is not a cached value and thus an actual reset is done. */ int ccid_get_atr (ccid_driver_t handle, unsigned char *atr, size_t maxatrlen, size_t *atrlen) { int rc; int statusbits; unsigned char msg[100]; unsigned char *tpdu; size_t msglen, tpdulen; unsigned char seqno; int use_crc = 0; unsigned int edc; int tried_iso = 0; int got_param; unsigned char param[7] = { /* For Protocol T=1 */ 0x11, /* bmFindexDindex */ 0x10, /* bmTCCKST1 */ 0x00, /* bGuardTimeT1 */ 0x4d, /* bmWaitingIntegersT1 */ 0x00, /* bClockStop */ 0x20, /* bIFSC */ 0x00 /* bNadValue */ }; /* First check whether a card is available. */ rc = ccid_slot_status (handle, &statusbits); if (rc) return rc; if (statusbits == 2) return CCID_DRIVER_ERR_NO_CARD; /* For an inactive and also for an active card, issue the PowerOn command to get the ATR. */ again: msg[0] = PC_to_RDR_IccPowerOn; msg[5] = 0; /* slot */ msg[6] = seqno = handle->seqno++; /* power select (0=auto, 1=5V, 2=3V, 3=1.8V) */ msg[7] = handle->auto_voltage ? 0 : 1; msg[8] = 0; /* RFU */ msg[9] = 0; /* RFU */ set_msg_len (msg, 0); msglen = 10; rc = bulk_out (handle, msg, msglen, 0); if (rc) return rc; rc = bulk_in (handle, msg, sizeof msg, &msglen, RDR_to_PC_DataBlock, seqno, 5000, 0); if (rc) return rc; if (!tried_iso && CCID_COMMAND_FAILED (msg) && CCID_ERROR_CODE (msg) == 0xbb && ((handle->id_vendor == VENDOR_CHERRY && handle->id_product == 0x0005) || (handle->id_vendor == VENDOR_GEMPC && handle->id_product == 0x4433) )) { tried_iso = 1; /* Try switching to ISO mode. */ if (!send_escape_cmd (handle, (const unsigned char*)"\xF1\x01", 2, NULL, 0, NULL)) goto again; } else if (CCID_COMMAND_FAILED (msg)) return CCID_DRIVER_ERR_CARD_IO_ERROR; handle->powered_off = 0; if (atr) { size_t n = msglen - 10; if (n > maxatrlen) n = maxatrlen; memcpy (atr, msg+10, n); *atrlen = n; } param[6] = handle->nonnull_nad? ((1 << 4) | 0): 0; rc = update_param_by_atr (param, msg+10, msglen - 10); if (rc < 0) { DEBUGOUT_1 ("update_param_by_atr failed: %d\n", rc); return CCID_DRIVER_ERR_CARD_IO_ERROR; } got_param = 0; if (handle->auto_param) { msg[0] = PC_to_RDR_GetParameters; msg[5] = 0; /* slot */ msg[6] = seqno = handle->seqno++; msg[7] = 0; /* RFU */ msg[8] = 0; /* RFU */ msg[9] = 0; /* RFU */ set_msg_len (msg, 0); msglen = 10; rc = bulk_out (handle, msg, msglen, 0); if (!rc) rc = bulk_in (handle, msg, sizeof msg, &msglen, RDR_to_PC_Parameters, seqno, 2000, 0); if (rc) DEBUGOUT ("GetParameters failed\n"); else if (msglen == 17 && msg[9] == 1) got_param = 1; } else if (handle->auto_pps) ; else if (rc == 1) /* It's negotiable, send PPS. */ { msg[0] = PC_to_RDR_XfrBlock; msg[5] = 0; /* slot */ msg[6] = seqno = handle->seqno++; msg[7] = 0; msg[8] = 0; msg[9] = 0; msg[10] = 0xff; /* PPSS */ msg[11] = 0x11; /* PPS0: PPS1, Protocol T=1 */ msg[12] = param[0]; /* PPS1: Fi / Di */ msg[13] = 0xff ^ 0x11 ^ param[0]; /* PCK */ set_msg_len (msg, 4); msglen = 10 + 4; rc = bulk_out (handle, msg, msglen, 0); if (rc) return rc; rc = bulk_in (handle, msg, sizeof msg, &msglen, RDR_to_PC_DataBlock, seqno, 5000, 0); if (rc) return rc; if (msglen != 10 + 4) { DEBUGOUT_1 ("Setting PPS failed: %zu\n", msglen); return CCID_DRIVER_ERR_CARD_IO_ERROR; } if (msg[10] != 0xff || msg[11] != 0x11 || msg[12] != param[0]) { DEBUGOUT_1 ("Setting PPS failed: 0x%02x\n", param[0]); return CCID_DRIVER_ERR_CARD_IO_ERROR; } } /* Setup parameters to select T=1. */ msg[0] = PC_to_RDR_SetParameters; msg[5] = 0; /* slot */ msg[6] = seqno = handle->seqno++; msg[7] = 1; /* Select T=1. */ msg[8] = 0; /* RFU */ msg[9] = 0; /* RFU */ if (!got_param) memcpy (&msg[10], param, 7); set_msg_len (msg, 7); msglen = 10 + 7; rc = bulk_out (handle, msg, msglen, 0); if (rc) return rc; rc = bulk_in (handle, msg, sizeof msg, &msglen, RDR_to_PC_Parameters, seqno, 5000, 0); if (rc) DEBUGOUT ("SetParameters failed (ignored)\n"); if (!rc && msglen > 15 && msg[15] >= 16 && msg[15] <= 254 ) handle->ifsc = msg[15]; else handle->ifsc = 128; /* Something went wrong, assume 128 bytes. */ if (handle->nonnull_nad && msglen > 16 && msg[16] == 0) { DEBUGOUT ("Use Null-NAD, clearing handle->nonnull_nad.\n"); handle->nonnull_nad = 0; } handle->t1_ns = 0; handle->t1_nr = 0; /* Send an S-Block with our maximum IFSD to the CCID. */ if (!handle->apdu_level && !handle->auto_ifsd) { tpdu = msg+10; /* NAD: DAD=1, SAD=0 */ tpdu[0] = handle->nonnull_nad? ((1 << 4) | 0): 0; tpdu[1] = (0xc0 | 0 | 1); /* S-block request: change IFSD */ tpdu[2] = 1; tpdu[3] = handle->max_ifsd? handle->max_ifsd : 32; tpdulen = 4; edc = compute_edc (tpdu, tpdulen, use_crc); if (use_crc) tpdu[tpdulen++] = (edc >> 8); tpdu[tpdulen++] = edc; msg[0] = PC_to_RDR_XfrBlock; msg[5] = 0; /* slot */ msg[6] = seqno = handle->seqno++; msg[7] = 0; msg[8] = 0; /* RFU */ msg[9] = 0; /* RFU */ set_msg_len (msg, tpdulen); msglen = 10 + tpdulen; if (debug_level > 1) DEBUGOUT_3 ("T=1: put %c-block seq=%d%s\n", ((msg[11] & 0xc0) == 0x80)? 'R' : (msg[11] & 0x80)? 'S' : 'I', ((msg[11] & 0x80)? !!(msg[11]& 0x10) : !!(msg[11] & 0x40)), (!(msg[11] & 0x80) && (msg[11] & 0x20)? " [more]":"")); rc = bulk_out (handle, msg, msglen, 0); if (rc) return rc; rc = bulk_in (handle, msg, sizeof msg, &msglen, RDR_to_PC_DataBlock, seqno, 5000, 0); if (rc) return rc; tpdu = msg + 10; tpdulen = msglen - 10; if (tpdulen < 4) return CCID_DRIVER_ERR_ABORTED; if (debug_level > 1) DEBUGOUT_4 ("T=1: got %c-block seq=%d err=%d%s\n", ((msg[11] & 0xc0) == 0x80)? 'R' : (msg[11] & 0x80)? 'S' : 'I', ((msg[11] & 0x80)? !!(msg[11]& 0x10) : !!(msg[11] & 0x40)), ((msg[11] & 0xc0) == 0x80)? (msg[11] & 0x0f) : 0, (!(msg[11] & 0x80) && (msg[11] & 0x20)? " [more]":"")); if ((tpdu[1] & 0xe0) != 0xe0 || tpdu[2] != 1) { DEBUGOUT ("invalid response for S-block (Change-IFSD)\n"); return -1; } DEBUGOUT_1 ("IFSD has been set to %d\n", tpdu[3]); } return 0; } static unsigned int compute_edc (const unsigned char *data, size_t datalen, int use_crc) { if (use_crc) { return 0x42; /* Not yet implemented. */ } else { unsigned char crc = 0; for (; datalen; datalen--) crc ^= *data++; return crc; } } /* Return true if APDU is an extended length one. */ static int is_exlen_apdu (const unsigned char *apdu, size_t apdulen) { if (apdulen < 7 || apdu[4]) return 0; /* Too short or no Z byte. */ return 1; } /* Helper for ccid_transceive used for APDU level exchanges. */ static int ccid_transceive_apdu_level (ccid_driver_t handle, const unsigned char *apdu_buf, size_t apdu_buflen, unsigned char *resp, size_t maxresplen, size_t *nresp) { int rc; unsigned char send_buffer[10+261+300], recv_buffer[10+261+300]; const unsigned char *apdu; size_t apdulen; unsigned char *msg; size_t msglen; unsigned char seqno; int bwi = 4; msg = send_buffer; apdu = apdu_buf; apdulen = apdu_buflen; assert (apdulen); /* The maximum length for a short APDU T=1 block is 261. For an extended APDU T=1 block the maximum length 65544; however extended APDU exchange level is not fully supported yet. */ if (apdulen > sizeof (send_buffer) - 10) return CCID_DRIVER_ERR_INV_VALUE; /* Invalid length. */ msg[0] = PC_to_RDR_XfrBlock; msg[5] = 0; /* slot */ msg[6] = seqno = handle->seqno++; msg[7] = bwi; /* bBWI */ msg[8] = 0; /* RFU */ msg[9] = 0; /* RFU */ memcpy (msg+10, apdu, apdulen); set_msg_len (msg, apdulen); msglen = 10 + apdulen; rc = bulk_out (handle, msg, msglen, 0); if (rc) return rc; msg = recv_buffer; rc = bulk_in (handle, msg, sizeof recv_buffer, &msglen, RDR_to_PC_DataBlock, seqno, 5000, 0); if (rc) return rc; if (msg[9] == 1) { size_t total_msglen = msglen; while (1) { unsigned char status; msg = recv_buffer + total_msglen; msg[0] = PC_to_RDR_XfrBlock; msg[5] = 0; /* slot */ msg[6] = seqno = handle->seqno++; msg[7] = bwi; /* bBWI */ msg[8] = 0x10; /* Request next data block */ msg[9] = 0; set_msg_len (msg, 0); msglen = 10; rc = bulk_out (handle, msg, msglen, 0); if (rc) return rc; rc = bulk_in (handle, msg, sizeof recv_buffer - total_msglen, &msglen, RDR_to_PC_DataBlock, seqno, 5000, 0); if (rc) return rc; status = msg[9]; memmove (msg, msg+10, msglen - 10); total_msglen += msglen - 10; if (total_msglen >= sizeof recv_buffer) return CCID_DRIVER_ERR_OUT_OF_CORE; if (status == 0x02) break; } apdu = recv_buffer + 10; apdulen = total_msglen - 10; } else { apdu = msg + 10; apdulen = msglen - 10; } if (resp) { if (apdulen > maxresplen) { DEBUGOUT_2 ("provided buffer too short for received data " "(%u/%u)\n", (unsigned int)apdulen, (unsigned int)maxresplen); return CCID_DRIVER_ERR_INV_VALUE; } memcpy (resp, apdu, apdulen); *nresp = apdulen; } return 0; } /* Protocol T=1 overview Block Structure: Prologue Field: 1 byte Node Address (NAD) 1 byte Protocol Control Byte (PCB) 1 byte Length (LEN) Information Field: 0-254 byte APDU or Control Information (INF) Epilogue Field: 1 byte Error Detection Code (EDC) NAD: bit 7 unused bit 4..6 Destination Node Address (DAD) bit 3 unused bit 2..0 Source Node Address (SAD) If node adresses are not used, SAD and DAD should be set to 0 on the first block sent to the card. If they are used they should have different values (0 for one is okay); that first block sets up the addresses of the nodes. PCB: Information Block (I-Block): bit 7 0 bit 6 Sequence number (yep, that is modulo 2) bit 5 Chaining flag bit 4..0 reserved Received-Ready Block (R-Block): bit 7 1 bit 6 0 bit 5 0 bit 4 Sequence number bit 3..0 0 = no error 1 = EDC or parity error 2 = other error other values are reserved Supervisory Block (S-Block): bit 7 1 bit 6 1 bit 5 clear=request,set=response bit 4..0 0 = resyncronisation request 1 = information field size request 2 = abort request 3 = extension of BWT request 4 = VPP error other values are reserved */ int ccid_transceive (ccid_driver_t handle, const unsigned char *apdu_buf, size_t apdu_buflen, unsigned char *resp, size_t maxresplen, size_t *nresp) { int rc; /* The size of the buffer used to be 10+259. For the via_escape hack we need one extra byte, thus 11+259. */ unsigned char send_buffer[11+259], recv_buffer[11+259]; const unsigned char *apdu; size_t apdulen; unsigned char *msg, *tpdu, *p; size_t msglen, tpdulen, last_tpdulen, n; unsigned char seqno; unsigned int edc; int use_crc = 0; int hdrlen, pcboff; size_t dummy_nresp; int via_escape = 0; int next_chunk = 1; int sending = 1; int retries = 0; int resyncing = 0; int nad_byte; if (!nresp) nresp = &dummy_nresp; *nresp = 0; /* Smarter readers allow to send APDUs directly; divert here. */ if (handle->apdu_level) { /* We employ a hack for Omnikey readers which are able to send TPDUs using an escape sequence. There is no documentation but the Windows driver does it this way. Tested using a CM6121. This method works also for the Cherry XX44 keyboards; however there are problems with the ccid_tranceive_secure which leads to a loss of sync on the CCID level. If Cherry wants to make their keyboard work again, they should hand over some docs. */ if ((handle->id_vendor == VENDOR_OMNIKEY || (!handle->idev && handle->id_product == TRANSPORT_CM4040)) && handle->apdu_level < 2 && is_exlen_apdu (apdu_buf, apdu_buflen)) via_escape = 1; else return ccid_transceive_apdu_level (handle, apdu_buf, apdu_buflen, resp, maxresplen, nresp); } /* The other readers we support require sending TPDUs. */ tpdulen = 0; /* Avoid compiler warning about no initialization. */ msg = send_buffer; hdrlen = via_escape? 11 : 10; /* NAD: DAD=1, SAD=0 */ nad_byte = handle->nonnull_nad? ((1 << 4) | 0): 0; if (via_escape) nad_byte = 0; last_tpdulen = 0; /* Avoid gcc warning (controlled by RESYNCING). */ for (;;) { if (next_chunk) { next_chunk = 0; apdu = apdu_buf; apdulen = apdu_buflen; assert (apdulen); /* Construct an I-Block. */ tpdu = msg + hdrlen; tpdu[0] = nad_byte; tpdu[1] = ((handle->t1_ns & 1) << 6); /* I-block */ if (apdulen > handle->ifsc ) { apdulen = handle->ifsc; apdu_buf += handle->ifsc; apdu_buflen -= handle->ifsc; tpdu[1] |= (1 << 5); /* Set more bit. */ } tpdu[2] = apdulen; memcpy (tpdu+3, apdu, apdulen); tpdulen = 3 + apdulen; edc = compute_edc (tpdu, tpdulen, use_crc); if (use_crc) tpdu[tpdulen++] = (edc >> 8); tpdu[tpdulen++] = edc; } if (via_escape) { msg[0] = PC_to_RDR_Escape; msg[5] = 0; /* slot */ msg[6] = seqno = handle->seqno++; msg[7] = 0; /* RFU */ msg[8] = 0; /* RFU */ msg[9] = 0; /* RFU */ msg[10] = 0x1a; /* Omnikey command to send a TPDU. */ set_msg_len (msg, 1 + tpdulen); } else { msg[0] = PC_to_RDR_XfrBlock; msg[5] = 0; /* slot */ msg[6] = seqno = handle->seqno++; msg[7] = 4; /* bBWI */ msg[8] = 0; /* RFU */ msg[9] = 0; /* RFU */ set_msg_len (msg, tpdulen); } msglen = hdrlen + tpdulen; if (!resyncing) last_tpdulen = tpdulen; pcboff = hdrlen+1; if (debug_level > 1) DEBUGOUT_3 ("T=1: put %c-block seq=%d%s\n", ((msg[pcboff] & 0xc0) == 0x80)? 'R' : (msg[pcboff] & 0x80)? 'S' : 'I', ((msg[pcboff] & 0x80)? !!(msg[pcboff]& 0x10) : !!(msg[pcboff] & 0x40)), (!(msg[pcboff] & 0x80) && (msg[pcboff] & 0x20)? " [more]":"")); rc = bulk_out (handle, msg, msglen, 0); if (rc) return rc; msg = recv_buffer; rc = bulk_in (handle, msg, sizeof recv_buffer, &msglen, via_escape? RDR_to_PC_Escape : RDR_to_PC_DataBlock, seqno, 5000, 0); if (rc) return rc; tpdu = msg + hdrlen; tpdulen = msglen - hdrlen; resyncing = 0; if (tpdulen < 4) { usb_clear_halt (handle->idev, handle->ep_bulk_in); return CCID_DRIVER_ERR_ABORTED; } if (debug_level > 1) DEBUGOUT_4 ("T=1: got %c-block seq=%d err=%d%s\n", ((msg[pcboff] & 0xc0) == 0x80)? 'R' : (msg[pcboff] & 0x80)? 'S' : 'I', ((msg[pcboff] & 0x80)? !!(msg[pcboff]& 0x10) : !!(msg[pcboff] & 0x40)), ((msg[pcboff] & 0xc0) == 0x80)? (msg[pcboff] & 0x0f) : 0, (!(msg[pcboff] & 0x80) && (msg[pcboff] & 0x20)? " [more]":"")); if (!(tpdu[1] & 0x80)) { /* This is an I-block. */ retries = 0; if (sending) { /* last block sent was successful. */ handle->t1_ns ^= 1; sending = 0; } if (!!(tpdu[1] & 0x40) != handle->t1_nr) { /* Reponse does not match our sequence number. */ msg = send_buffer; tpdu = msg + hdrlen; tpdu[0] = nad_byte; tpdu[1] = (0x80 | (handle->t1_nr & 1) << 4 | 2); /* R-block */ tpdu[2] = 0; tpdulen = 3; edc = compute_edc (tpdu, tpdulen, use_crc); if (use_crc) tpdu[tpdulen++] = (edc >> 8); tpdu[tpdulen++] = edc; continue; } handle->t1_nr ^= 1; p = tpdu + 3; /* Skip the prologue field. */ n = tpdulen - 3 - 1; /* Strip the epilogue field. */ /* fixme: verify the checksum. */ if (resp) { if (n > maxresplen) { DEBUGOUT_2 ("provided buffer too short for received data " "(%u/%u)\n", (unsigned int)n, (unsigned int)maxresplen); return CCID_DRIVER_ERR_INV_VALUE; } memcpy (resp, p, n); resp += n; *nresp += n; maxresplen -= n; } if (!(tpdu[1] & 0x20)) return 0; /* No chaining requested - ready. */ msg = send_buffer; tpdu = msg + hdrlen; tpdu[0] = nad_byte; tpdu[1] = (0x80 | (handle->t1_nr & 1) << 4); /* R-block */ tpdu[2] = 0; tpdulen = 3; edc = compute_edc (tpdu, tpdulen, use_crc); if (use_crc) tpdu[tpdulen++] = (edc >> 8); tpdu[tpdulen++] = edc; } else if ((tpdu[1] & 0xc0) == 0x80) { /* This is a R-block. */ if ( (tpdu[1] & 0x0f)) { retries++; if (via_escape && retries == 1 && (msg[pcboff] & 0x0f)) { /* Error probably due to switching to TPDU. Send a resync request. We use the recv_buffer so that we don't corrupt the send_buffer. */ msg = recv_buffer; tpdu = msg + hdrlen; tpdu[0] = nad_byte; tpdu[1] = 0xc0; /* S-block resync request. */ tpdu[2] = 0; tpdulen = 3; edc = compute_edc (tpdu, tpdulen, use_crc); if (use_crc) tpdu[tpdulen++] = (edc >> 8); tpdu[tpdulen++] = edc; resyncing = 1; DEBUGOUT ("T=1: requesting resync\n"); } else if (retries > 3) { DEBUGOUT ("T=1: 3 failed retries\n"); return CCID_DRIVER_ERR_CARD_IO_ERROR; } else { /* Error: repeat last block */ msg = send_buffer; tpdulen = last_tpdulen; } } else if (sending && !!(tpdu[1] & 0x10) == handle->t1_ns) { /* Response does not match our sequence number. */ DEBUGOUT ("R-block with wrong seqno received on more bit\n"); return CCID_DRIVER_ERR_CARD_IO_ERROR; } else if (sending) { /* Send next chunk. */ retries = 0; msg = send_buffer; next_chunk = 1; handle->t1_ns ^= 1; } else { DEBUGOUT ("unexpected ACK R-block received\n"); return CCID_DRIVER_ERR_CARD_IO_ERROR; } } else { /* This is a S-block. */ retries = 0; DEBUGOUT_2 ("T=1: S-block %s received cmd=%d\n", (tpdu[1] & 0x20)? "response": "request", (tpdu[1] & 0x1f)); if ( !(tpdu[1] & 0x20) && (tpdu[1] & 0x1f) == 1 && tpdu[2] == 1) { /* Information field size request. */ unsigned char ifsc = tpdu[3]; if (ifsc < 16 || ifsc > 254) return CCID_DRIVER_ERR_CARD_IO_ERROR; msg = send_buffer; tpdu = msg + hdrlen; tpdu[0] = nad_byte; tpdu[1] = (0xc0 | 0x20 | 1); /* S-block response */ tpdu[2] = 1; tpdu[3] = ifsc; tpdulen = 4; edc = compute_edc (tpdu, tpdulen, use_crc); if (use_crc) tpdu[tpdulen++] = (edc >> 8); tpdu[tpdulen++] = edc; DEBUGOUT_1 ("T=1: requesting an ifsc=%d\n", ifsc); } else if ( !(tpdu[1] & 0x20) && (tpdu[1] & 0x1f) == 3 && tpdu[2]) { /* Wait time extension request. */ unsigned char bwi = tpdu[3]; msg = send_buffer; tpdu = msg + hdrlen; tpdu[0] = nad_byte; tpdu[1] = (0xc0 | 0x20 | 3); /* S-block response */ tpdu[2] = 1; tpdu[3] = bwi; tpdulen = 4; edc = compute_edc (tpdu, tpdulen, use_crc); if (use_crc) tpdu[tpdulen++] = (edc >> 8); tpdu[tpdulen++] = edc; DEBUGOUT_1 ("T=1: waittime extension of bwi=%d\n", bwi); print_progress (handle); } else if ( (tpdu[1] & 0x20) && (tpdu[1] & 0x1f) == 0 && !tpdu[2]) { DEBUGOUT ("T=1: resync ack from reader\n"); /* Repeat previous block. */ msg = send_buffer; tpdulen = last_tpdulen; } else return CCID_DRIVER_ERR_CARD_IO_ERROR; } } /* end T=1 protocol loop. */ return 0; } /* Send the CCID Secure command to the reader. APDU_BUF should contain the APDU template. PIN_MODE defines how the pin gets formatted: 1 := The PIN is ASCII encoded and of variable length. The length of the PIN entered will be put into Lc by the reader. The APDU should me made up of 4 bytes without Lc. PINLEN_MIN and PINLEN_MAX define the limits for the pin length. 0 may be used t enable reasonable defaults. When called with RESP and NRESP set to NULL, the function will merely check whether the reader supports the secure command for the given APDU and PIN_MODE. */ int ccid_transceive_secure (ccid_driver_t handle, const unsigned char *apdu_buf, size_t apdu_buflen, pininfo_t *pininfo, unsigned char *resp, size_t maxresplen, size_t *nresp) { int rc; unsigned char send_buffer[10+259], recv_buffer[10+259]; unsigned char *msg, *tpdu, *p; size_t msglen, tpdulen, n; unsigned char seqno; size_t dummy_nresp; int testmode; int cherry_mode = 0; int enable_varlen = 0; testmode = !resp && !nresp; if (!nresp) nresp = &dummy_nresp; *nresp = 0; if (apdu_buflen >= 4 && apdu_buf[1] == 0x20 && (handle->has_pinpad & 1)) ; else if (apdu_buflen >= 4 && apdu_buf[1] == 0x24 && (handle->has_pinpad & 2)) ; else return CCID_DRIVER_ERR_NO_PINPAD; if (!pininfo->minlen) pininfo->minlen = 1; if (!pininfo->maxlen) pininfo->maxlen = 15; /* Note that the 25 is the maximum value the SPR532 allows. */ if (pininfo->minlen < 1 || pininfo->minlen > 25 || pininfo->maxlen < 1 || pininfo->maxlen > 25 || pininfo->minlen > pininfo->maxlen) return CCID_DRIVER_ERR_INV_VALUE; /* We have only tested a few readers so better don't risk anything and do not allow the use with other readers. */ switch (handle->id_vendor) { case VENDOR_SCM: /* Tested with SPR 532. */ case VENDOR_KAAN: /* Tested with KAAN Advanced (1.02). */ case VENDOR_FSIJ: /* Tested with Gnuk (0.21). */ pininfo->maxlen = 25; enable_varlen = 1; break; case VENDOR_REINER:/* Tested with cyberJack go */ case VENDOR_VASCO: /* Tested with DIGIPASS 920 */ enable_varlen = 1; break; case VENDOR_CHERRY: pininfo->maxlen = 25; enable_varlen = 1; /* The CHERRY XX44 keyboard echos an asterisk for each entered character on the keyboard channel. We use a special variant of PC_to_RDR_Secure which directs these characters to the smart card's bulk-in channel. We also need to append a zero Lc byte to the APDU. It seems that it will be replaced with the actual length instead of being appended before the APDU is send to the card. */ if (handle->id_product != CHERRY_ST2000) cherry_mode = 1; break; default: if ((handle->id_vendor == VENDOR_GEMPC && handle->id_product == GEMPC_PINPAD) || (handle->id_vendor == VENDOR_VEGA && handle->id_product == VEGA_ALPHA)) { enable_varlen = 0; pininfo->minlen = 4; pininfo->maxlen = 8; break; } return CCID_DRIVER_ERR_NOT_SUPPORTED; } if (enable_varlen) pininfo->fixedlen = 0; if (testmode) return 0; /* Success */ if (pininfo->fixedlen < 0 || pininfo->fixedlen >= 16) return CCID_DRIVER_ERR_NOT_SUPPORTED; msg = send_buffer; if (handle->id_vendor == VENDOR_SCM) { DEBUGOUT ("sending escape sequence to switch to a case 1 APDU\n"); rc = send_escape_cmd (handle, (const unsigned char*)"\x80\x02\x00", 3, NULL, 0, NULL); if (rc) return rc; } msg[0] = cherry_mode? 0x89 : PC_to_RDR_Secure; msg[5] = 0; /* slot */ msg[6] = seqno = handle->seqno++; msg[7] = 0; /* bBWI */ msg[8] = 0; /* RFU */ msg[9] = 0; /* RFU */ msg[10] = apdu_buf[1] == 0x20 ? 0 : 1; /* Perform PIN verification or PIN modification. */ msg[11] = 0; /* Timeout in seconds. */ msg[12] = 0x82; /* bmFormatString: Byte, pos=0, left, ASCII. */ if (handle->id_vendor == VENDOR_SCM) { /* For the SPR532 the next 2 bytes need to be zero. We do this for all SCM products. Kudos to Martin Paljak for this hint. */ msg[13] = msg[14] = 0; } else { msg[13] = pininfo->fixedlen; /* bmPINBlockString: 0 bits of pin length to insert. PIN block size by fixedlen. */ msg[14] = 0x00; /* bmPINLengthFormat: Units are bytes, position is 0. */ } msglen = 15; if (apdu_buf[1] == 0x24) { msg[msglen++] = 0; /* bInsertionOffsetOld */ msg[msglen++] = pininfo->fixedlen; /* bInsertionOffsetNew */ } /* The following is a little endian word. */ msg[msglen++] = pininfo->maxlen; /* wPINMaxExtraDigit-Maximum. */ msg[msglen++] = pininfo->minlen; /* wPINMaxExtraDigit-Minimum. */ if (apdu_buf[1] == 0x24) msg[msglen++] = apdu_buf[2] == 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) */ msg[msglen] = 0x02; /* bEntryValidationCondition: Validation key pressed */ if (pininfo->minlen && pininfo->maxlen && pininfo->minlen == pininfo->maxlen) msg[msglen] |= 0x01; /* Max size reached. */ msglen++; if (apdu_buf[1] == 0x20) msg[msglen++] = 0x01; /* bNumberMessage. */ else msg[msglen++] = 0x03; /* bNumberMessage. */ msg[msglen++] = 0x09; /* wLangId-Low: English FIXME: use the first entry. */ msg[msglen++] = 0x04; /* wLangId-High. */ if (apdu_buf[1] == 0x20) msg[msglen++] = 0; /* bMsgIndex. */ else { msg[msglen++] = 0; /* bMsgIndex1. */ msg[msglen++] = 1; /* bMsgIndex2. */ msg[msglen++] = 2; /* bMsgIndex3. */ } /* Calculate Lc. */ n = pininfo->fixedlen; if (apdu_buf[1] == 0x24) n += pininfo->fixedlen; /* bTeoProlog follows: */ msg[msglen++] = handle->nonnull_nad? ((1 << 4) | 0): 0; msg[msglen++] = ((handle->t1_ns & 1) << 6); /* I-block */ if (n) msg[msglen++] = n + 5; /* apdulen should be filled for fixed length. */ else msg[msglen++] = 0; /* The apdulen will be filled in by the reader. */ /* APDU follows: */ msg[msglen++] = apdu_buf[0]; /* CLA */ msg[msglen++] = apdu_buf[1]; /* INS */ msg[msglen++] = apdu_buf[2]; /* P1 */ msg[msglen++] = apdu_buf[3]; /* P2 */ if (cherry_mode) msg[msglen++] = 0; else if (pininfo->fixedlen != 0) { msg[msglen++] = n; memset (&msg[msglen], 0xff, n); msglen += n; } /* An EDC is not required. */ set_msg_len (msg, msglen - 10); rc = bulk_out (handle, msg, msglen, 0); if (rc) return rc; msg = recv_buffer; rc = bulk_in (handle, msg, sizeof recv_buffer, &msglen, RDR_to_PC_DataBlock, seqno, 30000, 0); if (rc) return rc; tpdu = msg + 10; tpdulen = msglen - 10; if (handle->apdu_level) { if (resp) { if (tpdulen > maxresplen) { DEBUGOUT_2 ("provided buffer too short for received data " "(%u/%u)\n", (unsigned int)tpdulen, (unsigned int)maxresplen); return CCID_DRIVER_ERR_INV_VALUE; } memcpy (resp, tpdu, tpdulen); *nresp = tpdulen; } return 0; } if (tpdulen < 4) { usb_clear_halt (handle->idev, handle->ep_bulk_in); return CCID_DRIVER_ERR_ABORTED; } if (debug_level > 1) DEBUGOUT_4 ("T=1: got %c-block seq=%d err=%d%s\n", ((msg[11] & 0xc0) == 0x80)? 'R' : (msg[11] & 0x80)? 'S' : 'I', ((msg[11] & 0x80)? !!(msg[11]& 0x10) : !!(msg[11] & 0x40)), ((msg[11] & 0xc0) == 0x80)? (msg[11] & 0x0f) : 0, (!(msg[11] & 0x80) && (msg[11] & 0x20)? " [more]":"")); if (!(tpdu[1] & 0x80)) { /* This is an I-block. */ /* Last block sent was successful. */ handle->t1_ns ^= 1; if (!!(tpdu[1] & 0x40) != handle->t1_nr) { /* Reponse does not match our sequence number. */ DEBUGOUT ("I-block with wrong seqno received\n"); return CCID_DRIVER_ERR_CARD_IO_ERROR; } handle->t1_nr ^= 1; p = tpdu + 3; /* Skip the prologue field. */ n = tpdulen - 3 - 1; /* Strip the epilogue field. */ /* fixme: verify the checksum. */ if (resp) { if (n > maxresplen) { DEBUGOUT_2 ("provided buffer too short for received data " "(%u/%u)\n", (unsigned int)n, (unsigned int)maxresplen); return CCID_DRIVER_ERR_INV_VALUE; } memcpy (resp, p, n); resp += n; *nresp += n; maxresplen -= n; } if (!(tpdu[1] & 0x20)) return 0; /* No chaining requested - ready. */ DEBUGOUT ("chaining requested but not supported for Secure operation\n"); return CCID_DRIVER_ERR_CARD_IO_ERROR; } else if ((tpdu[1] & 0xc0) == 0x80) { /* This is a R-block. */ if ( (tpdu[1] & 0x0f)) { /* Error: repeat last block */ DEBUGOUT ("No retries supported for Secure operation\n"); return CCID_DRIVER_ERR_CARD_IO_ERROR; } else if (!!(tpdu[1] & 0x10) == handle->t1_ns) { /* Reponse does not match our sequence number. */ DEBUGOUT ("R-block with wrong seqno received on more bit\n"); return CCID_DRIVER_ERR_CARD_IO_ERROR; } else { /* Send next chunk. */ DEBUGOUT ("chaining not supported on Secure operation\n"); return CCID_DRIVER_ERR_CARD_IO_ERROR; } } else { /* This is a S-block. */ DEBUGOUT_2 ("T=1: S-block %s received cmd=%d for Secure operation\n", (tpdu[1] & 0x20)? "response": "request", (tpdu[1] & 0x1f)); return CCID_DRIVER_ERR_CARD_IO_ERROR; } return 0; } #ifdef TEST static void print_error (int err) { const char *p; char buf[50]; switch (err) { case 0: p = "success"; case CCID_DRIVER_ERR_OUT_OF_CORE: p = "out of core"; break; case CCID_DRIVER_ERR_INV_VALUE: p = "invalid value"; break; case CCID_DRIVER_ERR_NO_DRIVER: p = "no driver"; break; case CCID_DRIVER_ERR_NOT_SUPPORTED: p = "not supported"; break; case CCID_DRIVER_ERR_LOCKING_FAILED: p = "locking failed"; break; case CCID_DRIVER_ERR_BUSY: p = "busy"; break; case CCID_DRIVER_ERR_NO_CARD: p = "no card"; break; case CCID_DRIVER_ERR_CARD_INACTIVE: p = "card inactive"; break; case CCID_DRIVER_ERR_CARD_IO_ERROR: p = "card I/O error"; break; case CCID_DRIVER_ERR_GENERAL_ERROR: p = "general error"; break; case CCID_DRIVER_ERR_NO_READER: p = "no reader"; break; case CCID_DRIVER_ERR_ABORTED: p = "aborted"; break; default: sprintf (buf, "0x%05x", err); p = buf; break; } fprintf (stderr, "operation failed: %s\n", p); } static void print_data (const unsigned char *data, size_t length) { if (length >= 2) { fprintf (stderr, "operation status: %02X%02X\n", data[length-2], data[length-1]); length -= 2; } if (length) { fputs (" returned data:", stderr); for (; length; length--, data++) fprintf (stderr, " %02X", *data); putc ('\n', stderr); } } static void print_result (int rc, const unsigned char *data, size_t length) { if (rc) print_error (rc); else if (data) print_data (data, length); } int main (int argc, char **argv) { int rc; ccid_driver_t ccid; int slotstat; unsigned char result[512]; size_t resultlen; int no_pinpad = 0; int verify_123456 = 0; int did_verify = 0; int no_poll = 0; if (argc) { argc--; argv++; } while (argc) { if ( !strcmp (*argv, "--list")) { char *p; p = ccid_get_reader_list (); if (!p) return 1; fputs (p, stderr); free (p); return 0; } else if ( !strcmp (*argv, "--debug")) { ccid_set_debug_level (ccid_set_debug_level (-1)+1); argc--; argv++; } else if ( !strcmp (*argv, "--no-poll")) { no_poll = 1; argc--; argv++; } else if ( !strcmp (*argv, "--no-pinpad")) { no_pinpad = 1; argc--; argv++; } else if ( !strcmp (*argv, "--verify-123456")) { verify_123456 = 1; argc--; argv++; } else break; } rc = ccid_open_reader (&ccid, argc? *argv:NULL); if (rc) return 1; if (!no_poll) ccid_poll (ccid); fputs ("getting ATR ...\n", stderr); rc = ccid_get_atr (ccid, NULL, 0, NULL); if (rc) { print_error (rc); return 1; } if (!no_poll) ccid_poll (ccid); fputs ("getting slot status ...\n", stderr); rc = ccid_slot_status (ccid, &slotstat); if (rc) { print_error (rc); return 1; } if (!no_poll) ccid_poll (ccid); fputs ("selecting application OpenPGP ....\n", stderr); { static unsigned char apdu[] = { 0, 0xA4, 4, 0, 6, 0xD2, 0x76, 0x00, 0x01, 0x24, 0x01}; rc = ccid_transceive (ccid, apdu, sizeof apdu, result, sizeof result, &resultlen); print_result (rc, result, resultlen); } if (!no_poll) ccid_poll (ccid); fputs ("getting OpenPGP DO 0x65 ....\n", stderr); { static unsigned char apdu[] = { 0, 0xCA, 0, 0x65, 254 }; rc = ccid_transceive (ccid, apdu, sizeof apdu, result, sizeof result, &resultlen); print_result (rc, result, resultlen); } if (!no_pinpad) { } if (!no_pinpad) { static unsigned char apdu[] = { 0, 0x20, 0, 0x81 }; if (ccid_transceive_secure (ccid, apdu, sizeof apdu, 1, 0, 0, 0, NULL, 0, NULL)) fputs ("can't verify using a PIN-Pad reader\n", stderr); else { fputs ("verifying CHV1 using the PINPad ....\n", stderr); rc = ccid_transceive_secure (ccid, apdu, sizeof apdu, 1, 0, 0, 0, result, sizeof result, &resultlen); print_result (rc, result, resultlen); did_verify = 1; } } if (verify_123456 && !did_verify) { fputs ("verifying that CHV1 is 123456....\n", stderr); { static unsigned char apdu[] = {0, 0x20, 0, 0x81, 6, '1','2','3','4','5','6'}; rc = ccid_transceive (ccid, apdu, sizeof apdu, result, sizeof result, &resultlen); print_result (rc, result, resultlen); } } if (!rc) { fputs ("getting OpenPGP DO 0x5E ....\n", stderr); { static unsigned char apdu[] = { 0, 0xCA, 0, 0x5E, 254 }; rc = ccid_transceive (ccid, apdu, sizeof apdu, result, sizeof result, &resultlen); print_result (rc, result, resultlen); } } ccid_close_reader (ccid); return 0; } /* * Local Variables: * compile-command: "gcc -DTEST -Wall -I/usr/local/include -lusb -g ccid-driver.c" * End: */ #endif /*TEST*/ #endif /*HAVE_LIBUSB*/ diff --git a/sm/fingerprint.c b/sm/fingerprint.c index b849afb4e..a82945eb2 100644 --- a/sm/fingerprint.c +++ b/sm/fingerprint.c @@ -1,349 +1,346 @@ /* 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 "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 = (((unsigned long)digest[12]<<24) - |(digest[13]<<16) - |(digest[14]<< 8) - |digest[15]); - return (((unsigned long)digest[16]<<24) - |(digest[17]<<16) - |(digest[18]<<8) - |digest[19]); + *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) { rc = gpg_error (GPG_ERR_GENERAL); log_error ("can't calculate keygrip\n"); return NULL; } if (DBG_X509) log_printhex ("keygrip=", array, 20); 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/tools/ccidmon.c b/tools/ccidmon.c index 1137bab00..4e39b5c1d 100644 --- a/tools/ccidmon.c +++ b/tools/ccidmon.c @@ -1,879 +1,879 @@ /* ccidmon.c - CCID monitor for use with the Linux usbmon facility. * Copyright (C) 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 . */ /* This utility takes the output of usbmon, filters out the bulk data and prints the CCID messages in a human friendly way. */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include #include #include #include #include #ifndef PACKAGE_VERSION # define PACKAGE_VERSION "[build on " __DATE__ " " __TIME__ "]" #endif #ifndef PACKAGE_BUGREPORT # define PACKAGE_BUGREPORT "devnull@example.org" #endif #define PGM "ccidmon" /* Option flags. */ static int verbose; static int debug; static int skip_escape; static int usb_bus, usb_dev; static int sniffusb; /* Error counter. */ static int any_error; /* Data storage. */ struct { int is_bi; char address[50]; int count; char data[2000]; } databuffer; enum { RDR_to_PC_NotifySlotChange= 0x50, RDR_to_PC_HardwareError = 0x51, PC_to_RDR_SetParameters = 0x61, PC_to_RDR_IccPowerOn = 0x62, PC_to_RDR_IccPowerOff = 0x63, PC_to_RDR_GetSlotStatus = 0x65, PC_to_RDR_Secure = 0x69, PC_to_RDR_T0APDU = 0x6a, PC_to_RDR_Escape = 0x6b, PC_to_RDR_GetParameters = 0x6c, PC_to_RDR_ResetParameters = 0x6d, PC_to_RDR_IccClock = 0x6e, PC_to_RDR_XfrBlock = 0x6f, PC_to_RDR_Mechanical = 0x71, PC_to_RDR_Abort = 0x72, PC_to_RDR_SetDataRate = 0x73, RDR_to_PC_DataBlock = 0x80, RDR_to_PC_SlotStatus = 0x81, RDR_to_PC_Parameters = 0x82, RDR_to_PC_Escape = 0x83, RDR_to_PC_DataRate = 0x84 }; #define digitp(p) ((p) >= '0' && (p) <= '9') #define hexdigitp(a) (digitp (a) \ || ((a) >= 'A' && (a) <= 'F') \ || ((a) >= 'a' && (a) <= 'f')) #define ascii_isspace(a) ((a)==' ' || (a)=='\n' || (a)=='\r' || (a)=='\t') #define xtoi_1(p) ((p) <= '9'? ((p)- '0'): \ (p) <= 'F'? ((p)-'A'+10):((p)-'a'+10)) /* Print diagnostic message and exit with failure. */ static void die (const char *format, ...) { va_list arg_ptr; fflush (stdout); fprintf (stderr, "%s: ", PGM); va_start (arg_ptr, format); vfprintf (stderr, format, arg_ptr); va_end (arg_ptr); putc ('\n', stderr); exit (1); } /* Print diagnostic message. */ static void err (const char *format, ...) { va_list arg_ptr; any_error = 1; fflush (stdout); fprintf (stderr, "%s: ", PGM); va_start (arg_ptr, format); vfprintf (stderr, format, arg_ptr); va_end (arg_ptr); putc ('\n', stderr); } /* Convert a little endian stored 4 byte value into an unsigned integer. */ static unsigned int convert_le_u32 (const unsigned char *buf) { - return buf[0] | (buf[1] << 8) | (buf[2] << 16) | (buf[3] << 24); + return buf[0] | (buf[1] << 8) | (buf[2] << 16) | ((unsigned int)buf[3] << 24); } /* Convert a little endian stored 2 byte value into an unsigned integer. */ static unsigned int convert_le_u16 (const unsigned char *buf) { return buf[0] | (buf[1] << 8); } static void print_pr_data (const unsigned char *data, size_t datalen, size_t off) { int needlf = 0; int first = 1; for (; off < datalen; off++) { if (!(off % 16) || first) { if (needlf) putchar ('\n'); printf (" [%04d] ", off); } printf (" %02X", data[off]); needlf = 1; first = 0; } if (needlf) putchar ('\n'); } static void print_p2r_header (const char *name, const unsigned char *msg, size_t msglen) { printf ("%s:\n", name); if (msglen < 7) return; printf (" dwLength ..........: %u\n", convert_le_u32 (msg+1)); printf (" bSlot .............: %u\n", msg[5]); printf (" bSeq ..............: %u\n", msg[6]); } static void print_p2r_iccpoweron (const unsigned char *msg, size_t msglen) { print_p2r_header ("PC_to_RDR_IccPowerOn", msg, msglen); if (msglen < 10) return; printf (" bPowerSelect ......: 0x%02x (%s)\n", msg[7], msg[7] == 0? "auto": msg[7] == 1? "5.0 V": msg[7] == 2? "3.0 V": msg[7] == 3? "1.8 V":""); print_pr_data (msg, msglen, 8); } static void print_p2r_iccpoweroff (const unsigned char *msg, size_t msglen) { print_p2r_header ("PC_to_RDR_IccPowerOff", msg, msglen); print_pr_data (msg, msglen, 7); } static void print_p2r_getslotstatus (const unsigned char *msg, size_t msglen) { print_p2r_header ("PC_to_RDR_GetSlotStatus", msg, msglen); print_pr_data (msg, msglen, 7); } static void print_p2r_xfrblock (const unsigned char *msg, size_t msglen) { unsigned int val; print_p2r_header ("PC_to_RDR_XfrBlock", msg, msglen); if (msglen < 10) return; printf (" bBWI ..............: 0x%02x\n", msg[7]); val = convert_le_u16 (msg+8); printf (" wLevelParameter ...: 0x%04x%s\n", val, val == 1? " (continued)": val == 2? " (continues+ends)": val == 3? " (continues+continued)": val == 16? " (DataBlock-expected)":""); print_pr_data (msg, msglen, 10); } static void print_p2r_getparameters (const unsigned char *msg, size_t msglen) { print_p2r_header ("PC_to_RDR_GetParameters", msg, msglen); print_pr_data (msg, msglen, 7); } static void print_p2r_resetparameters (const unsigned char *msg, size_t msglen) { print_p2r_header ("PC_to_RDR_ResetParameters", msg, msglen); print_pr_data (msg, msglen, 7); } static void print_p2r_setparameters (const unsigned char *msg, size_t msglen) { print_p2r_header ("PC_to_RDR_SetParameters", msg, msglen); if (msglen < 10) return; printf (" bProtocolNum ......: 0x%02x\n", msg[7]); print_pr_data (msg, msglen, 8); } static void print_p2r_escape (const unsigned char *msg, size_t msglen) { if (skip_escape) return; print_p2r_header ("PC_to_RDR_Escape", msg, msglen); print_pr_data (msg, msglen, 7); } static void print_p2r_iccclock (const unsigned char *msg, size_t msglen) { print_p2r_header ("PC_to_RDR_IccClock", msg, msglen); if (msglen < 10) return; printf (" bClockCommand .....: 0x%02x\n", msg[7]); print_pr_data (msg, msglen, 8); } static void print_p2r_to0apdu (const unsigned char *msg, size_t msglen) { print_p2r_header ("PC_to_RDR_T0APDU", msg, msglen); if (msglen < 10) return; printf (" bmChanges .........: 0x%02x\n", msg[7]); printf (" bClassGetResponse .: 0x%02x\n", msg[8]); printf (" bClassEnvelope ....: 0x%02x\n", msg[9]); print_pr_data (msg, msglen, 10); } static void print_p2r_secure (const unsigned char *msg, size_t msglen) { unsigned int val; print_p2r_header ("PC_to_RDR_Secure", msg, msglen); if (msglen < 10) return; printf (" bBMI ..............: 0x%02x\n", msg[7]); val = convert_le_u16 (msg+8); printf (" wLevelParameter ...: 0x%04x%s\n", val, val == 1? " (continued)": val == 2? " (continues+ends)": val == 3? " (continues+continued)": val == 16? " (DataBlock-expected)":""); print_pr_data (msg, msglen, 10); } static void print_p2r_mechanical (const unsigned char *msg, size_t msglen) { print_p2r_header ("PC_to_RDR_Mechanical", msg, msglen); if (msglen < 10) return; printf (" bFunction .........: 0x%02x\n", msg[7]); print_pr_data (msg, msglen, 8); } static void print_p2r_abort (const unsigned char *msg, size_t msglen) { print_p2r_header ("PC_to_RDR_Abort", msg, msglen); print_pr_data (msg, msglen, 7); } static void print_p2r_setdatarate (const unsigned char *msg, size_t msglen) { print_p2r_header ("PC_to_RDR_SetDataRate", msg, msglen); if (msglen < 10) return; print_pr_data (msg, msglen, 7); } static void print_p2r_unknown (const unsigned char *msg, size_t msglen) { char buf[100]; snprintf (buf, sizeof buf, "Unknown PC_to_RDR command 0x%02X", msglen? msg[0]:0); print_p2r_header (buf, msg, msglen); if (msglen < 10) return; print_pr_data (msg, msglen, 0); } static void print_p2r (const unsigned char *msg, size_t msglen) { switch (msglen? msg[0]:0) { case PC_to_RDR_IccPowerOn: print_p2r_iccpoweron (msg, msglen); break; case PC_to_RDR_IccPowerOff: print_p2r_iccpoweroff (msg, msglen); break; case PC_to_RDR_GetSlotStatus: print_p2r_getslotstatus (msg, msglen); break; case PC_to_RDR_XfrBlock: print_p2r_xfrblock (msg, msglen); break; case PC_to_RDR_GetParameters: print_p2r_getparameters (msg, msglen); break; case PC_to_RDR_ResetParameters: print_p2r_resetparameters (msg, msglen); break; case PC_to_RDR_SetParameters: print_p2r_setparameters (msg, msglen); break; case PC_to_RDR_Escape: print_p2r_escape (msg, msglen); break; case PC_to_RDR_IccClock: print_p2r_iccclock (msg, msglen); break; case PC_to_RDR_T0APDU: print_p2r_to0apdu (msg, msglen); break; case PC_to_RDR_Secure: print_p2r_secure (msg, msglen); break; case PC_to_RDR_Mechanical: print_p2r_mechanical (msg, msglen); break; case PC_to_RDR_Abort: print_p2r_abort (msg, msglen); break; case PC_to_RDR_SetDataRate: print_p2r_setdatarate (msg, msglen); break; default: print_p2r_unknown (msg, msglen); break; } } static void print_r2p_header (const char *name, const unsigned char *msg, size_t msglen) { printf ("%s:\n", name); if (msglen < 9) return; printf (" dwLength ..........: %u\n", convert_le_u32 (msg+1)); printf (" bSlot .............: %u\n", msg[5]); printf (" bSeq ..............: %u\n", msg[6]); printf (" bStatus ...........: %u\n", msg[7]); if (msg[8]) printf (" bError ............: %u\n", msg[8]); } static void print_r2p_datablock (const unsigned char *msg, size_t msglen) { print_r2p_header ("RDR_to_PC_DataBlock", msg, msglen); if (msglen < 10) return; if (msg[9]) printf (" bChainParameter ...: 0x%02x%s\n", msg[9], msg[9] == 1? " (continued)": msg[9] == 2? " (continues+ends)": msg[9] == 3? " (continues+continued)": msg[9] == 16? " (XferBlock-expected)":""); print_pr_data (msg, msglen, 10); } static void print_r2p_slotstatus (const unsigned char *msg, size_t msglen) { print_r2p_header ("RDR_to_PC_SlotStatus", msg, msglen); if (msglen < 10) return; printf (" bClockStatus ......: 0x%02x%s\n", msg[9], msg[9] == 0? " (running)": msg[9] == 1? " (stopped-L)": msg[9] == 2? " (stopped-H)": msg[9] == 3? " (stopped)":""); print_pr_data (msg, msglen, 10); } static void print_r2p_parameters (const unsigned char *msg, size_t msglen) { print_r2p_header ("RDR_to_PC_Parameters", msg, msglen); if (msglen < 10) return; printf (" protocol ..........: T=%d\n", msg[9]); if (msglen == 17 && msg[9] == 1) { /* Protocol T=1. */ printf (" bmFindexDindex ....: %02X\n", msg[10]); printf (" bmTCCKST1 .........: %02X\n", msg[11]); printf (" bGuardTimeT1 ......: %02X\n", msg[12]); printf (" bmWaitingIntegersT1: %02X\n", msg[13]); printf (" bClockStop ........: %02X\n", msg[14]); printf (" bIFSC .............: %d\n", msg[15]); printf (" bNadValue .........: %d\n", msg[16]); } else print_pr_data (msg, msglen, 10); } static void print_r2p_escape (const unsigned char *msg, size_t msglen) { if (skip_escape) return; print_r2p_header ("RDR_to_PC_Escape", msg, msglen); if (msglen < 10) return; printf (" buffer[9] .........: %02X\n", msg[9]); print_pr_data (msg, msglen, 10); } static void print_r2p_datarate (const unsigned char *msg, size_t msglen) { print_r2p_header ("RDR_to_PC_DataRate", msg, msglen); if (msglen < 10) return; if (msglen >= 18) { printf (" dwClockFrequency ..: %u\n", convert_le_u32 (msg+10)); printf (" dwDataRate ..... ..: %u\n", convert_le_u32 (msg+14)); print_pr_data (msg, msglen, 18); } else print_pr_data (msg, msglen, 10); } static void print_r2p_unknown (const unsigned char *msg, size_t msglen) { char buf[100]; snprintf (buf, sizeof buf, "Unknown RDR_to_PC command 0x%02X", msglen? msg[0]:0); print_r2p_header (buf, msg, msglen); if (msglen < 10) return; printf (" bMessageType ......: %02X\n", msg[0]); printf (" buffer[9] .........: %02X\n", msg[9]); print_pr_data (msg, msglen, 10); } static void print_r2p (const unsigned char *msg, size_t msglen) { switch (msglen? msg[0]:0) { case RDR_to_PC_DataBlock: print_r2p_datablock (msg, msglen); break; case RDR_to_PC_SlotStatus: print_r2p_slotstatus (msg, msglen); break; case RDR_to_PC_Parameters: print_r2p_parameters (msg, msglen); break; case RDR_to_PC_Escape: print_r2p_escape (msg, msglen); break; case RDR_to_PC_DataRate: print_r2p_datarate (msg, msglen); break; default: print_r2p_unknown (msg, msglen); break; } } static void flush_data (void) { if (!databuffer.count) return; if (verbose) printf ("Address: %s\n", databuffer.address); if (databuffer.is_bi) { print_r2p (databuffer.data, databuffer.count); if (verbose) putchar ('\n'); } else print_p2r (databuffer.data, databuffer.count); databuffer.count = 0; } static void collect_data (char *hexdata, const char *address, unsigned int lineno) { size_t length; int is_bi; char *s; unsigned int value; is_bi = (*address && address[1] == 'i'); if (databuffer.is_bi != is_bi || strcmp (databuffer.address, address)) flush_data (); databuffer.is_bi = is_bi; if (strlen (address) >= sizeof databuffer.address) die ("address field too long"); strcpy (databuffer.address, address); length = databuffer.count; for (s=hexdata; *s; s++ ) { if (ascii_isspace (*s)) continue; if (!hexdigitp (*s)) { err ("invalid hex digit in line %u - line skipped", lineno); break; } value = xtoi_1 (*s) * 16; s++; if (!hexdigitp (*s)) { err ("invalid hex digit in line %u - line skipped", lineno); break; } value += xtoi_1 (*s); if (length >= sizeof (databuffer.data)) { err ("too much data at line %u - can handle only up to % bytes", lineno, sizeof (databuffer.data)); break; } databuffer.data[length++] = value; } databuffer.count = length; } static void parse_line (char *line, unsigned int lineno) { char *p; char *event_type, *address, *data, *status, *datatag; if (debug) printf ("line[%u] ='%s'\n", lineno, line); p = strtok (line, " "); if (!p) die ("invalid line %d (no URB)"); p = strtok (NULL, " "); if (!p) die ("invalid line %d (no timestamp)"); event_type = strtok (NULL, " "); if (!event_type) die ("invalid line %d (no event type)"); address = strtok (NULL, " "); if (!address) die ("invalid line %d (no address"); if (usb_bus || usb_dev) { int bus, dev; p = strchr (address, ':'); if (!p) die ("invalid line %d (invalid address"); p++; bus = atoi (p); p = strchr (p, ':'); if (!p) die ("invalid line %d (invalid address"); p++; dev = atoi (p); if ((usb_bus && usb_bus != bus) || (usb_dev && usb_dev != dev)) return; /* We don't want that one. */ } if (*address != 'B' || (address[1] != 'o' && address[1] != 'i')) return; /* We only want block in and block out. */ status = strtok (NULL, " "); if (!status) return; if (!strchr ("-0123456789", *status)) return; /* Setup packet. */ /* We don't support "Z[io]" types thus we don't need to check here. */ p = strtok (NULL, " "); if (!p) return; /* No data length. */ datatag = strtok (NULL, " "); if (datatag && *datatag == '=') { data = strtok (NULL, ""); collect_data (data?data:"", address, lineno); } } static void parse_line_sniffusb (char *line, unsigned int lineno) { char *p; if (debug) printf ("line[%u] ='%s'\n", lineno, line); p = strtok (line, " \t"); if (!p) return; p = strtok (NULL, " \t"); if (!p) return; p = strtok (NULL, " \t"); if (!p) return; if (hexdigitp (p[0]) && hexdigitp (p[1]) && hexdigitp (p[2]) && hexdigitp (p[3]) && p[4] == ':' && !p[5]) { size_t length; unsigned int value; length = databuffer.count; while ((p=strtok (NULL, " \t"))) { if (!hexdigitp (p[0]) || !hexdigitp (p[1])) { err ("invalid hex digit in line %u (%s)", lineno,p); break; } value = xtoi_1 (p[0]) * 16 + xtoi_1 (p[1]); if (length >= sizeof (databuffer.data)) { err ("too much data at line %u - can handle only up to % bytes", lineno, sizeof (databuffer.data)); break; } databuffer.data[length++] = value; } databuffer.count = length; } else if (!strcmp (p, "TransferFlags")) { flush_data (); *databuffer.address = 0; while ((p=strtok (NULL, " \t(,)"))) { if (!strcmp (p, "USBD_TRANSFER_DIRECTION_IN")) { databuffer.is_bi = 1; break; } else if (!strcmp (p, "USBD_TRANSFER_DIRECTION_OUT")) { databuffer.is_bi = 0; break; } } } } static void parse_input (FILE *fp) { char line[2000]; size_t length; unsigned int lineno = 0; while (fgets (line, sizeof (line), fp)) { lineno++; length = strlen (line); if (length && line[length - 1] == '\n') line[--length] = 0; else err ("line number %u too long or last line not terminated", lineno); if (length && line[length - 1] == '\r') line[--length] = 0; if (sniffusb) parse_line_sniffusb (line, lineno); else parse_line (line, lineno); } flush_data (); if (ferror (fp)) err ("error reading input at line %u: %s", lineno, strerror (errno)); } int main (int argc, char **argv) { int last_argc = -1; if (argc) { argc--; argv++; } while (argc && last_argc != argc ) { last_argc = argc; if (!strcmp (*argv, "--")) { argc--; argv++; break; } else if (!strcmp (*argv, "--version")) { fputs (PGM " ("GNUPG_NAME") " PACKAGE_VERSION "\n", stdout); exit (0); } else if (!strcmp (*argv, "--help")) { puts ("Usage: " PGM " [BUS:DEV]\n" "Parse the output of usbmod assuming it is CCID compliant.\n\n" " --skip-escape do not show escape packets\n" " --sniffusb Assume output from Sniffusb.exe\n" " --verbose enable extra informational output\n" " --debug enable additional debug output\n" " --help display this help and exit\n\n" "Report bugs to " PACKAGE_BUGREPORT "."); exit (0); } else if (!strcmp (*argv, "--verbose")) { verbose = 1; argc--; argv++; } else if (!strcmp (*argv, "--debug")) { verbose = debug = 1; argc--; argv++; } else if (!strcmp (*argv, "--skip-escape")) { skip_escape = 1; argc--; argv++; } else if (!strcmp (*argv, "--sniffusb")) { sniffusb = 1; argc--; argv++; } } if (argc && sniffusb) die ("no arguments expected when using --sniffusb\n"); else if (argc > 1) die ("usage: " PGM " [BUS:DEV] (try --help for more information)\n"); if (argc == 1) { const char *s = strchr (argv[0], ':'); usb_bus = atoi (argv[0]); if (s) usb_dev = atoi (s+1); if (usb_bus < 1 || usb_bus > 999 || usb_dev < 1 || usb_dev > 999) die ("invalid bus:dev specified"); } signal (SIGPIPE, SIG_IGN); parse_input (stdin); return any_error? 1:0; } /* Local Variables: compile-command: "gcc -Wall -Wno-pointer-sign -g -o ccidmon ccidmon.c" End: */