From e6d613711a327d63511601dd42aeff34e09ec95a Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Tue, 22 Jan 2019 09:07:24 +0100 Subject: [PATCH] card-tool: Add skeleton for new tool * tools/gpg-card-tool.c: New. * tools/gpg-card-tool-w32info.rc: New. * tools/Makefile.am: Add new tool. -- To support more cards than the OpenPGP card it is useful to have a separate tool. It will have have the "gpg --card-edit" style interactive interface as well as direct command line options for all commands. In a first step the OpenPGP card will be supported, to allow its use as an alternative to the gpg command, and the forthcoming PIV card support. The tool can be though as a direct interface to scdaemon. Signed-off-by: Werner Koch --- tools/Makefile.am | 20 +- tools/gpg-card-tool-w32info.rc | 51 ++ tools/gpg-card-tool.c | 869 +++++++++++++++++++++++++++++ tools/gpg-connect-agent-w32info.rc | 2 +- 4 files changed, 936 insertions(+), 6 deletions(-) create mode 100644 tools/gpg-card-tool-w32info.rc create mode 100644 tools/gpg-card-tool.c diff --git a/tools/Makefile.am b/tools/Makefile.am index e4fd81c5d..4833bff5e 100644 --- a/tools/Makefile.am +++ b/tools/Makefile.am @@ -20,14 +20,17 @@ EXTRA_DIST = \ Manifest watchgnupg.c no-libgcrypt.c \ addgnupghome applygnupgdefaults \ lspgpot mail-signed-keys convert-from-106 sockprox.c \ - ccidmon.c ChangeLog-2011 gpg-connect-agent-w32info.rc - + ccidmon.c ChangeLog-2011 \ + gpg-connect-agent-w32info.rc \ + gpg-card-tool-w32info.rc AM_CPPFLAGS = include $(top_srcdir)/am/cmacros.am if HAVE_W32_SYSTEM -resource_objs += gpg-connect-agent-w32info.o +gpg_connect_agent_rc_objs = gpg-connect-agent-w32info.o +gpg_card_tool_rc_objs = gpg-card-tool-w32info.o +resource_objs += $(gpg_connect_agent_rc_objs) $(gpg_card_tool_rc_objs) endif AM_CFLAGS = $(LIBGCRYPT_CFLAGS) $(GPG_ERROR_CFLAGS) $(LIBASSUAN_CFLAGS) @@ -48,7 +51,7 @@ endif libexec_PROGRAMS = gpg-wks-client gpg-pair-tool -bin_PROGRAMS = gpgconf gpg-connect-agent ${symcryptrun} +bin_PROGRAMS = gpgconf gpg-connect-agent gpg-card-tool ${symcryptrun} if !HAVE_W32_SYSTEM bin_PROGRAMS += watchgnupg gpgparsemail ${gpg_wks_server} endif @@ -118,7 +121,14 @@ gpg_connect_agent_LDADD = ../common/libgpgrl.a $(common_libs) \ $(LIBASSUAN_LIBS) $(LIBGCRYPT_LIBS) \ $(GPG_ERROR_LIBS) \ $(LIBREADLINE) $(LIBINTL) $(NETLIBS) $(LIBICONV) \ - $(resource_objs) + $(gpg_connect_agent_rc_objs) + +gpg_card_tool_SOURCES = gpg-card-tool.c +gpg_card_tool_LDADD = ../common/libgpgrl.a $(common_libs) \ + $(LIBASSUAN_LIBS) $(LIBGCRYPT_LIBS) \ + $(GPG_ERROR_LIBS) \ + $(LIBREADLINE) $(LIBINTL) $(NETLIBS) $(LIBICONV) \ + $(gpg_card_tool_rc_objs) if !DISABLE_REGEX diff --git a/tools/gpg-card-tool-w32info.rc b/tools/gpg-card-tool-w32info.rc new file mode 100644 index 000000000..6937c3e34 --- /dev/null +++ b/tools/gpg-card-tool-w32info.rc @@ -0,0 +1,51 @@ +/* gpg-card-toolt-w32info.rc -*- c -*- + * Copyright (C) 2019 g10 Code GmbH + * + * This file is free software; as a special exception the author gives + * unlimited permission to copy and/or distribute it, with or without + * modifications, as long as this notice is preserved. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY, to the extent permitted by law; without even the + * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + */ + +#include "afxres.h" +#include "../common/w32info-rc.h" + +1 ICON "../common/gnupg.ico" + +1 VERSIONINFO + FILEVERSION W32INFO_VI_FILEVERSION + PRODUCTVERSION W32INFO_VI_PRODUCTVERSION + FILEFLAGSMASK 0x3fL +#ifdef _DEBUG + FILEFLAGS 0x01L /* VS_FF_DEBUG (0x1)*/ +#else + FILEFLAGS 0x00L +#endif + FILEOS 0x40004L /* VOS_NT (0x40000) | VOS__WINDOWS32 (0x4) */ + FILETYPE 0x1L /* VFT_APP (0x1) */ + FILESUBTYPE 0x0L /* VFT2_UNKNOWN */ + BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" /* US English (0409), Unicode (04b0) */ + BEGIN + VALUE "FileDescription", L"GnuPG\x2019s card tool \ +to the agent\0" + VALUE "InternalName", "gpg-card-tool\0" + VALUE "OriginalFilename", "gpg-card-tool.exe\0" + VALUE "ProductName", W32INFO_PRODUCTNAME + VALUE "ProductVersion", W32INFO_PRODUCTVERSION + VALUE "CompanyName", W32INFO_COMPANYNAME + VALUE "FileVersion", W32INFO_FILEVERSION + VALUE "LegalCopyright", W32INFO_LEGALCOPYRIGHT + VALUE "Comments", W32INFO_COMMENTS + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 0x4b0 + END + END diff --git a/tools/gpg-card-tool.c b/tools/gpg-card-tool.c new file mode 100644 index 000000000..91f04108e --- /dev/null +++ b/tools/gpg-card-tool.c @@ -0,0 +1,869 @@ +/* gpg-card-tool.c - An interactive tool to work with cards. + * Copyright (C) 2019 g10 Code GmbH 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 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. + * + * 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + * SPDX-License-Identifier: GPL-3.0+ + */ + +#include +#include +#include +#include +#ifdef HAVE_LIBREADLINE +# define GNUPG_LIBREADLINE_H_INCLUDED +# include +#endif /*HAVE_LIBREADLINE*/ + +#include "../common/util.h" +#include "../common/status.h" +#include "../common/i18n.h" +#include "../common/init.h" +#include "../common/sysutils.h" +#include "../common/asshelp.h" +#include "../common/userids.h" +#include "../common/ccparray.h" +#include "../common/exectool.h" +#include "../common/ttyio.h" + +#define CONTROL_D ('D' - 'A' + 1) + +/* Constants to identify the commands and options. */ +enum cmd_and_opt_values + { + aNull = 0, + + oQuiet = 'q', + oVerbose = 'v', + + oDebug = 500, + + oGpgProgram, + oGpgsmProgram, + oStatusFD, + oWithColons, + + oDummy + }; + + +/* The list of commands and options. */ +static ARGPARSE_OPTS opts[] = { + ARGPARSE_group (300, ("@Commands:\n ")), + + ARGPARSE_group (301, ("@\nOptions:\n ")), + + ARGPARSE_s_n (oVerbose, "verbose", ("verbose")), + ARGPARSE_s_n (oQuiet, "quiet", ("be somewhat more quiet")), + ARGPARSE_s_s (oDebug, "debug", "@"), + ARGPARSE_s_s (oGpgProgram, "gpg", "@"), + ARGPARSE_s_s (oGpgsmProgram, "gpgsm", "@"), + ARGPARSE_s_i (oStatusFD, "status-fd", N_("|FD|write status info to this FD")), + ARGPARSE_s_n (oWithColons, "with-colons", "@"), + + ARGPARSE_end () +}; + +/* Debug values and macros. */ +#define DBG_IPC_VALUE 1024 /* Debug assuan communication. */ +#define DBG_EXTPROG_VALUE 16384 /* debug external program calls */ + + +/* The list of supported debug flags. */ +static struct debug_flags_s debug_flags [] = + { + { DBG_IPC_VALUE , "ipc" }, + { DBG_EXTPROG_VALUE, "extprog" }, + { 0, NULL } + }; + + + +/* We keep all global options in the structure OPT. */ +struct +{ + int verbose; + unsigned int debug; + int quiet; + int with_colons; + const char *gpg_program; + const char *gpgsm_program; +} opt; + + +static void wrong_args (const char *text) GPGRT_ATTR_NORETURN; +static void interactive_loop (void); +#ifdef HAVE_LIBREADLINE +static char **command_completion (const char *text, int start, int end); +#endif /*HAVE_LIBREADLINE*/ + + + +/* Print usage information and provide strings for help. */ +static const char * +my_strusage( int level ) +{ + const char *p; + + switch (level) + { + case 11: p = "gpg-card-tool"; break; + case 12: p = "@GNUPG@"; break; + case 13: p = VERSION; break; + case 17: p = PRINTABLE_OS_NAME; break; + case 19: p = ("Please report bugs to <@EMAIL@>.\n"); break; + + case 1: + case 40: + p = ("Usage: gpg-card-tool [command] [options] [args] (-h for help)"); + break; + case 41: + p = ("Syntax: gpg-card-tool [command] [options] [args]\n" + "Tool to configure cards and tokens\n"); + break; + + default: p = NULL; break; + } + return p; +} + + +static void +wrong_args (const char *text) +{ + es_fprintf (es_stderr, _("usage: %s [options] %s\n"), strusage (11), text); + exit (2); +} + + + +/* Command line parsing. */ +static enum cmd_and_opt_values +parse_arguments (ARGPARSE_ARGS *pargs, ARGPARSE_OPTS *popts) +{ + enum cmd_and_opt_values cmd = 0; + int no_more_options = 0; + + while (!no_more_options && optfile_parse (NULL, NULL, NULL, pargs, popts)) + { + switch (pargs->r_opt) + { + case oQuiet: opt.quiet = 1; break; + case oVerbose: opt.verbose++; break; + case oDebug: + if (parse_debug_flag (pargs->r.ret_str, &opt.debug, debug_flags)) + { + pargs->r_opt = ARGPARSE_INVALID_ARG; + pargs->err = ARGPARSE_PRINT_ERROR; + } + break; + + case oGpgProgram: + opt.gpg_program = pargs->r.ret_str; + break; + case oGpgsmProgram: + opt.gpgsm_program = pargs->r.ret_str; + break; + case oStatusFD: + gnupg_set_status_fd (translate_sys2libc_fd_int (pargs->r.ret_int, 1)); + break; + case oWithColons: + opt.with_colons = 1; + break; + + default: pargs->err = 2; break; + } + } + + return cmd; +} + + + +/* gpg-card-tool main. */ +int +main (int argc, char **argv) +{ + gpg_error_t err; + ARGPARSE_ARGS pargs; + enum cmd_and_opt_values cmd; + + gnupg_reopen_std ("gpg-card-tool"); + set_strusage (my_strusage); + gnupg_rl_initialize (); + log_set_prefix ("gpg-card-tool", GPGRT_LOG_WITH_PREFIX); + + /* Make sure that our subsystems are ready. */ + i18n_init(); + init_common_subsystems (&argc, &argv); + + assuan_set_gpg_err_source (GPG_ERR_SOURCE_DEFAULT); + setup_libassuan_logging (&opt.debug, NULL); + + /* Parse the command line. */ + pargs.argc = &argc; + pargs.argv = &argv; + pargs.flags = ARGPARSE_FLAG_KEEP; + cmd = parse_arguments (&pargs, opts); + + if (log_get_errorcount (0)) + exit (2); + + /* Print a warning if an argument looks like an option. */ + if (!opt.quiet && !(pargs.flags & ARGPARSE_FLAG_STOP_SEEN)) + { + int i; + + for (i=0; i < argc; i++) + if (argv[i][0] == '-' && argv[i][1] == '-') + log_info (("NOTE: '%s' is not considered an option\n"), argv[i]); + } + + /* Set defaults for non given options. */ + if (!opt.gpg_program) + opt.gpg_program = gnupg_module_name (GNUPG_MODULE_NAME_GPG); + if (!opt.gpgsm_program) + opt.gpgsm_program = gnupg_module_name (GNUPG_MODULE_NAME_GPGSM); + + /* Run the selected command. */ + switch (cmd) + { + default: + interactive_loop (); + err = 0; + break; + } + + if (err) + gnupg_status_printf (STATUS_FAILURE, "- %u", err); + else if (log_get_errorcount (0)) + gnupg_status_printf (STATUS_FAILURE, "- %u", GPG_ERR_GENERAL); + else + gnupg_status_printf (STATUS_SUCCESS, NULL); + return log_get_errorcount (0)? 1:0; +} + + + +/* Print all available information about the current card. */ +static void +print_card_status (char *serialno, size_t serialnobuflen) +{ + /* struct agent_card_info_s info; */ + /* PKT_public_key *pk = xcalloc (1, sizeof *pk); */ + /* kbnode_t keyblock = NULL; */ + /* int rc; */ + /* unsigned int uval; */ + /* const unsigned char *thefpr; */ + /* unsigned int thefprlen; */ + /* int i; */ + + /* if (serialno && serialnobuflen) */ + /* *serialno = 0; */ + + /* rc = agent_scd_learn (&info, 0); */ + /* if (rc) */ + /* { */ + /* if (opt.with_colons) */ + /* es_fputs ("AID:::\n", fp); */ + /* log_error (_("OpenPGP card not available: %s\n"), gpg_strerror (rc)); */ + /* xfree (pk); */ + /* return; */ + /* } */ + + /* if (opt.with_colons) */ + /* es_fprintf (fp, "Reader:%s:", info.reader? info.reader : ""); */ + /* else */ + /* tty_fprintf (fp, "Reader ...........: %s\n", */ + /* info.reader? info.reader : "[none]"); */ + /* if (opt.with_colons) */ + /* es_fprintf (fp, "AID:%s:", info.serialno? info.serialno : ""); */ + /* else */ + /* tty_fprintf (fp, "Application ID ...: %s\n", */ + /* info.serialno? info.serialno : "[none]"); */ + /* if (!info.serialno || strncmp (info.serialno, "D27600012401", 12) */ + /* || strlen (info.serialno) != 32 ) */ + /* { */ + /* if (info.apptype && !strcmp (info.apptype, "NKS")) */ + /* { */ + /* if (opt.with_colons) */ + /* es_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) */ + /* es_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) */ + /* es_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) */ + /* es_fputs ("geldkarte-card:\n", fp); */ + /* log_info ("this is a Geldkarte compliant card\n"); */ + /* } */ + /* else */ + /* { */ + /* if (opt.with_colons) */ + /* es_fputs ("unknown:\n", fp); */ + /* } */ + /* log_info ("not an OpenPGP card\n"); */ + /* agent_release_card_info (&info); */ + /* xfree (pk); */ + /* return; */ + /* } */ + + /* if (!serialno) */ + /* ; */ + /* else if (strlen (info.serialno)+1 > serialnobuflen) */ + /* log_error ("serial number longer than expected\n"); */ + /* else */ + /* strcpy (serialno, info.serialno); */ + + /* if (opt.with_colons) */ + /* es_fputs ("openpgp-card:\n", fp); */ + + + /* tty_fprintf (fp, "Version ..........: %.1s%c.%.1s%c\n", */ + /* info.serialno[12] == '0'?"":info.serialno+12, */ + /* info.serialno[13], */ + /* info.serialno[14] == '0'?"":info.serialno+14, */ + /* info.serialno[15]); */ + /* tty_fprintf (fp, "Manufacturer .....: %s\n", */ + /* get_manufacturer (xtoi_2(info.serialno+16)*256 */ + /* + xtoi_2 (info.serialno+18))); */ + /* tty_fprintf (fp, "Serial number ....: %.8s\n", info.serialno+20); */ + + /* print_isoname (fp, "Name of cardholder: ", "name", info.disp_name); */ + /* print_name (fp, "Language prefs ...: ", info.disp_lang); */ + /* tty_fprintf (fp, "Salutation .......: %s\n", */ + /* info.disp_sex == 1? _("Mr."): */ + /* info.disp_sex == 2? _("Mrs.") : ""); */ + /* print_name (fp, "URL of public key : ", info.pubkey_url); */ + /* print_name (fp, "Login data .......: ", info.login_data); */ + /* if (info.private_do[0]) */ + /* print_name (fp, "Private DO 1 .....: ", info.private_do[0]); */ + /* if (info.private_do[1]) */ + /* print_name (fp, "Private DO 2 .....: ", info.private_do[1]); */ + /* if (info.private_do[2]) */ + /* print_name (fp, "Private DO 3 .....: ", info.private_do[2]); */ + /* if (info.private_do[3]) */ + /* print_name (fp, "Private DO 4 .....: ", info.private_do[3]); */ + /* if (info.cafpr1len) */ + /* { */ + /* tty_fprintf (fp, "CA fingerprint %d .:", 1); */ + /* print_shax_fpr (fp, info.cafpr1, info.cafpr1len); */ + /* } */ + /* if (info.cafpr2len) */ + /* { */ + /* tty_fprintf (fp, "CA fingerprint %d .:", 2); */ + /* print_shax_fpr (fp, info.cafpr2, info.cafpr2len); */ + /* } */ + /* if (info.cafpr3len) */ + /* { */ + /* tty_fprintf (fp, "CA fingerprint %d .:", 3); */ + /* print_shax_fpr (fp, info.cafpr3, info.cafpr3len); */ + /* } */ + /* 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++) */ + /* if (info.key_attr[i].algo == PUBKEY_ALGO_RSA) */ + /* tty_fprintf (fp, " rsa%u", info.key_attr[i].nbits); */ + /* else if (info.key_attr[i].algo == PUBKEY_ALGO_ECDH */ + /* || info.key_attr[i].algo == PUBKEY_ALGO_ECDSA */ + /* || info.key_attr[i].algo == PUBKEY_ALGO_EDDSA) */ + /* { */ + /* const char *curve_for_print = "?"; */ + + /* if (info.key_attr[i].curve) */ + /* { */ + /* const char *oid; */ + /* oid = openpgp_curve_to_oid (info.key_attr[i].curve, NULL); */ + /* if (oid) */ + /* curve_for_print = openpgp_oid_to_curve (oid, 0); */ + /* } */ + /* tty_fprintf (fp, " %s", curve_for_print); */ + /* } */ + /* 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", */ + /* info.chvretry[0], info.chvretry[1], info.chvretry[2]); */ + /* tty_fprintf (fp, "Signature counter : %lu\n", info.sig_counter); */ + /* if (info.extcap.kdf) */ + /* { */ + /* tty_fprintf (fp, "KDF setting ......: %s\n", */ + /* info.kdf_do_enabled ? "on" : "off"); */ + /* } */ + /* if (info.extcap.bt) */ + /* { */ + /* tty_fprintf (fp, "UIF setting ......: Sign=%s Decrypt=%s Auth=%s\n", */ + /* info.uif[0] ? "on" : "off", info.uif[1] ? "on" : "off", */ + /* info.uif[2] ? "on" : "off"); */ + /* } */ + /* tty_fprintf (fp, "Signature key ....:"); */ + /* print_shax_fpr (fp, info.fpr1len? info.fpr1:NULL, info.fpr1len); */ + /* if (info.fpr1len && info.fpr1time) */ + /* { */ + /* tty_fprintf (fp, " created ....: %s\n", */ + /* isotimestamp (info.fpr1time)); */ + /* print_keygrip (fp, info.grp1); */ + /* } */ + /* tty_fprintf (fp, "Encryption key....:"); */ + /* print_shax_fpr (fp, info.fpr2len? info.fpr2:NULL, info.fpr2len); */ + /* if (info.fpr2len && info.fpr2time) */ + /* { */ + /* tty_fprintf (fp, " created ....: %s\n", */ + /* isotimestamp (info.fpr2time)); */ + /* print_keygrip (fp, info.grp2); */ + /* } */ + /* tty_fprintf (fp, "Authentication key:"); */ + /* print_shax_fpr (fp, info.fpr3len? info.fpr3:NULL, info.fpr3len); */ + /* if (info.fpr3len && info.fpr3time) */ + /* { */ + /* tty_fprintf (fp, " created ....: %s\n", */ + /* isotimestamp (info.fpr3time)); */ + /* print_keygrip (fp, info.grp3); */ + /* } */ + /* tty_fprintf (fp, "General key info..: "); */ + + /* thefpr = (info.fpr1len? info.fpr1 : info.fpr2len? info.fpr2 : */ + /* info.fpr3len? info.fpr3 : NULL); */ + /* thefprlen = (info.fpr1len? info.fpr1len : info.fpr2len? info.fpr2len : */ + /* info.fpr3len? info.fpr3len : 0); */ + /* /\* If the fingerprint is all 0xff, the key has no associated */ + /* OpenPGP certificate. *\/ */ + /* if ( thefpr && !fpr_is_ff (thefpr, thefprlen) */ + /* && !get_pubkey_byfprint (ctrl, pk, &keyblock, thefpr, thefprlen)) */ + /* { */ + /* print_pubkey_info (ctrl, fp, pk); */ + /* if (keyblock) */ + /* print_card_key_info (fp, keyblock); */ + /* } */ + /* else */ + /* tty_fprintf (fp, "[none]\n"); */ + + /* release_kbnode (keyblock); */ + /* free_public_key (pk); */ + /* agent_release_card_info (&info); */ +} + + + +static void +cmd_verify (void) +{ + /* agent_scd_checkpin (serialnobuf); */ +} + + +static void +cmd_name (void) +{ + /* change_name (); */ +} + + +static void +cmd_url (void) +{ + /* change_url (); */ +} + + +static void +cmd_fetch (void) +{ + /* fetch_url (); */ +} + + +static void +cmd_login (char *arg_string) +{ + /* change_login (arg_string); */ +} + + +static void +cmd_lang (void) +{ + /* change_lang (); */ +} + + +static void +cmd_salut (void) +{ + /* change_salut (); */ +} + + +static void +cmd_cafpr (int arg_number) +{ + if ( arg_number < 1 || arg_number > 3 ) + tty_printf ("usage: cafpr N\n" + " 1 <= N <= 3\n"); + /* else */ + /* change_cafpr (arg_number); */ +} + + +static void +cmd_privatedo (int arg_number, char *arg_string) +{ + if ( arg_number < 1 || arg_number > 4 ) + tty_printf ("usage: privatedo N\n" + " 1 <= N <= 4\n"); + /* else */ + /* change_private_do (arg_string, arg_number); */ +} + + +static void +cmd_writecert (int arg_number, char *arg_rest) +{ + if ( arg_number != 3 ) + tty_printf ("usage: writecert 3 < FILE\n"); + /* else */ + /* change_cert (arg_rest); */ +} + + +static void +cmd_readcert (int arg_number, char *arg_rest) +{ + if ( arg_number != 3 ) + tty_printf ("usage: readcert 3 > FILE\n"); + /* else */ + /* read_cert (arg_rest); */ +} + + +static void +cmd_forcesig (void) +{ + /* toggle_forcesig (); */ +} + + +static void +cmd_generate (void) +{ + /* generate_card_keys (); */ +} + + +static void +cmd_passwd (int allow_admin) +{ + /* change_pin (0, allow_admin); */ +} + + +static void +cmd_unblock (int allow_admin) +{ + /* change_pin (1, allow_admin); */ +} + + +static void +cmd_factoryreset (void) +{ + /* factory_reset (); */ +} + + +static void +cmd_kdfsetup (char *argstring) +{ + /* kdf_setup (arg_string); */ +} + + +static void +cmd_keyattr (void) +{ + /* key_attr (); */ +} + + +static void +cmd_uif (int arg_number, char *arg_rest) +{ + if ( arg_number < 1 || arg_number > 3 ) + tty_printf ("usage: uif N [on|off|permanent]\n" + " 1 <= N <= 3\n"); + /* else */ + /* uif (arg_number, arg_rest); */ +} + + + +/* Data used by the command parser. This needs to be outside of the + * function scope to allow readline based command completion. */ +enum cmdids + { + cmdNOP = 0, + cmdQUIT, cmdADMIN, cmdHELP, cmdLIST, cmdDEBUG, cmdVERIFY, + cmdNAME, cmdURL, cmdFETCH, cmdLOGIN, cmdLANG, cmdSALUT, cmdCAFPR, + cmdFORCESIG, cmdGENERATE, cmdPASSWD, cmdPRIVATEDO, cmdWRITECERT, + cmdREADCERT, cmdUNBLOCK, cmdFACTORYRESET, cmdKDFSETUP, + cmdKEYATTR, cmdUIF, + cmdINVCMD + }; + +static struct +{ + const char *name; + enum cmdids id; + int admin_only; + const char *desc; +} cmds[] = { + { "quit" , cmdQUIT , 0, N_("quit this menu")}, + { "q" , cmdQUIT , 0, NULL }, + { "admin" , cmdADMIN , 0, N_("show admin commands")}, + { "help" , cmdHELP , 0, N_("show this help")}, + { "?" , cmdHELP , 0, NULL }, + { "list" , cmdLIST , 0, N_("list all available data")}, + { "l" , cmdLIST , 0, NULL }, + { "debug" , cmdDEBUG , 0, NULL }, + { "name" , cmdNAME , 1, N_("change card holder's name")}, + { "url" , cmdURL , 1, N_("change URL to retrieve key")}, + { "fetch" , cmdFETCH , 0, N_("fetch the key specified in the card URL")}, + { "login" , cmdLOGIN , 1, N_("change the login name")}, + { "lang" , cmdLANG , 1, N_("change the language preferences")}, + { "salutation",cmdSALUT, 1, N_("change card holder's salutation")}, + { "cafpr" , cmdCAFPR , 1, N_("change a CA fingerprint")}, + { "forcesig", cmdFORCESIG, 1, N_("toggle the signature force PIN flag")}, + { "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")}, + { "unblock" , cmdUNBLOCK,0, N_("unblock the PIN using a Reset Code")}, + { "factory-reset", cmdFACTORYRESET, 1, N_("destroy all keys and data")}, + { "kdf-setup", cmdKDFSETUP, 1, N_("setup KDF for PIN authentication")}, + { "key-attr", cmdKEYATTR, 1, N_("change the key attribute")}, + { "uif", cmdUIF, 1, N_("change the User Interaction Flag")}, + /* 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 } +}; + + +/* The main loop. */ +static void +interactive_loop (void) +{ + char *answer = NULL; /* The input line. */ + enum cmdids cmd = cmdNOP; /* The command. */ + int cmd_admin_only; /* The command is an admin only command. */ + int arg_number; /* The first argument as a number. */ + char *arg_string = ""; /* The first argument as a string. */ + char *arg_rest = ""; /* The remaining arguments. */ + int redisplay = 1; /* Whether to redisplay the main info. */ + int allow_admin = 0; /* Whether admin commands are allowed. */ + char serialnobuf[50]; + char *p; + int i; + + for (;;) + { + + tty_printf ("\n"); + if (redisplay) + { + print_card_status (serialnobuf, DIM (serialnobuf)); + tty_printf("\n"); + redisplay = 0; + } + + do + { + xfree (answer); + tty_enable_completion (command_completion); + answer = tty_get (_("gpg/card> ")); + tty_kill_prompt(); + tty_disable_completion (); + trim_spaces(answer); + } + while ( *answer == '#' ); + + arg_number = 0; + cmd_admin_only = 0; + if (!*answer) + cmd = cmdLIST; /* We default to the list command */ + else if (*answer == CONTROL_D) + cmd = cmdQUIT; + else + { + if ((p=strchr (answer,' '))) + { + *p++ = 0; + trim_spaces (answer); + 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++ ) + if (!ascii_strcasecmp (answer, cmds[i].name )) + break; + + cmd = cmds[i].id; + cmd_admin_only = cmds[i].admin_only; + } + + if (!allow_admin && cmd_admin_only) + { + tty_printf ("\n"); + tty_printf (_("Admin-only command\n")); + continue; + } + + switch (cmd) + { + case cmdNOP: + break; + + case cmdQUIT: + goto leave; + + case cmdHELP: + for (i=0; cmds[i].name; i++ ) + if(cmds[i].desc + && (!cmds[i].admin_only || (cmds[i].admin_only && allow_admin))) + tty_printf("%-14s %s\n", cmds[i].name, _(cmds[i].desc) ); + break; + + case cmdADMIN: + if ( !strcmp (arg_string, "on") ) + allow_admin = 1; + else if ( !strcmp (arg_string, "off") ) + allow_admin = 0; + else if ( !strcmp (arg_string, "verify") ) + { + /* Force verification of the Admin Command. However, + this is only done if the retry counter is at initial + state. */ + /* FIXME: Must depend on the type of the card. */ + /* char *tmp = xmalloc (strlen (serialnobuf) + 6 + 1); */ + /* strcpy (stpcpy (tmp, serialnobuf), "[CHV3]"); */ + /* allow_admin = !agent_scd_checkpin (tmp); */ + /* xfree (tmp); */ + } + else /* Toggle. */ + allow_admin=!allow_admin; + if(allow_admin) + tty_printf(_("Admin commands are allowed\n")); + else + tty_printf(_("Admin commands are not allowed\n")); + break; + + case cmdVERIFY: cmd_verify (); redisplay = 1; break; + case cmdLIST: redisplay = 1; break; + case cmdNAME: cmd_name (); break; + case cmdURL: cmd_url (); break; + case cmdFETCH: cmd_fetch (); break; + case cmdLOGIN: cmd_login (arg_string); break; + case cmdLANG: cmd_lang (); break; + case cmdSALUT: cmd_salut (); break; + case cmdCAFPR: cmd_cafpr (arg_number); break; + case cmdPRIVATEDO: cmd_privatedo (arg_number, arg_string); break; + case cmdWRITECERT: cmd_writecert (arg_number, arg_rest); break; + case cmdREADCERT: cmd_readcert (arg_number, arg_rest); break; + case cmdFORCESIG: cmd_forcesig (); break; + case cmdGENERATE: cmd_generate (); break; + case cmdPASSWD: cmd_passwd (allow_admin); break; + case cmdUNBLOCK: cmd_unblock (allow_admin); break; + case cmdFACTORYRESET: cmd_factoryreset (); break; + case cmdKDFSETUP: cmd_kdfsetup (arg_string); break; + case cmdKEYATTR: cmd_keyattr (); break; + case cmdUIF: cmd_uif (arg_number, arg_rest); break; + + case cmdINVCMD: + default: + tty_printf ("\n"); + tty_printf (_("Invalid command (try \"help\")\n")); + break; + } /* End command switch. */ + } /* End of main menu loop. */ + + leave: + xfree (answer); +} + +#ifdef HAVE_LIBREADLINE +/* Helper function for readline's command completion. */ +static char * +command_generator (const char *text, int state) +{ + static int list_index, len; + const char *name; + + /* If this is a new word to complete, initialize now. This includes + * saving the length of TEXT for efficiency, and initializing the + index variable to 0. */ + if (!state) + { + list_index = 0; + len = strlen(text); + } + + /* Return the next partial match */ + while ((name = cmds[list_index].name)) + { + /* Only complete commands that have help text. */ + if (cmds[list_index++].desc && !strncmp (name, text, len)) + return strdup(name); + } + + return NULL; +} + +/* Second helper function for readline's command completion. */ +static char ** +command_completion (const char *text, int start, int end) +{ + (void)end; + + /* If we are at the start of a line, we try and command-complete. + * If not, just do nothing for now. */ + if (!start) + return rl_completion_matches (text, command_generator); + + rl_attempted_completion_over = 1; + + return NULL; +} +#endif /*HAVE_LIBREADLINE*/ diff --git a/tools/gpg-connect-agent-w32info.rc b/tools/gpg-connect-agent-w32info.rc index 4e7b19d35..8c6735918 100644 --- a/tools/gpg-connect-agent-w32info.rc +++ b/tools/gpg-connect-agent-w32info.rc @@ -1,4 +1,4 @@ -/* scdaemon-w32info.rc -*- c -*- +/* gpg-connect-agent-w32info.rc -*- c -*- * Copyright (C) 2013 g10 Code GmbH * * This file is free software; as a special exception the author gives