diff options
Diffstat (limited to 'tools')
33 files changed, 8232 insertions, 2638 deletions
diff --git a/tools/ChangeLog b/tools/ChangeLog index 00455c619..25ffae5d2 100644 --- a/tools/ChangeLog +++ b/tools/ChangeLog @@ -1,219 +1,479 @@ -2006-04-20 David Shaw <[email protected]> +2006-06-09 Marcus Brinkmann <[email protected]> - * make-dns-cert.c (main): Small exit code tweak from Peter - Palfrader. + * Makefile.am (gpgconf_LDADD): Add $(GPG_ERROR_LIBS). + (gpgkey2ssh_LDADD): Add ../jnlib/libjnlib.a. -2006-04-05 David Shaw <[email protected]> +2006-05-23 Werner Koch <[email protected]> - * make-dns-cert.c: Some changes from Peter Palfrader to send - errors to stderr and allow spaces in a fingerprint. Also warn - when a key is over 16k (as that is the default max-cert-size) and - fail when a key is over 64k as that is the DNS limit in many - places. + * gpgparsemail.c: Include config.h if available + (stpcpy): Conditional include it. -2006-04-04 David Shaw <[email protected]> + * gpgconf-comp.c (hextobyte): Removed as it is now availble in + jnlib. - * make-dns-cert.c: New program to generate properly formatted CERT - records so people don't have to do it manually. +2005-12-20 Werner Koch <[email protected]> -2006-02-14 Werner Koch <[email protected]> + * gpgconf-comp.c (gc_options_gpg): Add allow-pka-lookup. - * mk-tdata.c (main): Implement option --char. +2005-12-14 Werner Koch <[email protected]> -2005-08-05 David Shaw <[email protected]> + * Makefile.am (bin_PROGRAMS): Build gpgparsemail. - * gpg-zip.in: Add --decrypt functionality. Fix quoting so - filenames with spaces work properly. + * gpgparsemail.c (pkcs7_begin): New. + (parse_message, message_cb): Add support of direct pkcs signatures. -2005-08-04 David Shaw <[email protected]> +2005-10-19 Werner Koch <[email protected]> - * gpg-zip.in: New. Script wrapper to work with encrypted tar - files, a la PGP Zip. + * gpgconf-comp.c (gc_options_scdaemon): New option --disable-keypad. - * Makefile.am: Use it if we have a USTAR compatible tar. +2005-09-22 Werner Koch <[email protected]> -2004-12-18 David Shaw <[email protected]> + * rfc822parse.c (parse_field): Tread Content-Disposition special. - * Makefile.am: Link with readline where needed. +2005-10-08 Marcus Brinkmann <[email protected]> -2004-10-28 Werner Koch <[email protected]> + * Makefile.am (watchgnupg_LDADD): New variable. - * Makefile.am (other_libs): New. Also include LIBICONV. Noted by - Tim Mooney. + * Makefile.am (gpgconf_LDADD): Add ../gl/libgnu.a after + ../common/libcommon.a. + (symcryptrun_LDADD, gpg_connect_agent_LDADD, gpgkey2ssh_LDADD): + Likewise. + +2005-09-29 Marcus Brinkmann <[email protected]> + + * Makefile.am (AM_CFLAGS): Add $(LIBGCRYPT_CFLAGS). + +2005-09-06 Werner Koch <[email protected]> + + * rfc822parse.c, rfc822parse.h: Changed license to LGPL. + +2005-08-01 Werner Koch <[email protected]> + + * gpgsm-gencert.sh: Allow entering a keygrip to generate a CSR from + an existing key. + +2005-07-21 Werner Koch <[email protected]> + + * gpgsm-gencert.sh: Reworked to allow for multiple email addresses + as well as DNsanmes and URi. Present the parameter file before + creating the certificate. + +2005-07-04 Marcus Brinkmann <[email protected]> + + * symcryptrun.c (SYMC_BAD_PASSPHRASE, SYMC_CANCELED): New symbols, + use instead constants. + (hash_string): New function copied from simple-gettext.c. + (confucius_get_pass): Take new argument CACHEID. + (confucius_process): Calculate cacheid and pass it to + confucius_get_pass. Clear passphrase from cache if necessary. + +2005-06-16 Werner Koch <[email protected]> + + * gpg-connect-agent.c (read_and_print_response): Made LINELEN a + size_t. + +2005-06-04 Marcus Brinkmann <[email protected]> + + * symcryptrun.c (main): Allow any number of arguments, don't use + first argument as input file name. Pass extra arguments to + confucius_main. + (confucius_main): Accept new arguments argc and argv and pass them + to confucius_process. + (confucius_process): Accept new arguments argc and argv and pass + them to the confucius process. + +2005-06-01 Werner Koch <[email protected]> + + * symcryptrun.c: Include mkdtemp.h. + +2005-05-31 Werner Koch <[email protected]> + + * watchgnupg.c: Make sure that PF_LCOAL and AF_LOCAL are defines. + Noted by Ray Link. + +2005-05-28 Moritz Schulte <[email protected]> + + * gpgkey2ssh.c: New file. + * Makefile.am (bin_PROGRAMS): Added gpgkey2ssh. + +2005-05-20 Werner Koch <[email protected]> + + * gpg-connect-agent.c (add_definq, show_definq, clear_definq) + (handle_inquire): New. + (read_and_print_response): Handle INQUIRE command. + (main): Implement control commands. + +2005-04-21 Werner Koch <[email protected]> + + * symcryptrun.c (main): Optionally allow the input file as command + line argument. + + * gpgconf-comp.c: Add gpgsm option disable-trusted-cert-crl-check. + +2005-04-20 Werner Koch <[email protected]> + + * gpgconf-comp.c: Add gpg-agent:disable-scdaemon. + +2005-04-19 Marcus Brinkmann <[email protected]> + + * symcryptrun.c: Add --input option. + +2005-04-15 Marcus Brinkmann <[email protected]> + + * symcryptrun.c (TEMP_FAILURE_RETRY): Define if not defined. + + * symcryptrun.c (remove_file): New function. + (confucius_copy_file): Accept new argument PLAIN and shred the + file if it is set on error. + + * Makefile.am: Define symcryptrun make variable depending on + BUILD_SYMCRYPTUN. + (bin_PROGRAMS): Add ${symcryptrun} instead symcryptrun. + (symcryptrun_LDADD): Use $(LIBUTIL_LIBS) instead of -lutil. + +2005-04-11 Werner Koch <[email protected]> + + * symcryptrun.c (confucius_mktmpdir): Changed to use mkdtmp(3). + +2005-04-11 Marcus Brinkmann <[email protected]> + + * symcryptrun.c: Implement config file parsing. + + * Makefile.am (bin_PROGRAMS): Add symcryptrun. + (symcryptrun_SOURCES, symcryptrun_LDADD): New variables. + * symcryptrun.c: New file. + +2005-03-31 Werner Koch <[email protected]> + + * gpg-connect-agent.c (start_agent): Use PATHSEP_C instead of ':'. + +2005-03-09 Werner Koch <[email protected]> + + * gpgconf-comp.c <dirmngr>: Add honor-http-proxy. + +2005-02-25 Werner Koch <[email protected]> + + * no-libgcrypt.c (gcry_strdup): New. + +2005-02-24 Werner Koch <[email protected]> + + * gpg-connect-agent.c: New. + * Makefile.am: Add it. + +2004-12-21 Werner Koch <[email protected]> + + * gpgconf-comp.c (get_config_pathname) [DOSISH]: Detect absolute + pathnames with a drive letter. + +2004-12-15 Werner Koch <[email protected]> + + * Makefile.am (bin_PROGRAMS) [W32]: Do not build watchgnupg. + + * gpgconf-comp.c (gpg_agent_runtime_change) [W32]: No way yet to + send a signal. Disable. + (change_options_file, change_options_program) [W32]: No link(2), + so we disable it. + (gc_component_change_options): Use rename instead of link. + +2004-12-13 Werner Koch <[email protected]> + + * gpgconf-comp.c <ignore-ocsp-service-url>: Fixed typo. + +2004-11-24 Werner Koch <[email protected]> + + * gpgconf-comp.c <dirmngr>: Add --ignore-http-dp, --ignore-ldap-dp + and --ignore-ocsp-service-url. + +2004-11-23 Werner Koch <[email protected]> + + * gpgconf-comp.c <dirmngr>: Add the proxy options. + <gpgsm>: Add --prefer-system-daemon. + +2004-11-11 Werner Koch <[email protected]> + + * watchgnupg.c (main): Fixed test for read error. + +2004-10-22 Werner Koch <[email protected]> + + * Makefile.am (bin_SCRIPTS): Add gpgsm-gencert.sh + + * gpgsm-gencert.sh: Fixed copyright; its part of GnuPG thus FSF. 2004-10-01 Werner Koch <[email protected]> - * bftest.c (i18n_init): Always use LC_ALL. - * shmtest.c (i18n_init): Ditto. - * mpicalc.c (i18n_init): Ditto. + * gpgconf-comp.c: Made all strings for --log-file read the same. + +2004-10-01 Werner Koch <[email protected]> + + * gpgconf-comp.c (my_dgettext): Also switch codeset and directory + for the other used domains (i.e. dirmngr). + + * gpgconf.c (main): Fixed translation markers. + +2004-09-30 Werner Koch <[email protected]> + + * gpgconf.c (i18n_init): Always use LC_ALL. + + * Makefile.am: Adjusted for gettext 0.14. + +2004-09-29 Werner Koch <[email protected]> + + * gpgconf-comp.c: Made the entries fro GROUPs translatable. + Include i18n.h. + (my_dgettext): Hack to use the gnupg2 domain. -2004-09-13 David Shaw <[email protected]> +2004-08-09 Moritz Schulte <[email protected]> - * pgpgroup-to-gpggroup: New perl script to take groups from PGP - command line and write out GnuPG 'group' config lines. + * gpgsm-gencert.sh: New file. -2004-07-04 David Shaw <[email protected]> +2004-06-16 Werner Koch <[email protected]> - * ring-a-party: ElGamal -> Elgamal + * rfc822parse.c (rfc822parse_get_field): Add arg VALUEOFF. -2004-01-11 David Shaw <[email protected]> +2004-06-14 Werner Koch <[email protected]> - * convert-from-106, lspgpot: Check for gpg binary before - proceeding. Don't hardcode the path to gpg. + * no-libgcrypt.c (gcry_realloc, gcry_xmalloc, gcry_xcalloc): New. - * gpgsplit.c (handle_bzip2): Remove two cut and paste typecast - errors. Noted by Stefan Bellon. + * gpgconf-comp.c (retrieve_options_from_program) + (retrieve_options_from_file, change_options_file) + (change_options_program, gc_component_change_options): Replaced + getline by read_line and test for allocation failure. -2003-12-28 Stefan Bellon <[email protected]> +2004-05-21 Marcus Brinkmann <[email protected]> - * gpgsplit.c [__riscos__]: Removal of unnecessary #ifdef - __riscos__ sections. + * gpgconf-comp.c (gc_options_dirmngr): Remove CRL group, put its + only option "max-replies" into LDAP group. + (gc_component): Change description of dirmngr to "Directory + Manager". -2003-12-06 David Shaw <[email protected]> + * gpgconf-comp.c (gc_component_change_options): Move the + per-process backup file into a standard location. - * gpgsplit.c (write_part): Split off decompression code. - (handle_zlib): Move it here. - (handle_bzip2): Add this to handle BZIP2 compressed messages. +2004-05-03 Werner Koch <[email protected]> -2003-10-25 Werner Koch <[email protected]> + * gpgconf-comp.c: Add --allow-mark-trusted for the gpg-agent. - * Makefile.am: Replaced INTLLIBS by LIBINTL. +2004-04-30 Werner Koch <[email protected]> -2003-08-24 David Shaw <[email protected]> + * gpgconf-comp.c: Added more runtime flags for the gpg-agent + backend. - * Makefile.am: Use NETLIBS instead of EGDLIBS. +2004-04-29 Marcus Brinkmann <[email protected]> -2003-07-10 David Shaw <[email protected]> + * gpgconf-comp.c (change_options_program): Turn on utf8-strings in + the gpgconf specific part of the config file for the GnuPG + backend. - * Makefile.am: Use W32LIBS where appropriate. +2004-04-28 Werner Koch <[email protected]> -2003-05-30 David Shaw <[email protected]> + * gpgconf-comp.c: Add --ocsp-signer for the dirmngr backend. - * Makefile.am: Some cleanup, and use DLLIBS for -ldl. +2004-04-20 Marcus Brinkmann <[email protected]> -2003-05-24 David Shaw <[email protected]> + * gpgconf-comp.c (gc_options_gpg_agent): Change type of + ignore-cache-for-signing option to GC_ARG_TYPE_NONE. - * bftest.c, crlf.c, mk-tdata.c, mpicalc.c, shmtest.c: Edit all - preprocessor instructions to remove whitespace before the - '#'. This is not required by C89, but there are some compilers out - there that don't like it. +2004-04-07 Werner Koch <[email protected]> -2003-03-11 David Shaw <[email protected]> + * gpgconf-comp.c (my_dgettext): Switch the codeset once to utf-8. + Allow building with out NLS. - * Makefile.am: Use @CAPLIBS@ to link in -lcap if we are using - capabilities. +2004-03-23 Marcus Brinkmann <[email protected]> -2003-02-22 David Shaw <[email protected]> + * gpgconf-comp.c (gc_options_dirmngr): Set GC_OPT_FLAG_ARG_OPT for + "LDAP Server". + (change_options_file): Remove assertion that tests that this flag + is not present. Handle an empty string in OPTION->new_value. - * Makefile.am: Distribute convert-from-106. + * gpgconf.c (main): Remove obsolete warning. - * convert-from-106: Script to automate the 1.0.6->later - conversion. It marks all secret keys as ultimately trusted, adds - the signature caches, and checks the trustdb. Moved from the - scripts directory. +2004-03-23 Werner Koch <[email protected]> -2002-10-31 Stefan Bellon <[email protected]> + * gpgconf-comp.c (gc_options_gpg): New. + (gc_component_t, gc_component): Add GC_BACKEND_GPG. + (gc_options_dirmngr): Add allow-ocsp. - * gpgsplit.c (write_part) [__riscos__]: Use riscos_load_module() - to load ZLib module. +2004-03-23 Marcus Brinkmann <[email protected]> -2002-10-23 Werner Koch <[email protected]> + * gpgconf-comp.c (gc_flag): Add missing flags. - * gpgsplit.c: New options --secret-to-public and --no-split. - GNUified the indentation style. + * gpgconf-comp.c: Include <signal.h>. + (gc_backend): Add new member runtime_change. + (gpg_agent_runtime_change): New function. + (gc_component_change_options): New variable runtime. Initialize + it. If an option is changed that has the GC_OPT_FLAG_RUNTIME bit + set, also set the corresponding runtime variable. Finally, call + the runtime_change callback of the backend if needed. -2002-09-25 David Shaw <[email protected]> +2004-03-16 Werner Koch <[email protected]> - * Makefile.am: Link bftest with EGDLIBS (i.e. NETLIBS) as EGD uses - sockets. + * gpgconf-comp.c (gc_options_gpg_agent): Implemented. + (gc_options_gpgsm, gc_options_scdaemon): Implemented. + (gc_backend_t): Add GC_BACKEND_SCDAEMON. -2002-05-07 Stefan Bellon <[email protected]> +2004-03-12 Marcus Brinkmann <[email protected]> - * gpgsplit.c (create_filename): Use EXTSEP_S instead of ".". + * gpgconf-comp.c (gc_component_change_options): Set the filenames + of the option's backend, not of the component. + Also use GC_BACKEND_NR, not GC_COMPONENT_NR. -2002-04-23 David Shaw <[email protected]> +2004-03-09 Werner Koch <[email protected]> - * Makefile.am: Do not list libraries in -lxxx format in a - dependency line. + * gpgconf-comp.c [_riscos_]: Removed special code for RISC OS; we + don't want to clutter our code with system dependent stuff. -2002-01-02 Stefan Bellon <[email protected]> +2004-03-08 Marcus Brinkmann <[email protected]> - * gpgsplit.c [__riscos__]: Added RISC OS specific file name - code. + * gpgconf-comp.c (retrieve_options_from_file): Quote each string + in the list, not only the first. - * gpgsplit.c (write_part): Introduced two explicit casts. +2004-02-26 Marcus Brinkmann <[email protected]> -2001-12-21 David Shaw <[email protected]> + * gpgconf-comp.c (gc_component_list_options): Do not print empty + groups. - * gpgsplit.c (pkttype_to_string): PKT_PHOTO_ID -> PKT_ATTRIBUTE + * gpgconf-comp.c (option_check_validity): Check if option is + active. + (change_options_file): Implement. -2001-10-23 Werner Koch <[email protected]> + * gpgconf-comp.c (retrieve_options_from_program): Remove broken + string handling. - * Makefile.am (gpgsplit_LDADD): Add ZLIBS. + * gpgconf-comp.c (change_options_program): Support all types of + options, including list types. -2001-09-18 Werner Koch <[email protected]> + * README.gpgconf: Fix description of arguments. + * gpgconf-comp.c (option_check_validity): Rewritten to properly + support optional arguments in lists. - * gpgsplit.c: New option --uncompress. - (write_part): Handle old style uncompressing. + * README.gpgconf: Add info about optional arg and arg type 0. + * gpgconf-comp.c (gc_component_change_options): Parse list of + arg type 0 options. + (option_check_validity): Add new argument NEW_VALUE_NR. Perform + rigorous validity checks. + (change_options_program): Disable an option also if we have a new + value for it. -2001-06-20 Werner Koch <[email protected]> +2004-02-25 Marcus Brinkmann <[email protected]> - * gpgsplit.c: New. - * Makefile.am (bin_PROGRAMS): Install gpgsplit. + * gpgconf-comp.c (gc_component_list_options): Correct output for + lists of arg type none. + (struct gc_option): Add new member new_flags. + (option_check_validity): Check OPTION->new_flags beside + OPTION->new_value. Add new argument FLAGS. + (gc_component_change_options): Support default flag correctly. + (change_options_program): Likewise. -2001-03-27 Werner Koch <[email protected]> +2004-02-24 Marcus Brinkmann <[email protected]> - * mail-signed-keys: Add option --dry-run. + * README.gpgconf: Revert last change. Add new flags "default", + "default desc" and "no arg desc". Add new field ARGDEF. Add new + field FLAG to backend interface. + * gpgconf-comp.c (struct gc_option): Make flags of type unsigned + long. + (gc_component_list_options): Adjust type for flags. + Add default argument field. + (retrieve_options_from_program): Use "1" as value for non-option + arguments, not "Y". + (gc_component_change_options): Read in flags from input. -2001-03-21 Werner Koch <[email protected]> +2004-02-23 Marcus Brinkmann <[email protected]> - * shmtest.c: Add sys/types.h + * README.gpgconf: Change meaning of type 0 options value if it is + the empty string or "0". -Fri Sep 15 18:40:36 CEST 2000 Werner Koch <[email protected]> + * gpgconf.h (struct): Add member runtime. + * gpgconf.c: Add new option oRuntime. + (main): Same here. - * ring-a-party: An array start at offset 1 no 0. Many thanks to Mike - for finding this bug. + * gpgconf-comp.c (hextobyte): New function. + (percent_deescape): New function. + (get_config_pathname): Percent deescape pathname if taken from + option (default) value. Use default value only if it exists and + is not empty. Use empty string otherwise. Don't include leading + quote in pathname. + (change_options_program): Percent deescape string before writing + it out. + + * gpgconf-comp.c (gc_component_list_options): Do not skip groups + on output. -Thu Sep 14 14:20:38 CEST 2000 Werner Koch <[email protected]> +2004-02-18 Werner Koch <[email protected]> - * ring-a-party: Flush the last key. + * gpgconf-comp.c: Added empty components for gpgsm and scdaemon. -Wed Jul 5 13:28:45 CEST 2000 Werner Koch <wk@> +2004-02-12 Werner Koch <[email protected]> - * mail-signed-keys: New. + * watchgnupg.c (main): Implement option "--". + (print_version): New. -Tue May 23 09:19:00 CEST 2000 Werner Koch <[email protected]> + * Makefile.am: Include cmacros.am for common flags. - * ring-a-party: New. +2004-02-03 Werner Koch <[email protected]> -Thu Jul 8 16:21:27 CEST 1999 Werner Koch <[email protected]> + * addgnupghome: Try to use getent, so that it also works for NIS + setups. - * lspgpot: New +2004-01-31 Marcus Brinkmann <[email protected]> -Wed Jul 7 13:08:40 CEST 1999 Werner Koch <[email protected]> + * gpgconf-comp.c: Some bug fixes, parse only defaults from the + program, and read the current values from the configuration file + directly. - * Makefile.am: Support for libtool. +2004-01-30 Marcus Brinkmann <[email protected]> -Tue Jun 8 13:36:25 CEST 1999 Werner Koch <[email protected]> + * gpgconf-comp.c (gc_error): New function, use it instead of + error() throughout. - * mpicalc.c (main): hex digits may now be lowercase - (do_mulm): new. + * gpgconf-comp.c: Use xmalloc, libcommon's asctimestamp and + gnupg_get_time, fix error() invocation and use getline() + consistently. + +2004-01-30 Werner Koch <[email protected]> -Thu Dec 10 20:15:36 CET 1998 Werner Koch <[email protected]> + * addgnupghome: Also set the group of copied files. - * mpicalc.c (main): Moved initialization out of definition. +2004-01-30 Werner Koch <[email protected]> -Mon May 18 15:39:22 1998 Werner Koch ([email protected]) + * Makefile.am (sbin_SCRIPTS): New, to install addgnupghome. + (EXTRA_DIST): Added rfc822parse.c rfc822parse.h gpgparsemail.c + which might be useful for debugging. - * mk-tdata.c: New. +2004-01-29 Werner Koch <[email protected]> -Tue Apr 7 19:50:41 1998 Werner Koch ([email protected]) + * addgnupghome: New. - * bftest.c: Now supports all availabe ciphers. +2004-01-29 Marcus Brinkmann <[email protected]> + * gpgconf-list.c: File removed. + * README.gpgconf: New file. + * gpgconf-comp.c: New file. + * Makefile.am (gpgconf_SOURCES): Remove gpgconf-list.c, add + gpgconf-comp.c. +2004-01-16 Werner Koch <[email protected]> - Copyright 1998, 1999, 2000, 2001 Free Software Foundation, Inc. + * watchgnupg.c (main): Need to use FD_ISSET for the client + descriptors too; aiiih. Set the listening socket to non-blocking. + +2004-01-10 Werner Koch <[email protected]> + + * Makefile.am: Use GPG_ERROR_CFLAGS + +2004-01-05 Werner Koch <[email protected]> + + * Manifest: New. + * gpgconf.c, gpgconf.h, gpgconf-list.c: New. A skeleton for now. + * no-libgcrypt.c: New. + * Makefile.am: Add above. + +2003-12-23 Werner Koch <[email protected]> + + * Makefile.am: New. + * watchgnupg.c: New. + + + Copyright 2003, 2004, 2005 Free Software Foundation, Inc. This file is free software; as a special exception the author gives unlimited permission to copy and/or distribute it, with or without @@ -222,5 +482,3 @@ Tue Apr 7 19:50:41 1998 Werner Koch ([email protected]) This file 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. - - diff --git a/tools/Makefile.am b/tools/Makefile.am index 5efe2c1a6..6b4767a79 100644 --- a/tools/Makefile.am +++ b/tools/Makefile.am @@ -1,8 +1,8 @@ -# Copyright (C) 1998, 1999, 2000, 2001, 2003, -# 2004 Free Software Foundation, Inc. -# +# Makefile.am - Tools directory +# Copyright (C) 2003 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 2 of the License, or @@ -15,25 +15,59 @@ # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, +# USA. + +EXTRA_DIST = Manifest watchgnupg.c \ + addgnupghome gpgsm-gencert.sh -## Process this file with automake to produce Makefile.in +AM_CPPFLAGS = -I$(top_srcdir)/gl -I$(top_srcdir)/intl -I$(top_srcdir)/common +include $(top_srcdir)/am/cmacros.am -EXTRA_DIST = lspgpot ring-a-party mail-signed-keys convert-from-106 -INCLUDES = -I$(top_srcdir)/include -I$(top_srcdir)/intl -needed_libs = ../cipher/libcipher.a ../mpi/libmpi.a ../util/libutil.a -other_libs = $(LIBICONV) $(LIBINTL) $(CAPLIBS) +AM_CFLAGS = $(LIBGCRYPT_CFLAGS) $(GPG_ERROR_CFLAGS) $(LIBASSUAN_CFLAGS) -bin_PROGRAMS = gpgsplit -noinst_PROGRAMS = mpicalc bftest clean-sat mk-tdata shmtest make-dns-cert +sbin_SCRIPTS = addgnupghome + +bin_SCRIPTS = gpgsm-gencert.sh + +if BUILD_SYMCRYPTRUN + symcryptrun = symcryptrun +else + symcryptrun = +endif -if HAVE_USTAR -bin_SCRIPTS = gpg-zip +bin_PROGRAMS = gpgconf gpg-connect-agent gpgkey2ssh ${symcryptrun} gpgparsemail +if !HAVE_W32_SYSTEM +bin_PROGRAMS += watchgnupg endif -gpgsplit_LDADD = $(needed_libs) $(other_libs) @ZLIBS@ -mpicalc_LDADD = $(needed_libs) $(other_libs) @W32LIBS@ -bftest_LDADD = $(needed_libs) $(other_libs) @W32LIBS@ @DLLIBS@ @NETLIBS@ @LIBREADLINE@ -shmtest_LDADD = $(needed_libs) $(other_libs) @LIBREADLINE@ +gpgconf_SOURCES = gpgconf.c gpgconf.h gpgconf-comp.c no-libgcrypt.c + +# jnlib/common sucks in gpg-error, will they, nil they (some compilers +# do not eliminate the supposed-to-be-unused-inline-functions). +gpgconf_LDADD = ../jnlib/libjnlib.a ../common/libcommon.a \ + ../gl/libgnu.a @LIBINTL@ $(GPG_ERROR_LIBS) + +gpgparsemail_SOURCES = gpgparsemail.c rfc822parse.c rfc822parse.h +gpgparsemail_LDADD = + +symcryptrun_SOURCES = symcryptrun.c +symcryptrun_LDADD = $(LIBUTIL_LIBS) ../jnlib/libjnlib.a \ + ../common/libcommon.a ../gl/libgnu.a \ + ../common/libsimple-pwquery.a $(LIBGCRYPT_LIBS) \ + $(GPG_ERROR_LIBS) $(LIBINTL) + +watchgnupg_SOURCES = watchgnupg.c +watchgnupg_LDADD = $(NETLIBS) + +gpg_connect_agent_SOURCES = gpg-connect-agent.c no-libgcrypt.c +gpg_connect_agent_LDADD = ../jnlib/libjnlib.a \ + ../common/libcommon.a ../gl/libgnu.a \ + $(LIBASSUAN_LIBS) $(GPG_ERROR_LIBS) $(LIBINTL) -gpgsplit mpicalc bftest shmtest: $(needed_libs) +gpgkey2ssh_SOURCES = gpgkey2ssh.c +gpgkey2ssh_CFLAGS = $(LIBGCRYPT_CFLAGS) $(GPG_ERROR_CFLAGS) +# common sucks in jnlib, via use of BUG() in an inline function, which +# some compilers do not eliminate. +gpgkey2ssh_LDADD = ../jnlib/libjnlib.a ../common/libcommon.a ../gl/libgnu.a \ + $(LIBGCRYPT_LIBS) $(GPG_ERROR_LIBS) diff --git a/tools/Manifest b/tools/Manifest new file mode 100644 index 000000000..96423352d --- /dev/null +++ b/tools/Manifest @@ -0,0 +1,6 @@ +Makefile.am +watchgnupg.c +gpgconf.c +gpgconf.h +gpgconf-list.c +$names$ diff --git a/tools/README.gpgconf b/tools/README.gpgconf new file mode 100644 index 000000000..084711441 --- /dev/null +++ b/tools/README.gpgconf @@ -0,0 +1,79 @@ +============ + GPG Conf +============ + +Main documentation for this tool can be found in doc/tools.texi. + +BACKENDS +======== + +Backends should support the following commands: + +Command --gpgconf-list +---------------------- + +List the location of the configuration file, and all default values of +all options. The location of the configuration file must be an +absolute pathname. + +The format of each line is: + +NAME:FLAGS:DEFAULT:ARGDEF + +NAME + +This field contains a name tag for the group or option. The name tag +is used to specify the group or option in all communication with +GPGConf. The name tag is to be used verbatim. It is not in any +escaped format. + +FLAGS + +The flags field contains an unsigned number. Its value is the +OR-wise combination of the following flag values: + + 16 default If this flag is set, a default value is available. + 32 default desc If this flag is set, a (runtime) default is available. + This and the "default" flag are mutually exclusive. + 64 no arg desc If this flag is set, and the "optional arg" flag + is set, then the option has a special meaning if no + argument is given. + +DEFAULT + +This field is defined only for options. Its format is that of an +option argument (see section Format Conventions for details). If the +default value is empty, then no default is known. Otherwise, the +value specifies the default value for this option. Note that this +field is also meaningful if the option itself does not take a real +argument. + +ARGDEF + +This field is defined only for options for which the "optional arg" +flag is set. If the "no arg desc" flag is not set, its format is that +of an option argument (see section Format Conventions for details). +If the default value is empty, then no default is known. Otherwise, +the value specifies the default value for this option. If the "no arg +desc" flag is set, the field is either empty or contains a description +of the effect of this option if no argument is given. Note that this +field is also meaningful if the option itself does not take a real +argument. + + +Example: +$ dirmngr --gpgconf-list +gpgconf-config-file:/mnt/marcus/.gnupg/dirmngr.conf +ldapservers-file:/mnt/marcus/.gnupg/dirmngr_ldapservers.conf +add-servers:0 +max-replies:10 + + +TODO +---- + +* Implement --dry-run and --quiet. + +* Extend the backend interface to include gettext domain and +description, if available, to avoid repeating this information in +gpgconf. diff --git a/tools/addgnupghome b/tools/addgnupghome new file mode 100755 index 000000000..fb032b674 --- /dev/null +++ b/tools/addgnupghome @@ -0,0 +1,122 @@ +#!/bin/sh +# Add a new .gnupg home directory for a list of users -*- sh -*- +# +# Copyright 2004 Free Software Foundation, Inc. +# +# 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 file 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. + +PGM=addgnupghome +any_error=0 + + +error () { + echo "$PGM: $*" >&2 + any_error=1 +} + +info () { + echo "$PGM: $*" >&2 +} + +# Do it for one user +one_user () { + user="$1" + home=$(${cat_passwd} | awk -F: -v n="$user" '$1 == n {print $6}') + if [ -z "$home" ]; then + if ${cat_passwd} | awk -F: -v n="$user" '$1 == n {exit 1}'; then + error "no such user \`$user'" + else + error "no home directory for user \`$user'" + fi + return + fi + if [ ! -d "$home" ]; then + error "home directory \`$home' of user \`$user' does not exist" + return + fi + if [ -d "$home/.gnupg" ]; then + info "skipping user \`$user': \`.gnupg' already exists" + return + fi + info "creating home directory \`$home/.gnupg' for \`$user'" + if ! mkdir "$home/.gnupg" ; then + error "error creating \`$home/.gnupg'" + return + fi + + if ! chown $user "$home/.gnupg" ; then + error "error changing ownership of \`$home/.gnupg'" + return + fi + + group=$(id -g "$user") + [ -z "$group" ] && group="0" + + if [ "$group" -gt 0 ]; then + if ! chgrp $group "$home/.gnupg" ; then + error "error changing group of \`$home/.gnupg'" + return + fi + fi + + if ! cd "$home/.gnupg" ; then + error "error cd-ing to \`$home/.gnupg'" + return + fi + for f in $filelist; do + if [ -d /etc/skel/.gnupg/$f ]; then + mkdir $f + else + cp /etc/skel/.gnupg/$f $f + fi + if ! chown $user $f ; then + error "error changing ownership of \`$f'" + return + fi + if [ "$group" -gt 0 ]; then + if ! chgrp $group "$f" ; then + error "error changing group of \`$f'" + return + fi + fi + done + +} + +if [ -z "$1" ]; then + echo "usage: $PGM userids" + exit 1 +fi + +# Check whether we can use getent +if getent --help </dev/null >/dev/null 2>&1 ; then + cat_passwd='getent passwd' +else + cat_passwd='cat /etc/passwd' + info "please note that only users from /etc/passwd are checked" +fi + +if [ ! -d /etc/skel/.gnupg ]; then + error "skeleton directory \`/etc/skel/.gnupg' does not exist" + exit 1 +fi +cd "/etc/skel/.gnupg" || (error "error cd-ing to \`/etc/skel/.gnupg'"; exit 1) +filelist=$(find . \( -type f -or -type d \) -not -name '*~' -not -name . -print) + + +if ! umask 0077 ; then + error "error setting umask" + exit 1 +fi + +for name in $*; do + one_user $name +done + +exit $any_error diff --git a/tools/bftest.c b/tools/bftest.c deleted file mode 100644 index 829c429a0..000000000 --- a/tools/bftest.c +++ /dev/null @@ -1,108 +0,0 @@ -/* bftest.c - Blowfish test program - * Copyright (C) 1998, 1999, 2000, 2001 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 2 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, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, - * USA. - */ - -#include <config.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#ifdef HAVE_DOSISH_SYSTEM -#include <io.h> -#include <fcntl.h> -#endif - -#include "util.h" -#include "cipher.h" -#include "i18n.h" - -static void -my_usage(void) -{ - fprintf(stderr, "usage: bftest [-e][-d] algo key\n"); - exit(1); -} - -const char * -strusage( int level ) -{ - return default_strusage(level); -} - -static void -i18n_init(void) -{ -#ifdef ENABLE_NLS - setlocale( LC_ALL, "" ); - bindtextdomain( PACKAGE, G10_LOCALEDIR ); - textdomain( PACKAGE ); -#endif -} - -int -main(int argc, char **argv) -{ - int encode=0; - CIPHER_HANDLE hd; - char buf[4096]; - int n, size=4096; - int algo; - -#ifdef HAVE_DOSISH_SYSTEM - setmode( fileno(stdin), O_BINARY ); - setmode( fileno(stdout), O_BINARY ); -#endif - - i18n_init(); - if( argc > 1 && !strcmp(argv[1], "-e") ) { - encode++; - argc--; argv++; - } - else if( argc > 1 && !strcmp(argv[1], "-E") ) { - encode++; - argc--; argv++; - size = 10; - } - else if( argc > 1 && !strcmp(argv[1], "-d") ) { - argc--; argv++; - } - else if( argc > 1 && !strcmp(argv[1], "-D") ) { - argc--; argv++; - size = 10; - } - if( argc != 3 ) - my_usage(); - argc--; argv++; - algo = string_to_cipher_algo( *argv ); - argc--; argv++; - - hd = cipher_open( algo, CIPHER_MODE_CFB, 0 ); - cipher_setkey( hd, *argv, strlen(*argv) ); - cipher_setiv( hd, NULL, 0 ); - while( (n = fread( buf, 1, size, stdin )) > 0 ) { - if( encode ) - cipher_encrypt( hd, buf, buf, n ); - else - cipher_decrypt( hd, buf, buf, n ); - if( fwrite( buf, 1, n, stdout) != n ) - log_fatal("write error\n"); - } - cipher_close(hd); - return 0; -} diff --git a/tools/clean-sat.c b/tools/clean-sat.c deleted file mode 100644 index 8b6bfd77a..000000000 --- a/tools/clean-sat.c +++ /dev/null @@ -1,34 +0,0 @@ -/* clean-sat.c - * Copyright (C) 1998, 1999, 2000, 2001 Free Software Foundation, Inc. - * - * 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 <stdio.h> - -int -main(int argc, char **argv) -{ - int c; - - if( argc > 1 ) { - fprintf(stderr, "no arguments, please\n"); - return 1; - } - - while( (c=getchar()) == '\n' ) - ; - while( c != EOF ) { - putchar(c); - c = getchar(); - } - - return 0; -} - diff --git a/tools/convert-from-106 b/tools/convert-from-106 deleted file mode 100755 index 634152b11..000000000 --- a/tools/convert-from-106 +++ /dev/null @@ -1,57 +0,0 @@ -#!/bin/sh -# Copyright (C) 2002, 2004 Free Software Foundation, Inc. -# -# This program 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 2 of the License, or -# (at your option) any later version. -# -# This program 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, write to the Free Software -# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - -if ! gpg --version > /dev/null 2>&1 ; then - echo "GnuPG not available!" - exit 1 -fi - -gpg="gpg --no-greeting --no-secmem-warning" - -echo "This script converts your public keyring and trustdb from GnuPG" -echo "1.0.6 or earlier to the 1.0.7 and later format." - -echo "If you have already done this, there is no harm (but no point)" -echo "in doing it again." - -echo -n "Continue? (y/N)" - -read answer - -if test "x$answer" != "xy" ; then - exit 0 -fi - -echo -echo "Marking your keys as ultimately trusted" -for key in `$gpg --with-colons --list-secret-keys | grep sec: | cut -d: -f5` -do - $gpg --trusted-key $key --with-colons --list-keys $key > /dev/null 2>&1 - echo -n "." -done -echo - -echo -echo "Adding signature caches" -$gpg --rebuild-keydb-caches - -echo -echo "Checking trustdb" -$gpg --check-trustdb - -echo -echo "Done!" diff --git a/tools/crlf.c b/tools/crlf.c deleted file mode 100644 index ecb6eecdb..000000000 --- a/tools/crlf.c +++ /dev/null @@ -1,52 +0,0 @@ -/* crlf.c - * Copyright (C) 1999, 2000, 2001 Free Software Foundation, Inc. - * - * 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 <stdio.h> - -int -main(int argc, char **argv) -{ - int c, lc; - int off=0; - - if( argc > 1 ) { - fprintf(stderr, "no arguments, please\n"); - return 1; - } - - lc = -1; - while( (c=getchar()) != EOF ) { -#if 0 - if( c == '\r' && lc == ' ' ) - fprintf(stderr,"SP,CR at %d\n", off ); - if( c == '\n' && lc == ' ' ) - fprintf(stderr,"SP,LF at %d\n", off ); -#endif - if( c == '\n' && lc == '\r' ) - putchar(c); - else if( c == '\n' ) { - putchar('\r'); - putchar(c); - } - else if( c != '\n' && lc == '\r' ) { - putchar('\n'); - putchar(c); - } - else - putchar(c); - - lc = c; - off++; - } - - return 0; -} diff --git a/tools/der-to-pem b/tools/der-to-pem new file mode 100755 index 000000000..183996654 --- /dev/null +++ b/tools/der-to-pem @@ -0,0 +1,27 @@ +#!/bin/sh +# Convert A BER or DER encoding to PEM format. +# +# Copyright 20032 Free Software Foundation, Inc. +# +# This program 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. + +PGM="der-to-pem" +if [ $# == 0 ]; then + input="" +elif [ $# == 1 ]; then + input="$1" +else + echo "usage: $PGM [<inputfile>]" >&2 + exit 1 +fi + +echo "-----BEGIN CERTIFICATE-----" +mimencode $input +echo "-----END CERTIFICATE-----" + diff --git a/tools/gpg-connect-agent.c b/tools/gpg-connect-agent.c new file mode 100644 index 000000000..90e321000 --- /dev/null +++ b/tools/gpg-connect-agent.c @@ -0,0 +1,639 @@ +/* gpg-connect-agent.c - Tool to connect to the agent. + * Copyright (C) 2005 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 2 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. + */ + +#include <config.h> + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <ctype.h> +#include <assuan.h> + +#include "i18n.h" +#include "../common/util.h" +#include "../common/asshelp.h" + + + +/* Constants to identify the commands and options. */ +enum cmd_and_opt_values + { + aNull = 0, + oQuiet = 'q', + oVerbose = 'v', + oRawSocket = 'S', + + oNoVerbose = 500, + oHomedir, + oHex + + }; + + +/* The list of commands and options. */ +static ARGPARSE_OPTS opts[] = + { + { 301, NULL, 0, N_("@\nOptions:\n ") }, + + { oVerbose, "verbose", 0, N_("verbose") }, + { oQuiet, "quiet", 0, N_("quiet") }, + { oHex, "hex", 0, N_("print data out hex encoded") }, + { oRawSocket, "raw-socket", 2, N_("|NAME|connect to Assuan socket NAME")}, + + /* hidden options */ + { oNoVerbose, "no-verbose", 0, "@"}, + { oHomedir, "homedir", 2, "@" }, + {0} + }; + + +/* We keep all global options in the structure OPT. */ +struct +{ + int verbose; /* Verbosity level. */ + int quiet; /* Be extra quiet. */ + const char *homedir; /* Configuration directory name */ + int hex; /* Print data lines in hex format. */ + const char *raw_socket; /* Name of socket to connect in raw mode. */ +} opt; + + + +/* Definitions for /definq commands and a global linked list with all + the definitions. */ +struct definq_s +{ + struct definq_s *next; + char *name; /* Name of inquiry or NULL for any name. */ + int is_prog; /* True if this is a program to run. */ + char file[1]; /* Name of file or program. */ +}; +typedef struct definq_s *definq_t; + +static definq_t definq_list; +static definq_t *definq_list_tail = &definq_list; + + + +/*-- local prototypes --*/ +static int read_and_print_response (assuan_context_t ctx); +static assuan_context_t start_agent (void); + + + + +/* Print usage information and and provide strings for help. */ +static const char * +my_strusage( int level ) +{ + const char *p; + + switch (level) + { + case 11: p = "gpg-connect-agent (GnuPG)"; + break; + case 13: p = VERSION; break; + case 17: p = PRINTABLE_OS_NAME; break; + case 19: p = _("Please report bugs to <" PACKAGE_BUGREPORT ">.\n"); + break; + case 1: + case 40: p = _("Usage: gpg-connect-agent [options] (-h for help)"); + break; + case 41: + p = _("Syntax: gpg-connect-agent [options]\n" + "Connect to a running agent and send commands\n"); + break; + case 31: p = "\nHome: "; break; + case 32: p = opt.homedir; break; + case 33: p = "\n"; break; + + default: p = NULL; break; + } + return p; +} + + +/* Initialize the gettext system. */ +static void +i18n_init(void) +{ +#ifdef USE_SIMPLE_GETTEXT + set_gettext_file (PACKAGE_GT); +#else +# ifdef ENABLE_NLS + setlocale (LC_ALL, "" ); + bindtextdomain (PACKAGE_GT, LOCALEDIR); + textdomain (PACKAGE_GT); +# endif +#endif +} + +/* Store an inquire response pattern. Note, that this function may + change the content of LINE. We assume that leading white spaces + are already removed. */ +static void +add_definq (char *line, int is_prog) +{ + definq_t d; + char *name, *p; + + /* Get name. */ + name = line; + for (p=name; *p && !spacep (p); p++) + ; + if (*p) + *p++ = 0; + while (spacep (p)) + p++; + + d = xmalloc (sizeof *d + strlen (p) ); + strcpy (d->file, p); + d->is_prog = is_prog; + if ( !strcmp (name, "*")) + d->name = NULL; + else + d->name = xstrdup (name); + + d->next = NULL; + *definq_list_tail = d; + definq_list_tail = &d->next; +} + + +/* Show all inquiry defintions. */ +static void +show_definq (void) +{ + definq_t d; + + for (d=definq_list; d; d = d->next) + if (d->name) + printf ("%-20s %c %s\n", d->name, d->is_prog? 'p':'f', d->file); + for (d=definq_list; d; d = d->next) + if (!d->name) + printf ("%-20s %c %s\n", "*", d->is_prog? 'p':'f', d->file); +} + + +/* Clear all inquiry definitions. */ +static void +clear_definq (void) +{ + while (definq_list) + { + definq_t tmp = definq_list->next; + xfree (definq_list->name); + xfree (definq_list); + definq_list = tmp; + } + definq_list_tail = &definq_list; +} + + + +/* gpg-connect-agent's entry point. */ +int +main (int argc, char **argv) +{ + ARGPARSE_ARGS pargs; + const char *fname; + int no_more_options = 0; + assuan_context_t ctx; + char *line, *p; + size_t linesize; + int rc; + + set_strusage (my_strusage); + log_set_prefix ("gpg-connect-agent", 1); + + i18n_init(); + + opt.homedir = default_homedir (); + + /* Parse the command line. */ + pargs.argc = &argc; + pargs.argv = &argv; + pargs.flags = 1; /* Do not remove the args. */ + while (!no_more_options && optfile_parse (NULL, NULL, NULL, &pargs, opts)) + { + switch (pargs.r_opt) + { + case oQuiet: opt.quiet = 1; break; + case oVerbose: opt.verbose++; break; + case oNoVerbose: opt.verbose = 0; break; + case oHomedir: opt.homedir = pargs.r.ret_str; break; + case oHex: opt.hex = 1; break; + case oRawSocket: opt.raw_socket = pargs.r.ret_str; break; + + default: pargs.err = 2; break; + } + } + + if (log_get_errorcount (0)) + exit (2); + + fname = argc ? *argv : NULL; + + if (opt.raw_socket) + { + rc = assuan_socket_connect (&ctx, opt.raw_socket, 0); + if (rc) + { + log_error ("can't connect to socket `%s': %s\n", + opt.raw_socket, assuan_strerror (rc)); + exit (1); + } + + if (opt.verbose) + log_info ("connection to socket `%s' established\n", opt.raw_socket); + } + else + ctx = start_agent (); + line = NULL; + linesize = 0; + for (;;) + { + int n; + size_t maxlength; + + maxlength = 2048; + n = read_line (stdin, &line, &linesize, &maxlength); + if (n < 0) + { + log_error (_("error reading input: %s\n"), strerror (errno)); + exit (1); + } + if (!n) + break; /* EOF */ + if (!maxlength) + { + log_error (_("line too long - skipped\n")); + continue; + } + if (memchr (line, 0, n)) + log_info (_("line shortened due to embedded Nul character\n")); + if (line[n-1] == '\n') + line[n-1] = 0; + if (*line == '/') + { + /* Handle control commands. */ + char *cmd = line+1; + + for (p=cmd; *p && !spacep (p); p++) + ; + if (*p) + *p++ = 0; + while (spacep (p)) + p++; + if (!strcmp (cmd, "definqfile")) + { + add_definq (p, 0); + } + else if (!strcmp (cmd, "definqprog")) + { + add_definq (p, 1); + } + else if (!strcmp (cmd, "showdef")) + { + show_definq (); + } + else if (!strcmp (cmd, "cleardef")) + { + clear_definq (); + } + else if (!strcmp (cmd, "echo")) + { + puts (p); + } + else if (!strcmp (cmd, "help")) + { + puts ("Available commands:\n" + "/echo ARGS Echo ARGS.\n" + "/definqfile NAME FILE\n" + " Use content of FILE for inquiries with NAME.\n" + " NAME may be \"*\" to match any inquiry.\n" + "/definqprog NAME PGM\n" + " Run PGM for inquiries matching NAME and pass the\n" + " entire line to it as arguments.\n" + "/showdef Print all definitions.\n" + "/cleardef Delete all definitions.\n" + "/help Print this help."); + } + else + log_error (_("unknown command `%s'\n"), cmd ); + + continue; + } + + rc = assuan_write_line (ctx, line); + if (rc) + { + log_info (_("sending line failed: %s\n"), assuan_strerror (rc) ); + continue; + } + if (*line == '#' || !*line) + continue; /* Don't expect a response for a coment line. */ + + rc = read_and_print_response (ctx); + if (rc) + log_info (_("receiving line failed: %s\n"), assuan_strerror (rc) ); + } + + if (opt.verbose) + log_info ("closing connection to agent\n"); + + return 0; +} + + +/* Handle an Inquire from the server. Return False if it could not be + handled; in this case the caller shll complete the operation. LINE + is the complete line as received from the server. This function + may change the content of LINE. */ +static int +handle_inquire (assuan_context_t ctx, char *line) +{ + const char *name; + definq_t d; + FILE *fp; + char buffer[1024]; + int rc, n; + + /* Skip the command and trailing spaces. */ + for (; *line && !spacep (line); line++) + ; + while (spacep (line)) + line++; + /* Get the name. */ + name = line; + for (; *line && !spacep (line); line++) + ; + if (*line) + *line++ = 0; + + /* Now match it against our list. he second loop is todetect the + match all entry. **/ + for (d=definq_list; d; d = d->next) + if (d->name && !strcmp (d->name, name)) + break; + if (!d) + for (d=definq_list; d; d = d->next) + if (!d->name) + break; + if (!d) + { + if (opt.verbose) + log_info ("no handler for inquiry `%s' found\n", name); + return 0; + } + + if (d->is_prog) + { + fp = popen (d->file, "r"); + if (!fp) + log_error ("error executing `%s': %s\n", d->file, strerror (errno)); + else if (opt.verbose) + log_error ("handling inquiry `%s' by running `%s'\n", name, d->file); + } + else + { + fp = fopen (d->file, "rb"); + if (!fp) + log_error ("error opening `%s': %s\n", d->file, strerror (errno)); + else if (opt.verbose) + log_error ("handling inquiry `%s' by returning content of `%s'\n", + name, d->file); + } + if (!fp) + return 0; + + while ( (n = fread (buffer, 1, sizeof buffer, fp)) ) + { + rc = assuan_send_data (ctx, buffer, n); + if (rc) + { + log_error ("sending data back failed: %s\n", assuan_strerror (rc) ); + break; + } + } + if (ferror (fp)) + log_error ("error reading from `%s': %s\n", d->file, strerror (errno)); + + rc = assuan_send_data (ctx, NULL, 0); + if (rc) + log_error ("sending data back failed: %s\n", assuan_strerror (rc) ); + + if (d->is_prog) + { + if (pclose (fp)) + log_error ("error running `%s': %s\n", d->file, strerror (errno)); + } + else + fclose (fp); + return 1; +} + + +/* Read all response lines from server and print them. Returns 0 on + success or an assuan error code. */ +static int +read_and_print_response (assuan_context_t ctx) +{ + char *line; + size_t linelen; + assuan_error_t rc; + int i, j; + + for (;;) + { + do + { + rc = assuan_read_line (ctx, &line, &linelen); + if (rc) + return rc; + } + while (*line == '#' || !linelen); + + if (linelen >= 1 + && line[0] == 'D' && line[1] == ' ') + { + if (opt.hex) + { + for (i=2; i < linelen; ) + { + int save_i = i; + + printf ("D[%04X] ", i-2); + for (j=0; j < 16 ; j++, i++) + { + if (j == 8) + putchar (' '); + if (i < linelen) + printf (" %02X", ((unsigned char*)line)[i]); + else + fputs (" ", stdout); + } + fputs (" ", stdout); + i= save_i; + for (j=0; j < 16; j++, i++) + { + unsigned int c = ((unsigned char*)line)[i]; + if ( i >= linelen ) + putchar (' '); + else if (isascii (c) && isprint (c) && !iscntrl (c)) + putchar (c); + else + putchar ('.'); + } + putchar ('\n'); + } + } + else + { + fwrite (line, linelen, 1, stdout); + putchar ('\n'); + } + } + else if (linelen >= 1 + && line[0] == 'S' + && (line[1] == '\0' || line[1] == ' ')) + { + fwrite (line, linelen, 1, stdout); + putchar ('\n'); + } + else if (linelen >= 2 + && line[0] == 'O' && line[1] == 'K' + && (line[2] == '\0' || line[2] == ' ')) + { + fwrite (line, linelen, 1, stdout); + putchar ('\n'); + return 0; + } + else if (linelen >= 3 + && line[0] == 'E' && line[1] == 'R' && line[2] == 'R' + && (line[3] == '\0' || line[3] == ' ')) + { + fwrite (line, linelen, 1, stdout); + putchar ('\n'); + return 0; + } + else if (linelen >= 7 + && line[0] == 'I' && line[1] == 'N' && line[2] == 'Q' + && line[3] == 'U' && line[4] == 'I' && line[5] == 'R' + && line[6] == 'E' + && (line[7] == '\0' || line[7] == ' ')) + { + fwrite (line, linelen, 1, stdout); + putchar ('\n'); + if (!handle_inquire (ctx, line)) + assuan_write_line (ctx, "CANCEL"); + } + else if (linelen >= 3 + && line[0] == 'E' && line[1] == 'N' && line[2] == 'D' + && (line[3] == '\0' || line[3] == ' ')) + { + fwrite (line, linelen, 1, stdout); + putchar ('\n'); + /* Received from server, thus more responses are expected. */ + } + else + return ASSUAN_Invalid_Response; + } +} + + + + +/* Connect to the agent and send the standard options. */ +static assuan_context_t +start_agent (void) +{ + int rc = 0; + char *infostr, *p; + assuan_context_t ctx; + + infostr = getenv ("GPG_AGENT_INFO"); + if (!infostr || !*infostr) + { + char *sockname; + + /* Check whether we can connect at the standard socket. */ + sockname = make_filename (opt.homedir, "S.gpg-agent", NULL); + rc = assuan_socket_connect (&ctx, sockname, 0); + xfree (sockname); + } + else + { + int prot; + int pid; + + infostr = xstrdup (infostr); + if ( !(p = strchr (infostr, PATHSEP_C)) || p == infostr) + { + log_error (_("malformed GPG_AGENT_INFO environment variable\n")); + xfree (infostr); + exit (1); + } + *p++ = 0; + pid = atoi (p); + while (*p && *p != PATHSEP_C) + p++; + prot = *p? atoi (p+1) : 0; + if (prot != 1) + { + log_error (_("gpg-agent protocol version %d is not supported\n"), + prot); + xfree (infostr); + exit (1); + } + + rc = assuan_socket_connect (&ctx, infostr, pid); + xfree (infostr); + } + + if (rc) + { + log_error ("can't connect to the agent: %s\n", assuan_strerror (rc)); + exit (1); + } + + if (opt.verbose) + log_info ("connection to agent established\n"); + + rc = assuan_transact (ctx, "RESET", NULL, NULL, NULL, NULL, NULL, NULL); + if (rc) + { + log_error (_("error sending %s command: %s\n"), "RESET", + assuan_strerror (rc)); + exit (1); + } + + rc = send_pinentry_environment (ctx, GPG_ERR_SOURCE_DEFAULT, + NULL, NULL, NULL, NULL, NULL); + if (rc) + { + log_error (_("error sending standard options: %s\n"), gpg_strerror (rc)); + exit (1); + } + + return ctx; +} diff --git a/tools/gpg-zip.in b/tools/gpg-zip.in deleted file mode 100644 index 8b4ccfb2b..000000000 --- a/tools/gpg-zip.in +++ /dev/null @@ -1,142 +0,0 @@ -#!/bin/sh - -# gpg-archive - gpg-ized tar using the same format as PGP's PGP Zip. -# Copyright (C) 2005 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 2 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, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA - -# Despite the name, PGP Zip format is actually an OpenPGP-wrapped tar -# file. To be compatible with PGP itself, this must be a USTAR format -# tar file. Unclear on whether there is a distinction here between -# the GNU or POSIX variant of USTAR. - -VERSION=@VERSION@ -TAR=@TAR@ -GPG=gpg - -usage="\ -Usage: gpg-zip [--help] [--version] [--encrypt] [--decrypt] [--symmetric] - [--list-archive] [--output FILE] [--gpg GPG] [--gpg-args ARGS] - [--tar TAR] [--tar-args ARGS] filename1 [filename2, ...] - directory1 [directory2, ...] - -Encrypt or sign files into an archive." - -while test $# -gt 0 ; do - case $1 in - -h | --help | --h*) - echo "$usage" - exit 0 - ;; - --list-archive) - list=yes - create=no - unpack=no - shift - ;; - --encrypt | -e) - gpg_args="$gpg_args --encrypt" - list=no - create=yes - unpack=no - shift - ;; - --decrypt | -d) - gpg_args="$gpg_args --decrypt" - list=no - create=no - unpack=yes - shift - ;; - --symmetric | -c) - gpg_args="$gpg_args --symmetric" - list=no - create=yes - unpack=no - shift - ;; - --sign | -s) - gpg_args="$gpg_args --sign" - list=no - create=yes - unpack=no - shift - ;; - --recipient | -r) - gpg_args="$gpg_args --recipient $2" - shift - shift - ;; - --local-user | -u) - gpg_args="$gpg_args --local-user $2" - shift - shift - ;; - --output | -o) - gpg_args="$gpg_args --output $2" - shift - shift - ;; - --version) - echo "gpg-zip (GnuPG) $VERSION" - exit 0 - ;; - --gpg) - GPG=$1 - shift - ;; - --gpg-args) - gpg_args="$gpg_args $2" - shift - shift - ;; - --tar) - TAR=$1 - shift - ;; - --tar-args) - tar_args="$tar_args $2" - shift - shift - ;; - --) - shift - break - ;; - -*) - echo "$usage" 1>&2 - exit 1 - ;; - *) - break - ;; - esac -done - -if test x$create = xyes ; then -# echo "$TAR -cf - "$@" | $GPG --set-filename x.tar $gpg_args" 1>&2 - $TAR -cf - "$@" | $GPG --set-filename x.tar $gpg_args -elif test x$list = xyes ; then -# echo "cat \"$1\" | $GPG $gpg_args | $TAR $tar_args -tf -" 1>&2 - cat "$1" | $GPG $gpg_args | $TAR $tar_args -tf - -elif test x$unpack = xyes ; then -# echo "cat \"$1\" | $GPG $gpg_args | $TAR $tar_args -xvf -" 1>&2 - cat "$1" | $GPG $gpg_args | $TAR $tar_args -xvf - -else - echo "$usage" 1>&2 - exit 1 -fi diff --git a/tools/gpgconf-comp.c b/tools/gpgconf-comp.c new file mode 100644 index 000000000..04a61a193 --- /dev/null +++ b/tools/gpgconf-comp.c @@ -0,0 +1,2499 @@ +/* gpgconf-comp.c - Configuration utility for GnuPG. + * Copyright (C) 2004 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 2 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 GnuPG; if not, write to the Free Software Foundation, + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. + */ + +#if HAVE_CONFIG_H +#include <config.h> +#endif +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <fcntl.h> +#include <unistd.h> +#include <sys/types.h> +#include <assert.h> +#include <errno.h> +#include <time.h> +#include <stdarg.h> +#include <signal.h> + +/* For log_logv(), asctimestamp(), gnupg_get_time (). */ +#define JNLIB_NEED_LOG_LOGV +#include "util.h" +#include "i18n.h" + +#include "gpgconf.h" + + + +/* TODO: + Components: Add more components and their options. + Robustness: Do more validation. Call programs to do validation for us. + Don't use popen, as this will not tell us if the program had a + non-zero exit code. + Add options to change backend binary path. + Extract binary path for some backends from gpgsm/gpg config. +*/ + + +#if (__GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 5 )) +void gc_error (int status, int errnum, const char *fmt, ...) \ + __attribute__ ((format (printf, 3, 4))); +#endif + +/* Output a diagnostic message. If ERRNUM is not 0, then the output + is followed by a colon, a white space, and the error string for the + error number ERRNUM. In any case the output is finished by a + newline. The message is prepended by the program name, a colon, + and a whitespace. The output may be further formatted or + redirected by the jnlib logging facility. */ +void +gc_error (int status, int errnum, const char *fmt, ...) +{ + va_list arg_ptr; + + va_start (arg_ptr, fmt); + log_logv (JNLIB_LOG_ERROR, fmt, arg_ptr); + va_end (arg_ptr); + + if (errnum) + log_printf (": %s\n", strerror (errnum)); + else + log_printf ("\n"); + + if (status) + { + log_printf (NULL); + log_printf ("fatal error (exit status %i)\n", status); + exit (status); + } +} + + +/* Forward declaration. */ +void gpg_agent_runtime_change (void); + +/* Backend configuration. Backends are used to decide how the default + and current value of an option can be determined, and how the + option can be changed. To every option in every component belongs + exactly one backend that controls and determines the option. Some + backends are programs from the GPG system. Others might be + implemented by GPGConf itself. If you change this enum, don't + forget to update GC_BACKEND below. */ +typedef enum + { + /* Any backend, used for find_option (). */ + GC_BACKEND_ANY, + + /* The Gnu Privacy Guard. */ + GC_BACKEND_GPG, + + /* The Gnu Privacy Guard for S/MIME. */ + GC_BACKEND_GPGSM, + + /* The GPG Agent. */ + GC_BACKEND_GPG_AGENT, + + /* The GnuPG SCDaemon. */ + GC_BACKEND_SCDAEMON, + + /* The Aegypten directory manager. */ + GC_BACKEND_DIRMNGR, + + /* The LDAP server list file for the Aegypten director manager. */ + GC_BACKEND_DIRMNGR_LDAP_SERVER_LIST, + + /* The number of the above entries. */ + GC_BACKEND_NR + } gc_backend_t; + + +/* To be able to implement generic algorithms for the various + backends, we collect all information about them in this struct. */ +static struct +{ + /* The name of the backend. */ + const char *name; + + /* The name of the program that acts as the backend. Some backends + don't have an associated program, but are implemented directly by + GPGConf. In this case, PROGRAM is NULL. */ + char *program; + + /* The runtime change callback. */ + void (*runtime_change) (void); + + /* The option name for the configuration filename of this backend. + This must be an absolute pathname. It can be an option from a + different backend (but then ordering of the options might + matter). */ + const char *option_config_filename; + + /* If this is a file backend rather than a program backend, then + this is the name of the option associated with the file. */ + const char *option_name; +} gc_backend[GC_BACKEND_NR] = + { + { NULL }, /* GC_BACKEND_ANY dummy entry. */ + { "GnuPG", "gpg", NULL, "gpgconf-gpg.conf" }, + { "GPGSM", "gpgsm", NULL, "gpgconf-gpgsm.conf" }, + { "GPG Agent", "gpg-agent", gpg_agent_runtime_change, + "gpgconf-gpg-agent.conf" }, + { "SCDaemon", "scdaemon", NULL, "gpgconf-scdaemon.conf" }, + { "DirMngr", "dirmngr", NULL, "gpgconf-dirmngr.conf" }, + { "DirMngr LDAP Server List", NULL, NULL, "ldapserverlist-file", + "LDAP Server" }, + }; + + +/* Option configuration. */ + +/* An option might take an argument, or not. Argument types can be + basic or complex. Basic types are generic and easy to validate. + Complex types provide more specific information about the intended + use, but can be difficult to validate. If you add to this enum, + don't forget to update GC_ARG_TYPE below. YOU MUST NOT CHANGE THE + NUMBERS OF THE EXISTING ENTRIES, AS THEY ARE PART OF THE EXTERNAL + INTERFACE. */ +typedef enum + { + /* Basic argument types. */ + + /* No argument. */ + GC_ARG_TYPE_NONE = 0, + + /* A String argument. */ + GC_ARG_TYPE_STRING = 1, + + /* A signed integer argument. */ + GC_ARG_TYPE_INT32 = 2, + + /* An unsigned integer argument. */ + GC_ARG_TYPE_UINT32 = 3, + + /* ADD NEW BASIC TYPE ENTRIES HERE. */ + + /* Complex argument types. */ + + /* A complete pathname. */ + GC_ARG_TYPE_PATHNAME = 32, + + /* An LDAP server in the format + HOSTNAME:PORT:USERNAME:PASSWORD:BASE_DN. */ + GC_ARG_TYPE_LDAP_SERVER = 33, + + /* A 40 character fingerprint. */ + GC_ARG_TYPE_KEY_FPR = 34, + + /* ADD NEW COMPLEX TYPE ENTRIES HERE. */ + + /* The number of the above entries. */ + GC_ARG_TYPE_NR + } gc_arg_type_t; + + +/* For every argument, we record some information about it in the + following struct. */ +static struct +{ + /* For every argument type exists a basic argument type that can be + used as a fallback for input and validation purposes. */ + gc_arg_type_t fallback; + + /* Human-readable name of the type. */ + const char *name; +} gc_arg_type[GC_ARG_TYPE_NR] = + { + /* The basic argument types have their own types as fallback. */ + { GC_ARG_TYPE_NONE, "none" }, + { GC_ARG_TYPE_STRING, "string" }, + { GC_ARG_TYPE_INT32, "int32" }, + { GC_ARG_TYPE_UINT32, "uint32" }, + + /* Reserved basic type entries for future extension. */ + { GC_ARG_TYPE_NR, NULL }, { GC_ARG_TYPE_NR, NULL }, + { GC_ARG_TYPE_NR, NULL }, { GC_ARG_TYPE_NR, NULL }, + { GC_ARG_TYPE_NR, NULL }, { GC_ARG_TYPE_NR, NULL }, + { GC_ARG_TYPE_NR, NULL }, { GC_ARG_TYPE_NR, NULL }, + { GC_ARG_TYPE_NR, NULL }, { GC_ARG_TYPE_NR, NULL }, + { GC_ARG_TYPE_NR, NULL }, { GC_ARG_TYPE_NR, NULL }, + { GC_ARG_TYPE_NR, NULL }, { GC_ARG_TYPE_NR, NULL }, + { GC_ARG_TYPE_NR, NULL }, { GC_ARG_TYPE_NR, NULL }, + { GC_ARG_TYPE_NR, NULL }, { GC_ARG_TYPE_NR, NULL }, + { GC_ARG_TYPE_NR, NULL }, { GC_ARG_TYPE_NR, NULL }, + { GC_ARG_TYPE_NR, NULL }, { GC_ARG_TYPE_NR, NULL }, + { GC_ARG_TYPE_NR, NULL }, { GC_ARG_TYPE_NR, NULL }, + { GC_ARG_TYPE_NR, NULL }, { GC_ARG_TYPE_NR, NULL }, + { GC_ARG_TYPE_NR, NULL }, { GC_ARG_TYPE_NR, NULL }, + + /* The complex argument types have a basic type as fallback. */ + { GC_ARG_TYPE_STRING, "pathname" }, + { GC_ARG_TYPE_STRING, "ldap server" }, + { GC_ARG_TYPE_STRING, "key fpr" }, + }; + + +/* Every option has an associated expert level, than can be used to + hide advanced and expert options from beginners. If you add to + this list, don't forget to update GC_LEVEL below. YOU MUST NOT + CHANGE THE NUMBERS OF THE EXISTING ENTRIES, AS THEY ARE PART OF THE + EXTERNAL INTERFACE. */ +typedef enum + { + /* The basic options should always be displayed. */ + GC_LEVEL_BASIC, + + /* The advanced options may be hidden from beginners. */ + GC_LEVEL_ADVANCED, + + /* The expert options should only be displayed to experts. */ + GC_LEVEL_EXPERT, + + /* The invisible options should normally never be displayed. */ + GC_LEVEL_INVISIBLE, + + /* The internal options are never exported, they mark options that + are recorded for internal use only. */ + GC_LEVEL_INTERNAL, + + /* ADD NEW ENTRIES HERE. */ + + /* The number of the above entries. */ + GC_LEVEL_NR + } gc_expert_level_t; + +/* A description for each expert level. */ +static struct +{ + const char *name; +} gc_level[] = + { + { "basic" }, + { "advanced" }, + { "expert" }, + { "invisible" }, + { "internal" } + }; + + +/* Option flags. YOU MUST NOT CHANGE THE NUMBERS OF THE EXISTING + FLAGS, AS THEY ARE PART OF THE EXTERNAL INTERFACE. */ +#define GC_OPT_FLAG_NONE 0UL +/* Some entries in the option list are not options, but mark the + beginning of a new group of options. These entries have the GROUP + flag set. */ +#define GC_OPT_FLAG_GROUP (1UL << 0) +/* The ARG_OPT flag for an option indicates that the argument is + optional. This is never set for GC_ARG_TYPE_NONE options. */ +#define GC_OPT_FLAG_ARG_OPT (1UL << 1) +/* The LIST flag for an option indicates that the option can occur + several times. A comma separated list of arguments is used as the + argument value. */ +#define GC_OPT_FLAG_LIST (1UL << 2) +/* The RUNTIME flag for an option indicates that the option can be + changed at runtime. */ +#define GC_OPT_FLAG_RUNTIME (1UL << 3) + +/* The following flags are incorporated from the backend. */ +/* The DEFAULT flag for an option indicates that the option has a + default value. */ +#define GC_OPT_FLAG_DEFAULT (1UL << 4) +/* The DEF_DESC flag for an option indicates that the option has a + default, which is described by the value of the default field. */ +#define GC_OPT_FLAG_DEF_DESC (1UL << 5) +/* The NO_ARG_DESC flag for an option indicates that the argument has + a default, which is described by the value of the ARGDEF field. */ +#define GC_OPT_FLAG_NO_ARG_DESC (1UL << 6) + +/* A human-readable description for each flag. */ +static struct +{ + const char *name; +} gc_flag[] = + { + { "group" }, + { "optional arg" }, + { "list" }, + { "runtime" }, + { "default" }, + { "default desc" }, + { "no arg desc" } + }; + + +/* To each option, or group marker, the information in the GC_OPTION + struct is provided. If you change this, don't forget to update the + option list of each component. */ +struct gc_option +{ + /* If this is NULL, then this is a terminator in an array of unknown + length. Otherwise, if this entry is a group marker (see FLAGS), + then this is the name of the group described by this entry. + Otherwise it is the name of the option described by this + entry. The name must not contain a colon. */ + const char *name; + + /* The option flags. If the GROUP flag is set, then this entry is a + group marker, not an option, and only the fields LEVEL, + DESC_DOMAIN and DESC are valid. In all other cases, this entry + describes a new option and all fields are valid. */ + unsigned long flags; + + /* The expert level. This field is valid for options and groups. A + group has the expert level of the lowest-level option in the + group. */ + gc_expert_level_t level; + + /* A gettext domain in which the following description can be found. + If this is NULL, then DESC is not translated. Valid for groups + and options. + + Note that we try to keep the description of groups within the + gnupg domain. + + IMPORTANT: If you add a new domain please make sure to add a code + set switching call to the function my_dgettext further below. */ + const char *desc_domain; + + /* A gettext description for this group or option. If it starts + with a '|', then the string up to the next '|' describes the + argument, and the description follows the second '|'. + + In general enclosing these description in N_() is not required + because the description should be identical to the one in the + help menu of the respective program. */ + const char *desc; + + /* The following fields are only valid for options. */ + + /* The type of the option argument. */ + gc_arg_type_t arg_type; + + /* The backend that implements this option. */ + gc_backend_t backend; + + /* The following fields are set to NULL at startup (because all + option's are declared as static variables). They are at the end + of the list so that they can be omitted from the option + declarations. */ + + /* This is true if the option is supported by this version of the + backend. */ + int active; + + /* The default value for this option. This is NULL if the option is + not present in the backend, the empty string if no default is + available, and otherwise a quoted string. */ + char *default_value; + + /* The default argument is only valid if the "optional arg" flag is + set, and specifies the default argument (value) that is used if + the argument is omitted. */ + char *default_arg; + + /* The current value of this option. */ + char *value; + + /* The new flags for this option. The only defined flag is actually + GC_OPT_FLAG_DEFAULT, and it means that the option should be + deleted. In this case, NEW_VALUE is NULL. */ + unsigned long new_flags; + + /* The new value of this option. */ + char *new_value; +}; +typedef struct gc_option gc_option_t; + +/* Use this macro to terminate an option list. */ +#define GC_OPTION_NULL { NULL } + + +/* The options of the GC_COMPONENT_GPG_AGENT component. */ +static gc_option_t gc_options_gpg_agent[] = + { + /* The configuration file to which we write the changes. */ + { "gpgconf-gpg-agent.conf", GC_OPT_FLAG_NONE, GC_LEVEL_INTERNAL, + NULL, NULL, GC_ARG_TYPE_PATHNAME, GC_BACKEND_GPG_AGENT }, + + { "Monitor", + GC_OPT_FLAG_GROUP, GC_LEVEL_BASIC, + "gnupg", N_("Options controlling the diagnostic output") }, + { "verbose", GC_OPT_FLAG_LIST|GC_OPT_FLAG_RUNTIME, GC_LEVEL_BASIC, + "gnupg", "verbose", + GC_ARG_TYPE_NONE, GC_BACKEND_GPG_AGENT }, + { "quiet", GC_OPT_FLAG_NONE|GC_OPT_FLAG_RUNTIME, GC_LEVEL_BASIC, + "gnupg", "be somewhat more quiet", + GC_ARG_TYPE_NONE, GC_BACKEND_GPG_AGENT }, + { "no-greeting", GC_OPT_FLAG_NONE, GC_LEVEL_INVISIBLE, + NULL, NULL, + GC_ARG_TYPE_NONE, GC_BACKEND_GPG_AGENT }, + + { "Configuration", + GC_OPT_FLAG_GROUP, GC_LEVEL_EXPERT, + "gnupg", N_("Options controlling the configuration") }, + { "options", GC_OPT_FLAG_NONE, GC_LEVEL_EXPERT, + "gnupg", "|FILE|read options from FILE", + GC_ARG_TYPE_PATHNAME, GC_BACKEND_GPG_AGENT }, + { "disable-scdaemon", GC_OPT_FLAG_NONE, GC_LEVEL_ADVANCED, + "gnupg", "do not use the SCdaemon", + GC_ARG_TYPE_NONE, GC_BACKEND_GPG_AGENT }, + + { "Debug", + GC_OPT_FLAG_GROUP, GC_LEVEL_ADVANCED, + "gnupg", N_("Options useful for debugging") }, + { "debug-level", GC_OPT_FLAG_ARG_OPT|GC_OPT_FLAG_RUNTIME, GC_LEVEL_ADVANCED, + "gnupg", "|LEVEL|set the debugging level to LEVEL", + GC_ARG_TYPE_STRING, GC_BACKEND_GPG_AGENT }, + { "log-file", GC_OPT_FLAG_RUNTIME, GC_LEVEL_ADVANCED, + "gnupg", N_("|FILE|write server mode logs to FILE"), + GC_ARG_TYPE_PATHNAME, GC_BACKEND_GPG_AGENT }, + { "faked-system-time", GC_OPT_FLAG_NONE, GC_LEVEL_INVISIBLE, + NULL, NULL, + GC_ARG_TYPE_UINT32, GC_BACKEND_GPG_AGENT }, + + { "Security", + GC_OPT_FLAG_GROUP, GC_LEVEL_BASIC, + "gnupg", N_("Options controlling the security") }, + { "default-cache-ttl", GC_OPT_FLAG_RUNTIME, GC_LEVEL_BASIC, + "gnupg", "|N|expire cached PINs after N seconds", + GC_ARG_TYPE_UINT32, GC_BACKEND_GPG_AGENT }, + { "ignore-cache-for-signing", GC_OPT_FLAG_RUNTIME, GC_LEVEL_BASIC, + "gnupg", "do not use the PIN cache when signing", + GC_ARG_TYPE_NONE, GC_BACKEND_GPG_AGENT }, + { "allow-mark-trusted", GC_OPT_FLAG_RUNTIME, GC_LEVEL_ADVANCED, + "gnupg", "allow clients to mark keys as \"trusted\"", + GC_ARG_TYPE_NONE, GC_BACKEND_GPG_AGENT }, + { "no-grab", GC_OPT_FLAG_RUNTIME, GC_LEVEL_EXPERT, + "gnupg", "do not grab keyboard and mouse", + GC_ARG_TYPE_NONE, GC_BACKEND_GPG_AGENT }, + + + GC_OPTION_NULL + }; + + +/* The options of the GC_COMPONENT_SCDAEMON component. */ +static gc_option_t gc_options_scdaemon[] = + { + /* The configuration file to which we write the changes. */ + { "gpgconf-scdaemon.conf", GC_OPT_FLAG_NONE, GC_LEVEL_INTERNAL, + NULL, NULL, GC_ARG_TYPE_PATHNAME, GC_BACKEND_SCDAEMON }, + + { "Monitor", + GC_OPT_FLAG_GROUP, GC_LEVEL_BASIC, + "gnupg", N_("Options controlling the diagnostic output") }, + { "verbose", GC_OPT_FLAG_LIST, GC_LEVEL_BASIC, + "gnupg", "verbose", + GC_ARG_TYPE_NONE, GC_BACKEND_SCDAEMON }, + { "quiet", GC_OPT_FLAG_NONE, GC_LEVEL_BASIC, + "gnupg", "be somewhat more quiet", + GC_ARG_TYPE_NONE, GC_BACKEND_SCDAEMON }, + { "no-greeting", GC_OPT_FLAG_NONE, GC_LEVEL_INVISIBLE, + NULL, NULL, + GC_ARG_TYPE_NONE, GC_BACKEND_SCDAEMON }, + + { "Configuration", + GC_OPT_FLAG_GROUP, GC_LEVEL_EXPERT, + "gnupg", N_("Options controlling the configuration") }, + { "options", GC_OPT_FLAG_NONE, GC_LEVEL_EXPERT, + "gnupg", "|FILE|read options from FILE", + GC_ARG_TYPE_PATHNAME, GC_BACKEND_SCDAEMON }, + { "reader-port", GC_OPT_FLAG_NONE, GC_LEVEL_BASIC, + "gnupg", "|N|connect to reader at port N", + GC_ARG_TYPE_STRING, GC_BACKEND_SCDAEMON }, + { "ctapi-driver", GC_OPT_FLAG_NONE, GC_LEVEL_ADVANCED, + "gnupg", "|NAME|use NAME as ct-API driver", + GC_ARG_TYPE_STRING, GC_BACKEND_SCDAEMON }, + { "pcsc-driver", GC_OPT_FLAG_NONE, GC_LEVEL_ADVANCED, + "gnupg", "|NAME|use NAME as PC/SC driver", + GC_ARG_TYPE_STRING, GC_BACKEND_SCDAEMON }, + { "disable-opensc", GC_OPT_FLAG_NONE, GC_LEVEL_EXPERT, + "gnupg", "do not use the OpenSC layer", + GC_ARG_TYPE_NONE, GC_BACKEND_SCDAEMON }, + { "disable-ccid", GC_OPT_FLAG_NONE, GC_LEVEL_EXPERT, + "gnupg", "do not use the internal CCID driver", + GC_ARG_TYPE_NONE, GC_BACKEND_SCDAEMON }, + { "disable-keypad", GC_OPT_FLAG_NONE, GC_LEVEL_ADVANCED, + "gnupg", "do not use a reader's keypad", + GC_ARG_TYPE_NONE, GC_BACKEND_SCDAEMON }, + + { "Debug", + GC_OPT_FLAG_GROUP, GC_LEVEL_ADVANCED, + "gnupg", N_("Options useful for debugging") }, + { "debug-level", GC_OPT_FLAG_ARG_OPT, GC_LEVEL_ADVANCED, + "gnupg", "|LEVEL|set the debugging level to LEVEL", + GC_ARG_TYPE_STRING, GC_BACKEND_SCDAEMON }, + { "log-file", GC_OPT_FLAG_NONE, GC_LEVEL_ADVANCED, + "gnupg", N_("|FILE|write server mode logs to FILE"), + GC_ARG_TYPE_PATHNAME, GC_BACKEND_SCDAEMON }, + + { "Security", + GC_OPT_FLAG_GROUP, GC_LEVEL_BASIC, + "gnupg", N_("Options controlling the security") }, + { "allow-admin", GC_OPT_FLAG_NONE, GC_LEVEL_BASIC, + "gnupg", "allow the use of admin card commands", + GC_ARG_TYPE_NONE, GC_BACKEND_SCDAEMON }, + + + GC_OPTION_NULL + }; + + +/* The options of the GC_COMPONENT_GPG component. */ +static gc_option_t gc_options_gpg[] = + { + /* The configuration file to which we write the changes. */ + { "gpgconf-gpg.conf", GC_OPT_FLAG_NONE, GC_LEVEL_INTERNAL, + NULL, NULL, GC_ARG_TYPE_PATHNAME, GC_BACKEND_GPG }, + + { "Monitor", + GC_OPT_FLAG_GROUP, GC_LEVEL_BASIC, + "gnupg", N_("Options controlling the diagnostic output") }, + { "verbose", GC_OPT_FLAG_LIST, GC_LEVEL_BASIC, + "gnupg", "verbose", + GC_ARG_TYPE_NONE, GC_BACKEND_GPG }, + { "quiet", GC_OPT_FLAG_NONE, GC_LEVEL_BASIC, + "gnupg", "be somewhat more quiet", + GC_ARG_TYPE_NONE, GC_BACKEND_GPG }, + { "no-greeting", GC_OPT_FLAG_NONE, GC_LEVEL_INVISIBLE, + NULL, NULL, + GC_ARG_TYPE_NONE, GC_BACKEND_GPG }, + + { "Configuration", + GC_OPT_FLAG_GROUP, GC_LEVEL_EXPERT, + "gnupg", N_("Options controlling the configuration") }, + { "options", GC_OPT_FLAG_NONE, GC_LEVEL_EXPERT, + "gnupg", "|FILE|read options from FILE", + GC_ARG_TYPE_PATHNAME, GC_BACKEND_GPG }, + + { "Debug", + GC_OPT_FLAG_GROUP, GC_LEVEL_ADVANCED, + "gnupg", N_("Options useful for debugging") }, + { "debug-level", GC_OPT_FLAG_ARG_OPT, GC_LEVEL_ADVANCED, + "gnupg", "|LEVEL|set the debugging level to LEVEL", + GC_ARG_TYPE_STRING, GC_BACKEND_GPG }, + { "log-file", GC_OPT_FLAG_NONE, GC_LEVEL_ADVANCED, + "gnupg", N_("|FILE|write server mode logs to FILE"), + GC_ARG_TYPE_PATHNAME, GC_BACKEND_GPG }, +/* { "faked-system-time", GC_OPT_FLAG_NONE, GC_LEVEL_INVISIBLE, */ +/* NULL, NULL, */ +/* GC_ARG_TYPE_UINT32, GC_BACKEND_GPG }, */ + + { "Keyserver", + GC_OPT_FLAG_GROUP, GC_LEVEL_BASIC, + "gnupg", N_("Configuration for Keyservers") }, + { "keyserver", GC_OPT_FLAG_NONE, GC_LEVEL_BASIC, + "gnupg", "|URL|use keyserver at URL", + GC_ARG_TYPE_STRING, GC_BACKEND_GPG }, + { "allow-pka-lookup", GC_OPT_FLAG_NONE, GC_LEVEL_BASIC, + "gnupg", N_("allow PKA lookups (DNS requests)"), + GC_ARG_TYPE_NONE, GC_BACKEND_GPG }, + + + GC_OPTION_NULL + }; + + + +/* The options of the GC_COMPONENT_GPGSM component. */ +static gc_option_t gc_options_gpgsm[] = + { + /* The configuration file to which we write the changes. */ + { "gpgconf-gpgsm.conf", GC_OPT_FLAG_NONE, GC_LEVEL_INTERNAL, + NULL, NULL, GC_ARG_TYPE_PATHNAME, GC_BACKEND_GPGSM }, + + { "Monitor", + GC_OPT_FLAG_GROUP, GC_LEVEL_BASIC, + "gnupg", N_("Options controlling the diagnostic output") }, + { "verbose", GC_OPT_FLAG_LIST, GC_LEVEL_BASIC, + "gnupg", "verbose", + GC_ARG_TYPE_NONE, GC_BACKEND_GPGSM }, + { "quiet", GC_OPT_FLAG_NONE, GC_LEVEL_BASIC, + "gnupg", "be somewhat more quiet", + GC_ARG_TYPE_NONE, GC_BACKEND_GPGSM }, + { "no-greeting", GC_OPT_FLAG_NONE, GC_LEVEL_INVISIBLE, + NULL, NULL, + GC_ARG_TYPE_NONE, GC_BACKEND_GPGSM }, + + { "Configuration", + GC_OPT_FLAG_GROUP, GC_LEVEL_EXPERT, + "gnupg", N_("Options controlling the configuration") }, + { "options", GC_OPT_FLAG_NONE, GC_LEVEL_EXPERT, + "gnupg", "|FILE|read options from FILE", + GC_ARG_TYPE_PATHNAME, GC_BACKEND_GPGSM }, + { "prefer-system-dirmngr", GC_OPT_FLAG_NONE, GC_LEVEL_ADVANCED, + "gnupg", "use system's dirmngr if available", + GC_ARG_TYPE_NONE, GC_BACKEND_GPGSM }, + + { "Debug", + GC_OPT_FLAG_GROUP, GC_LEVEL_ADVANCED, + "gnupg", N_("Options useful for debugging") }, + { "debug-level", GC_OPT_FLAG_ARG_OPT, GC_LEVEL_ADVANCED, + "gnupg", "|LEVEL|set the debugging level to LEVEL", + GC_ARG_TYPE_STRING, GC_BACKEND_GPGSM }, + { "log-file", GC_OPT_FLAG_NONE, GC_LEVEL_ADVANCED, + "gnupg", N_("|FILE|write server mode logs to FILE"), + GC_ARG_TYPE_PATHNAME, GC_BACKEND_GPGSM }, + { "faked-system-time", GC_OPT_FLAG_NONE, GC_LEVEL_INVISIBLE, + NULL, NULL, + GC_ARG_TYPE_UINT32, GC_BACKEND_GPGSM }, + + { "Security", + GC_OPT_FLAG_GROUP, GC_LEVEL_BASIC, + "gnupg", N_("Options controlling the security") }, + { "disable-crl-checks", GC_OPT_FLAG_NONE, GC_LEVEL_BASIC, + "gnupg", "never consult a CRL", + GC_ARG_TYPE_NONE, GC_BACKEND_GPGSM }, + { "disable-trusted-cert-crl-check", GC_OPT_FLAG_NONE, GC_LEVEL_EXPERT, + "gnupg", N_("do not check CRLs for root certificates"), + GC_ARG_TYPE_NONE, GC_BACKEND_GPGSM }, + { "enable-ocsp", GC_OPT_FLAG_NONE, GC_LEVEL_ADVANCED, + "gnupg", "check validity using OCSP", + GC_ARG_TYPE_NONE, GC_BACKEND_GPGSM }, + { "include-certs", GC_OPT_FLAG_NONE, GC_LEVEL_EXPERT, + "gnupg", "|N|number of certificates to include", + GC_ARG_TYPE_INT32, GC_BACKEND_GPGSM }, + { "disable-policy-checks", GC_OPT_FLAG_NONE, GC_LEVEL_ADVANCED, + "gnupg", "do not check certificate policies", + GC_ARG_TYPE_NONE, GC_BACKEND_GPGSM }, + { "auto-issuer-key-retrieve", GC_OPT_FLAG_NONE, GC_LEVEL_BASIC, + "gnupg", "fetch missing issuer certificates", + GC_ARG_TYPE_NONE, GC_BACKEND_GPGSM }, + + GC_OPTION_NULL + }; + + +/* The options of the GC_COMPONENT_DIRMNGR component. */ +static gc_option_t gc_options_dirmngr[] = + { + /* The configuration file to which we write the changes. */ + { "gpgconf-dirmngr.conf", GC_OPT_FLAG_NONE, GC_LEVEL_INTERNAL, + NULL, NULL, GC_ARG_TYPE_PATHNAME, GC_BACKEND_DIRMNGR }, + + { "Monitor", + GC_OPT_FLAG_GROUP, GC_LEVEL_BASIC, + "gnupg", N_("Options controlling the diagnostic output") }, + { "verbose", GC_OPT_FLAG_LIST, GC_LEVEL_BASIC, + "dirmngr", "verbose", + GC_ARG_TYPE_NONE, GC_BACKEND_DIRMNGR }, + { "quiet", GC_OPT_FLAG_NONE, GC_LEVEL_BASIC, + "dirmngr", "be somewhat more quiet", + GC_ARG_TYPE_NONE, GC_BACKEND_DIRMNGR }, + { "no-greeting", GC_OPT_FLAG_NONE, GC_LEVEL_INVISIBLE, + NULL, NULL, + GC_ARG_TYPE_NONE, GC_BACKEND_DIRMNGR }, + + { "Format", + GC_OPT_FLAG_GROUP, GC_LEVEL_BASIC, + "gnupg", N_("Options controlling the format of the output") }, + { "sh", GC_OPT_FLAG_NONE, GC_LEVEL_BASIC, + "dirmngr", "sh-style command output", + GC_ARG_TYPE_NONE, GC_BACKEND_DIRMNGR }, + { "csh", GC_OPT_FLAG_NONE, GC_LEVEL_BASIC, + "dirmngr", "csh-style command output", + GC_ARG_TYPE_NONE, GC_BACKEND_DIRMNGR }, + + { "Configuration", + GC_OPT_FLAG_GROUP, GC_LEVEL_EXPERT, + "gnupg", N_("Options controlling the configuration") }, + { "options", GC_OPT_FLAG_NONE, GC_LEVEL_EXPERT, + "dirmngr", "|FILE|read options from FILE", + GC_ARG_TYPE_PATHNAME, GC_BACKEND_DIRMNGR }, + + { "Debug", + GC_OPT_FLAG_GROUP, GC_LEVEL_ADVANCED, + "gnupg", N_("Options useful for debugging") }, + { "debug-level", GC_OPT_FLAG_ARG_OPT, GC_LEVEL_ADVANCED, + "dirmngr", "|LEVEL|set the debugging level to LEVEL", + GC_ARG_TYPE_STRING, GC_BACKEND_DIRMNGR }, + { "no-detach", GC_OPT_FLAG_NONE, GC_LEVEL_ADVANCED, + "dirmngr", "do not detach from the console", + GC_ARG_TYPE_NONE, GC_BACKEND_DIRMNGR }, + { "log-file", GC_OPT_FLAG_NONE, GC_LEVEL_ADVANCED, + "dirmngr", N_("|FILE|write server mode logs to FILE"), + GC_ARG_TYPE_PATHNAME, GC_BACKEND_DIRMNGR }, + { "debug-wait", GC_OPT_FLAG_NONE, GC_LEVEL_INVISIBLE, + NULL, NULL, + GC_ARG_TYPE_UINT32, GC_BACKEND_DIRMNGR }, + { "faked-system-time", GC_OPT_FLAG_NONE, GC_LEVEL_INVISIBLE, + NULL, NULL, + GC_ARG_TYPE_UINT32, GC_BACKEND_DIRMNGR }, + + { "Enforcement", + GC_OPT_FLAG_GROUP, GC_LEVEL_BASIC, + "gnupg", N_("Options controlling the interactivity and enforcement") }, + { "batch", GC_OPT_FLAG_NONE, GC_LEVEL_BASIC, + "dirmngr", "run without asking a user", + GC_ARG_TYPE_NONE, GC_BACKEND_DIRMNGR }, + { "force", GC_OPT_FLAG_NONE, GC_LEVEL_BASIC, + "dirmngr", "force loading of outdated CRLs", + GC_ARG_TYPE_NONE, GC_BACKEND_DIRMNGR }, + + { "HTTP", + GC_OPT_FLAG_GROUP, GC_LEVEL_ADVANCED, + "gnupg", N_("Configuration for HTTP servers") }, + { "disable-http", GC_OPT_FLAG_NONE, GC_LEVEL_ADVANCED, + "dirmngr", "inhibit the use of HTTP", + GC_ARG_TYPE_NONE, GC_BACKEND_DIRMNGR }, + { "ignore-http-dp", GC_OPT_FLAG_NONE, GC_LEVEL_ADVANCED, + "dirmngr", "ignore HTTP CRL distribution points", + GC_ARG_TYPE_NONE, GC_BACKEND_DIRMNGR }, + { "http-proxy", GC_OPT_FLAG_NONE, GC_LEVEL_ADVANCED, + "dirmngr", "|URL|redirect all HTTP requests to URL", + GC_ARG_TYPE_STRING, GC_BACKEND_DIRMNGR }, + { "honor-http-proxy", GC_OPT_FLAG_NONE, GC_LEVEL_ADVANCED, + "dirmngr", N_("use system's HTTP proxy setting"), + GC_ARG_TYPE_NONE, GC_BACKEND_DIRMNGR }, + + { "LDAP", + GC_OPT_FLAG_GROUP, GC_LEVEL_BASIC, + "gnupg", N_("Configuration of LDAP servers to use") }, + { "disable-ldap", GC_OPT_FLAG_NONE, GC_LEVEL_ADVANCED, + "dirmngr", "inhibit the use of LDAP", + GC_ARG_TYPE_NONE, GC_BACKEND_DIRMNGR }, + { "ignore-ldap-dp", GC_OPT_FLAG_NONE, GC_LEVEL_ADVANCED, + "dirmngr", "ignore LDAP CRL distribution points", + GC_ARG_TYPE_NONE, GC_BACKEND_DIRMNGR }, + { "ldap-proxy", GC_OPT_FLAG_NONE, GC_LEVEL_BASIC, + "dirmngr", "|HOST|use HOST for LDAP queries", + GC_ARG_TYPE_STRING, GC_BACKEND_DIRMNGR }, + { "only-ldap-proxy", GC_OPT_FLAG_NONE, GC_LEVEL_ADVANCED, + "dirmngr", "do not use fallback hosts with --ldap-proxy", + GC_ARG_TYPE_NONE, GC_BACKEND_DIRMNGR }, + { "add-servers", GC_OPT_FLAG_NONE, GC_LEVEL_ADVANCED, + "dirmngr", "add new servers discovered in CRL distribution points" + " to serverlist", GC_ARG_TYPE_NONE, GC_BACKEND_DIRMNGR }, + { "ldaptimeout", GC_OPT_FLAG_NONE, GC_LEVEL_BASIC, + "dirmngr", "|N|set LDAP timeout to N seconds", + GC_ARG_TYPE_UINT32, GC_BACKEND_DIRMNGR }, + /* The following entry must not be removed, as it is required for + the GC_BACKEND_DIRMNGR_LDAP_SERVER_LIST. */ + { "ldapserverlist-file", + GC_OPT_FLAG_NONE, GC_LEVEL_INTERNAL, + "dirmngr", "|FILE|read LDAP server list from FILE", + GC_ARG_TYPE_PATHNAME, GC_BACKEND_DIRMNGR }, + /* This entry must come after at least one entry for + GC_BACKEND_DIRMNGR in this component, so that the entry for + "ldapserverlist-file will be initialized before this one. */ + { "LDAP Server", GC_OPT_FLAG_ARG_OPT|GC_OPT_FLAG_LIST, GC_LEVEL_BASIC, + NULL, "LDAP server list", + GC_ARG_TYPE_LDAP_SERVER, GC_BACKEND_DIRMNGR_LDAP_SERVER_LIST }, + { "max-replies", GC_OPT_FLAG_NONE, GC_LEVEL_BASIC, + "dirmngr", "|N|do not return more than N items in one query", + GC_ARG_TYPE_UINT32, GC_BACKEND_DIRMNGR }, + + { "OCSP", + GC_OPT_FLAG_GROUP, GC_LEVEL_ADVANCED, + "gnupg", N_("Configuration for OCSP") }, + { "allow-ocsp", GC_OPT_FLAG_NONE, GC_LEVEL_BASIC, + "dirmngr", "allow sending OCSP requests", + GC_ARG_TYPE_NONE, GC_BACKEND_DIRMNGR }, + { "ignore-ocsp-service-url", GC_OPT_FLAG_NONE, GC_LEVEL_ADVANCED, + "dirmngr", "ignore certificate contained OCSP service URLs", + GC_ARG_TYPE_NONE, GC_BACKEND_DIRMNGR }, + { "ocsp-responder", GC_OPT_FLAG_NONE, GC_LEVEL_ADVANCED, + "dirmngr", "|URL|use OCSP responder at URL", + GC_ARG_TYPE_STRING, GC_BACKEND_DIRMNGR }, + { "ocsp-signer", GC_OPT_FLAG_NONE, GC_LEVEL_ADVANCED, + "dirmngr", "|FPR|OCSP response signed by FPR", + GC_ARG_TYPE_STRING, GC_BACKEND_DIRMNGR }, + + + GC_OPTION_NULL + }; + + +/* Component system. Each component is a set of options that can be + configured at the same time. If you change this, don't forget to + update GC_COMPONENT below. */ +typedef enum + { + /* The classic GPG for OpenPGP. */ + GC_COMPONENT_GPG, + + /* The GPG Agent. */ + GC_COMPONENT_GPG_AGENT, + + /* The Smardcard Daemon. */ + GC_COMPONENT_SCDAEMON, + + /* GPG for S/MIME. */ + GC_COMPONENT_GPGSM, + + /* The LDAP Directory Manager for CRLs. */ + GC_COMPONENT_DIRMNGR, + + /* The number of components. */ + GC_COMPONENT_NR + } gc_component_t; + + +/* The information associated with each component. */ +static struct +{ + /* The name of this component. Must not contain a colon (':') + character. */ + const char *name; + + /* The gettext domain for the description DESC. If this is NULL, + then the description is not translated. */ + const char *desc_domain; + + /* The description for this domain. */ + const char *desc; + + /* The list of options for this component, terminated by + GC_OPTION_NULL. */ + gc_option_t *options; +} gc_component[] = + { + { "gpg", NULL, "GPG for OpenPGP", gc_options_gpg }, + { "gpg-agent", NULL, "GPG Agent", gc_options_gpg_agent }, + { "scdaemon", NULL, "Smartcard Daemon", gc_options_scdaemon }, + { "gpgsm", NULL, "GPG for S/MIME", gc_options_gpgsm }, + { "dirmngr", NULL, "Directory Manager", gc_options_dirmngr } + }; + + +/* Engine specific support. */ +void +gpg_agent_runtime_change (void) +{ +#ifndef HAVE_W32_SYSTEM + char *agent = getenv ("GPG_AGENT_INFO"); + char *pid_str; + unsigned long pid_long; + char *tail; + pid_t pid; + + if (!agent) + return; + + pid_str = strchr (agent, ':'); + if (!pid_str) + return; + + pid_str++; + errno = 0; + pid_long = strtoul (pid_str, &tail, 0); + if (errno || (*tail != ':' && *tail != '\0')) + return; + + pid = (pid_t) pid_long; + + /* Check for overflow. */ + if (pid_long != (unsigned long) pid) + return; + + /* Ignore any errors here. */ + kill (pid, SIGHUP); +#endif /*!HAVE_W32_SYSTEM*/ +} + + +/* More or less Robust version of dgettext. It has the side effect of + switching the codeset to utf-8 because this is what we want to + output. In theory it is posible to keep the orginal code set and + switch back for regular disgnostic output (redefine "_(" for that) + but given the natur of this tool, being something invoked from + other pograms, it does not make much sense. */ +static const char * +my_dgettext (const char *domain, const char *msgid) +{ +#ifdef ENABLE_NLS + if (domain) + { + static int switched_codeset; + char *text; + + if (!switched_codeset) + { + switched_codeset = 1; + bind_textdomain_codeset (PACKAGE_GT, "utf-8"); + + bindtextdomain ("dirmngr", LOCALEDIR); + bind_textdomain_codeset ("dirmngr", "utf-8"); + + } + + /* Note: This is a hack to actually use the gnupg2 domain as + long we are in a transition phase where gnupg 1.x and 1.9 may + coexist. */ + if (!strcmp (domain, "gnupg")) + domain = PACKAGE_GT; + + text = dgettext (domain, msgid); + return text ? text : msgid; + } + else +#endif + return msgid; +} + + +/* Percent-Escape special characters. The string is valid until the + next invocation of the function. */ +static char * +percent_escape (const char *src) +{ + static char *esc_str; + static int esc_str_len; + int new_len = 3 * strlen (src) + 1; + char *dst; + + if (esc_str_len < new_len) + { + char *new_esc_str = realloc (esc_str, new_len); + if (!new_esc_str) + gc_error (1, errno, "can not escape string"); + esc_str = new_esc_str; + esc_str_len = new_len; + } + + dst = esc_str; + while (*src) + { + if (*src == '%') + { + *(dst++) = '%'; + *(dst++) = '2'; + *(dst++) = '5'; + } + else if (*src == ':') + { + /* The colon is used as field separator. */ + *(dst++) = '%'; + *(dst++) = '3'; + *(dst++) = 'a'; + } + else if (*src == ',') + { + /* The comma is used as list separator. */ + *(dst++) = '%'; + *(dst++) = '2'; + *(dst++) = 'c'; + } + else + *(dst++) = *(src); + src++; + } + *dst = '\0'; + return esc_str; +} + + + +/* Percent-Deescape special characters. The string is valid until the + next invocation of the function. */ +static char * +percent_deescape (const char *src) +{ + static char *str; + static int str_len; + int new_len = 3 * strlen (src) + 1; + char *dst; + + if (str_len < new_len) + { + char *new_str = realloc (str, new_len); + if (!new_str) + gc_error (1, errno, "can not deescape string"); + str = new_str; + str_len = new_len; + } + + dst = str; + while (*src) + { + if (*src == '%') + { + int val = hextobyte (src + 1); + + if (val < 0) + gc_error (1, 0, "malformed end of string %s", src); + + *(dst++) = (char) val; + src += 3; + } + else + *(dst++) = *(src++); + } + *dst = '\0'; + return str; +} + + +/* List all components that are available. */ +void +gc_component_list_components (FILE *out) +{ + gc_component_t idx; + + for (idx = 0; idx < GC_COMPONENT_NR; idx++) + { + const char *desc = gc_component[idx].desc; + desc = my_dgettext (gc_component[idx].desc_domain, desc); + fprintf (out, "%s:%s\n", gc_component[idx].name, percent_escape (desc)); + } +} + + +/* Find the component with the name NAME. Returns -1 if not + found. */ +int +gc_component_find (const char *name) +{ + gc_component_t idx; + + for (idx = 0; idx < GC_COMPONENT_NR; idx++) + { + if (!strcmp (name, gc_component[idx].name)) + return idx; + } + return -1; +} + + +/* List the option OPTION. */ +static void +list_one_option (const gc_option_t *option, FILE *out) +{ + const char *desc = NULL; + char *arg_name = NULL; + + if (option->desc) + { + desc = my_dgettext (option->desc_domain, option->desc); + + if (*desc == '|') + { + const char *arg_tail = strchr (&desc[1], '|'); + + if (arg_tail) + { + int arg_len = arg_tail - &desc[1]; + arg_name = xmalloc (arg_len + 1); + memcpy (arg_name, &desc[1], arg_len); + arg_name[arg_len] = '\0'; + desc = arg_tail + 1; + } + } + } + + + /* YOU MUST NOT REORDER THE FIELDS IN THIS OUTPUT, AS THEIR ORDER IS + PART OF THE EXTERNAL INTERFACE. YOU MUST NOT REMOVE ANY + FIELDS. */ + + /* The name field. */ + fprintf (out, "%s", option->name); + + /* The flags field. */ + fprintf (out, ":%lu", option->flags); + if (opt.verbose) + { + putc (' ', out); + + if (!option->flags) + fprintf (out, "none"); + else + { + unsigned long flags = option->flags; + unsigned long flag = 0; + unsigned long first = 1; + + while (flags) + { + if (flags & 1) + { + if (first) + first = 0; + else + putc (',', out); + fprintf (out, "%s", gc_flag[flag].name); + } + flags >>= 1; + flag++; + } + } + } + + /* The level field. */ + fprintf (out, ":%u", option->level); + if (opt.verbose) + fprintf (out, " %s", gc_level[option->level].name); + + /* The description field. */ + fprintf (out, ":%s", desc ? percent_escape (desc) : ""); + + /* The type field. */ + fprintf (out, ":%u", option->arg_type); + if (opt.verbose) + fprintf (out, " %s", gc_arg_type[option->arg_type].name); + + /* The alternate type field. */ + fprintf (out, ":%u", gc_arg_type[option->arg_type].fallback); + if (opt.verbose) + fprintf (out, " %s", + gc_arg_type[gc_arg_type[option->arg_type].fallback].name); + + /* The argument name field. */ + fprintf (out, ":%s", arg_name ? percent_escape (arg_name) : ""); + if (arg_name) + xfree (arg_name); + + /* The default value field. */ + fprintf (out, ":%s", option->default_value ? option->default_value : ""); + + /* The default argument field. */ + fprintf (out, ":%s", option->default_arg ? option->default_arg : ""); + + /* The value field. */ + if (gc_arg_type[option->arg_type].fallback == GC_ARG_TYPE_NONE + && (option->flags & GC_OPT_FLAG_LIST) + && option->value) + /* The special format "1,1,1,1,...,1" is converted to a number + here. */ + fprintf (out, ":%u", (strlen (option->value) + 1) / 2); + else + fprintf (out, ":%s", option->value ? option->value : ""); + + /* ADD NEW FIELDS HERE. */ + + putc ('\n', out); +} + + +/* List all options of the component COMPONENT. */ +void +gc_component_list_options (int component, FILE *out) +{ + const gc_option_t *option = gc_component[component].options; + const gc_option_t *group_option = NULL; + + while (option->name) + { + /* Do not output unknown or internal options. */ + if (!(option->flags & GC_OPT_FLAG_GROUP) + && (!option->active || option->level == GC_LEVEL_INTERNAL)) + { + option++; + continue; + } + + if (option->flags & GC_OPT_FLAG_GROUP) + group_option = option; + else + { + if (group_option) + { + list_one_option (group_option, out); + group_option = NULL; + } + + list_one_option (option, out); + } + + option++; + } +} + + +/* Find the option NAME in component COMPONENT, for the backend + BACKEND. If BACKEND is GC_BACKEND_ANY, any backend will match. */ +static gc_option_t * +find_option (gc_component_t component, const char *name, + gc_backend_t backend) +{ + gc_option_t *option = gc_component[component].options; + while (option->name) + { + if (!(option->flags & GC_OPT_FLAG_GROUP) + && !strcmp (option->name, name) + && (backend == GC_BACKEND_ANY || option->backend == backend)) + break; + option++; + } + return option->name ? option : NULL; +} + + +/* Determine the configuration pathname for the component COMPONENT + and backend BACKEND. */ +static char * +get_config_pathname (gc_component_t component, gc_backend_t backend) +{ + char *pathname = NULL; + gc_option_t *option = find_option + (component, gc_backend[backend].option_config_filename, GC_BACKEND_ANY); + assert (option); + assert (option->arg_type == GC_ARG_TYPE_PATHNAME); + assert (!(option->flags & GC_OPT_FLAG_LIST)); + + if (!option->active || !option->default_value) + gc_error (1, 0, "Option %s, needed by backend %s, was not initialized", + gc_backend[backend].option_config_filename, + gc_backend[backend].name); + + if (option->value && *option->value) + pathname = percent_deescape (&option->value[1]); + else if (option->default_value && *option->default_value) + pathname = percent_deescape (&option->default_value[1]); + else + pathname = ""; + +#ifdef HAVE_DOSISH_SYSTEM + if (!(pathname[0] + && pathname[1] == ':' + && (pathname[2] == '/' || pathname[2] == '\\'))) +#else + if (pathname[0] != '/') +#endif + gc_error (1, 0, "Option %s, needed by backend %s, is not absolute", + gc_backend[backend].option_config_filename, + gc_backend[backend].name); + + return pathname; +} + + +/* Retrieve the options for the component COMPONENT from backend + BACKEND, which we already know is a program-type backend. */ +static void +retrieve_options_from_program (gc_component_t component, gc_backend_t backend) +{ + char *cmd_line; + char *line = NULL; + size_t line_len = 0; + ssize_t length; + FILE *config; + char *config_pathname; + + cmd_line = xasprintf ("%s --gpgconf-list", gc_backend[backend].program); + + config = popen (cmd_line, "r"); + if (!config) + gc_error (1, errno, "could not gather active options from %s", cmd_line); + + while ((length = read_line (config, &line, &line_len, NULL)) > 0) + { + gc_option_t *option; + char *linep; + unsigned long flags = 0; + char *default_value = NULL; + + /* Strip newline and carriage return, if present. */ + while (length > 0 + && (line[length - 1] == '\n' || line[length - 1] == '\r')) + line[--length] = '\0'; + + linep = strchr (line, ':'); + if (linep) + *(linep++) = '\0'; + + /* Extract additional flags. Default to none. */ + if (linep) + { + char *end; + char *tail; + + end = strchr (linep, ':'); + if (end) + *(end++) = '\0'; + + errno = 0; + flags = strtoul (linep, &tail, 0); + if (errno) + gc_error (1, errno, "malformed flags in option %s from %s", line, cmd_line); + if (!(*tail == '\0' || *tail == ':' || *tail == ' ')) + gc_error (1, 0, "garbage after flags in option %s from %s", line, cmd_line); + + linep = end; + } + + /* Extract default value, if present. Default to empty if + not. */ + if (linep) + { + char *end; + + end = strchr (linep, ':'); + if (end) + *(end++) = '\0'; + + if (flags & GC_OPT_FLAG_DEFAULT) + default_value = linep; + + linep = end; + } + + /* Look up the option in the component and install the + configuration data. */ + option = find_option (component, line, backend); + if (option) + { + if (option->active) + gc_error (1, errno, "option %s returned twice from %s", + line, cmd_line); + option->active = 1; + + option->flags |= flags; + if (default_value && *default_value) + option->default_value = xstrdup (default_value); + } + } + if (length < 0 || ferror (config)) + gc_error (1, errno, "error reading from %s", cmd_line); + if (fclose (config) && ferror (config)) + gc_error (1, errno, "error closing %s", cmd_line); + xfree (cmd_line); + + /* At this point, we can parse the configuration file. */ + config_pathname = get_config_pathname (component, backend); + + config = fopen (config_pathname, "r"); + if (!config) + gc_error (0, errno, "warning: can not open config file %s", + config_pathname); + else + { + while ((length = read_line (config, &line, &line_len, NULL)) > 0) + { + char *name; + char *value; + gc_option_t *option; + + name = line; + while (*name == ' ' || *name == '\t') + name++; + if (!*name || *name == '#' || *name == '\r' || *name == '\n') + continue; + + value = name; + while (*value && *value != ' ' && *value != '\t' + && *value != '#' && *value != '\r' && *value != '\n') + value++; + if (*value == ' ' || *value == '\t') + { + char *end; + + *(value++) = '\0'; + while (*value == ' ' || *value == '\t') + value++; + + end = value; + while (*end && *end != '#' && *end != '\r' && *end != '\n') + end++; + while (end > value && (end[-1] == ' ' || end[-1] == '\t')) + end--; + *end = '\0'; + } + else + *value = '\0'; + + /* Look up the option in the component and install the + configuration data. */ + option = find_option (component, line, backend); + if (option) + { + char *opt_value; + + if (gc_arg_type[option->arg_type].fallback == GC_ARG_TYPE_NONE) + { + if (*value) + gc_error (0, 0, + "warning: ignoring argument %s for option %s", + value, name); + opt_value = xstrdup ("1"); + } + else if (gc_arg_type[option->arg_type].fallback + == GC_ARG_TYPE_STRING) + opt_value = xasprintf ("\"%s", percent_escape (value)); + else + { + /* FIXME: Verify that the number is sane. */ + opt_value = xstrdup (value); + } + + /* Now enter the option into the table. */ + if (!(option->flags & GC_OPT_FLAG_LIST)) + { + if (option->value) + free (option->value); + option->value = opt_value; + } + else + { + if (!option->value) + option->value = opt_value; + else + { + char *opt_val = opt_value; + + option->value = xasprintf ("%s,%s", option->value, + opt_val); + xfree (opt_value); + } + } + } + } + + if (length < 0 || ferror (config)) + gc_error (1, errno, "error reading from %s", config_pathname); + if (fclose (config) && ferror (config)) + gc_error (1, errno, "error closing %s", config_pathname); + } + + xfree (line); +} + + +/* Retrieve the options for the component COMPONENT from backend + BACKEND, which we already know is of type file list. */ +static void +retrieve_options_from_file (gc_component_t component, gc_backend_t backend) +{ + gc_option_t *list_option; + char *list_pathname; + FILE *list_file; + char *line = NULL; + size_t line_len = 0; + ssize_t length; + char *list = NULL; + + list_option = find_option (component, + gc_backend[backend].option_name, GC_BACKEND_ANY); + assert (list_option); + assert (!list_option->active); + + list_pathname = get_config_pathname (component, backend); + list_file = fopen (list_pathname, "r"); + if (!list_file) + gc_error (0, errno, "warning: can not open list file %s", list_pathname); + else + { + + while ((length = read_line (list_file, &line, &line_len, NULL)) > 0) + { + char *start; + char *end; + char *new_list; + + start = line; + while (*start == ' ' || *start == '\t') + start++; + if (!*start || *start == '#' || *start == '\r' || *start == '\n') + continue; + + end = start; + while (*end && *end != '#' && *end != '\r' && *end != '\n') + end++; + /* Walk back to skip trailing white spaces. Looks evil, but + works because of the conditions on START and END imposed + at this point (END is at least START + 1, and START is + not a whitespace character). */ + while (*(end - 1) == ' ' || *(end - 1) == '\t') + end--; + *end = '\0'; + /* FIXME: Oh, no! This is so lame! Should use realloc and + really append. */ + if (list) + { + new_list = xasprintf ("%s,\"%s", list, percent_escape (start)); + xfree (list); + list = new_list; + } + else + list = xasprintf ("\"%s", percent_escape (start)); + } + if (length < 0 || ferror (list_file)) + gc_error (1, errno, "can not read list file %s", list_pathname); + } + + list_option->active = 1; + list_option->value = list; + + xfree (line); +} + + +/* Retrieve the currently active options and their defaults from all + involved backends for this component. */ +void +gc_component_retrieve_options (int component) +{ + int backend_seen[GC_BACKEND_NR]; + gc_backend_t backend; + gc_option_t *option = gc_component[component].options; + + for (backend = 0; backend < GC_BACKEND_NR; backend++) + backend_seen[backend] = 0; + + while (option->name) + { + if (!(option->flags & GC_OPT_FLAG_GROUP)) + { + backend = option->backend; + + if (backend_seen[backend]) + { + option++; + continue; + } + backend_seen[backend] = 1; + + assert (backend != GC_BACKEND_ANY); + + if (gc_backend[backend].program) + retrieve_options_from_program (component, backend); + else + retrieve_options_from_file (component, backend); + } + option++; + } +} + + +/* Perform a simple validity check based on the type. Return in + NEW_VALUE_NR the value of the number in NEW_VALUE if OPTION is of + type GC_ARG_TYPE_NONE. */ +static void +option_check_validity (gc_option_t *option, unsigned long flags, + char *new_value, unsigned long *new_value_nr) +{ + char *arg; + + if (!option->active) + gc_error (1, 0, "option %s not supported by backend", option->name); + + if (option->new_flags || option->new_value) + gc_error (1, 0, "option %s already changed", option->name); + + if (flags & GC_OPT_FLAG_DEFAULT) + { + if (*new_value) + gc_error (1, 0, "argument %s provided for deleted option %s", + new_value, option->name); + + return; + } + + /* GC_ARG_TYPE_NONE options have special list treatment. */ + if (gc_arg_type[option->arg_type].fallback == GC_ARG_TYPE_NONE) + { + char *tail; + + errno = 0; + *new_value_nr = strtoul (new_value, &tail, 0); + + if (errno) + gc_error (1, errno, "invalid argument for option %s", + option->name); + if (*tail) + gc_error (1, 0, "garbage after argument for option %s", + option->name); + + if (!(option->flags & GC_OPT_FLAG_LIST)) + { + if (*new_value_nr != 1) + gc_error (1, 0, "argument for non-list option %s of type 0 " + "(none) must be 1", option->name); + } + else + { + if (*new_value_nr == 0) + gc_error (1, 0, "argument for option %s of type 0 (none) " + "must be positive", option->name); + } + + return; + } + + arg = new_value; + do + { + if (*arg == '\0' || *arg == ',') + { + if (!(option->flags & GC_OPT_FLAG_ARG_OPT)) + gc_error (1, 0, "argument required for option %s", option->name); + + if (*arg == ',' && !(option->flags & GC_OPT_FLAG_LIST)) + gc_error (1, 0, "list found for non-list option %s", option->name); + } + else if (gc_arg_type[option->arg_type].fallback == GC_ARG_TYPE_STRING) + { + if (*arg != '"') + gc_error (1, 0, "string argument for option %s must begin " + "with a quote (\") character", option->name); + } + else if (gc_arg_type[option->arg_type].fallback == GC_ARG_TYPE_INT32) + { + errno = 0; + (void) strtol (arg, &arg, 0); + + if (errno) + gc_error (1, errno, "invalid argument for option %s", + option->name); + + if (*arg != '\0' && *arg != ',') + gc_error (1, 0, "garbage after argument for option %s", + option->name); + } + else if (gc_arg_type[option->arg_type].fallback == GC_ARG_TYPE_INT32) + { + errno = 0; + (void) strtoul (arg, &arg, 0); + + if (errno) + gc_error (1, errno, "invalid argument for option %s", + option->name); + + if (*arg != '\0' && *arg != ',') + gc_error (1, 0, "garbage after argument for option %s", + option->name); + } + arg = strchr (arg, ','); + if (arg) + arg++; + } + while (arg && *arg); +} + + +/* Create and verify the new configuration file for the specified + backend and component. Returns 0 on success and -1 on error. */ +static int +change_options_file (gc_component_t component, gc_backend_t backend, + char **src_filenamep, char **dest_filenamep, + char **orig_filenamep) +{ + static const char marker[] = "###+++--- GPGConf ---+++###"; + /* True if we are within the marker in the config file. */ + int in_marker = 0; + gc_option_t *option; + char *line = NULL; + size_t line_len; + ssize_t length; + int res; + int fd; + FILE *src_file = NULL; + FILE *dest_file = NULL; + char *src_filename; + char *dest_filename; + char *orig_filename; + char *arg; + char *cur_arg = NULL; + + option = find_option (component, + gc_backend[backend].option_name, GC_BACKEND_ANY); + assert (option); + assert (option->active); + assert (gc_arg_type[option->arg_type].fallback != GC_ARG_TYPE_NONE); + + /* FIXME. Throughout the function, do better error reporting. */ + /* Note that get_config_pathname() calls percent_deescape(), so we + call this before processing the arguments. */ + dest_filename = xstrdup (get_config_pathname (component, backend)); + src_filename = xasprintf ("%s.gpgconf.%i.new", dest_filename, getpid ()); + orig_filename = xasprintf ("%s.gpgconf.%i.bak", dest_filename, getpid ()); + + arg = option->new_value; + if (arg && arg[0] == '\0') + arg = NULL; + else if (arg) + { + char *end; + + arg++; + end = strchr (arg, ','); + if (end) + *end = '\0'; + + cur_arg = percent_deescape (arg); + if (end) + { + *end = ','; + arg = end + 1; + } + else + arg = NULL; + } + +#if HAVE_W32_SYSTEM + res = 0; +#warning no backups for W32 yet - need to write a copy function +#else + res = link (dest_filename, orig_filename); +#endif + if (res < 0 && errno != ENOENT) + return -1; + if (res < 0) + { + xfree (orig_filename); + orig_filename = NULL; + } + + /* We now initialize the return strings, so the caller can do the + cleanup for us. */ + *src_filenamep = src_filename; + *dest_filenamep = dest_filename; + *orig_filenamep = orig_filename; + + /* Use open() so that we can use O_EXCL. */ + fd = open (src_filename, O_CREAT | O_EXCL | O_WRONLY, 0644); + if (fd < 0) + return -1; + src_file = fdopen (fd, "w"); + res = errno; + if (!src_file) + { + errno = res; + return -1; + } + + /* Only if ORIG_FILENAME is not NULL did the configuration file + exist already. In this case, we will copy its content into the + new configuration file, changing it to our liking in the + process. */ + if (orig_filename) + { + dest_file = fopen (dest_filename, "r"); + if (!dest_file) + goto change_file_one_err; + + while ((length = read_line (dest_file, &line, &line_len, NULL)) > 0) + { + int disable = 0; + char *start; + + if (!strncmp (marker, line, sizeof (marker) - 1)) + { + if (!in_marker) + in_marker = 1; + else + break; + } + + start = line; + while (*start == ' ' || *start == '\t') + start++; + if (*start && *start != '\r' && *start != '\n' && *start != '#') + { + char *end; + char *endp; + char saved_end; + + endp = start; + end = endp; + + /* Search for the end of the line. */ + while (*endp && *endp != '#' && *endp != '\r' && *endp != '\n') + { + endp++; + if (*endp && *endp != ' ' && *endp != '\t' + && *endp != '\r' && *endp != '\n' && *endp != '#') + end = endp + 1; + } + saved_end = *end; + *end = '\0'; + + if ((option->new_flags & GC_OPT_FLAG_DEFAULT) + || !cur_arg || strcmp (start, cur_arg)) + disable = 1; + else + { + /* Find next argument. */ + if (arg) + { + char *arg_end; + + arg++; + arg_end = strchr (arg, ','); + if (arg_end) + *arg_end = '\0'; + + cur_arg = percent_deescape (arg); + if (arg_end) + { + *arg_end = ','; + arg = arg_end + 1; + } + else + arg = NULL; + } + else + cur_arg = NULL; + } + + *end = saved_end; + } + + if (disable) + { + if (!in_marker) + { + fprintf (src_file, + "# GPGConf disabled this option here at %s\n", + asctimestamp (gnupg_get_time ())); + if (ferror (src_file)) + goto change_file_one_err; + fprintf (src_file, "# %s", line); + if (ferror (src_file)) + goto change_file_one_err; + } + } + else + { + fprintf (src_file, "%s", line); + if (ferror (src_file)) + goto change_file_one_err; + } + } + if (length < 0 || ferror (dest_file)) + goto change_file_one_err; + } + + if (!in_marker) + { + /* There was no marker. This is the first time we edit the + file. We add our own marker at the end of the file and + proceed. Note that we first write a newline, this guards us + against files which lack the newline at the end of the last + line, while it doesn't hurt us in all other cases. */ + fprintf (src_file, "\n%s\n", marker); + if (ferror (src_file)) + goto change_file_one_err; + } + + /* At this point, we have copied everything up to the end marker + into the new file, except for the arguments we are going to add. + Now, dump the new arguments and write the end marker, possibly + followed by the rest of the original file. */ + while (cur_arg) + { + fprintf (src_file, "%s\n", cur_arg); + + /* Find next argument. */ + if (arg) + { + char *end; + + arg++; + end = strchr (arg, ','); + if (end) + *end = '\0'; + + cur_arg = percent_deescape (arg); + if (end) + { + *end = ','; + arg = end + 1; + } + else + arg = NULL; + } + else + cur_arg = NULL; + } + + fprintf (src_file, "%s %s\n", marker, asctimestamp (gnupg_get_time ())); + if (ferror (src_file)) + goto change_file_one_err; + + if (!in_marker) + { + fprintf (src_file, "# GPGConf edited this configuration file.\n"); + if (ferror (src_file)) + goto change_file_one_err; + fprintf (src_file, "# It will disable options before this marked " + "block, but it will\n"); + if (ferror (src_file)) + goto change_file_one_err; + fprintf (src_file, "# never change anything below these lines.\n"); + if (ferror (src_file)) + goto change_file_one_err; + } + if (dest_file) + { + while ((length = read_line (dest_file, &line, &line_len, NULL)) > 0) + { + fprintf (src_file, "%s", line); + if (ferror (src_file)) + goto change_file_one_err; + } + if (length < 0 || ferror (dest_file)) + goto change_file_one_err; + } + xfree (line); + line = NULL; + + res = fclose (src_file); + if (res) + { + res = errno; + close (fd); + if (dest_file) + fclose (dest_file); + errno = res; + return -1; + } + close (fd); + if (dest_file) + { + res = fclose (dest_file); + if (res) + return -1; + } + return 0; + + change_file_one_err: + xfree (line); + res = errno; + if (src_file) + { + fclose (src_file); + close (fd); + } + if (dest_file) + fclose (dest_file); + errno = res; + return -1; +} + + +/* Create and verify the new configuration file for the specified + backend and component. Returns 0 on success and -1 on error. */ +static int +change_options_program (gc_component_t component, gc_backend_t backend, + char **src_filenamep, char **dest_filenamep, + char **orig_filenamep) +{ + static const char marker[] = "###+++--- GPGConf ---+++###"; + /* True if we are within the marker in the config file. */ + int in_marker = 0; + gc_option_t *option; + char *line = NULL; + size_t line_len; + ssize_t length; + int res; + int fd; + FILE *src_file = NULL; + FILE *dest_file = NULL; + char *src_filename; + char *dest_filename; + char *orig_filename; + + /* FIXME. Throughout the function, do better error reporting. */ + dest_filename = xstrdup (get_config_pathname (component, backend)); + src_filename = xasprintf ("%s.gpgconf.%i.new", dest_filename, getpid ()); + orig_filename = xasprintf ("%s.gpgconf.%i.bak", dest_filename, getpid ()); + +#if HAVE_W32_SYSTEM + res = 0; +#warning no backups for W32 yet - need to write a copy function +#else + res = link (dest_filename, orig_filename); +#endif + if (res < 0 && errno != ENOENT) + return -1; + if (res < 0) + { + xfree (orig_filename); + orig_filename = NULL; + } + + /* We now initialize the return strings, so the caller can do the + cleanup for us. */ + *src_filenamep = src_filename; + *dest_filenamep = dest_filename; + *orig_filenamep = orig_filename; + + /* Use open() so that we can use O_EXCL. */ + fd = open (src_filename, O_CREAT | O_EXCL | O_WRONLY, 0644); + if (fd < 0) + return -1; + src_file = fdopen (fd, "w"); + res = errno; + if (!src_file) + { + errno = res; + return -1; + } + + /* Only if ORIG_FILENAME is not NULL did the configuration file + exist already. In this case, we will copy its content into the + new configuration file, changing it to our liking in the + process. */ + if (orig_filename) + { + dest_file = fopen (dest_filename, "r"); + if (!dest_file) + goto change_one_err; + + while ((length = read_line (dest_file, &line, &line_len, NULL)) > 0) + { + int disable = 0; + char *start; + + if (!strncmp (marker, line, sizeof (marker) - 1)) + { + if (!in_marker) + in_marker = 1; + else + break; + } + + start = line; + while (*start == ' ' || *start == '\t') + start++; + if (*start && *start != '\r' && *start != '\n' && *start != '#') + { + char *end; + char saved_end; + + end = start; + while (*end && *end != ' ' && *end != '\t' + && *end != '\r' && *end != '\n' && *end != '#') + end++; + saved_end = *end; + *end = '\0'; + + option = find_option (component, start, backend); + *end = saved_end; + if (option && ((option->new_flags & GC_OPT_FLAG_DEFAULT) + || option->new_value)) + disable = 1; + } + if (disable) + { + if (!in_marker) + { + fprintf (src_file, + "# GPGConf disabled this option here at %s\n", + asctimestamp (gnupg_get_time ())); + if (ferror (src_file)) + goto change_one_err; + fprintf (src_file, "# %s", line); + if (ferror (src_file)) + goto change_one_err; + } + } + else + { + fprintf (src_file, "%s", line); + if (ferror (src_file)) + goto change_one_err; + } + } + if (length < 0 || ferror (dest_file)) + goto change_one_err; + } + + if (!in_marker) + { + /* There was no marker. This is the first time we edit the + file. We add our own marker at the end of the file and + proceed. Note that we first write a newline, this guards us + against files which lack the newline at the end of the last + line, while it doesn't hurt us in all other cases. */ + fprintf (src_file, "\n%s\n", marker); + if (ferror (src_file)) + goto change_one_err; + } + /* At this point, we have copied everything up to the end marker + into the new file, except for the options we are going to change. + Now, dump the changed options (except for those we are going to + revert to their default), and write the end marker, possibly + followed by the rest of the original file. */ + + /* We have to turn on UTF8 strings for GnuPG. */ + if (backend == GC_BACKEND_GPG) + fprintf (src_file, "utf8-strings\n"); + + option = gc_component[component].options; + while (option->name) + { + if (!(option->flags & GC_OPT_FLAG_GROUP) + && option->backend == backend + && option->new_value) + { + char *arg = option->new_value; + + do + { + if (*arg == '\0' || *arg == ',') + { + fprintf (src_file, "%s\n", option->name); + if (ferror (src_file)) + goto change_one_err; + } + else if (gc_arg_type[option->arg_type].fallback + == GC_ARG_TYPE_NONE) + { + assert (*arg == '1'); + fprintf (src_file, "%s\n", option->name); + if (ferror (src_file)) + goto change_one_err; + + arg++; + } + else if (gc_arg_type[option->arg_type].fallback + == GC_ARG_TYPE_STRING) + { + char *end; + + assert (*arg == '"'); + arg++; + + end = strchr (arg, ','); + if (end) + *end = '\0'; + + fprintf (src_file, "%s %s\n", option->name, + percent_deescape (arg)); + if (ferror (src_file)) + goto change_one_err; + + if (end) + *end = ','; + arg = end; + } + else + { + char *end; + + end = strchr (arg, ','); + if (end) + *end = '\0'; + + fprintf (src_file, "%s %s\n", option->name, arg); + if (ferror (src_file)) + goto change_one_err; + + if (end) + *end = ','; + arg = end; + } + + assert (arg == NULL || *arg == '\0' || *arg == ','); + if (arg && *arg == ',') + arg++; + } + while (arg && *arg); + } + option++; + } + + fprintf (src_file, "%s %s\n", marker, asctimestamp (gnupg_get_time ())); + if (ferror (src_file)) + goto change_one_err; + + if (!in_marker) + { + fprintf (src_file, "# GPGConf edited this configuration file.\n"); + if (ferror (src_file)) + goto change_one_err; + fprintf (src_file, "# It will disable options before this marked " + "block, but it will\n"); + if (ferror (src_file)) + goto change_one_err; + fprintf (src_file, "# never change anything below these lines.\n"); + if (ferror (src_file)) + goto change_one_err; + } + if (dest_file) + { + while ((length = read_line (dest_file, &line, &line_len, NULL)) > 0) + { + fprintf (src_file, "%s", line); + if (ferror (src_file)) + goto change_one_err; + } + if (length < 0 || ferror (dest_file)) + goto change_one_err; + } + xfree (line); + line = NULL; + + res = fclose (src_file); + if (res) + { + res = errno; + close (fd); + if (dest_file) + fclose (dest_file); + errno = res; + return -1; + } + close (fd); + if (dest_file) + { + res = fclose (dest_file); + if (res) + return -1; + } + return 0; + + change_one_err: + xfree (line); + res = errno; + if (src_file) + { + fclose (src_file); + close (fd); + } + if (dest_file) + fclose (dest_file); + errno = res; + return -1; +} + + +/* Read the modifications from IN and apply them. */ +void +gc_component_change_options (int component, FILE *in) +{ + int err = 0; + int runtime[GC_BACKEND_NR]; + char *src_pathname[GC_BACKEND_NR]; + char *dest_pathname[GC_BACKEND_NR]; + char *orig_pathname[GC_BACKEND_NR]; + gc_backend_t backend; + gc_option_t *option; + char *line = NULL; + size_t line_len = 0; + ssize_t length; + + for (backend = 0; backend < GC_BACKEND_NR; backend++) + { + runtime[backend] = 0; + src_pathname[backend] = NULL; + dest_pathname[backend] = NULL; + orig_pathname[backend] = NULL; + } + + while ((length = read_line (in, &line, &line_len, NULL)) > 0) + { + char *linep; + unsigned long flags = 0; + char *new_value = ""; + unsigned long new_value_nr = 0; + + /* Strip newline and carriage return, if present. */ + while (length > 0 + && (line[length - 1] == '\n' || line[length - 1] == '\r')) + line[--length] = '\0'; + + linep = strchr (line, ':'); + if (linep) + *(linep++) = '\0'; + + /* Extract additional flags. Default to none. */ + if (linep) + { + char *end; + char *tail; + + end = strchr (linep, ':'); + if (end) + *(end++) = '\0'; + + errno = 0; + flags = strtoul (linep, &tail, 0); + if (errno) + gc_error (1, errno, "malformed flags in option %s", line); + if (!(*tail == '\0' || *tail == ':' || *tail == ' ')) + gc_error (1, 0, "garbage after flags in option %s", line); + + linep = end; + } + + /* Extract default value, if present. Default to empty if + not. */ + if (linep) + { + char *end; + + end = strchr (linep, ':'); + if (end) + *(end++) = '\0'; + + new_value = linep; + + linep = end; + } + + option = find_option (component, line, GC_BACKEND_ANY); + if (!option) + gc_error (1, 0, "unknown option %s", line); + + option_check_validity (option, flags, new_value, &new_value_nr); + + if (option->flags & GC_OPT_FLAG_RUNTIME) + runtime[option->backend] = 1; + + option->new_flags = flags; + if (!(flags & GC_OPT_FLAG_DEFAULT)) + { + if (gc_arg_type[option->arg_type].fallback == GC_ARG_TYPE_NONE + && (option->flags & GC_OPT_FLAG_LIST)) + { + char *str; + + /* We convert the number to a list of 1's for + convenient list handling. */ + assert (new_value_nr > 0); + option->new_value = xmalloc ((2 * (new_value_nr - 1) + 1) + 1); + str = option->new_value; + *(str++) = '1'; + while (--new_value_nr > 0) + { + *(str++) = ','; + *(str++) = '1'; + } + *(str++) = '\0'; + } + else + option->new_value = xstrdup (new_value); + } + } + + /* Now that we have collected and locally verified the changes, + write them out to new configuration files, verify them + externally, and then commit them. */ + option = gc_component[component].options; + while (option->name) + { + /* Go on if we have already seen this backend, or if there is + nothing to do. */ + if (src_pathname[option->backend] + || !(option->new_flags || option->new_value)) + { + option++; + continue; + } + + if (gc_backend[option->backend].program) + err = change_options_program (component, option->backend, + &src_pathname[option->backend], + &dest_pathname[option->backend], + &orig_pathname[option->backend]); + else + err = change_options_file (component, option->backend, + &src_pathname[option->backend], + &dest_pathname[option->backend], + &orig_pathname[option->backend]); + + if (err) + break; + + option++; + } + + if (!err) + { + int i; + + for (i = 0; i < GC_BACKEND_NR; i++) + { + if (src_pathname[i]) + { + /* FIXME: Make a verification here. */ + + assert (dest_pathname[i]); + + if (orig_pathname[i]) + err = rename (src_pathname[i], dest_pathname[i]); + else + { +#ifdef HAVE_W32_SYSTEM + /* FIXME: Won't work becuase W32 doesn't silently + overwrite. Fix it by creating a backup copy and + deliting the orginal file first. */ + err = rename (src_pathname[i], dest_pathname[i]); +#else /*!HAVE_W32_SYSTEM*/ + /* This is a bit safer than rename() because we + expect DEST_PATHNAME not to be there. If it + happens to be there, this will fail. */ + err = link (src_pathname[i], dest_pathname[i]); + if (!err) + unlink (src_pathname[i]); +#endif /*!HAVE_W32_SYSTEM*/ + } + if (err) + break; + src_pathname[i] = NULL; + } + } + } + + if (err) + { + int i; + int saved_errno = errno; + + /* An error occured. */ + for (i = 0; i < GC_BACKEND_NR; i++) + { + if (src_pathname[i]) + { + /* The change was not yet committed. */ + unlink (src_pathname[i]); + if (orig_pathname[i]) + unlink (orig_pathname[i]); + } + else + { + /* The changes were already committed. FIXME: This is a + tad dangerous, as we don't know if we don't overwrite + a version of the file that is even newer than the one + we just installed. */ + if (orig_pathname[i]) + rename (orig_pathname[i], dest_pathname[i]); + else + unlink (dest_pathname[i]); + } + } + gc_error (1, saved_errno, "could not commit changes"); + } + + /* If it all worked, notify the daemons of the changes. */ + if (opt.runtime) + for (backend = 0; backend < GC_BACKEND_NR; backend++) + { + if (runtime[backend] && gc_backend[backend].runtime_change) + (*gc_backend[backend].runtime_change) (); + } + + /* Move the per-process backup file into its place. */ + for (backend = 0; backend < GC_BACKEND_NR; backend++) + if (orig_pathname[backend]) + { + char *backup_pathname; + + assert (dest_pathname[backend]); + + backup_pathname = xasprintf ("%s.gpgconf.bak", dest_pathname[backend]); + rename (orig_pathname[backend], backup_pathname); + } + + xfree (line); +} diff --git a/tools/gpgconf.c b/tools/gpgconf.c new file mode 100644 index 000000000..87ba45ae1 --- /dev/null +++ b/tools/gpgconf.c @@ -0,0 +1,203 @@ +/* gpgconf.c - Configuration utility for GnuPG + * Copyright (C) 2003 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 2 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. + */ + +#include <config.h> +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "gpgconf.h" +#include "i18n.h" + +/* Constants to identify the commands and options. */ +enum cmd_and_opt_values + { + aNull = 0, + oDryRun = 'n', + oOutput = 'o', + oQuiet = 'q', + oVerbose = 'v', + oRuntime = 'r', + oComponent = 'c', + oNoVerbose = 500, + oHomedir, + + aListComponents, + aListOptions, + aChangeOptions, + + }; + + +/* The list of commands and options. */ +static ARGPARSE_OPTS opts[] = + { + { 300, NULL, 0, N_("@Commands:\n ") }, + + { aListComponents, "list-components", 256, N_("list all components") }, + { aListOptions, "list-options", 256, N_("|COMPONENT|list options") }, + { aChangeOptions, "change-options", 256, N_("|COMPONENT|change options") }, + + { 301, NULL, 0, N_("@\nOptions:\n ") }, + + { oOutput, "output", 2, N_("use as output file") }, + { oVerbose, "verbose", 0, N_("verbose") }, + { oQuiet, "quiet", 0, N_("quiet") }, + { oDryRun, "dry-run", 0, N_("do not make any changes") }, + { oRuntime, "runtime", 0, N_("activate changes at runtime, if possible") }, + + /* hidden options */ + { oNoVerbose, "no-verbose", 0, "@"}, + {0} + }; + + +/* Print usage information and and provide strings for help. */ +static const char * +my_strusage( int level ) +{ + const char *p; + + switch (level) + { + case 11: p = "gpgconf (GnuPG)"; + break; + case 13: p = VERSION; break; + case 17: p = PRINTABLE_OS_NAME; break; + case 19: p = _("Please report bugs to <" PACKAGE_BUGREPORT ">.\n"); + break; + case 1: + case 40: p = _("Usage: gpgconf [options] (-h for help)"); + break; + case 41: + p = _("Syntax: gpgconf [options]\n" + "Manage configuration options for tools of the GnuPG system\n"); + break; + + default: p = NULL; break; + } + return p; +} + + +/* Initialize the gettext system. */ +static void +i18n_init(void) +{ +#ifdef USE_SIMPLE_GETTEXT + set_gettext_file (PACKAGE_GT); +#else +# ifdef ENABLE_NLS + setlocale (LC_ALL, "" ); + bindtextdomain (PACKAGE_GT, LOCALEDIR); + textdomain (PACKAGE_GT); +# endif +#endif +} + + +/* gpgconf main. */ +int +main (int argc, char **argv) +{ + ARGPARSE_ARGS pargs; + const char *fname; + int no_more_options = 0; + enum cmd_and_opt_values cmd = 0; + + set_strusage (my_strusage); + log_set_prefix ("gpgconf", 1); + + i18n_init(); + + /* Parse the command line. */ + pargs.argc = &argc; + pargs.argv = &argv; + pargs.flags = 1; /* Do not remove the args. */ + while (!no_more_options && optfile_parse (NULL, NULL, NULL, &pargs, opts)) + { + switch (pargs.r_opt) + { + case oOutput: opt.outfile = pargs.r.ret_str; break; + case oQuiet: opt.quiet = 1; break; + case oDryRun: opt.dry_run = 1; break; + case oRuntime: + opt.runtime = 1; + break; + case oVerbose: opt.verbose++; break; + case oNoVerbose: opt.verbose = 0; break; + + case aListComponents: + case aListOptions: + case aChangeOptions: + cmd = pargs.r_opt; + break; + + default: pargs.err = 2; break; + } + } + + if (log_get_errorcount (0)) + exit (2); + + fname = argc ? *argv : NULL; + + switch (cmd) + { + case aListComponents: + default: + /* List all components. */ + gc_component_list_components (stdout); + break; + + case aListOptions: + case aChangeOptions: + if (!fname) + { + fputs (_("usage: gpgconf [options] "), stderr); + putc ('\n',stderr); + fputs (_("Need one component argument"), stderr); + putc ('\n',stderr); + exit (2); + } + else + { + int idx = gc_component_find (fname); + if (idx < 0) + { + fputs (_("Component not found"), stderr); + putc ('\n', stderr); + exit (1); + } + gc_component_retrieve_options (idx); + if (cmd == aListOptions) + gc_component_list_options (idx, stdout); + else + gc_component_change_options (idx, stdin); + } + } + + return 0; +} + + + diff --git a/tools/gpgconf.h b/tools/gpgconf.h new file mode 100644 index 000000000..c083c26aa --- /dev/null +++ b/tools/gpgconf.h @@ -0,0 +1,59 @@ +/* gpgconf.h - Global definitions for gpgconf + * Copyright (C) 2003 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 2 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. + */ + +#ifndef GPGCONF_H +#define GPGCONF_H + +#include "../common/util.h" + +/* We keep all global options in the structure OPT. */ +struct +{ + int verbose; /* Verbosity level. */ + int quiet; /* Be extra quiet. */ + int dry_run; /* Don't change any persistent data. */ + int runtime; /* Make changes active at runtime. */ + char *outfile; /* Name of output file. */ + + int component; /* The active component. */ +} opt; + + + +/*-- gpgconf-comp.c --*/ +/* List all components that are available. */ +void gc_component_list_components (FILE *out); + +/* Find the component with the name NAME. Returns -1 if not + found. */ +int gc_component_find (const char *name); + +/* Retrieve the currently active options and their defaults from all + involved backends for this component. */ +void gc_component_retrieve_options (int component); + +/* List all options of the component COMPONENT. */ +void gc_component_list_options (int component, FILE *out); + +/* Read the modifications from IN and apply them. */ +void gc_component_change_options (int component, FILE *in); + +#endif /*GPGCONF_H*/ diff --git a/tools/gpgkey2ssh.c b/tools/gpgkey2ssh.c new file mode 100644 index 000000000..3dcb6516e --- /dev/null +++ b/tools/gpgkey2ssh.c @@ -0,0 +1,298 @@ +/* gpgkey2ssh.c - Converter ... + * Copyright (C) 2005 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 2 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. + */ + +#include <config.h> + +#include <gcrypt.h> +#include <unistd.h> +#include <stdlib.h> +#include <assert.h> +#include <stdio.h> +#include <errno.h> + +#include "util.h" + + + +typedef struct pkdbuf +{ + unsigned char *buffer; + size_t buffer_n; +} pkdbuf_t; + + + +/* Retrieve the public key material for the RSA key, whose fingerprint + is FPR, from gpg output, which can be read through the stream FP. + The RSA modulus will be stored at the address of M and MLEN, the + public exponent at E and ELEN. Returns zero on success, an error + code on failure. Caller must release the allocated buffers at M + and E if the function returns success. */ +static gpg_error_t +retrieve_key_material (FILE *fp, const char *hexkeyid, int *algorithm_id, + pkdbuf_t **pkdbuf, size_t *pkdbuf_n) +{ + pkdbuf_t *pkdbuf_new; + pkdbuf_t *pkdbuf_tmp; + size_t pkdbuf_new_n; + gcry_error_t err = 0; + char *line = NULL; /* read_line() buffer. */ + size_t line_size = 0; /* Helper for for read_line. */ + int found_key = 0; /* Helper to find a matching key. */ + int id; + unsigned char *buffer; + size_t buffer_n; + int i; + + pkdbuf_new = NULL; + pkdbuf_new_n = 0; + id = 0; + + /* Loop over all records until we have found the subkey + corresponsing to the fingerprint. Inm general the first record + should be the pub record, but we don't rely on that. Given that + we only need to look at one key, it is sufficient to compare the + keyid so that we don't need to look at "fpr" records. */ + for (;;) + { + char *p; + char *fields[6]; + int nfields; + size_t max_length; + gcry_mpi_t mpi; + + max_length = 4096; + i = read_line (fp, &line, &line_size, &max_length); + if (!i) + break; /* EOF. */ + if (i < 0) + { + err = gpg_error_from_errno (errno); + goto leave; /* Error. */ + } + if (!max_length) + { + err = gpg_error (GPG_ERR_TRUNCATED); + goto leave; /* Line truncated - we better stop processing. */ + } + + /* Parse the line into fields. */ + for (nfields=0, p=line; p && nfields < DIM (fields); nfields++) + { + fields[nfields] = p; + p = strchr (p, ':'); + if (p) + *(p++) = 0; + } + if (!nfields) + continue; /* No fields at all - skip line. */ + + if (!found_key) + { + if ( (!strcmp (fields[0], "sub") || !strcmp (fields[0], "pub") ) + && nfields > 4 && + (((strlen (hexkeyid) == 8) + && (strlen (fields[4]) == 16) + && (! strcmp (fields[4] + 8, hexkeyid))) + || ((strlen (hexkeyid) == 16) + && (! strcmp (fields[4], hexkeyid))))) + { + found_key = 1; + /* Save algorithm ID. */ + id = atoi (fields[3]); + } + continue; + } + + if ( !strcmp (fields[0], "sub") || !strcmp (fields[0], "pub") ) + break; /* Next key - stop. */ + + if ( strcmp (fields[0], "pkd") ) + continue; /* Not a key data record. */ + + /* FIXME, necessary? */ + + i = atoi (fields[1]); + if ((nfields < 4) || (i < 0)) + { + err = gpg_error (GPG_ERR_GENERAL); + goto leave; + } + + err = gcry_mpi_scan (&mpi, GCRYMPI_FMT_HEX, fields[3], 0, NULL); + if (err) + mpi = NULL; + + err = gcry_mpi_aprint (GCRYMPI_FMT_STD, &buffer, &buffer_n, mpi); + gcry_mpi_release (mpi); + if (err) + goto leave; + + pkdbuf_tmp = xrealloc (pkdbuf_new, sizeof (*pkdbuf_new) * (pkdbuf_new_n + 1)); + if (pkdbuf_new != pkdbuf_tmp) + pkdbuf_new = pkdbuf_tmp; + pkdbuf_new[pkdbuf_new_n].buffer = buffer; + pkdbuf_new[pkdbuf_new_n].buffer_n = buffer_n; + pkdbuf_new_n++; + } + + *algorithm_id = id; + *pkdbuf = pkdbuf_new; + *pkdbuf_n = pkdbuf_new_n; + + leave: + + if (err) + if (pkdbuf_new) + { + for (i = 0; i < pkdbuf_new_n; i++) + xfree (pkdbuf_new[i].buffer); + xfree (pkdbuf_new); + } + xfree (line); + + return err; +} + + + +int +key_to_blob (unsigned char **blob, size_t *blob_n, const char *identifier, ...) +{ + unsigned char *blob_new; + size_t blob_new_n; + unsigned char uint32_buffer[4]; + u32 identifier_n; + FILE *stream; + va_list ap; + int ret; + pkdbuf_t *pkd; + + stream = tmpfile (); + assert (stream); + + identifier_n = strlen (identifier); + uint32_buffer[0] = identifier_n >> 24; + uint32_buffer[1] = identifier_n >> 16; + uint32_buffer[2] = identifier_n >> 8; + uint32_buffer[3] = identifier_n >> 0; + ret = fwrite (uint32_buffer, sizeof (uint32_buffer), 1, stream); + assert (ret == 1); + ret = fwrite (identifier, identifier_n, 1, stream); + assert (ret == 1); + + va_start (ap, identifier); + while (1) + { + pkd = va_arg (ap, pkdbuf_t *); + if (! pkd) + break; + + uint32_buffer[0] = pkd->buffer_n >> 24; + uint32_buffer[1] = pkd->buffer_n >> 16; + uint32_buffer[2] = pkd->buffer_n >> 8; + uint32_buffer[3] = pkd->buffer_n >> 0; + ret = fwrite (uint32_buffer, sizeof (uint32_buffer), 1, stream); + assert (ret == 1); + ret = fwrite (pkd->buffer, pkd->buffer_n, 1, stream); + assert (ret == 1); + } + + blob_new_n = ftell (stream); + rewind (stream); + + blob_new = xmalloc (blob_new_n); + ret = fread (blob_new, blob_new_n, 1, stream); + assert (ret == 1); + + *blob = blob_new; + *blob_n = blob_new_n; + + fclose (stream); + + return 0; +} + +int +main (int argc, char **argv) +{ + const char *keyid; + int algorithm_id; + pkdbuf_t *pkdbuf; + size_t pkdbuf_n; + char *command; + FILE *fp; + int ret; + gcry_error_t err; + unsigned char *blob; + size_t blob_n; + struct b64state b64_state; + const char *identifier; + + pkdbuf = NULL; + pkdbuf_n = 0; + + algorithm_id = 0; /* (avoid cc warning) */ + identifier = NULL; /* (avoid cc warning) */ + + assert (argc == 2); + + keyid = argv[1]; + + ret = asprintf (&command, + "gpg --list-keys --with-colons --with-key-data '%s'", + keyid); + assert (ret > 0); + + fp = popen (command, "r"); + assert (fp); + + err = retrieve_key_material (fp, keyid, &algorithm_id, &pkdbuf, &pkdbuf_n); + assert (! err); + assert ((algorithm_id == 1) || (algorithm_id == 17)); + + if (algorithm_id == 1) + { + identifier = "ssh-rsa"; + ret = key_to_blob (&blob, &blob_n, identifier, + &pkdbuf[0], &pkdbuf[1], NULL); + } + else if (algorithm_id == 17) + { + identifier = "ssh-dsa"; + ret = key_to_blob (&blob, &blob_n, identifier, + &pkdbuf[0], &pkdbuf[1], &pkdbuf[2], &pkdbuf[3], NULL); + } + assert (! ret); + + printf ("%s ", identifier); + + err = b64enc_start (&b64_state, stdout, ""); + assert (! err); + err = b64enc_write (&b64_state, blob, blob_n); + assert (! err); + err = b64enc_finish (&b64_state); + assert (! err); + + printf (" COMMENT\n"); + + return 0; +} diff --git a/tools/gpgparsemail.c b/tools/gpgparsemail.c new file mode 100644 index 000000000..30759f9a4 --- /dev/null +++ b/tools/gpgparsemail.c @@ -0,0 +1,767 @@ +/* gpgparsemail.c - Standalone crypto mail parser + * Copyright (C) 2004 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 2 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. + */ + + +/* This utility prints an RFC8222, possible MIME structured, message + in an annotated format with the first column having an indicator + for the content of the line. Several options are available to + scrutinize the message. S/MIME and OpenPGP support is included. */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <stdio.h> +#include <stdlib.h> +#include <stddef.h> +#include <string.h> +#include <errno.h> +#include <stdarg.h> +#include <assert.h> +#include <time.h> +#include <signal.h> +#include <unistd.h> +#include <fcntl.h> +#include <sys/wait.h> + +#include "rfc822parse.h" + + +#define PGM "gpgparsemail" + +/* Option flags. */ +static int verbose; +static int debug; +static int opt_crypto; /* Decrypt or verify messages. */ +static int opt_no_header; /* Don't output the header lines. */ + +/* Structure used to communicate with the parser callback. */ +struct parse_info_s { + int show_header; /* Show the header lines. */ + int show_data; /* Show the data lines. */ + unsigned int skip_show; /* Temporary disable above for these + number of lines. */ + int show_data_as_note; /* The next data line should be shown + as a note. */ + int show_boundary; + int nesting_level; + + int is_pkcs7; /* Old style S/MIME message. */ + + int gpgsm_mime; /* gpgsm shall be used from S/MIME. */ + char *signing_protocol; + int hashing_level; /* The nesting level we are hashing. */ + int hashing; + FILE *hash_file; + + FILE *sig_file; /* Signature part with MIME or full + pkcs7 data if IS_PCKS7 is set. */ + int verify_now; /* Flag set when all signature data is + available. */ +}; + + +/* Print diagnostic message and exit with failure. */ +static void +die (const char *format, ...) +{ + va_list arg_ptr; + + fflush (stdout); + fprintf (stderr, "%s: ", PGM); + + va_start (arg_ptr, format); + vfprintf (stderr, format, arg_ptr); + va_end (arg_ptr); + putc ('\n', stderr); + + exit (1); +} + + +/* Print diagnostic message. */ +static void +err (const char *format, ...) +{ + va_list arg_ptr; + + fflush (stdout); + fprintf (stderr, "%s: ", PGM); + + va_start (arg_ptr, format); + vfprintf (stderr, format, arg_ptr); + va_end (arg_ptr); + putc ('\n', stderr); +} + +static void * +xmalloc (size_t n) +{ + void *p = malloc (n); + if (!p) + die ("out of core: %s", strerror (errno)); + return p; +} + +/* static void * */ +/* xcalloc (size_t n, size_t m) */ +/* { */ +/* void *p = calloc (n, m); */ +/* if (!p) */ +/* die ("out of core: %s", strerror (errno)); */ +/* return p; */ +/* } */ + +/* static void * */ +/* xrealloc (void *old, size_t n) */ +/* { */ +/* void *p = realloc (old, n); */ +/* if (!p) */ +/* die ("out of core: %s", strerror (errno)); */ +/* return p; */ +/* } */ + +static char * +xstrdup (const char *string) +{ + void *p = malloc (strlen (string)+1); + if (!p) + die ("out of core: %s", strerror (errno)); + strcpy (p, string); + return p; +} + +#ifndef HAVE_STPCPY +static char * +stpcpy (char *a,const char *b) +{ + while (*b) + *a++ = *b++; + *a = 0; + + return (char*)a; +} +#endif + +static int +run_gnupg (int smime, int sig_fd, int data_fd, int *close_list) +{ + int rp[2]; + pid_t pid; + int i, c, is_status; + unsigned int pos; + char status_buf[10]; + const char *cmd = smime? "gpgsm":"gpg"; + FILE *fp; + + if (pipe (rp) == -1) + die ("error creating a pipe: %s", strerror (errno)); + + pid = fork (); + if (pid == -1) + die ("error forking process: %s", strerror (errno)); + + if (!pid) + { /* Child. */ + char data_fd_buf[50]; + int fd; + + /* Connect our signature fd to stdin. */ + if (sig_fd != 0) + { + if (dup2 (sig_fd, 0) == -1) + die ("dup2 stdin failed: %s", strerror (errno)); + } + + /* Keep our data fd and format it for gpg/gpgsm use. */ + if (data_fd == -1) + *data_fd_buf = 0; + else + sprintf (data_fd_buf, "-&%d", data_fd); + + /* Send stdout to the bit bucket. */ + fd = open ("/dev/null", O_WRONLY); + if (fd == -1) + die ("can't open `/dev/null': %s", strerror (errno)); + if (fd != 1) + { + if (dup2 (fd, 1) == -1) + die ("dup2 stderr failed: %s", strerror (errno)); + } + + /* Connect stderr to our pipe. */ + if (rp[1] != 2) + { + if (dup2 (rp[1], 2) == -1) + die ("dup2 stderr failed: %s", strerror (errno)); + } + + /* Close other files. */ + for (i=0; (fd=close_list[i]) != -1; i++) + if (fd > 2 && fd != data_fd) + close (fd); + errno = 0; + + execlp (cmd, cmd, + "--enable-special-filenames", + "--status-fd", "2", + "--assume-base64", + "--verify", + "--", + "-", data_fd == -1? NULL : data_fd_buf, + NULL); + + die ("failed to exec the crypto command: %s", strerror (errno)); + } + + /* Parent. */ + close (rp[1]); + + fp = fdopen (rp[0], "r"); + if (!fp) + die ("can't fdopen pipe for reading: %s", strerror (errno)); + + pos = 0; + is_status = 0; + assert (sizeof status_buf > 9); + while ((c=getc (fp)) != EOF) + { + if (pos < 9) + status_buf[pos] = c; + else + { + if (pos == 9) + { + is_status = !memcmp (status_buf, "[GNUPG:] ", 9); + if (is_status) + fputs ( "c ", stdout); + else if (verbose) + fputs ( "# ", stdout); + fwrite (status_buf, 9, 1, stdout); + } + putchar (c); + } + if (c == '\n') + { + if (verbose && pos < 9) + { + fputs ( "# ", stdout); + fwrite (status_buf, pos+1, 1, stdout); + } + pos = 0; + } + else + pos++; + } + if (pos) + { + if (verbose && pos < 9) + { + fputs ( "# ", stdout); + fwrite (status_buf, pos+1, 1, stdout); + } + putchar ('\n'); + } + fclose (fp); + + while ( (i=waitpid (pid, NULL, 0)) == -1 && errno == EINTR) + ; + if (i == -1) + die ("waiting for child failed: %s", strerror (errno)); + + return 0; +} + + + + +/* Verify the signature in the current temp files. */ +static void +verify_signature (struct parse_info_s *info) +{ + int close_list[10]; + + if (info->is_pkcs7) + { + assert (!info->hash_file); + assert (info->sig_file); + rewind (info->sig_file); + } + else + { + assert (info->hash_file); + assert (info->sig_file); + rewind (info->hash_file); + rewind (info->sig_file); + } + +/* printf ("# Begin hashed data\n"); */ +/* while ( (c=getc (info->hash_file)) != EOF) */ +/* putchar (c); */ +/* printf ("# End hashed data signature\n"); */ +/* printf ("# Begin signature\n"); */ +/* while ( (c=getc (info->sig_file)) != EOF) */ +/* putchar (c); */ +/* printf ("# End signature\n"); */ +/* rewind (info->hash_file); */ +/* rewind (info->sig_file); */ + + close_list[0] = -1; + run_gnupg (1, fileno (info->sig_file), + info->hash_file ? fileno (info->hash_file) : -1, close_list); +} + + + + + +/* Prepare for a multipart/signed. + FIELD_CTX is the parsed context of the content-type header.*/ +static void +mime_signed_begin (struct parse_info_s *info, rfc822parse_t msg, + rfc822parse_field_t field_ctx) +{ + const char *s; + s = rfc822parse_query_parameter (field_ctx, "protocol", 1); + if (s) + { + printf ("h signed.protocol: %s\n", s); + if (!strcmp (s, "application/pkcs7-signature") + || !strcmp (s, "application/x-pkcs7-signature")) + { + if (info->gpgsm_mime) + err ("note: ignoring nested pkcs7-signature"); + else + { + info->gpgsm_mime = 1; + free (info->signing_protocol); + info->signing_protocol = xstrdup (s); + } + } + else if (verbose) + printf ("# this protocol is not supported\n"); + } +} + + +/* Prepare for a multipart/encrypted. + FIELD_CTX is the parsed context of the content-type header.*/ +static void +mime_encrypted_begin (struct parse_info_s *info, rfc822parse_t msg, + rfc822parse_field_t field_ctx) +{ + const char *s; + s = rfc822parse_query_parameter (field_ctx, "protocol", 0); + if (s) + printf ("h encrypted.protocol: %s\n", s); +} + + +/* Prepare for old-style pkcs7 messages. */ +static void +pkcs7_begin (struct parse_info_s *info, rfc822parse_t msg, + rfc822parse_field_t field_ctx) +{ + const char *s; + s = rfc822parse_query_parameter (field_ctx, "name", 0); + if (s) + printf ("h pkcs7.name: %s\n", s); + if (info->is_pkcs7) + err ("note: ignoring nested pkcs7 data"); + else + { + info->is_pkcs7 = 1; + if (opt_crypto) + { + assert (!info->sig_file); + info->sig_file = tmpfile (); + if (!info->sig_file) + die ("error creating temp file: %s", strerror (errno)); + } + } +} + + +/* Print the event received by the parser for debugging as comment + line. */ +static void +show_event (rfc822parse_event_t event) +{ + const char *s; + + switch (event) + { + case RFC822PARSE_OPEN: s= "Open"; break; + case RFC822PARSE_CLOSE: s= "Close"; break; + case RFC822PARSE_CANCEL: s= "Cancel"; break; + case RFC822PARSE_T2BODY: s= "T2Body"; break; + case RFC822PARSE_FINISH: s= "Finish"; break; + case RFC822PARSE_RCVD_SEEN: s= "Rcvd_Seen"; break; + case RFC822PARSE_LEVEL_DOWN: s= "Level_Down"; break; + case RFC822PARSE_LEVEL_UP: s= "Level_Up"; break; + case RFC822PARSE_BOUNDARY: s= "Boundary"; break; + case RFC822PARSE_LAST_BOUNDARY: s= "Last_Boundary"; break; + case RFC822PARSE_BEGIN_HEADER: s= "Begin_Header"; break; + case RFC822PARSE_PREAMBLE: s= "Preamble"; break; + case RFC822PARSE_EPILOGUE: s= "Epilogue"; break; + default: s= "[unknown event]"; break; + } + printf ("# *** got RFC822 event %s\n", s); +} + +/* This function is called by the parser to communicate events. This + callback comminucates with the main program using a structure + passed in OPAQUE. Should retrun 0 or set errno and return -1. */ +static int +message_cb (void *opaque, rfc822parse_event_t event, rfc822parse_t msg) +{ + struct parse_info_s *info = opaque; + + if (debug) + show_event (event); + if (event == RFC822PARSE_OPEN) + { + /* Initialize for a new message. */ + info->show_header = 1; + } + else if (event == RFC822PARSE_T2BODY) + { + rfc822parse_field_t ctx; + + ctx = rfc822parse_parse_field (msg, "Content-Type", -1); + if (ctx) + { + const char *s1, *s2; + s1 = rfc822parse_query_media_type (ctx, &s2); + if (s1) + { + printf ("h media: %*s%s %s\n", + info->nesting_level*2, "", s1, s2); + if (info->gpgsm_mime == 3) + { + char *buf = xmalloc (strlen (s1) + strlen (s2) + 2); + strcpy (stpcpy (stpcpy (buf, s1), "/"), s2); + assert (info->signing_protocol); + if (strcmp (buf, info->signing_protocol)) + err ("invalid S/MIME structure; expected `%s', found `%s'", + info->signing_protocol, buf); + else + { + printf ("c begin_signature\n"); + info->gpgsm_mime++; + if (opt_crypto) + { + assert (!info->sig_file); + info->sig_file = tmpfile (); + if (!info->sig_file) + die ("error creating temp file: %s", + strerror (errno)); + } + } + free (buf); + } + else if (!strcmp (s1, "multipart")) + { + if (!strcmp (s2, "signed")) + mime_signed_begin (info, msg, ctx); + else if (!strcmp (s2, "encrypted")) + mime_encrypted_begin (info, msg, ctx); + } + else if (!strcmp (s1, "application") + && (!strcmp (s2, "pkcs7-mime") + || !strcmp (s2, "x-pkcs7-mime"))) + pkcs7_begin (info, msg, ctx); + } + else + printf ("h media: %*s none\n", info->nesting_level*2, ""); + + rfc822parse_release_field (ctx); + } + else + printf ("h media: %*stext plain [assumed]\n", + info->nesting_level*2, ""); + info->show_header = 0; + info->show_data = 1; + info->skip_show = 1; + } + else if (event == RFC822PARSE_PREAMBLE) + info->show_data_as_note = 1; + else if (event == RFC822PARSE_LEVEL_DOWN) + { + printf ("b down\n"); + info->nesting_level++; + } + else if (event == RFC822PARSE_LEVEL_UP) + { + printf ("b up\n"); + if (info->nesting_level) + info->nesting_level--; + else + err ("invalid structure (bad nesting level)"); + } + else if (event == RFC822PARSE_BOUNDARY || event == RFC822PARSE_LAST_BOUNDARY) + { + info->show_data = 0; + info->show_boundary = 1; + if (event == RFC822PARSE_BOUNDARY) + { + info->show_header = 1; + info->skip_show = 1; + printf ("b part\n"); + } + else + printf ("b last\n"); + + if (info->gpgsm_mime == 2 && info->nesting_level == info->hashing_level) + { + printf ("c end_hash\n"); + info->gpgsm_mime++; + info->hashing = 0; + } + else if (info->gpgsm_mime == 4) + { + printf ("c end_signature\n"); + info->verify_now = 1; + } + } + else if (event == RFC822PARSE_BEGIN_HEADER) + { + if (info->gpgsm_mime == 1) + { + printf ("c begin_hash\n"); + info->hashing = 1; + info->hashing_level = info->nesting_level; + info->gpgsm_mime++; + + if (opt_crypto) + { + assert (!info->hash_file); + info->hash_file = tmpfile (); + if (!info->hash_file) + die ("failed to create temporary file: %s", strerror (errno)); + } + } + } + + return 0; +} + + +/* Read a message from FP and process it according to the global + options. */ +static void +parse_message (FILE *fp) +{ + char line[5000]; + size_t length; + rfc822parse_t msg; + unsigned int lineno = 0; + int no_cr_reported = 0; + struct parse_info_s info; + + memset (&info, 0, sizeof info); + + msg = rfc822parse_open (message_cb, &info); + if (!msg) + die ("can't open parser: %s", strerror (errno)); + + /* Fixme: We should not use fgets becuase it can't cope with + embedded nul characters. */ + while (fgets (line, sizeof (line), fp)) + { + lineno++; + if (lineno == 1 && !strncmp (line, "From ", 5)) + continue; /* We better ignore a leading From line. */ + + length = strlen (line); + if (length && line[length - 1] == '\n') + line[--length] = 0; + else + err ("line number %u too long or last line not terminated", lineno); + if (length && line[length - 1] == '\r') + line[--length] = 0; + else if (verbose && !no_cr_reported) + { + err ("non canonical ended line detected (line %u)", lineno); + no_cr_reported = 1; + } + + + if (rfc822parse_insert (msg, line, length)) + die ("parser failed: %s", strerror (errno)); + + if (info.hashing) + { + /* Delay hashing of the CR/LF because the last line ending + belongs to the next boundary. */ + if (debug) + printf ("# hashing %s`%s'\n", info.hashing==2?"CR,LF+":"", line); + if (opt_crypto) + { + if (info.hashing == 2) + fputs ("\r\n", info.hash_file); + fputs (line, info.hash_file); + if (ferror (info.hash_file)) + die ("error writing to temporary file: %s", strerror (errno)); + } + + info.hashing = 2; + } + + if (info.sig_file && opt_crypto) + { + if (info.verify_now) + { + verify_signature (&info); + if (info.hash_file) + fclose (info.hash_file); + info.hash_file = NULL; + fclose (info.sig_file); + info.sig_file = NULL; + info.gpgsm_mime = 0; + info.is_pkcs7 = 0; + } + else + { + fputs (line, info.sig_file); + fputs ("\r\n", info.sig_file); + if (ferror (info.sig_file)) + die ("error writing to temporary file: %s", strerror (errno)); + } + } + + if (info.show_boundary) + { + if (!opt_no_header) + printf (":%s\n", line); + info.show_boundary = 0; + } + + if (info.skip_show) + info.skip_show--; + else if (info.show_data) + { + if (info.show_data_as_note) + { + if (verbose) + printf ("# DATA: %s\n", line); + info.show_data_as_note = 0; + } + else + printf (" %s\n", line); + } + else if (info.show_header && !opt_no_header) + printf (".%s\n", line); + + } + + if (info.sig_file && opt_crypto && info.is_pkcs7) + { + verify_signature (&info); + fclose (info.sig_file); + info.sig_file = NULL; + info.is_pkcs7 = 0; + } + + rfc822parse_close (msg); +} + + +int +main (int argc, char **argv) +{ + int last_argc = -1; + + if (argc) + { + argc--; argv++; + } + while (argc && last_argc != argc ) + { + last_argc = argc; + if (!strcmp (*argv, "--")) + { + argc--; argv++; + break; + } + else if (!strcmp (*argv, "--help")) + { + puts ( + "Usage: " PGM " [OPTION] [FILE]\n" + "Parse a mail message into an annotated format.\n\n" + " --crypto decrypt or verify messages\n" + " --no-header don't output the header lines\n" + " --verbose enable extra informational output\n" + " --debug enable additional debug output\n" + " --help display this help and exit\n\n" + "With no FILE, or when FILE is -, read standard input.\n\n" + "WARNING: This tool is under development.\n" + " The semantics may change without notice\n\n" + "Report bugs to <[email protected]>."); + exit (0); + } + else if (!strcmp (*argv, "--verbose")) + { + verbose = 1; + argc--; argv++; + } + else if (!strcmp (*argv, "--debug")) + { + verbose = debug = 1; + argc--; argv++; + } + else if (!strcmp (*argv, "--crypto")) + { + opt_crypto = 1; + argc--; argv++; + } + else if (!strcmp (*argv, "--no-header")) + { + opt_no_header = 1; + argc--; argv++; + } + } + + if (argc > 1) + die ("usage: " PGM " [OPTION] [FILE] (try --help for more information)\n"); + + signal (SIGPIPE, SIG_IGN); + + if (argc && strcmp (*argv, "-")) + { + FILE *fp = fopen (*argv, "rb"); + if (!fp) + die ("can't open `%s': %s", *argv, strerror (errno)); + parse_message (fp); + fclose (fp); + } + else + parse_message (stdin); + + return 0; +} + + +/* +Local Variables: +compile-command: "gcc -Wall -g -o gpgparsemail rfc822parse.c gpgparsemail.c" +End: +*/ diff --git a/tools/gpgsm-gencert.sh b/tools/gpgsm-gencert.sh new file mode 100755 index 000000000..3949f2361 --- /dev/null +++ b/tools/gpgsm-gencert.sh @@ -0,0 +1,171 @@ +#!/bin/sh +# -*- sh -*- +# gpgsm-gencert.c - Generate X.509 certificates through GPGSM. +# Copyright (C) 2004, 2005 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 2 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, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA +# 02111-1307, USA. + +set -e + +ASSUAN_FP_IN=4 +ASSUAN_FP_OUT=5 + +ASSUAN_COMMANDS="\ +INPUT FD=$ASSUAN_FP_IN\n\ +OUTPUT FD=$ASSUAN_FP_OUT --armor\n\ +GENKEY\n\ +BYE" + +ANSWER="" + +query_user() +{ + message=$1; shift + + echo "$message" >&2 + echo -n "> " >&2 + read answer + + ANSWER=$answer; +} + +query_user_menu() +{ + message=$1; shift + i=0 + + echo "$message" >&2 + for choice in "$@"; do + i=$(expr $i + 1) + echo " [$i] $choice" >&2 + done + + while true; do + j=1 + echo -n "Your selection: " >&2 + read idx + + while [ $j -lt $i -o $j -eq $i ]; do + if [ "$idx" = $j ]; then + break + fi + j=$(expr $j + 1) + done + if [ $j -lt $i -o $j -eq $i ]; then + break + fi + done + + i=0 + for choice in "$@"; do + i=$(expr $i + 1) + if [ $i -eq $idx ]; then + ANSWER=$1 + break; + fi + shift + done + + echo "You selected: $ANSWER" >&2 +} + +query_user_menu "Key type" "RSA" "existing key" +if [ "$ANSWER" = "existing key" ]; then + # User requested to use an existing key; need to set some dummy defaults + KEY_TYPE=RSA + KEY_LENGTH=1024 + query_user "Keygrip " + KEY_GRIP=$ANSWER +else + KEY_TYPE=$ANSWER + query_user_menu "Key length" "1024" "2048" + KEY_LENGTH=$ANSWER + KEY_GRIP= +fi + + +query_user_menu "Key usage" "sign, encrypt" "sign" "encrypt" +KEY_USAGE=$ANSWER + +query_user "Name (DN)" +NAME=$ANSWER + +EMAIL_ADDRESSES= +LF= +while : ; do + query_user "E-Mail addresses (end with an empty line)" + [ -z "$ANSWER" ] && break + EMAIL_ADDRESSES="${EMAIL_ADDRESSES}${LF}Name-Email: $ANSWER" + LF=' +' +done + +DNS_ADDRESSES= +LF= +while : ; do + query_user "DNS Names (optional; end with an empty line)" + [ -z "$ANSWER" ] && break + DNS_ADDRESSES="${DNS_ADDRESSES}${LF}Name-DNS: $ANSWER" + LF=' +' +done + +URI_ADDRESSES= +LF= +while : ; do + query_user "URIs (optional; end with an empty line)" + [ -z "$ANSWER" ] && break + URI_ADDRESSES="${URI_ADDRESSES}${LF}Name-URI: $ANSWER" + LF=' +' +done + +file_parameter=$(mktemp "/tmp/gpgsm.XXXXXX") +outfile=$(mktemp "/tmp/gpgsm.XXXXXX") + + +( +cat <<EOF +Key-Type: $KEY_TYPE +Key-Length: $KEY_LENGTH +Key-Usage: $KEY_USAGE +Name-DN: $NAME +EOF +[ -n "$KEY_GRIP" ] && echo "Key-Grip: $KEY_GRIP" +[ -n "$EMAIL_ADDRESSES" ] && echo "$EMAIL_ADDRESSES" +[ -n "$DNS_ADDRESSES" ] && echo "$DNS_ADDRESSES" +[ -n "$URI_ADDRESSES" ] && echo "$URI_ADDRESSES" +) > "$file_parameter" + + +echo 'Parameters for certificate request to create:' >&2 +cat -n "$file_parameter" >&2 +echo >&2 + +query_user_menu "Really create such a CSR?" "yes" "no" +[ "$ANSWER" != "yes" ] && exit 1 + + +echo -e "$ASSUAN_COMMANDS" | \ + gpgsm --no-log-file --debug-level none --debug-none \ + --server 4< "$file_parameter" 5>"$outfile" >/dev/null + +cat "$outfile" + +rm "$file_parameter" "$outfile" +exit 0 diff --git a/tools/gpgsplit.c b/tools/gpgsplit.c deleted file mode 100644 index 3cdae05bf..000000000 --- a/tools/gpgsplit.c +++ /dev/null @@ -1,879 +0,0 @@ -/* gpgsplit.c - An OpenPGP packet splitting tool - * Copyright (C) 2001, 2002, 2003 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 2 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, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, - * USA. - */ - -/* - * TODO: Add an option to uncompress packets. This should come quite handy. - */ - -#include <config.h> -#include <errno.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <ctype.h> -#include <unistd.h> -#include <assert.h> -#ifdef HAVE_DOSISH_SYSTEM -# include <fcntl.h> /* for setmode() */ -#endif -#include <zlib.h> -#ifdef HAVE_BZIP2 -#include <bzlib.h> -#endif /* HAVE_BZIP2 */ -#if defined(__riscos__) && defined(USE_ZLIBRISCOS) -# include "zlib-riscos.h" -#endif - -#define INCLUDED_BY_MAIN_MODULE 1 -#include "../g10/packet.h" -#include "util.h" - -static int opt_verbose; -static const char *opt_prefix = ""; -static int opt_uncompress; -static int opt_secret_to_public; -static int opt_no_split; - -static void g10_exit( int rc ); -static void split_packets (const char *fname); - - -enum cmd_and_opt_values { - aNull = 0, - oVerbose = 'v', - oPrefix = 'p', - oUncompress = 500, - oSecretToPublic, - oNoSplit, - - aTest -}; - - -static ARGPARSE_OPTS opts[] = { - - { 301, NULL, 0, "@Options:\n " }, - - { oVerbose, "verbose", 0, "verbose" }, - { oPrefix, "prefix", 2, "|STRING|Prepend filenames with STRING" }, - { oUncompress, "uncompress", 0, "uncompress a packet"}, - { oSecretToPublic, "secret-to-public", 0, "convert secret keys to public keys"}, - { oNoSplit, "no-split", 0, "write to stdout and don't actually split"}, -{0} }; - - -const char * -strusage( int level ) -{ - const char *p; - switch (level) - { - case 11: p = "gpgsplit (GnuPG)"; - break; - case 13: p = VERSION; break; - case 17: p = PRINTABLE_OS_NAME; break; - case 19: p = - "Please report bugs to <[email protected]>.\n"; - break; - case 1: - case 40: p = - "Usage: gpgsplit [options] [files] (-h for help)"; - break; - case 41: p = - "Syntax: gpgsplit [options] [files]\n" - "Split an OpenPGP message into packets\n"; - break; - - default: p = default_strusage(level); - } - return p; -} - - - -int -main( int argc, char **argv ) -{ - ARGPARSE_ARGS pargs; - -#ifdef HAVE_DOSISH_SYSTEM - setmode( fileno(stdin), O_BINARY ); - setmode( fileno(stdout), O_BINARY ); -#endif - log_set_name("gpgsplit"); - - pargs.argc = &argc; - pargs.argv = &argv; - pargs.flags= 1; /* do not remove the args */ - while (optfile_parse( NULL, NULL, NULL, &pargs, opts)) - { - switch (pargs.r_opt) - { - case oVerbose: opt_verbose = 1; break; - case oPrefix: opt_prefix = pargs.r.ret_str; break; - case oUncompress: opt_uncompress = 1; break; - case oSecretToPublic: opt_secret_to_public = 1; break; - case oNoSplit: opt_no_split = 1; break; - default : pargs.err = 2; break; - } - } - - if (log_get_errorcount(0)) - g10_exit (2); - - if (!argc) - split_packets (NULL); - else - { - for ( ;argc; argc--, argv++) - split_packets (*argv); - } - - g10_exit (0); - return 0; -} - - -static void -g10_exit (int rc) -{ - rc = rc? rc : log_get_errorcount(0)? 2 : 0; - exit(rc ); -} - -static const char * -pkttype_to_string (int pkttype) -{ - const char *s; - - switch (pkttype) - { - case PKT_PUBKEY_ENC : s = "pk_enc"; break; - case PKT_SIGNATURE : s = "sig"; break; - case PKT_SYMKEY_ENC : s = "sym_enc"; break; - case PKT_ONEPASS_SIG : s = "onepass_sig"; break; - case PKT_SECRET_KEY : s = "secret_key"; break; - case PKT_PUBLIC_KEY : s = "public_key"; break; - case PKT_SECRET_SUBKEY : s = "secret_subkey"; break; - case PKT_COMPRESSED : - s = opt_uncompress? "uncompressed":"compressed"; - break; - case PKT_ENCRYPTED : s = "encrypted"; break; - case PKT_MARKER : s = "marker"; break; - case PKT_PLAINTEXT : s = "plaintext"; break; - case PKT_RING_TRUST : s = "ring_trust"; break; - case PKT_USER_ID : s = "user_id"; break; - case PKT_PUBLIC_SUBKEY : s = "public_subkey"; break; - case PKT_OLD_COMMENT : s = "old_comment"; break; - case PKT_ATTRIBUTE : s = "attribute"; break; - case PKT_ENCRYPTED_MDC : s = "encrypted_mdc"; break; - case PKT_MDC : s = "mdc"; break; - case PKT_COMMENT : s = "comment"; break; - case PKT_GPG_CONTROL : s = "gpg_control"; break; - default: s = "unknown"; break; - } - return s; -} - - -/* - * Create a new filename and a return a pointer to a statically - * allocated buffer - */ -static char * -create_filename (int pkttype) -{ - static unsigned int partno = 0; - static char *name; - - if (!name) - name = xmalloc (strlen (opt_prefix) + 100 ); - - assert (pkttype < 1000 && pkttype >= 0 ); - partno++; - sprintf (name, "%s%06u-%03d" EXTSEP_S "%.40s", - opt_prefix, partno, pkttype, pkttype_to_string (pkttype)); - return name; -} - -static int -read_u16 (FILE *fp, size_t *rn) -{ - int c; - - if ( (c = getc (fp)) == EOF ) - return -1; - *rn = c << 8; - if ( (c = getc (fp)) == EOF ) - return -1; - *rn |= c; - return 0; -} - -static int -read_u32 (FILE *fp, unsigned long *rn) -{ - size_t tmp; - - if (read_u16 (fp, &tmp)) - return -1; - *rn = tmp << 16; - if (read_u16 (fp, &tmp)) - return -1; - *rn |= tmp; - return 0; -} - -static int -write_old_header (FILE *fp, int pkttype, unsigned int len) -{ - int ctb = (0x80 | ((pkttype & 15)<<2)); - - if (len < 256) - ; - else if (len < 65536) - ctb |= 1; - else - ctb |= 2; - - if ( putc ( ctb, fp) == EOF ) - return -1; - - if ( (ctb & 2) ) - { - if (putc ((len>>24), fp) == EOF) - return -1; - if (putc ((len>>16), fp) == EOF) - return -1; - } - if ( (ctb & 3) ) - { - if (putc ((len>>8), fp) == EOF) - return -1; - } - if (putc ((len&0xff), fp) == EOF) - return -1; - return 0; -} - -static int -write_new_header (FILE *fp, int pkttype, unsigned int len) -{ - if ( putc ((0xc0 | (pkttype & 0x3f)), fp) == EOF ) - return -1; - - if (len < 192) - { - if (putc (len, fp) == EOF) - return -1; - } - else if (len < 8384) - { - len -= 192; - if (putc ((len/256)+192, fp) == EOF) - return -1; - if (putc ((len%256), fp) == EOF) - return -1; - } - else - { - if (putc ( 0xff, fp) == EOF) - return -1; - if (putc ( (len >> 24), fp) == EOF) - return -1; - if (putc ( (len >> 16), fp) == EOF) - return -1; - if (putc ( (len >> 8), fp) == EOF) - return -1; - if (putc ( (len & 0xff), fp) == EOF) - return -1; - } - return 0; -} - -/* Return the length of the public key given BUF of BUFLEN with a - secret key. */ -static int -public_key_length (const unsigned char *buf, size_t buflen) -{ - const unsigned char *s; - int nmpis; - - /* byte version number (3 or 4) - u32 creation time - [u16 valid days (version 3 only)] - byte algorithm - n MPIs (n and e) */ - if (!buflen) - return 0; - if (buf[0] < 2 || buf[0] > 4) - return 0; /* wrong version number */ - if (buflen < (buf[0] == 4? 6:8)) - return 0; - s = buf + (buf[0] == 4? 6:8); - buflen -= (buf[0] == 4? 6:8); - switch (s[-1]) - { - case 1: - case 2: - case 3: - nmpis = 2; - break; - case 16: - case 20: - nmpis = 3; - break; - case 17: - nmpis = 4; - break; - default: - return 0; - } - - for (; nmpis; nmpis--) - { - unsigned int nbits, nbytes; - - if (buflen < 2) - return 0; - nbits = (s[0] << 8) | s[1]; - s += 2; buflen -= 2; - nbytes = (nbits+7) / 8; - if (buflen < nbytes) - return 0; - s += nbytes; buflen -= nbytes; - } - - return s - buf; -} - -static int -handle_zlib(int algo,FILE *fpin,FILE *fpout) -{ - z_stream zs; - byte *inbuf, *outbuf; - unsigned int inbufsize, outbufsize; - int c,zinit_done, zrc, nread, count; - size_t n; - - memset (&zs, 0, sizeof zs); - inbufsize = 2048; - inbuf = xmalloc (inbufsize); - outbufsize = 8192; - outbuf = xmalloc (outbufsize); - zs.avail_in = 0; - zinit_done = 0; - - do - { - if (zs.avail_in < inbufsize) - { - n = zs.avail_in; - if (!n) - zs.next_in = (Bytef *) inbuf; - count = inbufsize - n; - for (nread=0; - nread < count && (c=getc (fpin)) != EOF; - nread++) - inbuf[n+nread] = c; - - n += nread; - if (nread < count && algo == 1) - { - inbuf[n] = 0xFF; /* chew dummy byte */ - n++; - } - zs.avail_in = n; - } - zs.next_out = (Bytef *) outbuf; - zs.avail_out = outbufsize; - - if (!zinit_done) - { - zrc = (algo == 1? inflateInit2 ( &zs, -13) - : inflateInit ( &zs )); - if (zrc != Z_OK) - { - log_fatal ("zlib problem: %s\n", zs.msg? zs.msg : - zrc == Z_MEM_ERROR ? "out of core" : - zrc == Z_VERSION_ERROR ? - "invalid lib version" : - "unknown error" ); - } - zinit_done = 1; - } - else - { -#ifdef Z_SYNC_FLUSH - zrc = inflate (&zs, Z_SYNC_FLUSH); -#else - zrc = inflate (&zs, Z_PARTIAL_FLUSH); -#endif - if (zrc == Z_STREAM_END) - ; /* eof */ - else if (zrc != Z_OK && zrc != Z_BUF_ERROR) - { - if (zs.msg) - log_fatal ("zlib inflate problem: %s\n", zs.msg ); - else - log_fatal ("zlib inflate problem: rc=%d\n", zrc ); - } - for (n=0; n < outbufsize - zs.avail_out; n++) - { - if (putc (outbuf[n], fpout) == EOF ) - return 1; - } - } - } - while (zrc != Z_STREAM_END && zrc != Z_BUF_ERROR); - inflateEnd (&zs); - - return 0; -} - -#ifdef HAVE_BZIP2 -static int -handle_bzip2(int algo,FILE *fpin,FILE *fpout) -{ - bz_stream bzs; - byte *inbuf, *outbuf; - unsigned int inbufsize, outbufsize; - int c,zinit_done, zrc, nread, count; - size_t n; - - memset (&bzs, 0, sizeof bzs); - inbufsize = 2048; - inbuf = xmalloc (inbufsize); - outbufsize = 8192; - outbuf = xmalloc (outbufsize); - bzs.avail_in = 0; - zinit_done = 0; - - do - { - if (bzs.avail_in < inbufsize) - { - n = bzs.avail_in; - if (!n) - bzs.next_in = inbuf; - count = inbufsize - n; - for (nread=0; - nread < count && (c=getc (fpin)) != EOF; - nread++) - inbuf[n+nread] = c; - - n += nread; - if (nread < count && algo == 1) - { - inbuf[n] = 0xFF; /* chew dummy byte */ - n++; - } - bzs.avail_in = n; - } - bzs.next_out = outbuf; - bzs.avail_out = outbufsize; - - if (!zinit_done) - { - zrc = BZ2_bzDecompressInit(&bzs,0,0); - if (zrc != BZ_OK) - log_fatal ("bz2lib problem: %d\n",zrc); - zinit_done = 1; - } - else - { - zrc = BZ2_bzDecompress(&bzs); - if (zrc == BZ_STREAM_END) - ; /* eof */ - else if (zrc != BZ_OK && zrc != BZ_PARAM_ERROR) - log_fatal ("bz2lib inflate problem: %d\n", zrc ); - for (n=0; n < outbufsize - bzs.avail_out; n++) - { - if (putc (outbuf[n], fpout) == EOF ) - return 1; - } - } - } - while (zrc != BZ_STREAM_END && zrc != BZ_PARAM_ERROR); - BZ2_bzDecompressEnd(&bzs); - - return 0; -} -#endif /* HAVE_BZIP2 */ - -/* hdr must point to a buffer large enough to hold all header bytes */ -static int -write_part ( const char *fname, FILE *fpin, unsigned long pktlen, - int pkttype, int partial, unsigned char *hdr, size_t hdrlen) -{ - FILE *fpout; - int c, first; - unsigned char *p; - const char *outname = create_filename (pkttype); - -#if defined(__riscos__) && defined(USE_ZLIBRISCOS) - static int initialized = 0; - - if (!initialized) - initialized = riscos_load_module("ZLib", zlib_path, 1); -#endif - if (opt_no_split) - fpout = stdout; - else - { - if (opt_verbose) - log_info ("writing `%s'\n", outname); - fpout = fopen (outname, "wb"); - if (!fpout) - { - log_error ("error creating `%s': %s\n", outname, strerror(errno)); - /* stop right now, otherwise we would mess up the sequence - of the part numbers */ - g10_exit (1); - } - } - - if (opt_secret_to_public - && (pkttype == PKT_SECRET_KEY || pkttype == PKT_SECRET_SUBKEY)) - { - unsigned char *blob = xmalloc (pktlen); - int i, len; - - pkttype = pkttype == PKT_SECRET_KEY? PKT_PUBLIC_KEY:PKT_PUBLIC_SUBKEY; - - for (i=0; i < pktlen; i++) - { - c = getc (fpin); - if (c == EOF) - goto read_error; - blob[i] = c; - } - len = public_key_length (blob, pktlen); - if (!len) - { - log_error ("error calcualting public key length\n"); - g10_exit (1); - } - if ( (hdr[0] & 0x40) ) - { - if (write_new_header (fpout, pkttype, len)) - goto write_error; - } - else - { - if (write_old_header (fpout, pkttype, len)) - goto write_error; - } - - for (i=0; i < len; i++) - { - if ( putc (blob[i], fpout) == EOF ) - goto write_error; - } - - goto ready; - } - - - if (!opt_uncompress) - { - for (p=hdr; hdrlen; p++, hdrlen--) - { - if ( putc (*p, fpout) == EOF ) - goto write_error; - } - } - - first = 1; - while (partial) - { - size_t partlen; - - if (partial == 1) - { /* openpgp */ - if (first ) - { - c = pktlen; - assert( c >= 224 && c < 255 ); - first = 0; - } - else if ((c = getc (fpin)) == EOF ) - goto read_error; - else - hdr[hdrlen++] = c; - - if (c < 192) - { - pktlen = c; - partial = 0; /* (last segment may follow) */ - } - else if (c < 224 ) - { - pktlen = (c - 192) * 256; - if ((c = getc (fpin)) == EOF) - goto read_error; - hdr[hdrlen++] = c; - pktlen += c + 192; - partial = 0; - } - else if (c == 255) - { - if (read_u32 (fpin, &pktlen)) - goto read_error; - hdr[hdrlen++] = pktlen >> 24; - hdr[hdrlen++] = pktlen >> 16; - hdr[hdrlen++] = pktlen >> 8; - hdr[hdrlen++] = pktlen; - partial = 0; - } - else - { /* next partial body length */ - for (p=hdr; hdrlen; p++, hdrlen--) - { - if ( putc (*p, fpout) == EOF ) - goto write_error; - } - partlen = 1 << (c & 0x1f); - for (; partlen; partlen--) - { - if ((c = getc (fpin)) == EOF) - goto read_error; - if ( putc (c, fpout) == EOF ) - goto write_error; - } - } - } - else if (partial == 2) - { /* old gnupg */ - assert (!pktlen); - if ( read_u16 (fpin, &partlen) ) - goto read_error; - hdr[hdrlen++] = partlen >> 8; - hdr[hdrlen++] = partlen; - for (p=hdr; hdrlen; p++, hdrlen--) - { - if ( putc (*p, fpout) == EOF ) - goto write_error; - } - if (!partlen) - partial = 0; /* end of packet */ - for (; partlen; partlen--) - { - c = getc (fpin); - if (c == EOF) - goto read_error; - if ( putc (c, fpout) == EOF ) - goto write_error; - } - } - else - { /* compressed: read to end */ - pktlen = 0; - partial = 0; - hdrlen = 0; - if (opt_uncompress) - { - if ((c = getc (fpin)) == EOF) - goto read_error; - - if(c==1 || c==2) - { - if(handle_zlib(c,fpin,fpout)) - goto write_error; - } -#ifdef HAVE_BZIP2 - else if(c==3) - { - if(handle_bzip2(c,fpin,fpout)) - goto write_error; - } -#endif /* HAVE_BZIP2 */ - else - { - log_error("invalid compression algorithm (%d)\n",c); - goto read_error; - } - } - else - { - while ( (c=getc (fpin)) != EOF ) - { - if ( putc (c, fpout) == EOF ) - goto write_error; - } - } - if (!feof (fpin)) - goto read_error; - } - } - - for (p=hdr; hdrlen; p++, hdrlen--) - { - if ( putc (*p, fpout) == EOF ) - goto write_error; - } - - /* standard packet or last segment of partial length encoded packet */ - for (; pktlen; pktlen--) - { - c = getc (fpin); - if (c == EOF) - goto read_error; - if ( putc (c, fpout) == EOF ) - goto write_error; - } - - ready: - if ( !opt_no_split && fclose (fpout) ) - log_error ("error closing `%s': %s\n", outname, strerror (errno)); - return 0; - - write_error: - log_error ("error writing `%s': %s\n", outname, strerror (errno)); - if (!opt_no_split) - fclose (fpout); - return 2; - - read_error: - if (!opt_no_split) - { - int save = errno; - fclose (fpout); - errno = save; - } - return -1; -} - - - -static int -do_split (const char *fname, FILE *fp) -{ - int c, ctb, pkttype; - unsigned long pktlen = 0; - int partial = 0; - unsigned char header[20]; - int header_idx = 0; - - ctb = getc (fp); - if (ctb == EOF) - return 3; /* ready */ - header[header_idx++] = ctb; - - if (!(ctb & 0x80)) - { - log_error("invalid CTB %02x\n", ctb ); - return 1; - } - if ( (ctb & 0x40) ) - { /* new CTB */ - pkttype = (ctb & 0x3f); - if( (c = getc (fp)) == EOF ) - return -1; - header[header_idx++] = c; - - if ( c < 192 ) - pktlen = c; - else if ( c < 224 ) - { - pktlen = (c - 192) * 256; - if( (c = getc (fp)) == EOF ) - return -1; - header[header_idx++] = c; - pktlen += c + 192; - } - else if ( c == 255 ) - { - if (read_u32 (fp, &pktlen)) - return -1; - header[header_idx++] = pktlen >> 24; - header[header_idx++] = pktlen >> 16; - header[header_idx++] = pktlen >> 8; - header[header_idx++] = pktlen; - } - else - { /* partial body length */ - pktlen = c; - partial = 1; - } - } - else - { - int lenbytes; - - pkttype = (ctb>>2)&0xf; - lenbytes = ((ctb&3)==3)? 0 : (1<<(ctb & 3)); - if (!lenbytes ) - { - pktlen = 0; /* don't know the value */ - if( pkttype == PKT_COMPRESSED ) - partial = 3; - else - partial = 2; /* the old GnuPG partial length encoding */ - } - else - { - for ( ; lenbytes; lenbytes-- ) - { - pktlen <<= 8; - if( (c = getc (fp)) == EOF ) - return -1; - header[header_idx++] = c; - - pktlen |= c; - } - } - } - - return write_part (fname, fp, pktlen, pkttype, partial, - header, header_idx); -} - - -static void -split_packets (const char *fname) -{ - FILE *fp; - int rc; - - if (!fname || !strcmp (fname, "-")) - { - fp = stdin; - fname = "-"; - } - else if ( !(fp = fopen (fname,"rb")) ) - { - log_error ("can't open `%s': %s\n", fname, strerror (errno)); - return; - } - - while ( !(rc = do_split (fname, fp)) ) - ; - if ( rc > 0 ) - ; /* error already handled */ - else if ( ferror (fp) ) - log_error ("error reading `%s': %s\n", fname, strerror (errno)); - else - log_error ("premature EOF while reading `%s'\n", fname ); - - if ( fp != stdin ) - fclose (fp); -} diff --git a/tools/lspgpot b/tools/lspgpot deleted file mode 100755 index f406392eb..000000000 --- a/tools/lspgpot +++ /dev/null @@ -1,27 +0,0 @@ -#!/bin/sh -# lspgpot - script to extract the ownertrust values -# from PGP keyrings and list them in GnuPG ownertrust format. -# -# 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. - -if ! gpg --version > /dev/null 2>&1 ; then - echo "GnuPG not available!" - exit 1 -fi - -gpg --dry-run --with-fingerprint --with-colons $* | awk ' -BEGIN { FS=":" - printf "# Ownertrust listing generated by lspgpot\n" - printf "# This can be imported using the command:\n" - printf "# gpg --import-ownertrust\n\n" } -$1 == "fpr" { fpr = $10 } -$1 == "rtv" && $2 == 1 && $3 == 2 { printf "%s:3:\n", fpr; next } -$1 == "rtv" && $2 == 1 && $3 == 5 { printf "%s:4:\n", fpr; next } -$1 == "rtv" && $2 == 1 && $3 == 6 { printf "%s:5:\n", fpr; next } -' diff --git a/tools/mail-signed-keys b/tools/mail-signed-keys deleted file mode 100755 index 80fbb3481..000000000 --- a/tools/mail-signed-keys +++ /dev/null @@ -1,114 +0,0 @@ -#!/bin/sh -# Copyright (C) 2000, 2001 Free Software Foundation, Inc. -# -# 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. - -# FIXME: Add --dry-run, use only valid email addreses, extract only given keys - -dryrun=0 -if [ "$1" = "--dry-run" ]; then - dryrun=1 - shift -fi - -if [ -z "$1" -o -z "$2" -o -z "$3" ]; then - echo "usage: mail-signed-keys keyring signedby signame" >&2 - exit 1 -fi - -signame="$3" - -if [ ! -f $1 ]; then - echo "mail-signed-keys: '$1': no such file" >&2 - exit 1 -fi - -[ -f '.#tdb.tmp' ] && rm '.#tdb.tmp' -ro="--homedir . --no-options --trustdb-name=./.#tdb.tmp --dry-run --lock-never --no-default-keyring --keyring $1" - -signedby=`gpg $ro --list-keys --with-colons $2 \ - 2>/dev/null | awk -F: '$1=="pub" {print $5; exit 0}'` - -if [ -z "$signedby" ]; then - echo "mail-signed-keys: '$2': no such signator" >&2 - exit 1 -fi - -if [ "$dryrun" = "0" ]; then - echo "About to send the the keys signed by $signedby" >&2 - echo -n "to their owners. Do you really want to do this? (y/N)" >&2 - read - [ "$REPLY" != "y" -a "$REPLY" != "Y" ] && exit 0 -fi - -gpg $ro --check-sigs --with-colons 2>/dev/null \ - | awk -F: -v signedby="$signedby" -v gpgopt="$ro" \ - -v dryrun="$dryrun" -v signame="$signame" ' -BEGIN { sendmail="/usr/lib/sendmail -oi -t " } -$1 == "pub" { nextkid=$5; nextuid=$10 - if( uidcount > 0 ) { myflush() } - kid=nextkid; uid=nextuid; next - } -$1 == "uid" { uid=$10 ; next } -$1 == "sig" && $2 == "!" && $5 == signedby { uids[uidcount++] = uid; next } -END { if( uidcount > 0 ) { myflush() } } - -function myflush() -{ - if ( kid == signedby ) { uidcount=0; return } - print "sending key " substr(kid,9) " to" | "cat >&2" - for(i=0; i < 1; i++ ) { - print " " uids[i] | "cat >&2" - if( dryrun == 0 ) { - if( i == 0 ) { - printf "To: %s", uids[i] | sendmail - } - else { - printf ",\n %s", uids[i] | sendmail - } - } - } - if(dryrun == 0) { - printf "\n" | sendmail - print "Subject: I signed your key " substr(kid,9) | sendmail - print "" | sendmail - print "Hi," | sendmail - print "" | sendmail - print "Here you get back the signed key." | sendmail - print "Please send it yourself to a keyserver." | sendmail - print "" | sendmail - print "Peace," | sendmail - print " " signame | sendmail - print "" | sendmail - cmd = "gpg " gpgopt " --export -a " kid " 2>/dev/null" - while( (cmd | getline) > 0 ) { - print | sendmail - } - print "" | sendmail - close(cmd) - close( sendmail ) - } - uidcount=0 -} -' - - - - - - - - - - - - - - - diff --git a/tools/make-dns-cert.c b/tools/make-dns-cert.c deleted file mode 100644 index 496b63b0b..000000000 --- a/tools/make-dns-cert.c +++ /dev/null @@ -1,239 +0,0 @@ -/* make-dns-cert.c - An OpenPGP-to-DNS CERT conversion tool - * Copyright (C) 2006 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 2 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, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, - * USA. - */ - -#include <config.h> -#include <unistd.h> -#ifdef HAVE_GETOPT_H -#include <getopt.h> -#endif -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <errno.h> -#include <sys/types.h> -#include <sys/stat.h> -#include <fcntl.h> - -/* We use TYPE37 instead of CERT since not all nameservers can handle - CERT yet... */ - -static int -cert_key(const char *name,const char *keyfile) -{ - int fd,ret=1,err,i; - struct stat statbuf; - - fd=open(keyfile,O_RDONLY); - if(fd==-1) - { - fprintf(stderr,"Cannot open key file %s: %s\n",keyfile,strerror(errno)); - return 1; - } - - err=fstat(fd,&statbuf); - if(err==-1) - { - fprintf(stderr,"Unable to stat key file %s: %s\n", - keyfile,strerror(errno)); - goto fail; - } - - if(statbuf.st_size>65536) - { - fprintf(stderr,"Key %s too large for CERT encoding\n",keyfile); - goto fail; - } - - if(statbuf.st_size>16384) - fprintf(stderr,"Warning: key file %s is larger than the default" - " GnuPG max-cert-size\n",keyfile); - - printf("%s\tTYPE37\t\\# %u 0003 0000 00 ", - name,(unsigned int)statbuf.st_size+5); - - err=1; - while(err!=0) - { - unsigned char buffer[1024]; - - err=read(fd,buffer,1024); - if(err==-1) - { - fprintf(stderr,"Unable to read key file %s: %s\n", - keyfile,strerror(errno)); - goto fail; - } - - for(i=0;i<err;i++) - printf("%02X",buffer[i]); - } - - printf("\n"); - - ret=0; - - fail: - close(fd); - - return ret; -} - -static int -url_key(const char *name,const char *fpr,const char *url) -{ - int len=6,fprlen=0; - - if(fpr) - { - const char *tmp = fpr; - while (*tmp) - { - if ((*tmp >= 'A' && *tmp <= 'F') || - (*tmp >= 'a' && *tmp <= 'f') || - (*tmp >= '0' && *tmp <= '9')) - { - fprlen++; - } - else if (*tmp != ' ' && *tmp != '\t') - { - fprintf(stderr,"Fingerprint must consist of only hex digits" - " and whitespace\n"); - return 1; - } - - tmp++; - } - - if(fprlen%2) - { - fprintf(stderr,"Fingerprint must be an even number of characters\n"); - return 1; - } - - fprlen/=2; - len+=fprlen; - } - - if(url) - len+=strlen(url); - - if(!fpr && !url) - { - fprintf(stderr, - "Cannot generate a CERT without either a fingerprint or URL\n"); - return 1; - } - - printf("%s\tTYPE37\t\\# %d 0006 0000 00 %02X",name,len,fprlen); - - if(fpr) - printf(" %s",fpr); - - if(url) - { - const char *c; - printf(" "); - for(c=url;*c;c++) - printf("%02X",*c); - } - - printf("\n"); - - return 0; -} - -static void -usage(FILE *stream) -{ - fprintf(stream,"make-dns-cert\n"); - fprintf(stream,"\t-f\tfingerprint\n"); - fprintf(stream,"\t-u\tURL\n"); - fprintf(stream,"\t-k\tkey file\n"); - fprintf(stream,"\t-n\tDNS name\n"); -} - -int -main(int argc,char *argv[]) -{ - int arg,err=1; - char *fpr=NULL,*url=NULL,*keyfile=NULL,*name=NULL; - - if(argc==1) - { - usage(stderr); - return 1; - } - else if(argc>1 && strcmp(argv[1],"--version")==0) - { - printf("make-dns-cert (GnuPG) " VERSION "\n"); - return 0; - } - else if(argc>1 && strcmp(argv[1],"--help")==0) - { - usage(stdout); - return 0; - } - - while((arg=getopt(argc,argv,"hf:u:k:n:"))!=-1) - switch(arg) - { - default: - case 'h': - usage(stdout); - exit(0); - - case 'f': - fpr=optarg; - break; - - case 'u': - url=optarg; - break; - - case 'k': - keyfile=optarg; - break; - - case 'n': - name=optarg; - break; - } - - if(!name) - { - fprintf(stderr,"No name provided\n"); - return 1; - } - - if(keyfile && (fpr || url)) - { - fprintf(stderr,"Cannot generate a CERT record with both a keyfile and" - " a fingerprint or URL\n"); - return 1; - } - - if(keyfile) - err=cert_key(name,keyfile); - else - err=url_key(name,fpr,url); - - return err; -} diff --git a/tools/mk-tdata.c b/tools/mk-tdata.c deleted file mode 100644 index 833875d28..000000000 --- a/tools/mk-tdata.c +++ /dev/null @@ -1,67 +0,0 @@ -/* mk-tdata.c - Create some simple random testdata - * Copyright (C) 1998, 1999, 2000, 2001, 2006 Free Software Foundation, Inc. - * - * 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 <config.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <unistd.h> - - -#ifndef RAND_MAX /* for SunOS */ -#define RAND_MAX 32767 -#endif - -int -main(int argc, char **argv) -{ - int i, c = 0; - int limit =0; - int char_mode = 0; - - if (argc) - { - argc--; - argv++; - } - - /* Check for option --char N */ - if (argc > 1 && !strcmp (argv[0], "--char")) - { - char_mode = 1; - c = strtol (argv[1], NULL, 0); - argc -= 2; - argv += 2; - } - - limit = argc ? atoi(argv[0]) : 0; - - srand(getpid()); - - for (i=0; !limit || i < limit; i++ ) - { - if (char_mode) - { - putchar (c); - } - else - { -#ifdef HAVE_RAND - c = ((unsigned)(1 + (int) (256.0*rand()/(RAND_MAX+1.0)))-1); -#else - c = ((unsigned)(1 + (int) (256.0*random()/(RAND_MAX+1.0)))-1); -#endif - putchar (c); - } - } - return 0; -} diff --git a/tools/mpicalc.c b/tools/mpicalc.c deleted file mode 100644 index 915dcc32a..000000000 --- a/tools/mpicalc.c +++ /dev/null @@ -1,385 +0,0 @@ -/* mpitest.c - test the mpi functions - * Copyright (C) 1998, 1999, 2000, 2001 Free Software Foundation, Inc. - * - * This is an RPN calculator; values must be given in hex. - * Operation is like dc(1) except that the input/output radix is - * always 16 and you can use a '-' to prefix a negative number. - * Addition operators: ++ and --. All operators must be delimited by a blank - * - * - * 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 2 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, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, - * USA. - */ - -#include <config.h> -#include <stdio.h> -#include <stdlib.h> -#include <ctype.h> - -#include "util.h" -#include "mpi.h" -#include "i18n.h" - -#define STACKSIZE 100 -static MPI stack[STACKSIZE]; -static int stackidx; - - -const char * -strusage( int level ) -{ - const char *p; - switch( level ) { - case 10: - case 0: p = "mpicalc - v" VERSION "; " - "Copyright 1997 Werner Koch (dd9jn)" ; break; - case 13: p = "mpicalc"; break; - case 14: p = VERSION; break; - case 1: - case 11: p = "Usage: mpicalc (-h for help)"; - break; - case 2: - case 12: p = - "\nSyntax: mpicalc [options] [files]\n" - "MPI RPN calculator\n"; - break; - default: p = default_strusage(level); - } - return p; -} - - -static void -i18n_init(void) -{ -#ifdef ENABLE_NLS - setlocale( LC_ALL, "" ); - bindtextdomain( PACKAGE, G10_LOCALEDIR ); - textdomain( PACKAGE ); -#endif -} - - -static void -do_add(void) -{ - if( stackidx < 2 ) { - fputs("stack underflow\n",stderr); - return; - } - mpi_add( stack[stackidx-2], stack[stackidx-2], stack[stackidx-1] ); - stackidx--; -} - -static void -do_sub(void) -{ - if( stackidx < 2 ) { - fputs("stack underflow\n", stderr); - return; - } - mpi_sub( stack[stackidx-2], stack[stackidx-2], stack[stackidx-1] ); - stackidx--; -} - -static void -do_inc(void) -{ - if( stackidx < 1 ) { - fputs("stack underflow\n", stderr); - return; - } - mpi_add_ui( stack[stackidx-1], stack[stackidx-1], 1 ); -} - -static void -do_dec(void) -{ - if( stackidx < 1 ) { - fputs("stack underflow\n", stderr); - return; - } - /* mpi_sub_ui( stack[stackidx-1], stack[stackidx-1], 1 ); */ -} - -static void -do_mul(void) -{ - if( stackidx < 2 ) { - fputs("stack underflow\n", stderr); - return; - } - mpi_mul( stack[stackidx-2], stack[stackidx-2], stack[stackidx-1] ); - stackidx--; -} - -static void -do_mulm(void) -{ - if( stackidx < 3 ) { - fputs("stack underflow\n", stderr); - return; - } - mpi_mulm( stack[stackidx-3], stack[stackidx-3], - stack[stackidx-2], stack[stackidx-1] ); - stackidx -= 2; -} - -static void -do_div(void) -{ - if( stackidx < 2 ) { - fputs("stack underflow\n", stderr); - return; - } - mpi_fdiv_q( stack[stackidx-2], stack[stackidx-2], stack[stackidx-1] ); - stackidx--; -} - -static void -do_rem(void) -{ - if( stackidx < 2 ) { - fputs("stack underflow\n", stderr); - return; - } - mpi_fdiv_r( stack[stackidx-2], stack[stackidx-2], stack[stackidx-1] ); - stackidx--; -} - -static void -do_powm(void) -{ - MPI a; - if( stackidx < 3 ) { - fputs("stack underflow\n", stderr); - return; - } - a= mpi_alloc(10); - mpi_powm( a, stack[stackidx-3], stack[stackidx-2], stack[stackidx-1] ); - mpi_free(stack[stackidx-3]); - stack[stackidx-3] = a; - stackidx -= 2; -} - -static void -do_inv(void) -{ - MPI a = mpi_alloc(40); - if( stackidx < 2 ) { - fputs("stack underflow\n", stderr); - return; - } - mpi_invm( a, stack[stackidx-2], stack[stackidx-1] ); - mpi_set(stack[stackidx-2],a); - mpi_free(a); - stackidx--; -} - -static void -do_gcd(void) -{ - MPI a = mpi_alloc(40); - if( stackidx < 2 ) { - fputs("stack underflow\n", stderr); - return; - } - mpi_gcd( a, stack[stackidx-2], stack[stackidx-1] ); - mpi_set(stack[stackidx-2],a); - mpi_free(a); - stackidx--; -} - -static void -do_rshift(void) -{ - if( stackidx < 1 ) { - fputs("stack underflow\n", stderr); - return; - } - mpi_rshift( stack[stackidx-1],stack[stackidx-1], 1 ); -} - - -int -main(int argc, char **argv) -{ - static ARGPARSE_OPTS opts[] = { - {0} }; - ARGPARSE_ARGS pargs; - int i, c; - int state = 0; - char strbuf[1000]; - int stridx=0; - - pargs.argc = &argc; - pargs.argv = &argv; - pargs.flags = 0; - - i18n_init(); - while( arg_parse( &pargs, opts) ) { - switch( pargs.r_opt ) { - default : pargs.err = 2; break; - } - } - if( argc ) - usage(1); - - - for(i=0; i < STACKSIZE; i++ ) - stack[i] = NULL; - stackidx =0; - - while( (c=getc(stdin)) != EOF ) { - if( !state ) { /* waiting */ - if( isdigit(c) ) { - state = 1; - ungetc(c, stdin); - strbuf[0] = '0'; - strbuf[1] = 'x'; - stridx=2; - } - else if( isspace(c) ) - ; - else { - switch(c) { - case '+': - if( (c=getc(stdin)) == '+' ) - do_inc(); - else { - ungetc(c, stdin); - do_add(); - } - break; - case '-': - if( (c=getc(stdin)) == '-' ) - do_dec(); - else if( isdigit(c) || (c >='A' && c <= 'F') ) { - state = 1; - ungetc(c, stdin); - strbuf[0] = '-'; - strbuf[1] = '0'; - strbuf[2] = 'x'; - stridx=3; - } - else { - ungetc(c, stdin); - do_sub(); - } - break; - case '*': - do_mul(); - break; - case 'm': - do_mulm(); - break; - case '/': - do_div(); - break; - case '%': - do_rem(); - break; - case '^': - do_powm(); - break; - case 'I': - do_inv(); - break; - case 'G': - do_gcd(); - break; - case '>': - do_rshift(); - break; - case 'i': /* dummy */ - if( !stackidx ) - fputs("stack underflow\n", stderr); - else { - mpi_free(stack[stackidx-1]); - stackidx--; - } - break; - case 'd': /* duplicate the tos */ - if( !stackidx ) - fputs("stack underflow\n", stderr); - else if( stackidx < STACKSIZE ) { - mpi_free(stack[stackidx]); - stack[stackidx] = mpi_copy( stack[stackidx-1] ); - stackidx++; - } - else - fputs("stack overflow\n", stderr); - break; - case 'c': - for(i=0; i < stackidx; i++ ) - mpi_free(stack[i]), stack[i] = NULL; - stackidx = 0; - break; - case 'p': /* print the tos */ - if( !stackidx ) - puts("stack is empty"); - else { - mpi_print(stdout, stack[stackidx-1], 1 ); - putchar('\n'); - } - break; - case 'f': /* print the stack */ - for( i = stackidx-1 ; i >= 0; i-- ) { - printf("[%2d]: ", i ); - mpi_print(stdout, stack[i], 1 ); - putchar('\n'); - } - break; - default: - fputs("invalid operator\n", stderr); - } - } - } - else if( state == 1 ) { /* in a number */ - if( !isxdigit(c) ) { /* store the number */ - state = 0; - ungetc(c, stdin); - if( stridx < 1000 ) - strbuf[stridx] = 0; - - if( stackidx < STACKSIZE ) { - if( !stack[stackidx] ) - stack[stackidx] = mpi_alloc(10); - if( mpi_fromstr(stack[stackidx], strbuf) ) - fputs("invalid number\n", stderr); - else - stackidx++; - } - else - fputs("stack overflow\n", stderr); - } - else { /* store digit */ - if( stridx < 999 ) - strbuf[stridx++] = c; - else if( stridx == 999 ) { - strbuf[stridx] = 0; - fputs("string too large - truncated\n", stderr); - stridx++; - } - } - } - - } - for(i=0; i < stackidx; i++ ) - mpi_free(stack[i]); - return 0; -} diff --git a/tools/no-libgcrypt.c b/tools/no-libgcrypt.c new file mode 100644 index 000000000..636df10c6 --- /dev/null +++ b/tools/no-libgcrypt.c @@ -0,0 +1,112 @@ +/* no-libgcrypt.c - Replacement functions for libgcrypt. + * Copyright (C) 2003 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 2 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. + */ + +#include <config.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> + +#include "../common/util.h" +#include "i18n.h" + + +/* Replace libgcrypt's malloc functions which are used by + ../jnlib/libjnlib.a . ../common/util.h defines macros to map them + to xmalloc etc. */ +static void +out_of_core (void) +{ + log_fatal (_("error allocating enough memory: %s\n"), strerror (errno)); +} + + +void * +gcry_malloc (size_t n) +{ + return malloc (n); +} + +void * +gcry_xmalloc (size_t n) +{ + void *p = malloc (n); + if (!p) + out_of_core (); + return p; +} + +char * +gcry_strdup (const char *string) +{ + return malloc (strlen (string)+1); +} + + +void * +gcry_realloc (void *a, size_t n) +{ + return realloc (a, n); +} + +void * +gcry_xrealloc (void *a, size_t n) +{ + void *p = realloc (a, n); + if (!p) + out_of_core (); + return p; +} + + + +void * +gcry_calloc (size_t n, size_t m) +{ + return calloc (n, m); +} + +void * +gcry_xcalloc (size_t n, size_t m) +{ + void *p = calloc (n, m); + if (!p) + out_of_core (); + return p; +} + + +char * +gcry_xstrdup (const char *string) +{ + void *p = malloc (strlen (string)+1); + if (!p) + out_of_core (); + strcpy( p, string ); + return p; +} + +void +gcry_free (void *a) +{ + if (a) + free (a); +} diff --git a/tools/pgpgroup-to-gpggroup b/tools/pgpgroup-to-gpggroup deleted file mode 100755 index 5ea9990b6..000000000 --- a/tools/pgpgroup-to-gpggroup +++ /dev/null @@ -1,42 +0,0 @@ -#!/usr/bin/perl -w - -# pgpgroup-to-gpggroup - convert PGP groups to GnuPG groups -# Copyright (C) 2004 Free Software Foundation, Inc. -# -# This program 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 2 of the License, or -# (at your option) any later version. -# -# This program 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, write to the Free Software -# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - -# This program requires a PGP command line program that supports -# groups (note that the PGP 8 command line doesn't). - -$pgp="pgp -gvv 2>/dev/null"; - -open(PGP,"$pgp|") || die "Unable to call PGP: $!"; - -while(<PGP>) -{ - # If the line begins with a ">", then it is a new group. - - if(/^ > (\S+)/) - { - print "\ngroup $1 = "; - } - elsif(/\s+(0x\S+)/) - { - print "$1 "; - } -} - -print "\n"; -close(PGP); diff --git a/tools/rfc822parse.c b/tools/rfc822parse.c new file mode 100644 index 000000000..303ddad13 --- /dev/null +++ b/tools/rfc822parse.c @@ -0,0 +1,1258 @@ +/* rfc822parse.c - Simple mail and MIME parser + * Copyright (C) 1999, 2000 Werner Koch, Duesseldorf + * Copyright (C) 2003, 2004 g10 Code GmbH + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program 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 Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + + +/* According to RFC822 binary zeroes are allowed at many places. We do + * not handle this correct especially in the field parsing code. It + * should be easy to fix and the API provides a interfaces which + * returns the length but in addition makes sure that returned strings + * are always ended by a \0. + * + * Furthermore, the case of field names is changed and thus it is not + * always a good idea to use these modified header + * lines (e.g. signatures may break). + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <errno.h> +#include <stdarg.h> +#include <assert.h> + +#include "rfc822parse.h" + +enum token_type + { + tSPACE, + tATOM, + tQUOTED, + tDOMAINLIT, + tSPECIAL + }; + +/* For now we directly use our TOKEN as the parse context */ +typedef struct rfc822parse_field_context *TOKEN; +struct rfc822parse_field_context +{ + TOKEN next; + enum token_type type; + struct { + unsigned int cont:1; + unsigned int lowered:1; + } flags; + /*TOKEN owner_pantry; */ + char data[1]; +}; + +struct hdr_line +{ + struct hdr_line *next; + int cont; /* This is a continuation of the previous line. */ + unsigned char line[1]; +}; + +typedef struct hdr_line *HDR_LINE; + + +struct part +{ + struct part *right; /* The next part. */ + struct part *down; /* A contained part. */ + HDR_LINE hdr_lines; /* Header lines os that part. */ + HDR_LINE *hdr_lines_tail; /* Helper for adding lines. */ + char *boundary; /* Only used in the first part. */ +}; +typedef struct part *part_t; + +struct rfc822parse_context +{ + rfc822parse_cb_t callback; + void *callback_value; + int callback_error; + int in_body; + int in_preamble; /* Wether we are before the first boundary. */ + part_t parts; /* The tree of parts. */ + part_t current_part; /* Whom we are processing (points into parts). */ + const char *boundary; /* Current boundary. */ +}; + +static HDR_LINE find_header (rfc822parse_t msg, const char *name, + int which, HDR_LINE * rprev); + + +static size_t +length_sans_trailing_ws (const unsigned char *line, size_t len) +{ + const unsigned char *p, *mark; + size_t n; + + for (mark=NULL, p=line, n=0; n < len; n++, p++) + { + if (strchr (" \t\r\n", *p )) + { + if( !mark ) + mark = p; + } + else + mark = NULL; + } + + if (mark) + return mark - line; + return len; +} + + +static void +lowercase_string (unsigned char *string) +{ + for (; *string; string++) + if (*string >= 'A' && *string <= 'Z') + *string = *string - 'A' + 'a'; +} + +/* Transform a header name into a standard capitalized format; i.e + "Content-Type". Conversion stops at the colon. As usual we don't + use the localized versions of ctype.h. + */ +static void +capitalize_header_name (unsigned char *name) +{ + int first = 1; + + for (; *name && *name != ':'; name++) + if (*name == '-') + first = 1; + else if (first) + { + if (*name >= 'a' && *name <= 'z') + *name = *name - 'a' + 'A'; + first = 0; + } + else if (*name >= 'A' && *name <= 'Z') + *name = *name - 'A' + 'a'; +} + +#ifndef HAVE_STPCPY +static char * +stpcpy (char *a,const char *b) +{ + while (*b) + *a++ = *b++; + *a = 0; + + return (char*)a; +} +#endif + + +/* If a callback has been registerd, call it for the event of type + EVENT. */ +static int +do_callback (rfc822parse_t msg, rfc822parse_event_t event) +{ + int rc; + + if (!msg->callback || msg->callback_error) + return 0; + rc = msg->callback (msg->callback_value, event, msg); + if (rc) + msg->callback_error = rc; + return rc; +} + +static part_t +new_part (void) +{ + part_t part; + + part = calloc (1, sizeof *part); + if (part) + { + part->hdr_lines_tail = &part->hdr_lines; + } + return part; +} + + +static void +release_part (part_t part) +{ + part_t tmp; + HDR_LINE hdr, hdr2; + + for (; part; part = tmp) + { + tmp = part->right; + if (part->down) + release_part (part->down); + for (hdr = part->hdr_lines; hdr; hdr = hdr2) + { + hdr2 = hdr->next; + free (hdr); + } + free (part->boundary); + free (part); + } +} + + +static void +release_handle_data (rfc822parse_t msg) +{ + release_part (msg->parts); + msg->parts = NULL; + msg->current_part = NULL; + msg->boundary = NULL; +} + + +/* Create a new parsing context for an entire rfc822 message and + return it. CB and CB_VALUE may be given to callback for certain + events. NULL is returned on error with errno set appropriately. */ +rfc822parse_t +rfc822parse_open (rfc822parse_cb_t cb, void *cb_value) +{ + rfc822parse_t msg = calloc (1, sizeof *msg); + if (msg) + { + msg->parts = msg->current_part = new_part (); + if (!msg->parts) + { + free (msg); + msg = NULL; + } + else + { + msg->callback = cb; + msg->callback_value = cb_value; + if (do_callback (msg, RFC822PARSE_OPEN)) + { + release_handle_data (msg); + free (msg); + msg = NULL; + } + } + } + return msg; +} + + +void +rfc822parse_cancel (rfc822parse_t msg) +{ + if (msg) + { + do_callback (msg, RFC822PARSE_CANCEL); + release_handle_data (msg); + free (msg); + } +} + + +void +rfc822parse_close (rfc822parse_t msg) +{ + if (msg) + { + do_callback (msg, RFC822PARSE_CLOSE); + release_handle_data (msg); + free (msg); + } +} + +static part_t +find_parent (part_t tree, part_t target) +{ + part_t part; + + for (part = tree->down; part; part = part->right) + { + if (part == target) + return tree; /* Found. */ + if (part->down) + { + part_t tmp = find_parent (part, target); + if (tmp) + return tmp; + } + } + return NULL; +} + +static void +set_current_part_to_parent (rfc822parse_t msg) +{ + part_t parent; + + assert (msg->current_part); + parent = find_parent (msg->parts, msg->current_part); + if (!parent) + return; /* Already at the top. */ + +#ifndef NDEBUG + { + part_t part; + for (part = parent->down; part; part = part->right) + if (part == msg->current_part) + break; + assert (part); + } +#endif + msg->current_part = parent; + + parent = find_parent (msg->parts, parent); + msg->boundary = parent? parent->boundary: NULL; +} + + + +/**************** + * We have read in all header lines and are about to receive the body + * part. The delimiter line has already been processed. + * + * FIXME: we's better return an error in case of memory failures. + */ +static int +transition_to_body (rfc822parse_t msg) +{ + rfc822parse_field_t ctx; + int rc; + + rc = do_callback (msg, RFC822PARSE_T2BODY); + if (!rc) + { + /* Store the boundary if we have multipart type. */ + ctx = rfc822parse_parse_field (msg, "Content-Type", -1); + if (ctx) + { + const char *s; + + s = rfc822parse_query_media_type (ctx, NULL); + if (s && !strcmp (s,"multipart")) + { + s = rfc822parse_query_parameter (ctx, "boundary", 0); + if (s) + { + assert (!msg->current_part->boundary); + msg->current_part->boundary = malloc (strlen (s) + 1); + if (msg->current_part->boundary) + { + part_t part; + + strcpy (msg->current_part->boundary, s); + msg->boundary = msg->current_part->boundary; + part = new_part (); + if (!part) + { + int save_errno = errno; + rfc822parse_release_field (ctx); + errno = save_errno; + return -1; + } + rc = do_callback (msg, RFC822PARSE_LEVEL_DOWN); + assert (!msg->current_part->down); + msg->current_part->down = part; + msg->current_part = part; + msg->in_preamble = 1; + } + } + } + rfc822parse_release_field (ctx); + } + } + + return rc; +} + +/* We have just passed a MIME boundary and need to prepare for new part. + headers. */ +static int +transition_to_header (rfc822parse_t msg) +{ + part_t part; + + assert (msg->current_part); + assert (!msg->current_part->right); + + part = new_part (); + if (!part) + return -1; + + msg->current_part->right = part; + msg->current_part = part; + return 0; +} + + +static int +insert_header (rfc822parse_t msg, const unsigned char *line, size_t length) +{ + HDR_LINE hdr; + + assert (msg->current_part); + if (!length) + { + msg->in_body = 1; + return transition_to_body (msg); + } + + if (!msg->current_part->hdr_lines) + do_callback (msg, RFC822PARSE_BEGIN_HEADER); + + length = length_sans_trailing_ws (line, length); + hdr = malloc (sizeof (*hdr) + length); + if (!hdr) + return -1; + hdr->next = NULL; + hdr->cont = (*line == ' ' || *line == '\t'); + memcpy (hdr->line, line, length); + hdr->line[length] = 0; /* Make it a string. */ + + /* Transform a field name into canonical format. */ + if (!hdr->cont && strchr (line, ':')) + capitalize_header_name (hdr->line); + + *msg->current_part->hdr_lines_tail = hdr; + msg->current_part->hdr_lines_tail = &hdr->next; + + /* Lets help the caller to prevent mail loops and issue an event for + * every Received header. */ + if (length >= 9 && !memcmp (line, "Received:", 9)) + do_callback (msg, RFC822PARSE_RCVD_SEEN); + return 0; +} + + +/**************** + * Note: We handle the body transparent to allow binary zeroes in it. + */ +static int +insert_body (rfc822parse_t msg, const unsigned char *line, size_t length) +{ + int rc = 0; + + if (length > 2 && *line == '-' && line[1] == '-' && msg->boundary) + { + size_t blen = strlen (msg->boundary); + + if (length == blen + 2 + && !memcmp (line+2, msg->boundary, blen)) + { + rc = do_callback (msg, RFC822PARSE_BOUNDARY); + msg->in_body = 0; + if (!rc && !msg->in_preamble) + rc = transition_to_header (msg); + msg->in_preamble = 0; + } + else if (length == blen + 4 + && line[length-2] =='-' && line[length-1] == '-' + && !memcmp (line+2, msg->boundary, blen)) + { + rc = do_callback (msg, RFC822PARSE_LAST_BOUNDARY); + msg->boundary = NULL; /* No current boundary anymore. */ + set_current_part_to_parent (msg); + + /* Fixme: The next should actually be send right before the + next boundary, so that we can mark the epilogue. */ + if (!rc) + rc = do_callback (msg, RFC822PARSE_LEVEL_UP); + } + } + if (msg->in_preamble && !rc) + rc = do_callback (msg, RFC822PARSE_PREAMBLE); + + return rc; +} + +/* Insert the next line into the parser. Return 0 on success or true + on error with errno set appropriately. */ +int +rfc822parse_insert (rfc822parse_t msg, const unsigned char *line, size_t length) +{ + return (msg->in_body + ? insert_body (msg, line, length) + : insert_header (msg, line, length)); +} + + +/* Tell the parser that we have finished the message. */ +int +rfc822parse_finish (rfc822parse_t msg) +{ + return do_callback (msg, RFC822PARSE_FINISH); +} + + + +/**************** + * Get a copy of a header line. The line is returned as one long + * string with LF to separate the continuation line. Caller must free + * the return buffer. WHICH may be used to enumerate over all lines. + * Wildcards are allowed. This function works on the current headers; + * i.e. the regular mail headers or the MIME headers of the current + * part. + * + * WHICH gives the mode: + * -1 := Take the last occurence + * n := Take the n-th one. + * + * Returns a newly allocated buffer or NULL on error. errno is set in + * case of a memory failure or set to 0 if the requested field is not + * available. + * + * If VALUEOFF is not NULL it will receive the offset of the first non + * space character in the value part of the line (i.e. after the first + * colon). + */ +char * +rfc822parse_get_field (rfc822parse_t msg, const char *name, int which, + size_t *valueoff) +{ + HDR_LINE h, h2; + char *buf, *p; + size_t n; + + h = find_header (msg, name, which, NULL); + if (!h) + { + errno = 0; + return NULL; /* no such field */ + } + + n = strlen (h->line) + 1; + for (h2 = h->next; h2 && h2->cont; h2 = h2->next) + n += strlen (h2->line) + 1; + + buf = p = malloc (n); + if (buf) + { + p = stpcpy (p, h->line); + *p++ = '\n'; + for (h2 = h->next; h2 && h2->cont; h2 = h2->next) + { + p = stpcpy (p, h2->line); + *p++ = '\n'; + } + p[-1] = 0; + } + + if (valueoff) + { + p = strchr (buf, ':'); + if (!p) + *valueoff = 0; /* Oops: should never happen. */ + else + { + p++; + while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') + p++; + *valueoff = p - buf; + } + } + + return buf; +} + + +/**************** + * Enumerate all header. Caller has to provide the address of a pointer + * which has to be initialzed to NULL, the caller should then never change this + * pointer until he has closed the enumeration by passing again the address + * of the pointer but with msg set to NULL. + * The function returns pointers to all the header lines or NULL when + * all lines have been enumerated or no headers are available. + */ +const char * +rfc822parse_enum_header_lines (rfc822parse_t msg, void **context) +{ + HDR_LINE l; + + if (!msg) /* Close. */ + return NULL; + + if (*context == msg || !msg->current_part) + return NULL; + + l = *context ? (HDR_LINE) *context : msg->current_part->hdr_lines; + + if (l) + { + *context = l->next ? (void *) (l->next) : (void *) msg; + return l->line; + } + *context = msg; /* Mark end of list. */ + return NULL; +} + + + +/**************** + * Find a header field. If the Name does end in an asterisk this is meant + * to be a wildcard. + * + * which -1 : Retrieve the last field + * >0 : Retrieve the n-th field + + * RPREV may be used to return the predecessor of the returned field; + * which may be NULL for the very first one. It has to be initialzed + * to either NULL in which case the search start at the first header line, + * or it may point to a headerline, where the search should start + */ +static HDR_LINE +find_header (rfc822parse_t msg, const char *name, int which, HDR_LINE *rprev) +{ + HDR_LINE hdr, prev = NULL, mark = NULL; + unsigned char *p; + size_t namelen, n; + int found = 0; + int glob = 0; + + if (!msg->current_part) + return NULL; + + namelen = strlen (name); + if (namelen && name[namelen - 1] == '*') + { + namelen--; + glob = 1; + } + + hdr = msg->current_part->hdr_lines; + if (rprev && *rprev) + { + /* spool forward to the requested starting place. + * we cannot simply set this as we have to return + * the previous list element too */ + for (; hdr && hdr != *rprev; prev = hdr, hdr = hdr->next) + ; + } + + for (; hdr; prev = hdr, hdr = hdr->next) + { + if (hdr->cont) + continue; + if (!(p = strchr (hdr->line, ':'))) + continue; /* invalid header, just skip it. */ + n = p - hdr->line; + if (!n) + continue; /* invalid name */ + if ((glob ? (namelen <= n) : (namelen == n)) + && !memcmp (hdr->line, name, namelen)) + { + found++; + if (which == -1) + mark = hdr; + else if (found == which) + { + if (rprev) + *rprev = prev; + return hdr; + } + } + } + if (mark && rprev) + *rprev = prev; + return mark; +} + + + +static const char * +skip_ws (const char *s) +{ + while (*s == ' ' || *s == '\t' || *s == '\r' || *s == '\n') + s++; + return s; +} + + +static void +release_token_list (TOKEN t) +{ + while (t) + { + TOKEN t2 = t->next; + /* fixme: If we have owner_pantry, put the token back to + * this pantry so that it can be reused later */ + free (t); + t = t2; + } +} + + +static TOKEN +new_token (enum token_type type, const char *buf, size_t length) +{ + TOKEN t; + + /* fixme: look through our pantries to find a suitable + * token for reuse */ + t = malloc (sizeof *t + length); + if (t) + { + t->next = NULL; + t->type = type; + memset (&t->flags, 0, sizeof (t->flags)); + t->data[0] = 0; + if (buf) + { + memcpy (t->data, buf, length); + t->data[length] = 0; /* Make sure it is a C string. */ + } + else + t->data[0] = 0; + } + return t; +} + +static TOKEN +append_to_token (TOKEN old, const char *buf, size_t length) +{ + size_t n = strlen (old->data); + TOKEN t; + + t = malloc (sizeof *t + n + length); + if (t) + { + t->next = old->next; + t->type = old->type; + t->flags = old->flags; + memcpy (t->data, old->data, n); + memcpy (t->data + n, buf, length); + t->data[n + length] = 0; + old->next = NULL; + release_token_list (old); + } + return t; +} + + + +/* + Parse a field into tokens as defined by rfc822. + */ +static TOKEN +parse_field (HDR_LINE hdr) +{ + static const char specials[] = "<>@.,;:\\[]\"()"; + static const char specials2[] = "<>@.,;:"; + static const char tspecials[] = "/?=<>@,;:\\[]\"()"; + static const char tspecials2[] = "/?=<>@.,;:"; /* FIXME: really + include '.'?*/ + static struct + { + const unsigned char *name; + size_t namelen; + } tspecial_header[] = { + { "Content-Type", 12}, + { "Content-Transfer-Encoding", 25}, + { "Content-Disposition", 19}, + { NULL, 0} + }; + const char *delimiters; + const char *delimiters2; + const unsigned char *line, *s, *s2; + size_t n; + int i, invalid = 0; + TOKEN t, tok, *tok_tail; + + errno = 0; + if (!hdr) + return NULL; + + tok = NULL; + tok_tail = &tok; + + line = hdr->line; + if (!(s = strchr (line, ':'))) + return NULL; /* oops */ + + n = s - line; + if (!n) + return NULL; /* oops: invalid name */ + + delimiters = specials; + delimiters2 = specials2; + for (i = 0; tspecial_header[i].name; i++) + { + if (n == tspecial_header[i].namelen + && !memcmp (line, tspecial_header[i].name, n)) + { + delimiters = tspecials; + delimiters2 = tspecials2; + break; + } + } + + s++; /* Move over the colon. */ + for (;;) + { + if (!*s) + { + if (!hdr->next || !hdr->next->cont) + break; + hdr = hdr->next; + s = hdr->line; + } + + if (*s == '(') + { + int level = 1; + int in_quote = 0; + + invalid = 0; + for (s++;; s++) + { + if (!*s) + { + if (!hdr->next || !hdr->next->cont) + break; + hdr = hdr->next; + s = hdr->line; + } + + if (in_quote) + { + if (*s == '\"') + in_quote = 0; + else if (*s == '\\' && s[1]) /* what about continuation? */ + s++; + } + else if (*s == ')') + { + if (!--level) + break; + } + else if (*s == '(') + level++; + else if (*s == '\"') + in_quote = 1; + } + if (!*s) + ; /* Actually this is an error, but we don't care about it. */ + else + s++; + } + else if (*s == '\"' || *s == '[') + { + /* We do not check for non-allowed nesting of domainliterals */ + int term = *s == '\"' ? '\"' : ']'; + invalid = 0; + s++; + t = NULL; + + for (;;) + { + for (s2 = s; *s2; s2++) + { + if (*s2 == term) + break; + else if (*s2 == '\\' && s2[1]) /* what about continuation? */ + s2++; + } + + t = (t + ? append_to_token (t, s, s2 - s) + : new_token (term == '\"'? tQUOTED : tDOMAINLIT, s, s2 - s)); + if (!t) + goto failure; + + if (*s2 || !hdr->next || !hdr->next->cont) + break; + hdr = hdr->next; + s = hdr->line; + } + *tok_tail = t; + tok_tail = &t->next; + s = s2; + if (*s) + s++; /* skip the delimiter */ + } + else if ((s2 = strchr (delimiters2, *s))) + { /* Special characters which are not handled above. */ + invalid = 0; + t = new_token (tSPECIAL, s, 1); + if (!t) + goto failure; + *tok_tail = t; + tok_tail = &t->next; + s++; + } + else if (*s == ' ' || *s == '\t' || *s == '\r' || *s == '\n') + { + invalid = 0; + s = skip_ws (s + 1); + } + else if (*s > 0x20 && !(*s & 128)) + { /* Atom. */ + invalid = 0; + for (s2 = s + 1; *s2 > 0x20 + && !(*s2 & 128) && !strchr (delimiters, *s2); s2++) + ; + t = new_token (tATOM, s, s2 - s); + if (!t) + goto failure; + *tok_tail = t; + tok_tail = &t->next; + s = s2; + } + else + { /* Invalid character. */ + if (!invalid) + { /* For parsing we assume only one space. */ + t = new_token (tSPACE, NULL, 0); + if (!t) + goto failure; + *tok_tail = t; + tok_tail = &t->next; + invalid = 1; + } + s++; + } + } + + return tok; + + failure: + { + int save = errno; + release_token_list (tok); + errno = save; + } + return NULL; +} + + + + +/**************** + * Find and parse a header field. + * WHICH indicates what to do if there are multiple instance of the same + * field (like "Received"); the following value are defined: + * -1 := Take the last occurence + * 0 := Reserved + * n := Take the n-th one. + * Returns a handle for further operations on the parse context of the field + * or NULL if the field was not found. + */ +rfc822parse_field_t +rfc822parse_parse_field (rfc822parse_t msg, const char *name, int which) +{ + HDR_LINE hdr; + + if (!which) + return NULL; + + hdr = find_header (msg, name, which, NULL); + if (!hdr) + return NULL; + return parse_field (hdr); +} + +void +rfc822parse_release_field (rfc822parse_field_t ctx) +{ + if (ctx) + release_token_list (ctx); +} + + + +/**************** + * Check whether T points to a parameter. + * A parameter starts with a semicolon and it is assumed that t + * points to exactly this one. + */ +static int +is_parameter (TOKEN t) +{ + t = t->next; + if (!t || t->type != tATOM) + return 0; + t = t->next; + if (!t || !(t->type == tSPECIAL && t->data[0] == '=')) + return 0; + t = t->next; + if (!t) + return 1; /* We assume that an non existing value is an empty one. */ + return t->type == tQUOTED || t->type == tATOM; +} + +/* + Some header (Content-type) have a special syntax where attribute=value + pairs are used after a leading semicolon. The parse_field code + knows about these fields and changes the parsing to the one defined + in RFC2045. + Returns a pointer to the value which is valid as long as the + parse context is valid; NULL is returned in case that attr is not + defined in the header, a missing value is reppresented by an empty string. + + With LOWER_VALUE set to true, a matching field valuebe be + lowercased. + + Note, that ATTR should be lowercase. + */ +const char * +rfc822parse_query_parameter (rfc822parse_field_t ctx, const char *attr, + int lower_value) +{ + TOKEN t, a; + + for (t = ctx; t; t = t->next) + { + /* skip to the next semicolon */ + for (; t && !(t->type == tSPECIAL && t->data[0] == ';'); t = t->next) + ; + if (!t) + return NULL; + if (is_parameter (t)) + { /* Look closer. */ + a = t->next; /* We know that this is an atom */ + if ( !a->flags.lowered ) + { + lowercase_string (a->data); + a->flags.lowered = 1; + } + if (!strcmp (a->data, attr)) + { /* found */ + t = a->next->next; + /* Either T is now an atom, a quoted string or NULL in + * which case we return an empty string. */ + + if ( lower_value && t && !t->flags.lowered ) + { + lowercase_string (t->data); + t->flags.lowered = 1; + } + return t ? t->data : ""; + } + } + } + return NULL; +} + +/**************** + * This function may be used for the Content-Type header to figure out + * the media type and subtype. Note, that the returned strings are + * guaranteed to be lowercase as required by MIME. + * + * Returns: a pointer to the media type and if subtype is not NULL, + * a pointer to the subtype. + */ +const char * +rfc822parse_query_media_type (rfc822parse_field_t ctx, const char **subtype) +{ + TOKEN t = ctx; + const char *type; + + if (t->type != tATOM) + return NULL; + if (!t->flags.lowered) + { + lowercase_string (t->data); + t->flags.lowered = 1; + } + type = t->data; + t = t->next; + if (!t || t->type != tSPECIAL || t->data[0] != '/') + return NULL; + t = t->next; + if (!t || t->type != tATOM) + return NULL; + + if (subtype) + { + if (!t->flags.lowered) + { + lowercase_string (t->data); + t->flags.lowered = 1; + } + *subtype = t->data; + } + return type; +} + + + + + +#ifdef TESTING + +/* Internal debug function to print the structure of the message. */ +static void +dump_structure (rfc822parse_t msg, part_t part, int indent) +{ + if (!part) + { + printf ("*** Structure of this message:\n"); + part = msg->parts; + } + + for (; part; part = part->right) + { + rfc822parse_field_t ctx; + part_t save_part; /* ugly hack - we should have a function to + get part inforation. */ + const char *s; + + save_part = msg->current_part; + msg->current_part = part; + ctx = rfc822parse_parse_field (msg, "Content-Type", -1); + msg->current_part = save_part; + if (ctx) + { + const char *s1, *s2; + s1 = rfc822parse_query_media_type (ctx, &s2); + if (s1) + printf ("*** %*s %s/%s", indent*2, "", s1, s2); + else + printf ("*** %*s [not found]", indent*2, ""); + + s = rfc822parse_query_parameter (ctx, "boundary", 0); + if (s) + printf (" (boundary=\"%s\")", s); + rfc822parse_release_field (ctx); + } + else + printf ("*** %*s text/plain [assumed]", indent*2, ""); + putchar('\n'); + + if (part->down) + dump_structure (msg, part->down, indent + 1); + } + +} + + + +static void +show_param (rfc822parse_field_t ctx, const char *name) +{ + const char *s; + + if (!ctx) + return; + s = rfc822parse_query_parameter (ctx, name, 0); + if (s) + printf ("*** %s: `%s'\n", name, s); +} + + + +static void +show_event (rfc822parse_event_t event) +{ + const char *s; + + switch (event) + { + case RFC822PARSE_OPEN: s= "Open"; break; + case RFC822PARSE_CLOSE: s= "Close"; break; + case RFC822PARSE_CANCEL: s= "Cancel"; break; + case RFC822PARSE_T2BODY: s= "T2Body"; break; + case RFC822PARSE_FINISH: s= "Finish"; break; + case RFC822PARSE_RCVD_SEEN: s= "Rcvd_Seen"; break; + case RFC822PARSE_BOUNDARY: s= "Boundary"; break; + case RFC822PARSE_LAST_BOUNDARY: s= "Last_Boundary"; break; + default: s= "***invalid event***"; break; + } + printf ("*** got RFC822 event %s\n", s); +} + +static int +msg_cb (void *dummy_arg, rfc822parse_event_t event, rfc822parse_t msg) +{ + show_event (event); + if (event == RFC822PARSE_T2BODY) + { + rfc822parse_field_t ctx; + void *ectx; + const char *line; + + for (ectx=NULL; (line = rfc822parse_enum_header_lines (msg, &ectx)); ) + { + printf ("*** HDR: %s\n", line); + } + rfc822parse_enum_header_lines (NULL, &ectx); /* Close enumerator. */ + + ctx = rfc822parse_parse_field (msg, "Content-Type", -1); + if (ctx) + { + const char *s1, *s2; + s1 = rfc822parse_query_media_type (ctx, &s2); + if (s1) + printf ("*** media: `%s/%s'\n", s1, s2); + else + printf ("*** media: [not found]\n"); + show_param (ctx, "boundary"); + show_param (ctx, "protocol"); + rfc822parse_release_field (ctx); + } + else + printf ("*** media: text/plain [assumed]\n"); + + } + + + return 0; +} + + + +int +main (int argc, char **argv) +{ + char line[5000]; + size_t length; + rfc822parse_t msg; + + msg = rfc822parse_open (msg_cb, NULL); + if (!msg) + abort (); + + while (fgets (line, sizeof (line), stdin)) + { + length = strlen (line); + if (length && line[length - 1] == '\n') + line[--length] = 0; + if (length && line[length - 1] == '\r') + line[--length] = 0; + if (rfc822parse_insert (msg, line, length)) + abort (); + } + + dump_structure (msg, NULL, 0); + + rfc822parse_close (msg); + return 0; +} +#endif + +/* +Local Variables: +compile-command: "gcc -Wall -g -DTESTING -o rfc822parse rfc822parse.c" +End: +*/ diff --git a/tools/rfc822parse.h b/tools/rfc822parse.h new file mode 100644 index 000000000..8a56c51cb --- /dev/null +++ b/tools/rfc822parse.h @@ -0,0 +1,81 @@ +/* rfc822parse.h - Simple mail and MIME parser + * Copyright (C) 1999 Werner Koch, Duesseldorf + * Copyright (C) 2003, g10 Code GmbH + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program 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 Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +#ifndef RFC822PARSE_H +#define RFC822PARSE_H + +struct rfc822parse_context; +typedef struct rfc822parse_context *rfc822parse_t; + +typedef enum + { + RFC822PARSE_OPEN = 1, + RFC822PARSE_CLOSE, + RFC822PARSE_CANCEL, + RFC822PARSE_T2BODY, + RFC822PARSE_FINISH, + RFC822PARSE_RCVD_SEEN, + RFC822PARSE_LEVEL_DOWN, + RFC822PARSE_LEVEL_UP, + RFC822PARSE_BOUNDARY, + RFC822PARSE_LAST_BOUNDARY, + RFC822PARSE_BEGIN_HEADER, + RFC822PARSE_PREAMBLE, + RFC822PARSE_EPILOGUE + } +rfc822parse_event_t; + +struct rfc822parse_field_context; +typedef struct rfc822parse_field_context *rfc822parse_field_t; + + +typedef int (*rfc822parse_cb_t) (void *opaque, + rfc822parse_event_t event, + rfc822parse_t msg); + + +rfc822parse_t rfc822parse_open (rfc822parse_cb_t cb, void *opaque_value); + +void rfc822parse_close (rfc822parse_t msg); + +void rfc822parse_cancel (rfc822parse_t msg); +int rfc822parse_finish (rfc822parse_t msg); + +int rfc822parse_insert (rfc822parse_t msg, + const unsigned char *line, size_t length); + +char *rfc822parse_get_field (rfc822parse_t msg, const char *name, int which, + size_t *valueoff); + +const char *rfc822parse_enum_header_lines (rfc822parse_t msg, void **context); + +rfc822parse_field_t rfc822parse_parse_field (rfc822parse_t msg, + const char *name, + int which); + +void rfc822parse_release_field (rfc822parse_field_t field); + +const char *rfc822parse_query_parameter (rfc822parse_field_t ctx, + const char *attr, int lower_value); + +const char *rfc822parse_query_media_type (rfc822parse_field_t ctx, + const char **subtype); + +#endif /*RFC822PARSE_H */ diff --git a/tools/ring-a-party b/tools/ring-a-party deleted file mode 100755 index 01e0f3ff3..000000000 --- a/tools/ring-a-party +++ /dev/null @@ -1,114 +0,0 @@ -#!/bin/sh -# ring-a-party - print a keyring suitable for a key signing party -# Copyright (C) 2000, 2001 Free Software Foundation, Inc. -# -# 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. - -if [ $# -lt 1 ]; then - echo "usage: ring-a-party keyring [headerline]" >&2 - exit 1 -fi - -keyring="$1" -hdrline="$1" -if [ $# -gt 1 ]; then - hdrline="$2" -fi - -if [ ! -f $keyring ]; then - echo "ring-a-party: '$keyring': no such file" >&2 - exit 1 -fi - -echo "ring-a-party: output will be written to 'a.pub'" >&2 - - -gpg --dry-run --with-fingerprint --with-colons $keyring \ - | gawk -v "KEYRING=$hdrline" ' -BEGIN { FS=":" - algos[1] = "RSA"; - algos[16] = "Elgamal"; - algos[17] = "DSA"; - any = 0; - lines = -1; - page = 0; - now = strftime("%b %d %H:%M %Y"); - } -END { - if (any) myflush(); -} -$1 == "pub" { - if( any ) myflush(); - uidcount = 0; - signencrypt = 0; - uids[uidcount++] = $10; - nbits = $3; - keyid = substr($5,9); - created = $6; - expires = $7; - algostr = mapalgo($4); - if( $4 == 20 || $4 == 1 ) signencrypt = 1; - any = 1; - } -$1 == "fpr" { fpr = $10 } -$1 == "uid" { uids[uidcount++] = $10 } -$1 == "sub" { if( $4 != 17 && $4 != 3 ) signencrypt=1 } - -function myflush() -{ - # fixme: take lines to print here into account - if( lines > 45 || lines == -1 ) { - if( lines != -1 ) printf "\f"; - page++; - printf "%s %-50.50s Page %d\n\n", now, KEYRING, page ; - printf " Type Bits KeyID Created Expires Algorithm Use\n\n"; - lines = 1; - } - printf "[ ] pub %04d 0x%s %10s %10s %-10s %15s\n", - nbits, keyid, created, expires == ""? "----------":expires, algostr, - signencrypt == 1? "Sign & Encrypt":"Sign only"; - length(fpr) == 40 ? printfpr20( fpr ) : printfpr16( fpr ); - lnes += 2; - for( i=0; i < uidcount; i++ ) { - printf "( ) uid %s\n", uids[i]; - lines++; - } - printf "\n\n"; - lines += 2; -} - -function mapalgo( no ) -{ - if( no in algos ) - return algos[no]; - return sprintf( "algoID=%ds", no ); -} - - -function printfpr16( s ) -{ - printf " f16 Fingerprint16 ="; - for(i=0; i < 16; i++ ) { - if( i == 8 ) printf " "; - printf " %s", substr( s, i*2+1, 2 ); - } - printf "\n" -} - -function printfpr20( s ) -{ - printf " f20 Fingerprint20 ="; - for(i=0; i < 10; i++ ) { - if( i == 5 ) printf " "; - printf " %s", substr( s, i*4+1, 4 ); - } - printf "\n" -} - -' | tee a.pub | gpg --print-mds diff --git a/tools/shmtest.c b/tools/shmtest.c deleted file mode 100644 index f48340508..000000000 --- a/tools/shmtest.c +++ /dev/null @@ -1,201 +0,0 @@ -/* shmtest.c - * Copyright (C) 1998, 1999, 2000, 2001 Free Software Foundation, Inc. - * - * 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 <config.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <errno.h> -#include <signal.h> -#include <unistd.h> -#ifdef HAVE_SYS_IPC_H -#include <sys/types.h> -#include <sys/ipc.h> -#endif -#ifdef HAVE_SYS_SHM_H -#include <sys/shm.h> -#endif -#include "util.h" -#include "ttyio.h" -#include "i18n.h" - -#ifdef HAVE_DOSISH_SYSTEM -int main( int argc, char **argv ) -{ - fprintf(stderr, "Sorry, not yet available for DOSish systems\n"); - exit(1); -} -#else - -static int serverpid = -1; - -static void -my_usage(void) -{ - fprintf(stderr, "usage: shmtest gpg-command-line\n"); - exit(1); -} - -const char * -strusage( int level ) -{ - return default_strusage(level); -} - -static void -i18n_init(void) -{ -#ifdef ENABLE_NLS - setlocale( LC_ALL, "" ); - bindtextdomain( PACKAGE, G10_LOCALEDIR ); - textdomain( PACKAGE ); -#endif -} - - -static void -do_get_string( int mode, const char *keyword, byte *area, size_t areasize ) -{ - size_t n, len; - char *p=NULL; - int yes=0; - - n = area[0] << 8 | area[1]; - /* fixme: do some sanity checks here */ - if( mode == 1 ) - p = tty_get( keyword ); - else if( mode == 3 ) - p = tty_get_hidden( keyword ); - else - yes = tty_get_answer_is_yes( keyword ); - if( p ) { - len = strlen(p); - memcpy( area+n+2, p, len ); - area[n] = len >> 8; - area[n+1] = len; - xfree(p); - } - else { /* bool */ - area[n] = 0; - area[n+1] = 1; - area[n+2] = yes; - } - area[3] = 1; /* we should better use a semaphore */ - kill( serverpid, SIGUSR1 ); -} - - - -int -main(int argc, char **argv) -{ - void *area = NULL; - size_t areasize = 4096; - int shm_id = -1; - FILE *fp; - char buf[200]; - char *p, *p2; - size_t n; - int i; - - log_set_name("shmtest"); - i18n_init(); -#ifndef USE_SHM_COPROCESSING - log_info("SHM_COPRPOCESSING is not available\n"); -#else - if( argc < 1 ) - my_usage(); - - for(n=0,i=1; i < argc; i++ ) - n += strlen(argv[i]) + 1; - p = xmalloc( 100 + n ); - strcpy( p, "../g10/gpg --status-fd 1 --run-as-shm-coprocess 0"); - for(i=1; i < argc; i++ ) { - strcat(p, " " ); - strcat(p, argv[i] ); - } - - fp = popen( p, "r" ); - xfree( p ); - if( !fp ) - log_error("popen failed: %s\n", strerror(errno)); - - while ( fgets (buf, sizeof (buf) - 1, fp ) != NULL ) { - size_t len = strlen(buf); - if( len >= 9 && !memcmp( buf, "[GNUPG:] ", 9 ) ) { - int word=0; - int is_info = 0, is_get = 0; - - for( p = strtok(buf+9, " \n"); p ; p = strtok(NULL, " \n")) { - word++; - if( word==1 && !strcmp(p,"SHM_INFO") ) { - if( !area ) - is_info=1; - else - log_error("duplicate SHM_INFO ignored\n" ); - } - else if( is_info && (p2 = strchr(p, '=' )) ) { - int val; - *p2++ = 0; - val = atoi(p2); /* should be atou() for some values */ - if( !strcmp(p, "pv" ) ) { - if( atoi(p2) != 1 ) - log_fatal("invalid protocol version %d\n", val ); - is_info = 2; - } - else if( !strcmp(p, "pid" ) ) - serverpid = val; - else if( !strcmp(p, "shmid" ) ) - shm_id = val; - } - else if( word == 1 && !strcmp(p,"SHM_GET") ) - is_get = 1; - else if( word == 1 && !strcmp(p,"SHM_GET_BOOL") ) - is_get = 2; - else if( word == 1 && !strcmp(p,"SHM_GET_HIDDEN") ) - is_get = 3; - else if( word == 2 && is_get ) { - do_get_string( is_get, p, area, areasize ); - break; - } - else if( word == 1 ) - log_info("Status: %s\n", p); - } - if( is_info ) { - if( is_info < 2 ) - log_fatal("SHM info without protocol version\n"); - if( serverpid == -1 ) - log_fatal("SHM info without server's pid\n"); - if( shm_id == -1 ) - log_fatal("SHM info without id\n"); - log_info("Shared memory info: server=%d shm_id=%d\n", - serverpid, shm_id); - area = shmat( shm_id, 0, 0 ); - if( area == (void*)-1 ) - log_fatal("attach to shared memory failed: %s\n", - strerror(errno)); - } - } - else - fputs (buf, stdout); - } - - - if( pclose(fp) ) - log_error("pclose failed\n"); - - return 0; -#endif -} - -#endif diff --git a/tools/signmany b/tools/signmany deleted file mode 100644 index d628e7ba3..000000000 --- a/tools/signmany +++ /dev/null @@ -1,33 +0,0 @@ -#!/bin/sh - -if [ ! -f ./$1 ]; then - echo "usage: signmany keyring" >&2 - exit 1 -fi - - -ro="--trustdb-name=./tdb.tmp --no-default-keyring --secret-keyring /floppy/secring.gpg --keyring ./$1" - -kis=`gpg $ro --fast-list-mode --list-keys --with-colons \ - | awk -F: '$1=="pub" { print $5 }'` - -for k in $kis; do - echo "Keyid: $k" - answer=A - while [ "$answer" = "A" ]; do - gpg $ro --lock-never --no-interactive-selection --sign-key $k - answer="" - while [ "$answer" = "" ]; do - read -p 'Okay, Again or Quit? (O/A/Q) ' - case "$REPLY" in - o|O) answer=O ;; - a|A) answer=A ;; - q|Q) answer=Q ;; - *) ;; - esac - done - done - [ "$answer" = "Q" ] && break -done - - diff --git a/tools/symcryptrun.c b/tools/symcryptrun.c new file mode 100644 index 000000000..406cbb2a2 --- /dev/null +++ b/tools/symcryptrun.c @@ -0,0 +1,1073 @@ +/* symcryptrun.c - Tool to call simple symmetric encryption tools. + * Copyright (C) 2005 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 2 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. + */ + + +/* Sometimes simple encryption tools are already in use for a long + time and there is a desire to integrate them into the GnuPG + framework. The protocols and encryption methods might be + non-standard or not even properly documented, so that a + full-fledged encryption tool with an interface like gpg is not + doable. This simple wrapper program provides a solution: It + operates by calling the encryption/decryption module and providing + the passphrase for a key (or even the key directly) using the + standard pinentry mechanism through gpg-agent. */ + +/* This program is invoked in the following way: + + symcryptrun --class CLASS --program PROGRAM --keyfile KEYFILE \ + [--decrypt | --encrypt] + + For encryption, the plain text must be provided on STDIN, and the + ciphertext will be output to STDOUT. For decryption vice versa. + + CLASS can currently only be "confucius". + + PROGRAM must be the path to the crypto engine. + + KEYFILE must contain the secret key, which may be protected by a + passphrase. The passphrase is retrieved via the pinentry program. + + + The GPG Agent _must_ be running before starting symcryptrun. + + The possible exit status codes: + + 0 Success + 1 Some error occured + 2 No valid passphrase was provided + 3 The operation was canceled by the user + + Other classes may be added in the future. */ + +#define SYMC_BAD_PASSPHRASE 2 +#define SYMC_CANCELED 3 + + +#include <config.h> + +#include <unistd.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <assert.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <sys/wait.h> +#include <pty.h> +#include <utmp.h> +#include <ctype.h> +#ifdef HAVE_LOCALE_H +#include <locale.h> +#endif +#ifdef HAVE_LANGINFO_CODESET +#include <langinfo.h> +#endif +#include <gpg-error.h> + +#define JNLIB_NEED_LOG_LOGV +#include "i18n.h" +#include "../common/util.h" +#include "mkdtemp.h" + +/* FIXME: Bah. For spwq_secure_free. */ +#define SIMPLE_PWQUERY_IMPLEMENTATION 1 +#include "../common/simple-pwquery.h" + + +/* Used by gcry for logging */ +static void +my_gcry_logger (void *dummy, int level, const char *fmt, va_list arg_ptr) +{ + /* translate the log levels */ + switch (level) + { + case GCRY_LOG_CONT: level = JNLIB_LOG_CONT; break; + case GCRY_LOG_INFO: level = JNLIB_LOG_INFO; break; + case GCRY_LOG_WARN: level = JNLIB_LOG_WARN; break; + case GCRY_LOG_ERROR:level = JNLIB_LOG_ERROR; break; + case GCRY_LOG_FATAL:level = JNLIB_LOG_FATAL; break; + case GCRY_LOG_BUG: level = JNLIB_LOG_BUG; break; + case GCRY_LOG_DEBUG:level = JNLIB_LOG_DEBUG; break; + default: level = JNLIB_LOG_ERROR; break; } + log_logv (level, fmt, arg_ptr); +} + + +/* From simple-gettext.c. */ + +/* We assume to have `unsigned long int' value with at least 32 bits. */ +#define HASHWORDBITS 32 + +/* The so called `hashpjw' function by P.J. Weinberger + [see Aho/Sethi/Ullman, COMPILERS: Principles, Techniques and Tools, + 1986, 1987 Bell Telephone Laboratories, Inc.] */ + +static __inline__ ulong +hash_string( const char *str_param ) +{ + unsigned long int hval, g; + const char *str = str_param; + + hval = 0; + while (*str != '\0') + { + hval <<= 4; + hval += (unsigned long int) *str++; + g = hval & ((unsigned long int) 0xf << (HASHWORDBITS - 4)); + if (g != 0) + { + hval ^= g >> (HASHWORDBITS - 8); + hval ^= g; + } + } + return hval; +} + + +/* Constants to identify the commands and options. */ +enum cmd_and_opt_values + { + aNull = 0, + oQuiet = 'q', + oVerbose = 'v', + + oNoVerbose = 500, + oOptions, + oNoOptions, + oLogFile, + oHomedir, + oClass, + oProgram, + oKeyfile, + oDecrypt, + oEncrypt, + oInput + }; + + +/* The list of commands and options. */ +static ARGPARSE_OPTS opts[] = + { + { 301, NULL, 0, N_("@\nCommands:\n ") }, + + { oDecrypt, "decrypt", 0, N_("decryption modus") }, + { oEncrypt, "encrypt", 0, N_("encryption modus") }, + + { 302, NULL, 0, N_("@\nOptions:\n ") }, + + { oClass, "class", 2, N_("tool class (confucius)") }, + { oProgram, "program", 2, N_("program filename") }, + + { oKeyfile, "keyfile", 2, N_("secret key file (required)") }, + { oInput, "inputfile", 2, N_("input file name (default stdin)") }, + { oVerbose, "verbose", 0, N_("verbose") }, + { oQuiet, "quiet", 0, N_("quiet") }, + { oLogFile, "log-file", 2, N_("use a log file for the server") }, + { oOptions, "options" , 2, N_("|FILE|read options from FILE") }, + + /* Hidden options. */ + { oNoVerbose, "no-verbose", 0, "@" }, + { oHomedir, "homedir", 2, "@" }, + { oNoOptions, "no-options", 0, "@" },/* shortcut for --options /dev/null */ + + {0} + }; + + +/* We keep all global options in the structure OPT. */ +struct +{ + int verbose; /* Verbosity level. */ + int quiet; /* Be extra quiet. */ + const char *homedir; /* Configuration directory name */ + + char *class; + char *program; + char *keyfile; + char *input; +} opt; + + +/* Print usage information and and provide strings for help. */ +static const char * +my_strusage (int level) +{ + const char *p; + + switch (level) + { + case 11: p = "symcryptrun (GnuPG)"; + break; + case 13: p = VERSION; break; + case 17: p = PRINTABLE_OS_NAME; break; + case 19: p = _("Please report bugs to <" PACKAGE_BUGREPORT ">.\n"); + break; + case 1: + case 40: p = _("Usage: symcryptrun [options] (-h for help)"); + break; + case 41: + p = _("Syntax: symcryptrun --class CLASS --program PROGRAM " + "--keyfile KEYFILE [options...] COMMAND [inputfile]\n" + "Call a simple symmetric encryption tool\n"); + break; + case 31: p = "\nHome: "; break; + case 32: p = opt.homedir; break; + case 33: p = "\n"; break; + + default: p = NULL; break; + } + return p; +} + + +/* Initialize the gettext system. */ +static void +i18n_init(void) +{ +#ifdef USE_SIMPLE_GETTEXT + set_gettext_file (PACKAGE_GT); +#else +# ifdef ENABLE_NLS + setlocale (LC_ALL, ""); + bindtextdomain (PACKAGE_GT, LOCALEDIR); + textdomain (PACKAGE_GT); +# endif +#endif +} + + +/* This is in the GNU C library in unistd.h. */ + +#ifndef TEMP_FAILURE_RETRY +/* Evaluate EXPRESSION, and repeat as long as it returns -1 with `errno' + set to EINTR. */ + +# define TEMP_FAILURE_RETRY(expression) \ + (__extension__ \ + ({ long int __result; \ + do __result = (long int) (expression); \ + while (__result == -1L && errno == EINTR); \ + __result; })) +#endif + + +/* Unlink a file, and shred it if SHRED is true. */ +int +remove_file (char *name, int shred) +{ + if (!shred) + return unlink (name); + else + { + int status; + pid_t pid; + + pid = fork (); + if (pid == 0) + { + /* Child. */ + + /* -f forces file to be writable, and -u unlinks it afterwards. */ + char *args[] = { SHRED, "-uf", name, NULL }; + + execv (SHRED, args); + _exit (127); + } + else if (pid < 0) + { + /* Fork failed. */ + status = -1; + } + else + { + /* Parent. */ + + if (TEMP_FAILURE_RETRY (waitpid (pid, &status, 0)) != pid) + status = -1; + } + + if (!WIFEXITED (status)) + { + log_error (_("%s on %s aborted with status %i\n"), + SHRED, name, status); + unlink (name); + return 1; + } + else if (WEXITSTATUS (status)) + { + log_error (_("%s on %s failed with status %i\n"), SHRED, name, + WEXITSTATUS (status)); + unlink (name); + return 1; + } + + return 0; + } +} + + +/* Class Confucius. + + "Don't worry that other people don't know you; + worry that you don't know other people." Analects--1.16. */ + +/* Create temporary directory with mode 0700. Returns a dynamically + allocated string with the filename of the directory. */ +static char * +confucius_mktmpdir (void) +{ + char *name; + + name = strdup ("/tmp/gpg-XXXXXX"); + if (!name || !mkdtemp (name)) + { + log_error (_("can't create temporary directory `%s': %s\n"), + name?name:"", strerror (errno)); + return NULL; + } + + return name; +} + + +/* Buffer size for I/O operations. */ +#define CONFUCIUS_BUFSIZE 4096 + +/* Buffer size for output lines. */ +#define CONFUCIUS_LINESIZE 4096 + + +/* Copy the file IN to OUT, either of which may be "-". If PLAIN is + true, and the copying fails, and OUT is not STDOUT, then shred the + file instead unlinking it. */ +static int +confucius_copy_file (char *infile, char *outfile, int plain) +{ + FILE *in; + int in_is_stdin = 0; + FILE *out; + int out_is_stdout = 0; + char data[CONFUCIUS_BUFSIZE]; + ssize_t data_len; + + if (infile[0] == '-' && infile[1] == '\0') + { + /* FIXME: Is stdin in binary mode? */ + in = stdin; + in_is_stdin = 1; + } + else + { + in = fopen (infile, "rb"); + if (!in) + { + log_error (_("could not open %s for writing: %s\n"), + infile, strerror (errno)); + return 1; + } + } + + if (outfile[0] == '-' && outfile[1] == '\0') + { + /* FIXME: Is stdout in binary mode? */ + out = stdout; + out_is_stdout = 1; + } + else + { + out = fopen (outfile, "wb"); + if (!out) + { + log_error (_("could not open %s for writing: %s\n"), + infile, strerror (errno)); + return 1; + } + } + + /* Now copy the data. */ + while ((data_len = fread (data, 1, sizeof (data), in)) > 0) + { + if (fwrite (data, 1, data_len, out) != data_len) + { + log_error (_("error writing to %s: %s\n"), outfile, + strerror (errno)); + goto copy_err; + } + } + if (data_len < 0 || ferror (in)) + { + log_error (_("error reading from %s: %s\n"), infile, strerror (errno)); + goto copy_err; + } + + /* Close IN if appropriate. */ + if (!in_is_stdin && fclose (in) && ferror (in)) + { + log_error (_("error closing %s: %s\n"), infile, strerror (errno)); + goto copy_err; + } + + /* Close OUT if appropriate. */ + if (!out_is_stdout && fclose (out) && ferror (out)) + { + log_error (_("error closing %s: %s\n"), infile, strerror (errno)); + goto copy_err; + } + + return 0; + + copy_err: + if (!out_is_stdout) + remove_file (outfile, plain); + + return 1; +} + + +/* Get a passphrase in secure storage (if possible). If AGAIN is + true, then this is a repeated attempt. If CANCELED is not a null + pointer, it will be set to true or false, depending on if the user + canceled the operation or not. On error (including cancelation), a + null pointer is returned. The passphrase must be deallocated with + confucius_drop_pass. CACHEID is the ID to be used for passphrase + caching and can be NULL to disable caching. */ +char * +confucius_get_pass (const char *cacheid, int again, int *canceled) +{ + int err; + char *pw; +#ifdef HAVE_LANGINFO_CODESET + char *orig_codeset = NULL; +#endif + + if (canceled) + *canceled = 0; + +#ifdef ENABLE_NLS + /* The Assuan agent protocol requires us to transmit utf-8 strings */ + orig_codeset = bind_textdomain_codeset (PACKAGE_GT, NULL); +#ifdef HAVE_LANGINFO_CODESET + if (!orig_codeset) + orig_codeset = nl_langinfo (CODESET); +#endif + if (orig_codeset && !strcmp (orig_codeset, "UTF-8")) + orig_codeset = NULL; + if (orig_codeset) + { + /* We only switch when we are able to restore the codeset later. */ + orig_codeset = xstrdup (orig_codeset); + if (!bind_textdomain_codeset (PACKAGE_GT, "utf-8")) + orig_codeset = NULL; + } +#endif + + pw = simple_pwquery (cacheid, + again ? _("does not match - try again"):NULL, + _("Passphrase:"), NULL, &err); + +#ifdef ENABLE_NLS + if (orig_codeset) + { + bind_textdomain_codeset (PACKAGE_GT, orig_codeset); + xfree (orig_codeset); + } +#endif + + if (!pw) + { + if (err) + log_error (_("error while asking for the passphrase: %s\n"), + gpg_strerror (err)); + else + { + log_info (_("cancelled\n")); + if (canceled) + *canceled = 1; + } + } + + return pw; +} + + +/* Drop a passphrase retrieved with confucius_get_pass. */ +void +confucius_drop_pass (char *pass) +{ + if (pass) + spwq_secure_free (pass); +} + + +/* Run a confucius crypto engine. If MODE is oEncrypt, encryption is + requested. If it is oDecrypt, decryption is requested. INFILE and + OUTFILE are the temporary files used in the process. */ +int +confucius_process (int mode, char *infile, char *outfile, + int argc, char *argv[]) +{ + char **args; + int cstderr[2]; + int master; + int slave; + int res; + pid_t pid; + pid_t wpid; + int tries = 0; + char cacheid[40]; + + signal (SIGPIPE, SIG_IGN); + + if (!opt.program) + { + log_error (_("no --program option provided\n")); + return 1; + } + + if (mode != oDecrypt && mode != oEncrypt) + { + log_error (_("only --decrypt and --encrypt are supported\n")); + return 1; + } + + if (!opt.keyfile) + { + log_error (_("no --keyfile option provided\n")); + return 1; + } + + /* Generate a hash from the keyfile name for caching. */ + snprintf (cacheid, sizeof (cacheid), "confucius:%lu", + hash_string (opt.keyfile)); + cacheid[sizeof (cacheid) - 1] = '\0'; + args = malloc (sizeof (char *) * (10 + argc)); + if (!args) + { + log_error (_("cannot allocate args vector\n")); + return 1; + } + args[0] = opt.program; + args[1] = (mode == oEncrypt) ? "-m1" : "-m2"; + args[2] = "-q"; + args[3] = infile; + args[4] = "-z"; + args[5] = outfile; + args[6] = "-s"; + args[7] = opt.keyfile; + args[8] = (mode == oEncrypt) ? "-af" : "-f"; + args[9 + argc] = NULL; + while (argc--) + args[9 + argc] = argv[argc]; + + if (pipe (cstderr) < 0) + { + log_error (_("could not create pipe: %s\n"), strerror (errno)); + free (args); + return 1; + } + + if (openpty (&master, &slave, NULL, NULL, NULL) == -1) + { + log_error (_("could not create pty: %s\n"), strerror (errno)); + close (cstderr[0]); + close (cstderr[1]); + free (args); + return -1; + } + + /* We don't want to deal with the worst case scenarios. */ + assert (master > 2); + assert (slave > 2); + assert (cstderr[0] > 2); + assert (cstderr[1] > 2); + + pid = fork (); + if (pid < 0) + { + log_error (_("could not fork: %s\n"), strerror (errno)); + close (master); + close (slave); + close (cstderr[0]); + close (cstderr[1]); + free (args); + return 1; + } + else if (pid == 0) + { + /* Child. */ + + /* Close the parent ends. */ + close (master); + close (cstderr[0]); + + /* Change controlling terminal. */ + if (login_tty (slave)) + { + /* It's too early to output a debug message. */ + _exit (1); + } + + dup2 (cstderr[1], 2); + close (cstderr[1]); + + /* Now kick off the engine program. */ + execv (opt.program, args); + log_error (_("execv failed: %s\n"), strerror (errno)); + _exit (1); + } + else + { + /* Parent. */ + char buffer[CONFUCIUS_LINESIZE]; + int buffer_len = 0; + fd_set fds; + int slave_closed = 0; + int stderr_closed = 0; + + close (slave); + close (cstderr[1]); + free (args); + + /* Listen on the output FDs. */ + do + { + FD_ZERO (&fds); + + if (!slave_closed) + FD_SET (master, &fds); + if (!stderr_closed) + FD_SET (cstderr[0], &fds); + + res = select (FD_SETSIZE, &fds, NULL, NULL, NULL); + if (res < 0) + { + log_error (_("select failed: %s\n"), strerror (errno)); + + kill (pid, SIGTERM); + close (master); + close (cstderr[0]); + return 1; + } + + if (FD_ISSET (cstderr[0], &fds)) + { + /* We got some output on stderr. This is just passed + through via the logging facility. */ + + res = read (cstderr[0], &buffer[buffer_len], + sizeof (buffer) - buffer_len - 1); + if (res < 0) + { + log_error (_("read failed: %s\n"), strerror (errno)); + + kill (pid, SIGTERM); + close (master); + close (cstderr[0]); + return 1; + } + else + { + char *newline; + + buffer_len += res; + for (;;) + { + buffer[buffer_len] = '\0'; + newline = strchr (buffer, '\n'); + if (newline) + { + *newline = '\0'; + log_error ("%s\n", buffer); + buffer_len -= newline + 1 - buffer; + memmove (buffer, newline + 1, buffer_len); + } + else if (buffer_len == sizeof (buffer) - 1) + { + /* Overflow. */ + log_error ("%s\n", buffer); + buffer_len = 0; + } + else + break; + } + + if (res == 0) + stderr_closed = 1; + } + } + else if (FD_ISSET (master, &fds)) + { + char data[512]; + + res = read (master, data, sizeof (data)); + if (res < 0) + { + if (errno == EIO) + { + /* Slave-side close leads to readable fd and + EIO. */ + slave_closed = 1; + } + else + { + log_error (_("pty read failed: %s\n"), strerror (errno)); + + kill (pid, SIGTERM); + close (master); + close (cstderr[0]); + return 1; + } + } + else if (res == 0) + /* This never seems to be what happens on slave-side + close. */ + slave_closed = 1; + else + { + /* Check for password prompt. */ + if (data[res - 1] == ':') + { + char *pass; + int canceled; + + /* If this is not the first attempt, the + passphrase seems to be wrong, so clear the + cache. */ + if (tries) + simple_pwclear (cacheid); + + pass = confucius_get_pass (cacheid, + tries ? 1 : 0, &canceled); + if (!pass) + { + kill (pid, SIGTERM); + close (master); + close (cstderr[0]); + return canceled ? SYMC_CANCELED : 1; + } + write (master, pass, strlen (pass)); + write (master, "\n", 1); + confucius_drop_pass (pass); + + tries++; + } + } + } + } + while (!stderr_closed || !slave_closed); + + close (master); + close (cstderr[0]); + + wpid = waitpid (pid, &res, 0); + if (wpid < 0) + { + log_error (_("waitpid failed: %s\n"), strerror (errno)); + + kill (pid, SIGTERM); + /* State of cached password is unclear. Just remove it. */ + simple_pwclear (cacheid); + return 1; + } + else + { + /* Shouldn't happen, as we don't use WNOHANG. */ + assert (wpid != 0); + + if (!WIFEXITED (res)) + { + log_error (_("child aborted with status %i\n"), res); + + /* State of cached password is unclear. Just remove it. */ + simple_pwclear (cacheid); + + return 1; + } + + if (WEXITSTATUS (res)) + { + /* The passphrase was wrong. Remove it from the cache. */ + simple_pwclear (cacheid); + + /* We probably exceeded our number of attempts at guessing + the password. */ + if (tries >= 3) + return SYMC_BAD_PASSPHRASE; + else + return 1; + } + + return 0; + } + } + + /* Not reached. */ +} + + +/* Class confucius main program. If MODE is oEncrypt, encryption is + requested. If it is oDecrypt, decryption is requested. The other + parameters are taken from the global option data. */ +int +confucius_main (int mode, int argc, char *argv[]) +{ + int res; + char *tmpdir; + char *infile; + int infile_from_stdin = 0; + char *outfile; + + tmpdir = confucius_mktmpdir (); + if (!tmpdir) + return 1; + + if (opt.input && !(opt.input[0] == '-' && opt.input[1] == '\0')) + infile = xstrdup (opt.input); + else + { + infile_from_stdin = 1; + + /* TMPDIR + "/" + "in" + "\0". */ + infile = malloc (strlen (tmpdir) + 1 + 2 + 1); + if (!infile) + { + log_error (_("cannot allocate infile string: %s\n"), + strerror (errno)); + rmdir (tmpdir); + return 1; + } + strcpy (infile, tmpdir); + strcat (infile, "/in"); + } + + /* TMPDIR + "/" + "out" + "\0". */ + outfile = malloc (strlen (tmpdir) + 1 + 3 + 1); + if (!outfile) + { + log_error (_("cannot allocate outfile string: %s\n"), strerror (errno)); + free (infile); + rmdir (tmpdir); + return 1; + } + strcpy (outfile, tmpdir); + strcat (outfile, "/out"); + + if (infile_from_stdin) + { + /* Create INFILE and fill it with content. */ + res = confucius_copy_file ("-", infile, mode == oEncrypt); + if (res) + { + free (outfile); + free (infile); + rmdir (tmpdir); + return res; + } + } + + /* Run the engine and thus create the output file, handling + passphrase retrieval. */ + res = confucius_process (mode, infile, outfile, argc, argv); + if (res) + { + remove_file (outfile, mode == oDecrypt); + if (infile_from_stdin) + remove_file (infile, mode == oEncrypt); + free (outfile); + free (infile); + rmdir (tmpdir); + return res; + } + + /* Dump the output file to stdout. */ + res = confucius_copy_file (outfile, "-", mode == oDecrypt); + if (res) + { + remove_file (outfile, mode == oDecrypt); + if (infile_from_stdin) + remove_file (infile, mode == oEncrypt); + free (outfile); + free (infile); + rmdir (tmpdir); + return res; + } + + remove_file (outfile, mode == oDecrypt); + if (infile_from_stdin) + remove_file (infile, mode == oEncrypt); + free (outfile); + free (infile); + rmdir (tmpdir); + return 0; +} + + +/* symcryptrun's entry point. */ +int +main (int argc, char **argv) +{ + ARGPARSE_ARGS pargs; + int orig_argc; + char **orig_argv; + FILE *configfp = NULL; + char *configname = NULL; + unsigned configlineno; + int mode = 0; + int res; + char *logfile = NULL; + int default_config = 1; + + set_strusage (my_strusage); + log_set_prefix ("symcryptrun", 1); + + /* Try to auto set the character set. */ + set_native_charset (NULL); + + i18n_init(); + + opt.homedir = default_homedir (); + + /* Check whether we have a config file given on the commandline */ + orig_argc = argc; + orig_argv = argv; + pargs.argc = &argc; + pargs.argv = &argv; + pargs.flags= 1|(1<<6); /* do not remove the args, ignore version */ + while (arg_parse( &pargs, opts)) + { + if (pargs.r_opt == oOptions) + { /* Yes there is one, so we do not try the default one, but + read the option file when it is encountered at the + commandline */ + default_config = 0; + } + else if (pargs.r_opt == oNoOptions) + default_config = 0; /* --no-options */ + else if (pargs.r_opt == oHomedir) + opt.homedir = pargs.r.ret_str; + } + + if (default_config) + configname = make_filename (opt.homedir, "symcryptrun.conf", NULL ); + + argc = orig_argc; + argv = orig_argv; + pargs.argc = &argc; + pargs.argv = &argv; + pargs.flags= 1; /* do not remove the args */ + next_pass: + if (configname) + { + configlineno = 0; + configfp = fopen (configname, "r"); + if (!configfp) + { + if (!default_config) + { + log_error (_("option file `%s': %s\n"), + configname, strerror(errno) ); + exit(1); + } + xfree (configname); + configname = NULL; + } + default_config = 0; + } + + /* Parse the command line. */ + while (optfile_parse (configfp, configname, &configlineno, &pargs, opts)) + { + switch (pargs.r_opt) + { + case oDecrypt: mode = oDecrypt; break; + case oEncrypt: mode = oEncrypt; break; + + case oQuiet: opt.quiet = 1; break; + case oVerbose: opt.verbose++; break; + case oNoVerbose: opt.verbose = 0; break; + + case oClass: opt.class = pargs.r.ret_str; break; + case oProgram: opt.program = pargs.r.ret_str; break; + case oKeyfile: opt.keyfile = pargs.r.ret_str; break; + case oInput: opt.input = pargs.r.ret_str; break; + + case oLogFile: logfile = pargs.r.ret_str; break; + + case oOptions: + /* Config files may not be nested (silently ignore them) */ + if (!configfp) + { + xfree(configname); + configname = xstrdup(pargs.r.ret_str); + goto next_pass; + } + break; + case oNoOptions: break; /* no-options */ + case oHomedir: /* Ignore this option here. */; break; + + default : pargs.err = configfp? 1:2; break; + } + } + if (configfp) + { + fclose( configfp ); + configfp = NULL; + configname = NULL; + goto next_pass; + } + xfree (configname); + configname = NULL; + + if (!mode) + log_error (_("either %s or %s must be given\n"), + "--decrypt", "--encrypt"); + + if (log_get_errorcount (0)) + exit (1); + + if (logfile) + log_set_file (logfile); + + gcry_control (GCRYCTL_SUSPEND_SECMEM_WARN); + if (!gcry_check_version (NEED_LIBGCRYPT_VERSION) ) + { + log_fatal( _("libgcrypt is too old (need %s, have %s)\n"), + NEED_LIBGCRYPT_VERSION, gcry_check_version (NULL) ); + } + gcry_set_log_handler (my_gcry_logger, NULL); + gcry_control (GCRYCTL_INIT_SECMEM, 16384, 0); + + if (!opt.class) + { + log_error (_("no class provided\n")); + res = 1; + } + else if (!strcmp (opt.class, "confucius")) + res = confucius_main (mode, argc, argv); + else + { + log_error (_("class %s is not supported\n"), opt.class); + res = 1; + } + + return res; +} diff --git a/tools/watchgnupg.c b/tools/watchgnupg.c new file mode 100644 index 000000000..051ca50fe --- /dev/null +++ b/tools/watchgnupg.c @@ -0,0 +1,402 @@ +/* watchgnupg.c - Socket server for GnuPG logs + * Copyright (C) 2003, 2004 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 2 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif +#include <stdio.h> +#include <stdlib.h> +#include <stddef.h> +#include <string.h> +#include <errno.h> +#include <stdarg.h> +#include <assert.h> +#include <unistd.h> +#include <sys/socket.h> +#include <sys/un.h> +#include <fcntl.h> +#include <time.h> + +#define PGM "watchgnupg" + +/* Allow for a standalone build. */ +#ifdef VERSION +#define MYVERSION_LINE PGM " (GnuPG) " VERSION +#define BUGREPORT_LINE "\nReport bugs to <[email protected]>.\n" +#else +#define MYVERSION_LINE PGM +#define BUGREPORT_LINE "" +#endif + +#ifndef PF_LOCAL +# ifdef PF_UNIX +# define PF_LOCAL PF_UNIX +# else +# define PF_LOCAL AF_UNIX +# endif +# ifndef AF_LOCAL +# define AF_LOCAL AF_UNIX +# endif +#endif + + +static int verbose; + + +static void +die (const char *format, ...) +{ + va_list arg_ptr; + + fflush (stdout); + fprintf (stderr, "%s: ", PGM); + + va_start (arg_ptr, format); + vfprintf (stderr, format, arg_ptr); + va_end (arg_ptr); + putc ('\n', stderr); + + exit (1); +} + + +/* static void */ +/* err (const char *format, ...) */ +/* { */ +/* va_list arg_ptr; */ + +/* fflush (stdout); */ +/* fprintf (stderr, "%s: ", PGM); */ + +/* va_start (arg_ptr, format); */ +/* vfprintf (stderr, format, arg_ptr); */ +/* va_end (arg_ptr); */ +/* putc ('\n', stderr); */ +/* } */ + +static void * +xmalloc (size_t n) +{ + void *p = malloc (n); + if (!p) + die ("out of core"); + return p; +} + +static void * +xcalloc (size_t n, size_t m) +{ + void *p = calloc (n, m); + if (!p) + die ("out of core"); + return p; +} + +static void * +xrealloc (void *old, size_t n) +{ + void *p = realloc (old, n); + if (!p) + die ("out of core"); + return p; +} + + +struct client_s { + struct client_s *next; + int fd; + size_t size; /* Allocated size of buffer. */ + size_t len; /* Current length of buffer. */ + unsigned char *buffer; /* Buffer to with data already read. */ + +}; +typedef struct client_s *client_t; + + + +static void +print_fd_and_time (int fd) +{ + struct tm *tp; + time_t atime = time (NULL); + + tp = localtime (&atime); + printf ("%3d - %04d-%02d-%02d %02d:%02d:%02d ", + fd, + 1900+tp->tm_year, tp->tm_mon+1, tp->tm_mday, + tp->tm_hour, tp->tm_min, tp->tm_sec ); +} + + +/* Print LINE for the client identified by C. Calling this function + witgh LINE set to NULL, will flush the internal buffer. */ +static void +print_line (client_t c, const char *line) +{ + const char *s; + size_t n; + + if (!line) + { + if (c->buffer && c->len) + { + print_fd_and_time (c->fd); + fwrite (c->buffer, c->len, 1, stdout); + putc ('\n', stdout); + c->len = 0; + } + return; + } + + while ((s = strchr (line, '\n'))) + { + print_fd_and_time (c->fd); + if (c->buffer && c->len) + { + fwrite (c->buffer, c->len, 1, stdout); + c->len = 0; + } + fwrite (line, s - line + 1, 1, stdout); + line = s + 1; + } + n = strlen (line); + if (n) + { + if (c->len + n >= c->size) + { + c->size += ((n + 255) & ~255); + c->buffer = (c->buffer + ? xrealloc (c->buffer, c->size) + : xmalloc (c->size)); + } + memcpy (c->buffer + c->len, line, n); + c->len += n; + } +} + + +static void +print_version (int with_help) +{ + fputs (MYVERSION_LINE "\n" + "Copyright (C) 2004 Free Software Foundation, Inc.\n" + "This program comes with ABSOLUTELY NO WARRANTY.\n" + "This is free software, and you are welcome to redistribute it\n" + "under certain conditions. See the file COPYING for details.\n", + stdout); + + if (with_help) + fputs ("\n" + "Usage: " PGM " [OPTIONS] SOCKETNAME\n" + "Open the local socket SOCKETNAME and display log messages\n" + "\n" + " --force delete an already existing socket file\n" + " --verbose enable extra informational output\n" + " --version print version of the program and exit\n" + " --help display this help and exit\n" + BUGREPORT_LINE, stdout ); + + exit (0); +} + +int +main (int argc, char **argv) +{ + int last_argc = -1; + int force = 0; + + struct sockaddr_un srvr_addr; + socklen_t addrlen; + int server; + int flags; + client_t client_list = NULL; + + if (argc) + { + argc--; argv++; + } + while (argc && last_argc != argc ) + { + last_argc = argc; + if (!strcmp (*argv, "--")) + { + argc--; argv++; + break; + } + else if (!strcmp (*argv, "--version")) + print_version (0); + else if (!strcmp (*argv, "--help")) + print_version (1); + else if (!strcmp (*argv, "--verbose")) + { + verbose = 1; + argc--; argv++; + } + else if (!strcmp (*argv, "--force")) + { + force = 1; + argc--; argv++; + } + } + + if (argc != 1) + { + fprintf (stderr, "usage: " PGM " socketname\n"); + exit (1); + } + + + if (verbose) + fprintf (stderr, "opening socket `%s'\n", *argv); + + setvbuf (stdout, NULL, _IOLBF, 0); + + server = socket (PF_LOCAL, SOCK_STREAM, 0); + if (server == -1) + die ("socket() failed: %s\n", strerror (errno)); + + /* We better set the listening socket to non-blocking so that we + don't get bitten by race conditions in accept. The should not + happen for Unix Domain sockets but well, shit happens. */ + flags = fcntl (server, F_GETFL, 0); + if (flags == -1) + die ("fcntl (F_GETFL) failed: %s\n", strerror (errno)); + if ( fcntl (server, F_SETFL, (flags | O_NONBLOCK)) == -1) + die ("fcntl (F_SETFL) failed: %s\n", strerror (errno)); + + + memset (&srvr_addr, 0, sizeof srvr_addr); + srvr_addr.sun_family = AF_LOCAL; + strncpy (srvr_addr.sun_path, *argv, sizeof (srvr_addr.sun_path) - 1); + srvr_addr.sun_path[sizeof (srvr_addr.sun_path) - 1] = 0; + addrlen = (offsetof (struct sockaddr_un, sun_path) + + strlen (srvr_addr.sun_path) + 1); + + + again: + if (bind (server, (struct sockaddr *) &srvr_addr, addrlen)) + { + if (errno == EADDRINUSE && force) + { + force = 0; + remove (srvr_addr.sun_path); + goto again; + } + die ("bind to `%s' failed: %s\n", *argv, strerror (errno)); + } + + if (listen (server, 5)) + die ("listen failed: %s\n", strerror (errno)); + + for (;;) + { + fd_set rfds; + int max_fd; + client_t client; + + /* Usually we don't have that many connections, thus it is okay + to set them allways from scratch and don't maintain an active + fd_set. */ + FD_ZERO (&rfds); + FD_SET (server, &rfds); + max_fd = server; + for (client = client_list; client; client = client->next) + if (client->fd != -1) + { + FD_SET (client->fd, &rfds); + if (client->fd > max_fd) + max_fd = client->fd; + } + + if (select (max_fd + 1, &rfds, NULL, NULL, NULL) <= 0) + continue; /* Ignore any errors. */ + + if (FD_ISSET (server, &rfds)) /* New connection. */ + { + struct sockaddr_un clnt_addr; + int fd; + + addrlen = sizeof clnt_addr; + fd = accept (server, (struct sockaddr *) &clnt_addr, &addrlen); + if (fd == -1) + { + printf ("[accepting connection failed: %s]\n", strerror (errno)); + } + else if (fd >= FD_SETSIZE) + { + close (fd); + printf ("[connection request denied: too many connections]\n"); + } + else + { + for (client = client_list; client && client->fd != -1; + client = client->next) + ; + if (!client) + { + client = xcalloc (1, sizeof *client); + client->next = client_list; + client_list = client; + } + client->fd = fd; + printf ("[client at fd %d connected]\n", client->fd); + } + } + for (client = client_list; client; client = client->next) + if (client->fd != -1 && FD_ISSET (client->fd, &rfds)) + { + char line[256]; + int n; + + n = read (client->fd, line, sizeof line - 1); + if (n < 0) + { + int save_errno = errno; + print_line (client, NULL); /* flush */ + printf ("[client at fd %d read error: %s]\n", + client->fd, strerror (save_errno)); + close (client->fd); + client->fd = -1; + } + else if (!n) + { + print_line (client, NULL); /* flush */ + close (client->fd); + printf ("[client at fd %d disconnected]\n", client->fd); + client->fd = -1; + } + else + { + line[n] = 0; + print_line (client, line); + } + } + } + + return 0; +} + + +/* +Local Variables: +compile-command: "gcc -Wall -g -o watchgnupg watchgnupg.c" +End: +*/ |
