diff options
Diffstat (limited to 'g10/card-util.c')
-rw-r--r-- | g10/card-util.c | 459 |
1 files changed, 345 insertions, 114 deletions
diff --git a/g10/card-util.c b/g10/card-util.c index 07d8ddad4..8c402a00f 100644 --- a/g10/card-util.c +++ b/g10/card-util.c @@ -1,5 +1,5 @@ /* card-util.c - Utility functions for the OpenPGP card. - * Copyright (C) 2003, 2004, 2005 Free Software Foundation, Inc. + * Copyright (C) 2003, 2004, 2005, 2009 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -25,7 +25,7 @@ #include <assert.h> #if GNUPG_MAJOR_VERSION != 1 -#include "gpg.h" +# include "gpg.h" #endif /*GNUPG_MAJOR_VERSION != 1*/ #include "util.h" #include "i18n.h" @@ -35,22 +35,46 @@ #include "main.h" #include "keyserver-internal.h" #if GNUPG_MAJOR_VERSION == 1 -#ifdef HAVE_LIBREADLINE -#include <stdio.h> -#include <readline/readline.h> -#endif /*HAVE_LIBREADLINE*/ -#include "cardglue.h" +# ifdef HAVE_LIBREADLINE +# define GNUPG_LIBREADLINE_H_INCLUDED +# include <stdio.h> +# include <readline/readline.h> +# endif /*HAVE_LIBREADLINE*/ +# include "cardglue.h" #else /*GNUPG_MAJOR_VERSION!=1*/ -#include "call-agent.h" +# include "call-agent.h" #endif /*GNUPG_MAJOR_VERSION!=1*/ #define CONTROL_D ('D' - 'A' + 1) +static void +write_sc_op_status (gpg_error_t err) +{ + switch (gpg_err_code (err)) + { + case 0: + write_status (STATUS_SC_OP_SUCCESS); + break; +#if GNUPG_MAJOR_VERSION != 1 + case GPG_ERR_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; +#endif /* GNUPG_MAJOR_VERSION != 1 */ + } +} + + /* Change the PIN of a an OpenPGP card. This is an interactive function. */ void -change_pin (int chvno, int allow_admin) +change_pin (int unblock_v2, int allow_admin) { struct agent_card_info_s info; int rc; @@ -75,16 +99,31 @@ change_pin (int chvno, int allow_admin) return; } - if(!allow_admin) + + if (unblock_v2) + { + if (!info.is_v2) + log_error (_("This command is only available for version 2 cards\n")); + else if (!info.chvretry[1]) + log_error (_("Reset Code not or not anymore available\n")); + else + { + rc = agent_scd_change_pin (2, info.serialno); + write_sc_op_status (rc); + if (rc) + tty_printf ("Error changing the PIN: %s\n", gpg_strerror (rc)); + else + tty_printf ("PIN changed.\n"); + } + } + else if (!allow_admin) { rc = agent_scd_change_pin (1, info.serialno); + write_sc_op_status (rc); if (rc) tty_printf ("Error changing the PIN: %s\n", gpg_strerror (rc)); else - { - write_status (STATUS_SC_OP_SUCCESS); - tty_printf ("PIN changed.\n"); - } + tty_printf ("PIN changed.\n"); } else for (;;) @@ -95,6 +134,7 @@ change_pin (int chvno, int allow_admin) tty_printf ("1 - change PIN\n" "2 - unblock PIN\n" "3 - change Admin PIN\n" + "4 - set the Reset Code\n" "Q - quit\n"); tty_printf ("\n"); @@ -106,36 +146,44 @@ change_pin (int chvno, int allow_admin) rc = 0; if (*answer == '1') { + /* Change PIN. */ rc = agent_scd_change_pin (1, info.serialno); + write_sc_op_status (rc); if (rc) tty_printf ("Error changing the PIN: %s\n", gpg_strerror (rc)); else - { - write_status (STATUS_SC_OP_SUCCESS); - tty_printf ("PIN changed.\n"); - } + tty_printf ("PIN changed.\n"); } else if (*answer == '2') { + /* Unblock PIN. */ rc = agent_scd_change_pin (101, info.serialno); + write_sc_op_status (rc); if (rc) tty_printf ("Error unblocking the PIN: %s\n", gpg_strerror (rc)); else - { - write_status (STATUS_SC_OP_SUCCESS); - tty_printf ("PIN unblocked and new PIN set.\n"); - } + tty_printf ("PIN unblocked and new PIN set.\n"); } else if (*answer == '3') { + /* Change Admin PIN. */ rc = agent_scd_change_pin (3, info.serialno); + write_sc_op_status (rc); if (rc) tty_printf ("Error changing the PIN: %s\n", gpg_strerror (rc)); else - { - write_status (STATUS_SC_OP_SUCCESS); - tty_printf ("PIN changed.\n"); - } + tty_printf ("PIN changed.\n"); + } + else if (*answer == '4') + { + /* Set a new Reset Code. */ + rc = agent_scd_change_pin (102, info.serialno); + write_sc_op_status (rc); + if (rc) + tty_printf ("Error setting the Reset Code: %s\n", + gpg_strerror (rc)); + else + tty_printf ("Reset Code set.\n"); } else if (*answer == 'q' || *answer == 'Q') { @@ -156,11 +204,13 @@ get_manufacturer (unsigned int no) case 0x0002: return "Prism"; case 0x0003: return "OpenFortress"; case 0x0004: return "Wewid AB"; + case 0x0005: return "ZeitControl"; + case 0x002A: return "Magrathea"; /* 0x00000 and 0xFFFF are defined as test cards per spec, 0xFFF00 to 0xFFFE are assigned for use with randomly created serial numbers. */ - case 0: + case 0x0000: case 0xffff: return "test card"; default: return (no & 0xff00) == 0xff00? "unmanaged S/N range":"unknown"; } @@ -287,6 +337,18 @@ fpr_is_zero (const char *fpr) } +/* Return true if the SHA1 fingerprint FPR consists only of 0xFF. */ +static int +fpr_is_ff (const char *fpr) +{ + int i; + + for (i=0; i < 20 && fpr[i] == '\xff'; i++) + ; + return (i == 20); +} + + /* Print all available information about the current card. */ void card_status (FILE *fp, char *serialno, size_t serialnobuflen) @@ -320,8 +382,35 @@ card_status (FILE *fp, char *serialno, size_t serialnobuflen) if (!info.serialno || strncmp (info.serialno, "D27600012401", 12) || strlen (info.serialno) != 32 ) { - if (opt.with_colons) - fputs ("unknown:\n", fp); + if (info.apptype && !strcmp (info.apptype, "NKS")) + { + if (opt.with_colons) + fputs ("netkey-card:\n", fp); + log_info ("this is a NetKey card\n"); + } + else if (info.apptype && !strcmp (info.apptype, "DINSIG")) + { + if (opt.with_colons) + fputs ("dinsig-card:\n", fp); + log_info ("this is a DINSIG compliant card\n"); + } + else if (info.apptype && !strcmp (info.apptype, "P15")) + { + if (opt.with_colons) + fputs ("pkcs15-card:\n", fp); + log_info ("this is a PKCS#15 compliant card\n"); + } + else if (info.apptype && !strcmp (info.apptype, "GELDKARTE")) + { + if (opt.with_colons) + fputs ("geldkarte-card:\n", fp); + log_info ("this is a Geldkarte compliant card\n"); + } + else + { + if (opt.with_colons) + fputs ("unknown:\n", fp); + } log_info ("not an OpenPGP card\n"); agent_release_card_info (&info); xfree (pk); @@ -367,6 +456,10 @@ card_status (FILE *fp, char *serialno, size_t serialnobuflen) fputs (":\n", fp); fprintf (fp, "forcepin:%d:::\n", !info.chv1_cached); + for (i=0; i < DIM (info.key_attr); i++) + if (info.key_attr[0].algo) + fprintf (fp, "keyattr:%d:%d:%u:\n", i+1, + info.key_attr[i].algo, info.key_attr[i].nbits); fprintf (fp, "maxpinlen:%d:%d:%d:\n", info.chvmaxlen[0], info.chvmaxlen[1], info.chvmaxlen[2]); fprintf (fp, "pinretry:%d:%d:%d:\n", @@ -442,6 +535,16 @@ card_status (FILE *fp, char *serialno, size_t serialnobuflen) } tty_fprintf (fp, "Signature PIN ....: %s\n", info.chv1_cached? _("not forced"): _("forced")); + if (info.key_attr[0].algo) + { + tty_fprintf (fp, "Key attributes ...:"); + for (i=0; i < DIM (info.key_attr); i++) + tty_fprintf (fp, " %u%c", + info.key_attr[i].nbits, + info.key_attr[i].algo == 1? 'R': + info.key_attr[i].algo == 17? 'D': '?'); + tty_fprintf (fp, "\n"); + } tty_fprintf (fp, "Max. PIN lengths .: %d %d %d\n", info.chvmaxlen[0], info.chvmaxlen[1], info.chvmaxlen[2]); tty_fprintf (fp, "PIN retry counter : %d %d %d\n", @@ -466,7 +569,10 @@ card_status (FILE *fp, char *serialno, size_t serialnobuflen) thefpr = (info.fpr1valid? info.fpr1 : info.fpr2valid? info.fpr2 : info.fpr3valid? info.fpr3 : NULL); - if ( thefpr && !get_pubkey_byfprint (pk, thefpr, 20)) + /* If the fingerprint is all 0xff, the key has no asssociated + OpenPGP certificate. */ + if ( thefpr && !fpr_is_ff (thefpr) + && !get_pubkey_byfprint (pk, thefpr, 20)) { KBNODE keyblock = NULL; @@ -599,6 +705,7 @@ change_url (void) if (rc) log_error ("error setting URL: %s\n", gpg_strerror (rc)); xfree (url); + write_sc_op_status (rc); return rc; } @@ -608,7 +715,6 @@ change_url (void) static int fetch_url(void) { -#if GNUPG_MAJOR_VERSION == 1 int rc; struct agent_card_info_s info; @@ -648,9 +754,91 @@ fetch_url(void) } return rc; -#else - return 0; +} + + +/* Read data from file FNAME up to MAXLEN characters. On error return + -1 and store NULL at R_BUFFER; on success return the number of + bytes read and store the address of a newly allocated buffer at + R_BUFFER. */ +static int +get_data_from_file (const char *fname, size_t maxlen, char **r_buffer) +{ + FILE *fp; + char *data; + int n; + + *r_buffer = NULL; + + fp = fopen (fname, "rb"); +#if GNUPG_MAJOR_VERSION == 1 + if (fp && is_secured_file (fileno (fp))) + { + fclose (fp); + fp = NULL; + errno = EPERM; + } #endif + if (!fp) + { + tty_printf (_("can't open `%s': %s\n"), fname, strerror (errno)); + return -1; + } + + data = xtrymalloc (maxlen? maxlen:1); + if (!data) + { + tty_printf (_("error allocating enough memory: %s\n"), strerror (errno)); + fclose (fp); + return -1; + } + + if (maxlen) + n = fread (data, 1, maxlen, fp); + else + n = 0; + fclose (fp); + if (n < 0) + { + tty_printf (_("error reading `%s': %s\n"), fname, strerror (errno)); + xfree (data); + return -1; + } + *r_buffer = data; + return n; +} + + +/* Write LENGTH bytes from BUFFER to file FNAME. Return 0 on + success. */ +static int +put_data_to_file (const char *fname, const void *buffer, size_t length) +{ + FILE *fp; + + fp = fopen (fname, "wb"); +#if GNUPG_MAJOR_VERSION == 1 + if (fp && is_secured_file (fileno (fp))) + { + fclose (fp); + fp = NULL; + errno = EPERM; + } +#endif + if (!fp) + { + tty_printf (_("can't create `%s': %s\n"), fname, strerror (errno)); + return -1; + } + + if (length && fwrite (buffer, length, 1, fp) != 1) + { + tty_printf (_("error writing `%s': %s\n"), fname, strerror (errno)); + fclose (fp); + return -1; + } + fclose (fp); + return 0; } @@ -663,34 +851,11 @@ change_login (const char *args) if (args && *args == '<') /* Read it from a file */ { - FILE *fp; - for (args++; spacep (args); args++) ; - fp = fopen (args, "rb"); -#if GNUPG_MAJOR_VERSION == 1 - if (fp && is_secured_file (fileno (fp))) - { - fclose (fp); - fp = NULL; - errno = EPERM; - } -#endif - if (!fp) - { - tty_printf (_("can't open `%s': %s\n"), args, strerror (errno)); - return -1; - } - - data = xmalloc (254); - n = fread (data, 1, 254, fp); - fclose (fp); + n = get_data_from_file (args, 254, &data); if (n < 0) - { - tty_printf (_("error reading `%s': %s\n"), args, strerror (errno)); - xfree (data); - return -1; - } + return -1; } else { @@ -715,6 +880,7 @@ change_login (const char *args) if (rc) log_error ("error setting login data: %s\n", gpg_strerror (rc)); xfree (data); + write_sc_op_status (rc); return rc; } @@ -731,35 +897,11 @@ change_private_do (const char *args, int nr) if (args && (args = strchr (args, '<'))) /* Read it from a file */ { - FILE *fp; - - /* Fixme: Factor this duplicated code out. */ for (args++; spacep (args); args++) ; - fp = fopen (args, "rb"); -#if GNUPG_MAJOR_VERSION == 1 - if (fp && is_secured_file (fileno (fp))) - { - fclose (fp); - fp = NULL; - errno = EPERM; - } -#endif - if (!fp) - { - tty_printf (_("can't open `%s': %s\n"), args, strerror (errno)); - return -1; - } - - data = xmalloc (254); - n = fread (data, 1, 254, fp); - fclose (fp); + n = get_data_from_file (args, 254, &data); if (n < 0) - { - tty_printf (_("error reading `%s': %s\n"), args, strerror (errno)); - xfree (data); - return -1; - } + return -1; } else { @@ -784,9 +926,74 @@ change_private_do (const char *args, int nr) if (rc) log_error ("error setting private DO: %s\n", gpg_strerror (rc)); xfree (data); + write_sc_op_status (rc); + return rc; +} + + +static int +change_cert (const char *args) +{ + char *data; + int n; + int rc; + + if (args && *args == '<') /* Read it from a file */ + { + for (args++; spacep (args); args++) + ; + n = get_data_from_file (args, 16384, &data); + if (n < 0) + return -1; + } + else + { + tty_printf ("usage error: redirectrion to file required\n"); + return -1; + } + +#warning need to implement this fucntion + rc = -1; /*agent_scd_writecert ("OPENPGP.3", data, n);*/ + if (rc) + log_error ("error writing certificate to card: %s\n", gpg_strerror (rc)); + xfree (data); + write_sc_op_status (rc); return rc; } + +static int +read_cert (const char *args) +{ + const char *fname; + void *buffer; + size_t length; + int rc; + + if (args && *args == '>') /* Write it to a file */ + { + for (args++; spacep (args); args++) + ; + fname = args; + } + else + { + tty_printf ("usage error: redirectrion to file required\n"); + return -1; + } + +#warning need to implement this fucntion + rc = -1; /*agent_scd_readcert ("OPENPGP.3", &buffer, &length);*/ + if (rc) + log_error ("error reading certificate from card: %s\n", gpg_strerror (rc)); + else + rc = put_data_to_file (fname, buffer, length); + xfree (buffer); + write_sc_op_status (rc); + return rc; +} + + static int change_lang (void) { @@ -820,6 +1027,7 @@ change_lang (void) if (rc) log_error ("error setting lang: %s\n", gpg_strerror (rc)); xfree (data); + write_sc_op_status (rc); return rc; } @@ -855,6 +1063,7 @@ change_sex (void) if (rc) log_error ("error setting sex: %s\n", gpg_strerror (rc)); xfree (data); + write_sc_op_status (rc); return rc; } @@ -899,6 +1108,7 @@ change_cafpr (int fprno) fprno==3?"CA-FPR-3":"x", fpr, 20, NULL ); if (rc) log_error ("error setting cafpr: %s\n", gpg_strerror (rc)); + write_sc_op_status (rc); return rc; } @@ -924,6 +1134,7 @@ toggle_forcesig (void) rc = agent_scd_setattr ("CHV-STATUS-1", newstate? "\x01":"", 1, NULL); if (rc) log_error ("error toggling signature PIN flag: %s\n", gpg_strerror (rc)); + write_sc_op_status (rc); } @@ -963,7 +1174,7 @@ check_pin_for_key_operation (struct agent_card_info_s *info, int *forced_chv1) *forced_chv1 = !info->chv1_cached; if (*forced_chv1) - { /* Switch of the forced mode so that during key generation we + { /* Switch off the forced mode so that during key generation we don't get bothered with PIN queries for each self-signature. */ rc = agent_scd_setattr ("CHV-STATUS-1", "\x01", 1, info->serialno); @@ -981,8 +1192,11 @@ check_pin_for_key_operation (struct agent_card_info_s *info, int *forced_chv1) binding signature. */ rc = agent_scd_checkpin (info->serialno); if (rc) - log_error ("error checking the PIN: %s\n", gpg_strerror (rc)); - } + { + log_error ("error checking the PIN: %s\n", gpg_strerror (rc)); + write_sc_op_status (rc); + } + } return rc; } @@ -1003,7 +1217,7 @@ restore_forced_chv1 (int *forced_chv1) } } -#if GNUPG_MAJOR_VERSION == 1 + /* Helper for the key generation/edit functions. */ static void show_card_key_info (struct agent_card_info_s *info) @@ -1016,9 +1230,8 @@ show_card_key_info (struct agent_card_info_s *info) print_sha1_fpr (NULL, info->fpr3valid? info->fpr3:NULL); tty_printf ("\n"); } -#endif -#if GNUPG_MAJOR_VERSION == 1 + /* Helper for the key generation/edit functions. */ static int replace_existing_key_p (struct agent_card_info_s *info, int keyno) @@ -1038,11 +1251,10 @@ replace_existing_key_p (struct agent_card_info_s *info, int keyno) } return 0; } -#endif static void -generate_card_keys (const char *serialno) +generate_card_keys (void) { struct agent_card_info_s info; int forced_chv1; @@ -1094,12 +1306,8 @@ generate_card_keys (const char *serialno) if (check_pin_for_key_operation (&info, &forced_chv1)) goto leave; -#if GNUPG_MAJOR_VERSION == 1 generate_keypair (NULL, info.serialno, want_backup? opt.homedir:NULL); -#else - generate_keypair (NULL, info.serialno); -#endif leave: agent_release_card_info (&info); @@ -1112,7 +1320,6 @@ generate_card_keys (const char *serialno) int card_generate_subkey (KBNODE pub_keyblock, KBNODE sec_keyblock) { -#if GNUPG_MAJOR_VERSION == 1 struct agent_card_info_s info; int okay = 0; int forced_chv1 = 0; @@ -1159,9 +1366,6 @@ card_generate_subkey (KBNODE pub_keyblock, KBNODE sec_keyblock) agent_release_card_info (&info); restore_forced_chv1 (&forced_chv1); return okay; -#else - return 0; -#endif } @@ -1172,7 +1376,6 @@ card_generate_subkey (KBNODE pub_keyblock, KBNODE sec_keyblock) int card_store_subkey (KBNODE node, int use) { -#if GNUPG_MAJOR_VERSION == 1 struct agent_card_info_s info; int okay = 0; int rc; @@ -1192,7 +1395,8 @@ card_store_subkey (KBNODE node, int use) show_card_key_info (&info); - if (!is_RSA (sk->pubkey_algo) || nbits_from_sk (sk) != 1024 ) + if (!is_RSA (sk->pubkey_algo) + || (!info.is_v2 && nbits_from_sk (sk) != 1024) ) { tty_printf ("You may only store a 1024 bit RSA key on the card\n"); tty_printf ("\n"); @@ -1260,7 +1464,10 @@ card_store_subkey (KBNODE node, int use) rc = save_unprotected_key_to_card (sk, keyno); if (rc) - goto leave; + { + log_error (_("error writing key to card: %s\n"), gpg_strerror (rc)); + goto leave; + } /* Get back to the maybe protected original secret key. */ if (copied_sk) @@ -1274,11 +1481,11 @@ card_store_subkey (KBNODE node, int use) n = pubkey_get_nskey (sk->pubkey_algo); for (i=pubkey_get_npkey (sk->pubkey_algo); i < n; i++) { - mpi_free (sk->skey[i]); + gcry_mpi_release (sk->skey[i]); sk->skey[i] = NULL; } i = pubkey_get_npkey (sk->pubkey_algo); - sk->skey[i] = mpi_set_opaque (NULL, xstrdup ("dummydata"), 10); + sk->skey[i] = gcry_mpi_set_opaque (NULL, xstrdup ("dummydata"), 10*8); sk->is_protected = 1; sk->protect.s2k.mode = 1002; s = info.serialno; @@ -1293,9 +1500,6 @@ card_store_subkey (KBNODE node, int use) free_secret_key (copied_sk); agent_release_card_info (&info); return okay; -#else - return 0; -#endif } @@ -1307,7 +1511,8 @@ enum cmdids cmdNOP = 0, cmdQUIT, cmdADMIN, cmdHELP, cmdLIST, cmdDEBUG, cmdVERIFY, cmdNAME, cmdURL, cmdFETCH, cmdLOGIN, cmdLANG, cmdSEX, cmdCAFPR, - cmdFORCESIG, cmdGENERATE, cmdPASSWD, cmdPRIVATEDO, + cmdFORCESIG, cmdGENERATE, cmdPASSWD, cmdPRIVATEDO, cmdWRITECERT, + cmdREADCERT, cmdUNBLOCK, cmdINVCMD }; @@ -1338,8 +1543,11 @@ static struct { "generate", cmdGENERATE, 1, N_("generate new keys")}, { "passwd" , cmdPASSWD, 0, N_("menu to change or unblock the PIN")}, { "verify" , cmdVERIFY, 0, N_("verify the PIN and list all data")}, - /* Note, that we do not announce this command yet. */ + { "unblock" , cmdUNBLOCK,0, N_("unblock the PIN using a Reset Code") }, + /* Note, that we do not announce these command yet. */ { "privatedo", cmdPRIVATEDO, 0, NULL }, + { "readcert", cmdREADCERT, 0, NULL }, + { "writecert", cmdWRITECERT, 1, NULL }, { NULL, cmdINVCMD, 0, NULL } }; @@ -1392,13 +1600,13 @@ card_edit_completion(const char *text, int start, int end) /* Menu to edit all user changeable values on an OpenPGP card. Only Key creation is not handled here. */ void -card_edit (STRLIST commands) +card_edit (strlist_t commands) { enum cmdids cmd = cmdNOP; int have_commands = !!commands; int redisplay = 1; char *answer = NULL; - int did_checkpin = 0, allow_admin=0; + int allow_admin=0; char serialnobuf[50]; @@ -1414,6 +1622,7 @@ card_edit (STRLIST commands) { int arg_number; const char *arg_string = ""; + const char *arg_rest = ""; char *p; int i; int cmd_admin_only; @@ -1482,6 +1691,11 @@ card_edit (STRLIST commands) trim_spaces (p); arg_number = atoi(p); arg_string = p; + arg_rest = p; + while (digitp (arg_rest)) + arg_rest++; + while (spacep (arg_rest)) + arg_rest++; } for (i=0; cmds[i].name; i++ ) @@ -1580,17 +1794,34 @@ card_edit (STRLIST commands) change_private_do (arg_string, arg_number); break; + case cmdWRITECERT: + if ( arg_number != 3 ) + tty_printf ("usage: writecert 3 < FILE\n"); + else + change_cert (arg_rest); + break; + + case cmdREADCERT: + if ( arg_number != 3 ) + tty_printf ("usage: readcert 3 > FILE\n"); + else + read_cert (arg_rest); + break; + case cmdFORCESIG: toggle_forcesig (); break; case cmdGENERATE: - generate_card_keys (serialnobuf); + generate_card_keys (); break; case cmdPASSWD: change_pin (0, allow_admin); - did_checkpin = 0; /* Need to reset it of course. */ + break; + + case cmdUNBLOCK: + change_pin (1, allow_admin); break; case cmdQUIT: |