diff --git a/tools/Makefile.am b/tools/Makefile.am index e4fd81c5d..4833bff5e 100644 --- a/tools/Makefile.am +++ b/tools/Makefile.am @@ -1,182 +1,192 @@ # Makefile.am - Tools directory # Copyright (C) 2003, 2007 Free Software Foundation, Inc. # # This file is part of GnuPG. # # GnuPG is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 3 of the License, or # (at your option) any later version. # # GnuPG is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, see . 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) sbin_SCRIPTS = addgnupghome applygnupgdefaults if BUILD_SYMCRYPTRUN symcryptrun = symcryptrun else symcryptrun = endif if BUILD_WKS_TOOLS gpg_wks_server = gpg-wks-server else gpg_wks_server = 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 if !DISABLE_REGEX libexec_PROGRAMS += gpg-check-pattern endif if !HAVE_W32CE_SYSTEM noinst_PROGRAMS = clean-sat make-dns-cert gpgsplit endif if !HAVE_W32CE_SYSTEM if BUILD_GPGTAR bin_PROGRAMS += gpgtar else noinst_PROGRAMS += gpgtar endif endif common_libs = $(libcommon) commonpth_libs = $(libcommonpth) # Some modules require PTH under W32CE. if HAVE_W32CE_SYSTEM maybe_commonpth_libs = $(commonpth_libs) else maybe_commonpth_libs = $(common_libs) endif if HAVE_W32CE_SYSTEM pwquery_libs = else pwquery_libs = ../common/libsimple-pwquery.a endif if HAVE_W32CE_SYSTEM opt_libassuan_libs = $(LIBASSUAN_LIBS) endif gpgsplit_LDADD = $(common_libs) \ $(LIBGCRYPT_LIBS) $(GPG_ERROR_LIBS) \ $(ZLIBS) $(LIBINTL) $(NETLIBS) $(LIBICONV) gpgconf_SOURCES = gpgconf.c gpgconf.h gpgconf-comp.c # common sucks in gpg-error, will they, nil they (some compilers # do not eliminate the supposed-to-be-unused-inline-functions). gpgconf_LDADD = $(maybe_commonpth_libs) $(opt_libassuan_libs) \ $(LIBINTL) $(LIBGCRYPT_LIBS) $(GPG_ERROR_LIBS) $(NETLIBS) \ $(LIBICONV) $(W32SOCKLIBS) gpgconf_LDFLAGS = $(extra_bin_ldflags) gpgparsemail_SOURCES = gpgparsemail.c rfc822parse.c rfc822parse.h gpgparsemail_LDADD = symcryptrun_SOURCES = symcryptrun.c symcryptrun_LDADD = $(LIBUTIL_LIBS) $(common_libs) $(pwquery_libs) \ $(LIBGCRYPT_LIBS) $(GPG_ERROR_LIBS) $(LIBINTL) \ $(LIBICONV) $(NETLIBS) $(W32SOCKLIBS) $(LIBASSUAN_LIBS) watchgnupg_SOURCES = watchgnupg.c watchgnupg_LDADD = $(NETLIBS) gpg_connect_agent_SOURCES = gpg-connect-agent.c 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 gpg_check_pattern_SOURCES = gpg-check-pattern.c gpg_check_pattern_CFLAGS = $(LIBGCRYPT_CFLAGS) $(GPG_ERROR_CFLAGS) $(INCICONV) gpg_check_pattern_LDADD = $(common_libs) $(LIBGCRYPT_LIBS) $(GPG_ERROR_LIBS) \ $(LIBINTL) $(NETLIBS) $(LIBICONV) $(W32SOCKLIBS) \ $(LIBICONV) endif gpgtar_SOURCES = \ gpgtar.c gpgtar.h \ gpgtar-create.c \ gpgtar-extract.c \ gpgtar-list.c gpgtar_CFLAGS = $(GPG_ERROR_CFLAGS) gpgtar_LDADD = $(libcommon) $(LIBGCRYPT_LIBS) $(GPG_ERROR_LIBS) \ $(LIBINTL) $(NETLIBS) $(LIBICONV) $(W32SOCKLIBS) gpg_wks_server_SOURCES = \ gpg-wks-server.c \ gpg-wks.h \ wks-util.c \ wks-receive.c \ rfc822parse.c rfc822parse.h \ mime-parser.c mime-parser.h \ mime-maker.c mime-maker.h \ send-mail.c send-mail.h gpg_wks_server_CFLAGS = $(GPG_ERROR_CFLAGS) $(INCICONV) gpg_wks_server_LDADD = $(libcommon) $(LIBGCRYPT_LIBS) $(GPG_ERROR_LIBS) \ $(LIBINTL) $(LIBICONV) gpg_wks_client_SOURCES = \ gpg-wks-client.c \ gpg-wks.h \ wks-util.c \ wks-receive.c \ rfc822parse.c rfc822parse.h \ mime-parser.c mime-parser.h \ mime-maker.h mime-maker.c \ send-mail.c send-mail.h \ call-dirmngr.c call-dirmngr.h gpg_wks_client_CFLAGS = $(LIBASSUAN_CFLAGS) $(GPG_ERROR_CFLAGS) $(INCICONV) gpg_wks_client_LDADD = $(libcommon) \ $(LIBASSUAN_LIBS) $(LIBGCRYPT_LIBS) $(GPG_ERROR_LIBS) \ $(LIBINTL) $(LIBICONV) gpg_pair_tool_SOURCES = \ gpg-pair-tool.c gpg_pair_tool_CFLAGS = $(GPG_ERROR_CFLAGS) $(INCICONV) gpg_pair_tool_LDADD = $(libcommon) \ $(LIBGCRYPT_LIBS) $(GPG_ERROR_LIBS) \ $(LIBINTL) $(LIBICONV) $(W32SOCKLIBS) # Make sure that all libs are build before we use them. This is # important for things like make -j2. $(PROGRAMS): $(common_libs) $(pwquery_libs) ../common/libgpgrl.a diff --git a/tools/gpg-connect-agent-w32info.rc b/tools/gpg-card-tool-w32info.rc similarity index 83% copy from tools/gpg-connect-agent-w32info.rc copy to tools/gpg-card-tool-w32info.rc index 4e7b19d35..6937c3e34 100644 --- a/tools/gpg-connect-agent-w32info.rc +++ b/tools/gpg-card-tool-w32info.rc @@ -1,51 +1,51 @@ -/* scdaemon-w32info.rc -*- c -*- - * Copyright (C) 2013 g10 Code GmbH +/* 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 command line access \ + VALUE "FileDescription", L"GnuPG\x2019s card tool \ to the agent\0" - VALUE "InternalName", "gpg-connect-agent\0" - VALUE "OriginalFilename", "gpg-connect-agent.exe\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,51 +1,51 @@ -/* 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 * 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 command line access \ to the agent\0" VALUE "InternalName", "gpg-connect-agent\0" VALUE "OriginalFilename", "gpg-connect-agent.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