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 <wk@gnupg.org>
This commit is contained in:
Werner Koch 2019-01-22 09:07:24 +01:00
parent 03cf23b43e
commit e6d613711a
No known key found for this signature in database
GPG Key ID: E3FDFF218E45B72B
4 changed files with 936 additions and 6 deletions

View File

@ -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

View File

@ -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

869
tools/gpg-card-tool.c Normal file
View File

@ -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 <https://gnu.org/licenses/>.
* SPDX-License-Identifier: GPL-3.0+
*/
#include <config.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifdef HAVE_LIBREADLINE
# define GNUPG_LIBREADLINE_H_INCLUDED
# include <readline/readline.h>
#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*/

View File

@ -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