gnupg/tools/gpg-card-tool.c

870 lines
25 KiB
C
Raw Normal View History

/* 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*/