From 92cd25550836198cf1e3a6aac239eef98364359d Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Fri, 14 Jul 2000 17:34:53 +0000 Subject: [PATCH] See ChangeLog: Fri Jul 14 19:38:23 CEST 2000 Werner Koch --- ChangeLog | 65 ++- acconfig.h | 2 + acinclude.m4 | 119 ++++-- cipher/ChangeLog | 82 ++++ cipher/Makefile.am | 19 +- cipher/blowfish.c | 9 +- cipher/cast5.c | 10 +- cipher/des.c | 9 +- cipher/dsa.c | 19 +- cipher/elgamal.c | 141 +++++-- cipher/md.c | 10 +- cipher/md5.c | 8 +- cipher/primegen.c | 25 +- cipher/pubkey.c | 59 ++- cipher/random.c | 204 ++++++++- cipher/rmd160.c | 8 +- cipher/rndegd.c | 11 +- cipher/rndunix.c | 53 ++- cipher/rndw32.c | 739 +++++++++++++++++++++++++++++++- cipher/rsa.c | 375 +++++++++++++++++ cipher/rsa.h | 36 ++ cipher/sha1.c | 8 +- cipher/tiger.c | 8 +- cipher/twofish.c | 26 +- configure.in | 118 ++++-- g10/ChangeLog | 814 ++++++++++++++++++++++++++++------- g10/Makefile.am | 5 +- g10/armor.c | 63 ++- g10/build-packet.c | 88 +++- g10/cipher.c | 49 ++- g10/comment.c | 2 +- g10/compress.c | 4 +- g10/dearmor.c | 4 +- g10/decrypt.c | 4 +- g10/delkey.c | 7 +- g10/encode.c | 9 +- g10/encr-data.c | 49 ++- g10/export.c | 35 +- g10/filter.h | 12 +- g10/free-packet.c | 6 +- g10/getkey.c | 120 +++++- g10/gpg.c | 279 +++++++----- g10/gpgd.c | 7 +- g10/helptext.c | 25 +- g10/hkp.c | 38 +- g10/hkp.h | 2 +- g10/import.c | 106 +++-- g10/kbnode.c | 7 +- g10/kbxblob.c | 10 +- g10/kbxfile.c | 2 +- g10/keydb.h | 5 +- g10/keyedit.c | 182 +++++--- g10/keygen.c | 954 +++++++++++++++++++++++++++++++++++++----- g10/keyid.c | 10 +- g10/keylist.c | 95 +++-- g10/ks-proto.h | 2 +- g10/main.h | 24 +- g10/mainproc.c | 161 ++++--- g10/mdfilter.c | 4 +- g10/misc.c | 9 +- g10/openfile.c | 37 +- g10/options.h | 20 +- g10/packet.h | 21 +- g10/parse-packet.c | 136 +++++- g10/passphrase.c | 9 +- g10/pkclist.c | 154 ++++++- g10/plaintext.c | 16 +- g10/pubkey-enc.c | 37 +- g10/revoke.c | 352 +++++++--------- g10/ringedit.c | 65 +-- g10/seckey-cert.c | 35 +- g10/seskey.c | 20 +- g10/sig-check.c | 193 ++++----- g10/sign.c | 52 ++- g10/signal.c | 17 +- g10/skclist.c | 4 +- g10/status.c | 155 ++++++- g10/status.h | 17 +- g10/tdbdump.c | 3 +- g10/tdbio.c | 22 +- g10/tdbio.h | 5 +- g10/textfilter.c | 38 +- g10/trustdb.c | 294 ++++++++++--- g10/trustdb.h | 3 +- g10/verify.c | 91 +++- include/ChangeLog | 10 + include/errors.h | 5 +- include/http.h | 7 +- include/iobuf.h | 1 + include/types.h | 7 +- include/util.h | 1 + jnlib/ChangeLog | 6 + jnlib/argparse.c | 4 +- jnlib/dotlock.c | 40 +- jnlib/dotlock.h | 1 + util/ChangeLog | 25 ++ util/Makefile.am | 2 +- util/http.c | 42 +- util/iobuf.c | 42 +- util/logger.c | 8 +- util/miscutil.c | 4 +- util/simple-gettext.c | 22 +- util/strgutil.c | 2 +- util/ttyio.c | 30 +- 104 files changed, 5871 insertions(+), 1540 deletions(-) create mode 100644 cipher/rsa.c create mode 100644 cipher/rsa.h diff --git a/ChangeLog b/ChangeLog index e0091e20c..d33da8126 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,6 +1,69 @@ +Fri Jul 14 19:38:23 CEST 2000 Werner Koch + + The big merge between this one and the stable branch 1.0. Still need + to merge TNANKS, AUTHORS and such. It probaly does not compile yet. + + * acinclude.m4 (GNUPG_CHECK_MLOCK): Fixed syntax error in C code. + + * configure.in: Add check for termio.h, wait unctiosn and sigaction. + + * acinclude.m4, configure.in (GNUPG_CHECK_GNUMAKE): New. + + * acinclude.m4 (MKDIR_TAKES_ONE_ARG): Check some headers. By Gaël Quéri. + + * configure.in (AM_INIT_AUTOMAKE): Use this now. By Gaël. + + * acinclude.m4 (GNUPG_CHECK_EXPORTDYNAMIC): Replacement for + GNUPG_CHECK_RDYNAMIC which should handle gcc with non GNU ld nicer. + Contributed by Dave Dykstra. + * configure.in (GNYPG_CHECK_RDYNAMIC): Replaced by the new check. + + * configure.in: Add a test for unisgned long long. + + * configure.in (DYNLINK_MOD_CFLAGS): Set different for NetBSD. + + * configure.in: Add check for clock_gettime + + * configure.in (ALL_LINGUAS): Add nl. + * configure.in (ALL_LINGUAS): Add Esperanto. + * configure.in (ALL_LINGUAS): Add sv and ja. + + * configure.in: Use /usr/local for CFLAGS and LDFLAGS when + target is freebsd. By Rémi. + + * configure.in: Do not set development version when the version has + a dash in it. Suggested by Dave Dykstra. + + * configure.in: Removed substitution for doc/gph/Makefile. + Do all the gcc warning only in maintainer mode. + + * configure.in (dlopen): Use CHECK_FUNC for a test of dlopen in libc. + Suggested by Alexandre Oliva. + (-Wall): Moved the settting of gcc warning options near to the end + so that tests don't get confused. Suggested by Paul D. Smith. + + * acinclude.m4 (GNUPG_SYS_NM_PARSE): Added BSDI support. + (GNUPG_CHECK_RDYNAMIC): Ditto. + + * acinclude.m4 (GNUPG_CHECK_MLOCK): Changed the way to test for + librt. Test suggested by Jeff Long. + + * acinclude.m4 (GNUPG_CHECK_MLOCK): Do librt check only when + we can't link a test program. This way GNU systems don't need + to link against linrt. + (GNUPG_CHECK_IPC): Fixed use of TRY_COMPILE macro. From Tim Mooney. + + * acinclude.m4 (GNUPG_SYS_SYMBOL_UNDERSCORE): Add support for + DJGPP. + (GNUPG_CHECK_MLOCK): Check whether mlock sits in librt. + + * acinclude.m4 (GNUPG_CHECK_RDYNAMIC): Add NetBSD. By Thomas Klausner. + + * acconfig.h (HAVE_MLOCK): Added + Mon Mar 13 19:22:46 CET 2000 Werner Koch - * configure.in: Now uses the Docbook M$s from GPH. + * configure.in: Now uses the Docbook M4s from GPH. Mon Jan 31 17:46:35 CET 2000 Werner Koch diff --git a/acconfig.h b/acconfig.h index d364c7b7a..a2c91283f 100644 --- a/acconfig.h +++ b/acconfig.h @@ -53,6 +53,8 @@ #undef HAVE_STPCPY +#undef HAVE_MLOCK + #undef BIG_ENDIAN_HOST #undef LITTLE_ENDIAN_HOST diff --git a/acinclude.m4 b/acinclude.m4 index 20c92be52..9f8bfd01d 100644 --- a/acinclude.m4 +++ b/acinclude.m4 @@ -50,6 +50,25 @@ AC_DEFUN(GNUPG_FIX_HDR_VERSION, ]) +dnl GNUPG_CHECK_GNUMAKE +dnl +AC_DEFUN(GNUPG_CHECK_GNUMAKE, + [ + if ${MAKE-make} --version 2>/dev/null | grep '^GNU ' >/dev/null 2>&1; then + : + else + AC_MSG_WARN([[ +*** +*** It seems that you are not using GNU make. Some make tools have serious +*** flaws and you may not be able to build this software at all. Before you +*** complain, please try GNU make: GNU make is easy to build and available +*** at all GNU archives. It is always available from ftp.gnu.org:/gnu/make. +***]]) + fi + ]) + + + dnl GNUPG_LINK_FILES( SRC, DEST ) dnl same as AC_LINK_FILES, but collect the files to link in dnl some special variables and do the link @@ -212,39 +231,38 @@ define(GNUPG_CHECK_PIC, ###################################################################### -# Check for rdynamic flag -# This sets CFLAGS_RDYNAMIC to the required flags +# Check for export-dynamic flag +# This sets CFLAGS_EXPORTDYNAMIC to the required flags ###################################################################### -dnl GNUPG_CHECK_RDYNAMIC +dnl GNUPG_CHECK_EXPORTDYNAMIC dnl -define(GNUPG_CHECK_RDYNAMIC, - [ AC_MSG_CHECKING(how to specify -rdynamic) - CFLAGS_RDYNAMIC= +define(GNUPG_CHECK_EXPORTDYNAMIC, + [ AC_MSG_CHECKING(how to specify -export-dynamic) if test "$cross_compiling" = yes; then - AC_MSG_RESULT(assume none) + AC_MSG_RESULT(assume none) + CFLAGS_EXPORTDYNAMIC="" else - case "$host_os" in - solaris* ) - CFLAGS_RDYNAMIC="-Wl,-dy" - ;; - - hpux* ) - CFLAGS_RDYNAMIC="-Wl,-E" - ;; - - openbsd* | freebsd2* | osf4* | irix* ) - CFLAGS_RDYNAMIC="" - ;; - - * ) - CFLAGS_RDYNAMIC="-Wl,-export-dynamic" - ;; - esac - AC_MSG_RESULT($CFLAGS_RDYNAMIC) + AC_CACHE_VAL(gnupg_cv_export_dynamic,[ + if AC_TRY_COMMAND([${CC-cc} $CFLAGS -Wl,--version 2>&1 | + grep "GNU ld" >/dev/null]); then + # using gnu's linker + gnupg_cv_export_dynamic="-Wl,-export-dynamic" + else + case "$host_os" in + hpux* ) + gnupg_cv_export_dynamic="-Wl,-E" + ;; + * ) + gnupg_cv_export_dynamic="" + ;; + esac + fi + ]) + AC_MSG_RESULT($gnupg_cv_export_dynamic) + CFLAGS_EXPORTDYNAMIC="$gnupg_cv_export_dynamic" fi ]) - ##################################################################### # Check for SysV IPC (from GIMP) # And see whether we have a SHM_LOCK (FreeBSD does not have it). @@ -299,7 +317,8 @@ define(GNUPG_CHECK_IPC, AC_TRY_COMPILE([#include #include #include ],[ - int foo( int shm_id ) { shmctl(shm_id, SHM_LOCK, 0); } + int shm_id; + shmctl(shm_id, SHM_LOCK, 0); ], gnupg_cv_ipc_have_shm_lock="yes", gnupg_cv_ipc_have_shm_lock="no" @@ -318,11 +337,46 @@ define(GNUPG_CHECK_IPC, ###################################################################### # Check whether mlock is broken (hpux 10.20 raises a SIGBUS if mlock # is not called from uid 0 (not tested whether uid 0 works) +# For DECs Tru64 we have also to check whether mlock is in librt +# mlock is there a macro using memlk() ###################################################################### dnl GNUPG_CHECK_MLOCK dnl define(GNUPG_CHECK_MLOCK, [ AC_CHECK_FUNCS(mlock) + if test "$ac_cv_func_mlock" = "no"; then + AC_CHECK_HEADERS(sys/mman.h) + if test "$ac_cv_header_sys_mman_h" = "yes"; then + # Add librt to LIBS: + AC_CHECK_LIB(rt, memlk) + AC_CACHE_CHECK([whether mlock is in sys/mman.h], + gnupg_cv_mlock_is_in_sys_mman, + [AC_TRY_LINK([ + #include + #ifdef HAVE_SYS_MMAN_H + #include + #endif + ], [ + int i; + mkdir ("foo", 0); + /* glibc defines this for functions which it implements + * to always fail with ENOSYS. Some functions are actually + * named something starting with __ and the normal name + * is an alias. */ + #if defined (__stub_mlock) || defined (__stub___mlock) + choke me + #else + mlock(&i, 4); + #endif + ; return 0; + ], + gnupg_cv_mlock_is_in_sys_mman=yes, + gnupg_cv_mlock_is_in_sys_mman=no)]) + if test "$gnupg_cv_mlock_is_in_sys_mman" = "yes"; then + AC_DEFINE(HAVE_MLOCK) + fi + fi + fi if test "$ac_cv_func_mlock" = "yes"; then AC_MSG_CHECKING(whether mlock is broken) AC_CACHE_VAL(gnupg_cv_have_broken_mlock, @@ -372,9 +426,10 @@ define(GNUPG_CHECK_MLOCK, ]) -################################################################ +################################################################ # GNUPG_PROG_NM - find the path to a BSD-compatible name lister +################################################################ AC_DEFUN(GNUPG_PROG_NM, [AC_MSG_CHECKING([for BSD-compatible nm]) AC_CACHE_VAL(ac_cv_path_NM, @@ -433,7 +488,7 @@ case "$host_os" in aix*) ac_symcode='[BCDTU]' ;; -freebsd* | netbsd* | openbsd* | sunos* | cygwin32* | mingw32*) +freebsd* | netbsd* | openbsd* | bsdi* | sunos* | cygwin32* | mingw32*) ac_sympat='_\([_A-Za-z][_A-Za-z0-9]*\)' ac_symxfrm='_\1 \1' ;; @@ -586,7 +641,7 @@ AC_CHECK_TOOL(AS, as, false) AC_DEFUN(GNUPG_SYS_SYMBOL_UNDERSCORE, [tmp_do_check="no" case "${target}" in - i386-emx-os2 | i[3456]86-pc-os2*emx ) + i386-emx-os2 | i[3456]86-pc-os2*emx | i386-pc-msdosdjgpp) ac_cv_sys_symbol_underscore=yes ;; *) @@ -645,7 +700,8 @@ dnl Stolen from gcc dnl Define MKDIR_TAKES_ONE_ARG if mkdir accepts only one argument instead dnl of the usual 2. AC_DEFUN(GNUPG_FUNC_MKDIR_TAKES_ONE_ARG, -[AC_CACHE_CHECK([if mkdir takes one argument], gnupg_cv_mkdir_takes_one_arg, +[AC_CHECK_HEADERS(sys/stat.h unistd.h direct.h) +AC_CACHE_CHECK([if mkdir takes one argument], gnupg_cv_mkdir_takes_one_arg, [AC_TRY_COMPILE([ #include #ifdef HAVE_SYS_STAT_H @@ -663,7 +719,6 @@ if test $gnupg_cv_mkdir_takes_one_arg = yes ; then fi ]) - dnl GPH_PROG_DOCBOOK() dnl Check whether we have the needed Docbook environment dnl and issue a warning if this is not the case. diff --git a/cipher/ChangeLog b/cipher/ChangeLog index 49595a93c..877a2ad26 100644 --- a/cipher/ChangeLog +++ b/cipher/ChangeLog @@ -1,3 +1,85 @@ +Fri Jul 14 19:38:23 CEST 2000 Werner Koch + + * md.c (gcry_md_ctl): Support GCRYCTL_{START,STOP}_DUMP. + + * Makefile.am: Never compile mingw32 as module. + + * Makefile.am: Tweaked module build and removed libtool + + * Makefile.am: Replaced -O1 by -O. Suggested by Alec Habig. + + * elgamal.c (sign): Removed inactive code. + + * rsa.c, rsa.h: New based on the old module version (only in CVS for now). + * pubkey.c (setup_pubkey_table): Added commented support for RSA. + + * rndunix.c (waitpid): New. For UTS 2.1. All by Dave Dykstra. + (my_popen): Do the FD_CLOEXEC only if it is available + (start_gatherer): Cope with missing _SC_OPEN_MAX + + * rndunix.c: Add some more headers for QNX. By Sam Roberts. + + * rndegd.c (gather_random): Shortcut level 0. + * rndunix.c (gather_random): Ditto. + * rndw32.c (gather_random): Ditto. + + * rndw32.c: Replaced with code from Cryptlib and commented the old stuff. + * rndw32.c: Add some debuging code enabled by an environment variable. + + * random.c (read_seed_file): Binary open for DOSish system + (update_random_seed_file): Ditto. + * random.c [MINGW32]: Include process.h for getpid. + * random.c (fast_random_poll): Add clock_gettime() as fallback for + system which support this POSIX.4 fucntion. By Sam Roberts. + + * random.c (read_seed_file): Removed the S_ISLNK test becuase it + is already covered by !S_ISREG and is not defined in Unixware. + Reported by Dave Dykstra. + (update_random_seed_file): Silently ignore update request when pool + is not filled. + + * random.c (read_seed_file): New. + (set_random_seed_file): New. + (read_pool): Try to read the seeding file. + (update_random_seed_file): New. + + (read_pool): Do an initial extra seeding when level 2 quality random + is requested the first time. This requestes at least POOLSIZE/2 bytes + of entropy. Compined with the seeding file this should make normal + random bytes cheaper and increase the quality of the random bytes + used for key generation. + + * random.c (read_pool): Print a more friendly error message in + cases when too much random is requested in one call. + + * random.c (fast_random_poll): Check whether RUSAGE_SELF is defined; + this is not the case for some ESIX and Unixware, although they have + getrusage(). + + * primegen.c (generate_elg_prime): All primes are now generated with + the lowest random quality level. Because they are public anyway we + don't need stronger random and by this we do not drain the systems + entropy so much. + + * primegen.c (register_primegen_progress): New. + * dsa.c (register_pk_dsa_progress): New. + * elgamal.c (register_pk_elg_progress): New. + + * elgamal.c (wiener_map): New. + (gen_k): Use a much smaller k. + (generate): Calculate the qbits using the wiener map and + choose an x at a size comparable to the one choosen in gen_k + + * rmd160.c (rmd160_get_info): Moved casting to the left side due to a + problem with UTS4.3. Suggested by Dave Dykstra. + * sha1.c (sha1_get_info): Ditto. + * tiger.c (tiger_get_info): Ditto. + * md5.c (md5_get_info): Ditto + * des.c (des_get_info): Ditto. + * blowfish.c (blowfish_get_info): Ditto. + * cast5.c (cast5_get_info): Ditto. + * twofish.c (twofish_get_info): Ditto. + Fri Mar 24 11:25:45 CET 2000 Werner Koch * md.c (md_open): Add hmac arg and allocate space for the pads. diff --git a/cipher/Makefile.am b/cipher/Makefile.am index aa766bbc4..9792419f5 100644 --- a/cipher/Makefile.am +++ b/cipher/Makefile.am @@ -5,15 +5,8 @@ INCLUDES = -I$(top_srcdir)/gcrypt noinst_LTLIBRARIES = libcipher.la -# The configure script greps the module names from the following lines. -# You must also add all these names to EXTRA_PROGRAMS some lines below -# and EXTRA_foo_SOURCES entries. -# Hmmm is there a more easy way to do this? (EXTRA_PROGRAMS -# might also list programs which are not modules) -# MODULES: rndunix rndlinux rndegd rndw32 -# MODULES: sha1 rmd160 md5 tiger -EXTRA_PROGRAMS = rndunix rndlinux rndegd rndw32 \ - sha1 rmd160 md5 tiger +# The configure script greps the module names from the EXTRA_PROGRAMS line +EXTRA_PROGRAMS = rndlinux rndunix rndegd rndw32 sha1 rmd160 md5 tiger EXTRA_rndlinux_SOURCES = rndlinux.c EXTRA_rndunix_SOURCES = rndunix.c @@ -73,7 +66,7 @@ libcipher_la_LIBADD = @STATIC_CIPHER_OBJS@ tiger: $(srcdir)/tiger.c `echo $(COMPILE) $(DYNLINK_MOD_CFLAGS) -o tiger $(srcdir)/tiger.c | \ - sed -e 's/-O[2-9s]*/-O1/g' ` + sed -e 's/-O[2-9s]*/-O/g' ` tiger.o: $(srcdir)/tiger.c `echo $(COMPILE) -c $(srcdir)/tiger.c | sed -e 's/-O[2-9s]*/-O1/g' ` @@ -98,9 +91,3 @@ rndlinux: $(srcdir)/rndlinux.c rndegd: $(srcdir)/rndegd.c $(COMPILE) $(DYNLINK_MOD_CFLAGS) -o rndegd $(srcdir)/rndegd.c -# We need to have a better system for selection which modules -# to compile. For now this works. -rndw32: $(srcdir)/rndw32.c - echo "#!/bin/sh" >rndw32 - echo "Not usable as a module" >>rndw32 - diff --git a/cipher/blowfish.c b/cipher/blowfish.c index e7f047ff5..2bdb3b5e0 100644 --- a/cipher/blowfish.c +++ b/cipher/blowfish.c @@ -584,9 +584,12 @@ blowfish_get_info( int algo, size_t *keylen, *keylen = 128; *blocksize = BLOWFISH_BLOCKSIZE; *contextsize = sizeof(BLOWFISH_context); - *r_setkey = FNCCAST_SETKEY(bf_setkey); - *r_encrypt= FNCCAST_CRYPT(encrypt_block); - *r_decrypt= FNCCAST_CRYPT(decrypt_block); + *(int (**)(BLOWFISH_context*, byte*, unsigned))r_setkey + = bf_setkey; + *(void (**)(BLOWFISH_context*, byte*, byte*))r_encrypt + = encrypt_block; + *(void (**)(BLOWFISH_context*, byte*, byte*))r_decrypt + = decrypt_block; if( algo == CIPHER_ALGO_BLOWFISH ) return "BLOWFISH"; diff --git a/cipher/cast5.c b/cipher/cast5.c index aaa0a42ef..64f6bb0e0 100644 --- a/cipher/cast5.c +++ b/cipher/cast5.c @@ -610,9 +610,13 @@ cast5_get_info( int algo, size_t *keylen, *keylen = 128; *blocksize = CAST5_BLOCKSIZE; *contextsize = sizeof(CAST5_context); - *r_setkey = FNCCAST_SETKEY(cast_setkey); - *r_encrypt= FNCCAST_CRYPT(encrypt_block); - *r_decrypt= FNCCAST_CRYPT(decrypt_block); + *(int (**)(CAST5_context*, byte*, unsigned))r_setkey + = cast_setkey; + *(void (**)(CAST5_context*, byte*, byte*))r_encrypt + = encrypt_block; + *(void (**)(CAST5_context*, byte*, byte*))r_decrypt + = decrypt_block; + if( algo == CIPHER_ALGO_CAST5 ) return "CAST5"; diff --git a/cipher/des.c b/cipher/des.c index af5b5845b..9c3c54194 100644 --- a/cipher/des.c +++ b/cipher/des.c @@ -1001,9 +1001,12 @@ des_get_info( int algo, size_t *keylen, *keylen = 192; *blocksize = 8; *contextsize = sizeof(struct _tripledes_ctx); - *r_setkey = FNCCAST_SETKEY(do_tripledes_setkey); - *r_encrypt= FNCCAST_CRYPT(do_tripledes_encrypt); - *r_decrypt= FNCCAST_CRYPT(do_tripledes_decrypt); + *(int (**)(struct _tripledes_ctx*, byte*, unsigned))r_setkey + = do_tripledes_setkey; + *(void (**)(struct _tripledes_ctx*, byte*, byte*))r_encrypt + = do_tripledes_encrypt; + *(void (**)(struct _tripledes_ctx*, byte*, byte*))r_decrypt + = do_tripledes_decrypt; return "3DES"; } return NULL; diff --git a/cipher/dsa.c b/cipher/dsa.c index 1f132ae0c..255fa372c 100644 --- a/cipher/dsa.c +++ b/cipher/dsa.c @@ -1,5 +1,5 @@ /* dsa.c - DSA signature scheme - * Copyright (C) 1998 Free Software Foundation, Inc. + * Copyright (C) 1998, 2000 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -52,13 +52,28 @@ static void generate( DSA_secret_key *sk, unsigned nbits, MPI **ret_factors ); static void sign(MPI r, MPI s, MPI input, DSA_secret_key *skey); static int verify(MPI r, MPI s, MPI input, DSA_public_key *pkey); +static void (*progress_cb) ( void *, int ); +static void *progress_cb_data; + +void +register_pk_dsa_progress ( void (*cb)( void *, int), void *cb_data ) +{ + progress_cb = cb; + progress_cb_data = cb_data; +} + + static void progress( int c ) { - fputc( c, stderr ); + if ( progress_cb ) + progress_cb ( progress_cb_data, c ); + else + fputc( c, stderr ); } + /**************** * Generate a random secret exponent k less than q */ diff --git a/cipher/elgamal.c b/cipher/elgamal.c index 02995e02e..f2c029b36 100644 --- a/cipher/elgamal.c +++ b/cipher/elgamal.c @@ -1,5 +1,5 @@ /* elgamal.c - ElGamal Public Key encryption - * Copyright (C) 1998 Free Software Foundation, Inc. + * Copyright (C) 1998, 2000 Free Software Foundation, Inc. * * For a description of the algorithm, see: * Bruce Schneier: Applied Cryptography. John Wiley & Sons, 1996. @@ -56,13 +56,67 @@ static void sign(MPI a, MPI b, MPI input, ELG_secret_key *skey); static int verify(MPI a, MPI b, MPI input, ELG_public_key *pkey); +static void (*progress_cb) ( void *, int ); +static void *progress_cb_data; + +void +register_pk_elg_progress ( void (*cb)( void *, int), void *cb_data ) +{ + progress_cb = cb; + progress_cb_data = cb_data; +} + + static void progress( int c ) { - fputc( c, stderr ); + if ( progress_cb ) + progress_cb ( progress_cb_data, c ); + else + fputc( c, stderr ); } +/**************** + * Michael Wiener's table on subgroup sizes to match field sizes + * (floating around somewhere - Fixme: need a reference) + */ +static unsigned int +wiener_map( unsigned int n ) +{ + static struct { unsigned int p_n, q_n; } t[] = + { /* p q attack cost */ + { 512, 119 }, /* 9 x 10^17 */ + { 768, 145 }, /* 6 x 10^21 */ + { 1024, 165 }, /* 7 x 10^24 */ + { 1280, 183 }, /* 3 x 10^27 */ + { 1536, 198 }, /* 7 x 10^29 */ + { 1792, 212 }, /* 9 x 10^31 */ + { 2048, 225 }, /* 8 x 10^33 */ + { 2304, 237 }, /* 5 x 10^35 */ + { 2560, 249 }, /* 3 x 10^37 */ + { 2816, 259 }, /* 1 x 10^39 */ + { 3072, 269 }, /* 3 x 10^40 */ + { 3328, 279 }, /* 8 x 10^41 */ + { 3584, 288 }, /* 2 x 10^43 */ + { 3840, 296 }, /* 4 x 10^44 */ + { 4096, 305 }, /* 7 x 10^45 */ + { 4352, 313 }, /* 1 x 10^47 */ + { 4608, 320 }, /* 2 x 10^48 */ + { 4864, 328 }, /* 2 x 10^49 */ + { 5120, 335 }, /* 3 x 10^50 */ + { 0, 0 } + }; + int i; + + for(i=0; t[i].p_n; i++ ) { + if( n <= t[i].p_n ) + return t[i].q_n; + } + /* not in table - use some arbitrary high number ;-) */ + return n / 8 + 200; +} + static void test_keys( ELG_secret_key *sk, unsigned nbits ) { @@ -104,38 +158,44 @@ gen_k( MPI p ) MPI k = mpi_alloc_secure( 0 ); MPI temp = mpi_alloc( mpi_get_nlimbs(p) ); MPI p_1 = mpi_copy(p); - unsigned int nbits = mpi_get_nbits(p); - unsigned int nbytes = (nbits+7)/8; + unsigned int orig_nbits = mpi_get_nbits(p); + unsigned int nbits, nbytes; char *rndbuf = NULL; + /* IMO using a k much lesser than p is sufficient and it greatly + * improves the encryption performance. We use Wiener's table + * and add a large safety margin. + */ + nbits = wiener_map( orig_nbits ) * 3 / 2; + if( nbits >= orig_nbits ) + BUG(); + + nbytes = (nbits+7)/8; if( DBG_CIPHER ) log_debug("choosing a random k "); mpi_sub_ui( p_1, p, 1); for(;;) { - if( DBG_CIPHER ) - progress('.'); if( !rndbuf || nbits < 32 ) { g10_free(rndbuf); rndbuf = gcry_random_bytes_secure( nbytes, GCRY_STRONG_RANDOM ); } else { /* change only some of the higher bits */ - /* we could imporove this by directly requesting more memory + /* we could improve this by directly requesting more memory * at the first call to get_random_bytes() and use this the here - * maybe it is easier to do this directly in random.c */ + * maybe it is easier to do this directly in random.c + * Anyway, it is highly inlikely that we will ever reach this code + */ char *pp = gcry_random_bytes_secure( 4, GCRY_STRONG_RANDOM ); memcpy( rndbuf, pp, 4 ); g10_free(pp); + log_debug("gen_k: tsss, never expected to reach this\n"); } mpi_set_buffer( k, rndbuf, nbytes, 0 ); for(;;) { - /* make sure that the number is of the exact lenght */ - if( mpi_test_bit( k, nbits-1 ) ) - mpi_set_highbit( k, nbits-1 ); - else { - mpi_set_highbit( k, nbits-1 ); - mpi_clear_bit( k, nbits-1 ); - } + /* Hmm, actually we don't need this step here + * because we use k much smaller than p - we do it anyway + * just in case the keep on adding a one to k ;) */ if( !(mpi_cmp( k, p_1 ) < 0) ) { /* check: k < (p-1) */ if( DBG_CIPHER ) progress('+'); @@ -149,6 +209,8 @@ gen_k( MPI p ) if( mpi_gcd( temp, k, p_1 ) ) goto found; /* okay, k is relatively prime to (p-1) */ mpi_add_ui( k, k, 1 ); + if( DBG_CIPHER ) + progress('.'); } } found: @@ -167,7 +229,7 @@ gen_k( MPI p ) * and an array with n-1 factors of (p-1) */ static void -generate( ELG_secret_key *sk, unsigned nbits, MPI **ret_factors ) +generate( ELG_secret_key *sk, unsigned int nbits, MPI **ret_factors ) { MPI p; /* the prime */ MPI p_min1; @@ -175,19 +237,15 @@ generate( ELG_secret_key *sk, unsigned nbits, MPI **ret_factors ) MPI x; /* the secret exponent */ MPI y; MPI temp; - unsigned qbits; + unsigned int qbits; + unsigned int xbits; byte *rndbuf; p_min1 = mpi_alloc( (nbits+BITS_PER_MPI_LIMB-1)/BITS_PER_MPI_LIMB ); temp = mpi_alloc( (nbits+BITS_PER_MPI_LIMB-1)/BITS_PER_MPI_LIMB ); - if( nbits < 512 ) - qbits = 120; - else if( nbits <= 1024 ) - qbits = 160; - else if( nbits <= 2048 ) - qbits = 200; - else - qbits = 240; + qbits = wiener_map( nbits ); + if( qbits & 1 ) /* better have a even one */ + qbits++; g = mpi_alloc(1); p = generate_elg_prime( 0, nbits, qbits, g, ret_factors ); mpi_sub_ui(p_min1, p, 1); @@ -198,18 +256,26 @@ generate( ELG_secret_key *sk, unsigned nbits, MPI **ret_factors ) * This must be a very good random number because this is the * secret part. The prime is public and may be shared anyway, * so a random generator level of 1 is used for the prime. + * + * I don't see a reason to have a x of about the same size + * as the p. It should be sufficient to have one about the size + * of q or the later used k plus a large safety margin. Decryption + * will be much faster with such an x. */ - x = mpi_alloc_secure( nbits/BITS_PER_MPI_LIMB ); + xbits = qbits * 3 / 2; + if( xbits >= nbits ) + BUG(); + x = mpi_alloc_secure( xbits/BITS_PER_MPI_LIMB ); if( DBG_CIPHER ) - log_debug("choosing a random x "); + log_debug("choosing a random x of size %u", xbits ); rndbuf = NULL; do { if( DBG_CIPHER ) progress('.'); if( rndbuf ) { /* change only some of the higher bits */ - if( nbits < 16 ) {/* should never happen ... */ + if( xbits < 16 ) {/* should never happen ... */ g10_free(rndbuf); - rndbuf = gcry_random_bytes_secure( (nbits+7)/8, + rndbuf = gcry_random_bytes_secure( (xbits+7)/8, GCRY_VERY_STRONG_RANDOM ); } else { @@ -220,11 +286,11 @@ generate( ELG_secret_key *sk, unsigned nbits, MPI **ret_factors ) } } else { - rndbuf = gcry_random_bytes_secure( (nbits+7)/8, + rndbuf = gcry_random_bytes_secure( (xbits+7)/8, GCRY_VERY_STRONG_RANDOM ); } - mpi_set_buffer( x, rndbuf, (nbits+7)/8, 0 ); - mpi_clear_highbit( x, nbits+1 ); + mpi_set_buffer( x, rndbuf, (xbits+7)/8, 0 ); + mpi_clear_highbit( x, xbits+1 ); } while( !( mpi_cmp_ui( x, 0 )>0 && mpi_cmp( x, p_min1 )<0 ) ); g10_free(rndbuf); @@ -311,7 +377,6 @@ decrypt(MPI output, MPI a, MPI b, ELG_secret_key *skey ) MPI t1 = mpi_alloc_secure( mpi_get_nlimbs( skey->p ) ); /* output = b/(a^x) mod p */ - gcry_mpi_powm( t1, a, skey->x, skey->p ); mpi_invm( t1, t1, skey->p ); mpi_mulm( output, b, t1, skey->p ); @@ -351,10 +416,6 @@ sign(MPI a, MPI b, MPI input, ELG_secret_key *skey ) gcry_mpi_powm( a, skey->g, k, skey->p ); mpi_mul(t, skey->x, a ); mpi_subm(t, input, t, p_1 ); - while( mpi_is_neg(t) ) { - BUG(); /* That is nonsense code - left over from a very early test?*/ - mpi_add(t, t, p_1); - } mpi_invm(inv, k, p_1 ); mpi_mulm(b, t, inv, p_1 ); @@ -557,7 +618,7 @@ elg_verify( int algo, MPI hash, MPI *data, MPI *pkey, -unsigned +unsigned int elg_get_nbits( int algo, MPI *pkey ) { if( !is_ELGAMAL(algo) ) @@ -587,10 +648,10 @@ elg_get_info( int algo, int *npkey, int *nskey, int *nenc, int *nsig, *nsig = 2; switch( algo ) { - case PUBKEY_ALGO_ELGAMAL: + case GCRY_PK_ELG: *use = GCRY_PK_USAGE_SIGN|GCRY_PK_USAGE_ENCR; return "ELG"; - case PUBKEY_ALGO_ELGAMAL_E: + case GCRY_PK_ELG_E: *use = GCRY_PK_USAGE_SIGN|GCRY_PK_USAGE_ENCR; return "ELG-E"; default: *use = 0; return NULL; diff --git a/cipher/md.c b/cipher/md.c index 98795206c..e8ac8ac24 100644 --- a/cipher/md.c +++ b/cipher/md.c @@ -562,6 +562,12 @@ gcry_md_ctl( GCRY_MD_HD hd, int cmd, byte *buffer, size_t buflen) else if ( !(rc = prepare_macpads( hd, buffer, buflen )) ) gcry_md_reset( hd ); } + else if( cmd == GCRYCTL_START_DUMP ) { + md_start_debug( hd, buffer ); + } + else if( cmd == GCRYCTL_STOP_DUMP ) { + md_stop_debug( hd ); + } else rc = GCRYERR_INV_OP; return set_lasterr( rc ); @@ -834,7 +840,7 @@ gcry_md_algo_info( int algo, int what, void *buffer, size_t *nbytes) -void +static void md_start_debug( GCRY_MD_HD md, const char *suffix ) { static int idx=0; @@ -851,7 +857,7 @@ md_start_debug( GCRY_MD_HD md, const char *suffix ) log_debug("md debug: can't open %s\n", buf ); } -void +static void md_stop_debug( GCRY_MD_HD md ) { if( md->ctx->debug ) { diff --git a/cipher/md5.c b/cipher/md5.c index 161d44300..c4351f1f0 100644 --- a/cipher/md5.c +++ b/cipher/md5.c @@ -344,10 +344,10 @@ md5_get_info( int algo, size_t *contextsize, *r_asnoid = asn; *r_asnlen = DIM(asn); *r_mdlen = 16; - *r_init = (void (*)(void *))md5_init; - *r_write = (void (*)(void *, byte*, size_t))md5_write; - *r_final = (void (*)(void *))md5_final; - *r_read = (byte *(*)(void *))md5_read; + *(void (**)(MD5_CONTEXT *))r_init = md5_init; + *(void (**)(MD5_CONTEXT *, byte*, size_t))r_write = md5_write; + *(void (**)(MD5_CONTEXT *))r_final = md5_final; + *(byte *(**)(MD5_CONTEXT *))r_read = md5_read; return "MD5"; } diff --git a/cipher/primegen.c b/cipher/primegen.c index 2f16b0838..f5dca8592 100644 --- a/cipher/primegen.c +++ b/cipher/primegen.c @@ -1,5 +1,5 @@ /* primegen.c - prime number generator - * Copyright (C) 1998 Free Software Foundation, Inc. + * Copyright (C) 1998, 2000 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -38,11 +38,24 @@ static int check_prime( MPI prime, MPI val_2 ); static int is_prime( MPI n, int steps, int *count ); static void m_out_of_n( char *array, int m, int n ); +static void (*progress_cb) ( void *, int ); +static void *progress_cb_data; + +void +register_primegen_progress ( void (*cb)( void *, int), void *cb_data ) +{ + progress_cb = cb; + progress_cb_data = cb_data; +} + static void progress( int c ) { - fputc( c, stderr ); + if ( progress_cb ) + progress_cb ( progress_cb_data, c ); + else + fputc( c, stderr ); } @@ -117,8 +130,8 @@ generate_elg_prime( int mode, unsigned pbits, unsigned qbits, log_debug("gen prime: pbits=%u qbits=%u fbits=%u/%u n=%d\n", pbits, req_qbits, qbits, fbits, n ); prime = mpi_alloc( (pbits + BITS_PER_MPI_LIMB - 1) / BITS_PER_MPI_LIMB ); - q = gen_prime( qbits, 0, 1 ); - q_factor = mode==1? gen_prime( req_qbits, 0, 1 ) : NULL; + q = gen_prime( qbits, 0, 0 ); + q_factor = mode==1? gen_prime( req_qbits, 0, 0 ) : NULL; /* allocate an array to hold the factors + 2 for later usage */ factors = g10_xcalloc( n+2, sizeof *factors ); @@ -177,7 +190,7 @@ generate_elg_prime( int mode, unsigned pbits, unsigned qbits, count1 = 0; qbits++; progress('>'); - q = gen_prime( qbits, 0, 1 ); + q = gen_prime( qbits, 0, 0 ); goto next_try; } } @@ -188,7 +201,7 @@ generate_elg_prime( int mode, unsigned pbits, unsigned qbits, count2 = 0; qbits--; progress('<'); - q = gen_prime( qbits, 0, 1 ); + q = gen_prime( qbits, 0, 0 ); goto next_try; } } diff --git a/cipher/pubkey.c b/cipher/pubkey.c index 3ace1d83c..b139720d6 100644 --- a/cipher/pubkey.c +++ b/cipher/pubkey.c @@ -1,5 +1,5 @@ /* pubkey.c - pubkey dispatcher - * Copyright (C) 1998, 1999 Free Software Foundation, Inc. + * Copyright (C) 1998, 1999, 2000 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -30,6 +30,9 @@ #include "cipher.h" #include "elgamal.h" #include "dsa.h" +#if 0 +#include "rsa.h" +#endif #include "dynload.h" /* FIXME: use set_lasterr() */ @@ -193,6 +196,60 @@ setup_pubkey_table(void) BUG(); i++; + #if 0 + pubkey_table[i].algo = PUBKEY_ALGO_RSA; + pubkey_table[i].name = rsa_get_info( pubkey_table[i].algo, + &pubkey_table[i].npkey, + &pubkey_table[i].nskey, + &pubkey_table[i].nenc, + &pubkey_table[i].nsig, + &pubkey_table[i].use ); + pubkey_table[i].generate = rsa_generate; + pubkey_table[i].check_secret_key = rsa_check_secret_key; + pubkey_table[i].encrypt = rsa_encrypt; + pubkey_table[i].decrypt = rsa_decrypt; + pubkey_table[i].sign = rsa_sign; + pubkey_table[i].verify = rsa_verify; + pubkey_table[i].get_nbits = rsa_get_nbits; + if( !pubkey_table[i].name ) + BUG(); + i++; + pubkey_table[i].algo = PUBKEY_ALGO_RSA_E; + pubkey_table[i].name = rsa_get_info( pubkey_table[i].algo, + &pubkey_table[i].npkey, + &pubkey_table[i].nskey, + &pubkey_table[i].nenc, + &pubkey_table[i].nsig, + &pubkey_table[i].use ); + pubkey_table[i].generate = rsa_generate; + pubkey_table[i].check_secret_key = rsa_check_secret_key; + pubkey_table[i].encrypt = rsa_encrypt; + pubkey_table[i].decrypt = rsa_decrypt; + pubkey_table[i].sign = dummy_sign; + pubkey_table[i].verify = dummy_verify; + pubkey_table[i].get_nbits = rsa_get_nbits; + if( !pubkey_table[i].name ) + BUG(); + i++; + pubkey_table[i].algo = PUBKEY_ALGO_RSA_S; + pubkey_table[i].name = rsa_get_info( pubkey_table[i].algo, + &pubkey_table[i].npkey, + &pubkey_table[i].nskey, + &pubkey_table[i].nenc, + &pubkey_table[i].nsig, + &pubkey_table[i].use ); + pubkey_table[i].generate = rsa_generate; + pubkey_table[i].check_secret_key = rsa_check_secret_key; + pubkey_table[i].encrypt = dummy_encrypt; + pubkey_table[i].decrypt = dummy_decrypt; + pubkey_table[i].sign = rsa_sign; + pubkey_table[i].verify = rsa_verify; + pubkey_table[i].get_nbits = rsa_get_nbits; + if( !pubkey_table[i].name ) + BUG(); + i++; + #endif + for( ; i < TABLE_SIZE; i++ ) pubkey_table[i].name = NULL; } diff --git a/cipher/random.c b/cipher/random.c index 38991a182..f8faeebf0 100644 --- a/cipher/random.c +++ b/cipher/random.c @@ -1,5 +1,5 @@ /* random.c - random number generator - * Copyright (C) 1998 Free Software Foundation, Inc. + * Copyright (C) 1998, 2000 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -36,15 +36,22 @@ #include #include #include +#include #ifdef HAVE_GETHRTIME #include #endif #ifdef HAVE_GETTIMEOFDAY #include #endif +#ifdef HAVE_CLOCK_GETTIME + #include +#endif #ifdef HAVE_GETRUSAGE #include #endif +#ifdef __MINGW32__ + #include +#endif #include "g10lib.h" #include "rmd.h" #include "random.h" @@ -89,6 +96,9 @@ static size_t pool_writepos; static int pool_filled; static int pool_balance; static int just_mixed; +static int did_initial_extra_seeding; +static char *seed_file_name; +static int allow_seed_file_update; static int secure_alloc; static int quick_test; @@ -274,6 +284,139 @@ mix_pool(byte *pool) } } +void +set_random_seed_file( const char *name ) +{ + if( seed_file_name ) + BUG(); + seed_file_name = g10_xstrdup( name ); +} + +/**************** + * Read in a seed form the random_seed file + * and return true if this was successful + */ +static int +read_seed_file() +{ + int fd; + struct stat sb; + unsigned char buffer[POOLSIZE]; + int n; + + if( !seed_file_name ) + return 0; + + #ifdef HAVE_DOSISH_SYSTEM + fd = open( seed_file_name, O_RDONLY | O_BINARY ); + #else + fd = open( seed_file_name, O_RDONLY ); + #endif + if( fd == -1 && errno == ENOENT) { + allow_seed_file_update = 1; + return 0; + } + + if( fd == -1 ) { + log_info(_("can't open `%s': %s\n"), seed_file_name, strerror(errno) ); + return 0; + } + if( fstat( fd, &sb ) ) { + log_info(_("can't stat `%s': %s\n"), seed_file_name, strerror(errno) ); + close(fd); + return 0; + } + if( !S_ISREG(sb.st_mode) ) { + log_info(_("`%s' is not a regular file - ignored\n"), seed_file_name ); + close(fd); + return 0; + } + if( !sb.st_size ) { + log_info(_("note: random_seed file is empty\n") ); + close(fd); + allow_seed_file_update = 1; + return 0; + } + if( sb.st_size != POOLSIZE ) { + log_info(_("warning: invalid size of random_seed file - not used\n") ); + close(fd); + return 0; + } + do { + n = read( fd, buffer, POOLSIZE ); + } while( n == -1 && errno == EINTR ); + if( n != POOLSIZE ) { + log_fatal(_("can't read `%s': %s\n"), seed_file_name,strerror(errno) ); + close(fd); + return 0; + } + + close(fd); + + add_randomness( buffer, POOLSIZE, 0 ); + /* add some minor entropy to the pool now (this will also force a mixing) */ + { pid_t x = getpid(); + add_randomness( &x, sizeof(x), 0 ); + } + { time_t x = time(NULL); + add_randomness( &x, sizeof(x), 0 ); + } + { clock_t x = clock(); + add_randomness( &x, sizeof(x), 0 ); + } + /* And read a few bytes from our entropy source. By using + * a level of 0 this will not block and might not return anything + * with some entropy drivers, however the rndlinux driver will use + * /dev/urandom and return some stuff - Do not read to much as we + * want to be friendly to the scare system entropy resource. */ + read_random_source( 0, 16, 0 ); + + allow_seed_file_update = 1; + return 1; +} + +void +update_random_seed_file() +{ + ulong *sp, *dp; + int fd, i; + + if( !seed_file_name || !is_initialized || !pool_filled ) + return; + if( !allow_seed_file_update ) { + log_info(_("note: random_seed file not updated\n")); + return; + } + + + /* copy the entropy pool to a scratch pool and mix both of them */ + for(i=0,dp=(ulong*)keypool, sp=(ulong*)rndpool; + i < POOLWORDS; i++, dp++, sp++ ) { + *dp = *sp + ADD_VALUE; + } + mix_pool(rndpool); rndstats.mixrnd++; + mix_pool(keypool); rndstats.mixkey++; + + #ifdef HAVE_DOSISH_SYSTEM + fd = open( seed_file_name, O_WRONLY|O_CREAT|O_TRUNC|O_BINARY, + S_IRUSR|S_IWUSR ); + #else + fd = open( seed_file_name, O_WRONLY|O_CREAT|O_TRUNC, S_IRUSR|S_IWUSR ); + #endif + if( fd == -1 ) { + log_info(_("can't create `%s': %s\n"), seed_file_name, strerror(errno) ); + return; + } + do { + i = write( fd, keypool, POOLSIZE ); + } while( i == -1 && errno == EINTR ); + if( i != POOLSIZE ) { + log_info(_("can't write `%s': %s\n"), seed_file_name, strerror(errno) ); + } + if( close(fd) ) + log_info(_("can't close `%s': %s\n"), seed_file_name, strerror(errno) ); +} + static void read_pool( byte *buffer, size_t length, int level ) @@ -281,8 +424,31 @@ read_pool( byte *buffer, size_t length, int level ) int i; ulong *sp, *dp; - if( length >= POOLSIZE ) - BUG(); /* not allowed */ + if( length >= POOLSIZE ) { + log_fatal(_("too many random bits requested; the limit is %d\n"), + POOLSIZE*8-1 ); + } + + if( !pool_filled ) { + if( read_seed_file() ) + pool_filled = 1; + } + + /* For level 2 quality (key generation) we alwas make + * sure that the pool has been seeded enough initially */ + if( level == 2 && !did_initial_extra_seeding ) { + size_t needed; + + pool_balance = 0; + needed = length - pool_balance; + if( needed < POOLSIZE/2 ) + needed = POOLSIZE/2; + else if( needed > POOLSIZE ) + BUG(); + read_random_source( 3, needed, 2 ); + pool_balance += needed; + did_initial_extra_seeding=1; + } /* for level 2 make sure that there is enough random in the pool */ if( level == 2 && pool_balance < length ) { @@ -347,6 +513,12 @@ read_pool( byte *buffer, size_t length, int level ) /**************** * Add LENGTH bytes of randomness from buffer to the pool. * source may be used to specify the randomness source. + * Source is: + * 0 - used ony for initialization + * 1 - fast random poll function + * 2 - normal poll function + * 3 - used when level 2 random quality has been requested + * to do an extra pool seed. */ static void add_randomness( const void *buffer, size_t length, int source ) @@ -410,6 +582,13 @@ fast_random_poll() add_randomness( &tv.tv_sec, sizeof(tv.tv_sec), 1 ); add_randomness( &tv.tv_usec, sizeof(tv.tv_usec), 1 ); } + #elif HAVE_CLOCK_GETTIME + { struct timespec tv; + if( clock_gettime( CLOCK_REALTIME, &tv ) == -1 ) + BUG(); + add_randomness( &tv.tv_sec, sizeof(tv.tv_sec), 1 ); + add_randomness( &tv.tv_nsec, sizeof(tv.tv_nsec), 1 ); + } #else /* use times */ #ifndef HAVE_DOSISH_SYSTEM { struct tms buf; @@ -419,13 +598,28 @@ fast_random_poll() #endif #endif #ifdef HAVE_GETRUSAGE + #ifndef RUSAGE_SELF + #ifdef __GCC__ + #warning There is no RUSAGE_SELF on this system + #endif + #else { struct rusage buf; if( getrusage( RUSAGE_SELF, &buf ) ) BUG(); add_randomness( &buf, sizeof buf, 1 ); memset( &buf, 0, sizeof buf ); } + #endif #endif + /* time and clock are availabe on all systems - so + * we better do it just in case one of the above functions + * didn't work */ + { time_t x = time(NULL); + add_randomness( &x, sizeof(x), 1 ); + } + { clock_t x = clock(); + add_randomness( &x, sizeof(x), 1 ); + } } @@ -472,9 +666,9 @@ gather_faked( void (*add)(const void*, size_t, int), int requester, #endif initialized=1; #ifdef HAVE_RAND - srand( time(NULL) * getpid()); + srand(make_timestamp()*getpid()); #else - srandom( time(NULL) * getpid()); + srandom(make_timestamp()*getpid()); #endif } diff --git a/cipher/rmd160.c b/cipher/rmd160.c index 7b230087f..fb9d6fa50 100644 --- a/cipher/rmd160.c +++ b/cipher/rmd160.c @@ -562,10 +562,10 @@ rmd160_get_info( int algo, size_t *contextsize, *r_asnoid = asn; *r_asnlen = DIM(asn); *r_mdlen = 20; - *r_init = (void (*)(void *))rmd160_init; - *r_write = (void (*)(void *, byte*, size_t))rmd160_write; - *r_final = (void (*)(void *))rmd160_final; - *r_read = (byte *(*)(void *))rmd160_read; + *(void (**)(RMD160_CONTEXT *))r_init = rmd160_init; + *(void (**)(RMD160_CONTEXT *, byte*, size_t))r_write = rmd160_write; + *(void (**)(RMD160_CONTEXT *))r_final = rmd160_final; + *(byte *(**)(RMD160_CONTEXT *))r_read = rmd160_read; return "RIPEMD160"; } diff --git a/cipher/rndegd.c b/cipher/rndegd.c index 4d5f0ef3a..f6870cdd6 100644 --- a/cipher/rndegd.c +++ b/cipher/rndegd.c @@ -1,5 +1,5 @@ /* rndegd.c - interface to the EGD - * Copyright (C) 1999 Free Software Foundation, Inc. + * Copyright (C) 1999, 2000 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -117,9 +117,13 @@ do_read( int fd, void *buf, size_t nbytes ) -/* Note: we always use the highest level. +/**************** + * Note: we always use the highest level. * TO boost the performance we may want to add some * additional code for level 1 + * + * Using a level of 0 should never block and better add nothing + * to the pool. So this is just a dummy for EGD. */ static int gather_random( void (*add)(const void*, size_t, int), int requester, @@ -133,7 +137,8 @@ gather_random( void (*add)(const void*, size_t, int), int requester, if( !length ) return 0; - + if( !level ) + return 0; restart: if( do_restart ) { diff --git a/cipher/rndunix.c b/cipher/rndunix.c index 4ab9f65f6..99a416ea7 100644 --- a/cipher/rndunix.c +++ b/cipher/rndunix.c @@ -77,7 +77,7 @@ #ifndef __QNX__ #include #endif /* __QNX__ */ -#ifdef _AIX +#if defined( _AIX ) || defined( __QNX__ ) #include #endif /* _AIX */ #ifndef __QNX__ @@ -91,6 +91,10 @@ #endif /* __hpux 9.x, after that it's in unistd.h */ #include /* #include */ +#ifdef __QNX__ +#include +#include +#endif /* __QNX__ */ #include #include "types.h" /* for byte and u32 typedefs */ @@ -100,7 +104,13 @@ #include "g10lib.h" #ifndef EAGAIN - #define EAGAIN EWOULDBLOCK +#define EAGAIN EWOULDBLOCK +#endif +#ifndef STDIN_FILENO +#define STDIN_FILENO 0 +#endif +#ifndef STDOUT_FILENO +#define STDOUT_FILENO 1 #endif #define GATHER_BUFSIZE 49152 /* Usually about 25K are filled */ @@ -306,6 +316,32 @@ typedef struct { char data[500]; /* gathered data */ } GATHER_MSG; +#ifndef HAVE_WAITPID +pid_t +waitpid(pid_t pid, int *statptr, int options) +{ + #ifdef HAVE_WAIT4 + return wait4(pid, statptr, options, NULL); + #else + /* If wait4 is also not available, try wait3 for SVR3 variants */ + /* Less ideal because can't actually request a specific pid */ + /* For that reason, first check to see if pid is for an */ + /* existing process. */ + int tmp_pid, dummystat;; + if (kill(pid, 0) == -1) { + errno = ECHILD; + return -1; + } + if (statptr == NULL) + statptr = &dummystat; + while (((tmp_pid = wait3(statptr, options, 0)) != pid) && + (tmp_pid != -1) && (tmp_pid != 0) && (pid != -1)) + ; + return tmp_pid; + #endif +} +#endif + /* Under SunOS popen() doesn't record the pid of the child process. When * pclose() is called, instead of calling waitpid() for the correct child, it * calls wait() repeatedly until the right child is reaped. The problem is @@ -376,7 +412,9 @@ my_popen(struct RI *entry) * close on exec, so new children won't see it */ close(pipedes[STDOUT_FILENO]); +#ifdef FD_CLOEXEC fcntl(pipedes[STDIN_FILENO], F_SETFD, FD_CLOEXEC); +#endif stream = fdopen(pipedes[STDIN_FILENO], "r"); @@ -616,6 +654,7 @@ start_gatherer( int pipefd ) } /* close all files but the ones we need */ { int nmax, n1, n2, i; + #ifdef _SC_OPEN_MAX if( (nmax=sysconf( _SC_OPEN_MAX )) < 0 ) { #ifdef _POSIX_OPEN_MAX nmax = _POSIX_OPEN_MAX; @@ -623,6 +662,9 @@ start_gatherer( int pipefd ) nmax = 20; /* assume a reasonable value */ #endif } + #else + nmax = 20; /* assume a reasonable value */ + #endif n1 = fileno( stderr ); n2 = dbgfp? fileno( dbgfp ) : -1; for(i=0; i < nmax; i++ ) { @@ -718,6 +760,10 @@ read_a_msg( int fd, GATHER_MSG *msg ) } +/**************** + * Using a level of 0 should never block and better add nothing + * to the pool. So this is just a dummy for this gatherer. + */ static int gather_random( void (*add)(const void*, size_t, int), int requester, size_t length, int level ) @@ -727,6 +773,9 @@ gather_random( void (*add)(const void*, size_t, int), int requester, GATHER_MSG msg; size_t n; + if( !level ) + return 0; + if( !gatherer_pid ) { /* make sure we are not setuid */ if( getuid() != geteuid() ) diff --git a/cipher/rndw32.c b/cipher/rndw32.c index c1045851f..25830693e 100644 --- a/cipher/rndw32.c +++ b/cipher/rndw32.c @@ -1,5 +1,6 @@ -/* rndw32.c - interface to the Winseed DLL - * Copyright (C) 1999 Free Software Foundation, Inc. +/* rndw32.c - W32 entropy gatherer + * Copyright (C) 1999, 2000 Free Software Foundation, Inc. + * Copyright Peter Gutmann, Matt Thomlinson and Blake Coverett 1996-1999 * * This file is part of GnuPG. * @@ -16,6 +17,46 @@ * 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 + * + ************************************************************************* + * The code here is based on code from Cryptlib 3.0 beta by Peter Gutmann. + * Source file misc/rndwin32.c "Win32 Randomness-Gathering Code" with this + * copyright notice: + * + * This module is part of the cryptlib continuously seeded pseudorandom + * number generator. For usage conditions, see lib_rand.c + * + * [Here is the notice from lib_rand.c, which is now called dev_sys.c] + * + * This module and the misc/rnd*.c modules represent the cryptlib + * continuously seeded pseudorandom number generator (CSPRNG) as described in + * my 1998 Usenix Security Symposium paper "The generation of random numbers + * for cryptographic purposes". + * + * The CSPRNG code is copyright Peter Gutmann (and various others) 1996, + * 1997, 1998, 1999, all rights reserved. Redistribution of the CSPRNG + * modules and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice + * and this permission notice in its entirety. + * + * 2. Redistributions in binary form must reproduce the copyright notice in + * the documentation and/or other materials provided with the distribution. + * + * 3. A copy of any bugfixes or enhancements made must be provided to the + * author, to allow them to be added to the + * baseline version of the code. + * + * ALTERNATIVELY, the code may be distributed under the terms of the GNU + * General Public License, version 2 or any later version published by the + * Free Software Foundation, in which case the provisions of the GNU GPL are + * required INSTEAD OF the above restrictions. + * + * Although not required under the terms of the GPL, it would still be nice if + * you could make any changes available to the author to allow a consistent + * code base to be maintained + ************************************************************************* */ #include @@ -27,10 +68,16 @@ #include + #include "types.h" #include "g10lib.h" #include "dynload.h" +/* We do not use the netropy DLL anymore because a standalone program is + * easier to maintain and */ +/*#define USE_ENTROPY_DLL*/ + + #ifdef IS_MODULE #define _(a) (a) @@ -39,6 +86,10 @@ #endif +static int debug_me; + +#ifdef USE_ENTROPY_DLL + #define WIN32_SLOW_SEEDER 0 #define WIN32_FAST_SEEDER 1 @@ -53,6 +104,17 @@ #define PCP_DLL_FUNC 8 #define PCP_UNKNOWN_SEEDER_TYPE 9 + +/**************** + * We sometimes get a SEEDER_TOO_SMALL error, in which case we increment + * the internal buffer by SEEDER_INC_CHUNK until we reach MAX_SEEDER_SIZE + * MAX_SEEDER_SIZE is used as an arbitrary limit to protect against + * bugs in Winseed. + */ +#define MAX_SEEDER_SIZE 500000 +#define SEEDER_INC_CHUNK 50000 + + typedef void *WIN32_SEEDER; static WIN32_SEEDER (WINAPI *create_instance)( byte type, unsigned int *reason); @@ -68,8 +130,6 @@ static WIN32_SEEDER slow_seeder, fast_seeder; static byte *entropy_buffer; static size_t entropy_buffer_size; -static char *entropy_dll; - /**************** * Load and initialize the winseed DLL * NOTE: winseed is not part of the GnuPG distribution. It should be available @@ -80,11 +140,17 @@ static char *entropy_dll; static void load_and_init_winseed( void ) { - int hInstance; + HANDLE hInstance; void *addr; unsigned int reason = 0; unsigned int n1, n2; - const char *dllname = entropy_dll? entropy_dll : "c:/gnupg/entropy.dll"; + const char *dllname; + + dllname = read_w32_registry_string( "HKEY_LOCAL_MACHINE", + "Software\\GNU\\GnuPG", + "EntropyDLL" ); + if( !dllname ) + dllname = "c:/gnupg/entropy.dll"; hInstance = LoadLibrary( dllname ); if( !hInstance ) @@ -119,15 +185,14 @@ load_and_init_winseed( void ) g10_log_fatal("error creating winseed fast seeder: rc=%u\n", reason ); goto failure; } - g10_log_info("slow and fast seeders created.\n"); n1 = get_internal_seed_size( slow_seeder ); - g10_log_info("slow buffer size=%u\n", n1); + /*g10_log_info("slow buffer size=%u\n", n1);*/ n2 = get_internal_seed_size( fast_seeder ); - g10_log_info("fast buffer size=%u\n", n2); + /*g10_log_info("fast buffer size=%u\n", n2);*/ entropy_buffer_size = n1 > n2? n1: n2; - entropy_buffer = g10_xmalloc( entropy_buffer_size ); - g10_log_info("using a buffer of size=%u\n", entropy_buffer_size ); + entropy_buffer = m_alloc( entropy_buffer_size ); + /*g10_log_info("using a buffer of size=%u\n", entropy_buffer_size );*/ return; @@ -150,13 +215,16 @@ gather_random( void (*add)(const void*, size_t, int), int requester, unsigned int result; unsigned int nbytes; + if( !level ) + return 0; + if( !slow_seeder ) load_and_init_winseed(); /* Our estimation on how much entropy we should use is very vague. * Winseed delivers some amount of entropy on each slow poll and * we add it to our random pool. Depending on the required quality - * level we adjust the requested length so that for higer quality + * level we adjust the requested length so that for higher quality * we make sure to add more entropy to our pool. However, as we don't * like to waste any entropy collected by winseed, we always add * at least everything we got from winseed. @@ -169,17 +237,35 @@ gather_random( void (*add)(const void*, size_t, int), int requester, for(;;) { nbytes = entropy_buffer_size; result = get_seed( slow_seeder, entropy_buffer, &nbytes); + if( result == PCP_SEEDER_TOO_SMALL ) { + unsigned int n1 = get_internal_seed_size( slow_seeder ); + + if( n1 > MAX_SEEDER_SIZE ) { + g10_log_fatal("rndw32: internal seeder problem (size=%u)\n", + n1); + return -1; /* actually never reached */ + } + n1 += SEEDER_INC_CHUNK; + set_internal_seed_size( slow_seeder, n1 ); + if( n1 > entropy_buffer_size ) { + entropy_buffer_size = n1; + entropy_buffer = m_realloc( entropy_buffer, + entropy_buffer_size ); + } + continue; + } + + if( result ) { g10_log_fatal("rndw32: get_seed(slow) failed: rc=%u\n", result); return -1; /* actually never reached */ } - g10_log_info("rndw32: slow poll level %d, need %u, got %u\n", - level, (unsigned int)length, (unsigned int)nbytes ); + /*g10_log_info("rndw32: slow poll level %d, need %u, got %u\n", + level, (unsigned int)length, (unsigned int)nbytes );*/ (*add)( entropy_buffer, nbytes, requester ); if( length <= nbytes ) return 0; /* okay */ length -= nbytes; - g10_log_info("rndw32: need more\n"); } } @@ -206,6 +292,619 @@ gather_random_fast( void (*add)(const void*, size_t, int), int requester ) return 0; } +#else /* !USE_ENTROPY_DLL */ +/* This is the new code which does not require the entropy.dll */ + +/* + * Definitions which are missing from the current GNU Windows32Api + */ + +#define TH32CS_SNAPHEAPLIST 1 +#define TH32CS_SNAPPROCESS 2 +#define TH32CS_SNAPTHREAD 4 +#define TH32CS_SNAPMODULE 8 +#define TH32CS_SNAPALL (1|2|4|8) +#define TH32CS_INHERIT 0x80000000 + +#define IOCTL_DISK_PERFORMANCE 0x00070020 +#define VER_PLATFORM_WIN32_WINDOWS 1 + + +typedef struct { + DWORD dwSize; + DWORD th32ProcessID; + DWORD th32HeapID; + DWORD dwFlags; +} HEAPLIST32; + +typedef struct { + DWORD dwSize; + HANDLE hHandle; + DWORD dwAddress; + DWORD dwBlockSize; + DWORD dwFlags; + DWORD dwLockCount; + DWORD dwResvd; + DWORD th32ProcessID; + DWORD th32HeapID; +} HEAPENTRY32; + +typedef struct { + DWORD dwSize; + DWORD cntUsage; + DWORD th32ProcessID; + DWORD th32DefaultHeapID; + DWORD th32ModuleID; + DWORD cntThreads; + DWORD th32ParentProcessID; + LONG pcPriClassBase; + DWORD dwFlags; + char szExeFile[260]; +} PROCESSENTRY32; + +typedef struct { + DWORD dwSize; + DWORD cntUsage; + DWORD th32ThreadID; + DWORD th32OwnerProcessID; + LONG tpBasePri; + LONG tpDeltaPri; + DWORD dwFlags; +} THREADENTRY32; + +typedef struct { + DWORD dwSize; + DWORD th32ModuleID; + DWORD th32ProcessID; + DWORD GlblcntUsage; + DWORD ProccntUsage; + BYTE *modBaseAddr; + DWORD modBaseSize; + HMODULE hModule; + char szModule[256]; + char szExePath[260]; +} MODULEENTRY32; + + + +/* Type definitions for function pointers to call Toolhelp32 functions + * used with the windows95 gatherer */ +typedef BOOL (WINAPI * MODULEWALK) (HANDLE hSnapshot, MODULEENTRY32 *lpme); +typedef BOOL (WINAPI * THREADWALK) (HANDLE hSnapshot, THREADENTRY32 *lpte); +typedef BOOL (WINAPI * PROCESSWALK) (HANDLE hSnapshot, PROCESSENTRY32 *lppe); +typedef BOOL (WINAPI * HEAPLISTWALK) (HANDLE hSnapshot, HEAPLIST32 *lphl); +typedef BOOL (WINAPI * HEAPFIRST) (HEAPENTRY32 *lphe, DWORD th32ProcessID, + DWORD th32HeapID); +typedef BOOL (WINAPI * HEAPNEXT) (HEAPENTRY32 *lphe); +typedef HANDLE (WINAPI * CREATESNAPSHOT) (DWORD dwFlags, DWORD th32ProcessID); + +/* Type definitions for function pointers to call NetAPI32 functions */ +typedef DWORD (WINAPI * NETSTATISTICSGET) (LPWSTR szServer, LPWSTR szService, + DWORD dwLevel, DWORD dwOptions, + LPBYTE * lpBuffer); +typedef DWORD (WINAPI * NETAPIBUFFERSIZE) (LPVOID lpBuffer, LPDWORD cbBuffer); +typedef DWORD (WINAPI * NETAPIBUFFERFREE) (LPVOID lpBuffer); + + +/* When we query the performance counters, we allocate an initial buffer and + * then reallocate it as required until RegQueryValueEx() stops returning + * ERROR_MORE_DATA. The following values define the initial buffer size and + * step size by which the buffer is increased + */ +#define PERFORMANCE_BUFFER_SIZE 65536 /* Start at 64K */ +#define PERFORMANCE_BUFFER_STEP 16384 /* Step by 16K */ + + +static void +slow_gatherer_windows95( void (*add)(const void*, size_t, int), int requester ) +{ + static CREATESNAPSHOT pCreateToolhelp32Snapshot = NULL; + static MODULEWALK pModule32First = NULL; + static MODULEWALK pModule32Next = NULL; + static PROCESSWALK pProcess32First = NULL; + static PROCESSWALK pProcess32Next = NULL; + static THREADWALK pThread32First = NULL; + static THREADWALK pThread32Next = NULL; + static HEAPLISTWALK pHeap32ListFirst = NULL; + static HEAPLISTWALK pHeap32ListNext = NULL; + static HEAPFIRST pHeap32First = NULL; + static HEAPNEXT pHeap32Next = NULL; + HANDLE hSnapshot; + + + /* initialize the Toolhelp32 function pointers */ + if ( !pCreateToolhelp32Snapshot ) { + HANDLE hKernel; + + if ( debug_me ) + log_debug ("rndw32#slow_gatherer_95: init toolkit\n" ); + + /* Obtain the module handle of the kernel to retrieve the addresses + * of the Toolhelp32 functions */ + if ( ( !(hKernel = GetModuleHandle ("KERNEL32.DLL"))) ) { + g10_log_fatal ( "rndw32: can't get module handle\n" ); + } + + /* Now get pointers to the functions */ + pCreateToolhelp32Snapshot = (CREATESNAPSHOT) GetProcAddress (hKernel, + "CreateToolhelp32Snapshot"); + pModule32First = (MODULEWALK) GetProcAddress (hKernel, "Module32First"); + pModule32Next = (MODULEWALK) GetProcAddress (hKernel, "Module32Next"); + pProcess32First = (PROCESSWALK) GetProcAddress (hKernel, + "Process32First"); + pProcess32Next = (PROCESSWALK) GetProcAddress (hKernel, + "Process32Next"); + pThread32First = (THREADWALK) GetProcAddress (hKernel, "Thread32First"); + pThread32Next = (THREADWALK) GetProcAddress (hKernel, "Thread32Next"); + pHeap32ListFirst = (HEAPLISTWALK) GetProcAddress (hKernel, + "Heap32ListFirst"); + pHeap32ListNext = (HEAPLISTWALK) GetProcAddress (hKernel, + "Heap32ListNext"); + pHeap32First = (HEAPFIRST) GetProcAddress (hKernel, "Heap32First"); + pHeap32Next = (HEAPNEXT) GetProcAddress (hKernel, "Heap32Next"); + + if ( !pCreateToolhelp32Snapshot + || !pModule32First || !pModule32Next + || !pProcess32First || !pProcess32Next + || !pThread32First || !pThread32Next + || !pHeap32ListFirst || !pHeap32ListNext + || !pHeap32First || !pHeap32Next ) { + g10_log_fatal ( "rndw32: failed to get a toolhep function\n" ); + } + } + + /* Take a snapshot of everything we can get to which is currently + * in the system */ + if ( !(hSnapshot = pCreateToolhelp32Snapshot (TH32CS_SNAPALL, 0)) ) { + g10_log_fatal ( "rndw32: failed to take a toolhelp snapshot\n" ); + } + + /* Walk through the local heap */ + { HEAPLIST32 hl32; + hl32.dwSize = sizeof (HEAPLIST32); + if (pHeap32ListFirst (hSnapshot, &hl32)) { + if ( debug_me ) + log_debug ("rndw32#slow_gatherer_95: walk heap\n" ); + do { + HEAPENTRY32 he32; + + /* First add the information from the basic Heaplist32 struct */ + (*add) ( &hl32, sizeof (hl32), requester ); + + /* Now walk through the heap blocks getting information + * on each of them */ + he32.dwSize = sizeof (HEAPENTRY32); + if (pHeap32First (&he32, hl32.th32ProcessID, hl32.th32HeapID)){ + do { + (*add) ( &he32, sizeof (he32), requester ); + } while (pHeap32Next (&he32)); + } + } while (pHeap32ListNext (hSnapshot, &hl32)); + } + } + + + /* Walk through all processes */ + { PROCESSENTRY32 pe32; + pe32.dwSize = sizeof (PROCESSENTRY32); + if (pProcess32First (hSnapshot, &pe32)) { + if ( debug_me ) + log_debug ("rndw32#slow_gatherer_95: walk processes\n" ); + do { + (*add) ( &pe32, sizeof (pe32), requester ); + } while (pProcess32Next (hSnapshot, &pe32)); + } + } + + /* Walk through all threads */ + { THREADENTRY32 te32; + te32.dwSize = sizeof (THREADENTRY32); + if (pThread32First (hSnapshot, &te32)) { + if ( debug_me ) + log_debug ("rndw32#slow_gatherer_95: walk threads\n" ); + do { + (*add) ( &te32, sizeof (te32), requester ); + } while (pThread32Next (hSnapshot, &te32)); + } + } + + /* Walk through all modules associated with the process */ + { MODULEENTRY32 me32; + me32.dwSize = sizeof (MODULEENTRY32); + if (pModule32First (hSnapshot, &me32)) { + if ( debug_me ) + log_debug ("rndw32#slow_gatherer_95: walk modules\n" ); + do { + (*add) ( &me32, sizeof (me32), requester ); + } while (pModule32Next (hSnapshot, &me32)); + } + } + + CloseHandle (hSnapshot); +} + + + +static void +slow_gatherer_windowsNT( void (*add)(const void*, size_t, int), int requester ) +{ + static int is_initialized = 0; + static NETSTATISTICSGET pNetStatisticsGet = NULL; + static NETAPIBUFFERSIZE pNetApiBufferSize = NULL; + static NETAPIBUFFERFREE pNetApiBufferFree = NULL; + static int is_workstation = 1; + + static int cbPerfData = PERFORMANCE_BUFFER_SIZE; + PERF_DATA_BLOCK *pPerfData; + HANDLE hDevice, hNetAPI32 = NULL; + DWORD dwSize, status; + int nDrive; + + if ( !is_initialized ) { + HKEY hKey; + + if ( debug_me ) + log_debug ("rndw32#slow_gatherer_nt: init toolkit\n" ); + /* Find out whether this is an NT server or workstation if necessary */ + if (RegOpenKeyEx (HKEY_LOCAL_MACHINE, + "SYSTEM\\CurrentControlSet\\Control\\ProductOptions", + 0, KEY_READ, &hKey) == ERROR_SUCCESS) { + BYTE szValue[32]; + dwSize = sizeof (szValue); + + if ( debug_me ) + log_debug ("rndw32#slow_gatherer_nt: check product options\n" ); + status = RegQueryValueEx (hKey, "ProductType", 0, NULL, + szValue, &dwSize); + if (status == ERROR_SUCCESS && stricmp (szValue, "WinNT")) { + /* Note: There are (at least) three cases for ProductType: + * WinNT = NT Workstation, ServerNT = NT Server, LanmanNT = + * NT Server acting as a Domain Controller */ + is_workstation = 0; + if ( debug_me ) + log_debug ("rndw32: this is a NT server\n"); + } + RegCloseKey (hKey); + } + + /* Initialize the NetAPI32 function pointers if necessary */ + if ( (hNetAPI32 = LoadLibrary ("NETAPI32.DLL")) ) { + if ( debug_me ) + log_debug ("rndw32#slow_gatherer_nt: netapi32 loaded\n" ); + pNetStatisticsGet = (NETSTATISTICSGET) GetProcAddress (hNetAPI32, + "NetStatisticsGet"); + pNetApiBufferSize = (NETAPIBUFFERSIZE) GetProcAddress (hNetAPI32, + "NetApiBufferSize"); + pNetApiBufferFree = (NETAPIBUFFERFREE) GetProcAddress (hNetAPI32, + "NetApiBufferFree"); + + if ( !pNetStatisticsGet + || !pNetApiBufferSize || !pNetApiBufferFree ) { + FreeLibrary (hNetAPI32); + hNetAPI32 = NULL; + g10_log_debug ("rndw32: No NETAPI found\n" ); + } + } + + is_initialized = 1; + } + + /* Get network statistics. Note: Both NT Workstation and NT Server by + * default will be running both the workstation and server services. The + * heuristic below is probably useful though on the assumption that the + * majority of the network traffic will be via the appropriate service. + * In any case the network statistics return almost no randomness */ + { LPBYTE lpBuffer; + if (hNetAPI32 && !pNetStatisticsGet (NULL, + is_workstation ? L"LanmanWorkstation" : + L"LanmanServer", 0, 0, &lpBuffer) ) { + if ( debug_me ) + log_debug ("rndw32#slow_gatherer_nt: get netstats\n" ); + pNetApiBufferSize (lpBuffer, &dwSize); + (*add) ( lpBuffer, dwSize,requester ); + pNetApiBufferFree (lpBuffer); + } + } + + /* Get disk I/O statistics for all the hard drives */ + for (nDrive = 0;; nDrive++) { + DISK_PERFORMANCE diskPerformance; + char szDevice[50]; + + /* Check whether we can access this device */ + sprintf (szDevice, "\\\\.\\PhysicalDrive%d", nDrive); + hDevice = CreateFile (szDevice, 0, FILE_SHARE_READ | FILE_SHARE_WRITE, + NULL, OPEN_EXISTING, 0, NULL); + if (hDevice == INVALID_HANDLE_VALUE) + break; + + /* Note: This only works if you have turned on the disk performance + * counters with 'diskperf -y'. These counters are off by default */ + if (DeviceIoControl (hDevice, IOCTL_DISK_PERFORMANCE, NULL, 0, + &diskPerformance, sizeof (DISK_PERFORMANCE), + &dwSize, NULL)) + { + if ( debug_me ) + log_debug ("rndw32#slow_gatherer_nt: iostats drive %d\n", + nDrive ); + (*add) ( &diskPerformance, dwSize, requester ); + } + else { + log_info ("NOTE: you should run 'diskperf -y' " + "to enable the disk statistics\n"); + } + CloseHandle (hDevice); + } + + #if 0 /* we don't need this in GnuPG */ + /* Wait for any async keyset driver binding to complete. You may be + * wondering what this call is doing here... the reason it's necessary is + * because RegQueryValueEx() will hang indefinitely if the async driver + * bind is in progress. The problem occurs in the dynamic loading and + * linking of driver DLL's, which work as follows: + * + * hDriver = LoadLibrary( DRIVERNAME ); + * pFunction1 = ( TYPE_FUNC1 ) GetProcAddress( hDriver, NAME_FUNC1 ); + * pFunction2 = ( TYPE_FUNC1 ) GetProcAddress( hDriver, NAME_FUNC2 ); + * + * If RegQueryValueEx() is called while the GetProcAddress()'s are in + * progress, it will hang indefinitely. This is probably due to some + * synchronisation problem in the NT kernel where the GetProcAddress() + * calls affect something like a module reference count or function + * reference count while RegQueryValueEx() is trying to take a snapshot + * of the statistics, which include the reference counts. Because of + * this, we have to wait until any async driver bind has completed + * before we can call RegQueryValueEx() */ + waitSemaphore (SEMAPHORE_DRIVERBIND); + #endif + + /* Get information from the system performance counters. This can take + * a few seconds to do. In some environments the call to + * RegQueryValueEx() can produce an access violation at some random time + * in the future, adding a short delay after the following code block + * makes the problem go away. This problem is extremely difficult to + * reproduce, I haven't been able to get it to occur despite running it + * on a number of machines. The best explanation for the problem is that + * on the machine where it did occur, it was caused by an external driver + * or other program which adds its own values under the + * HKEY_PERFORMANCE_DATA key. The NT kernel calls the required external + * modules to map in the data, if there's a synchronisation problem the + * external module would write its data at an inappropriate moment, + * causing the access violation. A low-level memory checker indicated + * that ExpandEnvironmentStrings() in KERNEL32.DLL, called an + * interminable number of calls down inside RegQueryValueEx(), was + * overwriting memory (it wrote twice the allocated size of a buffer to a + * buffer allocated by the NT kernel). This may be what's causing the + * problem, but since it's in the kernel there isn't much which can be + * done. + * + * In addition to these problems the code in RegQueryValueEx() which + * estimates the amount of memory required to return the performance + * counter information isn't very accurate, since it always returns a + * worst-case estimate which is usually nowhere near the actual amount + * required. For example it may report that 128K of memory is required, + * but only return 64K of data */ + { pPerfData = m_alloc (cbPerfData); + for (;;) { + dwSize = cbPerfData; + if ( debug_me ) + log_debug ("rndw32#slow_gatherer_nt: get perf data\n" ); + status = RegQueryValueEx (HKEY_PERFORMANCE_DATA, "Global", NULL, + NULL, (LPBYTE) pPerfData, &dwSize); + if (status == ERROR_SUCCESS) { + if (!memcmp (pPerfData->Signature, L"PERF", 8)) { + (*add) ( pPerfData, dwSize, requester ); + } + else + g10_log_debug ( "rndw32: no PERF signature\n"); + break; + } + else if (status == ERROR_MORE_DATA) { + cbPerfData += PERFORMANCE_BUFFER_STEP; + pPerfData = m_realloc (pPerfData, cbPerfData); + } + else { + g10_log_debug ( "rndw32: get performance data problem\n"); + break; + } + } + m_free (pPerfData); + } + /* Although this isn't documented in the Win32 API docs, it's necessary + to explicitly close the HKEY_PERFORMANCE_DATA key after use (it's + implicitly opened on the first call to RegQueryValueEx()). If this + isn't done then any system components which provide performance data + can't be removed or changed while the handle remains active */ + RegCloseKey (HKEY_PERFORMANCE_DATA); +} + + +static int +gather_random( void (*add)(const void*, size_t, int), int requester, + size_t length, int level ) +{ + static int is_initialized; + static int is_windows95; + + + if( !level ) + return 0; + /* We don't differentiate between level 1 and 2 here because + * there is no nternal entropy pool as a scary resource. It may + * all work slower, but because our entropy source will never + * block but deliver some not easy to measure entropy, we assume level 2 + */ + + + if ( !is_initialized ) { + OSVERSIONINFO osvi = { sizeof( osvi ) }; + DWORD platform; + + GetVersionEx( &osvi ); + platform = osvi.dwPlatformId; + is_windows95 = platform == VER_PLATFORM_WIN32_WINDOWS; + + if ( platform == VER_PLATFORM_WIN32s ) { + g10_log_fatal("can't run on a W32s platform\n" ); + } + is_initialized = 1; + if ( debug_me ) + log_debug ("rndw32#gather_random: platform=%d\n", (int)platform ); + } + + + if ( debug_me ) + log_debug ("rndw32#gather_random: req=%d len=%u lvl=%d\n", + requester, (unsigned int)length, level ); + + if (is_windows95 ) { + slow_gatherer_windows95( add, requester ); + } + else { + slow_gatherer_windowsNT( add, requester ); + } + + return 0; +} + + + +static int +gather_random_fast( void (*add)(const void*, size_t, int), int requester ) +{ + static int addedFixedItems = 0; + + if ( debug_me ) + log_debug ("rndw32#gather_random_fast: req=%d\n", requester ); + + /* Get various basic pieces of system information: Handle of active + * window, handle of window with mouse capture, handle of clipboard owner + * handle of start of clpboard viewer list, pseudohandle of current + * process, current process ID, pseudohandle of current thread, current + * thread ID, handle of desktop window, handle of window with keyboard + * focus, whether system queue has any events, cursor position for last + * message, 1 ms time for last message, handle of window with clipboard + * open, handle of process heap, handle of procs window station, types of + * events in input queue, and milliseconds since Windows was started */ + { byte buffer[20*sizeof(ulong)], *bufptr; + bufptr = buffer; + #define ADD(f) do { ulong along = (ulong)(f); \ + memcpy (bufptr, &along, sizeof (along) ); \ + bufptr += sizeof (along); } while (0) + ADD ( GetActiveWindow ()); + ADD ( GetCapture ()); + ADD ( GetClipboardOwner ()); + ADD ( GetClipboardViewer ()); + ADD ( GetCurrentProcess ()); + ADD ( GetCurrentProcessId ()); + ADD ( GetCurrentThread ()); + ADD ( GetCurrentThreadId ()); + ADD ( GetDesktopWindow ()); + ADD ( GetFocus ()); + ADD ( GetInputState ()); + ADD ( GetMessagePos ()); + ADD ( GetMessageTime ()); + ADD ( GetOpenClipboardWindow ()); + ADD ( GetProcessHeap ()); + ADD ( GetProcessWindowStation ()); + ADD ( GetQueueStatus (QS_ALLEVENTS)); + ADD ( GetTickCount ()); + + assert ( bufptr-buffer < sizeof (buffer) ); + (*add) ( buffer, bufptr-buffer, requester ); + #undef ADD + } + + /* Get multiword system information: Current caret position, current + * mouse cursor position */ + { POINT point; + GetCaretPos (&point); + (*add) ( &point, sizeof (point), requester ); + GetCursorPos (&point); + (*add) ( &point, sizeof (point), requester ); + } + + /* Get percent of memory in use, bytes of physical memory, bytes of free + * physical memory, bytes in paging file, free bytes in paging file, user + * bytes of address space, and free user bytes */ + { MEMORYSTATUS memoryStatus; + memoryStatus.dwLength = sizeof (MEMORYSTATUS); + GlobalMemoryStatus (&memoryStatus); + (*add) ( &memoryStatus, sizeof (memoryStatus), requester ); + } + + /* Get thread and process creation time, exit time, time in kernel mode, + and time in user mode in 100ns intervals */ + { HANDLE handle; + FILETIME creationTime, exitTime, kernelTime, userTime; + DWORD minimumWorkingSetSize, maximumWorkingSetSize; + + handle = GetCurrentThread (); + GetThreadTimes (handle, &creationTime, &exitTime, + &kernelTime, &userTime); + (*add) ( &creationTime, sizeof (creationTime), requester ); + (*add) ( &exitTime, sizeof (exitTime), requester ); + (*add) ( &kernelTime, sizeof (kernelTime), requester ); + (*add) ( &userTime, sizeof (userTime), requester ); + + handle = GetCurrentProcess (); + GetProcessTimes (handle, &creationTime, &exitTime, + &kernelTime, &userTime); + (*add) ( &creationTime, sizeof (creationTime), requester ); + (*add) ( &exitTime, sizeof (exitTime), requester ); + (*add) ( &kernelTime, sizeof (kernelTime), requester ); + (*add) ( &userTime, sizeof (userTime), requester ); + + /* Get the minimum and maximum working set size for the current process */ + GetProcessWorkingSetSize (handle, &minimumWorkingSetSize, + &maximumWorkingSetSize); + (*add) ( &minimumWorkingSetSize, + sizeof (&minimumWorkingSetSize), requester ); + (*add) ( &maximumWorkingSetSize, + sizeof (&maximumWorkingSetSize), requester ); + } + + + /* The following are fixed for the lifetime of the process so we only + * add them once */ + if (!addedFixedItems) { + STARTUPINFO startupInfo; + + /* Get name of desktop, console window title, new window position and + * size, window flags, and handles for stdin, stdout, and stderr */ + startupInfo.cb = sizeof (STARTUPINFO); + GetStartupInfo (&startupInfo); + (*add) ( &startupInfo, sizeof (STARTUPINFO), requester ); + addedFixedItems = 1; + } + + /* The performance of QPC varies depending on the architecture it's + * running on and on the OS. Under NT it reads the CPU's 64-bit timestamp + * counter (at least on a Pentium and newer '486's, it hasn't been tested + * on anything without a TSC), under Win95 it reads the 1.193180 MHz PIC + * timer. There are vague mumblings in the docs that it may fail if the + * appropriate hardware isn't available (possibly '386's or MIPS machines + * running NT), but who's going to run NT on a '386? */ + { LARGE_INTEGER performanceCount; + if (QueryPerformanceCounter (&performanceCount)) { + if ( debug_me ) + log_debug ("rndw32#gather_random_fast: perf data\n"); + (*add) (&performanceCount, sizeof (&performanceCount), requester); + } + else { /* Millisecond accuracy at best... */ + DWORD aword = GetTickCount (); + (*add) (&aword, sizeof (aword), requester ); + } + } + + return 0; +} + + + + + +#endif /* !USE_ENTROPY_DLL */ #ifndef IS_MODULE @@ -232,6 +931,8 @@ gnupgext_enum_func( int what, int *sequence, int *class, int *vers ) void *ret; int i = *sequence; + debug_me = !!getenv("DEBUG_RNDW32"); + do { if ( i >= DIM(func_table) || i < 0 ) { return NULL; @@ -246,14 +947,6 @@ gnupgext_enum_func( int what, int *sequence, int *class, int *vers ) return ret; } -#ifdef USE_STATIC_RNDW32 -void -rndw32_set_dll_name( const char *name ) -{ - entropy_dll = m_strdup( name ); -} -#endif - #ifndef IS_MODULE void rndw32_constructor(void) diff --git a/cipher/rsa.c b/cipher/rsa.c new file mode 100644 index 000000000..5d852cd88 --- /dev/null +++ b/cipher/rsa.c @@ -0,0 +1,375 @@ +/* rsa.c - RSA function + * Copyright (C) 1997, 1998, 1999 by Werner Koch (dd9jn) + * Copyright (C) 2000 Free Software Foundation, Inc. + *********************************************************************** + * ATTENTION: This code should not be used in the United States + * before the U.S. Patent #4,405,829 expires on September 20, 2000! + *********************************************************************** + * + * 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 + */ + +#include +#include +#include +#include +#include "util.h" +#include "mpi.h" +#include "cipher.h" +#include "rsa.h" + + +typedef struct { + MPI n; /* modulus */ + MPI e; /* exponent */ +} RSA_public_key; + + +typedef struct { + MPI n; /* public modulus */ + MPI e; /* public exponent */ + MPI d; /* exponent */ + MPI p; /* prime p. */ + MPI q; /* prime q. */ + MPI u; /* inverse of p mod q. */ +} RSA_secret_key; + + +static void test_keys( RSA_secret_key *sk, unsigned nbits ); +static void generate( RSA_secret_key *sk, unsigned nbits ); +static int check_secret_key( RSA_secret_key *sk ); +static void public(MPI output, MPI input, RSA_public_key *skey ); +static void secret(MPI output, MPI input, RSA_secret_key *skey ); + + +static void +test_keys( RSA_secret_key *sk, unsigned nbits ) +{ + RSA_public_key pk; + MPI test = mpi_alloc( (nbits+BITS_PER_MPI_LIMB-1)/BITS_PER_MPI_LIMB ); + MPI out1 = mpi_alloc( (nbits+BITS_PER_MPI_LIMB-1)/BITS_PER_MPI_LIMB ); + MPI out2 = mpi_alloc( (nbits+BITS_PER_MPI_LIMB-1)/BITS_PER_MPI_LIMB ); + + pk.n = sk->n; + pk.e = sk->e; + { char *p = get_random_bits( nbits, 0, 0 ); + mpi_set_buffer( test, p, (nbits+7)/8, 0 ); + m_free(p); + } + + public( out1, test, &pk ); + secret( out2, out1, sk ); + if( mpi_cmp( test, out2 ) ) + log_fatal("RSA operation: public, secret failed\n"); + secret( out1, test, sk ); + public( out2, out1, &pk ); + if( mpi_cmp( test, out2 ) ) + log_fatal("RSA operation: secret, public failed\n"); + mpi_free( test ); + mpi_free( out1 ); + mpi_free( out2 ); +} + +/**************** + * Generate a key pair with a key of size NBITS + * Returns: 2 structures filles with all needed values + */ +static void +generate( RSA_secret_key *sk, unsigned nbits ) +{ + MPI p, q; /* the two primes */ + MPI d; /* the private key */ + MPI u; + MPI t1, t2; + MPI n; /* the public key */ + MPI e; /* the exponent */ + MPI phi; /* helper: (p-a)(q-1) */ + MPI g; + MPI f; + + /* select two (very secret) primes */ + p = generate_secret_prime( nbits / 2 ); + q = generate_secret_prime( nbits / 2 ); + if( mpi_cmp( p, q ) > 0 ) /* p shall be smaller than q (for calc of u)*/ + mpi_swap(p,q); + /* calculate Euler totient: phi = (p-1)(q-1) */ + t1 = mpi_alloc_secure( mpi_get_nlimbs(p) ); + t2 = mpi_alloc_secure( mpi_get_nlimbs(p) ); + phi = mpi_alloc_secure( (nbits+BITS_PER_MPI_LIMB-1)/BITS_PER_MPI_LIMB ); + g = mpi_alloc_secure( (nbits+BITS_PER_MPI_LIMB-1)/BITS_PER_MPI_LIMB ); + f = mpi_alloc_secure( (nbits+BITS_PER_MPI_LIMB-1)/BITS_PER_MPI_LIMB ); + mpi_sub_ui( t1, p, 1 ); + mpi_sub_ui( t2, q, 1 ); + mpi_mul( phi, t1, t2 ); + mpi_gcd(g, t1, t2); + mpi_fdiv_q(f, phi, g); + /* multiply them to make the private key */ + n = mpi_alloc( (nbits+BITS_PER_MPI_LIMB-1)/BITS_PER_MPI_LIMB ); + mpi_mul( n, p, q ); + /* find a public exponent */ + e = mpi_alloc( (6+BITS_PER_MPI_LIMB-1)/BITS_PER_MPI_LIMB ); + mpi_set_ui( e, 17); /* start with 17 */ + while( !mpi_gcd(t1, e, phi) ) /* (while gcd is not 1) */ + mpi_add_ui( e, e, 2); + /* calculate the secret key d = e^1 mod phi */ + d = mpi_alloc( (nbits+BITS_PER_MPI_LIMB-1)/BITS_PER_MPI_LIMB ); + mpi_invm(d, e, f ); + /* calculate the inverse of p and q (used for chinese remainder theorem)*/ + u = mpi_alloc( (nbits+BITS_PER_MPI_LIMB-1)/BITS_PER_MPI_LIMB ); + mpi_invm(u, p, q ); + + if( DBG_CIPHER ) { + log_mpidump(" p= ", p ); + log_mpidump(" q= ", q ); + log_mpidump("phi= ", phi ); + log_mpidump(" g= ", g ); + log_mpidump(" f= ", f ); + log_mpidump(" n= ", n ); + log_mpidump(" e= ", e ); + log_mpidump(" d= ", d ); + log_mpidump(" u= ", u ); + } + + mpi_free(t1); + mpi_free(t2); + mpi_free(phi); + mpi_free(f); + mpi_free(g); + + sk->n = n; + sk->e = e; + sk->p = p; + sk->q = q; + sk->d = d; + sk->u = u; + + /* now we can test our keys (this should never fail!) */ + test_keys( sk, nbits - 64 ); +} + + +/**************** + * Test wether the secret key is valid. + * Returns: true if this is a valid key. + */ +static int +check_secret_key( RSA_secret_key *sk ) +{ + int rc; + MPI temp = mpi_alloc( mpi_get_nlimbs(sk->p)*2 ); + + mpi_mul(temp, sk->p, sk->q ); + rc = mpi_cmp( temp, sk->n ); + mpi_free(temp); + return !rc; +} + + + +/**************** + * Public key operation. Encrypt INPUT with PKEY and put result into OUTPUT. + * + * c = m^e mod n + * + * Where c is OUTPUT, m is INPUT and e,n are elements of PKEY. + */ +static void +public(MPI output, MPI input, RSA_public_key *pkey ) +{ + if( output == input ) { /* powm doesn't like output and input the same */ + MPI x = mpi_alloc( mpi_get_nlimbs(input)*2 ); + mpi_powm( x, input, pkey->e, pkey->n ); + mpi_set(output, x); + mpi_free(x); + } + else + mpi_powm( output, input, pkey->e, pkey->n ); +} + +/**************** + * Secret key operation. Encrypt INPUT with SKEY and put result into OUTPUT. + * + * m = c^d mod n + * + * Where m is OUTPUT, c is INPUT and d,n are elements of PKEY. + * + * FIXME: We should better use the Chinese Remainder Theorem + */ +static void +secret(MPI output, MPI input, RSA_secret_key *skey ) +{ + mpi_powm( output, input, skey->d, skey->n ); +} + + +/********************************************* + ************** interface ****************** + *********************************************/ + +int +rsa_generate( int algo, unsigned nbits, MPI *skey, MPI **retfactors ) +{ + RSA_secret_key sk; + + if( !is_RSA(algo) ) + return G10ERR_PUBKEY_ALGO; + + generate( &sk, nbits ); + skey[0] = sk.n; + skey[1] = sk.e; + skey[2] = sk.d; + skey[3] = sk.p; + skey[4] = sk.q; + skey[5] = sk.u; + /* make an empty list of factors */ + *retfactors = m_alloc_clear( 1 * sizeof **retfactors ); + return 0; +} + + +int +rsa_check_secret_key( int algo, MPI *skey ) +{ + RSA_secret_key sk; + + if( !is_RSA(algo) ) + return G10ERR_PUBKEY_ALGO; + + sk.n = skey[0]; + sk.e = skey[1]; + sk.d = skey[2]; + sk.p = skey[3]; + sk.q = skey[4]; + sk.u = skey[5]; + if( !check_secret_key( &sk ) ) + return G10ERR_BAD_SECKEY; + + return 0; +} + + + +int +rsa_encrypt( int algo, MPI *resarr, MPI data, MPI *pkey ) +{ + RSA_public_key pk; + + if( algo != 1 && algo != 2 ) + return G10ERR_PUBKEY_ALGO; + + pk.n = pkey[0]; + pk.e = pkey[1]; + resarr[0] = mpi_alloc( mpi_get_nlimbs( pk.n ) ); + public( resarr[0], data, &pk ); + return 0; +} + +int +rsa_decrypt( int algo, MPI *result, MPI *data, MPI *skey ) +{ + RSA_secret_key sk; + + if( algo != 1 && algo != 2 ) + return G10ERR_PUBKEY_ALGO; + + sk.n = skey[0]; + sk.e = skey[1]; + sk.d = skey[2]; + sk.p = skey[3]; + sk.q = skey[4]; + sk.u = skey[5]; + *result = mpi_alloc_secure( mpi_get_nlimbs( sk.n ) ); + secret( *result, data[0], &sk ); + return 0; +} + +int +rsa_sign( int algo, MPI *resarr, MPI data, MPI *skey ) +{ + RSA_secret_key sk; + + if( algo != 1 && algo != 3 ) + return G10ERR_PUBKEY_ALGO; + + sk.n = skey[0]; + sk.e = skey[1]; + sk.d = skey[2]; + sk.p = skey[3]; + sk.q = skey[4]; + sk.u = skey[5]; + resarr[0] = mpi_alloc( mpi_get_nlimbs( sk.n ) ); + secret( resarr[0], data, &sk ); + + return 0; +} + +int +rsa_verify( int algo, MPI hash, MPI *data, MPI *pkey, + int (*cmp)(void *opaque, MPI tmp), void *opaquev ) +{ + RSA_public_key pk; + MPI result; + int rc; + + if( algo != 1 && algo != 3 ) + return G10ERR_PUBKEY_ALGO; + pk.n = pkey[0]; + pk.e = pkey[1]; + result = mpi_alloc( (160+BITS_PER_MPI_LIMB-1)/BITS_PER_MPI_LIMB); + public( result, data[0], &pk ); + /*rc = (*cmp)( opaquev, result );*/ + rc = mpi_cmp( result, hash )? G10ERR_BAD_SIGN:0; + mpi_free(result); + + return rc; +} + + +unsigned int +rsa_get_nbits( int algo, MPI *pkey ) +{ + if( !is_RSA(algo) ) + return 0; + return mpi_get_nbits( pkey[0] ); +} + + +/**************** + * Return some information about the algorithm. We need algo here to + * distinguish different flavors of the algorithm. + * Returns: A pointer to string describing the algorithm or NULL if + * the ALGO is invalid. + * Usage: Bit 0 set : allows signing + * 1 set : allows encryption + */ +const char * +rsa_get_info( int algo, + int *npkey, int *nskey, int *nenc, int *nsig, int *usage ) +{ + *npkey = 2; + *nskey = 6; + *nenc = 1; + *nsig = 1; + + switch( algo ) { + case 1: *usage = PUBKEY_USAGE_SIG | PUBKEY_USAGE_ENC; return "RSA"; + case 2: *usage = PUBKEY_USAGE_ENC; return "RSA-E"; + case 3: *usage = PUBKEY_USAGE_SIG; return "RSA-S"; + default:*usage = 0; return NULL; + } +} + diff --git a/cipher/rsa.h b/cipher/rsa.h new file mode 100644 index 000000000..8b60ecbb0 --- /dev/null +++ b/cipher/rsa.h @@ -0,0 +1,36 @@ +/* rsa.h + * Copyright (C) 1997,1998 by Werner Koch (dd9jn) + * Copyright (C) 2000 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 + */ +#ifndef G10_RSA_H +#define G10_RSA_H + +int rsa_generate( int algo, unsigned nbits, MPI *skey, MPI **retfactors ); +int rsa_check_secret_key( int algo, MPI *skey ); +int rsa_encrypt( int algo, MPI *resarr, MPI data, MPI *pkey ); +int rsa_decrypt( int algo, MPI *result, MPI *data, MPI *skey ); +int rsa_sign( int algo, MPI *resarr, MPI data, MPI *skey ); +int rsa_verify( int algo, MPI hash, MPI *data, MPI *pkey, + int (*cmp)(void *, MPI), void *opaquev ); +unsigned rsa_get_nbits( int algo, MPI *pkey ); +const char *rsa_get_info( int algo, int *npkey, int *nskey, + int *nenc, int *nsig, int *use ); + + +#endif /*G10_RSA_H*/ diff --git a/cipher/sha1.c b/cipher/sha1.c index f231e37b0..aa3ac092f 100644 --- a/cipher/sha1.c +++ b/cipher/sha1.c @@ -337,10 +337,10 @@ sha1_get_info( int algo, size_t *contextsize, *r_asnoid = asn; *r_asnlen = DIM(asn); *r_mdlen = 20; - *r_init = (void (*)(void *))sha1_init; - *r_write = (void (*)(void *, byte*, size_t))sha1_write; - *r_final = (void (*)(void *))sha1_final; - *r_read = (byte *(*)(void *))sha1_read; + *(void (**)(SHA1_CONTEXT *))r_init = sha1_init; + *(void (**)(SHA1_CONTEXT *, byte*, size_t))r_write = sha1_write; + *(void (**)(SHA1_CONTEXT *))r_final = sha1_final; + *(byte *(**)(SHA1_CONTEXT *))r_read = sha1_read; return "SHA1"; } diff --git a/cipher/tiger.c b/cipher/tiger.c index 0e42160a5..abe449ab9 100644 --- a/cipher/tiger.c +++ b/cipher/tiger.c @@ -899,10 +899,10 @@ tiger_get_info( int algo, size_t *contextsize, *r_asnoid = asn; *r_asnlen = DIM(asn); *r_mdlen = 24; - *r_init = (void (*)(void *))tiger_init; - *r_write = (void (*)(void *, byte*, size_t))tiger_write; - *r_final = (void (*)(void *))tiger_final; - *r_read = (byte *(*)(void *))tiger_read; + *(void (**)(TIGER_CONTEXT *))r_init = tiger_init; + *(void (**)(TIGER_CONTEXT *, byte*, size_t))r_write = tiger_write; + *(void (**)(TIGER_CONTEXT *))r_final = tiger_final; + *(byte *(**)(TIGER_CONTEXT *))r_read = tiger_read; return "TIGER"; } diff --git a/cipher/twofish.c b/cipher/twofish.c index 1eea4b8e4..5766021cd 100644 --- a/cipher/twofish.c +++ b/cipher/twofish.c @@ -34,10 +34,6 @@ /* Prototype for the self-test function. */ static const char *selftest(void); -/* Macros used by the info function. */ -#define FNCCAST_SETKEY(f) ((int(*)(void*, byte*, unsigned))(f)) -#define FNCCAST_CRYPT(f) ((void(*)(void*, byte*, byte*))(f)) - /* Structure for an expanded Twofish key. s contains the key-dependent * S-boxes composed with the MDS matrix; w contains the eight "whitening" * subkeys, K[0] through K[7]. k holds the remaining, "round" subkeys. Note @@ -990,16 +986,20 @@ twofish_get_info (int algo, size_t *keylen, *keylen = algo==10? 256 : 128; *blocksize = 16; *contextsize = sizeof (TWOFISH_context); - *r_setkey = FNCCAST_SETKEY (twofish_setkey); - *r_encrypt= FNCCAST_CRYPT (twofish_encrypt); - *r_decrypt= FNCCAST_CRYPT (twofish_decrypt); - if( algo == 10 ) - return "TWOFISH"; - if (algo == 102) /* This algorithm number is assigned for - * experiments, so we can use it */ - return "TWOFISH128"; - return NULL; + *(int (**)(TWOFISH_context*, const byte*, const unsigned))r_setkey + = twofish_setkey; + *(void (**)(const TWOFISH_context*, byte*, const byte*))r_encrypt + = twofish_encrypt; + *(void (**)(const TWOFISH_context*, byte*, const byte*))r_decrypt + = twofish_decrypt; + + if( algo == 10 ) + return "TWOFISH"; + if (algo == 102) /* This algorithm number is assigned for + * experiments, so we can use it */ + return "TWOFISH128"; + return NULL; } diff --git a/configure.in b/configure.in index fddb93d2a..b3e44ec7d 100644 --- a/configure.in +++ b/configure.in @@ -11,19 +11,17 @@ AC_PREREQ(2.13) AC_INIT(g10/gpg.c) AC_CONFIG_AUX_DIR(scripts) AM_CONFIG_HEADER(config.h) +AC_CANONICAL_SYSTEM +AM_INIT_AUTOMAKE(gnupg,`cat $srcdir/VERSION`) - -VERSION=`cat $srcdir/VERSION` -PACKAGE=gnupg -ALL_LINGUAS="de es_ES fr it pl pt_BR pt_PT ru" +ALL_LINGUAS="da de eo es_ES fr id it ja nl pl pt_BR pt_PT ru sv" static_modules="sha1 md5 rmd160" -AC_SUBST(VERSION) -AC_SUBST(PACKAGE) -AC_DEFINE_UNQUOTED(VERSION, "$VERSION") -AC_DEFINE_UNQUOTED(PACKAGE, "$PACKAGE") +static_random_module="" -MODULES_IN_CIPHER=`awk '/# MODULES: / { for(i=3;i<=NF;i++) print $i}' \ - $srcdir/cipher/Makefile.am` +AC_PROG_AWK + +MODULES_IN_CIPHER=`$AWK '/^EXTRA_PROGRAMS/ { for(i=3;i<=NF;i++) print $i}' \ + $srcdir/cipher/Makefile.am` dnl dnl Check for random module options @@ -132,7 +130,6 @@ AM_MAINTAINER_MODE dnl Checks for programs. -AC_CANONICAL_SYSTEM dnl dnl Setup some stuff depending on host/target. dnl @@ -145,8 +142,10 @@ case "${target}" in CC="${target}-gcc" CPP="${target}-gcc -E" RANLIB="${target}-ranlib" + disallowed_modules="rndunix rndlinux rndegd" ;; *) + disallowed_modules="rndw32" ;; esac @@ -177,9 +176,6 @@ AM_PROG_LIBTOOL MPI_OPT_FLAGS="" -if test "$GCC" = yes; then - CFLAGS="$CFLAGS -Wall -Wcast-align -Wshadow -Wstrict-prototypes" -fi try_gettext=yes @@ -213,6 +209,12 @@ case "${target}" in try_gdbm="no" ;; + *-*-freebsd*) + # FreeBSD + CPPFLAGS="$CPPFLAGS -I/usr/local/include" + LDFLAGS="$LDFLAGS -L/usr/local/lib" + ;; + *-*-hpux*) if test -z "$GCC" ; then CFLAGS="$CFLAGS -Ae -D_HPUX_SOURCE" @@ -234,7 +236,7 @@ esac AC_SUBST(MPI_OPT_FLAGS) GNUPG_SYS_SYMBOL_UNDERSCORE GNUPG_CHECK_PIC -GNUPG_CHECK_RDYNAMIC +GNUPG_CHECK_EXPORTDYNAMIC if test "$NO_PIC" = yes; then try_dynload=no fi @@ -272,6 +274,13 @@ case "${target}" in NAME_OF_DEV_URANDOM="/dev/urandom" DYNLINK_MOD_CFLAGS="-shared -rdynamic -fpic -Wl,-Bshareable -Wl,-x" ;; + + *-netbsd*) + NAME_OF_DEV_RANDOM="/dev/random" + NAME_OF_DEV_URANDOM="/dev/urandom" + DYNLINK_MOD_CFLAGS="-shared -rdynamic -fpic -Wl,-Bshareable -Wl,-x" + ;; + *) NAME_OF_DEV_RANDOM="/dev/random" NAME_OF_DEV_URANDOM="/dev/urandom" @@ -327,21 +336,21 @@ if test "$try_dynload" = yes ; then if test "$ac_cv_lib_dl_dlopen" = "yes"; then AC_DEFINE(USE_DYNAMIC_LINKING) AC_DEFINE(HAVE_DL_DLOPEN) - DYNLINK_LDFLAGS="$CFLAGS_RDYNAMIC" + DYNLINK_LDFLAGS="$CFLAGS_EXPORTDYNAMIC" use_gnupg_extensions=yes else - AC_CHECK_LIB(c,dlopen) - if test "$ac_cv_lib_c_dlopen" = "yes"; then + AC_CHECK_FUNCS(dlopen) + if test "$ac_cv_func_dlopen" = "yes"; then AC_DEFINE(USE_DYNAMIC_LINKING) AC_DEFINE(HAVE_DL_DLOPEN) - DYNLINK_LDFLAGS="$CFLAGS_RDYNAMIC" + DYNLINK_LDFLAGS="$CFLAGS_EXPORTDYNAMIC" use_gnupg_extensions=yes else AC_CHECK_LIB(dld,shl_load) if test "$ac_cv_lib_dld_shl_load" = "yes"; then AC_DEFINE(USE_DYNAMIC_LINKING) AC_DEFINE(HAVE_DL_SHL_LOAD) - DYNLINK_LDFLAGS="$CFLAGS_RDYNAMIC" + DYNLINK_LDFLAGS="$CFLAGS_EXPORTDYNAMIC" use_gnupg_extensions=yes dnl ----------------- dnl DLD is not ready for use. So I better disable this test @@ -350,7 +359,7 @@ dnl AC_CHECK_LIB(dld,dld_link) dnl if test "$ac_cv_lib_dld_dld_link" = "yes"; then dnl AC_DEFINE(USE_DYNAMIC_LINKING) dnl AC_DEFINE(HAVE_DLD_DLD_LINK) -dnl DYNLINK_LDFLAGS="$CFLAGS_RDYNAMIC" +dnl DYNLINK_LDFLAGS="$CFLAGS_EXPORTDYNAMIC" dnl use_gnupg_extensions=yes dnl --------------- fi @@ -371,7 +380,7 @@ AC_SUBST(DYNLINK_MOD_CFLAGS) dnl Checks for header files. AC_HEADER_STDC -AC_CHECK_HEADERS(unistd.h langinfo.h) +AC_CHECK_HEADERS(unistd.h langinfo.h termio.h) dnl Checks for typedefs, structures, and compiler characteristics. @@ -393,6 +402,7 @@ GNUPG_CHECK_TYPEDEF(u32, HAVE_U32_TYPEDEF) AC_CHECK_SIZEOF(unsigned short, 2) AC_CHECK_SIZEOF(unsigned int, 4) AC_CHECK_SIZEOF(unsigned long, 4) +AC_CHECK_SIZEOF(unsigned long long, 0) if test "$ac_cv_sizeof_unsigned_short" = "0" \ || test "$ac_cv_sizeof_unsigned_int" = "0" \ @@ -405,8 +415,9 @@ fi dnl Checks for library functions. AC_FUNC_VPRINTF AC_CHECK_FUNCS(strerror stpcpy strlwr stricmp tcgetattr rand strtoul mmap) -AC_CHECK_FUNCS(memmove gettimeofday getrusage gethrtime setrlimit) +AC_CHECK_FUNCS(memmove gettimeofday getrusage gethrtime setrlimit clock_gettime) AC_CHECK_FUNCS(memicmp atexit raise getpagesize strftime nl_langinfo) +AC_CHECK_FUNCS(waitpid wait4 sigaction sigprocmask) GNUPG_CHECK_MLOCK GNUPG_FUNC_MKDIR_TAKES_ONE_ARG @@ -483,29 +494,27 @@ fi dnl dnl Figure out the default linkage mode for cipher modules dnl -dnl (We always need a static rmd160) print_egd_notice=no -static_modules="$static_modules rmd160" if test "$use_static_rnd" = default; then if test "$ac_cv_have_dev_random" = yes; then - static_modules="$static_modules rndlinux" + static_random_module="rndlinux" else case "${target}" in *-*-mingw32) - static_modules="$static_modules rndw32" + static_random_module="rndw32" AC_DEFINE(USE_STATIC_RNDW32) ;; i?86-emx-os2|i?86-*-os2*emx) - static_modules="$static_modules rndos2" + static_random_module="rndos2" ;; m68k-atari-mint) - static_modules="$static_modules rndatari" + static_random_module="rndatari" ;; i?86-*-msdosdjgpp*) - static_modules="$static_modules" + : ;; *) - static_modules="$static_modules rndunix" + static_random_module="rndunix" print_egd_notice=yes ;; esac @@ -514,7 +523,7 @@ else if test "$use_static_rnd" = none; then : else - static_modules="$static_modules rnd$use_static_rnd" + static_random_module="rnd$use_static_rnd" if test "$use_static_rnd" = "unix"; then print_egd_notice=yes fi @@ -547,23 +556,31 @@ dnl dnl Parse the modules list and build the list dnl of static and dymically linked modules dnl +dnl (We always need a static rmd160) +static_modules="$static_modules rmd160 $static_random_module" STATIC_CIPHER_NAMES="" STATIC_CIPHER_OBJS="" DYNAMIC_CIPHER_MODS="" GNUPG_MSG_PRINT([dynamically linked cipher modules:]) for name in $MODULES_IN_CIPHER; do - x="no" - for i in $static_modules; do - if test "$name" = "$i" ; then - x="yes" - fi + x="yes" + for i in $disallowed_modules; do + if test "$name" = "$i" ; then x="no" ; fi done; if test $x = yes; then - STATIC_CIPHER_NAMES="$STATIC_CIPHER_NAMES $name" - STATIC_CIPHER_OBJS="$STATIC_CIPHER_OBJS $name.lo" - else - DYNAMIC_CIPHER_MODS="$DYNAMIC_CIPHER_MODS $name" - GNUPG_MSG_PRINT([$name]) + x="no" + for i in $static_modules; do + if test "$name" = "$i" ; then + x="yes" + fi + done; + if test $x = yes; then + STATIC_CIPHER_NAMES="$STATIC_CIPHER_NAMES $name" + STATIC_CIPHER_OBJS="$STATIC_CIPHER_OBJS $name.o" + else + DYNAMIC_CIPHER_MODS="$DYNAMIC_CIPHER_MODS $name" + GNUPG_MSG_PRINT([$name]) + fi fi done AC_MSG_RESULT() @@ -663,6 +680,9 @@ fi AC_SUBST(ZLIBS) +# Allow users to append something to the version string without +# flagging it as development version. The user version parts is +# considered everything after a dash. changequote(,)dnl tmp_pat='[a-zA-Z]' changequote([,])dnl @@ -678,12 +698,26 @@ AM_CONDITIONAL(CROSS_COMPILING, test x$cross_compiling = xyes) GNUPG_DO_LINK_FILES +GNUPG_CHECK_GNUMAKE + +if test "$GCC" = yes; then + if test "$MAINTAINER_MODE" = "yes"; then + CFLAGS="$CFLAGS -Wall -Wcast-align -Wshadow -Wstrict-prototypes" + else + CFLAGS="$CFLAGS -Wall" + fi +fi + dnl dnl Make the version number in gcrypt/gcrypt.h the same as the one here. dnl (this is easier than to have a .in file just for one substitution) dnl GNUPG_FIX_HDR_VERSION(gcrypt/gcrypt.h, GCRYPT_VERSION) +GCRYPT_LIBS="-L${libdir} -lgcrypt" +GCRYPT_CFLAGS="" +AC_SUBST(GCRYPT_LIBS) +AC_SUBST(GCRYPT_CFLAGS) AC_OUTPUT_COMMANDS([ chmod +x scripts/db2html diff --git a/g10/ChangeLog b/g10/ChangeLog index 64056be69..6d3aa7cc4 100644 --- a/g10/ChangeLog +++ b/g10/ChangeLog @@ -1,231 +1,710 @@ -Thu May 25 18:39:11 CEST 2000 Werner Koch +Fri Jul 14 19:38:23 CEST 2000 Werner Koch - * kbxio.c: New. + Replaced everything with the code from the STABLE-BRANCH-1-0 and + started to backport the changes from the 1.1 development branch + which are dated according to the ChangeLog of the 1.1 from + Sat Sep 18 12:16:08 CEST 1999 to Thu May 25 18:39:11 CEST 2000. + Here are those changes, some of them are duplicates because they + have been done on both branch simultaneously. - * kbxfile.c (print_kbxfile): Add a loop - (do_print_kbxfile): Fixed passing to kbx_dump_blob. + * gpg.c (print_mds): Add arg keys as a kludge to print hmacs + (main): New option --print-hmac. -Fri Mar 24 11:25:45 CET 2000 Werner Koch + * trustdb.c (verify_own_keys): Do not print warning about unprotected + key when in quiet mode. - * gpg.c (print_mds): Add arg keys as a kludge to print hmacs - (main): New option --print-hmac. + * build-paket.c (do_user_id): Save offset where name has been stored. + + * ringedit.c : Add new access method KBXF + + * kbxfile.c: New. + + * kbx.h: New. + * kbxblob.c: Started to work on the keybox stuff. + + * keygen.c (gen_dsa): Modified to work with gcry_pk_genkey. + + * Removed dummy-cipher.h from all files. + + * keygen.c (gen_elg): Modified to work with gcry_pk_genkey. + (key_from_sexp): New. + (factors_from_sexp): New. + + * g10.c : Renamed to ... + * gpg.c : ... this + * Makefile.am: And fixed it here. + + * Changed all "g10_"/"GPG_" prefixes to "gpg_"/"GPG_". + + * misc.c (mpi_read_opaque): Fixed double counting. + + * seckey-cert.c (do_check): Removed buffer and the unmotivated free + on it. + + * pubkey-enc.c (pk_decrypt): New wrapper for the gcry_ function. + * seckey-cert.c (pk_check_secret_key): Likewise. + * encode.c (pk_encrypt): Likewise. + + * parse-packet.c (parse_key): Fixed case of unencrypted secret keys. + + * misc.c (mpi_print): Use gcry_mpi_aprint. + (pubkey_nbits): Kludge to use the gcry_pk_ API. + + * seskey.c (encode_session_key): Replaced mpi_set_buffer by *_scan. + (do_encode_md): Ditto. + (encode_md_value): Ditto. + * seckey-cert.c (protect_secret_key): Ditto. + * comment.c (make_mpi_comment_node): Replaced mpi_get_buffer by _print. + * pubkey-enc.c (get_it): Ditto. + * sig-check.c (do_signature_check): Ditto. + + * keyid.c (do_fingerprint_md): Replaced mpi_get_buffer by gcry_mpi_print. + (v3_keyid): New. + (keyid_from_sk): And use it here. + (keyid_from_pk): Ditto. + (fingerprint_from_sk): Ditto. + (fingerprint_from_pk): Ditto. + + * misc.c (mpi_print): New. + + * misc.c (checksum_mpi): Now uses gcry_mpi_print to get the data. + + * seckey-cert.c (do_check): Replaced mpi_read_from_buffer. + + * armor.c (armor_filter): Made the "Comment:" header translatable. + + * seckey-cert.c: Removed obsolete mpi_*_protect_flag. + * parse-packet.c: Ditto. + + * misc.c (mpi_read): Removed the secure argumet becuase it is + never used. Changed all Callers. + (mpi_read_opaque): New. + (mpi_write_opaque): New. + * parse-packet.c (parse_key): Use the opaque method also for + v3 keys. + * build-packet.c (do_secret_key): Likewise. + + * g10.c (main): Check libgcrypt version. + + * packet.h: replaced inclusion of mpi.h by a plain typeedef of the + gcry_mpi structure and removed all inclusions of "mpi.h" in all + sources. + + * g10.c: Add --delete-secret-key to the help page. + + * g10.c (main): Changed the default homedir to "~/.gnupg-test" so + that we don't mess up with the stable version. + + * misc.c (mpi_write): New. + (mpi_write): New. + + * misc.c (checksum_u16_nobug): Removed. + (checksum_mpi_counted_nbits): Renamed to ... + (checksum_mpi): ... this to superseed the old one. Changed all + callers. This is because we do not emulate the old gpg bug anymore. + * g10.c (oEmuChecksumBug): Removed. + + * g10.c (register_extension): New... + (main): Use it here instead of register_cipher_extesnion. + (strusage): s/strusage/my_strusage/ . Made static. + (main): Use set_strusage(). + + * tdbdump.c (HEXTOBIN): Changed the name of the argument, so that + traditional cpp don't mess up the macros. Suggested by Jos Backus. + + * armor.c (parse_header_line): Stop parsing on a only WS line too. + Suggested by Aric Cyr. + + * misc.c (pull_in_libs): Removed. + + * mainproc.c (list_node): Print the PK algo in the --with-colon mode. + * keylist.c (list_keyblock): Ditto. + + * misc.c (pull_in_libs): Removed pull in of g10c. + + * misc.c (map_gcry_rc): Removed here and chnaged all users. + + * getkey.c: Replaced check_pubkey_algo by openpgp_pk_test_algo. + * import.c (delete_inv_parts): Ditto. + * pkclist.c: Ditto. + * skclist.c: Ditto. + * pubkey-enc.c: Ditto. + + * g10.c (main): Replaced the function to diable PK algos. + + * g10.c (main): Replaced get_random_bits by gcry_random_bytes. + * seskey.c (encode_session_key): Likewise. + (make_session_key): Renamed randomize_buffer to gcry_randomize + and use the GCRY_xxx_RANDOM constants. + * cipher.c (write_header): Ditto. + * passphrase.c (hash_passphrase): Ditto. + * seckey-cert.c (protect_secret_key): Ditto. + + * getkey.c (find_by_name): Replaced rmd160_hash_buffer + by gcry_md_hash_buffer. + * keyedit.c (show_prefs): Ditto. + * keylist.c (list_keyblock): Ditto. + * trustdb.c (print_uid_from_keyblock): Ditto. + (make_uid_records): Ditto. + + * skclist.c (build_sk_list): Removed the test on faked RNGs. + (is_insecure): Removed. + * g10.c (--quick-random): Removed this option. + + * Replaced all PUBKEY_ALGO_xxx by GCRY_PK_xxxx. + + * misc.c (pubkey_algo_npkey): New as a wrapper around the gcry fucntion. + (pubkey_algo_nskey): Ditto. + (pubkey_algo_nsig): Ditto. + (pubkey_algo_nenc): Ditto. + + * Makefile.am (basicdefs.h): Added. + (install-data-local): Removed the handling for historic gpgm. + + * misc.c (openpgp_cipher_test_algo): New. + (openpgp_pk_test_algo): New. + (openpgp_md_test_algo): New. + + * g10.c (build_list): Changed to use the new functions from libgcrypt. + + * ringedit.c (enum_keyblocks): Set .rt to 0 on open. + + * encode.c (encode_simple): Use new CTB when we don't have the + length of the file. This is somewhat strange as the comment above + indicates that this part is actually fixed for PGP 5 - maybe I simply + lost the source line, tsss. + + * sign.c (clearsign_file): Avoid duplicated Entries in the "Hash:" + line. Those headers are now only _not_ printed when there are + only old-style keys _and_ all hashs are MD5. + + (clearsign_file): Use gcry_md_test_algo() and gcry_md_algo_name(). + + * openfile.c (make_outfile_name): Use case-insenstive compare for + DOS systems. Add ".pgp" to the list of know extensions. + (open_outfile): For DOS systems try to replace the suffix instead of + appending it. + + * encr-data.c (decrypt_data): Reset error on a weak key. + + * cipher.c: Replaced the cipher and digest functions by the gcry_ ones. + * seckey-cert.c: Ditto. + * seskey.c: Ditto. + * g10.c (print_mds): Replaced digst functions with the new gcry_ ones. + * keyid.c: Ditto. + * mainproc.c: Ditto. + * passphrase.c: Ditto. + * sig-check.c: Ditto. + * sign.c: Ditto. + + * pkclist.c (do_edit_ownertrust): Made the answer string const. + + * basicdefs.h: New. Move some defs and decl to this header. + + * openfile.c (open_outfile): Fixed the 8dot3 handling. + + * passphrase.c (passphrase_to_dek): Print uid using utf8 func. + * delkey.c (delete_key): Ditto. + * pkclist.c (show_paths,do_edit_ownertrust,do_we_trust): Ditto + (do_we_trust_pre): Ditto. + * trustdb.c (print_user_id,check_uidsigs): Ditto. + * revoke.c (gen_revoke,ask_revoke_sig): Ditto. + + * filter.h: Changed cipher handle types to the the GCRY_xxx ones. + replaces include cipher by system header include gcrypt.h. + * cipher.c: replaced the cipher functions by the gcry_ ones. + Ditto for the md functions. + + * misc.c (map_gcry_rc): New. + +Wed Jun 28 11:54:44 CEST 2000 Werner Koch + + * armor.c (armor_filter): Set sigclass to 0 in case of non-dash-escaped + clearsig. This makes this mode work again. + + * mainproc.c (proc_tree): Fixed handling of one-pass-sig packets in textmode. + Disabled the ugly workaround for PGP 5 - let's see whether thi breaks less + cases. Found by Ted Cabeen. + + * options.h (DBG_HASHING): New. All commented md_start_debug are now + controlled by this debug option. + + * sign.c (print_status_sig_created): New and called from 2 places. + + * keygen.c (gen_rsa): New, but commented. + (ask_algo): Commented support for RSA. + + * seckey-cert.c (protect_secret_key): Started to fix the code for v4 RSA + keys - it is not solved yet. However, we have time until, Sep 20th ;) + +Wed Jun 14 12:27:09 CEST 2000 Werner Koch + + * status.c (init_shm_coprocessing): Changed the sequence of the get,attach + to cope with the changes in newer Linux kernels. This bug has been found + by who also proposed this solution. Hopefully + this does not break gpg on to many systems. + + * cipher.c (write_header): Protect the IV with the MDC too. + * encr-data.c (decrypt_data): Likewise. + +Fri Jun 9 10:09:52 CEST 2000 Werner Koch + + * g10.c: New options --no-auto-key-retrieve + * options.h (auto_key_retrieve): New. + * mainproc.c (check_sig_and_print): Implemented that. + +Wed Jun 7 19:19:09 CEST 2000 Werner Koch + + * sig-check.c (do_check): Use EMULATE_MDENCODE also on v4 paclets. + +Wed Jun 7 17:25:38 CEST 2000 Werner Koch + + * cipher.c (write_header): Use plain CFB mode for MDC encrypted packets. + * encr-data.c (decrypt_data): Ditto. + +Mon Jun 5 23:41:54 CEST 2000 Werner Koch + + * seskey.c (do_encode_md, encode_md_value): Add new arg v3compathack to work + around a bug in old versions. + * sig-check.c (do_check): use the aboved workaround when enabled. + * g10.c: New option --emulate-md-decode-bug + +Mon Jun 5 12:37:43 CEST 2000 Werner Koch + + * build-packet.c (do_mdc): New. + (do_encrypted_mdc): Changed for the new proposal. + * parse-packet.c (parse_mdc): New. + (parse_encrypted): Fixed for the new proposal. + * packet.h (PKT_MDC): New. + * cipher.c (cipher_filter): Build the MDC packet here. + * g10.c (main): Enable --force-mdc. + * encr-data.c (mdc_decode_filter): Fixed for new MDC method + + * options.h(rfc2440): New. + * g10.c (main): Changed the selected values for --openpgp to not include + optional algorithms. + +Thu May 18 11:38:54 CEST 2000 Werner Koch + + * keyedit.c (keyedit_menu): Add a keyword arg to the prompt. + + * status.c, status.h: Added 3 new status tokens. + * status.c (do_get_from_fd): New. + (cpr_enabled,cpr_get,cpr_get_hidden,cpr_kill_prompt, + cpr_get_answer_is_yes,cpr_get_answer_yes_no_quit): Modified to work + with the new function. + * g10.c: Add new option --command-fd. + + * status.c (progress_cb): New. + (set_status_fd): Register progress functions + +Fri May 12 14:01:20 CEST 2000 Werner Koch + + * delkey.c (delete_key): Add 2 new status messages + * status.c, status.h (STATUS_DELETE_PROBLEM): New. + + Fixed years of copyright in all source files. + +Mon May 1 17:08:14 CEST 2000 Werner Koch + + * trustdb.c (propagate_validity): Fixed the bug that only one uid + gets fully trusted even when all are signed by an ultimate key. + +Mon May 1 15:38:04 CEST 2000 Werner Koch + + * getkey.c (key_byname): Always returned a defined context. Fixed + a segv for invalid user id specifications. Reported by Walter Koch. + + * getkey.c (get_user_id): I18ned "no user id" string. By Walter. + + * pkclist.c (do_show_revocation_reason): Typo fixes. + * helptext.c: Ditto. + + * armor.c (armor_filter): Fixed some CRLF issues. By Mike McEwan. + +Fri Apr 14 19:37:08 CEST 2000 Werner Koch + + * pkclist.c (do_show_revocation_reason): New. + (show_revocation_reason): New and called at various places. + + * g10.c (main): Fixed small typo. + + * pkclist.c (do_we_trust): Act on always_trust but not for revoked + keys. Suggested by Chip Salzenberg. + + * g10.c: New option --lock-never. + + * ringedit.c (get_writable_keyblock_file): New. + * keygen.c (do_generate_keypair): Use this instead of the hardwired one. + + * keygen.c (ask_user_id): Check that the email address is in the + correct field. Suggested by Christian Kurz. + +Mon Apr 10 13:34:19 CEST 2000 Werner Koch + + * keyedit.c (show_key_with_all_names): s/sbb/ssb/ + +Tue Mar 28 14:26:58 CEST 2000 Werner Koch * trustdb.c (verify_own_keys): Do not print warning about unprotected key when in quiet mode. -Mon Mar 13 19:22:46 CET 2000 Werner Koch +Wed Mar 22 13:50:24 CET 2000 Werner Koch - * build-paket.c (do_user_id): Save offset where name has been stored. + * mainproc.c (print_userid): Do UTF8 conversion before printing. + * import.c (import_one): Ditto. + (import_secret_one): Ditto. + (delete_inv_parts): Ditto. - * ringedit.c : Add new access method KBXF +Thu Mar 16 16:20:23 CET 2000 Werner Koch - * kbxfile.c: New. + * keylist.c (print_key_data): Handle a NULL pk gracefully. -Mon Feb 21 22:43:01 CET 2000 Werner Koch + * getkey.c (merge_one_pk_and_selfsig): Fixed silly code for + getting the primary keys keyID but kept using the one from the + subkey. + * pubkey-enc.c (get_it): Print a note for expired subkeys. - * kbx.h: New. - * kbxblob.c: Started to work on the keybox stuff. + * getkey.c (has_expired): New. + (subkeys_expiretime): New. + (finish_lookup): Check for expired subkeys needed for encryption. + (merge_keys_and_selfsig): Fixed expiration date merging for subkeys. -Mon Jan 31 16:37:34 CET 2000 Werner Koch + * keylist.c (list_keyblock): Print expiration time for "sub". + (list_one): Add missing merging for public keys. + * mainproc.c (list_node): Ditto. - * keygen.c (gen_dsa): Modified to work with gcry_pk_genkey. +2000-03-14 13:49:38 Werner Koch (wk@habibti.openit.de) - * Removed dummy-cipher.h from all files. + * keygen.c (keyedit_menu): Do not allow to use certain commands + while the secret key is selected. -Thu Jan 27 18:00:44 CET 2000 Werner Koch +2000-03-09 12:53:09 Werner Koch (wk@habibti.openit.de) - * keygen.c (gen_elg): Modified to work with gcry_pk_genkey. - (key_from_sexp): New. - (factors_from_sexp): New. + * keygen.c (ask_expire_interval): Movede parsig to ... + (parse_expire_string): ... this new function. And some new control + commands. + (proc_parameter_file): Add expire date parsing. + (do_generate_keypair): Allow the use of specified output files. - * g10.c : Renamed to ... - * gpg.c : ... this - * Makefile.am: And fixed it here. +2000-03-08 10:38:38 Werner Koch (wk@habibti.openit.de) - * Changed all "g10_"/"GPG_" prefixes to "gpg_"/"GPG_". + * keygen.c (ask_algo): Removed is_v4 return value and the commented + code to create Elg keys in a v3 packet. Removed the rounding + of key sizes here. + (do_create): Likewise removed arg v4_packet. + (gen_elg): Likewise removed arg version. Now rounding keysizes here. + (gen_dsa): Rounding keysize now here. + (release_parameter_list): New + (get_parameter*): New. + (proc_parameter_file): New. + (read_parameter_file): New. + (generate_keypair): Splitted. Now uses read_parameter_file when in + batch mode. Additional argument to specify a parameter file. + (do_generate_keypair): Main bulk of above fucntion and uses the + parameter list. + (do_create): Don't print long notice in batch mode. + * g10.c (main): Allow batched key generation. -Mon Jan 24 22:24:38 CET 2000 Werner Koch +Thu Mar 2 15:37:46 CET 2000 Werner Koch - * misc.c (mpi_read_opaque): Fixed double counting. + * pubkey-enc.c (get_it): Print a note about unknown cipher algos. - * seckey-cert.c (do_check): Removed buffer and the unmotivated free - on it. + * g10.c (opts): Add a note to the help listing about the man page + and removed some options from the help listing. - * pubkey-enc.c (pk_decrypt): New wrapper for the gcry_ function. - * seckey-cert.c (pk_check_secret_key): Likewise. - * encode.c (pk_encrypt): Likewise. + * keyedit.c (print_and_check_one_sig): Use a new function to truncate + the output of the user ID. Suggested by Jan-Benedict Glaw. - * parse-packet.c (parse_key): Fixed case of unencrypted secret keys. +Wed Feb 23 10:07:57 CET 2000 Werner Koch -Mon Jan 24 13:04:28 CET 2000 Werner Koch + * helptext.c: typo fix. - * misc.c (mpi_print): Use gcry_mpi_aprint. - (pubkey_nbits): Kludge to use the gcry_pk_ API. +Thu Feb 17 13:39:32 CET 2000 Werner Koch - * seskey.c (encode_session_key): Replaced mpi_set_buffer by *_scan. - (do_encode_md): Ditto. - (encode_md_value): Ditto. - * seckey-cert.c (protect_secret_key): Ditto. - * comment.c (make_mpi_comment_node): Replaced mpi_get_buffer by _print. - * pubkey-enc.c (get_it): Ditto. - * sig-check.c (do_signature_check): Ditto. + * revoke.c: Removed a bunch of commented code. -Fri Dec 31 12:48:31 CET 1999 Werner Koch + * packet.h (SIGSUBPKT_REVOC_REASON): New. + * build-packet.c (build_sig_subpkt): Support new sub packet. + * parse-packet.c (parse_one_sig_subpkt): Ditto. + (dump_sig_subpkt): Ditto. + * revoke.c (ask_revocation_reason): New. + (release_revocation_reason_info): New. + (revocation_reason_build_cb): New. + (gen_revoke): Ask for reason. + * main.h (struct revocation_reason_info): Add declaration. + * keyedit.c (menu_revsig): Add support for revocation reason. + (menu_revkey): Ditto. + (sign_uid_mk_attrib): Renamed to ... + (sign_mk_attrib): ... this, made static and add support for reasons. - * keyid.c (do_fingerprint_md): Replaced mpi_get_buffer by gcry_mpi_print. - (v3_keyid): New. - (keyid_from_sk): And use it here. - (keyid_from_pk): Ditto. - (fingerprint_from_sk): Ditto. - (fingerprint_from_pk): Ditto. +Tue Feb 15 08:48:13 CET 2000 Werner Koch - * misc.c (mpi_print): New. + * build-packet.c (build_packet): Fixed fixing of old comment packets. - * misc.c (checksum_mpi): Now uses gcry_mpi_print to get the data. + * import.c (import_keys): Fixed importing from stdin when called with + nnames set to zero as it normally happens. - * seckey-cert.c (do_check): Replaced mpi_read_from_buffer. +Mon Feb 14 14:30:20 CET 2000 Werner Koch + + * sig-check.c (check_key_signature2): Add new arg r_expired. + (do_signature_check): New arg to pass it down to ... + (do_check): New arg r-expire which is set when the signature + has expired. + * trustdb.c (check_sig_record): Set SIGF_EXPIRED flag and set + the expiretime to zero so that thi signature will not be checked + anymore. + +Fri Feb 11 17:44:40 CET 2000 Werner Koch + + * g10.c (g10_exit): Update the random seed_file. + (main): Set the random seed file. New option --no-random-seed-file. + +Thu Feb 10 17:39:44 CET 2000 Werner Koch + + * keyedit.c (menu_expire): Fixed segv due to unitialized sub_pk. + By Rémi. + +Thu Feb 10 11:39:41 CET 2000 Werner Koch + + * keylist.c (list_keyblock): Don't print warnings in the middle of + regulat output lines. By Rémi. + + * sig-check.c: Include options.h + +Wed Feb 9 15:33:44 CET 2000 Werner Koch + + * gpg.c: New option --ignore-time-conflict + * sig-check.c (do_check): Implemented this option. + * trustdb.c (check_trust): Ditto. + * sign.c (do_sign): Ditto. + * keygen.c (generate_subkeypair): Ditto. + + * encode.c (encode_simple): use iobuf_cancel after open failure. + Reported by Huy Le. + +Fri Jan 14 18:32:01 CET 2000 Werner Koch + + * packet.h (STRING2KEY): Changed mode from byte to int. + * parse-packet.c (parse_key): Add the special GNU protection stuff + * build-packet.c (so_secret_key): Ditto. + * seckey-cert.c (do_check): Ditto. + * keyedit.c (change_passphrase): Ditto. + * export.c (export_secsubkeys): New. + (do_export_stream): Hack to export the primary key using mode 1001. + * g10.c: New command --export-secret-subkeys + +Thu Jan 13 19:31:58 CET 2000 Werner Koch + + * armor.c (is_armored): Check for 1-pass-sig packets. Reported by + David Hallinan . + (armor_filter): Replaced one LF by the LF macro. Reported by + Wolfgang Redtenbacher. + +Wed Jan 5 11:51:17 CET 2000 Werner Koch + + * g10.c (main): Reset new global flag opt.pgp2_workarounds + when --openpgp is used. + * mainproc.c (proc_plaintext): Do the PGP2,5 workarounds only + when the global flag is set. + (proc_tree): Ditto. + * textfilter.c (copy_clearsig_text): Ditto. + * armor.c (armor_filter): Ditto. + + * g10.c: New option --list-only + * mainproc.c (proc_tree): Don't do it if opt.list_only is active. + (proc_pubkey_enc): Implement option. + + * status.h, status.c ({BEGIN,END}_{EN,DE}CRYPTION): New. + * cipher.c (cipher_filter): New status outputs. + * mainproc.c (proc_encrypted): New status outputs. + +Fri Dec 31 14:08:15 CET 1999 Werner Koch * armor.c (armor_filter): Made the "Comment:" header translatable. -Wed Dec 8 21:58:32 CET 1999 Werner Koch + * hkp.c (hkp_import): Make sure that the program does not return + success when there is a connection problem. Reported by Phillip Jones. - * seckey-cert.c: Removed obsolete mpi_*_protect_flag. - * parse-packet.c: Ditto. +Sun Dec 19 15:22:26 CET 1999 Werner Koch - * misc.c (mpi_read): Removed the secure argumet becuase it is - never used. Changed all Callers. - (mpi_read_opaque): New. - (mpi_write_opaque): New. - * parse-packet.c (parse_key): Use the opaque method also for - v3 keys. - * build-packet.c (do_secret_key): Likewise. + * armor.c (LF): Use this new macro at all places where a line LF + is needed. This way DOSish textfiles should be created when the + input data is also in dos mode. + * sign.c (LF): Ditto. + * textfilter.c (LF): Ditto. + (copy_clearsig_text): Disabled the forcing of CR,LF sequences + for DOS systems. - * g10.c (main): Check libgcrypt version. + * plaintext.c (handle_plaintext): Fixes for line endings on DOS. + and react on a LF in cleartext. + * armor.c (fake_packet): Restore the original line ending after + removing trailing spaces. - * packet.h: replaced inclusion of mpi.h by a plain typeedef of the - gcry_mpi structure and removed all inclusions of "mpi.h" in all - sources. + * signal.c (got_fatal_signal): DOS fix. + +Thu Dec 16 10:07:58 CET 1999 Werner Koch + + * mainproc.c (print_failed_pkenc): Fix for unknown algorithm. + Found by fygrave@epr0.org. + +Thu Dec 9 10:31:05 CET 1999 Werner Koch + + * hkp.c: i18n the strings. + +Sat Dec 4 15:32:20 CET 1999 Werner Koch + + * trustdb.c (verify_key): Shortcut for ultimately trusted keys. + +Sat Dec 4 12:30:28 CET 1999 Werner Koch + + * pkclist.c (build_pk_list): Validate the trust using the namehash + if this one has been set by the key lookup. * g10.c: Add --delete-secret-key to the help page. - * g10.c (main): Changed the default homedir to "~/.gnupg-test" so - that we don't mess up with the stable version. + * openfile.c (copy_options_file): Made static. + (try_make_homedir): New. + * ringedit.c (add_keyblock_resource): Use the try_make_hoemdir logic. + * tdbio.c (tdbio_set_dbname): Likewise. - * misc.c (mpi_write): New. - (mpi_write): New. + * keygen.c (generate_user_id): Use m_alloc_clear() here. We should + better use an allocation function specific to the user_id packet. - * misc.c (checksum_u16_nobug): Removed. - (checksum_mpi_counted_nbits): Renamed to ... - (checksum_mpi): ... this to superseed the old one. Changed all - callers. This is because we do not emulate the old gpg bug anymore. - * g10.c (oEmuChecksumBug): Removed. + * keygen.c (keygen_add_std_prefs): Changed symmetric preferences + to include Blowfish again. This is due to it's better speed compared + to CAST5. -Fri Nov 19 17:15:20 CET 1999 Werner Koch + * g10.c (strusage): Print the home directory. - * g10.c (register_extension): New... - (main): Use it here instead of register_cipher_extesnion. - (strusage): s/strusage/my_strusage/ . Made static. - (main): Use set_strusage(). + * armor.c (armor_filter): Take action on the cancel control msg. + * filter.h (armor_filter_context_t): Add cancel flag. + +Mon Nov 29 21:52:11 CET 1999 Werner Koch + + * g10.c: New option --fast-list-mode .. + * keylist.c (list_keyblock): .. and implemented. + * mainproc.c (list_node): Ditto. + + * import.c (mark_non_selfsigned_uids_valid): Fixed the case that there + is a uid without any packet following. + +Mon Nov 22 11:14:53 CET 1999 Werner Koch + + * mainproc.c (proc_plaintext): Never enable the hash processing + when skip_verify is active. + + * armor.c (parse_header_line): Stop parsing on a WS line too. + Suggested by Aric Cyr. * tdbdump.c (HEXTOBIN): Changed the name of the argument, so that traditional cpp don't mess up the macros. Suggested by Jos Backus. - * armor.c (parse_header_line): Stop parsing on a only WS line too. - Suggested by Aric Cyr. - -Mon Nov 15 21:36:02 CET 1999 Werner Koch - - * misc.c (pull_in_libs): Removed. - -Sat Nov 13 17:44:23 CET 1999 Werner Koch - * mainproc.c (list_node): Print the PK algo in the --with-colon mode. * keylist.c (list_keyblock): Ditto. - * misc.c (pull_in_libs): Removed pull in of g10c. + * signal.c (got_fatal_signal): Found the reason why exit(8) did not + work - it is better to set the disposition back to default before + raising the signal. Print the notice on stderr always. - * misc.c (map_gcry_rc): Removed here and chnaged all users. +Fri Nov 12 20:33:19 CET 1999 Werner Koch - * getkey.c: Replaced check_pubkey_algo by openpgp_pk_test_algo. - * import.c (delete_inv_parts): Ditto. - * pkclist.c: Ditto. - * skclist.c: Ditto. - * pubkey-enc.c: Ditto. + * g10.c (make_username): Swapped the logic. + * keylist.c (public_key_list): Now takes a STRLIST as arg and moved + the creation ot this list to the caller, so that he can copy with + UTF-conversion of user IDs. Changed all callers. + (secret_key_list): Likewise. - * g10.c (main): Replaced the function to diable PK algos. + * getkey.c (get_user_id_string_native): New and ... + * encode.c (write_pubkey_enc_from_list): ... use it here. - * g10.c (main): Replaced get_random_bits by gcry_random_bytes. - * seskey.c (encode_session_key): Likewise. - (make_session_key): Renamed randomize_buffer to gcry_randomize - and use the GCRY_xxx_RANDOM constants. - * cipher.c (write_header): Ditto. - * passphrase.c (hash_passphrase): Ditto. - * seckey-cert.c (protect_secret_key): Ditto. + * pubring.asc: Updated. - * getkey.c (find_by_name): Replaced rmd160_hash_buffer - by gcry_md_hash_buffer. + * packet.h (PKT_PHOTO_ID): New. + * parse-packet.c (parse_photo_id): New. + * build-packet.c (do_user_id: Handle photo IDs. + (build_packet): Change CTB for photo IDs + * free-packet.c (free_user_id): Release memory used for photo IDs + * sig-check.c (hash_uid_node): Handle photo IDs too. + * trustdb.c (print_uid_from_keyblock): Hash photo ID. + (make_uid_records): Ditto. + * getkey.c (find_by_name): Ditto. * keyedit.c (show_prefs): Ditto. * keylist.c (list_keyblock): Ditto. - * trustdb.c (print_uid_from_keyblock): Ditto. - (make_uid_records): Ditto. - * skclist.c (build_sk_list): Removed the test on faked RNGs. - (is_insecure): Removed. - * g10.c (--quick-random): Removed this option. +Thu Oct 28 16:08:20 CEST 1999 Werner Koch - * Replaced all PUBKEY_ALGO_xxx by GCRY_PK_xxxx. + * keygen.c (ask_expire_interval): Print a warning for systems + with a signed 32 time_t if the exiration time is beyoind 2038. - * misc.c (pubkey_algo_npkey): New as a wrapper around the gcry fucntion. - (pubkey_algo_nskey): Ditto. - (pubkey_algo_nsig): Ditto. - (pubkey_algo_nenc): Ditto. +Fri Oct 8 20:40:50 CEST 1999 Werner Koch -Tue Oct 26 20:03:44 CEST 1999 Werner Koch + * ringedit.c (enum_keyblocks): The last fix way really stupid; + reverted and set rt to Unknown. - * Makefile.am (basicdefs.h): Added. - (install-data-local): Removed the handling for historic gpgm. +Fri Oct 8 20:32:01 CEST 1999 Werner Koch -Tue Oct 26 14:10:21 CEST 1999 Werner Koch + * ringedit.c (enum_keyblocks): Zero the entire kbpos out on open. - * misc.c (openpgp_cipher_test_algo): New. - (openpgp_pk_test_algo): New. - (openpgp_md_test_algo): New. + * g10.c (oEntropyDLL): Removed option. + (main): Made the warning on development versions more verbose. - * g10.c (build_list): Changed to use the new functions from libgcrypt. + * g10.c (oHonorHttpProxy): New option. + * hkp.c (hkp_ask_import,hkp_export): Implement this option. + * options.skel: Enable this option for new installations - * ringedit.c (enum_keyblocks): Set .rt to 0 on open. +Mon Oct 4 21:23:04 CEST 1999 Werner Koch + + * import.c (import_keys): Changed calling interface, adjusted caller. + (import): Moved printing of stats out ... + (print_stats): New. ... to here. + (import_keys_stream): Call stats print here. + (import_keys): Print stats as totals for all files. + + * tdbio.h (DIRF_NEWKEYS): New + * tdbio.c (tdbio_dump_record): Print the new flag. + * trustdb.c (check_trust_record): New arg sigs_only. Adapted all + callers. + (do_update_trust_record): Removed recheck arg and add a new sigs_only + do we can later improve on the performance. Changed all callers too. + (check_trustdb): Evalutate the new flag and add a status output. + Do a check when the dir record has not been checked. + (build_cert_tree): Evaluate the new flag. + (check_trust): Ditto. Do a trust_record check, when the dir record + is not marked as checked. + (mark_fresh_keys): New. + (clear_lid_table): New. + (sync_trustdb): New. + * import.c (import_keys): Call sync_trustdb() after processing. + (import_keys_stream): Ditto. + * tdbdump.c (import_ownertrust): Ditto. + + * import.c (import_revoke_cert): Notify the trust DB. + (do_update_trust_record): Use |= to set the REVOKED bit and not &=; + shame on me for this bad copy+paste introduced bug. + (do_we_trust): Add trustmask to allow revoked key override to work. + Chnaged are to allow return of a mofified trustlevel. Adapted the + one caller. + + * g10.c: New options --emulate-3des-s2k-bug + * passphrase.c (hash_passphrase): Implemented above. + + * mainproc.c (proc_tree): Check for standalone signatures. + (do_check_sig): Print a notice for a standalone revocation + (check_sig_and_print): Do not print an error for unchecked standalone + revocations. + +Tue Sep 28 20:54:37 CEST 1999 Werner Koch * encode.c (encode_simple): Use new CTB when we don't have the length of the file. This is somewhat strange as the comment above indicates that this part is actually fixed for PGP 5 - maybe I simply lost the source line, tsss. - * sign.c (clearsign_file): Avoid duplicated Entries in the "Hash:" - line. Those headers are now only _not_ printed when there are - only old-style keys _and_ all hashs are MD5. + * armor.c (armor_filter): Set a flag if no OpenPGP data has been found. + * verify.c (verify_signatures): Add an error helptext. - (clearsign_file): Use gcry_md_test_algo() and gcry_md_algo_name(). - - * openfile.c (make_outfile_name): Use case-insenstive compare for - DOS systems. Add ".pgp" to the list of know extensions. - (open_outfile): For DOS systems try to replace the suffix instead of - appending it. - - * encr-data.c (decrypt_data): Reset error on a weak key. - - * cipher.c: Replaced the cipher and digest functions by the gcry_ ones. - * seckey-cert.c: Ditto. - * seskey.c: Ditto. - * g10.c (print_mds): Replaced digst functions with the new gcry_ ones. - * keyid.c: Ditto. - * mainproc.c: Ditto. - * passphrase.c: Ditto. - * sig-check.c: Ditto. - * sign.c: Ditto. - - * pkclist.c (do_edit_ownertrust): Made the answer string const. - - * basicdefs.h: New. Move some defs and decl to this header. +Thu Sep 23 19:24:30 CEST 1999 Werner Koch * openfile.c (open_outfile): Fixed the 8dot3 handling. @@ -236,18 +715,34 @@ Tue Oct 26 14:10:21 CEST 1999 Werner Koch * trustdb.c (print_user_id,check_uidsigs): Ditto. * revoke.c (gen_revoke,ask_revoke_sig): Ditto. -Sat Sep 18 12:16:08 CEST 1999 Werner Koch +Thu Sep 23 09:52:58 CEST 1999 Werner Koch - * filter.h: Changed cipher handle types to the the GCRY_xxx ones. - replaces include cipher by system header include gcrypt.h. - * cipher.c: replaced the cipher functions by the gcry_ ones. - Ditto for the md functions. + * verify.c (print_file_status): New. + (verify_one_file): Moved status print to th new fnc. Add error status. + * status.c, status.h (STATUS_FILE_ERROR): New - * misc.c (map_gcry_rc): New. +Wed Sep 22 10:14:17 CEST 1999 Werner Koch + + * openfile.c (make_outfile_name): Use case-insenstive compare for + DOS systems. Add ".pgp" to the list of know extensions. + (open_outfile): For DOS systems try to replace the suffiy instead of + appending it. + + * status.c, status.h: Add STATUS_FILE_{START,DONE}. + * verify.c (verify_one_file): Emit these new stati. + + * sign.c (clearsign_file): Avoid duplicated Entries in the "Hash:" + line. Those headers are now only _not_ printed when there are + only old-style keys _and_ all hashs are MD5. + +Mon Sep 20 12:24:41 CEST 1999 Werner Koch + + + * verify.c (verify_files, ferify_one_file): New. + * g10.c: New command --verify-files Fri Sep 17 12:56:42 CEST 1999 Werner Koch - * g10.c: Add UK spelling as alias for armor options ;-) * import.c (append_uid): Fixed a SEGV when there is no selfsig and @@ -256,16 +751,13 @@ Fri Sep 17 12:56:42 CEST 1999 Werner Koch Wed Sep 15 16:22:17 CEST 1999 Werner Koch - * g10.c: New option --entropy-dll-name Mon Sep 13 10:51:29 CEST 1999 Werner Koch - * signal.c (got_fatal_signal): Print message using write(2) and only for development versions. - Mon Sep 6 19:59:08 CEST 1999 Werner Koch * tdbio.c (tdbio_set_dbname): Use mkdir macro diff --git a/g10/Makefile.am b/g10/Makefile.am index 55d0ed222..6b95ac3a5 100644 --- a/g10/Makefile.am +++ b/g10/Makefile.am @@ -6,10 +6,11 @@ OMIT_DEPENDENCIES = zlib.h zconf.h LDFLAGS = -static @LDFLAGS@ @DYNLINK_LDFLAGS@ # we need to add libutil.la a second time because we have to resolve # gpg_log_ in some libjnlib modules. - very ugly - should be removed soon. -needed_libs = ../util/libutil.la ../gcrypt/libgcrypt.la ../jnlib/libjnlib.la ../util/libutil.la +needed_libs = ../util/libutil.la ../gcrypt/libgcrypt.la \ + ../jnlib/libjnlib.la ../util/libutil.la #noinst_PROGRAMS = gpgd -bin_PROGRAMS = gpg kbxutil +bin_PROGRAMS = gpg kbxutil common_source = \ build-packet.c \ diff --git a/g10/armor.c b/g10/armor.c index 0661d76ef..819c951dc 100644 --- a/g10/armor.c +++ b/g10/armor.c @@ -1,5 +1,5 @@ /* armor.c - Armor flter - * Copyright (C) 1998, 1999 Free Software Foundation, Inc. + * Copyright (C) 1998, 1999, 2000 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -37,6 +37,11 @@ #include "status.h" #include "i18n.h" +#ifdef HAVE_DOSISH_SYSTEM + #define LF "\r\n" +#else + #define LF "\n" +#endif #define MAX_LINELEN 20000 @@ -162,6 +167,7 @@ is_armored( const byte *buf ) switch( pkttype ) { case PKT_MARKER: case PKT_SYMKEY_ENC: + case PKT_ONEPASS_SIG: case PKT_PUBLIC_KEY: case PKT_SECRET_KEY: case PKT_PUBKEY_ENC: @@ -485,13 +491,26 @@ fake_packet( armor_filter_context_t *afx, IOBUF a, if( !maxlen ) afx->truncated++; if( !afx->not_dash_escaped ) { + int crlf; + p = afx->buffer; + n = afx->buffer_len; + crlf = n > 1 && p[n-2] == '\r' && p[n-1]=='\n'; + /* PGP2 does not treat a tab as white space character */ - afx->buffer_len = - trim_trailing_chars( afx->buffer, afx->buffer_len, + afx->buffer_len = trim_trailing_chars( p, n, afx->pgp2mode ? " \r\n" : " \t\r\n"); /* the buffer is always allocated with enough space to append - * a CR, LF, Nul */ - afx->buffer[afx->buffer_len++] = '\r'; + * the removed [CR], LF and a Nul + * The reason for this complicated procedure is to keep at least + * the original tupe of lineending - handling of the removed + * trailing spaces seems to be impossible in our method + * of faking a packet; either we have to use a temporary file + * or calculate the hash here in this module and somehow find + * a way to send the hash down the processing line (well, a special + * faked packet could do the job). + */ + if( crlf ) + afx->buffer[afx->buffer_len++] = '\r'; afx->buffer[afx->buffer_len++] = '\n'; afx->buffer[afx->buffer_len] = 0; } @@ -819,7 +838,8 @@ armor_filter( void *opaque, int control, hashes &= 1|2|4|8; if( !hashes ) { hashes |= 4; /* default to MD 5 */ - afx->pgp2mode = 1; + if( opt.pgp2_workarounds ) + afx->pgp2mode = 1; } n=0; do { @@ -827,7 +847,7 @@ armor_filter( void *opaque, int control, buf[n++] = 0x90; /* old format, type 4, 1 length byte */ buf[n++] = 13; /* length */ buf[n++] = 3; /* version */ - buf[n++] = 0x01; /* sigclass 0x01 (canonical text mode)*/ + buf[n++] = afx->not_dash_escaped? 0:1; /* sigclass */ if( hashes & 1 ) { hashes &= ~1; buf[n++] = GCRY_MD_RMD160; @@ -874,7 +894,7 @@ armor_filter( void *opaque, int control, #endif *ret_len = n; } - else if( control == IOBUFCTRL_FLUSH ) { + else if( control == IOBUFCTRL_FLUSH && !afx->cancel ) { if( !afx->status ) { /* write the header line */ const char *s; @@ -882,10 +902,10 @@ armor_filter( void *opaque, int control, log_bug("afx->what=%d", afx->what); iobuf_writestr(a, "-----"); iobuf_writestr(a, head_strings[afx->what] ); - iobuf_writestr(a, "-----\n"); + iobuf_writestr(a, "-----" LF ); if( !opt.no_version ) iobuf_writestr(a, "Version: GnuPG v" VERSION " (" - PRINTABLE_OS_NAME ")\n"); + PRINTABLE_OS_NAME ")" LF ); /* write the comment string or a default one */ s = opt.comment_string ? opt.comment_string @@ -902,16 +922,17 @@ armor_filter( void *opaque, int control, else iobuf_put(a, *s ); } - iobuf_put(a, '\n' ); + iobuf_writestr(a, LF ); } if( afx->hdrlines ) iobuf_writestr(a, afx->hdrlines); - iobuf_put(a, '\n'); + iobuf_writestr(a, LF ); afx->status++; afx->idx = 0; afx->idx2 = 0; afx->crc = CRCINIT; + } crc = afx->crc; idx = afx->idx; @@ -936,7 +957,7 @@ armor_filter( void *opaque, int control, c = bintoasc[radbuf[2]&077]; iobuf_put(a, c); if( ++idx2 >= (64/4) ) { /* pgp doesn't like 72 here */ - iobuf_put(a, '\n'); + iobuf_writestr(a, LF ); idx2=0; } } @@ -951,8 +972,13 @@ armor_filter( void *opaque, int control, if( !is_initialized ) initialize(); } + else if( control == IOBUFCTRL_CANCEL ) { + afx->cancel = 1; + } else if( control == IOBUFCTRL_FREE ) { - if( afx->status ) { /* pad, write cecksum, and bottom line */ + if( afx->cancel ) + ; + else if( afx->status ) { /* pad, write cecksum, and bottom line */ crc = afx->crc; idx = afx->idx; idx2 = afx->idx2; @@ -975,13 +1001,13 @@ armor_filter( void *opaque, int control, iobuf_put(a, '='); } if( ++idx2 >= (64/4) ) { /* pgp doesn't like 72 here */ - iobuf_put(a, '\n'); + iobuf_writestr(a, LF ); idx2=0; } } /* may need a linefeed */ if( idx2 ) - iobuf_put(a, '\n'); + iobuf_writestr(a, LF ); /* write the CRC */ iobuf_put(a, '='); radbuf[0] = crc >>16; @@ -995,16 +1021,17 @@ armor_filter( void *opaque, int control, iobuf_put(a, c); c = bintoasc[radbuf[2]&077]; iobuf_put(a, c); - iobuf_put(a, '\n'); + iobuf_writestr(a, LF ); /* and the the trailer */ if( afx->what >= DIM(tail_strings) ) log_bug("afx->what=%d", afx->what); iobuf_writestr(a, "-----"); iobuf_writestr(a, tail_strings[afx->what] ); - iobuf_writestr(a, "-----\n"); + iobuf_writestr(a, "-----" LF ); } else if( !afx->any_data && !afx->inp_bypass ) { log_error(_("no valid OpenPGP data found.\n")); + afx->no_openpgp_data = 1; write_status_text( STATUS_NODATA, "1" ); } if( afx->truncated ) diff --git a/g10/build-packet.c b/g10/build-packet.c index 84912ac05..878158917 100644 --- a/g10/build-packet.c +++ b/g10/build-packet.c @@ -1,5 +1,5 @@ /* build-packet.c - assemble packets and write them - * Copyright (C) 1998 Free Software Foundation, Inc. + * Copyright (C) 1998, 1999, 2000 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -24,11 +24,11 @@ #include #include +#include #include "packet.h" #include "errors.h" #include "iobuf.h" #include "util.h" -#include #include "options.h" #include "main.h" @@ -43,6 +43,7 @@ static u32 calc_plaintext( PKT_plaintext *pt ); static int do_plaintext( IOBUF out, int ctb, PKT_plaintext *pt ); static int do_encrypted( IOBUF out, int ctb, PKT_encrypted *ed ); static int do_encrypted_mdc( IOBUF out, int ctb, PKT_encrypted *ed ); +static int do_mdc( IOBUF out, PKT_mdc *mdc ); static int do_compressed( IOBUF out, int ctb, PKT_compressed *cd ); static int do_signature( IOBUF out, int ctb, PKT_signature *sig ); static int do_onepass_sig( IOBUF out, int ctb, PKT_onepass_sig *ops ); @@ -66,25 +67,31 @@ int build_packet( IOBUF out, PACKET *pkt ) { int new_ctb=0, rc=0, ctb; + int pkttype; if( DBG_PACKET ) log_debug("build_packet() type=%d\n", pkt->pkttype ); assert( pkt->pkt.generic ); - switch( pkt->pkttype ) { - case PKT_OLD_COMMENT: pkt->pkttype = PKT_COMMENT; break; + switch( (pkttype = pkt->pkttype) ) { + case PKT_OLD_COMMENT: pkttype = pkt->pkttype = PKT_COMMENT; break; case PKT_PLAINTEXT: new_ctb = pkt->pkt.plaintext->new_ctb; break; case PKT_ENCRYPTED: case PKT_ENCRYPTED_MDC: new_ctb = pkt->pkt.encrypted->new_ctb; break; case PKT_COMPRESSED:new_ctb = pkt->pkt.compressed->new_ctb; break; + case PKT_USER_ID: + if( pkt->pkt.user_id->photo ) + pkttype = PKT_PHOTO_ID; + break; default: break; } - if( new_ctb || pkt->pkttype > 15 ) /* new format */ - ctb = 0xc0 | (pkt->pkttype & 0x3f); + if( new_ctb || pkttype > 15 ) /* new format */ + ctb = 0xc0 | (pkttype & 0x3f); else - ctb = 0x80 | ((pkt->pkttype & 15)<<2); - switch( pkt->pkttype ) { + ctb = 0x80 | ((pkttype & 15)<<2); + switch( pkttype ) { + case PKT_PHOTO_ID: case PKT_USER_ID: rc = do_user_id( out, ctb, pkt->pkt.user_id ); break; @@ -114,6 +121,9 @@ build_packet( IOBUF out, PACKET *pkt ) case PKT_ENCRYPTED_MDC: rc = do_encrypted_mdc( out, ctb, pkt->pkt.encrypted ); break; + case PKT_MDC: + rc = do_mdc( out, pkt->pkt.mdc ); + break; case PKT_COMPRESSED: rc = do_compressed( out, ctb, pkt->pkt.compressed ); break; @@ -148,6 +158,7 @@ calc_packet_length( PACKET *pkt ) n = calc_plaintext( pkt->pkt.plaintext ); new_ctb = pkt->pkt.plaintext->new_ctb; break; + case PKT_PHOTO_ID: case PKT_USER_ID: case PKT_COMMENT: case PKT_PUBLIC_KEY: @@ -172,11 +183,11 @@ static void write_fake_data( IOBUF out, MPI a ) { if( a ) { - size_t i; + int i; void *p; - p = gcry_mpi_get_opaque( a, &i ); - iobuf_write( out, p, (i+7)/8 ); + p = mpi_get_opaque( a, &i ); + iobuf_write( out, p, i ); } } @@ -195,11 +206,20 @@ do_comment( IOBUF out, int ctb, PKT_comment *rem ) static int do_user_id( IOBUF out, int ctb, PKT_user_id *uid ) { - write_header(out, ctb, uid->len); - uid->stored_at = iobuf_get_temp_length ( out ); /* what a hack ... */ - /* ... and it does only work when used with a temp iobuf */ - if( iobuf_write( out, uid->name, uid->len ) ) - return GPGERR_WRITE_FILE; + if( uid->photo ) { + write_header(out, ctb, uid->photolen); + uid->stored_at = iobuf_get_temp_length ( out ); /* what a hack ... */ + /* ... and it does only work when used with a temp iobuf */ + if( iobuf_write( out, uid->photo, uid->photolen ) ) + return GPGERR_WRITE_FILE; + } + else { + write_header(out, ctb, uid->len); + uid->stored_at = iobuf_get_temp_length ( out ); /* what a hack ... */ + /* ... and it does only work when used with a temp iobuf */ + if( iobuf_write( out, uid->name, uid->len ) ) + return GPGERR_WRITE_FILE; + } return 0; } @@ -358,19 +378,30 @@ do_secret_key( IOBUF out, int ctb, PKT_secret_key *sk ) else { iobuf_put(a, 0xff ); iobuf_put(a, sk->protect.algo ); - iobuf_put(a, sk->protect.s2k.mode ); - iobuf_put(a, sk->protect.s2k.hash_algo ); + if( sk->protect.s2k.mode >= 1000 ) { + iobuf_put(a, 101 ); + iobuf_put(a, sk->protect.s2k.hash_algo ); + iobuf_write(a, "GNU", 3 ); + iobuf_put(a, sk->protect.s2k.mode - 1000 ); + } + else { + iobuf_put(a, sk->protect.s2k.mode ); + iobuf_put(a, sk->protect.s2k.hash_algo ); + } if( sk->protect.s2k.mode == 1 || sk->protect.s2k.mode == 3 ) iobuf_write(a, sk->protect.s2k.salt, 8 ); if( sk->protect.s2k.mode == 3 ) iobuf_put(a, sk->protect.s2k.count ); - iobuf_write(a, sk->protect.iv, sk->protect.ivlen ); + if( sk->protect.s2k.mode != 1001 ) + iobuf_write(a, sk->protect.iv, sk->protect.ivlen ); } } else iobuf_put(a, 0 ); - if( sk->is_protected && sk->version >= 4 ) { + if( sk->protect.s2k.mode == 1001 ) + ; + else if( sk->is_protected && sk->version >= 4 ) { byte *p; size_t n; assert( gcry_mpi_get_flag( sk->skey[i], GCRYMPI_FLAG_OPAQUE ) ); @@ -379,7 +410,7 @@ do_secret_key( IOBUF out, int ctb, PKT_secret_key *sk ) } else { for( ; i < nskey; i++ ) - mpi_write_opaque(a, sk->skey[i] ); + mpi_write(a, sk->skey[i] ); write_16(a, sk->csum ); } @@ -527,13 +558,24 @@ do_encrypted_mdc( IOBUF out, int ctb, PKT_encrypted *ed ) n = ed->len ? (ed->len + 10) : 0; write_header(out, ctb, n ); iobuf_put(out, 1 ); /* version */ - iobuf_put(out, ed->mdc_method ); /* This is all. The caller has to write the real data */ return rc; } + +static int +do_mdc( IOBUF out, PKT_mdc *mdc ) +{ + /* This packet requires a fixed header encoding */ + iobuf_put( out, 0xd3 ); /* packet ID and 1 byte length */ + iobuf_put( out, 0x14 ); /* length = 20 */ + if( iobuf_write( out, mdc->hash, sizeof(mdc->hash) ) ) + return GPGERR_WRITE_FILE; + return 0; +} + static int do_compressed( IOBUF out, int ctb, PKT_compressed *cd ) { @@ -623,7 +665,6 @@ void build_sig_subpkt( PKT_signature *sig, sigsubpkttype_t type, const byte *buffer, size_t buflen ) { - byte *data; size_t hlen, dlen, nlen; int found=0; @@ -659,6 +700,7 @@ build_sig_subpkt( PKT_signature *sig, sigsubpkttype_t type, case SIGSUBPKT_KEY_EXPIRE: case SIGSUBPKT_NOTATION: case SIGSUBPKT_POLICY: + case SIGSUBPKT_REVOC_REASON: hashed = 1; break; default: hashed = 0; break; } diff --git a/g10/cipher.c b/g10/cipher.c index 8fc0d3815..cad6ff664 100644 --- a/g10/cipher.c +++ b/g10/cipher.c @@ -1,5 +1,5 @@ /* cipher.c - En-/De-ciphering filter - * Copyright (C) 1998,1999 Free Software Foundation, Inc. + * Copyright (C) 1998, 1999, 2000 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -33,6 +33,7 @@ #include "packet.h" #include "options.h" #include "main.h" +#include "status.h" #define MIN_PARTIAL_SIZE 512 @@ -44,10 +45,18 @@ write_header( cipher_filter_context_t *cfx, IOBUF a ) PACKET pkt; PKT_encrypted ed; byte temp[18]; - int blocksize; - unsigned nprefix; - int use_mdc = opt.force_mdc; + unsigned int blocksize; + unsigned int nprefix; int rc; + int use_mdc = opt.force_mdc; + + blocksize = gcry_cipher_get_algo_blklen( cfx->dek->algo ); + if( blocksize < 8 || blocksize > 16 ) + log_fatal("unsupported blocksize %u\n", blocksize ); + if( blocksize != 8 ) + use_mdc = 1; /* enable it for all modern ciphers */ + if( opt.rfc2440 ) + use_mdc = 0; /* override - rfc2440 does not know about MDC */ memset( &ed, 0, sizeof ed ); ed.len = cfx->datalen; @@ -55,18 +64,16 @@ write_header( cipher_filter_context_t *cfx, IOBUF a ) if( use_mdc ) { ed.mdc_method = GCRY_MD_SHA1; cfx->mdc_hash = gcry_md_open( GCRY_MD_SHA1, 0 ); - /*should we check the function works, or is it better to provide - a flag which makes the function die itself ?? FIXME */ - /*md_start_debug( cfx->mdc_hash, "mdccreat" );*/ + if( !cfx->mdc_hash ) + BUG(); + if ( DBG_HASHING ) + gcry_md_start_debug( cfx->mdc_hash, "creatmdc" ); } init_packet( &pkt ); pkt.pkttype = use_mdc? PKT_ENCRYPTED_MDC : PKT_ENCRYPTED; pkt.pkt.encrypted = &ed; if( build_packet( a, &pkt )) log_bug("build_packet(ENCR_DATA) failed\n"); - blocksize = gcry_cipher_get_algo_blklen( cfx->dek->algo ); - if( blocksize < 8 || blocksize > 16 ) - log_fatal("unsupported blocksize %d\n", blocksize ); nprefix = blocksize; gcry_randomize( temp, nprefix, GCRY_STRONG_RANDOM ); temp[nprefix] = temp[nprefix-2]; @@ -75,7 +82,7 @@ write_header( cipher_filter_context_t *cfx, IOBUF a ) if( !(cfx->cipher_hd = gcry_cipher_open( cfx->dek->algo, GCRY_CIPHER_MODE_CFB, GCRY_CIPHER_SECURE - | (cfx->dek->algo >= 100 ? + | ((use_mdc || cfx->dek->algo >= 100) ? 0 : GCRY_CIPHER_ENABLE_SYNC))) ) { /* we should never get an error here cause we already checked, that @@ -83,6 +90,7 @@ write_header( cipher_filter_context_t *cfx, IOBUF a ) BUG(); } + /* log_hexdump( "thekey", cfx->dek->key, cfx->dek->keylen );*/ rc = gcry_cipher_setkey( cfx->cipher_hd, cfx->dek->key, cfx->dek->keylen ); if( !rc ) @@ -99,6 +107,7 @@ write_header( cipher_filter_context_t *cfx, IOBUF a ) log_fatal("encrypt failed: %s\n", gcry_strerror(rc) ); iobuf_write(a, temp, nprefix+2); cfx->header=1; + } @@ -120,6 +129,7 @@ cipher_filter( void *opaque, int control, else if( control == IOBUFCTRL_FLUSH ) { /* encrypt */ assert(a); if( !cfx->header ) { + write_status( STATUS_BEGIN_ENCRYPTION ); write_header( cfx, a ); } if( cfx->mdc_hash ) @@ -134,15 +144,26 @@ cipher_filter( void *opaque, int control, if( cfx->mdc_hash ) { byte *hash; int hashlen = gcry_md_get_algo_dlen( gcry_md_get_algo( cfx->mdc_hash ) ); + byte temp[22]; + + assert( hashlen == 20 ); + /* we must hash the prefix of the MDC packet here */ + temp[0] = 0xd3; + temp[1] = 0x14; + gcry_md_putc( cfx->mdc_hash, temp[0] ); + gcry_md_putc( cfx->mdc_hash, temp[1] ); + hash = gcry_md_read( cfx->mdc_hash, 0 ); - rc = gcry_cipher_encrypt( cfx->cipher_hd, hash, hashlen, NULL, 0 ); + memcpy(temp+2, hash, 20); + rc = gcry_cipher_encrypt( cfx->cipher_hd, temp, 22, NULL, 0 ); if( rc ) log_fatal("encrypt failed: %s\n", gcry_strerror(rc) ); - if( iobuf_write( a, hash, hashlen ) ) - rc = GPGERR_WRITE_FILE; gcry_md_close( cfx->mdc_hash ); cfx->mdc_hash = NULL; + if( iobuf_write( a, temp, 22 ) ) + log_error("writing MDC packet failed\n" ); } gcry_cipher_close(cfx->cipher_hd); + write_status( STATUS_END_ENCRYPTION ); } else if( control == IOBUFCTRL_DESC ) { *(char**)buf = "cipher_filter"; diff --git a/g10/comment.c b/g10/comment.c index fe80a1cf8..f0d884979 100644 --- a/g10/comment.c +++ b/g10/comment.c @@ -1,5 +1,5 @@ /* comment.c - write comment stuff - * Copyright (C) 1998 Free Software Foundation, Inc. + * Copyright (C) 1998, 2000 Free Software Foundation, Inc. * * This file is part of GnuPG. * diff --git a/g10/compress.c b/g10/compress.c index 48335ba89..2666e9051 100644 --- a/g10/compress.c +++ b/g10/compress.c @@ -1,5 +1,5 @@ /* compress.c - compress filter - * Copyright (C) 1998 Free Software Foundation, Inc. + * Copyright (C) 1998, 1999, 2000 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -27,8 +27,8 @@ #include #include -#include "util.h" #include +#include "util.h" #include "packet.h" #include "filter.h" #include "options.h" diff --git a/g10/dearmor.c b/g10/dearmor.c index 1c0a15bc6..937961e7f 100644 --- a/g10/dearmor.c +++ b/g10/dearmor.c @@ -1,5 +1,5 @@ /* dearmor.c - Armor utility - * Copyright (C) 1998 Free Software Foundation, Inc. + * Copyright (C) 1998, 1999, 2000 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -25,9 +25,9 @@ #include #include +#include #include "errors.h" #include "iobuf.h" -#include #include "util.h" #include "filter.h" #include "packet.h" diff --git a/g10/decrypt.c b/g10/decrypt.c index e9f1d2b93..981275602 100644 --- a/g10/decrypt.c +++ b/g10/decrypt.c @@ -1,5 +1,5 @@ /* decrypt.c - verify signed data - * Copyright (C) 1998 Free Software Foundation, Inc. + * Copyright (C) 1998, 2000 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -25,12 +25,12 @@ #include #include +#include #include "options.h" #include "packet.h" #include "errors.h" #include "iobuf.h" #include "keydb.h" -#include #include "util.h" #include "main.h" #include "i18n.h" diff --git a/g10/delkey.c b/g10/delkey.c index c452567a8..0c64d41a3 100644 --- a/g10/delkey.c +++ b/g10/delkey.c @@ -1,5 +1,5 @@ /* delkey.c - delete keys - * Copyright (C) 1998 Free Software Foundation, Inc. + * Copyright (C) 1998, 1999, 2000 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -62,6 +62,7 @@ delete_key( const char *username, int secret ) : find_keyblock_byname( &kbpos, username ); if( rc ) { log_error(_("%s: user not found\n"), username ); + write_status_text( STATUS_DELETE_PROBLEM, "1" ); goto leave; } @@ -93,10 +94,12 @@ delete_key( const char *username, int secret ) "there is a secret key for this public key!\n")); log_info(_( "use option \"--delete-secret-key\" to delete it first.\n")); + write_status_text( STATUS_DELETE_PROBLEM, "2" ); rc = -1; } - else if( rc != GPGERR_NO_SECKEY ) + else if( rc != GPGERR_NO_SECKEY ) { log_error("%s: get secret key: %s\n", username, gpg_errstr(rc) ); + } else rc = 0; } diff --git a/g10/encode.c b/g10/encode.c index ed4b1df0f..6195b533c 100644 --- a/g10/encode.c +++ b/g10/encode.c @@ -1,5 +1,5 @@ /* encode.c - encode data - * Copyright (C) 1998 Free Software Foundation, Inc. + * Copyright (C) 1998, 1999, 2000 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -41,6 +41,7 @@ static int encode_simple( const char *filename, int mode ); static int write_pubkey_enc_from_list( PK_LIST pk_list, DEK *dek, IOBUF out ); + /**************** * Emulate our old PK interface here - sometime in the future we might * change the internal design to directly fit to libgcrypt. @@ -162,7 +163,7 @@ encode_simple( const char *filename, int mode ) } if( (rc = open_outfile( filename, opt.armor? 1:0, &out )) ) { - iobuf_close(inp); + iobuf_cancel(inp); gcry_free(cfx.dek); gcry_free(s2k); return rc; @@ -510,7 +511,7 @@ write_pubkey_enc_from_list( PK_LIST pk_list, DEK *dek, IOBUF out ) * number of bits we have to use. We then encode the session * key in some way and we get it back in the big intger value * FRAME. Then we use FRAME, the public key PK->PKEY and the - * algorithm number PK->PUBKEY_ALGO and pass it to pk_encrypt + * algorithm number PK->PUBKEY_ALGO and pass it to pubkey_encrypt * which returns the encrypted value in the array ENC->DATA. * This array has a size which depends on the used algorithm * (e.g. 2 for ElGamal). We don't need frame anymore because we @@ -525,7 +526,7 @@ write_pubkey_enc_from_list( PK_LIST pk_list, DEK *dek, IOBUF out ) log_error("pubkey_encrypt failed: %s\n", gpg_errstr(rc) ); else { if( opt.verbose ) { - char *ustr = get_user_id_string( enc->keyid ); + char *ustr = get_user_id_string_native( enc->keyid ); log_info(_("%s/%s encrypted for: %s\n"), gcry_pk_algo_name(enc->pubkey_algo), gcry_cipher_algo_name(dek->algo), ustr ); diff --git a/g10/encr-data.c b/g10/encr-data.c index 7c90829e2..17d43e9d6 100644 --- a/g10/encr-data.c +++ b/g10/encr-data.c @@ -1,5 +1,5 @@ /* encr-data.c - process an encrypted data packet - * Copyright (C) 1998, 1999 Free Software Foundation, Inc. + * Copyright (C) 1998, 1999, 2000 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -23,6 +23,7 @@ #include #include #include + #include #include "util.h" #include "packet.h" @@ -30,9 +31,9 @@ #include "i18n.h" -static int decode_filter( void *opaque, int control, IOBUF a, - byte *buf, size_t *ret_len); static int mdc_decode_filter( void *opaque, int control, IOBUF a, + byte *buf, size_t *ret_len); +static int decode_filter( void *opaque, int control, IOBUF a, byte *buf, size_t *ret_len); typedef struct { @@ -54,8 +55,8 @@ decrypt_data( void *procctx, PKT_encrypted *ed, DEK *dek ) byte *p; int rc=0, c, i; byte temp[32]; - int blocksize; - unsigned nprefix; + unsigned int blocksize; + unsigned int nprefix; memset( &dfx, 0, sizeof dfx ); if( gcry_cipher_test_algo( dek->algo ) ) { @@ -68,7 +69,7 @@ decrypt_data( void *procctx, PKT_encrypted *ed, DEK *dek ) log_info(_("%s encrypted data\n"), gcry_cipher_algo_name( dek->algo ) ); blocksize = gcry_cipher_get_algo_blklen( dek->algo ); - if( blocksize < 1 || blocksize > 16 ) + if( !blocksize || blocksize > 16 ) log_fatal("unsupported blocksize %u\n", blocksize ); nprefix = blocksize; if( ed->len && ed->len < (nprefix+2) ) @@ -76,13 +77,13 @@ decrypt_data( void *procctx, PKT_encrypted *ed, DEK *dek ) if( ed->mdc_method ) { dfx.mdc_hash = gcry_md_open( ed->mdc_method, 0 ); - if( !dfx.mdc_hash ) - BUG(); + if ( DBG_HASHING ) + gcry_md_start_debug(dfx.mdc_hash, "checkmdc"); } if( !(dfx.cipher_hd = gcry_cipher_open( dek->algo, GCRY_CIPHER_MODE_CFB, GCRY_CIPHER_SECURE - | (dek->algo >= 100 ? + | ((ed->mdc_method || dek->algo >= 100)? 0 : GCRY_CIPHER_ENABLE_SYNC) )) ) { /* we should never get an error here cause we already checked, that @@ -122,8 +123,6 @@ decrypt_data( void *procctx, PKT_encrypted *ed, DEK *dek ) temp[i] = c; } gcry_cipher_decrypt( dfx.cipher_hd, temp, nprefix+2, NULL, 0 ); - if( dfx.mdc_hash ) - gcry_md_write( dfx.mdc_hash, temp, nprefix+2 ); gcry_cipher_sync( dfx.cipher_hd ); p = temp; /* log_hexdump( "prefix", temp, nprefix+2 ); */ @@ -131,22 +130,30 @@ decrypt_data( void *procctx, PKT_encrypted *ed, DEK *dek ) rc = GPGERR_BAD_KEY; goto leave; } + + if( dfx.mdc_hash ) + gcry_md_write( dfx.mdc_hash, temp, nprefix+2 ); + if( ed->mdc_method ) iobuf_push_filter( ed->buf, mdc_decode_filter, &dfx ); else iobuf_push_filter( ed->buf, decode_filter, &dfx ); - proc_packets( procctx, ed->buf); + + proc_packets( procctx, ed->buf ); ed->buf = NULL; if( ed->mdc_method && dfx.eof_seen == 2 ) rc = GPGERR_INVALID_PACKET; else if( ed->mdc_method ) { /* check the mdc */ int datalen = gcry_md_get_algo_dlen( ed->mdc_method ); + + gcry_cipher_decrypt( dfx.cipher_hd, dfx.defer, 20, NULL, 0); if( datalen != 20 || memcmp(gcry_md_read( dfx.mdc_hash, 0 ), dfx.defer, datalen) ) rc = GPGERR_BAD_SIGN; - log_hexdump("MDC calculated:", gcry_md_read( dfx.mdc_hash, 0), datalen); - log_hexdump("MDC message :", dfx.defer, 20); + /*log_hexdump("MDC calculated:", md_read( dfx.mdc_hash, 0), datalen);*/ + /*log_hexdump("MDC message :", dfx.defer, 20);*/ } + leave: gcry_cipher_close(dfx.cipher_hd); gcry_md_close( dfx.mdc_hash ); @@ -154,6 +161,7 @@ decrypt_data( void *procctx, PKT_encrypted *ed, DEK *dek ) } + /* I think we should merge this with cipher_filter */ static int mdc_decode_filter( void *opaque, int control, IOBUF a, @@ -180,11 +188,14 @@ mdc_decode_filter( void *opaque, int control, IOBUF a, } if( n == 40 ) { /* we have enough stuff - flush the deferred stuff */ - /* (we have asserted that the buffer is large enough */ - if( !dfx->defer_filled ) /* the first time */ + /* (we have asserted that the buffer is large enough) */ + if( !dfx->defer_filled ) { /* the first time */ memcpy(buf, buf+20, 20 ); - else + n = 20; + } + else { memcpy(buf, dfx->defer, 20 ); + } /* now fill up */ for(; n < size; n++ ) { if( (c = iobuf_get(a)) == -1 ) @@ -198,7 +209,7 @@ mdc_decode_filter( void *opaque, int control, IOBUF a, dfx->defer_filled = 1; } else if( !dfx->defer_filled ) { /* eof seen buf empty defer */ - /* this is very bad because there is an incomplete hash */ + /* this is bad because there is an incomplete hash */ n -= 20; memcpy(buf, buf+20, n ); dfx->eof_seen = 2; /* eof with incomplete hash */ @@ -238,7 +249,7 @@ decode_filter( void *opaque, int control, IOBUF a, byte *buf, size_t *ret_len) n = iobuf_read( a, buf, size ); if( n == -1 ) n = 0; if( n ) - gcry_cipher_decrypt( fc->cipher_hd, buf, n, NULL, 0); + gcry_cipher_decrypt( fc->cipher_hd, buf, n, NULL, 0 ); else rc = -1; /* eof */ *ret_len = n; diff --git a/g10/export.c b/g10/export.c index df81babf4..2de9f91bf 100644 --- a/g10/export.c +++ b/g10/export.c @@ -1,5 +1,5 @@ /* export.c - * Copyright (C) 1998 Free Software Foundation, Inc. + * Copyright (C) 1998, 1999, 2000 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -25,11 +25,11 @@ #include #include +#include #include "options.h" #include "packet.h" #include "errors.h" #include "keydb.h" -#include #include "util.h" #include "main.h" #include "i18n.h" @@ -71,6 +71,12 @@ export_seckeys( STRLIST users ) return do_export( users, 1, 0 ); } +int +export_secsubkeys( STRLIST users ) +{ + return do_export( users, 2, 0 ); +} + static int do_export( STRLIST users, int secret, int onlyrfc ) { @@ -168,6 +174,16 @@ do_export_stream( IOBUF out, STRLIST users, int secret, int onlyrfc, int *any ) } } + /* we can't apply GNU mode 1001 on an unprotected key */ + if( secret == 2 + && (node = find_kbnode( keyblock, PKT_SECRET_KEY )) + && !node->pkt->pkt.secret_key->is_protected ) + { + log_info(_("key %08lX: not protected - skipped\n"), + (ulong)keyid_from_sk( node->pkt->pkt.secret_key, NULL) ); + continue; + } + /* and write it */ for( kbctx=NULL; (node = walk_kbnode( keyblock, &kbctx, 0 )); ) { /* don't export any comment packets but those in the @@ -183,7 +199,20 @@ do_export_stream( IOBUF out, STRLIST users, int secret, int onlyrfc, int *any ) continue; /* not exportable */ } - if( (rc = build_packet( out, node->pkt )) ) { + if( secret == 2 && node->pkt->pkttype == PKT_SECRET_KEY ) { + /* we don't want to export the secret parts of the + * primary key, this is done by using GNU protection mode 1001 + */ + int save_mode = node->pkt->pkt.secret_key->protect.s2k.mode; + node->pkt->pkt.secret_key->protect.s2k.mode = 1001; + rc = build_packet( out, node->pkt ); + node->pkt->pkt.secret_key->protect.s2k.mode = save_mode; + } + else { + rc = build_packet( out, node->pkt ); + } + + if( rc ) { log_error("build_packet(%d) failed: %s\n", node->pkt->pkttype, gpg_errstr(rc) ); rc = GPGERR_WRITE_FILE; diff --git a/g10/filter.h b/g10/filter.h index 389026d2c..a29d2aa29 100644 --- a/g10/filter.h +++ b/g10/filter.h @@ -1,5 +1,5 @@ /* filter.h - * Copyright (C) 1998 Free Software Foundation, Inc. + * Copyright (C) 1998, 1999, 2000 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -26,7 +26,6 @@ #include "iobuf.h" - typedef struct { GCRY_MD_HD md; /* catch all */ GCRY_MD_HD md2; /* if we want to calculate an alternate hash */ @@ -39,6 +38,9 @@ typedef struct { int only_keyblocks; /* skip all headers but ".... key block" */ const char *hdrlines; /* write these headerlines */ + /* these fileds must be initialized to zero */ + int no_openpgp_data; /* output flag: "No valid OpenPGP data found" */ + /* the following fields must be initialized to zero */ int inp_checked; /* set if the input has been checked */ int inp_bypass; /* set if the input is not armored */ @@ -60,6 +62,7 @@ typedef struct { u32 crc; int status; /* an internal state flag */ + int cancel; int any_data; /* any valid armored data seen */ int pending_lf; /* used together with faked */ } armor_filter_context_t; @@ -83,6 +86,8 @@ typedef struct { GCRY_CIPHER_HD cipher_hd; int header; GCRY_MD_HD mdc_hash; + byte enchash[20]; + int create_mdc; /* flag will be set by the cipher filter */ } cipher_filter_context_t; @@ -93,7 +98,6 @@ typedef struct { } encrypt_filter_context_t; - typedef struct { byte *buffer; /* malloced buffer */ unsigned buffer_size; /* and size of this buffer */ @@ -108,8 +112,6 @@ typedef struct { } text_filter_context_t; -/* encrypt_filter_context_t defined in main.h */ - /*-- mdfilter.c --*/ int md_filter( void *opaque, int control, IOBUF a, byte *buf, size_t *ret_len); void free_md_filter_context( md_filter_context_t *mfx ); diff --git a/g10/free-packet.c b/g10/free-packet.c index 173f8d812..1d9aacff6 100644 --- a/g10/free-packet.c +++ b/g10/free-packet.c @@ -1,5 +1,5 @@ /* free-packet.c - cleanup stuff for packets - * Copyright (C) 1998 Free Software Foundation, Inc. + * Copyright (C) 1998, 1999, 2000 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -24,10 +24,10 @@ #include #include +#include #include "packet.h" #include "iobuf.h" #include "util.h" -#include #include "options.h" #include "main.h" @@ -219,6 +219,8 @@ free_comment( PKT_comment *rem ) void free_user_id( PKT_user_id *uid ) { + if( uid->photo ) + gcry_free( uid->photo ); gcry_free(uid); } diff --git a/g10/getkey.c b/g10/getkey.c index d0b9cc1dd..e9b4a231a 100644 --- a/g10/getkey.c +++ b/g10/getkey.c @@ -1,5 +1,5 @@ /* getkey.c - Get a key from the database - * Copyright (C) 1998, 1999 Free Software Foundation, Inc. + * Copyright (C) 1998, 1999, 2000 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -24,9 +24,10 @@ #include #include #include -#include + #include "util.h" #include "packet.h" +#include #include "iobuf.h" #include "keydb.h" #include "options.h" @@ -45,7 +46,11 @@ * that they are all valid. * Note: We must use numerical values here in case that this program * will be converted to those little blue HAL9000s with their strange - * EBCDIC character set (user ids are UTF-8). */ + * EBCDIC character set (user ids are UTF-8). + * wk 2000-04-13: Hmmm, does this really make sense, given the fact that + * we can run gpg now on a S/390 running GNU/Linux, where the code + * translation is done by the device drivers? + */ static const byte word_match_chars[256] = { /* 00 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 08 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, @@ -154,6 +159,7 @@ static int uid_cache_entries; /* number of entries in uid cache */ static char* prepare_word_match( const byte *name ); static int lookup_pk( GETKEY_CTX ctx, PKT_public_key *pk, KBNODE *ret_kb ); static int lookup_sk( GETKEY_CTX ctx, PKT_secret_key *sk, KBNODE *ret_kb ); +static u32 subkeys_expiretime( KBNODE node, u32 *mainkid ); #if 0 @@ -696,6 +702,8 @@ key_byname( GETKEY_CTX *retctx, STRLIST namelist, STRLIST r; GETKEY_CTX ctx; + if( retctx ) /* reset the returned context in case of error */ + *retctx = NULL; assert( !pk ^ !sk ); /* build the search context */ @@ -941,7 +949,7 @@ get_seckey_bynames( GETKEY_CTX *retctx, PKT_secret_key *sk, if( !sk ) { /* Performance Hint: key_byname should not need a sk here */ - sk = gcry_xcalloc_secure( 1, sizeof *sk ); + sk = gcry_xcalloc_secure( 1, sizeof *sk ); rc = key_byname( retctx, names, NULL, sk, ret_keyblock ); free_secret_key( sk ); } @@ -959,7 +967,7 @@ get_seckey_next( GETKEY_CTX ctx, PKT_secret_key *sk, KBNODE *ret_keyblock ) if( !sk ) { /* Performance Hint: lookup_read should not need a pk in this case */ - sk = gcry_xcalloc_secure( 1, sizeof *sk ); + sk = gcry_xcalloc_secure( 1, sizeof *sk ); rc = lookup_sk( ctx, sk, ret_keyblock ); free_secret_key( sk ); } @@ -1150,7 +1158,7 @@ merge_one_pk_and_selfsig( KBNODE keyblock, KBNODE knode, k = find_kbnode( keyblock, PKT_PUBLIC_KEY ); if( !k ) BUG(); /* keyblock without primary key!!! */ - keyid_from_pk( knode->pkt->pkt.public_key, kid ); + keyid_from_pk( k->pkt->pkt.public_key, kid ); } else keyid_from_pk( pk, kid ); @@ -1208,6 +1216,10 @@ merge_keys_and_selfsig( KBNODE keyblock ) pk = NULL; /* not needed for old keys */ else if( k->pkt->pkttype == PKT_PUBLIC_KEY ) keyid_from_pk( pk, kid ); + else if( !pk->expiredate ) { /* and subkey */ + /* insert the expiration date here */ + pk->expiredate = subkeys_expiretime( k, kid ); + } sigdate = 0; } else if( k->pkt->pkttype == PKT_SECRET_KEY @@ -1222,8 +1234,11 @@ merge_keys_and_selfsig( KBNODE keyblock ) else if( (pk || sk ) && k->pkt->pkttype == PKT_SIGNATURE && (sig=k->pkt->pkt.signature)->sig_class >= 0x10 && sig->sig_class <= 0x30 && sig->version > 3 + && !(sig->sig_class == 0x18 || sig->sig_class == 0x28) && sig->keyid[0] == kid[0] && sig->keyid[1] == kid[1] ) { /* okay this is a self-signature which can be used. + * This is not used for subkey binding signature, becuase this + * is done above. * FIXME: We should only use this if the signature is valid * but this is time consuming - we must provide another * way to handle this @@ -1279,9 +1294,16 @@ find_by_name( KBNODE keyblock, PKT_public_key *pk, const char *name, u32 aki[2]; keyid_from_pk( kk->pkt->pkt.public_key, aki ); cache_user_id( k->pkt->pkt.user_id, aki ); - gcry_md_hash_buffer( GCRY_MD_RMD160, namehash, - k->pkt->pkt.user_id->name, - k->pkt->pkt.user_id->len ); + if( k->pkt->pkt.user_id->photo ) { + gcry_md_hash_buffer( GCRY_MD_RMD160, namehash, + k->pkt->pkt.user_id->photo, + k->pkt->pkt.user_id->photolen ); + } + else { + gcry_md_hash_buffer( GCRY_MD_RMD160, namehash, + k->pkt->pkt.user_id->name, + k->pkt->pkt.user_id->len ); + } *use_namehash = 1; return kk; } @@ -1516,6 +1538,56 @@ find_by_fpr_sk( KBNODE keyblock, PKT_secret_key *sk, } +/**************** + * Return the expiretime of a subkey. + */ +static u32 +subkeys_expiretime( KBNODE node, u32 *mainkid ) +{ + KBNODE k; + PKT_signature *sig; + u32 expires = 0, sigdate = 0; + + assert( node->pkt->pkttype == PKT_PUBLIC_SUBKEY ); + for(k=node->next; k; k = k->next ) { + if( k->pkt->pkttype == PKT_SIGNATURE + && (sig=k->pkt->pkt.signature)->sig_class == 0x18 + && sig->keyid[0] == mainkid[0] + && sig->keyid[1] == mainkid[1] + && sig->version > 3 + && sig->timestamp > sigdate ) { + /* okay this is a key-binding which can be used. + * We use the latest self-signature. + * FIXME: We should only use this if the binding signature is valid + * but this is time consuming - we must provide another + * way to handle this + */ + const byte *p; + u32 ed; + + p = parse_sig_subpkt( sig->hashed_data, SIGSUBPKT_KEY_EXPIRE, NULL ); + ed = p? node->pkt->pkt.public_key->timestamp + buffer_to_u32(p):0; + sigdate = sig->timestamp; + expires = ed; + } + else if( k->pkt->pkttype == PKT_PUBLIC_SUBKEY ) + break; /* stop at the next subkey */ + } + + return expires; +} + + +/**************** + * Check whether the subkey has expired. Node must point to the subkey + */ +static int +has_expired( KBNODE node, u32 *mainkid, u32 cur_time ) +{ + u32 expires = subkeys_expiretime( node, mainkid ); + return expires && expires <= cur_time; +} + static void finish_lookup( KBNODE keyblock, PKT_public_key *pk, KBNODE k, byte *namehash, int use_namehash, int primary ) @@ -1534,6 +1606,10 @@ finish_lookup( KBNODE keyblock, PKT_public_key *pk, KBNODE k, byte *namehash, pk->pubkey_usage ) == GPGERR_WR_PUBKEY_ALGO ) { /* if the usage is not correct, try to use a subkey */ KBNODE save_k = k; + u32 mainkid[2]; + u32 cur_time = make_timestamp(); + + keyid_from_pk( keyblock->pkt->pkt.public_key, mainkid ); k = NULL; /* kludge for pgp 5: which doesn't accept type 20: @@ -1545,7 +1621,8 @@ finish_lookup( KBNODE keyblock, PKT_public_key *pk, KBNODE k, byte *namehash, == GCRY_PK_ELG_E && !openpgp_pk_test_algo( k->pkt->pkt.public_key->pubkey_algo, - pk->pubkey_usage ) ) + pk->pubkey_usage ) + && !has_expired(k, mainkid, cur_time) ) break; } } @@ -1555,7 +1632,10 @@ finish_lookup( KBNODE keyblock, PKT_public_key *pk, KBNODE k, byte *namehash, if( k->pkt->pkttype == PKT_PUBLIC_SUBKEY && !openpgp_pk_test_algo( k->pkt->pkt.public_key->pubkey_algo, - pk->pubkey_usage ) ) + pk->pubkey_usage ) + && ( pk->pubkey_usage != GCRY_PK_USAGE_ENCR + || !has_expired( k, mainkid, cur_time ) ) + ) break; } } @@ -1887,6 +1967,18 @@ get_user_id_string( u32 *keyid ) return p; } + +char* +get_user_id_string_native( u32 *keyid ) +{ + char *p = get_user_id_string( keyid ); + char *p2 = utf8_to_native( p, strlen(p) ); + + gcry_free(p); + return p2; +} + + char* get_long_user_id_string( u32 *keyid ) { @@ -1914,6 +2006,7 @@ get_user_id( u32 *keyid, size_t *rn ) user_id_db_t r; char *p; int pass=0; + /* try it two times; second pass reads from key resources */ do { for(r=user_id_db; r; r = r->next ) @@ -1924,9 +2017,8 @@ get_user_id( u32 *keyid, size_t *rn ) return p; } } while( ++pass < 2 && !get_pubkey( NULL, keyid ) ); - p = gcry_xmalloc( 19 ); - memcpy(p, "[User id not found]", 19 ); - *rn = 19; + p = gcry_xstrdup( _("[User id not found]") ); + *rn = strlen(p); return p; } diff --git a/g10/gpg.c b/g10/gpg.c index 2b66509f4..fb1be046f 100644 --- a/g10/gpg.c +++ b/g10/gpg.c @@ -26,8 +26,8 @@ #include #include -#include +#include #include "packet.h" #include "iobuf.h" #include "util.h" @@ -77,6 +77,7 @@ enum cmd_and_opt_values { aNull = 0, aImport, aFastImport, aVerify, + aVerifyFiles, aListKeys, aListSigs, aListSecretKeys, @@ -85,12 +86,13 @@ enum cmd_and_opt_values { aNull = 0, aExport, aExportAll, aExportSecret, + aExportSecretSub, aCheckKeys, aGenRevoke, aPrimegen, aPrintMD, - aPrintHMAC, aPrintMDs, + aPrintHMAC, aCheckTrustDB, aUpdateTrustDB, aFixTrustDB, @@ -130,6 +132,7 @@ enum cmd_and_opt_values { aNull = 0, oDigestAlgo, oCompressAlgo, oPasswdFD, + oCommandFD, oNoVerbose, oTrustDBName, oNoSecmemWarn, @@ -146,7 +149,6 @@ enum cmd_and_opt_values { aNull = 0, oCompressKeys, oCompressSigs, oAlwaysTrust, - oEmuChecksumBug, oRunAsShmCP, oSetFilename, oSetPolicyURL, @@ -164,6 +166,7 @@ enum cmd_and_opt_values { aNull = 0, oEscapeFrom, oLockOnce, oLockMultiple, + oLockNever, oKeyServer, oEncryptTo, oNoEncryptTo, @@ -175,7 +178,14 @@ enum cmd_and_opt_values { aNull = 0, oAllowNonSelfsignedUID, oNoLiteral, oSetFilesize, - oEntropyDLLName, + oHonorHttpProxy, + oFastListMode, + oListOnly, + oIgnoreTimeConflict, + oNoRandomSeedFile, + oNoAutoKeyRetrieve, + oEmu3DESS2KBug, /* will be removed in 1.1 */ + oEmuMDEncodeBug, aTest }; @@ -191,6 +201,7 @@ static ARGPARSE_OPTS opts[] = { { aStore, "store", 256, N_("store only")}, { aDecrypt, "decrypt", 256, N_("decrypt data (default)")}, { aVerify, "verify" , 256, N_("verify a signature")}, + { aVerifyFiles, "verify-files" , 256, "@" }, { aListKeys, "list-keys", 256, N_("list keys")}, { aListKeys, "list-public-keys", 256, "@" }, { aListSigs, "list-sigs", 256, N_("list keys and signatures")}, @@ -210,6 +221,7 @@ static ARGPARSE_OPTS opts[] = { { aRecvKeys, "recv-keys" , 256, N_("import keys from a key server") }, { aExportAll, "export-all" , 256, "@" }, { aExportSecret, "export-secret-keys" , 256, "@" }, + { aExportSecretSub, "export-secret-subkeys" , 256, "@" }, { aImport, "import", 256 , N_("import/merge keys")}, { aFastImport, "fast-import", 256 , "@"}, { aListPackets, "list-packets",256,N_("list only the sequence of packets")}, @@ -266,12 +278,12 @@ static ARGPARSE_OPTS opts[] = { { oCharset, "charset" , 2, N_("|NAME|set terminal charset to NAME") }, { oOptions, "options" , 2, N_("read options from file")}, - { oDebug, "debug" ,4|16, N_("set debugging flags")}, - { oDebugAll, "debug-all" ,0, N_("enable full debugging")}, + { oDebug, "debug" ,4|16, "@"}, + { oDebugAll, "debug-all" ,0, "@"}, { oStatusFD, "status-fd" ,1, N_("|FD|write status info to this FD") }, - { oNoComment, "no-comment", 0, N_("do not write comment packets")}, - { oCompletesNeeded, "completes-needed", 1, N_("(default is 1)")}, - { oMarginalsNeeded, "marginals-needed", 1, N_("(default is 3)")}, + { oNoComment, "no-comment", 0, "@"}, + { oCompletesNeeded, "completes-needed", 1, "@"}, + { oMarginalsNeeded, "marginals-needed", 1, "@"}, { oMaxCertDepth, "max-cert-depth", 1, "@" }, { oLoadExtension, "load-extension" ,2, N_("|FILE|load extension module FILE")}, { oRFC1991, "rfc1991", 0, N_("emulate the mode described in RFC1991")}, @@ -287,7 +299,11 @@ static ARGPARSE_OPTS opts[] = { { oThrowKeyid, "throw-keyid", 0, N_("throw keyid field of encrypted packets")}, { oNotation, "notation-data", 2, N_("|NAME=VALUE|use this notation data")}, - { 302, NULL, 0, N_("@\nExamples:\n\n" + { 302, NULL, 0, N_( + "@\n(See the man page for a complete listing of all commands and options)\n" + )}, + + { 303, NULL, 0, N_("@\nExamples:\n\n" " -se -r Bob [file] sign and encrypt for user Bob\n" " --clearsign [file] make a clear text signature\n" " --detach-sign [file] make a detached signature\n" @@ -301,6 +317,7 @@ static ARGPARSE_OPTS opts[] = { { aListTrustPath, "list-trust-path",0, "@"}, { oKOption, NULL, 0, "@"}, { oPasswdFD, "passphrase-fd",1, "@" }, + { oCommandFD, "command-fd",1, "@" }, { oNoVerbose, "no-verbose", 0, "@"}, { oTrustDBName, "trustdb-name", 2, "@" }, { oNoSecmemWarn, "no-secmem-warning", 0, "@" }, /* used only by regression tests */ @@ -331,6 +348,7 @@ static ARGPARSE_OPTS opts[] = { { oEscapeFrom, "escape-from-lines", 0, "@" }, { oLockOnce, "lock-once", 0, "@" }, { oLockMultiple, "lock-multiple", 0, "@" }, + { oLockNever, "lock-never", 0, "@" }, { oLoggerFD, "logger-fd",1, "@" }, { oUseEmbeddedFilename, "use-embedded-filename", 0, "@" }, { oUtf8Strings, "utf8-strings", 0, "@" }, @@ -341,7 +359,14 @@ static ARGPARSE_OPTS opts[] = { { oAllowNonSelfsignedUID, "allow-non-selfsigned-uid", 0, "@" }, { oNoLiteral, "no-literal", 0, "@" }, { oSetFilesize, "set-filesize", 20, "@" }, - { oEntropyDLLName, "entropy-dll-name", 2, "@" }, + { oHonorHttpProxy,"honor-http-proxy", 0, "@" }, + { oFastListMode,"fast-list-mode", 0, "@" }, + { oListOnly, "list-only", 0, "@"}, + { oIgnoreTimeConflict, "ignore-time-conflict", 0, "@" }, + { oNoRandomSeedFile, "no-random-seed-file", 0, "@" }, + { oNoAutoKeyRetrieve, "no-auto-key-retrieve", 0, "@" }, + { oEmu3DESS2KBug, "emulate-3des-s2k-bug", 0, "@"}, + { oEmuMDEncodeBug, "emulate-md-encode-bug", 0, "@"}, {0} }; @@ -351,8 +376,8 @@ int gpg_errors_seen = 0; static int utf8_strings = 0; static int maybe_setuid = 1; -static char *build_list( const char *text, const char * (*mapf)(int), - int (*chkf)(int) ); +static char *build_list( const char *text, + const char *(*mapf)(int), int (*chkf)(int) ); static void set_cmd( enum cmd_and_opt_values *ret_cmd, enum cmd_and_opt_values new_cmd ); static void print_hex( byte *p, size_t n ); @@ -360,13 +385,13 @@ static void print_mds( const char *fname, int algo, const char *key ); static void add_notation_data( const char *string ); static int check_policy_url( const char *s ); + static int our_pk_test_algo( int algo ) { return openpgp_pk_test_algo( algo, 0 ); } - static const char * my_strusage( int level ) { @@ -390,26 +415,29 @@ my_strusage( int level ) "default operation depends on the input data\n"); break; - case 31: p = _("\nSupported algorithms:\n"); break; - case 32: + case 31: p = "\nHome: "; break; + case 32: p = opt.homedir; break; + case 33: p = _("\nSupported algorithms:\n"); break; + case 34: if( !ciphers ) ciphers = build_list("Cipher: ", gcry_cipher_algo_name, openpgp_cipher_test_algo ); p = ciphers; break; - case 33: + case 35: if( !pubkeys ) pubkeys = build_list("Pubkey: ", gcry_pk_algo_name, our_pk_test_algo ); p = pubkeys; break; - case 34: + case 36: if( !digests ) digests = build_list("Hash: ", gcry_md_algo_name, openpgp_md_test_algo ); p = digests; break; + default: p = NULL; } return p; @@ -465,27 +493,6 @@ i18n_init(void) #endif } -static void -wrong_args( const char *text) -{ - fputs(_("usage: gpg [options] "),stderr); - fputs(text,stderr); - putc('\n',stderr); - gpg_exit(2); -} - - -static char * -make_username( const char *string ) -{ - char *p; - if( utf8_strings ) - p = native_to_utf8( string ); - else - p = gcry_xstrdup(string); - return p; -} - static void register_extension( const char *mainpgm, const char *fname ) @@ -509,16 +516,38 @@ register_extension( const char *mainpgm, const char *fname ) +static void +wrong_args( const char *text) +{ + fputs(_("usage: gpg [options] "),stderr); + fputs(text,stderr); + putc('\n',stderr); + gpg_exit(2); +} + + +static char * +make_username( const char *string ) +{ + char *p; + if( utf8_strings ) + p = gcry_xstrdup(string); + else + p = native_to_utf8( string ); + return p; +} + + static void set_debug(void) { - #if 0 - #warning memory debuggig not enabled + #if 0 + #warning memory debugging not enabled if( opt.debug & DBG_MEMORY_VALUE ) memory_debug_mode = 1; if( opt.debug & DBG_MEMSTAT_VALUE ) memory_stat_debug_mode = 1; - #endif + #endif if( opt.debug & DBG_MPI_VALUE ) gcry_control( GCRYCTL_SET_DEBUG_FLAGS, 2 ); @@ -579,6 +608,7 @@ main( int argc, char **argv ) int default_keyring = 1; int greeting = 0; int nogreeting = 0; + int use_random_seed = 1; enum cmd_and_opt_values cmd = 0; const char *trustdb_name = NULL; char *def_cipher_string = NULL; @@ -611,6 +641,7 @@ main( int argc, char **argv ) init_signals(); create_dotlock(NULL); /* register locking cleanup */ i18n_init(); + opt.command_fd = -1; /* no command fd */ opt.compress = -1; /* defaults to standard compress level */ /* note: if you change these lines, look at oOpenPGP */ opt.def_cipher_algo = 0; @@ -622,7 +653,13 @@ main( int argc, char **argv ) opt.completes_needed = 1; opt.marginals_needed = 3; opt.max_cert_depth = 5; + opt.pgp2_workarounds = 1; + opt.auto_key_retrieve = 1; + #ifdef __MINGW32__ + opt.homedir = read_w32_registry_string( NULL, "Software\\GNU\\GnuPG", "HomeDir" ); + #else opt.homedir = getenv("GNUPGHOME"); + #endif if( !opt.homedir || !*opt.homedir ) { #ifdef HAVE_DRIVE_LETTERS opt.homedir = "c:/gnupg-test"; @@ -679,7 +716,6 @@ main( int argc, char **argv ) if( default_config ) configname = make_filename(opt.homedir, "options", NULL ); - argc = orig_argc; argv = orig_argv; pargs.argc = &argc; @@ -721,13 +757,16 @@ main( int argc, char **argv ) case aListKeys: set_cmd( &cmd, aListKeys); break; case aListSigs: set_cmd( &cmd, aListSigs); break; case aExportSecret: set_cmd( &cmd, aExportSecret); break; + case aExportSecretSub: set_cmd( &cmd, aExportSecretSub); break; case aDeleteSecretKey: set_cmd( &cmd, aDeleteSecretKey); greeting=1; break; case aDeleteKey: set_cmd( &cmd, aDeleteKey); greeting=1; break; case aDetachedSign: detached_sig = 1; set_cmd( &cmd, aSign ); break; case aSym: set_cmd( &cmd, aSym); break; + case aDecrypt: set_cmd( &cmd, aDecrypt); break; + case aEncr: set_cmd( &cmd, aEncr); break; case aSign: set_cmd( &cmd, aSign ); break; case aKeygen: set_cmd( &cmd, aKeygen); greeting=1; break; @@ -738,11 +777,12 @@ main( int argc, char **argv ) case aClearsign: set_cmd( &cmd, aClearsign); break; case aGenRevoke: set_cmd( &cmd, aGenRevoke); break; case aVerify: set_cmd( &cmd, aVerify); break; + case aVerifyFiles: set_cmd( &cmd, aVerifyFiles); break; case aPrimegen: set_cmd( &cmd, aPrimegen); break; case aGenRandom: set_cmd( &cmd, aGenRandom); break; case aPrintMD: set_cmd( &cmd, aPrintMD); break; - case aPrintHMAC: set_cmd( &cmd, aPrintHMAC); break; case aPrintMDs: set_cmd( &cmd, aPrintMDs); break; + case aPrintHMAC: set_cmd( &cmd, aPrintHMAC); break; case aListTrustDB: set_cmd( &cmd, aListTrustDB); break; case aCheckTrustDB: set_cmd( &cmd, aCheckTrustDB); break; case aUpdateTrustDB: set_cmd( &cmd, aUpdateTrustDB); break; @@ -828,11 +868,14 @@ main( int argc, char **argv ) break; case oRFC1991: opt.rfc1991 = 1; + opt.rfc2440 = 0; opt.no_comment = 1; opt.escape_from = 1; break; case oOpenPGP: opt.rfc1991 = 0; + opt.rfc2440 = 1; + opt.pgp2_workarounds = 0; opt.escape_from = 0; opt.force_v3_sigs = 0; opt.compress_keys = 0; /* not mandated but we do it */ @@ -840,11 +883,13 @@ main( int argc, char **argv ) opt.not_dash_escaped = 0; opt.def_cipher_algo = 0; opt.def_digest_algo = 0; - opt.def_compress_algo = 2; + opt.def_compress_algo = 1; opt.s2k_mode = 3; /* iterated+salted */ - opt.s2k_digest_algo = GCRY_MD_RMD160; - opt.s2k_cipher_algo = GCRY_CIPHER_BLOWFISH; + opt.s2k_digest_algo = GCRY_MD_SHA1; + opt.s2k_cipher_algo = GCRY_CIPHER_CAST5; break; + case oEmu3DESS2KBug: opt.emulate_bugs |= EMUBUG_3DESS2K; break; + case oEmuMDEncodeBug: opt.emulate_bugs |= EMUBUG_MDENCODE; break; case oCompressSigs: opt.compress_sigs = 1; break; case oRunAsShmCP: #ifndef USE_SHM_COPROCESSING @@ -880,6 +925,7 @@ main( int argc, char **argv ) break; case oCompress: opt.compress = pargs.r.ret_int; break; case oPasswdFD: pwfd = pargs.r.ret_int; break; + case oCommandFD: opt.command_fd = pargs.r.ret_int; break; case oCipherAlgo: def_cipher_string = gcry_xstrdup(pargs.r.ret_str); break; case oDigestAlgo: def_digest_string = gcry_xstrdup(pargs.r.ret_str); break; case oNoSecmemWarn: gcry_control( GCRYCTL_DISABLE_SECMEM_WARN ); break; @@ -891,6 +937,10 @@ main( int argc, char **argv ) case oNotDashEscaped: opt.not_dash_escaped = 1; break; case oEscapeFrom: opt.escape_from = 1; break; case oLockOnce: opt.lock_once = 1; break; + #if 0 + #warning no disable_dotlock() yet + case oLockNever: disable_dotlock(); break; + #endif case oLockMultiple: opt.lock_once = 0; break; case oKeyServer: opt.keyserver_name = pargs.r.ret_str; break; case oNotation: add_notation_data( pargs.r.ret_str ); break; @@ -908,22 +958,15 @@ main( int argc, char **argv ) &algo, sizeof algo ); } break; - case oAllowNonSelfsignedUID: - opt.allow_non_selfsigned_uid = 1; - break; - case oNoLiteral: - opt.no_literal = 1; - break; - case oSetFilesize: - opt.set_filesize = pargs.r.ret_ulong; - break; - - case oEntropyDLLName: - #ifdef USE_STATIC_RNDW32 - log_info("set dllname to `%s'\n", pargs.r.ret_str ); - rndw32_set_dll_name( pargs.r.ret_str ); - #endif - break; + case oAllowNonSelfsignedUID: opt.allow_non_selfsigned_uid = 1; break; + case oNoLiteral: opt.no_literal = 1; break; + case oSetFilesize: opt.set_filesize = pargs.r.ret_ulong; break; + case oHonorHttpProxy: opt.honor_http_proxy = 1; break; + case oFastListMode: opt.fast_list_mode = 1; break; + case oListOnly: opt.list_only=1; break; + case oIgnoreTimeConflict: opt.ignore_time_conflict = 1; break; + case oNoRandomSeedFile: use_random_seed = 0; break; + case oNoAutoKeyRetrieve: opt.auto_key_retrieve = 0; break; default : pargs.err = configfp? 1:2; break; } @@ -946,14 +989,12 @@ main( int argc, char **argv ) fprintf(stderr, "%s\n", strusage(15) ); } #ifdef IS_DEVELOPMENT_VERSION - if( !opt.batch ) - log_info("NOTE: this is a development version!\n"); - #endif - if( opt.force_mdc ) { - log_info("--force-mdc ignored because" - " the OpenPGP WG has not yet aggreed on MDCs\n"); - opt.force_mdc = 0; + if( !opt.batch ) { + log_info("NOTE: THIS IS A DEVELOPMENT VERSION!\n"); + log_info("It is only intended for test purposes and should NOT be\n"); + log_info("used in a production environment or with production keys!\n"); } + #endif if (opt.no_literal) { log_info(_("NOTE: %s is not for normal use!\n"), "--no-literal"); if (opt.textmode) @@ -975,7 +1016,7 @@ main( int argc, char **argv ) * gpg_opt_homedir = opt.homedir; */ /* must do this after dropping setuid, because string_to... - * may try to load a module */ + * may try to load an module */ if( def_cipher_string ) { opt.def_cipher_algo = gcry_cipher_map_name(def_cipher_string); gcry_free(def_cipher_string); def_cipher_string = NULL; @@ -1025,8 +1066,19 @@ main( int argc, char **argv ) if( log_get_errorcount(0) ) gpg_exit(2); - if( !cmd && opt.fingerprint && !with_fpr ) + /* set the random seed file */ + if( use_random_seed ) { + char *p = make_filename(opt.homedir, "random_seed", NULL ); + #if 0 + #warning set_random_seed_file missing + set_random_seed_file(p); + #endif + gcry_free(p); + } + + if( !cmd && opt.fingerprint && !with_fpr ) { set_cmd( &cmd, aListKeys); + } if( cmd == aKMode || cmd == aKModeC ) { /* kludge to be compatible to pgp */ if( cmd == aKModeC ) { @@ -1077,8 +1129,8 @@ main( int argc, char **argv ) switch( cmd ) { case aPrimegen: case aPrintMD: - case aPrintHMAC: case aPrintMDs: + case aPrintHMAC: case aGenRandom: case aDeArmor: case aEnArmor: @@ -1167,6 +1219,11 @@ main( int argc, char **argv ) log_error("verify signatures failed: %s\n", gpg_errstr(rc) ); break; + case aVerifyFiles: + if( (rc = verify_files( argc, argv ) )) + log_error("verify files failed: %s\n", gpg_errstr(rc) ); + break; + case aDecrypt: if( argc > 1 ) wrong_args(_("--decrypt [filename]")); @@ -1225,16 +1282,28 @@ main( int argc, char **argv ) case aListSigs: opt.list_sigs = 1; case aListKeys: - /* fixme: we chnaged this in 1.0 */ - public_key_list( argc, argv ); + sl = NULL; + for( ; argc; argc--, argv++ ) + add_to_strlist2( &sl, *argv, utf8_strings ); + public_key_list( sl ); + free_strlist(sl); break; case aListSecretKeys: - secret_key_list( argc, argv ); + sl = NULL; + for( ; argc; argc--, argv++ ) + add_to_strlist2( &sl, *argv, utf8_strings ); + secret_key_list( sl ); + free_strlist(sl); break; case aKMode: /* list keyring -- NOTE: This will be removed soon */ - if( argc < 2 ) /* -kv [userid] */ - public_key_list( (argc && **argv)? 1:0, argv ); + if( argc < 2 ) { /* -kv [userid] */ + sl = NULL; + if (argc && **argv) + add_to_strlist2( &sl, *argv, utf8_strings ); + public_key_list( sl ); + free_strlist(sl); + } else if( argc == 2 ) { /* -kv userid keyring */ if( access( argv[1], R_OK ) ) { log_error(_("can't open %s: %s\n"), @@ -1244,32 +1313,33 @@ main( int argc, char **argv ) /* add keyring (default keyrings are not registered in this * special case */ add_keyblock_resource( argv[1], 0, 0 ); - public_key_list( **argv?1:0, argv ); + sl = NULL; + if (**argv) + add_to_strlist2( &sl, *argv, utf8_strings ); + public_key_list( sl ); + free_strlist(sl); } } else wrong_args(_("-k[v][v][v][c] [user-id] [keyring]") ); break; - case aKeygen: /* generate a key (interactive) */ - if( argc ) - wrong_args("--gen-key"); - generate_keypair(); + case aKeygen: /* generate a key */ + if( opt.batch ) { + if( argc > 1 ) + wrong_args("--gen-key [parameterfile]"); + generate_keypair( argc? *argv : NULL ); + } + else { + if( argc ) + wrong_args("--gen-key"); + generate_keypair(NULL); + } break; case aFastImport: case aImport: - if( !argc ) { - rc = import_keys( NULL, (cmd == aFastImport) ); - if( rc ) - log_error("import failed: %s\n", gpg_errstr(rc) ); - } - for( ; argc; argc--, argv++ ) { - rc = import_keys( *argv, (cmd == aFastImport) ); - if( rc ) - log_error("import from `%s' failed: %s\n", - *argv, gpg_errstr(rc) ); - } + import_keys( argc? argv:NULL, argc, (cmd == aFastImport) ); break; case aExport: @@ -1296,6 +1366,14 @@ main( int argc, char **argv ) free_strlist(sl); break; + case aExportSecretSub: + sl = NULL; + for( ; argc; argc--, argv++ ) + add_to_strlist2( &sl, *argv, utf8_strings ); + export_secsubkeys( sl ); + free_strlist(sl); + break; + case aGenRevoke: if( argc != 1 ) wrong_args("--gen-revoke user-id"); @@ -1342,7 +1420,7 @@ main( int argc, char **argv ) mpi_print( stdout, factors[0], 1 ); /* print q */ } else if( mode == 4 && argc == 3 ) { - MPI g = mpi_new(8); + MPI g = mpi_alloc(1); mpi_print( stdout, generate_elg_prime( 0, atoi(argv[1]), atoi(argv[2]), g, NULL ), 1); @@ -1424,6 +1502,7 @@ main( int argc, char **argv ) } break; + case aPrintMDs: /* old option */ if( !argc ) print_mds(NULL,0,NULL); @@ -1461,11 +1540,9 @@ main( int argc, char **argv ) break; case aFixTrustDB: - log_error("this command is not yet implemented.\"\n"); + log_error("this command is not yet implemented.\n"); log_error("A workaround is to use \"--export-ownertrust\", remove\n"); log_error("the trustdb file and do an \"--import-ownertrust\".\n" ); - #warning removed the next line - export_as_kbxfile(); break; case aListTrustPath: @@ -1533,6 +1610,10 @@ main( int argc, char **argv ) void gpg_exit( int rc ) { + #if 0 + #warning no update_random_seed_file + update_random_seed_file(); + #endif if( opt.debug & DBG_MEMSTAT_VALUE ) { gcry_control( GCRYCTL_DUMP_MEMORY_STATS ); gcry_control( GCRYCTL_DUMP_RANDOM_STATS ); @@ -1629,7 +1710,7 @@ print_mds( const char *fname, int algo, const char *key ) if( algo ) { if( fname ) fputs( pname, stdout ); - print_hex( gcry_md_read(md, algo), gcry_md_get_algo_dlen(algo) ); + print_hex(gcry_md_read(md, algo), gcry_md_get_algo_dlen(algo) ); } else { printf( "%s MD5 = ", fname?pname:"" ); diff --git a/g10/gpgd.c b/g10/gpgd.c index 99c91f6fc..8ca37d34a 100644 --- a/g10/gpgd.c +++ b/g10/gpgd.c @@ -1,4 +1,4 @@ -/* gpg.c - The GnuPG daemon (keyserver) +/* ggpd.c - The GnuPG daemon (keyserver) * Copyright (C) 1998 Free Software Foundation, Inc. * * This file is part of GnuPG. @@ -38,6 +38,7 @@ #include #include "util.h" +#include "cipher.h" #include "options.h" #include "main.h" @@ -84,7 +85,7 @@ strusage( int level ) case 33: if( !pubkeys ) pubkeys = build_list("Supported pubkeys: ", pubkey_algo_to_string, - openpgp_pk_test_algo ); + check_pubkey_algo ); p = pubkeys; break; case 34: @@ -137,7 +138,7 @@ set_debug(void) if( opt.debug & DBG_MPI_VALUE ) mpi_debug_mode = 1; if( opt.debug & DBG_CIPHER_VALUE ) - gpgc_debug_mode = 1; + g10c_debug_mode = 1; if( opt.debug & DBG_IOBUF_VALUE ) iobuf_debug_mode = 1; } diff --git a/g10/helptext.c b/g10/helptext.c index e42902512..4a7a14fde 100644 --- a/g10/helptext.c +++ b/g10/helptext.c @@ -1,5 +1,5 @@ /* helptext.c - English help texts - * Copyright (C) 1998 Free Software Foundation, Inc. + * Copyright (C) 1998, 1999, 2000 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -224,6 +224,29 @@ static struct helptexts { const char *key; const char *help; } helptexts[] = { "file (which is shown in brackets) will be used." )}, +/* revoke.c (ask_revocation_reason) */ +{ "ask_revocation_reason.code", N_( + "You should specify a reason for the certification. Depending on the\n" + "context you have the ability to choose from this list:\n" + " \"Key has been compromised\"\n" + " Use this if you have a reason to believe that unauthorized persons\n" + " got access to your secret key.\n" + " \"Key is superseeded\"\n" + " Use this if you have replaced this key with a newer one.\n" + " \"Key is no longer used\"\n" + " Use this if you have retired this key.\n" + " \"User ID is no longer valid\"\n" + " Use this to state that the user ID should not longer be used;\n" + " this is normally used to mark an email address invalid.\n" +)}, + +/* revoke.c (ask_revocation_reason) */ +{ "ask_revocation_reason.text", N_( + "If you like, you can enter a text describing why you issue this\n" + "revocation certificate. Please keep this text concise.\n" + "An empty line ends the text.\n" +)}, + /* end of list */ { NULL, NULL } }; diff --git a/g10/hkp.c b/g10/hkp.c index b60b3cc6f..2f6031361 100644 --- a/g10/hkp.c +++ b/g10/hkp.c @@ -30,7 +30,6 @@ #include "util.h" #include "ttyio.h" #include "i18n.h" -#include #include "options.h" #include "filter.h" #include "http.h" @@ -56,19 +55,24 @@ hkp_ask_import( u32 *keyid ) struct http_context hd; char *request; int rc; + unsigned int hflags = opt.honor_http_proxy? HTTP_FLAG_TRY_PROXY : 0; if( !opt.keyserver_name ) return -1; - log_info("requesting key %08lX from %s ...\n", (ulong)keyid[1], + log_info(_("requesting key %08lX from %s ...\n"), (ulong)keyid[1], opt.keyserver_name ); request = gcry_xmalloc( strlen( opt.keyserver_name ) + 100 ); /* hkp does not accept the long keyid - we should really write a - * nicer one */ + * nicer one :-) + * FIXME: request binary mode - need to pass no_armor mode + * down to the import function. Marc told that there is such a + * binary mode ... how? + */ sprintf( request, "x-hkp://%s:11371/pks/lookup?op=get&search=0x%08lX", opt.keyserver_name, (ulong)keyid[1] ); - rc = http_open_document( &hd, request, 0 ); + rc = http_open_document( &hd, request, hflags ); if( rc ) { - log_info("can't get key from keyserver: %s\n", + log_info(_("can't get key from keyserver: %s\n"), rc == GPGERR_NETWORK? strerror(errno) : gpg_errstr(rc) ); } @@ -91,7 +95,7 @@ hkp_import( STRLIST users ) return -1; #else if( !opt.keyserver_name ) { - log_error("no keyserver known (use option --keyserver)\n"); + log_error(_("no keyserver known (use option --keyserver)\n")); return -1; } @@ -99,10 +103,15 @@ hkp_import( STRLIST users ) u32 kid[2]; int type = classify_user_id( users->d, kid, NULL, NULL, NULL ); if( type != 10 && type != 11 ) { - log_info("%s: not a valid key ID\n", users->d ); + log_info(_("%s: not a valid key ID\n"), users->d ); continue; } - hkp_ask_import( kid ); + /* because the function may use log_info in some situations, the + * errorcounter ist not increaed and the program will return + * with success - which is not good when this function is used. + */ + if( hkp_ask_import( kid ) ) + log_inc_errorcount(); } return 0; #endif @@ -121,9 +130,10 @@ hkp_export( STRLIST users ) struct http_context hd; char *request; unsigned int status; + unsigned int hflags = opt.honor_http_proxy? HTTP_FLAG_TRY_PROXY : 0; if( !opt.keyserver_name ) { - log_error("no keyserver known (use option --keyserver)\n"); + log_error(_("no keyserver known (use option --keyserver)\n")); return -1; } @@ -143,9 +153,9 @@ hkp_export( STRLIST users ) request = gcry_xmalloc( strlen( opt.keyserver_name ) + 100 ); sprintf( request, "x-hkp://%s:11371/pks/add", opt.keyserver_name ); - rc = http_open( &hd, HTTP_REQ_POST, request , 0 ); + rc = http_open( &hd, HTTP_REQ_POST, request , hflags ); if( rc ) { - log_error("can't connect to `%s': %s\n", + log_error(_("can't connect to `%s': %s\n"), opt.keyserver_name, rc == GPGERR_NETWORK? strerror(errno) : gpg_errstr(rc) ); @@ -169,7 +179,7 @@ hkp_export( STRLIST users ) rc = http_wait_response( &hd, &status ); if( rc ) { - log_error("error sending to `%s': %s\n", + log_error(_("error sending to `%s': %s\n"), opt.keyserver_name, gpg_errstr(rc) ); } else { @@ -181,10 +191,10 @@ hkp_export( STRLIST users ) } #endif if( (status/100) == 2 ) - log_info("success sending to `%s' (status=%u)\n", + log_info(_("success sending to `%s' (status=%u)\n"), opt.keyserver_name, status ); else - log_error("failed sending to `%s': status=%u\n", + log_error(_("failed sending to `%s': status=%u\n"), opt.keyserver_name, status ); } http_close( &hd ); diff --git a/g10/hkp.h b/g10/hkp.h index a7b78da96..64f049739 100644 --- a/g10/hkp.h +++ b/g10/hkp.h @@ -1,5 +1,5 @@ /* hkp.h - Horrowitz Keyserver Protocol - * Copyright (C) 1999 Free Software Foundation, Inc. + * Copyright (C) 1999, 2000 Free Software Foundation, Inc. * * This file is part of GnuPG. * diff --git a/g10/import.c b/g10/import.c index 731351275..578082577 100644 --- a/g10/import.c +++ b/g10/import.c @@ -1,5 +1,5 @@ /* import.c - * Copyright (C) 1998, 1999 Free Software Foundation, Inc. + * Copyright (C) 1998, 1999, 2000 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -38,6 +38,7 @@ static struct { + ulong count; ulong no_user_id; ulong imported; ulong imported_rsa; @@ -53,6 +54,7 @@ static struct { static int import( IOBUF inp, int fast, const char* fname ); +static void print_stats(void); static int read_block( IOBUF a, PACKET **pending_pkt, KBNODE *ret_root ); static int import_one( const char *fname, KBNODE keyblock, int fast ); static int import_secret_one( const char *fname, KBNODE keyblock ); @@ -105,30 +107,51 @@ static int merge_keysigs( KBNODE dst, KBNODE src, int *n_sigs, * Key revocation certificates have special handling. * */ -int -import_keys( const char *fname, int fast ) +void +import_keys( char **fnames, int nnames, int fast ) { - IOBUF inp = NULL; - int rc; + int i; - inp = iobuf_open(fname); - if( !fname ) - fname = "[stdin]"; - if( !inp ) { - log_error(_("can't open `%s': %s\n"), fname, strerror(errno) ); - return GPGERR_OPEN_FILE; + /* fixme: don't use static variables */ + memset( &stats, 0, sizeof( stats ) ); + + if( !fnames && !nnames ) + nnames = 1; /* Ohh what a ugly hack to jump into the loop */ + + for(i=0; i < nnames; i++ ) { + const char *fname = fnames? fnames[i] : NULL; + IOBUF inp = iobuf_open(fname); + if( !fname ) + fname = "[stdin]"; + if( !inp ) + log_error(_("can't open `%s': %s\n"), fname, strerror(errno) ); + else { + int rc = import( inp, fast, fname ); + iobuf_close(inp); + if( rc ) + log_error("import from `%s' failed: %s\n", fname, + gpg_errstr(rc) ); + } + if( !fname ) + break; } - - rc = import( inp, fast, fname ); - - iobuf_close(inp); - return rc; + print_stats(); + if( !fast ) + sync_trustdb(); } int import_keys_stream( IOBUF inp, int fast ) { - return import( inp, fast, "[stream]" ); + int rc = 0; + + /* fixme: don't use static variables */ + memset( &stats, 0, sizeof( stats ) ); + rc = import( inp, fast, "[stream]" ); + print_stats(); + if( !fast ) + sync_trustdb(); + return rc; } static int @@ -137,10 +160,6 @@ import( IOBUF inp, int fast, const char* fname ) PACKET *pending_pkt = NULL; KBNODE keyblock; int rc = 0; - ulong count=0; - - /* fixme: don't use static variables */ - memset( &stats, 0, sizeof( stats ) ); getkey_disable_caches(); @@ -165,16 +184,23 @@ import( IOBUF inp, int fast, const char* fname ) release_kbnode(keyblock); if( rc ) break; - if( !(++count % 100) && !opt.quiet ) - log_info(_("%lu keys so far processed\n"), count ); + if( !(++stats.count % 100) && !opt.quiet ) + log_info(_("%lu keys so far processed\n"), stats.count ); } if( rc == -1 ) rc = 0; else if( rc && rc != GPGERR_INV_KEYRING ) log_error( _("error reading `%s': %s\n"), fname, gpg_errstr(rc)); + return rc; +} + + +static void +print_stats() +{ if( !opt.quiet ) { - log_info(_("Total number processed: %lu\n"), count ); + log_info(_("Total number processed: %lu\n"), stats.count ); if( stats.no_user_id ) log_info(_(" w/o user IDs: %lu\n"), stats.no_user_id ); if( stats.imported || stats.imported_rsa ) { @@ -202,9 +228,9 @@ import( IOBUF inp, int fast, const char* fname ) } if( is_status_enabled() ) { - char buf[12*16]; + char buf[12*20]; sprintf(buf, "%lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu", - count, + stats.count, stats.no_user_id, stats.imported, stats.imported_rsa, @@ -218,8 +244,6 @@ import( IOBUF inp, int fast, const char* fname ) stats.secret_dups); write_status_text( STATUS_IMPORT_RES, buf ); } - - return rc; } @@ -354,8 +378,8 @@ import_one( const char *fname, KBNODE keyblock, int fast ) pubkey_letter( pk->pubkey_algo ), (ulong)keyid[1], datestr_from_pk(pk) ); if( uidnode ) - print_string( stderr, uidnode->pkt->pkt.user_id->name, - uidnode->pkt->pkt.user_id->len, 0 ); + print_utf8_string( stderr, uidnode->pkt->pkt.user_id->name, + uidnode->pkt->pkt.user_id->len ); putc('\n', stderr); } if( !uidnode ) { @@ -545,8 +569,8 @@ import_secret_one( const char *fname, KBNODE keyblock ) pubkey_letter( sk->pubkey_algo ), (ulong)keyid[1], datestr_from_sk(sk) ); if( uidnode ) - print_string( stderr, uidnode->pkt->pkt.user_id->name, - uidnode->pkt->pkt.user_id->len, 0 ); + print_utf8_string( stderr, uidnode->pkt->pkt.user_id->name, + uidnode->pkt->pkt.user_id->len ); putc('\n', stderr); } stats.secret_read++; @@ -678,6 +702,15 @@ import_revoke_cert( const char *fname, KBNODE node ) log_info( _("key %08lX: revocation certificate imported\n"), (ulong)keyid[1]); stats.n_revoc++; + if( clear_trust_checked_flag( pk ) ) { + /* seems that we have to insert the record first */ + rc = insert_trust_record( keyblock ); + if( rc ) + log_error("key %08lX: trustdb insert failed: %s\n", + (ulong)keyid[1], gpg_errstr(rc) ); + else + rc = clear_trust_checked_flag( pk ); + } leave: release_kbnode( keyblock ); @@ -764,7 +797,8 @@ mark_non_selfsigned_uids_valid( KBNODE keyblock, u32 *kid ) KBNODE node; for(node=keyblock->next; node; node = node->next ) { if( node->pkt->pkttype == PKT_USER_ID && !(node->flag & 1) ) { - if( node->next && node->next->pkt->pkttype == PKT_SIGNATURE ) { + if( (node->next && node->next->pkt->pkttype == PKT_SIGNATURE) + || !node->next ) { node->flag |= 1; log_info( _("key %08lX: accepted non self-signed user ID '"), (ulong)kid[1]); @@ -797,8 +831,8 @@ delete_inv_parts( const char *fname, KBNODE keyblock, u32 *keyid ) if( opt.verbose ) { log_info( _("key %08lX: skipped user ID '"), (ulong)keyid[1]); - print_string( stderr, node->pkt->pkt.user_id->name, - node->pkt->pkt.user_id->len, 0 ); + print_utf8_string( stderr, node->pkt->pkt.user_id->name, + node->pkt->pkt.user_id->len ); fputs("'\n", stderr ); } delete_kbnode( node ); /* the user-id */ @@ -831,7 +865,7 @@ delete_inv_parts( const char *fname, KBNODE keyblock, u32 *keyid ) } } else if( node->pkt->pkttype == PKT_SIGNATURE - && openpgp_pk_test_algo( node->pkt->pkt.signature->pubkey_algo, 0) + && openpgp_pk_test_algo( node->pkt->pkt.signature->pubkey_algo, 0 ) && node->pkt->pkt.signature->pubkey_algo != GCRY_PK_RSA ) delete_kbnode( node ); /* build_packet() can't handle this */ else if( node->pkt->pkttype == PKT_SIGNATURE diff --git a/g10/kbnode.c b/g10/kbnode.c index 79c1386aa..2c1e2ad3c 100644 --- a/g10/kbnode.c +++ b/g10/kbnode.c @@ -1,5 +1,5 @@ /* kbnode.c - keyblock node utility functions - * Copyright (C) 1998 Free Software Foundation, Inc. + * Copyright (C) 1998, 1999, 2000 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -23,8 +23,9 @@ #include #include #include -#include "util.h" + #include +#include "util.h" #include "packet.h" #include "keydb.h" @@ -165,7 +166,7 @@ find_prev_kbnode( KBNODE root, KBNODE node, int pkttype ) KBNODE n1; for(n1=NULL ; root && root != node; root = root->next ) - if( !pkttype || root->pkt->pkttype == pkttype ) + if( !pkttype || root->pkt->pkttype == pkttype ) n1 = root; return n1; } diff --git a/g10/kbxblob.c b/g10/kbxblob.c index c298d587f..01d0dfe10 100644 --- a/g10/kbxblob.c +++ b/g10/kbxblob.c @@ -511,7 +511,7 @@ kbx_create_blob ( KBXBLOB *r_blob, KBNODE keyblock ) KBXBLOB blob; *r_blob = NULL; - blob = gcry_calloc (1, sizeof *blob ); + blob = gcry_xcalloc (1, sizeof *blob ); if( !blob ) return GCRYERR_NO_MEM; @@ -529,9 +529,9 @@ kbx_create_blob ( KBXBLOB *r_blob, KBNODE keyblock ) default: break; } } - blob->keys = gcry_calloc ( blob->nkeys, sizeof ( *blob->keys ) ); - blob->uids = gcry_calloc ( blob->nuids, sizeof ( *blob->uids ) ); - blob->sigs = gcry_calloc ( blob->nsigs, sizeof ( *blob->sigs ) ); + blob->keys = gcry_xcalloc ( blob->nkeys, sizeof ( *blob->keys ) ); + blob->uids = gcry_xcalloc ( blob->nuids, sizeof ( *blob->uids ) ); + blob->sigs = gcry_xcalloc ( blob->nsigs, sizeof ( *blob->sigs ) ); if ( !blob->keys || !blob->uids || !blob->sigs ) { rc = GCRYERR_NO_MEM; goto leave; @@ -581,7 +581,7 @@ kbx_new_blob ( KBXBLOB *r_blob, char *image, size_t imagelen ) KBXBLOB blob; *r_blob = NULL; - blob = gcry_calloc (1, sizeof *blob ); + blob = gcry_xcalloc (1, sizeof *blob ); if( !blob ) return GCRYERR_NO_MEM; blob->blob = image; diff --git a/g10/kbxfile.c b/g10/kbxfile.c index 7bf615098..457fb1716 100644 --- a/g10/kbxfile.c +++ b/g10/kbxfile.c @@ -321,7 +321,7 @@ print_kbxfile( const char *filename ) fp = fopen ( filename, "rb" ); if( !fp ) { log_error(_("can't open `%s': %s\n"), filename, strerror(errno) ); - return 1; + return; } while ( !do_print_kbxfile( filename, fp ) ) diff --git a/g10/keydb.h b/g10/keydb.h index 05aac77d3..e0af39aa0 100644 --- a/g10/keydb.h +++ b/g10/keydb.h @@ -1,5 +1,5 @@ /* keydb.h - Key database - * Copyright (C) 1998 Free Software Foundation, Inc. + * Copyright (C) 1998, 1999, 2000 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -25,6 +25,7 @@ #include #endif +#include "types.h" #include "basicdefs.h" #include "packet.h" @@ -156,6 +157,7 @@ void get_seckey_end( GETKEY_CTX ctx ); int enum_secret_keys( void **context, PKT_secret_key *sk, int with_subkeys ); void merge_keys_and_selfsig( KBNODE keyblock ); char*get_user_id_string( u32 *keyid ); +char*get_user_id_string_native( u32 *keyid ); char*get_long_user_id_string( u32 *keyid ); char*get_user_id( u32 *keyid, size_t *rn ); @@ -197,6 +199,7 @@ const char *enum_keyblock_resources( int *sequence, int secret ); int add_keyblock_resource( const char *resname, int force, int secret ); const char *keyblock_resource_name( KBPOS *kbpos ); int get_keyblock_handle( const char *filename, int secret, KBPOS *kbpos ); +char *get_writable_keyblock_file( int secret ); int locate_keyblock_by_fpr( KBPOS *kbpos, const byte *fpr, int fprlen, int secret ); int locate_keyblock_by_keyid( KBPOS *kbpos, u32 *keyid, diff --git a/g10/keyedit.c b/g10/keyedit.c index fdb8bf6ad..a64c7e86f 100644 --- a/g10/keyedit.c +++ b/g10/keyedit.c @@ -1,5 +1,5 @@ /* keyedit.c - keyedit stuff - * Copyright (C) 1998, 1999 Free Software Foundation, Inc. + * Copyright (C) 1998, 1999, 2000 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -74,8 +74,9 @@ static int enable_disable_key( KBNODE keyblock, int disable ); #define NODFLG_SELSIG (1<<10) /* indicate a selected signature */ -struct sign_uid_attrib { +struct sign_attrib { int non_exportable; + struct revocation_reason_info *reason; }; @@ -157,7 +158,7 @@ print_and_check_one_sig( KBNODE keyblock, KBNODE node, else { size_t n; char *p = get_user_id( sig->keyid, &n ); - tty_print_utf8_string( p, n > 40? 40 : n ); + tty_print_utf8_string2( p, n, 40 ); gcry_free(p); } tty_printf("\n"); @@ -239,16 +240,18 @@ check_all_keysigs( KBNODE keyblock, int only_selected ) -int -sign_uid_mk_attrib( PKT_signature *sig, void *opaque ) +static int +sign_mk_attrib( PKT_signature *sig, void *opaque ) { - struct sign_uid_attrib *attrib = opaque; + struct sign_attrib *attrib = opaque; byte buf[8]; if( attrib->non_exportable ) { buf[0] = 0; /* not exportable */ build_sig_subpkt( sig, SIGSUBPKT_EXPORTABLE, buf, 1 ); } + if( attrib->reason ) + revocation_reason_build_cb( sig, attrib->reason ); return 0; } @@ -353,7 +356,7 @@ sign_uids( KBNODE keyblock, STRLIST locusr, int *ret_modified, int local ) && (node->flag & NODFLG_MARK_A) ) { PACKET *pkt; PKT_signature *sig; - struct sign_uid_attrib attrib; + struct sign_attrib attrib; assert( primary_pk ); memset( &attrib, 0, sizeof attrib ); @@ -364,7 +367,7 @@ sign_uids( KBNODE keyblock, STRLIST locusr, int *ret_modified, int local ) NULL, sk, 0x10, 0, - sign_uid_mk_attrib, + sign_mk_attrib, &attrib ); if( rc ) { log_error(_("signing failed: %s\n"), gpg_errstr(rc)); @@ -407,6 +410,7 @@ change_passphrase( KBNODE keyblock ) KBNODE node; PKT_secret_key *sk; char *passphrase = NULL; + int no_primary_secrets = 0; node = find_kbnode( keyblock, PKT_SECRET_KEY ); if( !node ) { @@ -423,10 +427,16 @@ change_passphrase( KBNODE keyblock ) tty_printf(_("This key is not protected.\n")); break; default: - tty_printf(_("Key is protected.\n")); - rc = check_secret_key( sk, 0 ); - if( !rc ) - passphrase = get_last_passphrase(); + if( sk->protect.s2k.mode == 1001 ) { + tty_printf(_("Secret parts of primary key are not available.\n")); + no_primary_secrets = 1; + } + else { + tty_printf(_("Key is protected.\n")); + rc = check_secret_key( sk, 0 ); + if( !rc ) + passphrase = get_last_passphrase(); + } break; } @@ -436,6 +446,8 @@ change_passphrase( KBNODE keyblock ) PKT_secret_key *subsk = node->pkt->pkt.secret_key; set_next_passphrase( passphrase ); rc = check_secret_key( subsk, 0 ); + if( !rc && !passphrase ) + passphrase = get_last_passphrase(); } } @@ -465,9 +477,12 @@ change_passphrase( KBNODE keyblock ) break; } else { /* okay */ - sk->protect.algo = dek->algo; - sk->protect.s2k = *s2k; - rc = protect_secret_key( sk, dek ); + rc = 0; + if( !no_primary_secrets ) { + sk->protect.algo = dek->algo; + sk->protect.s2k = *s2k; + rc = protect_secret_key( sk, dek ); + } for(node=keyblock; !rc && node; node = node->next ) { if( node->pkt->pkttype == PKT_SECRET_SUBKEY ) { PKT_secret_key *subsk = node->pkt->pkt.secret_key; @@ -558,41 +573,42 @@ keyedit_menu( const char *username, STRLIST locusr, STRLIST commands, static struct { const char *name; enum cmdids id; int need_sk; + int not_with_sk; int signmode; const char *desc; } cmds[] = { - { N_("quit") , cmdQUIT , 0,1, N_("quit this menu") }, - { N_("q") , cmdQUIT , 0,1, NULL }, - { N_("save") , cmdSAVE , 0,1, N_("save and quit") }, - { N_("help") , cmdHELP , 0,1, N_("show this help") }, - { "?" , cmdHELP , 0,1, NULL }, - { N_("fpr") , cmdFPR , 0,1, N_("show fingerprint") }, - { N_("list") , cmdLIST , 0,1, N_("list key and user IDs") }, - { N_("l") , cmdLIST , 0,1, NULL }, - { N_("uid") , cmdSELUID , 0,1, N_("select user ID N") }, - { N_("key") , cmdSELKEY , 0,0, N_("select secondary key N") }, - { N_("check") , cmdCHECK , 0,1, N_("list signatures") }, - { N_("c") , cmdCHECK , 0,1, NULL }, - { N_("sign") , cmdSIGN , 0,1, N_("sign the key") }, - { N_("s") , cmdSIGN , 0,1, NULL }, - { N_("lsign") , cmdLSIGN , 0,1, N_("sign the key locally") }, - { N_("debug") , cmdDEBUG , 0,0, NULL }, - { N_("adduid") , cmdADDUID , 1,0, N_("add a user ID") }, - { N_("deluid") , cmdDELUID , 0,0, N_("delete user ID") }, - { N_("addkey") , cmdADDKEY , 1,0, N_("add a secondary key") }, - { N_("delkey") , cmdDELKEY , 0,0, N_("delete a secondary key") }, - { N_("delsig") , cmdDELSIG , 0,0, N_("delete signatures") }, - { N_("expire") , cmdEXPIRE , 1,0, N_("change the expire date") }, - { N_("toggle") , cmdTOGGLE , 1,0, N_("toggle between secret " - "and public key listing") }, - { N_("t" ) , cmdTOGGLE , 1,0, NULL }, - { N_("pref") , cmdPREF , 0,0, N_("list preferences") }, - { N_("passwd") , cmdPASSWD , 1,0, N_("change the passphrase") }, - { N_("trust") , cmdTRUST , 0,0, N_("change the ownertrust") }, - { N_("revsig") , cmdREVSIG , 0,0, N_("revoke signatures") }, - { N_("revkey") , cmdREVKEY , 1,0, N_("revoke a secondary key") }, - { N_("disable") , cmdDISABLEKEY, 0,0, N_("disable a key") }, - { N_("enable") , cmdENABLEKEY , 0,0, N_("enable a key") }, + { N_("quit") , cmdQUIT , 0,0,1, N_("quit this menu") }, + { N_("q") , cmdQUIT , 0,0,1, NULL }, + { N_("save") , cmdSAVE , 0,0,1, N_("save and quit") }, + { N_("help") , cmdHELP , 0,0,1, N_("show this help") }, + { "?" , cmdHELP , 0,0,1, NULL }, + { N_("fpr") , cmdFPR , 0,0,1, N_("show fingerprint") }, + { N_("list") , cmdLIST , 0,0,1, N_("list key and user IDs") }, + { N_("l") , cmdLIST , 0,0,1, NULL }, + { N_("uid") , cmdSELUID , 0,0,1, N_("select user ID N") }, + { N_("key") , cmdSELKEY , 0,0,0, N_("select secondary key N") }, + { N_("check") , cmdCHECK , 0,0,1, N_("list signatures") }, + { N_("c") , cmdCHECK , 0,0,1, NULL }, + { N_("sign") , cmdSIGN , 0,1,1, N_("sign the key") }, + { N_("s") , cmdSIGN , 0,1,1, NULL }, + { N_("lsign") , cmdLSIGN , 0,1,1, N_("sign the key locally") }, + { N_("debug") , cmdDEBUG , 0,1,0, NULL }, + { N_("adduid") , cmdADDUID , 1,1,0, N_("add a user ID") }, + { N_("deluid") , cmdDELUID , 0,1,0, N_("delete user ID") }, + { N_("addkey") , cmdADDKEY , 1,1,0, N_("add a secondary key") }, + { N_("delkey") , cmdDELKEY , 0,1,0, N_("delete a secondary key") }, + { N_("delsig") , cmdDELSIG , 0,1,0, N_("delete signatures") }, + { N_("expire") , cmdEXPIRE , 1,1,0, N_("change the expire date") }, + { N_("toggle") , cmdTOGGLE , 1,0,0, N_("toggle between secret " + "and public key listing") }, + { N_("t" ) , cmdTOGGLE , 1,0,0, NULL }, + { N_("pref") , cmdPREF , 0,1,0, N_("list preferences") }, + { N_("passwd") , cmdPASSWD , 1,1,0, N_("change the passphrase") }, + { N_("trust") , cmdTRUST , 0,1,0, N_("change the ownertrust") }, + { N_("revsig") , cmdREVSIG , 0,1,0, N_("revoke signatures") }, + { N_("revkey") , cmdREVKEY , 1,1,0, N_("revoke a secondary key") }, + { N_("disable") , cmdDISABLEKEY, 0,1,0, N_("disable a key") }, + { N_("enable") , cmdENABLEKEY , 0,1,0, N_("enable a key") }, { NULL, cmdNONE } }; enum cmdids cmd = 0; @@ -678,7 +694,7 @@ keyedit_menu( const char *username, STRLIST locusr, STRLIST commands, have_commands = 0; } if( !have_commands ) { - answer = cpr_get("", _("Command> ")); + answer = cpr_get("keyedit.prompt", _("Command> ")); cpr_kill_prompt(); } trim_spaces(answer); @@ -711,6 +727,10 @@ keyedit_menu( const char *username, STRLIST locusr, STRLIST commands, tty_printf(_("Need the secret key to do this.\n")); cmd = cmdNOP; } + else if( cmds[i].not_with_sk && sec_keyblock && toggle ) { + tty_printf(_("Please use the command \"toggle\" first.\n")); + cmd = cmdNOP; + } else cmd = cmds[i].id; } @@ -763,6 +783,11 @@ keyedit_menu( const char *username, STRLIST locusr, STRLIST commands, if( !sign_uids( keyblock, locusr, &modified, cmd == cmdLSIGN ) && sign_mode ) goto do_cmd_save; + /* Actually we should do a update_trust_record() here so that + * the trust gets displayed correctly. however this is not possible + * because we would have to save the keyblock first - something + * we don't want to do without an explicit save command. + */ break; case cmdDEBUG: @@ -1005,7 +1030,13 @@ show_prefs( KBNODE keyblock, PKT_user_id *uid ) return; } - gcry_md_hash_buffer( GCRY_MD_RMD160, namehash, uid->name, uid->len ); + if( uid->photo ) { + gcry_md_hash_buffer( GCRY_MD_RMD160, namehash, uid->photo, + uid->photolen ); + } + else { + gcry_md_hash_buffer( GCRY_MD_RMD160, namehash, uid->name, uid->len ); + } p = get_pref_data( pk->local_id, namehash, &n ); if( !p ) @@ -1049,7 +1080,7 @@ show_key_with_all_names( KBNODE keyblock, int only_marked, otrust = get_ownertrust_info( pk->local_id ); } - tty_printf("%s%c %4u%c/%08lX created: %s expires: %s", + tty_printf(_("%s%c %4u%c/%08lX created: %s expires: %s"), node->pkt->pkttype == PKT_PUBLIC_KEY? "pub":"sub", (node->flag & NODFLG_SELKEY)? '*':' ', nbits_from_pk( pk ), @@ -1058,7 +1089,7 @@ show_key_with_all_names( KBNODE keyblock, int only_marked, datestr_from_pk(pk), expirestr_from_pk(pk) ); if( node->pkt->pkttype == PKT_PUBLIC_KEY ) { - tty_printf(" trust: %c/%c", otrust, trust ); + tty_printf(_(" trust: %c/%c"), otrust, trust ); if( node->pkt->pkttype == PKT_PUBLIC_KEY && (get_ownertrust( pk->local_id )&TRUST_FLAG_DISABLED)) { tty_printf("\n*** "); @@ -1075,14 +1106,15 @@ show_key_with_all_names( KBNODE keyblock, int only_marked, else if( node->pkt->pkttype == PKT_SECRET_KEY || (with_subkeys && node->pkt->pkttype == PKT_SECRET_SUBKEY) ) { PKT_secret_key *sk = node->pkt->pkt.secret_key; - tty_printf("%s%c %4u%c/%08lX created: %s expires: %s\n", - node->pkt->pkttype == PKT_SECRET_KEY? "sec":"sbb", + tty_printf(_("%s%c %4u%c/%08lX created: %s expires: %s"), + node->pkt->pkttype == PKT_SECRET_KEY? "sec":"ssb", (node->flag & NODFLG_SELKEY)? '*':' ', nbits_from_sk( sk ), pubkey_letter( sk->pubkey_algo ), (ulong)keyid_from_sk(sk,NULL), datestr_from_sk(sk), expirestr_from_sk(sk) ); + tty_printf("\n"); } else if( with_subkeys && node->pkt->pkttype == PKT_SIGNATURE && node->pkt->pkt.signature->sig_class == 0x28 ) { @@ -1090,12 +1122,12 @@ show_key_with_all_names( KBNODE keyblock, int only_marked, rc = check_key_signature( keyblock, node, NULL ); if( !rc ) - tty_printf( "rev! subkey has been revoked: %s\n", + tty_printf( _("rev! subkey has been revoked: %s\n"), datestr_from_sig( sig ) ); else if( rc == GPGERR_BAD_SIGN ) - tty_printf( "rev- faked revocation found\n" ); + tty_printf( _("rev- faked revocation found\n") ); else if( rc ) - tty_printf( "rev? problem checking revocation: %s\n", + tty_printf( _("rev? problem checking revocation: %s\n"), gpg_errstr(rc) ); } } @@ -1156,7 +1188,7 @@ show_fingerprint( PKT_public_key *pk ) fingerprint_from_pk( pk, array, &n ); p = array; - tty_printf(" Fingerprint:"); + tty_printf(_(" Fingerprint:")); if( n == 20 ) { for(i=0; i < n ; i++, i++, p += 2 ) { if( i == 10 ) @@ -1471,7 +1503,8 @@ menu_expire( KBNODE pub_keyblock, KBNODE sec_keyblock ) } else if( node->pkt->pkttype == PKT_USER_ID ) uid = node->pkt->pkt.user_id; - else if( main_pk && node->pkt->pkttype == PKT_SIGNATURE ) { + else if( main_pk && node->pkt->pkttype == PKT_SIGNATURE + && sub_pk != NULL ) { PKT_signature *sig = node->pkt->pkt.signature; if( keyid[0] == sig->keyid[0] && keyid[1] == sig->keyid[1] && ( (mainkey && uid && (sig->sig_class&~3) == 0x10) @@ -1535,6 +1568,7 @@ menu_expire( KBNODE pub_keyblock, KBNODE sec_keyblock ) gcry_free( sn->pkt ); sn->pkt = newpkt; } + sub_pk = NULL; } } } @@ -1735,6 +1769,7 @@ menu_revsig( KBNODE keyblock ) int changed = 0; int upd_trust = 0; int rc, any; + struct revocation_reason_info *reason = NULL; /* FIXME: detect duplicates here */ tty_printf(_("You have signed these user IDs:\n")); @@ -1797,6 +1832,10 @@ menu_revsig( KBNODE keyblock ) _("Really create the revocation certificates? (y/N)")) ) return 0; /* forget it */ + reason = ask_revocation_reason( 0, 1, 0 ); + if( !reason ) { /* user decided to cancel */ + return 0; + } /* now we can sign the user ids */ reloop: /* (must use this, because we are modifing the list) */ @@ -1804,7 +1843,7 @@ menu_revsig( KBNODE keyblock ) for( node=keyblock; node; node = node->next ) { KBNODE unode; PACKET *pkt; - struct sign_uid_attrib attrib; + struct sign_attrib attrib; PKT_secret_key *sk; if( !(node->flag & NODFLG_MARK_A) @@ -1814,8 +1853,10 @@ menu_revsig( KBNODE keyblock ) assert( unode ); /* we already checked this */ memset( &attrib, 0, sizeof attrib ); + attrib.reason = reason; + node->flag &= ~NODFLG_MARK_A; - sk = gcry_xcalloc_secure( 1, sizeof *sk ); + sk = gcry_xcalloc_secure( 1, sizeof *sk ); if( get_seckey( sk, node->pkt->pkt.signature->keyid ) ) { log_info(_("no secret key\n")); continue; @@ -1825,11 +1866,12 @@ menu_revsig( KBNODE keyblock ) NULL, sk, 0x30, 0, - sign_uid_mk_attrib, + sign_mk_attrib, &attrib ); free_secret_key(sk); if( rc ) { log_error(_("signing failed: %s\n"), gpg_errstr(rc)); + release_revocation_reason_info( reason ); return changed; } changed = 1; /* we changed the keyblock */ @@ -1844,7 +1886,7 @@ menu_revsig( KBNODE keyblock ) if( upd_trust ) clear_trust_checked_flag( primary_pk ); - + release_revocation_reason_info( reason ); return changed; } @@ -1861,6 +1903,13 @@ menu_revkey( KBNODE pub_keyblock, KBNODE sec_keyblock ) int changed = 0; int upd_trust = 0; int rc; + struct revocation_reason_info *reason = NULL; + + reason = ask_revocation_reason( 1, 0, 0 ); + if( !reason ) { /* user decided to cancel */ + return 0; + } + reloop: /* (better this way because we are modifing the keyring) */ mainpk = pub_keyblock->pkt->pkt.public_key; @@ -1871,14 +1920,20 @@ menu_revkey( KBNODE pub_keyblock, KBNODE sec_keyblock ) PKT_signature *sig; PKT_secret_key *sk; PKT_public_key *subpk = node->pkt->pkt.public_key; + struct sign_attrib attrib; + + memset( &attrib, 0, sizeof attrib ); + attrib.reason = reason; node->flag &= ~NODFLG_SELKEY; sk = copy_secret_key( NULL, sec_keyblock->pkt->pkt.secret_key ); rc = make_keysig_packet( &sig, mainpk, NULL, subpk, sk, 0x28, 0, - NULL, NULL ); + sign_mk_attrib, + &attrib ); free_secret_key(sk); if( rc ) { log_error(_("signing failed: %s\n"), gpg_errstr(rc)); + release_revocation_reason_info( reason ); return changed; } changed = 1; /* we changed the keyblock */ @@ -1897,6 +1952,7 @@ menu_revkey( KBNODE pub_keyblock, KBNODE sec_keyblock ) if( upd_trust ) clear_trust_checked_flag( mainpk ); + release_revocation_reason_info( reason ); return changed; } diff --git a/g10/keygen.c b/g10/keygen.c index af288b3aa..b6c6cc7e3 100644 --- a/g10/keygen.c +++ b/g10/keygen.c @@ -1,5 +1,5 @@ /* keygen.c - generate a key pair - * Copyright (C) 1998, 1999 Free Software Foundation, Inc. + * Copyright (C) 1998, 1999, 2000 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -35,6 +35,58 @@ #include "status.h" #include "i18n.h" +enum para_name { + pKEYTYPE, + pKEYLENGTH, + pSUBKEYTYPE, + pSUBKEYLENGTH, + pNAMEREAL, + pNAMEEMAIL, + pNAMECOMMENT, + pUSERID, + pEXPIREDATE, + pKEYEXPIRE, /* in n seconds */ + pSUBKEYEXPIRE, /* in n seconds */ + pPASSPHRASE, + pPASSPHRASE_DEK, + pPASSPHRASE_S2K +}; + +struct para_data_s { + struct para_data_s *next; + int lnr; + enum para_name key; + union { + DEK *dek; + STRING2KEY *s2k; + u32 expire; + char value[1]; + } u; +}; + +struct output_control_s { + int lnr; + int dryrun; + int use_files; + struct { + char *fname; + char *newfname; + IOBUF stream; + armor_filter_context_t afx; + } pub; + struct { + char *fname; + char *newfname; + IOBUF stream; + armor_filter_context_t afx; + } sec; +}; + + +static void do_generate_keypair( struct para_data_s *para, + struct output_control_s *outctrl ); +static int write_keyblock( IOBUF out, KBNODE node ); + static void write_uid( KBNODE root, const char *s ) @@ -43,7 +95,7 @@ write_uid( KBNODE root, const char *s ) size_t n = strlen(s); pkt->pkttype = PKT_USER_ID; - pkt->pkt.user_id = gcry_xmalloc( sizeof *pkt->pkt.user_id + n - 1 ); + pkt->pkt.user_id = gcry_xcalloc( 1, sizeof *pkt->pkt.user_id + n - 1 ); pkt->pkt.user_id->len = n; strcpy(pkt->pkt.user_id->name, s); add_kbnode( root, new_kbnode( pkt ) ); @@ -84,8 +136,9 @@ keygen_add_std_prefs( PKT_signature *sig, void *opaque ) keygen_add_key_expire( sig, opaque ); buf[0] = GCRY_CIPHER_TWOFISH; - buf[1] = GCRY_CIPHER_CAST5; - build_sig_subpkt( sig, SIGSUBPKT_PREF_SYM, buf, 2 ); + buf[1] = GCRY_CIPHER_BLOWFISH; + buf[2] = GCRY_CIPHER_CAST5; + build_sig_subpkt( sig, SIGSUBPKT_PREF_SYM, buf, 3 ); buf[0] = GCRY_MD_RMD160; buf[1] = GCRY_MD_SHA1; @@ -189,6 +242,7 @@ write_keybinding( KBNODE root, KBNODE pub_root, PKT_secret_key *sk ) } + static int key_from_sexp( GCRY_MPI *array, GCRY_SEXP sexp, const char *topname, const char *elems ) @@ -252,7 +306,7 @@ factors_from_sexp( MPI **retarray, GCRY_SEXP sexp ) for( n=0; (l2 = gcry_sexp_enum( list, &ctx, 0 )); n++ ) ; - array = gcry_calloc( n, sizeof *array ); + array = gcry_xcalloc( n, sizeof *array ); if( !array ) return GCRYERR_NO_MEM; @@ -275,10 +329,10 @@ factors_from_sexp( MPI **retarray, GCRY_SEXP sexp ) } + static int gen_elg(int algo, unsigned nbits, KBNODE pub_root, KBNODE sec_root, DEK *dek, - STRING2KEY *s2k, PKT_secret_key **ret_sk, u32 expireval, - int version ) + STRING2KEY *s2k, PKT_secret_key **ret_sk, u32 expireval ) { int rc; int i; @@ -289,6 +343,18 @@ gen_elg(int algo, unsigned nbits, KBNODE pub_root, KBNODE sec_root, DEK *dek, char buf[100]; GCRY_SEXP s_parms, s_key; + assert( is_ELGAMAL(algo) ); + + if( nbits < 512 ) { + nbits = 1024; + log_info(_("keysize invalid; using %u bits\n"), nbits ); + } + + if( (nbits % 32) ) { + nbits = ((nbits + 31) / 32) * 32; + log_info(_("keysize rounded up to %u bits\n"), nbits ); + } + sprintf(buf, "%u", nbits ); s_parms = SEXP_CONS( SEXP_NEW( "genkey", 0 ), SEXP_CONS( SEXP_NEW(algo == GCRY_PK_ELG_E ? "openpgp-elg" : @@ -303,10 +369,11 @@ gen_elg(int algo, unsigned nbits, KBNODE pub_root, KBNODE sec_root, DEK *dek, return rc; } + sk = gcry_xcalloc( 1, sizeof *sk ); pk = gcry_xcalloc( 1, sizeof *pk ); sk->timestamp = pk->timestamp = make_timestamp(); - sk->version = pk->version = version; + sk->version = pk->version = 4; if( expireval ) { sk->expiredate = pk->expiredate = sk->timestamp + expireval; } @@ -361,7 +428,6 @@ gen_elg(int algo, unsigned nbits, KBNODE pub_root, KBNODE sec_root, DEK *dek, for(i=0; factors[i]; i++ ) { add_kbnode( sec_root, make_mpi_comment_node("#:ELG_factor:", factors[i] )); - gcry_mpi_release(factors[i]); } return 0; @@ -372,7 +438,7 @@ gen_elg(int algo, unsigned nbits, KBNODE pub_root, KBNODE sec_root, DEK *dek, * Generate a DSA key */ static int -gen_dsa(unsigned nbits, KBNODE pub_root, KBNODE sec_root, DEK *dek, +gen_dsa(unsigned int nbits, KBNODE pub_root, KBNODE sec_root, DEK *dek, STRING2KEY *s2k, PKT_secret_key **ret_sk, u32 expireval ) { int rc; @@ -384,8 +450,15 @@ gen_dsa(unsigned nbits, KBNODE pub_root, KBNODE sec_root, DEK *dek, char buf[100]; GCRY_SEXP s_parms, s_key; - if( nbits > 1024 ) + if( nbits > 1024 || nbits < 512 ) { nbits = 1024; + log_info(_("keysize invalid; using %u bits\n"), nbits ); + } + + if( (nbits % 64) ) { + nbits = ((nbits + 63) / 64) * 64; + log_info(_("keysize rounded up to %u bits\n"), nbits ); + } sprintf(buf, "%u", nbits ); s_parms = SEXP_CONS( SEXP_NEW( "genkey", 0 ), @@ -400,6 +473,7 @@ gen_dsa(unsigned nbits, KBNODE pub_root, KBNODE sec_root, DEK *dek, return rc; } + sk = gcry_xcalloc( 1, sizeof *sk ); pk = gcry_xcalloc( 1, sizeof *pk ); sk->timestamp = pk->timestamp = make_timestamp(); @@ -464,10 +538,91 @@ gen_dsa(unsigned nbits, KBNODE pub_root, KBNODE sec_root, DEK *dek, make_mpi_comment_node("#:DSA_factor:", factors[i] )); /* fixme: Merge this with the elg-generate function and release - * some more stuff */ + * some more stuff (memory-leak) */ return 0; } +#if 0 +static int +gen_rsa(int algo, unsigned nbits, KBNODE pub_root, KBNODE sec_root, DEK *dek, + STRING2KEY *s2k, PKT_secret_key **ret_sk, u32 expireval ) +{ + int rc; + PACKET *pkt; + PKT_secret_key *sk; + PKT_public_key *pk; + MPI skey[4]; + MPI *factors; + + assert( is_RSA(algo) ); + + if( nbits < 1024 ) { + nbits = 1024; + log_info(_("keysize invalid; using %u bits\n"), nbits ); + } + + if( (nbits % 32) ) { + nbits = ((nbits + 31) / 32) * 32; + log_info(_("keysize rounded up to %u bits\n"), nbits ); + } + + rc = pubkey_generate( algo, nbits, skey, &factors ); + if( rc ) { + log_error("pubkey_generate failed: %s\n", gpg_errstr(rc) ); + return rc; + } + + sk = gcry_xcalloc( 1, sizeof *sk ); + pk = gcry_xcalloc( 1, sizeof *pk ); + sk->timestamp = pk->timestamp = make_timestamp(); + sk->version = pk->version = 4; + if( expireval ) { + sk->expiredate = pk->expiredate = sk->timestamp + expireval; + } + sk->pubkey_algo = pk->pubkey_algo = algo; + pk->pkey[0] = mpi_copy( skey[0] ); + pk->pkey[1] = mpi_copy( skey[1] ); + sk->skey[0] = skey[0]; + sk->skey[1] = skey[1]; + sk->skey[2] = skey[2]; + sk->skey[3] = skey[3]; + sk->skey[4] = skey[4]; + sk->skey[5] = skey[5]; + sk->is_protected = 0; + sk->protect.algo = 0; + + sk->csum = checksum_mpi_counted_nbits( sk->skey[2] ); + sk->csum += checksum_mpi_counted_nbits( sk->skey[3] ); + sk->csum += checksum_mpi_counted_nbits( sk->skey[4] ); + sk->csum += checksum_mpi_counted_nbits( sk->skey[5] ); + if( ret_sk ) /* not a subkey: return an unprotected version of the sk */ + *ret_sk = copy_secret_key( NULL, sk ); + + if( dek ) { + sk->protect.algo = dek->algo; + sk->protect.s2k = *s2k; + rc = protect_secret_key( sk, dek ); + if( rc ) { + log_error("protect_secret_key failed: %s\n", gpg_errstr(rc) ); + free_public_key(pk); + free_secret_key(sk); + return rc; + } + } + + pkt = gcry_xcalloc( 1,sizeof *pkt); + pkt->pkttype = ret_sk ? PKT_PUBLIC_KEY : PKT_PUBLIC_SUBKEY; + pkt->pkt.public_key = pk; + add_kbnode(pub_root, new_kbnode( pkt )); + + pkt = gcry_xcalloc( 1,sizeof *pkt); + pkt->pkttype = ret_sk ? PKT_SECRET_KEY : PKT_SECRET_SUBKEY; + pkt->pkt.secret_key = sk; + add_kbnode(sec_root, new_kbnode( pkt )); + + return 0; +} +#endif /**************** @@ -502,7 +657,7 @@ check_valid_days( const char *s ) * Returns: 0 to create both a DSA and a ElGamal key. */ static int -ask_algo( int *ret_v4, int addmode ) +ask_algo( int addmode ) { char *answer; int algo; @@ -515,10 +670,9 @@ ask_algo( int *ret_v4, int addmode ) tty_printf( _(" (%d) ElGamal (encrypt only)\n"), 3 ); tty_printf( _(" (%d) ElGamal (sign and encrypt)\n"), 4 ); #if 0 - tty_printf( _(" (%d) ElGamal in a v3 packet\n"), 5 ); + tty_printf( _(" (%d) RSA (sign and encrypt)\n"), 5 ); #endif - *ret_v4 = 1; for(;;) { answer = cpr_get("keygen.algo",_("Your selection? ")); cpr_kill_prompt(); @@ -528,6 +682,15 @@ ask_algo( int *ret_v4, int addmode ) algo = 0; /* create both keys */ break; } + #if 0 + else if( algo == 5 ) { + if( cpr_get_answer_is_yes("keygen.algo.rsa_se",_( + "Do you really want to create a sign and encrypt key? "))) { + algo = GCRY_PK_RSA; + break; + } + } + #endif else if( algo == 4 ) { if( cpr_get_answer_is_yes("keygen.algo.elg_se",_( "Do you really want to create a sign and encrypt key? "))) { @@ -543,13 +706,6 @@ ask_algo( int *ret_v4, int addmode ) algo = GCRY_PK_DSA; break; } - #if 0 - else if( algo == 5 ) { - algo = GCRY_PK_ELG_E; - *ret_v4 = 0; - break; - } - #endif else tty_printf(_("Invalid selection.\n")); } @@ -578,6 +734,9 @@ ask_keysize( int algo ) tty_printf(_("DSA only allows keysizes from 512 to 1024\n")); else if( nbits < 768 ) tty_printf(_("keysize too small; 768 is smallest value allowed.\n")); + else if( algo == GCRY_PK_RSA && nbits < 1024 ) + tty_printf(_("keysize too small;" + " 1024 is smallest value allowed for RSA.\n")); else if( nbits > 4096 ) { /* It is ridiculous and an annoyance to use larger key sizes! * GnuPG can handle much larger sizes; but it takes an eternity @@ -602,7 +761,7 @@ ask_keysize( int algo ) break; } } - else if( nbits > 1536 && !cpr_enabled() ) { + else if( nbits > 1536 && !cpr_enabled() && algo != GCRY_PK_RSA ) { if( cpr_get_answer_is_yes("keygen.size.large.okay",_( "Do you really need such a large keysize? ")) ) break; @@ -623,6 +782,41 @@ ask_keysize( int algo ) } +/**************** + * Parse an expire string and return it's value in days. + * Returns -1 on error. + */ +static int +parse_expire_string( const char *string ) +{ + int mult; + u32 abs_date=0; + u32 curtime = make_timestamp(); + int valid_days; + + if( !*string ) + valid_days = 0; + else if( (abs_date = scan_isodatestr(string)) && abs_date > curtime ) { + /* This calculation is not perfectly okay because we + * are later going to simply multiply by 86400 and don't + * correct for leapseconds. A solution would be to change + * the whole implemenation to work with dates and not intervals + * which are required for v3 keys. + */ + valid_days = abs_date/86400-curtime/86400+1; + } + else if( (mult=check_valid_days(string)) ) { + valid_days = atoi(string) * mult; + if( valid_days < 0 || valid_days > 39447 ) + valid_days = 0; + } + else { + valid_days = -1; + } + return valid_days; +} + + static u32 ask_expire_interval(void) { @@ -642,32 +836,14 @@ ask_expire_interval(void) answer = NULL; for(;;) { - int mult; - u32 abs_date=0; - u32 curtime=0;; + u32 curtime=make_timestamp(); gcry_free(answer); answer = cpr_get("keygen.valid",_("Key is valid for? (0) ")); cpr_kill_prompt(); trim_spaces(answer); - curtime = make_timestamp(); - if( !*answer ) - valid_days = 0; - else if( (abs_date = scan_isodatestr(answer)) && abs_date > curtime ) { - /* This calculation is not perfectly okay because we - * are later going to simply multiply by 86400 and don't - * correct for leapseconds. A solution would be to change - * the whole implemenation to work with dates and not intervals - * which are required for v3 keys. - */ - valid_days = abs_date/86400-curtime/86400+1; - } - else if( (mult=check_valid_days(answer)) ) { - valid_days = atoi(answer) * mult; - if( valid_days < 0 || valid_days > 32767 ) - valid_days = 0; - } - else { + valid_days = parse_expire_string( answer ); + if( valid_days < 0 ) { tty_printf(_("invalid value\n")); continue; } @@ -680,7 +856,10 @@ ask_expire_interval(void) interval = valid_days * 86400L; /* print the date when the key expires */ tty_printf(_("Key expires at %s\n"), - asctimestamp(curtime + interval ) ); + asctimestamp((ulong)(curtime + interval) ) ); + if( (time_t)((ulong)(curtime+interval)) < 0 ) + tty_printf(_("Your system can't display dates beyond 2038.\n" + "However, it will be correctly handled up to 2106.\n")); } if( cpr_enabled() || cpr_get_answer_is_yes("keygen.valid.okay", @@ -734,6 +913,7 @@ ask_user_id( int mode ) uid = aname = acomment = amail = NULL; for(;;) { char *p; + int fail=0; if( !aname ) { for(;;) { @@ -785,6 +965,7 @@ ask_user_id( int mode ) } } + gcry_free(uid); uid = p = gcry_xmalloc(strlen(aname)+strlen(amail)+strlen(acomment)+12+10); p = stpcpy(p, aname ); @@ -812,8 +993,14 @@ ask_user_id( int mode ) tty_printf(_("You selected this USER-ID:\n \"%s\"\n\n"), uid); /* fixme: add a warning if this user-id already exists */ + if( !*amail && (strchr( aname, '@' ) || strchr( acomment, '@'))) { + fail = 1; + tty_printf(_("Please don't put the email address " + "into the real name or the comment\n") ); + } + for(;;) { - const char *ansstr = _("NnCcEeOoQq"); + char *ansstr = _("NnCcEeOoQq"); if( strlen(ansstr) != 10 ) BUG(); @@ -822,8 +1009,9 @@ ask_user_id( int mode ) answer[1] = 0; } else { - answer = cpr_get("keygen.userid.cmd",_( - "Change (N)ame, (C)omment, (E)mail or (O)kay/(Q)uit? ")); + answer = cpr_get("keygen.userid.cmd", fail? + _("Change (N)ame, (C)omment, (E)mail or (Q)uit? ") : + _("Change (N)ame, (C)omment, (E)mail or (O)kay/(Q)uit? ")); cpr_kill_prompt(); } if( strlen(answer) > 1 ) @@ -841,10 +1029,15 @@ ask_user_id( int mode ) break; } else if( *answer == ansstr[6] || *answer == ansstr[7] ) { - gcry_free(aname); aname = NULL; - gcry_free(acomment); acomment = NULL; - gcry_free(amail); amail = NULL; - break; + if( fail ) { + tty_printf(_("Please correct the error first\n")); + } + else { + gcry_free(aname); aname = NULL; + gcry_free(acomment); acomment = NULL; + gcry_free(amail); amail = NULL; + break; + } } else if( *answer == ansstr[8] || *answer == ansstr[9] ) { gcry_free(aname); aname = NULL; @@ -904,22 +1097,25 @@ ask_passphrase( STRING2KEY **ret_s2k ) static int do_create( int algo, unsigned nbits, KBNODE pub_root, KBNODE sec_root, - DEK *dek, STRING2KEY *s2k, PKT_secret_key **sk, u32 expiredate, - int v4_packet ) + DEK *dek, STRING2KEY *s2k, PKT_secret_key **sk, u32 expiredate ) { int rc=0; - tty_printf(_( + if( !opt.batch ) + tty_printf(_( "We need to generate a lot of random bytes. It is a good idea to perform\n" "some other action (type on the keyboard, move the mouse, utilize the\n" "disks) during the prime generation; this gives the random number\n" "generator a better chance to gain enough entropy.\n") ); if( algo == GCRY_PK_ELG || algo == GCRY_PK_ELG_E ) - rc = gen_elg(algo, nbits, pub_root, sec_root, dek, s2k, - sk, expiredate, v4_packet? 4:3 ); + rc = gen_elg(algo, nbits, pub_root, sec_root, dek, s2k, sk, expiredate); else if( algo == GCRY_PK_DSA ) rc = gen_dsa(nbits, pub_root, sec_root, dek, s2k, sk, expiredate); + #if 0 + else if( algo == GCRY_PK_RSA ) + rc = gen_rsa(algo, nbits, pub_root, sec_root, dek, s2k, sk, expiredate); + #endif else BUG(); @@ -951,61 +1147,553 @@ generate_user_id() if( !p ) return NULL; n = strlen(p); - uid = gcry_xmalloc( sizeof *uid + n - 1 ); + uid = gcry_xcalloc( 1, sizeof *uid + n - 1 ); uid->len = n; strcpy(uid->name, p); return uid; } +static void +release_parameter_list( struct para_data_s *r ) +{ + struct para_data_s *r2; + + for( ; r ; r = r2 ) { + r2 = r->next; + if( r->key == pPASSPHRASE_DEK ) + gcry_free( r->u.dek ); + else if( r->key == pPASSPHRASE_S2K ) + gcry_free( r->u.s2k ); + + gcry_free(r); + } +} + +static struct para_data_s * +get_parameter( struct para_data_s *para, enum para_name key ) +{ + struct para_data_s *r; + + for( r = para; r && r->key != key; r = r->next ) + ; + return r; +} + +static const char * +get_parameter_value( struct para_data_s *para, enum para_name key ) +{ + struct para_data_s *r = get_parameter( para, key ); + return (r && *r->u.value)? r->u.value : NULL; +} + +static int +get_parameter_algo( struct para_data_s *para, enum para_name key ) +{ + struct para_data_s *r = get_parameter( para, key ); + if( !r ) + return -1; + if( isdigit( *r->u.value ) ) + return atoi( r->u.value ); + return gcry_pk_map_name( r->u.value ); +} + + +static u32 +get_parameter_u32( struct para_data_s *para, enum para_name key ) +{ + struct para_data_s *r = get_parameter( para, key ); + + if( !r ) + return 0; + if( r->key == pKEYEXPIRE || r->key == pSUBKEYEXPIRE ) + return r->u.expire; + + return (unsigned int)strtoul( r->u.value, NULL, 10 ); +} + +static unsigned int +get_parameter_uint( struct para_data_s *para, enum para_name key ) +{ + return get_parameter_u32( para, key ); +} + +static DEK * +get_parameter_dek( struct para_data_s *para, enum para_name key ) +{ + struct para_data_s *r = get_parameter( para, key ); + return r? r->u.dek : NULL; +} + +static STRING2KEY * +get_parameter_s2k( struct para_data_s *para, enum para_name key ) +{ + struct para_data_s *r = get_parameter( para, key ); + return r? r->u.s2k : NULL; +} + + +static int +proc_parameter_file( struct para_data_s *para, const char *fname, + struct output_control_s *outctrl ) +{ + struct para_data_s *r; + const char *s1, *s2, *s3; + size_t n; + char *p; + int i; + + /* check that we have all required parameters */ + assert( get_parameter( para, pKEYTYPE ) ); + i = get_parameter_algo( para, pKEYTYPE ); + if( i < 1 || openpgp_pk_test_algo( i, GCRY_PK_USAGE_SIGN ) ) { + r = get_parameter( para, pKEYTYPE ); + log_error("%s:%d: invalid algorithm\n", fname, r->lnr ); + return -1; + } + + i = get_parameter_algo( para, pSUBKEYTYPE ); + if( i > 1 && openpgp_pk_test_algo( i, 0 ) ) { + r = get_parameter( para, pSUBKEYTYPE ); + log_error("%s:%d: invalid algorithm\n", fname, r->lnr ); + return -1; + } + + if( !get_parameter_value( para, pUSERID ) ) { + /* create the formatted user ID */ + s1 = get_parameter_value( para, pNAMEREAL ); + s2 = get_parameter_value( para, pNAMECOMMENT ); + s3 = get_parameter_value( para, pNAMEEMAIL ); + if( s1 || s2 || s3 ) { + n = (s1?strlen(s1):0) + (s2?strlen(s2):0) + (s3?strlen(s3):0); + r = gcry_xcalloc( 1, sizeof *r + n + 20 ); + r->key = pUSERID; + p = r->u.value; + if( s1 ) + p = stpcpy(p, s1 ); + if( s2 ) + p = stpcpy(stpcpy(stpcpy(p," ("), s2 ),")"); + if( s3 ) + p = stpcpy(stpcpy(stpcpy(p," <"), s3 ),">"); + r->next = para; + para = r; + } + } + + /* make DEK and S2K from the Passphrase */ + r = get_parameter( para, pPASSPHRASE ); + if( r && *r->u.value ) { + /* we have a plain text passphrase - create a DEK from it. + * It is a little bit ridiculous to keep it ih secure memory + * but becuase we do this alwasy, why not here */ + STRING2KEY *s2k; + DEK *dek; + + s2k = gcry_xmalloc_secure( sizeof *s2k ); + s2k->mode = opt.s2k_mode; + s2k->hash_algo = opt.s2k_digest_algo; + set_next_passphrase( r->u.value ); + dek = passphrase_to_dek( NULL, 0, opt.s2k_cipher_algo, s2k, 2 ); + set_next_passphrase( NULL ); + assert( dek ); + memset( r->u.value, 0, strlen(r->u.value) ); + + r = gcry_xcalloc( 1, sizeof *r ); + r->key = pPASSPHRASE_S2K; + r->u.s2k = s2k; + r->next = para; + para = r; + r = gcry_xcalloc( 1, sizeof *r ); + r->key = pPASSPHRASE_DEK; + r->u.dek = dek; + r->next = para; + para = r; + } + + /* make KEYEXPIRE from Expire-Date */ + r = get_parameter( para, pEXPIREDATE ); + if( r && *r->u.value ) { + i = parse_expire_string( r->u.value ); + if( i < 0 ) { + log_error("%s:%d: invalid expire date\n", fname, r->lnr ); + return -1; + } + r->u.expire = i * 86400L; + r->key = pKEYEXPIRE; /* change hat entry */ + /* also set it for the subkey */ + r = gcry_xcalloc( 1, sizeof *r + 20 ); + r->key = pSUBKEYEXPIRE; + r->u.expire = i * 86400L; + r->next = para; + para = r; + } + + if( !!outctrl->pub.newfname ^ !!outctrl->sec.newfname ) { + log_error("%s:%d: only one ring name is set\n", fname, outctrl->lnr ); + return -1; + } + + do_generate_keypair( para, outctrl ); + return 0; +} + + +/**************** + * Kludge to allow non interactive key generation controlled + * by a parameter file (which currently is only stdin) + * Note, that string parameters are expected to be in UTF-8 + */ +static void +read_parameter_file( const char *fname ) +{ + static struct { const char *name; + enum para_name key; + } keywords[] = { + { "Key-Type", pKEYTYPE}, + { "Key-Length", pKEYLENGTH }, + { "Subkey-Type", pSUBKEYTYPE }, + { "Subkey-Length", pSUBKEYLENGTH }, + { "Name-Real", pNAMEREAL }, + { "Name-Email", pNAMEEMAIL }, + { "Name-Comment", pNAMECOMMENT }, + { "Expire-Date", pEXPIREDATE }, + { "Passphrase", pPASSPHRASE }, + { NULL, 0 } + }; + FILE *fp; + char line[1024], *p; + int lnr; + const char *err = NULL; + struct para_data_s *para, *r; + int i; + struct output_control_s outctrl; + + memset( &outctrl, 0, sizeof( outctrl ) ); + + if( !fname || !*fname || !strcmp(fname,"-") ) { + fp = stdin; + fname = "-"; + } + else { + fp = fopen( fname, "r" ); + if( !fp ) { + log_error(_("can't open `%s': %s\n"), fname, strerror(errno) ); + return; + } + } + + lnr = 0; + err = NULL; + para = NULL; + while( fgets( line, DIM(line)-1, fp ) ) { + char *keyword, *value; + + lnr++; + if( *line && line[strlen(line)-1] != '\n' ) { + err = "line too long"; + break; + } + for( p = line; isspace(*p); p++ ) + ; + if( !*p || *p == '#' ) + continue; + keyword = p; + if( *keyword == '%' ) { + for( ; !isspace(*p); p++ ) + ; + if( *p ) + *p++ = 0; + for( ; isspace(*p); p++ ) + ; + value = p; + trim_trailing_ws( value, strlen(value) ); + if( !stricmp( keyword, "%echo" ) ) + log_info("%s\n", value ); + else if( !stricmp( keyword, "%dry-run" ) ) + outctrl.dryrun = 1; + else if( !stricmp( keyword, "%commit" ) ) { + outctrl.lnr = lnr; + proc_parameter_file( para, fname, &outctrl ); + release_parameter_list( para ); + para = NULL; + } + else if( !stricmp( keyword, "%pubring" ) ) { + if( outctrl.pub.fname && !strcmp( outctrl.pub.fname, value ) ) + ; /* still the same file - ignore it */ + else { + gcry_free( outctrl.pub.newfname ); + outctrl.pub.newfname = gcry_xstrdup( value ); + outctrl.use_files = 1; + } + } + else if( !stricmp( keyword, "%secring" ) ) { + if( outctrl.sec.fname && !strcmp( outctrl.sec.fname, value ) ) + ; /* still the same file - ignore it */ + else { + gcry_free( outctrl.sec.newfname ); + outctrl.sec.newfname = gcry_xstrdup( value ); + outctrl.use_files = 1; + } + } + else + log_info("skipping control `%s' (%s)\n", keyword, value ); + + + continue; + } + + + if( !(p = strchr( p, ':' )) || p == keyword ) { + err = "missing colon"; + break; + } + if( *p ) + *p++ = 0; + for( ; isspace(*p); p++ ) + ; + if( !*p ) { + err = "missing argument"; + break; + } + value = p; + trim_trailing_ws( value, strlen(value) ); + + for(i=0; keywords[i].name; i++ ) { + if( !stricmp( keywords[i].name, keyword ) ) + break; + } + if( !keywords[i].name ) { + err = "unknown keyword"; + break; + } + if( keywords[i].key != pKEYTYPE && !para ) { + err = "parameter block does not start with \"Key-Type\""; + break; + } + + if( keywords[i].key == pKEYTYPE && para ) { + outctrl.lnr = lnr; + proc_parameter_file( para, fname, &outctrl ); + release_parameter_list( para ); + para = NULL; + } + else { + for( r = para; r; r = r->next ) { + if( r->key == keywords[i].key ) + break; + } + if( r ) { + err = "duplicate keyword"; + break; + } + } + r = gcry_xcalloc( 1, sizeof *r + strlen( value ) ); + r->lnr = lnr; + r->key = keywords[i].key; + strcpy( r->u.value, value ); + r->next = para; + para = r; + } + if( err ) + log_error("%s:%d: %s\n", fname, lnr, err ); + else if( ferror(fp) ) { + log_error("%s:%d: read error: %s\n", fname, lnr, strerror(errno) ); + } + else if( para ) { + outctrl.lnr = lnr; + proc_parameter_file( para, fname, &outctrl ); + } + + if( outctrl.use_files ) { /* close open streams */ + iobuf_close( outctrl.pub.stream ); + iobuf_close( outctrl.sec.stream ); + gcry_free( outctrl.pub.fname ); + gcry_free( outctrl.pub.newfname ); + gcry_free( outctrl.sec.fname ); + gcry_free( outctrl.sec.newfname ); + } + + release_parameter_list( para ); + if( strcmp( fname, "-" ) ) + fclose(fp); +} + + /**************** * Generate a keypair + * (fname is only used in batch mode) */ void -generate_keypair() +generate_keypair( const char *fname ) { - unsigned nbits; - char *pub_fname = NULL; - char *sec_fname = NULL; + unsigned int nbits; char *uid = NULL; - KBNODE pub_root = NULL; - KBNODE sec_root = NULL; - PKT_secret_key *sk = NULL; DEK *dek; STRING2KEY *s2k; - int rc; int algo; - u32 expire; - int v4; int both = 0; + u32 expire; + struct para_data_s *para = NULL; + struct para_data_s *r; + struct output_control_s outctrl; - if( opt.batch || opt.answer_yes || opt.answer_no ) { - log_error(_("Key generation can only be used in interactive mode\n")); + memset( &outctrl, 0, sizeof( outctrl ) ); + + if( opt.batch ) { + read_parameter_file( fname ); return; } - algo = ask_algo( &v4, 0 ); - if( !algo ) { - algo = GCRY_PK_ELG_E; + algo = ask_algo( 0 ); + if( !algo ) { /* default: DSA with ElG subkey of the specified size */ both = 1; + r = gcry_xcalloc( 1, sizeof *r + 20 ); + r->key = pKEYTYPE; + sprintf( r->u.value, "%d", GCRY_PK_DSA ); + r->next = para; + para = r; tty_printf(_("DSA keypair will have 1024 bits.\n")); + r = gcry_xcalloc( 1, sizeof *r + 20 ); + r->key = pKEYLENGTH; + strcpy( r->u.value, "1024" ); + r->next = para; + para = r; + + algo = GCRY_PK_ELG_E; + r = gcry_xcalloc( 1, sizeof *r + 20 ); + r->key = pSUBKEYTYPE; + sprintf( r->u.value, "%d", algo ); + r->next = para; + para = r; } + else { + r = gcry_xcalloc( 1, sizeof *r + 20 ); + r->key = pKEYTYPE; + sprintf( r->u.value, "%d", algo ); + r->next = para; + para = r; + } + nbits = ask_keysize( algo ); + r = gcry_xcalloc( 1, sizeof *r + 20 ); + r->key = both? pSUBKEYLENGTH : pKEYLENGTH; + sprintf( r->u.value, "%u", nbits); + r->next = para; + para = r; + expire = ask_expire_interval(); + r = gcry_xcalloc( 1, sizeof *r + 20 ); + r->key = pKEYEXPIRE; + r->u.expire = expire; + r->next = para; + para = r; + r = gcry_xcalloc( 1, sizeof *r + 20 ); + r->key = pSUBKEYEXPIRE; + r->u.expire = expire; + r->next = para; + para = r; + uid = ask_user_id(0); if( !uid ) { log_error(_("Key generation canceled.\n")); + release_parameter_list( para ); return; } + r = gcry_xcalloc( 1, sizeof *r + strlen(uid) ); + r->key = pUSERID; + strcpy( r->u.value, uid ); + r->next = para; + para = r; + dek = ask_passphrase( &s2k ); + if( dek ) { + r = gcry_xcalloc( 1, sizeof *r ); + r->key = pPASSPHRASE_DEK; + r->u.dek = dek; + r->next = para; + para = r; + r = gcry_xcalloc( 1, sizeof *r ); + r->key = pPASSPHRASE_S2K; + r->u.s2k = s2k; + r->next = para; + para = r; + } + + proc_parameter_file( para, "[internal]", &outctrl ); + release_parameter_list( para ); +} - /* now check whether we are allowed to write to the keyrings */ - pub_fname = make_filename(opt.homedir, "pubring.gpg", NULL ); - sec_fname = make_filename(opt.homedir, "secring.gpg", NULL ); +static void +do_generate_keypair( struct para_data_s *para, + struct output_control_s *outctrl ) +{ + char *pub_fname = NULL; + char *sec_fname = NULL; + KBNODE pub_root = NULL; + KBNODE sec_root = NULL; + PKT_secret_key *sk = NULL; + const char *s; + int rc; + + if( outctrl->dryrun ) { + log_info("dry-run mode - key generation skipped\n"); + return; + } + + + if( outctrl->use_files ) { + if( outctrl->pub.newfname ) { + iobuf_close(outctrl->pub.stream); + outctrl->pub.stream = NULL; + gcry_free( outctrl->pub.fname ); + outctrl->pub.fname = outctrl->pub.newfname; + outctrl->pub.newfname = NULL; + + outctrl->pub.stream = iobuf_create( outctrl->pub.fname ); + if( !outctrl->pub.stream ) { + log_error("can't create `%s': %s\n", outctrl->pub.newfname, + strerror(errno) ); + return; + } + if( opt.armor ) { + outctrl->pub.afx.what = 1; + iobuf_push_filter( outctrl->pub.stream, armor_filter, + &outctrl->pub.afx ); + } + } + if( outctrl->sec.newfname ) { + iobuf_close(outctrl->sec.stream); + outctrl->sec.stream = NULL; + gcry_free( outctrl->sec.fname ); + outctrl->sec.fname = outctrl->sec.newfname; + outctrl->sec.newfname = NULL; + + outctrl->sec.stream = iobuf_create( outctrl->sec.fname ); + if( !outctrl->sec.stream ) { + log_error("can't create `%s': %s\n", outctrl->sec.newfname, + strerror(errno) ); + return; + } + if( opt.armor ) { + outctrl->sec.afx.what = 5; + iobuf_push_filter( outctrl->sec.stream, armor_filter, + &outctrl->sec.afx ); + } + } + pub_fname = outctrl->pub.fname; /* only for info output */ + sec_fname = outctrl->sec.fname; /* only for info output */ + assert( outctrl->pub.stream ); + assert( outctrl->sec.stream ); + } + else { + pub_fname = get_writable_keyblock_file( 0 ); + sec_fname = get_writable_keyblock_file( 1 ); + } + if( opt.verbose ) { - tty_printf(_("writing public certificate to `%s'\n"), pub_fname ); - tty_printf(_("writing secret certificate to `%s'\n"), sec_fname ); + log_info(_("writing public key to `%s'\n"), pub_fname ); + log_info(_("writing secret key to `%s'\n"), sec_fname ); } /* we create the packets as a tree of kbnodes. Because the structure @@ -1016,24 +1704,31 @@ generate_keypair() pub_root = make_comment_node("#"); delete_kbnode(pub_root); sec_root = make_comment_node("#"); delete_kbnode(sec_root); - if( both ) - rc = do_create( GCRY_PK_DSA, 1024, pub_root, sec_root, - dek, s2k, &sk, expire, 1); - else - rc = do_create( algo, nbits, pub_root, sec_root, - dek, s2k, &sk, expire, v4); - if( !rc ) - write_uid(pub_root, uid ); - if( !rc ) - write_uid(sec_root, uid ); - if( !rc ) - rc = write_selfsig(pub_root, pub_root, sk); - if( !rc ) - rc = write_selfsig(sec_root, pub_root, sk); + rc = do_create( get_parameter_algo( para, pKEYTYPE ), + get_parameter_uint( para, pKEYLENGTH ), + pub_root, sec_root, + get_parameter_dek( para, pPASSPHRASE_DEK ), + get_parameter_s2k( para, pPASSPHRASE_S2K ), + &sk, + get_parameter_u32( para, pKEYEXPIRE ) ); + if( !rc && (s=get_parameter_value(para, pUSERID)) ) { + write_uid(pub_root, s ); + if( !rc ) + write_uid(sec_root, s ); + if( !rc ) + rc = write_selfsig(pub_root, pub_root, sk); + if( !rc ) + rc = write_selfsig(sec_root, pub_root, sk); + } - if( both ) { - rc = do_create( algo, nbits, pub_root, sec_root, - dek, s2k, NULL, expire, 1 ); + if( get_parameter( para, pSUBKEYTYPE ) ) { + rc = do_create( get_parameter_algo( para, pSUBKEYTYPE ), + get_parameter_uint( para, pSUBKEYLENGTH ), + pub_root, sec_root, + get_parameter_dek( para, pPASSPHRASE_DEK ), + get_parameter_s2k( para, pPASSPHRASE_S2K ), + NULL, + get_parameter_u32( para, pSUBKEYEXPIRE ) ); if( !rc ) rc = write_keybinding(pub_root, pub_root, sk); if( !rc ) @@ -1041,7 +1736,18 @@ generate_keypair() } - if( !rc ) { + if( !rc && outctrl->use_files ) { /* direct write to specified files */ + rc = write_keyblock( outctrl->pub.stream, pub_root ); + if( rc ) + log_error("can't write public key: %s\n", gpg_errstr(rc) ); + if( !rc ) { + rc = write_keyblock( outctrl->sec.stream, sec_root ); + if( rc ) + log_error("can't write secret key: %s\n", gpg_errstr(rc) ); + } + + } + else if( !rc ) { /* write to the standard keyrings */ KBPOS pub_kbpos; KBPOS sec_kbpos; int rc1 = -1; @@ -1082,12 +1788,17 @@ generate_keypair() else if( (rc=insert_keyblock( &sec_kbpos, sec_root )) ) log_error("can't write secret key: %s\n", gpg_errstr(rc) ); else { - tty_printf(_("public and secret key created and signed.\n") ); - if( algo == GCRY_PK_DSA ) + if( !opt.batch ) + tty_printf(_("public and secret key created and signed.\n") ); + if( !opt.batch + && get_parameter_algo( para, pKEYTYPE ) == GCRY_PK_DSA + && !get_parameter( para, pSUBKEYTYPE ) ) + { tty_printf(_("Note that this key cannot be used for " "encryption. You may want to use\n" "the command \"--edit-key\" to generate a " "secondary key for this purpose.\n") ); + } } if( !rc1 ) @@ -1096,18 +1807,20 @@ generate_keypair() unlock_keyblock( &sec_kbpos ); } - - if( rc ) - tty_printf(_("Key generation failed: %s\n"), gpg_errstr(rc) ); + if( rc ) { + if( opt.batch ) + log_error("key generation failed: %s\n", gpg_errstr(rc) ); + else + tty_printf(_("Key generation failed: %s\n"), gpg_errstr(rc) ); + } release_kbnode( pub_root ); release_kbnode( sec_root ); if( sk ) /* the unprotected secret key */ free_secret_key(sk); - gcry_free(uid); - gcry_free(dek); - gcry_free(s2k); - gcry_free(pub_fname); - gcry_free(sec_fname); + if( !outctrl->use_files ) { + gcry_free(pub_fname); + gcry_free(sec_fname); + } } @@ -1121,7 +1834,7 @@ generate_subkeypair( KBNODE pub_keyblock, KBNODE sec_keyblock ) int okay=0, rc=0; KBNODE node; PKT_secret_key *sk = NULL; /* this is the primary sk */ - int v4, algo; + int algo; u32 expire; unsigned nbits; char *passphrase = NULL; @@ -1146,8 +1859,10 @@ generate_subkeypair( KBNODE pub_keyblock, KBNODE sec_keyblock ) "in future (time warp or clock problem)\n") : _("key has been created %lu seconds " "in future (time warp or clock problem)\n"), d ); - rc = GPGERR_TIME_CONFLICT; - goto leave; + if( !opt.ignore_time_conflict ) { + rc = GPGERR_TIME_CONFLICT; + goto leave; + } } @@ -1170,7 +1885,7 @@ generate_subkeypair( KBNODE pub_keyblock, KBNODE sec_keyblock ) goto leave; - algo = ask_algo( &v4, 1 ); + algo = ask_algo( 1 ); assert(algo); nbits = ask_keysize( algo ); expire = ask_expire_interval(); @@ -1187,7 +1902,7 @@ generate_subkeypair( KBNODE pub_keyblock, KBNODE sec_keyblock ) } rc = do_create( algo, nbits, pub_keyblock, sec_keyblock, - dek, s2k, NULL, expire, v4 ); + dek, s2k, NULL, expire ); if( !rc ) rc = write_keybinding(pub_keyblock, pub_keyblock, sk); if( !rc ) @@ -1207,3 +1922,20 @@ generate_subkeypair( KBNODE pub_keyblock, KBNODE sec_keyblock ) return okay; } +/**************** + * Write a keyblock to an output stream + */ +static int +write_keyblock( IOBUF out, KBNODE node ) +{ + for( ; node ; node = node->next ) { + int rc = build_packet( out, node->pkt ); + if( rc ) { + log_error("build_packet(%d) failed: %s\n", + node->pkt->pkttype, gpg_errstr(rc) ); + return GPGERR_WRITE_FILE; + } + } + return 0; +} + diff --git a/g10/keyid.c b/g10/keyid.c index ddaf0d5d3..14406b60b 100644 --- a/g10/keyid.c +++ b/g10/keyid.c @@ -1,5 +1,5 @@ /* keyid.c - jeyid and fingerprint handling - * Copyright (C) 1998 Free Software Foundation, Inc. + * Copyright (C) 1998, 1999, 2000 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -25,12 +25,14 @@ #include #include #include + #include #include "util.h" #include "main.h" #include "packet.h" #include "options.h" #include "keydb.h" +#include "i18n.h" int @@ -52,7 +54,7 @@ static GCRY_MD_HD do_fingerprint_md( PKT_public_key *pk ) { GCRY_MD_HD md; - unsigned n; + unsigned int n; unsigned int nn[GNUPG_MAX_NPKEY]; byte *pp[GNUPG_MAX_NPKEY]; int i; @@ -339,7 +341,7 @@ expirestr_from_pk( PKT_public_key *pk ) time_t atime; if( !pk->expiredate ) - return "never "; + return _("never "); atime = pk->expiredate; tp = gmtime( &atime ); sprintf(buffer,"%04d-%02d-%02d", 1900+tp->tm_year, tp->tm_mon+1, tp->tm_mday ); @@ -354,7 +356,7 @@ expirestr_from_sk( PKT_secret_key *sk ) time_t atime; if( !sk->expiredate ) - return "never "; + return _("never "); atime = sk->expiredate; tp = gmtime( &atime ); sprintf(buffer,"%04d-%02d-%02d", 1900+tp->tm_year, tp->tm_mon+1, tp->tm_mday ); diff --git a/g10/keylist.c b/g10/keylist.c index 0cefa2d56..751a61c9b 100644 --- a/g10/keylist.c +++ b/g10/keylist.c @@ -1,5 +1,5 @@ /* keylist.c - * Copyright (C) 1998 Free Software Foundation, Inc. + * Copyright (C) 1998, 1999, 2000 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -43,34 +43,24 @@ static void fingerprint( PKT_public_key *pk, PKT_secret_key *sk ); /**************** * List the keys - * If NNAMES is 0; all available keys are listed + * If list is NULL, all available keys are listed */ void -public_key_list( int nnames, char **names ) +public_key_list( STRLIST list ) { - if( !nnames ) + if( !list ) list_all(0); - else { /* List by user id */ - STRLIST list = NULL; - for( ; nnames ; nnames--, names++ ) - add_to_strlist( &list, *names ); + else list_one( list, 0 ); - free_strlist( list ); - } } void -secret_key_list( int nnames, char **names ) +secret_key_list( STRLIST list ) { - if( !nnames ) + if( !list ) list_all(1); - else { /* List by user id */ - STRLIST list = NULL; - for( ; nnames ; nnames--, names++ ) - add_to_strlist( &list, *names ); + else /* List by user id */ list_one( list, 1 ); - free_strlist( list ); - } } @@ -145,6 +135,7 @@ list_one( STRLIST names, int secret ) return; } do { + merge_keys_and_selfsig( keyblock ); list_keyblock( keyblock, 0 ); release_kbnode( keyblock ); } while( !get_pubkey_next( ctx, NULL, &keyblock ) ); @@ -155,7 +146,7 @@ list_one( STRLIST names, int secret ) static void print_key_data( PKT_public_key *pk, u32 *keyid ) { - int n = pubkey_get_npkey( pk->pubkey_algo ); + int n = pk ? pubkey_get_npkey( pk->pubkey_algo ) : 0; int i; for(i=0; i < n; i++ ) { @@ -211,21 +202,26 @@ list_keyblock( KBNODE keyblock, int secret ) sk = NULL; keyid_from_pk( pk, keyid ); if( opt.with_colons ) { - trustletter = query_trust_info( pk, NULL ); - if( trustletter == 'u' ) - ulti_hack = 1; - printf("pub:%c:%u:%d:%08lX%08lX:%s:%s:", - trustletter, + if ( opt.fast_list_mode ) { + fputs( "pub::", stdout ); + trustletter = 0; + } + else { + trustletter = query_trust_info( pk, NULL ); + if( trustletter == 'u' ) + ulti_hack = 1; + printf("pub:%c:", trustletter ); + } + printf("%u:%d:%08lX%08lX:%s:%s:", nbits_from_pk( pk ), pk->pubkey_algo, (ulong)keyid[0],(ulong)keyid[1], datestr_from_pk( pk ), - pk->expiredate? strtimestamp(pk->expiredate):"" - ); + pk->expiredate? strtimestamp(pk->expiredate):"" ); if( pk->local_id ) printf("%lu", pk->local_id ); putchar(':'); - if( pk->local_id ) + if( pk->local_id && !opt.fast_list_mode ) putchar( get_ownertrust_info( pk->local_id ) ); putchar(':'); } @@ -237,15 +233,22 @@ list_keyblock( KBNODE keyblock, int secret ) } for( kbctx=NULL; (node=walk_kbnode( keyblock, &kbctx, 0)) ; ) { - if( node->pkt->pkttype == PKT_USER_ID ) { + if( node->pkt->pkttype == PKT_USER_ID && !opt.fast_list_mode ) { if( any ) { - if( opt.with_colons ) { + if ( opt.with_colons ) { byte namehash[20]; if( pk && !ulti_hack ) { - gcry_md_hash_buffer( GCRY_MD_RMD160, namehash, - node->pkt->pkt.user_id->name, - node->pkt->pkt.user_id->len ); + if( node->pkt->pkt.user_id->photo ) { + gcry_md_hash_buffer( GCRY_MD_RMD160, namehash, + node->pkt->pkt.user_id->name, + node->pkt->pkt.user_id->len ); + } + else { + gcry_md_hash_buffer( GCRY_MD_RMD160, namehash, + node->pkt->pkt.user_id->name, + node->pkt->pkt.user_id->len ); + } trustletter = query_trust_info( pk, namehash ); } else @@ -288,8 +291,13 @@ list_keyblock( KBNODE keyblock, int secret ) keyid_from_pk( pk2, keyid2 ); if( opt.with_colons ) { - printf("sub:%c:%u:%d:%08lX%08lX:%s:%s:", - trustletter, + if ( opt.fast_list_mode ) { + fputs( "sub::", stdout ); + } + else { + printf("sub:%c:", trustletter ); + } + printf("%u:%d:%08lX%08lX:%s:%s:", nbits_from_pk( pk2 ), pk2->pubkey_algo, (ulong)keyid2[0],(ulong)keyid2[1], @@ -303,11 +311,16 @@ list_keyblock( KBNODE keyblock, int secret ) putchar(':'); putchar('\n'); } - else - printf("sub %4u%c/%08lX %s\n", nbits_from_pk( pk2 ), + else { + printf("sub %4u%c/%08lX %s", nbits_from_pk( pk2 ), pubkey_letter( pk2->pubkey_algo ), (ulong)keyid2[1], datestr_from_pk( pk2 ) ); + if( pk2->expiredate ) { + printf(_(" [expires: %s]"), expirestr_from_pk( pk2 ) ); + } + putchar('\n'); + } if( opt.fingerprint > 1 ) fingerprint( pk2, NULL ); if( opt.with_key_data ) @@ -346,6 +359,7 @@ list_keyblock( KBNODE keyblock, int secret ) else if( opt.list_sigs && node->pkt->pkttype == PKT_SIGNATURE ) { PKT_signature *sig = node->pkt->pkt.signature; int sigrc; + char *sigstr; if( !any ) { /* no user id, (maybe a revocation follows)*/ if( sig->sig_class == 0x20 ) @@ -363,11 +377,11 @@ list_keyblock( KBNODE keyblock, int secret ) if( sig->sig_class == 0x20 || sig->sig_class == 0x28 || sig->sig_class == 0x30 ) - fputs("rev", stdout); + sigstr = "rev"; else if( (sig->sig_class&~3) == 0x10 ) - fputs("sig", stdout); + sigstr = "sig"; else if( sig->sig_class == 0x18 ) - fputs("sig", stdout); + sigstr = "sig"; else { if( opt.with_colons ) printf("sig::::::::::%02x:\n",sig->sig_class ); @@ -390,6 +404,7 @@ list_keyblock( KBNODE keyblock, int secret ) rc = 0; sigrc = ' '; } + fputs( sigstr, stdout ); if( opt.with_colons ) { putchar(':'); if( sigrc != ' ' ) @@ -405,7 +420,7 @@ list_keyblock( KBNODE keyblock, int secret ) printf("[%s] ", gpg_errstr(rc) ); else if( sigrc == '?' ) ; - else { + else if ( !opt.fast_list_mode ) { size_t n; char *p = get_user_id( sig->keyid, &n ); if( opt.with_colons ) diff --git a/g10/ks-proto.h b/g10/ks-proto.h index 2a3b2ddb1..6e8bbad0b 100644 --- a/g10/ks-proto.h +++ b/g10/ks-proto.h @@ -1,5 +1,5 @@ /* ks-proto.h - * Copyright (C) 1998 Free Software Foundation, Inc. + * Copyright (C) 1998, 1999, 2000 Free Software Foundation, Inc. * * This file is part of GnuPG. * diff --git a/g10/main.h b/g10/main.h index 621fc57de..711f5b8d2 100644 --- a/g10/main.h +++ b/g10/main.h @@ -1,5 +1,5 @@ /* main.h - * Copyright (C) 1998 Free Software Foundation, Inc. + * Copyright (C) 1998, 1999, 2000 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -73,7 +73,6 @@ int pubkey_get_nsig( int algo ); int pubkey_get_nenc( int algo ); unsigned int pubkey_nbits( int algo, MPI *pkey ); - /*-- helptext.c --*/ void display_online_help( const char *keyword ); @@ -94,7 +93,7 @@ int clearsign_file( const char *fname, STRLIST locusr, const char *outfile ); /*-- sig-check.c --*/ int check_key_signature( KBNODE root, KBNODE node, int *is_selfsig ); int check_key_signature2( KBNODE root, KBNODE node, - int *is_selfsig, u32 *r_expire ); + int *is_selfsig, u32 *r_expiredate, int *r_expired ); /*-- delkey.c --*/ int delete_key( const char *username, int secure ); @@ -105,7 +104,7 @@ void keyedit_menu( const char *username, STRLIST locusr, STRLIST cmds, /*-- keygen.c --*/ u32 ask_expiredate(void); -void generate_keypair(void); +void generate_keypair( const char *fname ); int keygen_add_key_expire( PKT_signature *sig, void *opaque ); int keygen_add_std_prefs( PKT_signature *sig, void *opaque ); int generate_subkeypair( KBNODE pub_keyblock, KBNODE sec_keyblock ); @@ -116,20 +115,20 @@ char *make_outfile_name( const char *iname ); char *ask_outfile_name( const char *name, size_t namelen ); int open_outfile( const char *iname, int mode, IOBUF *a ); IOBUF open_sigfile( const char *iname ); -void copy_options_file( const char *destdir ); +void try_make_homedir( const char *fname ); /*-- seskey.c --*/ void make_session_key( DEK *dek ); MPI encode_session_key( DEK *dek, unsigned nbits ); MPI encode_md_value( int pubkey_algo, GCRY_MD_HD md, - int hash_algo, unsigned nbits ); + int hash_algo, unsigned nbits, int v3compathack ); /*-- comment.c --*/ KBNODE make_comment_node( const char *s ); KBNODE make_mpi_comment_node( const char *s, MPI a ); /*-- import.c --*/ -int import_keys( const char *filename, int fast ); +void import_keys( char **fnames, int nnames, int fast ); int import_keys_stream( IOBUF inp, int fast ); int collapse_uids( KBNODE *keyblock ); @@ -137,20 +136,27 @@ int collapse_uids( KBNODE *keyblock ); int export_pubkeys( STRLIST users, int onlyrfc ); int export_pubkeys_stream( IOBUF out, STRLIST users, int onlyrfc ); int export_seckeys( STRLIST users ); +int export_secsubkeys( STRLIST users ); /* dearmor.c --*/ int dearmor_file( const char *fname ); int enarmor_file( const char *fname ); /*-- revoke.c --*/ +struct revocation_reason_info; int gen_revoke( const char *uname ); +int revocation_reason_build_cb( PKT_signature *sig, void *opaque ); +struct revocation_reason_info * + ask_revocation_reason( int key_rev, int cert_rev, int hint ); +void release_revocation_reason_info( struct revocation_reason_info *reason ); /*-- keylist.c --*/ -void public_key_list( int nnames, char **names ); -void secret_key_list( int nnames, char **names ); +void public_key_list( STRLIST list ); +void secret_key_list( STRLIST list ); /*-- verify.c --*/ int verify_signatures( int nfiles, char **files ); +int verify_files( int nfiles, char **files ); /*-- decrypt.c --*/ int decrypt_message( const char *filename ); diff --git a/g10/mainproc.c b/g10/mainproc.c index f2637340c..09c35db21 100644 --- a/g10/mainproc.c +++ b/g10/mainproc.c @@ -1,5 +1,5 @@ /* mainproc.c - handle packets - * Copyright (C) 1998, 1999 Free Software Foundation, Inc. + * Copyright (C) 1998, 1999, 2000 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -160,11 +160,11 @@ add_signature( CTX c, PACKET *pkt ) if( pkt->pkttype == PKT_SIGNATURE && !c->list ) { /* This is the first signature for the following datafile. - * gpg does not write such packets; instead it always uses + * GPG does not write such packets; instead it always uses * onepass-sig packets. The drawback of PGP's method * of prepending the signature to the data is * that it is not possible to make a signature from data read - * from stdin. (gpg is able to read PGP stuff anyway.) */ + * from stdin. (GPG is able to read PGP stuff anyway.) */ node = new_kbnode( pkt ); c->list = node; return 1; @@ -224,10 +224,14 @@ proc_pubkey_enc( CTX c, PACKET *pkt ) || is_RSA(enc->pubkey_algo) ) { if ( !c->dek && ((!enc->keyid[0] && !enc->keyid[1]) || !seckey_available( enc->keyid )) ) { - c->dek = gcry_xmalloc_secure( sizeof *c->dek ); - if( (result = get_session_key( enc, c->dek )) ) { - /* error: delete the DEK */ - gcry_free(c->dek); c->dek = NULL; + if( opt.list_only ) + result = -1; + else { + c->dek = gcry_xmalloc_secure( sizeof *c->dek ); + if( (result = get_session_key( enc, c->dek )) ) { + /* error: delete the DEK */ + gcry_free(c->dek); c->dek = NULL; + } } } else @@ -267,6 +271,8 @@ print_failed_pkenc( struct kidlist_item *list ) PKT_public_key *pk = gcry_xcalloc( 1, sizeof *pk ); const char *algstr = gcry_pk_algo_name( list->pubkey_algo ); + if( !algstr ) + algstr = "[?]"; pk->pubkey_algo = list->pubkey_algo; if( !get_pubkey( pk, list->kid ) ) { size_t n; @@ -309,10 +315,14 @@ proc_encrypted( CTX c, PACKET *pkt ) print_failed_pkenc( c->failed_pkenc ); + write_status( STATUS_BEGIN_DECRYPTION ); + /*log_debug("dat: %sencrypted data\n", c->dek?"":"conventional ");*/ - if( !c->dek && !c->last_was_session_key ) { + if( opt.list_only ) + result = -1; + else if( !c->dek && !c->last_was_session_key ) { /* assume this is old conventional encrypted data - * Actually we should use IDEA and MD5 in this case, but becuase + * Actually we should use IDEA and MD5 in this case, but because * IDEA is patented we can't do so */ c->dek = passphrase_to_dek( NULL, 0, opt.def_cipher_algo ? opt.def_cipher_algo @@ -322,6 +332,7 @@ proc_encrypted( CTX c, PACKET *pkt ) result = GPGERR_NO_SECKEY; if( !result ) result = decrypt_data( c, pkt->pkt.encrypted, c->dek ); + gcry_free(c->dek); c->dek = NULL; if( result == -1 ) ; @@ -339,11 +350,12 @@ proc_encrypted( CTX c, PACKET *pkt ) else { write_status( STATUS_DECRYPTION_FAILED ); log_error(_("decryption failed: %s\n"), gpg_errstr(result)); - /* Hmmm: does this work when we have encrypted using a multiple + /* Hmmm: does this work when we have encrypted using multiple * ways to specify the session key (symmmetric and PK)*/ } free_packet(pkt); c->last_was_session_key = 0; + write_status( STATUS_END_DECRYPTION ); } @@ -360,7 +372,8 @@ proc_plaintext( CTX c, PACKET *pkt ) else if( opt.verbose ) log_info(_("original file name='%.*s'\n"), pt->namelen, pt->name); free_md_filter_context( &c->mfx ); - if( !(c->mfx.md = gcry_md_open( 0, 0)) ) + c->mfx.md = gcry_md_open( 0, 0); + if( !c->mfx.md ) BUG(); /* fixme: we may need to push the textfilter if we have sigclass 1 * and no armoring - Not yet tested @@ -372,8 +385,7 @@ proc_plaintext( CTX c, PACKET *pkt ) for(n=c->list; n; n = n->next ) { if( n->pkt->pkttype == PKT_ONEPASS_SIG ) { if( n->pkt->pkt.onepass_sig->digest_algo ) { - gcry_md_enable( c->mfx.md, - n->pkt->pkt.onepass_sig->digest_algo ); + gcry_md_enable( c->mfx.md, n->pkt->pkt.onepass_sig->digest_algo ); if( !any && n->pkt->pkt.onepass_sig->digest_algo == GCRY_MD_MD5 ) only_md5 = 1; @@ -398,24 +410,28 @@ proc_plaintext( CTX c, PACKET *pkt ) clearsig = 1; } } - if( !any ) { /* no onepass sig packet: enable all standard algos */ + + if( !any && !opt.skip_verify ) { + /* no onepass sig packet: enable all standard algos */ gcry_md_enable( c->mfx.md, GCRY_MD_RMD160 ); gcry_md_enable( c->mfx.md, GCRY_MD_SHA1 ); gcry_md_enable( c->mfx.md, GCRY_MD_MD5 ); } - if( only_md5 ) { + if( opt.pgp2_workarounds && only_md5 && !opt.skip_verify ) { /* This is a kludge to work around a bug in pgp2. It does only * catch those mails which are armored. To catch the non-armored * pgp mails we could see whether there is the signature packet * in front of the plaintext. If someone needs this, send me a patch. */ - if( !(c->mfx.md2 = gcry_md_open( GCRY_MD_MD5, 0)) ) + c->mfx.md2 = gcry_md_open( GCRY_MD_MD5, 0); + if( !c->mfx.md2 ) BUG(); } - #if 0 - #warning md_start_debug is enabled - md_start_debug( c->mfx.md, "verify" ); - #endif + if ( DBG_HASHING ) { + gcry_md_start_debug( c->mfx.md, "verify" ); + if ( c->mfx.md2 ) + gcry_md_start_debug( c->mfx.md2, "verify2" ); + } rc = handle_plaintext( pt, &c->mfx, c->sigs_only, clearsig ); if( rc == GPGERR_CREATE_FILE && !c->sigs_only) { /* can't write output but we hash it anyway to @@ -494,12 +510,17 @@ do_check_sig( CTX c, KBNODE node, int *is_selfsig ) * in canonical mode ??? (calculating both modes???) */ if( c->mfx.md ) { md = gcry_md_copy( c->mfx.md ); - if( c->mfx.md2 ) + if( !md ) + BUG(); + if( c->mfx.md2 ) { md2 = gcry_md_copy( c->mfx.md2 ); + if( !md2 ) + BUG(); + } } else { /* detached signature */ - log_debug("Do we really need this here?"); - md = gcry_md_open( 0, 0 ); /* signature_check() will enable the md*/ + log_debug("Do we really need this here?"); + md = gcry_md_open( 0, 0 ); /* signature_check() will enable the md*/ md2 = gcry_md_open( 0, 0 ); if( !md || !md2 ) BUG(); @@ -513,6 +534,11 @@ do_check_sig( CTX c, KBNODE node, int *is_selfsig ) || c->list->pkt->pkttype == PKT_PUBLIC_SUBKEY ) { return check_key_signature( c->list, node, is_selfsig ); } + else if( sig->sig_class == 0x20 ) { + log_info(_("standalone revocation - " + "use \"gpg --import\" to apply\n")); + return GPGERR_NOT_PROCESSED; + } else { log_error("invalid root packet for sigclass %02x\n", sig->sig_class); @@ -540,8 +566,12 @@ print_userid( PACKET *pkt ) printf("ERROR: unexpected packet type %d", pkt->pkttype ); return; } - print_string( stdout, pkt->pkt.user_id->name, pkt->pkt.user_id->len, - opt.with_colons ); + if( opt.with_colons ) + print_string( stdout, pkt->pkt.user_id->name, + pkt->pkt.user_id->len, ':'); + else + print_utf8_string( stdout, pkt->pkt.user_id->name, + pkt->pkt.user_id->len ); } @@ -644,11 +674,13 @@ list_node( CTX c, KBNODE node ) keyid_from_pk( pk, keyid ); if( mainkey ) { c->local_id = pk->local_id; - c->trustletter = query_trust_info( pk, NULL ); + c->trustletter = opt.fast_list_mode? + 0 : query_trust_info( pk, NULL ); } - printf("%s:%c:%u:%d:%08lX%08lX:%s:%s:", - mainkey? "pub":"sub", - c->trustletter, + printf("%s:", mainkey? "pub":"sub" ); + if( c->trustletter ) + putchar( c->trustletter ); + printf(":%u:%d:%08lX%08lX:%s:%s:", nbits_from_pk( pk ), pk->pubkey_algo, (ulong)keyid[0],(ulong)keyid[1], @@ -657,7 +689,7 @@ list_node( CTX c, KBNODE node ) if( c->local_id ) printf("%lu", c->local_id ); putchar(':'); - if( c->local_id ) + if( c->local_id && !opt.fast_list_mode ) putchar( get_ownertrust_info( c->local_id ) ); putchar(':'); if( node->next && node->next->pkt->pkttype == PKT_RING_TRUST) { @@ -675,6 +707,7 @@ list_node( CTX c, KBNODE node ) pubkey_letter( pk->pubkey_algo ), (ulong)keyid_from_pk( pk, NULL ), datestr_from_pk( pk ) ); + if( mainkey ) { /* and now list all userids with their signatures */ for( node = node->next; node; node = node->next ) { @@ -717,6 +750,10 @@ list_node( CTX c, KBNODE node ) } } } + else if( pk->expiredate ) { /* of subkey */ + printf(_(" [expires: %s]"), expirestr_from_pk( pk ) ); + } + if( !any ) putchar('\n'); if( !mainkey && opt.fingerprint > 1 ) @@ -847,7 +884,7 @@ list_node( CTX c, KBNODE node ) if( opt.with_colons ) putchar(':'); } - else { + else if( !opt.fast_list_mode ) { p = get_user_id( sig->keyid, &n ); print_string( stdout, p, n, opt.with_colons ); gcry_free(p); @@ -861,6 +898,7 @@ list_node( CTX c, KBNODE node ) } + int proc_packets( void *anchor, IOBUF a ) { @@ -873,6 +911,8 @@ proc_packets( void *anchor, IOBUF a ) return rc; } + + int proc_signature_packets( void *anchor, IOBUF a, STRLIST signedfiles, const char *sigfilename ) @@ -1027,7 +1067,7 @@ static int check_sig_and_print( CTX c, KBNODE node ) { PKT_signature *sig = node->pkt->pkt.signature; - const char *tstr; + const char *astr, *tstr; int rc; if( opt.skip_verify ) { @@ -1036,12 +1076,12 @@ check_sig_and_print( CTX c, KBNODE node ) } tstr = asctimestamp(sig->timestamp); + astr = gcry_pk_algo_name( sig->pubkey_algo ); log_info(_("Signature made %.*s using %s key ID %08lX\n"), - (int)strlen(tstr), tstr, gcry_pk_algo_name( sig->pubkey_algo ), - (ulong)sig->keyid[1] ); + (int)strlen(tstr), tstr, astr? astr: "?", (ulong)sig->keyid[1] ); rc = do_check_sig(c, node, NULL ); - if( rc == GPGERR_NO_PUBKEY && opt.keyserver_name ) { + if( rc == GPGERR_NO_PUBKEY && opt.keyserver_name && opt.auto_key_retrieve) { if( !hkp_ask_import( sig->keyid ) ) rc = do_check_sig(c, node, NULL ); } @@ -1121,7 +1161,8 @@ check_sig_and_print( CTX c, KBNODE node ) buf[16] = 0; write_status_text( STATUS_NO_PUBKEY, buf ); } - log_error(_("Can't check signature: %s\n"), gpg_errstr(rc) ); + if( rc != GPGERR_NOT_PROCESSED ) + log_error(_("Can't check signature: %s\n"), gpg_errstr(rc) ); } return rc; } @@ -1136,7 +1177,7 @@ proc_tree( CTX c, KBNODE node ) KBNODE n1; int rc; - if( opt.list_packets ) + if( opt.list_packets || opt.list_only ) return; c->local_id = 0; @@ -1155,7 +1196,7 @@ proc_tree( CTX c, KBNODE node ) if( !c->have_data ) { free_md_filter_context( &c->mfx ); /* prepare to create all requested message digests */ - if( !(c->mfx.md = gcry_md_open(0, 0)) ) + if ( !(c->mfx.md = gcry_md_open(0, 0)) ) BUG(); /* fixme: why looking for the signature packet and not 1passpacket*/ @@ -1163,13 +1204,16 @@ proc_tree( CTX c, KBNODE node ) gcry_md_enable( c->mfx.md, n1->pkt->pkt.signature->digest_algo); } /* ask for file and hash it */ - if( c->sigs_only ) + if( c->sigs_only ) { rc = hash_datafiles( c->mfx.md, NULL, c->signed_data, c->sigfilename, n1? (n1->pkt->pkt.onepass_sig->sig_class == 0x01):0 ); - else + } + else { rc = ask_for_detached_datafile( c->mfx.md, c->mfx.md2, - iobuf_get_fname(c->iobuf), 0 ); + iobuf_get_fname(c->iobuf), + n1? (n1->pkt->pkt.onepass_sig->sig_class == 0x01):0 ); + } if( rc ) { log_error("can't hash datafile: %s\n", gpg_errstr(rc)); return; @@ -1182,15 +1226,22 @@ proc_tree( CTX c, KBNODE node ) else if( node->pkt->pkttype == PKT_SIGNATURE ) { PKT_signature *sig = node->pkt->pkt.signature; - if( !c->have_data ) { + if( sig->sig_class != 0x00 && sig->sig_class != 0x01 ) + log_info(_("standalone signature of class 0x%02x\n"), + sig->sig_class); + else if( !c->have_data ) { /* detached signature */ free_md_filter_context( &c->mfx ); - if( !(c->mfx.md = gcry_md_open(sig->digest_algo, 0)) ) + c->mfx.md = gcry_md_open(sig->digest_algo, 0); + if ( !c->mfx.md ) BUG(); - if( sig->digest_algo == GCRY_MD_MD5 - && is_RSA( sig->pubkey_algo ) ) { + if( !opt.pgp2_workarounds ) + ; + else if( sig->digest_algo == GCRY_MD_MD5 + && is_RSA( sig->pubkey_algo ) ) { /* enable a workaround for a pgp2 bug */ - if( !(c->mfx.md2 = gcry_md_open( GCRY_MD_MD5, 0 )) ) + c->mfx.md2 = gcry_md_open( GCRY_MD_MD5, 0 ); + if ( !c->mfx.md2 ) BUG(); } else if( sig->digest_algo == GCRY_MD_SHA1 @@ -1198,9 +1249,11 @@ proc_tree( CTX c, KBNODE node ) && sig->sig_class == 0x01 ) { /* enable the workaround also for pgp5 when the detached * signature has been created in textmode */ - if( !(c->mfx.md2 = gcry_md_open( sig->digest_algo, 0 )) ) + c->mfx.md2 = gcry_md_open( sig->digest_algo, 0 ); + if ( !c->mfx.md2 ) BUG(); } + #if 0 /* workaround disabled */ /* Here we have another hack to work around a pgp 2 bug * It works by not using the textmode for detached signatures; * this will let the first signature check (on md) fail @@ -1208,14 +1261,18 @@ proc_tree( CTX c, KBNODE node ) * then produce the "correct" hash. This is very, very ugly * hack but it may help in some cases (and break others) */ - if( c->sigs_only ) + /* c->mfx.md2? 0 :(sig->sig_class == 0x01) */ + #endif + if( c->sigs_only ) { rc = hash_datafiles( c->mfx.md, c->mfx.md2, c->signed_data, c->sigfilename, - c->mfx.md2? 0 :(sig->sig_class == 0x01) ); - else + (sig->sig_class == 0x01) ); + } + else { rc = ask_for_detached_datafile( c->mfx.md, c->mfx.md2, - iobuf_get_fname(c->iobuf), - c->mfx.md2? 0 :(sig->sig_class == 0x01) ); + iobuf_get_fname(c->iobuf), + (sig->sig_class == 0x01) ); + } if( rc ) { log_error("can't hash datafile: %s\n", gpg_errstr(rc)); return; diff --git a/g10/mdfilter.c b/g10/mdfilter.c index abb55d8c1..c41ad857e 100644 --- a/g10/mdfilter.c +++ b/g10/mdfilter.c @@ -1,5 +1,5 @@ /* mdfilter.c - filter data and calculate a message digest - * Copyright (C) 1998 Free Software Foundation, Inc. + * Copyright (C) 1998, 1999, 2000 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -25,9 +25,9 @@ #include #include +#include #include "errors.h" #include "iobuf.h" -#include #include "util.h" #include "filter.h" diff --git a/g10/misc.c b/g10/misc.c index d3c01f4ef..e7bd8fac6 100644 --- a/g10/misc.c +++ b/g10/misc.c @@ -1,5 +1,5 @@ /* misc.c - miscellaneous functions - * Copyright (C) 1998, 1999 Free Software Foundation, Inc. + * Copyright (C) 1998, 1999, 2000 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -23,7 +23,6 @@ #include #include #include -#include #if defined(__linux__) && defined(__alpha__) && __GLIBC__ < 2 #include #include @@ -32,6 +31,8 @@ #include #include #endif +#include + #include #include "util.h" #include "main.h" @@ -41,7 +42,6 @@ #define MAX_EXTERN_MPI_BITS 16384 - #if defined(__linux__) && defined(__alpha__) && __GLIBC__ < 2 #warning using trap_unaligned static int @@ -246,6 +246,7 @@ mpi_print( FILE *fp, MPI a, int mode ) } + u16 checksum_u16( unsigned n ) { @@ -289,7 +290,6 @@ checksum_mpi( MPI a ) } - u32 buffer_to_u32( const byte *buffer ) { @@ -359,6 +359,7 @@ print_digest_algo_note( int algo ) } + /**************** * Wrapper around the libgcrypt function with addional checks on * openPGP contrainst for the algo ID. diff --git a/g10/openfile.c b/g10/openfile.c index 0b768fe5e..47dca0e76 100644 --- a/g10/openfile.c +++ b/g10/openfile.c @@ -1,5 +1,5 @@ /* openfile.c - * Copyright (C) 1998 Free Software Foundation, Inc. + * Copyright (C) 1998, 1999, 2000 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -24,6 +24,9 @@ #include #include #include +#include +#include +#include #include #include "util.h" #include @@ -39,13 +42,16 @@ #define SKELEXT ".skel" #endif - #ifdef HAVE_DRIVE_LETTERS #define CMP_FILENAME(a,b) stricmp( (a), (b) ) #else #define CMP_FILENAME(a,b) strcmp( (a), (b) ) #endif +#ifdef MKDIR_TAKES_ONE_ARG +# undef mkdir +# define mkdir(a,b) mkdir(a) +#endif /* FIXME: Implement opt.interactive. */ @@ -78,7 +84,6 @@ overwrite_filep( const char *fname ) } - /**************** * Strip know extensions from iname and return a newly allocated * filename. Return NULL if we can't do that. @@ -176,7 +181,7 @@ open_outfile( const char *iname, int mode, IOBUF *a ) name = opt.outfile; else { #ifdef USE_ONLY_8DOT3 - /* It is quite common for DOS system to have only one dot in a + /* It is quite common DOS system to have only one dot in a * a filename So if we have something like this, we simple * replace the suffix execpt in cases where the suffix is * larger than 3 characters and not the same as. @@ -195,7 +200,7 @@ open_outfile( const char *iname, int mode, IOBUF *a ) strcpy(dot, newsfx ); } else if( dot && !dot[1] ) /* don't duplicate a dot */ - strcat( dot, newsfx+1 ); + strcpy( dot, newsfx+1 ); else strcat( buf, newsfx ); #else @@ -253,7 +258,7 @@ open_sigfile( const char *iname ) /**************** * Copy the option file skeleton to the given directory. */ -void +static void copy_options_file( const char *destdir ) { const char *datadir = GNUPG_DATADIR; @@ -296,3 +301,23 @@ copy_options_file( const char *destdir ) gcry_free(fname); } + +void +try_make_homedir( const char *fname ) +{ + if( opt.dry_run ) + return; + if( strlen(fname) >= 7 + && !strcmp(fname+strlen(fname)-7, "/.gnupg" ) ) { + if( mkdir( fname, S_IRUSR|S_IWUSR|S_IXUSR ) ) + log_fatal( _("%s: can't create directory: %s\n"), + fname, strerror(errno) ); + else if( !opt.quiet ) + log_info( _("%s: directory created\n"), fname ); + copy_options_file( fname ); + log_info(_("you have to start GnuPG again, " + "so it can read the new options file\n") ); + gpg_exit(1); + } +} + diff --git a/g10/options.h b/g10/options.h index cbe6c24e0..79b6bcd55 100644 --- a/g10/options.h +++ b/g10/options.h @@ -1,5 +1,5 @@ /* options.h - * Copyright (C) 1998 Free Software Foundation, Inc. + * Copyright (C) 1998, 1999, 2000 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -28,11 +28,12 @@ struct { int verbose; int quiet; - unsigned debug; + unsigned int debug; int armor; int compress; char *outfile; int dry_run; + int list_only; int textmode; int batch; /* run in batch mode */ int answer_yes; /* answer yes on most questions */ @@ -63,7 +64,9 @@ struct { int compress_sigs; int always_trust; int rfc1991; - unsigned emulate_bugs; /* bug emulation flags EMUBUG_xxxx */ + int rfc2440; + int pgp2_workarounds; + unsigned int emulate_bugs; /* bug emulation flags EMUBUG_xxxx */ int shm_coprocess; const char *set_filename; const char *comment_string; @@ -83,9 +86,17 @@ struct { int allow_non_selfsigned_uid; int no_literal; ulong set_filesize; + int honor_http_proxy; + int fast_list_mode; + int ignore_time_conflict; + int command_fd; + int auto_key_retrieve; } opt; +#define EMUBUG_3DESS2K 2 +#define EMUBUG_MDENCODE 4 + #define DBG_PACKET_VALUE 1 /* debug packet reading/writing */ #define DBG_MPI_VALUE 2 /* debug mpi details */ #define DBG_CIPHER_VALUE 4 /* debug cipher handling */ @@ -96,6 +107,7 @@ struct { #define DBG_CACHE_VALUE 64 /* debug the cacheing */ #define DBG_MEMSTAT_VALUE 128 /* show memory statistics */ #define DBG_TRUST_VALUE 256 /* debug the trustdb */ +#define DBG_HASHING_VALUE 512 /* debug hashing operations */ #define DBG_PACKET (opt.debug & DBG_PACKET_VALUE) @@ -104,6 +116,6 @@ struct { #define DBG_CACHE (opt.debug & DBG_CACHE_VALUE) #define DBG_TRUST (opt.debug & DBG_TRUST_VALUE) #define DBG_CIPHER (opt.debug & DBG_CIPHER_VALUE) - +#define DBG_HASHING (opt.debug & DBG_HASHING_VALUE) #endif /*GPG_OPTIONS_H*/ diff --git a/g10/packet.h b/g10/packet.h index 02f701799..f6da15e17 100644 --- a/g10/packet.h +++ b/g10/packet.h @@ -1,5 +1,5 @@ /* packet.h - packet read/write stuff - * Copyright (C) 1998 Free Software Foundation, Inc. + * Copyright (C) 1998, 1999, 2000 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -54,14 +54,16 @@ typedef enum { PKT_USER_ID =13, /* user id packet */ PKT_PUBLIC_SUBKEY =14, /* public subkey (OpenPGP) */ PKT_OLD_COMMENT =16, /* comment packet from an OpenPGP draft */ + PKT_PHOTO_ID =17, /* PGP's photo ID */ + PKT_ENCRYPTED_MDC =18, /* integrity protected encrypted data */ + PKT_MDC =19, /* manipulaion detection code packet */ PKT_COMMENT =61, /* new comment packet (private) */ - PKT_ENCRYPTED_MDC =62, /* test: encrypted data with MDC */ } pkttype_t; typedef struct packet_struct PACKET; typedef struct { - byte mode; + int mode; byte hash_algo; byte salt[8]; u32 count; @@ -105,7 +107,7 @@ typedef struct { byte version; byte sig_class; /* sig classification, append for MD calculation*/ byte pubkey_algo; /* algorithm used for public key scheme */ - /* (PUBKEY_ALGO_xxx) */ + /* (GCRY_PK_xxx) */ byte digest_algo; /* algorithm used for digest (DIGEST_ALGO_xxxx) */ byte *hashed_data; /* all subpackets with hashed data (v4 only) */ byte *unhashed_data; /* ditto for unhashed data */ @@ -165,6 +167,8 @@ typedef struct { ulong stored_at; /* the stream offset where it was stored * by build-packet */ int len; /* length of the name */ + char *photo; /* if this is not NULL, the packet is a photo ID */ + int photolen; /* and the length of the photo */ char name[1]; } PKT_user_id; @@ -178,10 +182,14 @@ typedef struct { typedef struct { u32 len; /* length of encrypted data */ byte new_ctb; /* uses a new CTB */ - byte mdc_method; /* test: > 0: this is is an encrypted_mdc packet */ + byte mdc_method; /* > 0: integrity protected encrypted data packet */ IOBUF buf; /* IOBUF reference */ } PKT_encrypted; +typedef struct { + byte hash[20]; +} PKT_mdc; + typedef struct { unsigned int trustval; } PKT_ring_trust; @@ -212,6 +220,7 @@ struct packet_struct { PKT_user_id *user_id; /* PKT_USER_ID */ PKT_compressed *compressed; /* PKT_COMPRESSED */ PKT_encrypted *encrypted; /* PKT_ENCRYPTED[_MDC] */ + PKT_mdc *mdc; /* PKT_MDC */ PKT_ring_trust *ring_trust; /* PKT_RING_TRUST */ PKT_plaintext *plaintext; /* PKT_PLAINTEXT */ } pkt; @@ -246,6 +255,7 @@ typedef enum { SIGSUBPKT_POLICY =26, /* policy URL */ SIGSUBPKT_KEY_FLAGS =27, /* key flags */ SIGSUBPKT_SIGNERS_UID =28, /* signer's user id */ + SIGSUBPKT_REVOC_REASON =29, /* reason for revocation */ SIGSUBPKT_PRIV_ADD_SIG =101,/* signatur is also valid for this uid */ SIGSUBPKT_FLAG_CRITICAL=128 @@ -339,7 +349,6 @@ int handle_compressed( void *ctx, PKT_compressed *cd, /*-- encr-data.c --*/ int decrypt_data( void *ctx, PKT_encrypted *ed, DEK *dek ); -int encrypt_data( PKT_encrypted *ed, DEK *dek ); /*-- plaintext.c --*/ int handle_plaintext( PKT_plaintext *pt, md_filter_context_t *mfx, diff --git a/g10/parse-packet.c b/g10/parse-packet.c index 886ba4a25..867f93d1d 100644 --- a/g10/parse-packet.c +++ b/g10/parse-packet.c @@ -1,5 +1,5 @@ /* parse-packet.c - read packets - * Copyright (C) 1998, 1999 Free Software Foundation, Inc. + * Copyright (C) 1998, 1999, 2000 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -24,10 +24,10 @@ #include #include +#include #include "packet.h" #include "iobuf.h" #include "util.h" -#include #include "filter.h" #include "options.h" #include "main.h" @@ -59,6 +59,8 @@ static int parse_key( IOBUF inp, int pkttype, unsigned long pktlen, byte *hdr, int hdrlen, PACKET *packet ); static int parse_user_id( IOBUF inp, int pkttype, unsigned long pktlen, PACKET *packet ); +static int parse_photo_id( IOBUF inp, int pkttype, unsigned long pktlen, + PACKET *packet ); static int parse_comment( IOBUF inp, int pkttype, unsigned long pktlen, PACKET *packet ); static void parse_trust( IOBUF inp, int pkttype, unsigned long pktlen, @@ -69,6 +71,8 @@ static int parse_compressed( IOBUF inp, int pkttype, unsigned long pktlen, PACKET *packet, int new_ctb ); static int parse_encrypted( IOBUF inp, int pkttype, unsigned long pktlen, PACKET *packet, int new_ctb); +static int parse_mdc( IOBUF inp, int pkttype, unsigned long pktlen, + PACKET *packet, int new_ctb); static unsigned short read_16(IOBUF inp) @@ -415,6 +419,10 @@ parse( IOBUF inp, PACKET *pkt, int reqtype, ulong *retpos, case PKT_USER_ID: rc = parse_user_id(inp, pkttype, pktlen, pkt ); break; + case PKT_PHOTO_ID: + pkt->pkttype = pkttype = PKT_USER_ID; /* must fix it */ + rc = parse_photo_id(inp, pkttype, pktlen, pkt); + break; case PKT_OLD_COMMENT: case PKT_COMMENT: rc = parse_comment(inp, pkttype, pktlen, pkt); @@ -433,6 +441,9 @@ parse( IOBUF inp, PACKET *pkt, int reqtype, ulong *retpos, case PKT_ENCRYPTED_MDC: rc = parse_encrypted(inp, pkttype, pktlen, pkt, new_ctb ); break; + case PKT_MDC: + rc = parse_mdc(inp, pkttype, pktlen, pkt, new_ctb ); + break; default: skip_packet(inp, pkttype, pktlen); break; @@ -803,6 +814,13 @@ dump_sig_subpkt( int hashed, int type, int critical, case SIGSUBPKT_SIGNERS_UID: p = "signer's user ID"; break; + case SIGSUBPKT_REVOC_REASON: + if( length ) { + printf("revocation reason 0x%02x (", *buffer ); + print_string( stdout, buffer+1, length-1, ')' ); + p = ")"; + } + break; case SIGSUBPKT_PRIV_ADD_SIG: p = "signs additional user ID"; break; @@ -840,6 +858,10 @@ parse_one_sig_subpkt( const byte *buffer, size_t n, int type ) if( n < 8 ) /* minimum length needed */ break; return 0; + case SIGSUBPKT_REVOC_REASON: + if( !n ) + break; + return 0; case SIGSUBPKT_PREF_SYM: case SIGSUBPKT_PREF_HASH: case SIGSUBPKT_PREF_COMPR: @@ -877,7 +899,7 @@ can_handle_critical( const byte *buffer, size_t n, int type ) case SIGSUBPKT_PREF_COMPR: return 1; - case SIGSUBPKT_POLICY: /* Is enough to show the policy? */ + case SIGSUBPKT_POLICY: /* Is it enough to show the policy? */ default: return 0; } @@ -896,8 +918,12 @@ enum_sig_subpkt( const byte *buffer, sigsubpkttype_t reqtype, int seq = 0; int reqseq = start? *start: 0; - if( !buffer || reqseq == -1 ) - return NULL; + if( !buffer || reqseq == -1 ) { + /* return some value different from NULL to indicate that + * there is no crtitical bit we do not understand. The caller + * will never use the value. Yes I know, it is an ugly hack */ + return reqtype == SIGSUBPKT_TEST_CRITICAL? (const byte*)&buffer : NULL; + } buflen = (*buffer << 8) | buffer[1]; buffer += 2; while( buflen ) { @@ -1131,8 +1157,7 @@ parse_signature( IOBUF inp, int pkttype, unsigned long pktlen, unknown_pubkey_warning( sig->pubkey_algo ); /* we store the plain material in data[0], so that we are able * to write it back with build_packet() */ - sig->data[0] = gcry_mpi_set_opaque(NULL, - read_rest(inp, pktlen), pktlen*8 ); + sig->data[0] = mpi_set_opaque(NULL, read_rest(inp, pktlen), pktlen ); pktlen = 0; } else { @@ -1317,6 +1342,24 @@ parse_key( IOBUF inp, int pkttype, unsigned long pktlen, sk->protect.algo = iobuf_get_noeof(inp); pktlen--; sk->protect.s2k.mode = iobuf_get_noeof(inp); pktlen--; sk->protect.s2k.hash_algo = iobuf_get_noeof(inp); pktlen--; + /* check for the special GNU extension */ + if( is_v4 && sk->protect.s2k.mode == 101 ) { + for(i=0; i < 4 && pktlen; i++, pktlen-- ) + temp[i] = iobuf_get_noeof(inp); + if( i < 4 || memcmp( temp, "GNU", 3 ) ) { + if( list_mode ) + printf( "\tunknown S2K %d\n", + sk->protect.s2k.mode ); + rc = GPGERR_INVALID_PACKET; + goto leave; + } + /* here we know that it is a gnu extension + * What follows is the GNU protection mode: + * All values have special meanings + * and they are mapped in the mode with a base of 1000. + */ + sk->protect.s2k.mode = 1000 + temp[3]; + } switch( sk->protect.s2k.mode ) { case 1: case 3: @@ -1332,10 +1375,13 @@ parse_key( IOBUF inp, int pkttype, unsigned long pktlen, break; case 3: if( list_mode ) printf( "\titer+salt S2K" ); break; + case 1001: if( list_mode ) printf( "\tgnu-dummy S2K" ); + break; default: if( list_mode ) - printf( "\tunknown S2K %d\n", - sk->protect.s2k.mode ); + printf( "\tunknown %sS2K %d\n", + sk->protect.s2k.mode < 1000? "":"GNU ", + sk->protect.s2k.mode ); rc = GPGERR_INVALID_PACKET; goto leave; } @@ -1388,6 +1434,9 @@ parse_key( IOBUF inp, int pkttype, unsigned long pktlen, default: sk->protect.ivlen = 8; } + if( sk->protect.s2k.mode == 1001 ) + sk->protect.ivlen = 0; + if( pktlen < sk->protect.ivlen ) { rc = GPGERR_INVALID_PACKET; goto leave; @@ -1408,10 +1457,15 @@ parse_key( IOBUF inp, int pkttype, unsigned long pktlen, * If the user is so careless, not to protect his secret key, * we can assume, that he operates an open system :=(. * So we put the key into secure memory when we unprotect it. */ - if( is_v4 && sk->is_protected ) { + if( sk->protect.s2k.mode == 1001 ) { + /* better set some dummy stuff here */ + sk->skey[npkey] = mpi_set_opaque(NULL, gcry_xstrdup("dummydata"), 10); + pktlen = 0; + } + else if( is_v4 && sk->is_protected ) { /* ugly; the length is encrypted too, so we read all * stuff up to the end of the packet into the first - * skey element (which is the one indexed by npkey) */ + * skey element */ sk->skey[npkey] = gcry_mpi_set_opaque(NULL, read_rest(inp, pktlen), pktlen*8 ); pktlen = 0; @@ -1475,6 +1529,8 @@ parse_user_id( IOBUF inp, int pkttype, unsigned long pktlen, PACKET *packet ) packet->pkt.user_id = gcry_xmalloc(sizeof *packet->pkt.user_id + pktlen); packet->pkt.user_id->len = pktlen; + packet->pkt.user_id->photo = NULL; + packet->pkt.user_id->photolen = 0; p = packet->pkt.user_id->name; for( ; pktlen; pktlen--, p++ ) *p = iobuf_get_noeof(inp); @@ -1496,6 +1552,31 @@ parse_user_id( IOBUF inp, int pkttype, unsigned long pktlen, PACKET *packet ) } +/**************** + * PGP generates a packet of type 17. We assume this is a photo ID and + * simply store it here as a comment packet. + */ +static int +parse_photo_id( IOBUF inp, int pkttype, unsigned long pktlen, PACKET *packet ) +{ + byte *p; + + packet->pkt.user_id = gcry_xmalloc(sizeof *packet->pkt.user_id + 30); + sprintf( packet->pkt.user_id->name, "[image of size %lu]", pktlen ); + packet->pkt.user_id->len = strlen(packet->pkt.user_id->name); + + packet->pkt.user_id->photo = gcry_xmalloc(sizeof *packet->pkt.user_id + pktlen); + packet->pkt.user_id->photolen = pktlen; + p = packet->pkt.user_id->photo; + for( ; pktlen; pktlen--, p++ ) + *p = iobuf_get_noeof(inp); + + if( list_mode ) { + printf(":photo_id packet: %s\n", packet->pkt.user_id->name ); + } + return 0; +} + static int parse_comment( IOBUF inp, int pkttype, unsigned long pktlen, PACKET *packet ) @@ -1624,9 +1705,8 @@ parse_encrypted( IOBUF inp, int pkttype, unsigned long pktlen, ed->new_ctb = new_ctb; ed->mdc_method = 0; if( pkttype == PKT_ENCRYPTED_MDC ) { - /* test: this is the new encrypted_mdc packet */ /* fixme: add some pktlen sanity checks */ - int version, method; + int version; version = iobuf_get_noeof(inp); pktlen--; if( version != 1 ) { @@ -1634,12 +1714,7 @@ parse_encrypted( IOBUF inp, int pkttype, unsigned long pktlen, version); goto leave; } - method = iobuf_get_noeof(inp); pktlen--; - if( method != GCRY_MD_SHA1 ) { - log_error("encrypted_mdc does not use SHA1 method\n" ); - goto leave; - } - ed->mdc_method = method; + ed->mdc_method = GCRY_MD_SHA1; } if( pktlen && pktlen < 10 ) { /* actually this is blocksize+2 */ log_error("packet(%d) too short\n", pkttype); @@ -1662,3 +1737,26 @@ parse_encrypted( IOBUF inp, int pkttype, unsigned long pktlen, return 0; } + +static int +parse_mdc( IOBUF inp, int pkttype, unsigned long pktlen, + PACKET *pkt, int new_ctb ) +{ + PKT_mdc *mdc; + byte *p; + + mdc = pkt->pkt.mdc= gcry_xmalloc(sizeof *pkt->pkt.mdc ); + if( list_mode ) + printf(":mdc packet: length=%lu\n", pktlen); + if( !new_ctb || pktlen != 20 ) { + log_error("mdc_packet with invalid encoding\n"); + goto leave; + } + p = mdc->hash; + for( ; pktlen; pktlen--, p++ ) + *p = iobuf_get_noeof(inp); + + leave: + return 0; +} + diff --git a/g10/passphrase.c b/g10/passphrase.c index 53b2a82a3..24addd072 100644 --- a/g10/passphrase.c +++ b/g10/passphrase.c @@ -1,5 +1,5 @@ /* passphrase.c - Get a passphrase - * Copyright (C) 1998 Free Software Foundation, Inc. + * Copyright (C) 1998, 1999, 2000 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -24,8 +24,9 @@ #include #include #include -#include "util.h" + #include +#include "util.h" #include "options.h" #include "ttyio.h" #include "keydb.h" @@ -163,9 +164,9 @@ passphrase_to_dek( u32 *keyid, int pubkey_algo, tty_printf("\"\n"); if( !get_pubkey( pk, keyid ) ) { + const char *s = gcry_pk_algo_name( pk->pubkey_algo ); tty_printf( _("%u-bit %s key, ID %08lX, created %s"), - nbits_from_pk( pk ), - gcry_pk_algo_name( pk->pubkey_algo ), (ulong)keyid[1], + nbits_from_pk( pk ), s?s:"?", (ulong)keyid[1], strtimestamp(pk->timestamp) ); if( keyid[2] && keyid[3] && keyid[0] != keyid[2] && keyid[1] != keyid[3] ) diff --git a/g10/pkclist.c b/g10/pkclist.c index abd75f9cd..8e61ffdd0 100644 --- a/g10/pkclist.c +++ b/g10/pkclist.c @@ -1,5 +1,5 @@ /* pkclist.c - * Copyright (C) 1998 Free Software Foundation, Inc. + * Copyright (C) 1998, 1999, 2000 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -24,11 +24,10 @@ #include #include #include -#include +#include #include "options.h" #include "packet.h" -#include "main.h" #include "errors.h" #include "keydb.h" #include "util.h" @@ -36,6 +35,7 @@ #include "ttyio.h" #include "status.h" #include "i18n.h" +#include "main.h" #define CONTROL_D ('D' - 'A' + 1) @@ -97,6 +97,112 @@ fpr_info( PKT_public_key *pk ) } +/**************** + * Show the revocation reason as it is stored with the given signature + */ +static void +do_show_revocation_reason( PKT_signature *sig ) +{ + size_t n, nn; + const byte *p, *pp; + int seq = 0; + const char *text; + + while( (p = enum_sig_subpkt( sig->hashed_data, SIGSUBPKT_REVOC_REASON, + &n, &seq )) ) { + if( !n ) + continue; /* invalid - just skip it */ + + if( *p == 0 ) + text = _("No reason specified"); + else if( *p == 0x01 ) + text = _("Key is superseeded"); + else if( *p == 0x02 ) + text = _("Key has been compromised"); + else if( *p == 0x03 ) + text = _("Key is no longer used"); + else if( *p == 0x20 ) + text = _("User ID is no longer valid"); + else + text = NULL; + + log_info( _("Reason for revocation: ") ); + if( text ) + fputs( text, log_stream() ); + else + fprintf( log_stream(), "code=%02x", *p ); + putc( '\n', log_stream() ); + n--; p++; + pp = NULL; + do { + /* We don't want any empty lines, so skip them */ + while( n && *p == '\n' ) { + p++; + n--; + } + if( n ) { + pp = memchr( p, '\n', n ); + nn = pp? pp - p : n; + log_info( _("Revocation comment: ") ); + print_string( log_stream(), p, nn, 0 ); + putc( '\n', log_stream() ); + p += nn; n -= nn; + } + } while( pp ); + } +} + + +static void +show_revocation_reason( PKT_public_key *pk ) +{ + /* Hmmm, this is not so easy becuase we have to duplicate the code + * used in the trustbd to calculate the keyflags. We need to find + * a clean way to check revocation certificates on keys and signatures. + * And there should be no duplicate code. Because we enter this function + * only when the trustdb toldus, taht we have a revoked key, we could + * simplylook for a revocation cert and display this one, when there is + * only one. Let's try to do this until we have a better solution. + */ + KBNODE node, keyblock = NULL; + byte fingerprint[MAX_FINGERPRINT_LEN]; + size_t fingerlen; + int rc; + + /* get the keyblock */ + fingerprint_from_pk( pk, fingerprint, &fingerlen ); + rc = get_keyblock_byfprint( &keyblock, fingerprint, fingerlen ); + if( rc ) { /* that should never happen */ + log_debug( "failed to get the keyblock\n"); + return; + } + + for( node=keyblock; node; node = node->next ) { + if( ( node->pkt->pkttype == PKT_PUBLIC_KEY + || node->pkt->pkttype == PKT_PUBLIC_SUBKEY ) + && !cmp_public_keys( node->pkt->pkt.public_key, pk ) ) + break; + } + if( !node ) { + log_debug("Oops, PK not in keyblock\n"); + release_kbnode( keyblock ); + return; + } + /* now find the revocation certificate */ + for( node = node->next; node ; node = node->next ) { + if( node->pkt->pkttype == PKT_PUBLIC_SUBKEY ) + break; + if( node->pkt->pkttype == PKT_SIGNATURE + && (node->pkt->pkt.signature->sig_class == 0x20 + || node->pkt->pkt.signature->sig_class == 0x28 ) ) { + /* FIXME: we should check the signature here */ + do_show_revocation_reason ( node->pkt->pkt.signature ); + } + } + + release_kbnode( keyblock ); +} + static void show_paths( ulong lid, int only_first ) @@ -338,38 +444,53 @@ _("Could not find a valid trust path to the key. Let's see whether we\n" /**************** * Check whether we can trust this pk which has a trustlevel of TRUSTLEVEL - * Returns: true if we trust. + * Returns: true if we trust. Might change the trustlevel */ static int -do_we_trust( PKT_public_key *pk, int trustlevel ) +do_we_trust( PKT_public_key *pk, int *trustlevel ) { int rc; int did_add = 0; + int trustmask = 0; retry: - if( (trustlevel & TRUST_FLAG_REVOKED) ) { + if( (*trustlevel & TRUST_FLAG_REVOKED) ) { log_info(_("key %08lX: key has been revoked!\n"), (ulong)keyid_from_pk( pk, NULL) ); + show_revocation_reason( pk ); if( opt.batch ) return 0; if( !cpr_get_answer_is_yes("revoked_key.override", _("Use this key anyway? ")) ) return 0; + trustmask |= TRUST_FLAG_REVOKED; } - else if( (trustlevel & TRUST_FLAG_SUB_REVOKED) ) { + else if( (*trustlevel & TRUST_FLAG_SUB_REVOKED) ) { log_info(_("key %08lX: subkey has been revoked!\n"), (ulong)keyid_from_pk( pk, NULL) ); + show_revocation_reason( pk ); if( opt.batch ) return 0; if( !cpr_get_answer_is_yes("revoked_key.override", _("Use this key anyway? ")) ) return 0; + trustmask |= TRUST_FLAG_SUB_REVOKED; + } + *trustlevel &= ~trustmask; + + if( opt.always_trust) { + if( opt.verbose ) + log_info("No trust check due to --always-trust option\n"); + /* The problem with this, is that EXPIRE can't be checked as + * this needs to insert a ne key into the trustdb first and + * we don't want that */ + return 1; } - switch( (trustlevel & TRUST_MASK) ) { + switch( (*trustlevel & TRUST_MASK) ) { case TRUST_UNKNOWN: /* No pubkey in trustDB: Insert and check again */ rc = insert_trust_record_by_pk( pk ); if( rc ) { @@ -377,11 +498,12 @@ do_we_trust( PKT_public_key *pk, int trustlevel ) gpg_errstr(rc) ); return 0; /* no */ } - rc = check_trust( pk, &trustlevel, NULL, NULL, NULL ); + rc = check_trust( pk, trustlevel, NULL, NULL, NULL ); + *trustlevel &= ~trustmask; if( rc ) log_fatal("trust check after insert failed: %s\n", gpg_errstr(rc) ); - if( trustlevel == TRUST_UNKNOWN || trustlevel == TRUST_EXPIRED ) { + if( *trustlevel == TRUST_UNKNOWN || *trustlevel == TRUST_EXPIRED ) { log_debug("do_we_trust: oops at %d\n", __LINE__ ); return 0; } @@ -399,7 +521,8 @@ do_we_trust( PKT_public_key *pk, int trustlevel ) else { int quit; - rc = add_ownertrust( pk, &quit, &trustlevel ); + rc = add_ownertrust( pk, &quit, trustlevel ); + *trustlevel &= ~trustmask; if( !rc && !did_add && !quit ) { did_add = 1; goto retry; @@ -445,7 +568,7 @@ do_we_trust_pre( PKT_public_key *pk, int trustlevel ) { int rc; - rc = do_we_trust( pk, trustlevel ); + rc = do_we_trust( pk, &trustlevel ); if( (trustlevel & TRUST_FLAG_REVOKED) && !rc ) return 0; @@ -528,10 +651,12 @@ check_signatures_trust( PKT_signature *sig ) write_status( STATUS_KEYREVOKED ); log_info(_("WARNING: This key has been revoked by its owner!\n")); log_info(_(" This could mean that the signature is forgery.\n")); + show_revocation_reason( pk ); } else if( (trustlevel & TRUST_FLAG_SUB_REVOKED) ) { write_status( STATUS_KEYREVOKED ); log_info(_("WARNING: This subkey has been revoked by its owner!\n")); + show_revocation_reason( pk ); } @@ -770,7 +895,8 @@ build_pk_list( STRLIST remusr, PK_LIST *ret_pk_list, unsigned use ) else { int trustlevel; - rc = check_trust( pk, &trustlevel, NULL, NULL, NULL ); + rc = check_trust( pk, &trustlevel, pk->namehash, + NULL, NULL ); if( rc ) { log_error("error checking pk of `%s': %s\n", answer, gpg_errstr(rc) ); @@ -844,7 +970,7 @@ build_pk_list( STRLIST remusr, PK_LIST *ret_pk_list, unsigned use ) else if( !(rc=openpgp_pk_test_algo(pk->pubkey_algo, use )) ) { int trustlevel; - rc = check_trust( pk, &trustlevel, NULL, NULL, NULL ); + rc = check_trust( pk, &trustlevel, pk->namehash, NULL, NULL ); if( rc ) { free_public_key( pk ); pk = NULL; log_error(_("%s: error checking key: %s\n"), diff --git a/g10/plaintext.c b/g10/plaintext.c index f0c01ca8e..e3c7a865c 100644 --- a/g10/plaintext.c +++ b/g10/plaintext.c @@ -1,5 +1,5 @@ /* plaintext.c - process an plaintext packet - * Copyright (C) 1998, 1999 Free Software Foundation, Inc. + * Copyright (C) 1998, 1999, 2000 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -107,8 +107,10 @@ handle_plaintext( PKT_plaintext *pt, md_filter_context_t *mfx, } if( mfx->md ) gcry_md_putc(mfx->md, c ); - if( c == '\r' ) - continue; /* fixme: this hack might be too simple */ + #ifndef HAVE_DOSISH_SYSTEM + if( c == '\r' ) /* convert to native line ending */ + continue; /* fixme: this hack might be too simple */ + #endif if( fp ) { if( putc( c, fp ) == EOF ) { log_error("Error writing to `%s': %s\n", @@ -152,8 +154,10 @@ handle_plaintext( PKT_plaintext *pt, md_filter_context_t *mfx, while( (c = iobuf_get(pt->buf)) != -1 ) { if( mfx->md ) gcry_md_putc(mfx->md, c ); + #ifndef HAVE_DOSISH_SYSTEM if( convert && c == '\r' ) continue; /* fixme: this hack might be too simple */ + #endif if( fp ) { if( putc( c, fp ) == EOF ) { log_error("Error writing to `%s': %s\n", @@ -169,10 +173,10 @@ handle_plaintext( PKT_plaintext *pt, md_filter_context_t *mfx, int eof; for( eof=0; !eof; ) { /* Why do we check for len < 32768: - * If we won´ we would practically read 2 EOFS but + * If we won't, we would practically read 2 EOFs but * the first one has already popped the block_filter * off and therefore we don't catch the boundary. - * Always assume EOF if iobuf_read returns less bytes + * So, always assume EOF if iobuf_read returns less bytes * then requested */ int len = iobuf_read( pt->buf, buffer, 32768 ); if( len == -1 ) @@ -217,6 +221,8 @@ handle_plaintext( PKT_plaintext *pt, md_filter_context_t *mfx, if( !state ) { if( c == '\r' ) state = 1; + else if( c == '\n' ) + state = 2; else gcry_md_putc(mfx->md, c ); } diff --git a/g10/pubkey-enc.c b/g10/pubkey-enc.c index 587847aeb..2019d0301 100644 --- a/g10/pubkey-enc.c +++ b/g10/pubkey-enc.c @@ -1,5 +1,5 @@ /* pubkey-enc.c - public key encoded packet handling - * Copyright (C) 1998 Free Software Foundation, Inc. + * Copyright (C) 1998, 1999, 2000 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -23,14 +23,15 @@ #include #include #include -#include "util.h" + #include +#include "util.h" #include "packet.h" -#include "main.h" #include "keydb.h" #include "trustdb.h" #include "status.h" #include "options.h" +#include "main.h" #include "i18n.h" static int get_it( PKT_pubkey_enc *k, @@ -103,7 +104,6 @@ pk_decrypt( int algo, MPI *result, MPI *data, MPI *skey ) } - /**************** * Get the session key from a pubkey enc paket and return * it in DEK, which should have been allocated in secure memory. @@ -169,7 +169,7 @@ get_it( PKT_pubkey_enc *k, DEK *dek, PKT_secret_key *sk, u32 *keyid ) int rc; MPI plain_dek = NULL; byte *frame = NULL; - unsigned n; + unsigned int n; size_t nframe; u16 csum, csum2; @@ -199,7 +199,7 @@ get_it( PKT_pubkey_enc *k, DEK *dek, PKT_secret_key *sk, u32 *keyid ) * DEK is the encryption key (session key) with length k * CSUM */ - if( (opt.debug & DBG_CIPHER_VALUE) ) + if( DBG_CIPHER ) log_hexdump("DEK frame:", frame, nframe ); n=0; if( n + 7 > nframe ) @@ -219,10 +219,14 @@ get_it( PKT_pubkey_enc *k, DEK *dek, PKT_secret_key *sk, u32 *keyid ) dek->keylen = nframe - (n+1) - 2; dek->algo = frame[n++]; - if( dek->algo == GCRY_CIPHER_IDEA ) + if( dek->algo == GCRY_CIPHER_IDEA ) write_status(STATUS_RSA_OR_IDEA); rc = openpgp_cipher_test_algo( dek->algo ); if( rc ) { + if( !opt.quiet && rc == GPGERR_CIPHER_ALGO ) { + log_info(_("cipher algorithm %d is unknown or disabled\n"), + dek->algo); + } dek->algo = 0; goto leave; } @@ -241,9 +245,9 @@ get_it( PKT_pubkey_enc *k, DEK *dek, PKT_secret_key *sk, u32 *keyid ) rc = GPGERR_WRONG_SECKEY; goto leave; } - if( (opt.debug & DBG_CIPHER_VALUE) ) + if( DBG_CIPHER ) log_hexdump("DEK is:", dek->key, dek->keylen ); - /* check that the algo is in the preferences */ + /* check that the algo is in the preferences and whether it has expired */ { PKT_public_key *pk = gcry_xcalloc( 1, sizeof *pk ); if( (rc = get_pubkey( pk, keyid )) ) @@ -262,10 +266,25 @@ get_it( PKT_pubkey_enc *k, DEK *dek, PKT_secret_key *sk, u32 *keyid ) "NOTE: cipher algorithm %d not found in preferences\n"), dek->algo ); } + + + if( !rc && pk->expiredate && pk->expiredate <= make_timestamp() ) { + log_info(_("NOTE: secret key %08lX expired at %s\n"), + (ulong)keyid[1], asctimestamp( pk->expiredate) ); + } + + /* FIXME: check wheter the key has been revoked and display + * the revocation reason. Actually the user should know this himself, + * but the sender might not know already and therefor the user + * should get a notice that an revoked key has been used to decode + * the message. The user can than watch out for snakes send by + * one of those Eves outside his paradise :-) + */ free_public_key( pk ); rc = 0; } + leave: mpi_release(plain_dek); gcry_free(frame); diff --git a/g10/revoke.c b/g10/revoke.c index b1a50e786..4abd5edd7 100644 --- a/g10/revoke.c +++ b/g10/revoke.c @@ -1,5 +1,5 @@ /* revoke.c - * Copyright (C) 1998 Free Software Foundation, Inc. + * Copyright (C) 1998, 1999, 2000 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -24,6 +24,7 @@ #include #include #include +#include #include "options.h" #include "packet.h" @@ -37,6 +38,38 @@ #include "i18n.h" +struct revocation_reason_info { + int code; + char *desc; +}; + + +int +revocation_reason_build_cb( PKT_signature *sig, void *opaque ) +{ + struct revocation_reason_info *reason = opaque; + char *ud = NULL; + byte *buffer; + size_t buflen = 1; + + if( reason->desc ) { + ud = native_to_utf8( reason->desc ); + buflen += strlen(ud); + } + buffer = gcry_xmalloc( buflen ); + *buffer = reason->code; + if( ud ) { + memcpy(buffer+1, ud, strlen(ud) ); + gcry_free( ud ); + } + + build_sig_subpkt( sig, SIGSUBPKT_REVOC_REASON, buffer, buflen ); + gcry_free( buffer ); + return 0; +} + + + /**************** * Generate a revocation certificate for UNAME */ @@ -55,6 +88,7 @@ gen_revoke( const char *uname ) KBNODE keyblock = NULL; KBNODE node; KBPOS kbpos; + struct revocation_reason_info *reason = NULL; if( opt.batch ) { log_error(_("sorry, can't do this in batch mode\n")); @@ -62,19 +96,6 @@ gen_revoke( const char *uname ) } - /* FIXME: ask for the reason of revocation - 0x00 - No reason specified (key revocations or cert revocations) - Does not make sense! - - 0x01 - Key is superceded (key revocations) - 0x02 - Key material has been compromised (key revocations) - 0x03 - Key is no longer used (key revocations) - 0x20 - User id information is no longer valid (cert revocations) - - Following the revocation code is a string of octets which gives - information about the reason for revocation in human-readable form - */ - memset( &afx, 0, sizeof afx); memset( &zfx, 0, sizeof zfx); init_packet( &pkt ); @@ -136,6 +157,13 @@ gen_revoke( const char *uname ) goto leave; } + /* get the reason for the revocation */ + reason = ask_revocation_reason( 1, 0, 1 ); + if( !reason ) { /* user decided to cancel */ + rc = 0; + goto leave; + } + switch( is_secret_key_protected( sk ) ) { case -1: log_error(_("unknown protection algorithm\n")); @@ -163,7 +191,9 @@ gen_revoke( const char *uname ) iobuf_push_filter( out, armor_filter, &afx ); /* create it */ - rc = make_keysig_packet( &sig, pk, NULL, NULL, sk, 0x20, 0, NULL, NULL); + rc = make_keysig_packet( &sig, pk, NULL, NULL, sk, 0x20, 0, + revocation_reason_build_cb, + reason ); if( rc ) { log_error(_("make_keysig_packet failed: %s\n"), gpg_errstr(rc)); goto leave; @@ -198,193 +228,127 @@ gen_revoke( const char *uname ) iobuf_cancel(out); else iobuf_close(out); + release_revocation_reason_info( reason ); return rc; } -#if 0 /* The code is not complete but anyway, now we use */ - /* the edit menu to revoke signature */ -/**************** - * Return true if there is already a revocation signature for KEYID - * in KEYBLOCK at point node. - */ -static int -already_revoked( const KBNODE keyblock, const KBNODE node, u32 *keyid ) ) { -{ - const KBNODE n = find_prev_kbnode( keyblock, node, PKT_USER_ID ); - for( ; n; n = n->next ) { - PKT_signature *sig; - if( n->pkt->pkttype == PKT_SIGNATURE - && (sig = node->pkt->pkt.signature)->sig_class == 0x30 - && sig->keyid[0] == keyid[0] - && sig->keyid[1] == keyid[1] ) - return 1; + +struct revocation_reason_info * +ask_revocation_reason( int key_rev, int cert_rev, int hint ) +{ + int code; + char *description = NULL; + struct revocation_reason_info *reason; + const char *text_1 = _("Key has been compromised"); + const char *text_2 = _("Key is superseded"); + const char *text_3 = _("Key is no longer used"); + const char *text_4 = _("User ID is non longer valid"); + const char *code_text = NULL; + + do { + gcry_free(description); + description = NULL; + + tty_printf(_("Please select the reason for the revocation:\n")); + if( key_rev ) + tty_printf(" 1 = %s\n", text_1 ); + if( key_rev ) + tty_printf(" 2 = %s\n", text_2 ); + if( key_rev ) + tty_printf(" 3 = %s\n", text_3 ); + if( cert_rev ) + tty_printf(" 4 = %s\n", text_4 ); + tty_printf( " 0 = %s\n", _("Cancel") ); + if( hint ) + tty_printf(_("(Probably you want to select %d here)\n"), hint ); + + for(code = 0; !code;) { + int n; + char *answer = cpr_get("ask_revocation_reason.code", + _("Your decision? ")); + trim_spaces( answer ); + cpr_kill_prompt(); + if( *answer == 'q' || *answer == 'Q' ) + n = 0; + else if( !isdigit( *answer ) ) + n = -1; + else if( hint && !*answer ) + n = hint; + else + n = atoi(answer); + gcry_free(answer); + if( !n ) + return NULL; /* cancel */ + else if( key_rev && n == 1 ) { + code = 0x02; /* key has been compromised */ + code_text = text_1; + } + else if( key_rev && n == 2 ) { + code = 0x01; /* key is superseded */ + code_text = text_2; + } + else if( key_rev && n == 3 ) { + code = 0x03; /* key is no longer used */ + code_text = text_3; + } + else if( cert_rev && n == 4 ) { + code = 0x20; /* uid is non longer valid */ + code_text = text_4; + } + else + tty_printf(_("Invalid selection.\n")); } - else if( n->pkt->pkttype == PKT_USER_ID - break; - else if( n->pkt->pkttype == PKT_PUBLIC_SUBKEY - break; - } - return 0; + + tty_printf(_("Enter an optional description; " + "end it with an empty line:\n") ); + for(;;) { + char *answer = cpr_get("ask_revocation_reason.text", "> " ); + trim_trailing_ws( answer, strlen(answer) ); + cpr_kill_prompt(); + if( !*answer ) { + gcry_free(answer); + break; + } + + { + char *p = make_printable_string( answer, strlen(answer), 0 ); + gcry_free(answer); + answer = p; + } + + if( !description ) + description = gcry_xstrdup(answer); + else { + char *p = gcry_xmalloc( strlen(description) + strlen(answer) + 2 ); + strcpy(stpcpy(stpcpy( p, description),"\n"),answer); + gcry_free(description); + description = p; + } + gcry_free(answer); + } + + tty_printf(_("Reason for revocation: %s\n"), code_text ); + if( !description ) + tty_printf(_("(No description given)\n") ); + else + tty_printf("%s\n", description ); + + } while( !cpr_get_answer_is_yes("ask_revocation_reason.okay", + _("Is this okay? ")) ); + + reason = gcry_xmalloc( sizeof *reason ); + reason->code = code; + reason->desc = description; + return reason; } -/**************** - * Ask whether the signature should be revoked. If the user commits this, - * flag bit 0 is set. - */ -static void -ask_revoke_sig( KBNODE keyblock, KBNODE node, PKT_signature *sig ) ) { +void +release_revocation_reason_info( struct revocation_reason_info *reason ) { - KBNODE unode = find_prev_kbnode( keyblock, node, PKT_USER_ID ); - - if( !unode ) { - log_error("Oops: no user ID for signature\n"); - return; - } - - tty_printf(_("user ID: \"")); - tty_print_utf8_string( unode->pkt->pkt.user_id->name, - unode->pkt->pkt.user_id->len, 0 ); - tty_printf(_("\"\nsigned with your key %08lX at %s\n"), - sig->keyid[1], datestr_from_sig(sig) ); - - if( cpr_get_answer_is_yes("ask_revoke_sig.one", - _("Create a revocation certificate for this signature? (y/N)")) ) { - node->flag |= 1; + if( reason ) { + gcry_free( reason->desc ); + gcry_free( reason ); } } -/**************** - * Generate a signature revocation certificate for UNAME - */ -int -gen_sig_revoke( const char *uname ) -{ - int rc = 0; - armor_filter_context_t afx; - compress_filter_context_t zfx; - PACKET pkt; - IOBUF out = NULL; - KBNODE keyblock = NULL; - KBNODE node; - KBPOS kbpos; - int uidchg; - - if( opt.batch ) { - log_error(_("sorry, can't do this in batch mode\n")); - return GPGERR_GENERAL; - } - - - memset( &afx, 0, sizeof afx); - memset( &zfx, 0, sizeof zfx); - init_packet( &pkt ); - - - /* get the keyblock */ - rc = find_keyblock_byname( &kbpos, uname ); - if( rc ) { - log_error(_("public key for user `%s' not found\n"), uname ); - goto leave; - } - - /* read the keyblock */ - rc = read_keyblock( &kbpos, &keyblock ); - if( rc ) { - log_error(_("error reading the certificate: %s\n"), gpg_errstr(rc) ); - goto leave; - } - - /* get the keyid from the keyblock */ - node = find_kbnode( keyblock, PKT_PUBLIC_KEY ); - if( !node ) { - log_error(_("Oops; public key lost!\n")); - rc = GPGERR_GENERAL; - goto leave; - } - - if( (rc = open_outfile( NULL, 0, &out )) ) - goto leave; - - if( opt.armor ) { - afx.what = 1; - iobuf_push_filter( out, armor_filter, &afx ); - } - - /* Now walk over all signatures which we did with one of - * our secret keys. Hmmm: Should we check for duplicate signatures */ - clear_kbnode_flags( flags ); - for( node = keyblock; node; node = node->next ) { - PKT_signature *sig; - if( node->pkt->pkttype == PKT_SIGNATURE - && ((sig = node->pkt->pkt.signature)->sig_class&~3) == 0x10 - && seckey_available( sig->keyid ) - && !already_revoked( keyblock, node, sig->keyid ) ) { ) { - ask_revoke_sig( keyblock, node, sig ) - } - else if( node->pkt->pkttype == PKT_PUBLIC_SUBKEY - break; - } - - - for( node = keyblock; node; node = node->next ) { { - if( (node->flag & 1) ) - break; - } - if( !node ) { - log_info(_("nothing to revoke\n")); - iobuf_cancel(out); - out = NULL; - goto leave; - } - - init_packet( &pkt ); - pkt.pkttype = PKT_PUBLIC_KEY; - pkt.pkt.public_key = keyblock->pkt->pkt.public_key; - rc = build_packet( out, &pkt ); - if( rc ) { - log_error(_("build_packet failed: %s\n"), gpg_errstr(rc) ); - goto leave; - } - uidchg = 1; - for( node = keyblock; node; node = node->next ) { - if( node->pkt->pkttype == PKT_USER_ID ) - uidchg = 1; - if( !(node->flag & 1) ) - continue; - - if( uidchg ) { - /* create a user ID packet */ - ....... - uidchg = 0; - } - - /* create it */ - rc = make_keysig_packet( &sig, pk, NULL, NULL, sk, 0x30, 0, NULL, NULL); - if( rc ) { - log_error(_("make_keysig_packet failed: %s\n"), gpg_errstr(rc)); - goto leave; - } - init_packet( &pkt ); - pkt.pkttype = PKT_SIGNATURE; - pkt.pkt.signature = sig; - - rc = build_packet( out, &pkt ); - if( rc ) { - log_error(_("build_packet failed: %s\n"), gpg_errstr(rc) ); - goto leave; - } - } - - leave: - release_kbnode( keyblock ); - if( !out ) - ; - else if( rc ) - iobuf_cancel(out); - else - iobuf_close(out); - return rc; -} -#endif /* unused code */ - diff --git a/g10/ringedit.c b/g10/ringedit.c index 2fa79fd74..3a29654d2 100644 --- a/g10/ringedit.c +++ b/g10/ringedit.c @@ -1,5 +1,5 @@ /* ringedit.c - Function for key ring editing - * Copyright (C) 1998 Free Software Foundation, Inc. + * Copyright (C) 1998, 2000 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -51,9 +51,10 @@ #ifdef HAVE_LIBGDBM #include #endif + +#include #include "util.h" #include "packet.h" -#include #include "iobuf.h" #include "keydb.h" #include "host2net.h" @@ -63,10 +64,6 @@ #include "kbx.h" -#ifdef MKDIR_TAKES_ONE_ARG -# undef mkdir -# define mkdir(a,b) mkdir(a) -#endif struct resource_table_struct { @@ -191,7 +188,6 @@ enum_keyblock_resources( int *sequence, int secret ) } - /**************** * Register a resource (which currently may only be a keyring file). * The first keyring which is added by this function is @@ -312,24 +308,13 @@ add_keyblock_resource( const char *url, int force, int secret ) *last_slash_in_filename = 0; if( access(filename, F_OK) ) { - if( strlen(filename) >= 7 - && !strcmp(filename+strlen(filename)-7, "/.gnupg") ) { - if( mkdir(filename, S_IRUSR|S_IWUSR|S_IXUSR) ) - { - log_error( _("%s: can't create directory: %s\n"), - filename, strerror(errno)); - rc = GPGERR_OPEN_FILE; - goto leave; - } - else if( !opt.quiet ) - log_info( _("%s: directory created\n"), filename ); - copy_options_file( filename ); - } - else - { - rc = GPGERR_OPEN_FILE; - goto leave; - } + /* on the first time we try to create the default homedir and + * in this case the process will be terminated, so that on the + * next invocation it can read the options file in on startup + */ + try_make_homedir( filename ); + rc = GPGERR_OPEN_FILE; + goto leave; } *last_slash_in_filename = '/'; @@ -363,7 +348,6 @@ add_keyblock_resource( const char *url, int force, int secret ) #endif break; - #ifdef HAVE_LIBGDBM case rt_GDBM: resource_table[i].dbf = gdbm_open( filename, 0, @@ -457,6 +441,35 @@ get_keyblock_handle( const char *filename, int secret, KBPOS *kbpos ) } +/**************** + * Return the filename of the firstkeyblock resource which is intended + * for write access. This will either be the default resource or in + * case this is not writable one of the others. If no writable is found, + * the default filename in the homedirectory will be returned. + * Caller must free, will never return NULL. + */ +char * +get_writable_keyblock_file( int secret ) +{ + int i = secret? default_secret_resource : default_public_resource; + + if( resource_table[i].used && !resource_table[i].secret == !secret ) { + if( !access( resource_table[i].fname, R_OK|W_OK ) ) { + return gcry_xstrdup( resource_table[i].fname ); + } + } + for(i=0; i < MAX_RESOURCES; i++ ) { + if( resource_table[i].used && !resource_table[i].secret == !secret ) { + if( !access( resource_table[i].fname, R_OK|W_OK ) ) { + return gcry_xstrdup( resource_table[i].fname ); + } + } + } + /* Assume the home dir is always writable */ + return make_filename(opt.homedir, secret? "secring.gpg" + : "pubring.gpg", NULL ); +} + /**************** * Search a keyblock which starts with the given packet and puts all diff --git a/g10/seckey-cert.c b/g10/seckey-cert.c index bef75507e..ab09b8f4d 100644 --- a/g10/seckey-cert.c +++ b/g10/seckey-cert.c @@ -1,5 +1,5 @@ /* seckey-cert.c - secret key certificate packet handling - * Copyright (C) 1998, 1999 Free Software Foundation, Inc. + * Copyright (C) 1998, 1999, 2000 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -33,6 +33,7 @@ #include "i18n.h" #include "status.h" + /**************** * Emulate our old PK interface here - sometime in the future we might * change the internal design to directly fit to libgcrypt. @@ -82,9 +83,11 @@ pk_check_secret_key( int algo, MPI *skey ) return rc; } + static int do_check( PKT_secret_key *sk ) { + byte *buffer; u16 csum=0; int i, res; unsigned nbytes; @@ -95,6 +98,10 @@ do_check( PKT_secret_key *sk ) GCRY_CIPHER_HD cipher_hd=NULL; PKT_secret_key *save_sk; + if( sk->protect.s2k.mode == 1001 ) { + log_info(_("secret key parts are not available\n")); + return GPGERR_GENERAL; + } if( sk->protect.algo == GCRY_CIPHER_NONE ) BUG(); if( openpgp_cipher_test_algo( sk->protect.algo ) ) { @@ -112,6 +119,7 @@ do_check( PKT_secret_key *sk ) } dek = passphrase_to_dek( keyid, sk->pubkey_algo, sk->protect.algo, &sk->protect.s2k, 0 ); + /* Hmmm: Do we use sync mode here even for Twofish? */ if( !(cipher_hd = gcry_cipher_open( sk->protect.algo, GCRY_CIPHER_MODE_CFB, GCRY_CIPHER_SECURE @@ -198,7 +206,8 @@ do_check( PKT_secret_key *sk ) free_secret_key( save_sk ); return GPGERR_BAD_PASS; } - /* the checksum may fail, so we also check the key itself */ + /* the checksum may be correct in some cases, + * so we also check the key itself */ res = pk_check_secret_key( sk->pubkey_algo, sk->skey ); if( res ) { copy_secret_key( sk, save_sk ); @@ -300,8 +309,6 @@ protect_secret_key( PKT_secret_key *sk, DEK *dek ) ) { BUG(); } - - rc = gcry_cipher_setkey( cipher_hd, dek->key, dek->keylen ); if( rc == GCRYERR_WEAK_KEY ) { log_info(_("WARNING: Weak key detected" @@ -316,18 +323,19 @@ protect_secret_key( PKT_secret_key *sk, DEK *dek ) if( blocksize != 8 && blocksize != 16 ) log_fatal("unsupported blocksize %d\n", blocksize ); sk->protect.ivlen = blocksize; + assert( sk->protect.ivlen <= DIM(sk->protect.iv) ); } - - assert( sk->protect.ivlen <= DIM(sk->protect.iv) ); gcry_randomize(sk->protect.iv, sk->protect.ivlen, - GCRY_STRONG_RANDOM); + GCRY_STRONG_RANDOM); gcry_cipher_setiv( cipher_hd, sk->protect.iv, sk->protect.ivlen ); + #warning FIXME: replace set/get buffer if( sk->version >= 4 ) { - #define NMPIS (GNUPG_MAX_NSKEY - GNUPG_MAX_NPKEY) - byte *bufarr[NMPIS]; - unsigned narr[NMPIS]; - unsigned nbits[NMPIS]; + /* FIXME: There is a bug in this function for all algorithms + * where the secret MPIs are more than 1 */ + byte *bufarr[GNUPG_MAX_NSKEY]; + unsigned narr[GNUPG_MAX_NSKEY]; + unsigned nbits[GNUPG_MAX_NSKEY]; int ndata=0; byte *p, *data; @@ -342,13 +350,13 @@ protect_secret_key( PKT_secret_key *sk, DEK *dek ) nbits[j] = gcry_mpi_get_nbits( sk->skey[i] ); ndata += narr[j] + 2; } - for( ; j < NMPIS; j++ ) + for( ; j < GNUPG_MAX_NSKEY; j++ ) bufarr[j] = NULL; ndata += 2; /* for checksum */ data = gcry_xmalloc_secure( ndata ); p = data; - for(j=0; j < NMPIS && bufarr[j]; j++ ) { + for(j=0; j < GNUPG_MAX_NSKEY && bufarr[j]; j++ ) { p[0] = nbits[j] >> 8 ; p[1] = nbits[j]; p += 2; @@ -356,7 +364,6 @@ protect_secret_key( PKT_secret_key *sk, DEK *dek ) p += narr[j]; gcry_free(bufarr[j]); } - #undef NMPIS csum = checksum( data, ndata-2); sk->csum = csum; *p++ = csum >> 8; diff --git a/g10/seskey.c b/g10/seskey.c index 996118e0f..aa2a1511c 100644 --- a/g10/seskey.c +++ b/g10/seskey.c @@ -1,5 +1,5 @@ /* seskey.c - make sesssion keys etc. - * Copyright (C) 1998 Free Software Foundation, Inc. + * Copyright (C) 1998, 1999, 2000 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -152,7 +152,7 @@ encode_session_key( DEK *dek, unsigned nbits ) static MPI do_encode_md( GCRY_MD_HD md, int algo, size_t len, unsigned nbits, - const byte *asn, size_t asnlen ) + const byte *asn, size_t asnlen, int v3compathack ) { int nframe = (nbits+7) / 8; byte *frame; @@ -165,7 +165,7 @@ do_encode_md( GCRY_MD_HD md, int algo, size_t len, unsigned nbits, /* We encode the MD in this way: * - * 0 A PAD(n bytes) 0 ASN(asnlen bytes) MD(len bytes) + * 0 1 PAD(n bytes) 0 ASN(asnlen bytes) MD(len bytes) * * PAD consists of FF bytes. */ @@ -173,7 +173,7 @@ do_encode_md( GCRY_MD_HD md, int algo, size_t len, unsigned nbits, : gcry_xmalloc( nframe ); n = 0; frame[n++] = 0; - frame[n++] = algo; + frame[n++] = v3compathack? algo : 1; /* block type */ i = nframe - len - asnlen -3 ; assert( i > 1 ); memset( frame+n, 0xff, i ); n += i; @@ -188,8 +188,15 @@ do_encode_md( GCRY_MD_HD md, int algo, size_t len, unsigned nbits, } +/**************** + * Encode a message digest into an MPI. + * v3compathack is used to work around a bug in old GnuPG versions + * which did put the algo identifier inseatd of the block type 1 into + * the encoded value. setting this vare force the old behaviour. + */ MPI -encode_md_value( int pubkey_algo, GCRY_MD_HD md, int hash_algo, unsigned nbits ) +encode_md_value( int pubkey_algo, GCRY_MD_HD md, int hash_algo, + unsigned nbits, int v3compathack ) { int algo = hash_algo? hash_algo : gcry_md_get_algo(md); MPI frame; @@ -211,9 +218,10 @@ encode_md_value( int pubkey_algo, GCRY_MD_HD md, int hash_algo, unsigned nbits ) if( gcry_md_algo_info( algo, GCRYCTL_GET_ASNOID, asn, &asnlen ) ) BUG(); frame = do_encode_md( md, algo, gcry_md_get_algo_dlen( algo ), - nbits, asn, asnlen ); + nbits, asn, asnlen, v3compathack ); gcry_free( asn ); } return frame; } + diff --git a/g10/sig-check.c b/g10/sig-check.c index bd68d44d7..f12cfa6d3 100644 --- a/g10/sig-check.c +++ b/g10/sig-check.c @@ -1,5 +1,5 @@ /* sig-check.c - Check a signature - * Copyright (C) 1998 Free Software Foundation, Inc. + * Copyright (C) 1998, 1999, 2000 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -31,6 +31,7 @@ #include "main.h" #include "status.h" #include "i18n.h" +#include "options.h" struct cmp_help_context_s { PKT_signature *sig; @@ -39,9 +40,10 @@ struct cmp_help_context_s { static int do_signature_check( PKT_signature *sig, GCRY_MD_HD digest, - u32 *r_expire ); + u32 *r_expiredate, int *r_expired ); static int do_check( PKT_public_key *pk, PKT_signature *sig, - GCRY_MD_HD digest ); + GCRY_MD_HD digest, int *r_expired ); + /**************** @@ -131,11 +133,13 @@ int signature_check( PKT_signature *sig, GCRY_MD_HD digest ) { u32 dummy; - return do_signature_check( sig, digest, &dummy ); + int dum2; + return do_signature_check( sig, digest, &dummy, &dum2 ); } static int -do_signature_check( PKT_signature *sig, GCRY_MD_HD digest, u32 *r_expire ) +do_signature_check( PKT_signature *sig, GCRY_MD_HD digest, + u32 *r_expiredate, int *r_expired ) { PKT_public_key *pk = gcry_xcalloc( 1, sizeof *pk ); int rc=0; @@ -143,12 +147,12 @@ do_signature_check( PKT_signature *sig, GCRY_MD_HD digest, u32 *r_expire ) if( is_RSA(sig->pubkey_algo) ) write_status(STATUS_RSA_OR_IDEA); - *r_expire = 0; + *r_expiredate = 0; if( get_pubkey( pk, sig->keyid ) ) rc = GPGERR_NO_PUBKEY; else { - *r_expire = pk->expiredate; - rc = do_check( pk, sig, digest ); + *r_expiredate = pk->expiredate; + rc = do_check( pk, sig, digest, r_expired ); } free_public_key( pk ); @@ -199,97 +203,6 @@ do_signature_check( PKT_signature *sig, GCRY_MD_HD digest, u32 *r_expire ) } -#if 0 /* not anymore used */ -/**************** - * Check the MDC which is contained in SIG. - * The GCRY_MD_HD should be currently open, so that this function - * is able to append some data, before finalizing the digest. - */ -int -mdc_kludge_check( PKT_signature *sig, GCRY_MD_HD digest ) -{ - int rc=0; - - if( (rc=check_digest_algo(sig->digest_algo)) ) - return rc; - - /* make sure the digest algo is enabled (in case of a detached mdc??) */ - md_enable( digest, sig->digest_algo ); - - /* complete the digest */ - if( sig->version >= 4 ) - gcry_md_putc( digest, sig->version ); - gcry_md_putc( digest, sig->sig_class ); - if( sig->version < 4 ) { - u32 a = sig->timestamp; - gcry_md_putc( digest, (a >> 24) & 0xff ); - gcry_md_putc( digest, (a >> 16) & 0xff ); - gcry_md_putc( digest, (a >> 8) & 0xff ); - gcry_md_putc( digest, a & 0xff ); - } - else { - byte buf[6]; - size_t n; - gcry_md_putc( digest, sig->pubkey_algo ); - gcry_md_putc( digest, sig->digest_algo ); - if( sig->hashed_data ) { - n = (sig->hashed_data[0] << 8) | sig->hashed_data[1]; - gcry_md_write( digest, sig->hashed_data, n+2 ); - n += 6; - } - else - n = 6; - /* add some magic */ - buf[0] = sig->version; - buf[1] = 0xff; - buf[2] = n >> 24; - buf[3] = n >> 16; - buf[4] = n >> 8; - buf[5] = n; - gcry_md_write( digest, buf, 6 ); - } - md_final( digest ); - - rc = GPGERR_BAD_SIGN; - { const byte *s1 = md_read( digest, sig->digest_algo ); - int s1len = md_digest_length( sig->digest_algo ); - - log_hexdump( "MDC calculated", s1, s1len ); - - if( !sig->data[0] ) - log_debug("sig_data[0] is NULL\n"); - else { - unsigned s2len; - char *s2; - - if( gcry_mpi_print( GCRYMPI_FMT_USG, &s2, &s2len, sig->data[0] )) - BUG(); - - log_hexdump( "MDC stored ", s2, s2len ); - - if( s2len != s1len ) - log_debug("MDC check: len differ: %d/%d\n", s1len, s2len); - else if( memcmp( s1, s2, s1len ) ) - log_debug("MDC check: hashs differ\n"); - else - rc = 0; - gcry_free(s2); - } - } - - if( !rc && sig->flags.unknown_critical ) { - log_info(_("assuming bad MDC due to an unknown critical bit\n")); - rc = GPGERR_BAD_SIGN; - } - sig->flags.checked = 1; - sig->flags.valid = !rc; - - /* FIXME: check that we are actually in an encrypted packet */ - - return rc; -} -#endif - /**************** * This function gets called by pubkey_verify() if the algorithm needs it. */ @@ -366,16 +279,18 @@ cmp_help( void *opaque, MPI result ) static int -do_check( PKT_public_key *pk, PKT_signature *sig, GCRY_MD_HD digest ) +do_check( PKT_public_key *pk, PKT_signature *sig, GCRY_MD_HD digest, + int *r_expired ) { MPI result = NULL; int rc=0; struct cmp_help_context_s ctx; u32 cur_time; + *r_expired = 0; if( pk->version == 4 && pk->pubkey_algo == GCRY_PK_ELG_E ) { log_info(_("this is a PGP generated " - "ElGamal key which is NOT secure for signatures!\n")); + "ElGamal key which is NOT secure for signatures!\n")); return GPGERR_PUBKEY_ALGO; } @@ -385,7 +300,8 @@ do_check( PKT_public_key *pk, PKT_signature *sig, GCRY_MD_HD digest ) ? _("public key is %lu second newer than the signature\n") : _("public key is %lu seconds newer than the signature\n"), d ); - return GPGERR_TIME_CONFLICT; /* pubkey newer than signature */ + if( !opt.ignore_time_conflict ) + return GPGERR_TIME_CONFLICT; /* pubkey newer than signature */ } cur_time = make_timestamp(); @@ -395,13 +311,15 @@ do_check( PKT_public_key *pk, PKT_signature *sig, GCRY_MD_HD digest ) "in future (time warp or clock problem)\n") : _("key has been created %lu seconds " "in future (time warp or clock problem)\n"), d ); - return GPGERR_TIME_CONFLICT; + if( !opt.ignore_time_conflict ) + return GPGERR_TIME_CONFLICT; } if( pk->expiredate && pk->expiredate < cur_time ) { log_info(_("NOTE: signature key expired %s\n"), asctimestamp( pk->expiredate ) ); write_status(STATUS_SIGEXPIRED); + *r_expired = 1; } @@ -448,13 +366,24 @@ do_check( PKT_public_key *pk, PKT_signature *sig, GCRY_MD_HD digest ) gcry_md_final( digest ); result = encode_md_value( pk->pubkey_algo, digest, sig->digest_algo, - gcry_mpi_get_nbits(pk->pkey[0])); - + gcry_mpi_get_nbits(pk->pkey[0]), 0); ctx.sig = sig; ctx.md = digest; rc = pk_verify( pk->pubkey_algo, result, sig->data, pk->pkey, cmp_help, &ctx ); mpi_release( result ); + if( (opt.emulate_bugs & EMUBUG_MDENCODE) + && rc == GPGERR_BAD_SIGN && is_ELGAMAL(pk->pubkey_algo) ) { + /* In this case we try again because old GnuPG versions didn't encode + * the hash right. There is no problem with DSA however */ + result = encode_md_value( pk->pubkey_algo, digest, sig->digest_algo, + gcry_mpi_get_nbits(pk->pkey[0]), (sig->version < 5) ); + ctx.sig = sig; + ctx.md = digest; + rc = pk_verify( pk->pubkey_algo, result, sig->data, pk->pkey, + cmp_help, &ctx ); + } + if( !rc && sig->flags.unknown_critical ) { log_info(_("assuming bad signature due to an unknown critical bit\n")); rc = GPGERR_BAD_SIGN; @@ -472,16 +401,30 @@ hash_uid_node( KBNODE unode, GCRY_MD_HD md, PKT_signature *sig ) PKT_user_id *uid = unode->pkt->pkt.user_id; assert( unode->pkt->pkttype == PKT_USER_ID ); - if( sig->version >=4 ) { - byte buf[5]; - buf[0] = 0xb4; /* indicates a userid packet */ - buf[1] = uid->len >> 24; /* always use 4 length bytes */ - buf[2] = uid->len >> 16; - buf[3] = uid->len >> 8; - buf[4] = uid->len; - gcry_md_write( md, buf, 5 ); + if( uid->photo ) { + if( sig->version >=4 ) { + byte buf[5]; + buf[0] = 0xd1; /* packet of type 17 */ + buf[1] = uid->photolen >> 24; /* always use 4 length bytes */ + buf[2] = uid->photolen >> 16; + buf[3] = uid->photolen >> 8; + buf[4] = uid->photolen; + gcry_md_write( md, buf, 5 ); + } + gcry_md_write( md, uid->photo, uid->photolen ); + } + else { + if( sig->version >=4 ) { + byte buf[5]; + buf[0] = 0xb4; /* indicates a userid packet */ + buf[1] = uid->len >> 24; /* always use 4 length bytes */ + buf[2] = uid->len >> 16; + buf[3] = uid->len >> 8; + buf[4] = uid->len; + gcry_md_write( md, buf, 5 ); + } + gcry_md_write( md, uid->name, uid->len ); } - gcry_md_write( md, uid->name, uid->len ); } /**************** @@ -493,11 +436,13 @@ int check_key_signature( KBNODE root, KBNODE node, int *is_selfsig ) { u32 dummy; - return check_key_signature2(root, node, is_selfsig, &dummy ); + int dum2; + return check_key_signature2(root, node, is_selfsig, &dummy, &dum2 ); } int -check_key_signature2( KBNODE root, KBNODE node, int *is_selfsig, u32 *r_expire) +check_key_signature2( KBNODE root, KBNODE node, int *is_selfsig, + u32 *r_expiredate, int *r_expired ) { GCRY_MD_HD md; PKT_public_key *pk; @@ -507,7 +452,8 @@ check_key_signature2( KBNODE root, KBNODE node, int *is_selfsig, u32 *r_expire) if( is_selfsig ) *is_selfsig = 0; - *r_expire = 0; + *r_expiredate = 0; + *r_expired = 0; assert( node->pkt->pkttype == PKT_SIGNATURE ); assert( root->pkt->pkttype == PKT_PUBLIC_KEY ); @@ -528,7 +474,7 @@ check_key_signature2( KBNODE root, KBNODE node, int *is_selfsig, u32 *r_expire) if( !(md = gcry_md_open( algo, 0 )) ) BUG(); hash_public_key( md, pk ); - rc = do_check( pk, sig, md ); + rc = do_check( pk, sig, md, r_expired ); gcry_md_close(md); } else if( sig->sig_class == 0x28 ) { /* subkey revocation */ @@ -539,7 +485,7 @@ check_key_signature2( KBNODE root, KBNODE node, int *is_selfsig, u32 *r_expire) BUG(); hash_public_key( md, pk ); hash_public_key( md, snode->pkt->pkt.public_key ); - rc = do_check( pk, sig, md ); + rc = do_check( pk, sig, md, r_expired ); gcry_md_close(md); } else { @@ -562,7 +508,7 @@ check_key_signature2( KBNODE root, KBNODE node, int *is_selfsig, u32 *r_expire) BUG(); hash_public_key( md, pk ); hash_public_key( md, snode->pkt->pkt.public_key ); - rc = do_check( pk, sig, md ); + rc = do_check( pk, sig, md, r_expired ); gcry_md_close(md); } else { @@ -584,10 +530,11 @@ check_key_signature2( KBNODE root, KBNODE node, int *is_selfsig, u32 *r_expire) if( keyid[0] == sig->keyid[0] && keyid[1] == sig->keyid[1] ) { if( is_selfsig ) *is_selfsig = 1; - rc = do_check( pk, sig, md ); + rc = do_check( pk, sig, md, r_expired ); + } + else { + rc = do_signature_check( sig, md, r_expiredate, r_expired ); } - else - rc = do_signature_check( sig, md, r_expire ); gcry_md_close(md); } else { diff --git a/g10/sign.c b/g10/sign.c index bb2d2290c..9d0c203b8 100644 --- a/g10/sign.c +++ b/g10/sign.c @@ -1,5 +1,5 @@ /* sign.c - sign data - * Copyright (C) 1998 Free Software Foundation, Inc. + * Copyright (C) 1998, 1999, 2000 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -36,9 +36,16 @@ #include "filter.h" #include "ttyio.h" #include "trustdb.h" +#include "status.h" #include "i18n.h" +#ifdef HAVE_DOSISH_SYSTEM + #define LF "\r\n" +#else + #define LF "\n" +#endif + /**************** * Emulate our old PK interface here - sometime in the future we might * change the internal design to directly fit to libgcrypt. @@ -163,7 +170,8 @@ do_sign( PKT_secret_key *sk, PKT_signature *sig, "in future (time warp or clock problem)\n") : _("key has been created %lu seconds " "in future (time warp or clock problem)\n"), d ); - return GPGERR_TIME_CONFLICT; + if( !opt.ignore_time_conflict ) + return GPGERR_TIME_CONFLICT; } @@ -178,7 +186,7 @@ do_sign( PKT_secret_key *sk, PKT_signature *sig, sig->digest_start[0] = dp[0]; sig->digest_start[1] = dp[1]; frame = encode_md_value( sk->pubkey_algo, md, - digest_algo, gcry_mpi_get_nbits(sk->skey[0])); + digest_algo, gcry_mpi_get_nbits(sk->skey[0]), 0 ); rc = pk_sign( sk->pubkey_algo, sig->data, frame, sk->skey ); mpi_release(frame); if( rc ) @@ -240,6 +248,25 @@ only_old_style( SK_LIST sk_list ) } +static void +print_status_sig_created ( PKT_secret_key *sk, PKT_signature *sig, int what ) +{ + byte array[MAX_FINGERPRINT_LEN], *p; + char buf[100+MAX_FINGERPRINT_LEN*2]; + size_t i, n; + + sprintf(buf, "%c %d %d %02x %lu ", + what, sig->pubkey_algo, sig->digest_algo, sig->sig_class, + (ulong)sig->timestamp ); + + fingerprint_from_sk( sk, array, &n ); + p = buf + strlen(buf); + for(i=0; i < n ; i++ ) + sprintf(p+2*i, "%02X", array[i] ); + + write_status_text( STATUS_SIG_CREATED, buf ); +} + /**************** * Sign the files whose names are in FILENAME. @@ -521,6 +548,8 @@ sign_file( STRLIST filenames, int detached, STRLIST locusr, sig->sig_class = opt.textmode && !outfile? 0x01 : 0x00; md = gcry_md_copy( mfx.md ); + if( !md ) + BUG(); if( sig->version >= 4 ) { build_sig_subpkt_from_sig( sig ); @@ -573,12 +602,16 @@ sign_file( STRLIST filenames, int detached, STRLIST locusr, pkt.pkttype = PKT_SIGNATURE; pkt.pkt.signature = sig; rc = build_packet( out, &pkt ); + if( !rc && is_status_enabled() ) { + print_status_sig_created ( sk, sig, detached ? 'D':'S'); + } free_packet( &pkt ); if( rc ) log_error("build signature packet failed: %s\n", gpg_errstr(rc) ); } if( rc ) goto leave; + } @@ -640,7 +673,7 @@ clearsign_file( const char *fname, STRLIST locusr, const char *outfile ) else if( (rc = open_outfile( fname, 1, &out )) ) goto leave; - iobuf_writestr(out, "-----BEGIN PGP SIGNED MESSAGE-----\n" ); + iobuf_writestr(out, "-----BEGIN PGP SIGNED MESSAGE-----" LF ); for( sk_rover = sk_list; sk_rover; sk_rover = sk_rover->next ) { PKT_secret_key *sk = sk_rover->sk; @@ -683,13 +716,15 @@ clearsign_file( const char *fname, STRLIST locusr, const char *outfile ) } - if( !(textmd = gcry_md_open(0, 0)) ) + textmd = gcry_md_open(0, 0); + if( !textmd ) BUG(); for( sk_rover = sk_list; sk_rover; sk_rover = sk_rover->next ) { PKT_secret_key *sk = sk_rover->sk; gcry_md_enable(textmd, hash_for(sk->pubkey_algo)); } - /*md_start_debug( textmd, "sign" );*/ + if ( DBG_HASHING ) + gcry_md_start_debug( textmd, "clearsign" ); copy_clearsig_text( out, inp, textmd, !opt.not_dash_escaped, opt.escape_from, old_style ); /* fixme: check for read errors */ @@ -717,6 +752,8 @@ clearsign_file( const char *fname, STRLIST locusr, const char *outfile ) sig->sig_class = 0x01; md = gcry_md_copy( textmd ); + if( !md ) + BUG(); if( sig->version >= 4 ) { build_sig_subpkt_from_sig( sig ); gcry_md_putc( md, sig->version ); @@ -768,6 +805,9 @@ clearsign_file( const char *fname, STRLIST locusr, const char *outfile ) pkt.pkttype = PKT_SIGNATURE; pkt.pkt.signature = sig; rc = build_packet( out, &pkt ); + if( !rc && is_status_enabled() ) { + print_status_sig_created ( sk, sig, 'C'); + } free_packet( &pkt ); if( rc ) log_error("build signature packet failed: %s\n", gpg_errstr(rc) ); diff --git a/g10/signal.c b/g10/signal.c index fcb012e02..f61b0a8f8 100644 --- a/g10/signal.c +++ b/g10/signal.c @@ -1,5 +1,5 @@ /* signal.c - signal handling - * Copyright (C) 1998, 1999 Free Software Foundation, Inc. + * Copyright (C) 1998, 1999, 2000 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -27,9 +27,9 @@ #include #include +#include #include "options.h" #include "errors.h" -#include #include "util.h" #include "main.h" #include "ttyio.h" @@ -59,14 +59,23 @@ got_fatal_signal( int sig ) caught_fatal_sig = 1; gcry_control( GCRYCTL_TERM_SECMEM ); - #ifdef IS_DEVELOPMENT_VERSION + /* better don't transtale these messages */ write(2, "\n", 1 ); s = log_get_name(); if( s ) write(2, s, strlen(s) ); write(2, ": ", 2 ); s = get_signal_name(sig); write(2, s, strlen(s) ); write(2, " caught ... exiting\n", 21 ); + + #ifndef HAVE_DOSISH_SYSTEM + { /* reset action to default action and raise signal again */ + struct sigaction nact; + nact.sa_handler = SIG_DFL; + sigemptyset( &nact.sa_mask ); + nact.sa_flags = 0; + sigaction( sig, &nact, NULL); + } #endif - exit(8); /* Hmmm, for some reasons rais2e does not work */ + raise( sig ); } diff --git a/g10/skclist.c b/g10/skclist.c index a0d69a49e..8fcb22aba 100644 --- a/g10/skclist.c +++ b/g10/skclist.c @@ -1,5 +1,5 @@ /* skclist.c - * Copyright (C) 1998 Free Software Foundation, Inc. + * Copyright (C) 1998, 1999, 2000 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -66,7 +66,7 @@ build_sk_list( STRLIST locusr, SK_LIST *ret_sk_list, int unlock, } else if( !(rc=openpgp_pk_test_algo(sk->pubkey_algo, use)) ) { SK_LIST r; - if( sk->version == 4 && (use & GCRY_PK_USAGE_SIGN) + if( sk->version == 4 && (use & GCRY_PK_USAGE_SIGN ) && sk->pubkey_algo == GCRY_PK_ELG_E ) { log_info("this is a PGP generated " "ElGamal key which is NOT secure for signatures!\n"); diff --git a/g10/status.c b/g10/status.c index 3eeec4648..c35a8adba 100644 --- a/g10/status.c +++ b/g10/status.c @@ -1,5 +1,5 @@ /* status.c - * Copyright (C) 1998 Free Software Foundation, Inc. + * Copyright (C) 1998, 1999, 2000 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -38,12 +38,13 @@ #include #endif #endif + +#include #include "util.h" #include "status.h" #include "ttyio.h" #include "options.h" #include "main.h" -#include #include "i18n.h" static int fd = -1; @@ -54,10 +55,32 @@ static int fd = -1; static int shm_is_locked; #endif /*USE_SHM_COPROCESSING*/ + +static void +progress_cb ( void *ctx, int c ) +{ + char buf[50]; + + if ( c == '\n' ) + sprintf ( buf, "%.20s X 100 100", (char*)ctx ); + else + sprintf ( buf, "%.20s %c 0 0", (char*)ctx, c ); + write_status_text ( STATUS_PROGRESS, buf ); +} + + void set_status_fd ( int newfd ) { fd = newfd; + if ( fd != -1 ) { + #if 0 + #warning fixme - progress functions + register_primegen_progress ( progress_cb, "primegen" ); + register_pk_dsa_progress ( progress_cb, "pk_dsa" ); + register_pk_elg_progress ( progress_cb, "pk_elg" ); + #endif + } } int @@ -96,6 +119,10 @@ write_status_text ( int no, const char *text) case STATUS_TRUST_MARGINAL : s = "TRUST_MARGINAL\n"; break; case STATUS_TRUST_FULLY : s = "TRUST_FULLY\n"; break; case STATUS_TRUST_ULTIMATE : s = "TRUST_ULTIMATE\n"; break; + case STATUS_GET_BOOL : s = "GET_BOOL\n"; break; + case STATUS_GET_LINE : s = "GET_LINE\n"; break; + case STATUS_GET_HIDDEN : s = "GET_HIDDEN\n"; break; + case STATUS_GOT_IT : s = "GOT_IT\n"; break; case STATUS_SHM_INFO : s = "SHM_INFO\n"; break; case STATUS_SHM_GET : s = "SHM_GET\n"; break; case STATUS_SHM_GET_BOOL : s = "SHM_GET_BOOL\n"; break; @@ -118,6 +145,16 @@ write_status_text ( int no, const char *text) case STATUS_ERRMDC : s = "ERRMDC\n"; break; case STATUS_IMPORTED : s = "IMPORTED\n"; break; case STATUS_IMPORT_RES : s = "IMPORT_RES\n"; break; + case STATUS_FILE_START : s = "FILE_START\n"; break; + case STATUS_FILE_DONE : s = "FILE_DONE\n"; break; + case STATUS_FILE_ERROR : s = "FILE_ERROR\n"; break; + case STATUS_BEGIN_DECRYPTION:s = "BEGIN_DECRYPTION\n"; break; + case STATUS_END_DECRYPTION : s = "END_DECRYPTION\n"; break; + case STATUS_BEGIN_ENCRYPTION:s = "BEGIN_ENCRYPTION\n"; break; + case STATUS_END_ENCRYPTION : s = "END_ENCRYPTION\n"; break; + case STATUS_DELETE_PROBLEM : s = "DELETE_PROBLEM\n"; break; + case STATUS_PROGRESS : s = "PROGRESS\n"; break; + case STATUS_SIG_CREATED : s = "SIG_CREATED\n"; break; default: s = "?\n"; break; } @@ -164,6 +201,10 @@ init_shm_coprocessing ( ulong requested_shm_size, int lock_mem ) if ( shm_id == -1 ) log_fatal("can't get %uk of shared memory: %s\n", (unsigned)shm_size/1024, strerror(errno)); + + #if !defined(IPC_HAVE_SHM_LOCK) \ + && defined(HAVE_MLOCK) && !defined(HAVE_BROKEN_MLOCK) + /* part of the old code which uses mlock */ shm_area = shmat( shm_id, 0, 0 ); if ( shm_area == (char*)-1 ) log_fatal("can't attach %uk shared memory: %s\n", @@ -174,29 +215,17 @@ init_shm_coprocessing ( ulong requested_shm_size, int lock_mem ) #ifdef USE_CAPABILITIES cap_set_proc( cap_from_text("cap_ipc_lock+ep") ); #endif - #ifdef IPC_HAVE_SHM_LOCK - if ( shmctl (shm_id, SHM_LOCK, 0) ) - log_info("locking shared memory %d failed: %s\n", - shm_id, strerror(errno)); - else - shm_is_locked = 1; - #elif defined(HAVE_MLOCK) && !defined(HAVE_BROKEN_MLOCK) /* (need the cast for Solaris with Sun's workshop compilers) */ if ( mlock ( (char*)shm_area, shm_size) ) log_info("locking shared memory %d failed: %s\n", shm_id, strerror(errno)); else shm_is_locked = 1; - #else - log_info("Locking shared memory %d failed: No way to do it\n", shm_id ); - #endif #ifdef USE_CAPABILITIES cap_set_proc( cap_from_text("cap_ipc_lock+p") ); #endif } - - #ifdef IPC_RMID_DEFERRED_RELEASE if( shmctl( shm_id, IPC_RMID, 0) ) log_fatal("shmctl IPC_RMDID of %d failed: %s\n", @@ -213,13 +242,59 @@ init_shm_coprocessing ( ulong requested_shm_size, int lock_mem ) shm_id, strerror(errno)); } + #else /* this is the new code which handles the changes in the SHM semantics + * introduced with Linux 2.4. The changes is that we now change the + * permissions and then attach to the memory. + */ + + if( lock_mem ) { + #ifdef USE_CAPABILITIES + cap_set_proc( cap_from_text("cap_ipc_lock+ep") ); + #endif + #ifdef IPC_HAVE_SHM_LOCK + if ( shmctl (shm_id, SHM_LOCK, 0) ) + log_info("locking shared memory %d failed: %s\n", + shm_id, strerror(errno)); + else + shm_is_locked = 1; + #else + log_info("Locking shared memory %d failed: No way to do it\n", shm_id ); + #endif + #ifdef USE_CAPABILITIES + cap_set_proc( cap_from_text("cap_ipc_lock+p") ); + #endif + } + + if( shmctl( shm_id, IPC_STAT, &shmds ) ) + log_fatal("shmctl IPC_STAT of %d failed: %s\n", + shm_id, strerror(errno)); + if( shmds.shm_perm.uid != getuid() ) { + shmds.shm_perm.uid = getuid(); + if( shmctl( shm_id, IPC_SET, &shmds ) ) + log_fatal("shmctl IPC_SET of %d failed: %s\n", + shm_id, strerror(errno)); + } + + shm_area = shmat( shm_id, 0, 0 ); + if ( shm_area == (char*)-1 ) + log_fatal("can't attach %uk shared memory: %s\n", + (unsigned)shm_size/1024, strerror(errno)); + log_debug("mapped %uk shared memory at %p, id=%d\n", + (unsigned)shm_size/1024, shm_area, shm_id ); + + #ifdef IPC_RMID_DEFERRED_RELEASE + if( shmctl( shm_id, IPC_RMID, 0) ) + log_fatal("shmctl IPC_RMDID of %d failed: %s\n", + shm_id, strerror(errno)); + #endif + + #endif /* write info; Protocol version, id, size, locked size */ sprintf( buf, "pv=1 pid=%d shmid=%d sz=%u lz=%u", (int)getpid(), shm_id, (unsigned)shm_size, shm_is_locked? (unsigned)shm_size:0 ); write_status_text( STATUS_SHM_INFO, buf ); } - /**************** * Request a string from client * If bool, returns static string on true (do not free) or NULL for false @@ -269,10 +344,50 @@ do_shm_get( const char *keyword, int hidden, int bool ) #endif /* USE_SHM_COPROCESSING */ +/**************** + * Request a string from the client over the command-fd + * If bool, returns static string on true (do not free) or NULL for false + */ +static char * +do_get_from_fd( const char *keyword, int hidden, int bool ) +{ + int i, len; + char *string; + + write_status_text( bool? STATUS_GET_BOOL : + hidden? STATUS_GET_HIDDEN : STATUS_GET_LINE, keyword ); + + for( string = NULL, i = len = 200; ; i++ ) { + if( i >= len-1 ) { + char *save = string; + len += 100; + string = hidden? gcry_xmalloc_secure ( len ) : gcry_malloc ( len ); + if( save ) + memcpy(string, save, i ); + else + i=0; + } + /* Hmmm: why not use our read_line function here */ + if( read( fd, string+i, 1) != 1 || string[i] == '\n' ) + break; + } + string[i] = 0; + + write_status( STATUS_GOT_IT ); + + if( bool ) /* Fixme: is this correct??? */ + return string[0] == 'Y' ? "" : NULL; + + return string; +} + + int cpr_enabled() { + if( opt.command_fd != -1 ) + return 1; #ifdef USE_SHM_COPROCESSING if( opt.shm_coprocess ) return 1; @@ -285,6 +400,8 @@ cpr_get( const char *keyword, const char *prompt ) { char *p; + if( opt.command_fd != -1 ) + return do_get_from_fd ( keyword, 0, 0 ); #ifdef USE_SHM_COPROCESSING if( opt.shm_coprocess ) return do_shm_get( keyword, 0, 0 ); @@ -318,6 +435,8 @@ cpr_get_hidden( const char *keyword, const char *prompt ) { char *p; + if( opt.command_fd != -1 ) + return do_get_from_fd ( keyword, 1, 0 ); #ifdef USE_SHM_COPROCESSING if( opt.shm_coprocess ) return do_shm_get( keyword, 1, 0 ); @@ -336,6 +455,8 @@ cpr_get_hidden( const char *keyword, const char *prompt ) void cpr_kill_prompt(void) { + if( opt.command_fd != -1 ) + return; #ifdef USE_SHM_COPROCESSING if( opt.shm_coprocess ) return; @@ -350,6 +471,8 @@ cpr_get_answer_is_yes( const char *keyword, const char *prompt ) int yes; char *p; + if( opt.command_fd != -1 ) + return !!do_get_from_fd ( keyword, 0, 1 ); #ifdef USE_SHM_COPROCESSING if( opt.shm_coprocess ) return !!do_shm_get( keyword, 0, 1 ); @@ -376,6 +499,8 @@ cpr_get_answer_yes_no_quit( const char *keyword, const char *prompt ) int yes; char *p; + if( opt.command_fd != -1 ) + return !!do_get_from_fd ( keyword, 0, 1 ); #ifdef USE_SHM_COPROCESSING if( opt.shm_coprocess ) return !!do_shm_get( keyword, 0, 1 ); diff --git a/g10/status.h b/g10/status.h index 73cc1b1bf..f9cce5b6b 100644 --- a/g10/status.h +++ b/g10/status.h @@ -1,5 +1,5 @@ /* status.h - * Copyright (C) 1998 Free Software Foundation, Inc. + * Copyright (C) 1998, 1999, 2000 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -65,7 +65,22 @@ #define STATUS_ERRMDC 35 #define STATUS_IMPORTED 36 #define STATUS_IMPORT_RES 37 +#define STATUS_FILE_START 38 +#define STATUS_FILE_DONE 39 +#define STATUS_FILE_ERROR 40 +#define STATUS_BEGIN_DECRYPTION 41 +#define STATUS_END_DECRYPTION 42 +#define STATUS_BEGIN_ENCRYPTION 43 +#define STATUS_END_ENCRYPTION 44 + +#define STATUS_DELETE_PROBLEM 45 +#define STATUS_GET_BOOL 46 +#define STATUS_GET_LINE 47 +#define STATUS_GET_HIDDEN 48 +#define STATUS_GOT_IT 49 +#define STATUS_PROGRESS 50 +#define STATUS_SIG_CREATED 51 /*-- status.c --*/ void set_status_fd ( int fd ); diff --git a/g10/tdbdump.c b/g10/tdbdump.c index 9be91241b..6729d4e56 100644 --- a/g10/tdbdump.c +++ b/g10/tdbdump.c @@ -1,5 +1,5 @@ /* tdbdump.c - * Copyright (C) 1999 Free Software Foundation, Inc. + * Copyright (C) 1999, 2000 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -520,5 +520,6 @@ import_ownertrust( const char *fname ) if( !is_stdin ) fclose(fp); do_sync(); + sync_trustdb(); } diff --git a/g10/tdbio.c b/g10/tdbio.c index 926048fbf..669f66ffc 100644 --- a/g10/tdbio.c +++ b/g10/tdbio.c @@ -1,5 +1,5 @@ /* tdbio.c - * Copyright (C) 1998 Free Software Foundation, Inc. + * Copyright (C) 1998, 1999, 2000 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -40,11 +40,6 @@ #include "tdbio.h" -#ifdef MKDIR_TAKES_ONE_ARG -# undef mkdir -# define mkdir(a,b) mkdir(a) -#endif - /**************** * Yes, this is a very simple implementation. We should really * use a page aligned buffer and read complete pages. @@ -439,17 +434,8 @@ tdbio_set_dbname( const char *new_dbname, int create ) assert(p); *p = 0; if( access( fname, F_OK ) ) { - if( strlen(fname) >= 7 - && !strcmp(fname+strlen(fname)-7, "/.gnupg" ) ) { - if( mkdir( fname, S_IRUSR|S_IWUSR|S_IXUSR ) ) - log_fatal( _("%s: can't create directory: %s\n"), - fname, strerror(errno) ); - else if( !opt.quiet ) - log_info( _("%s: directory created\n"), fname ); - copy_options_file( fname ); - } - else - log_fatal( _("%s: directory does not exist!\n"), fname ); + try_make_homedir( fname ); + log_fatal( _("%s: directory does not exist!\n"), fname ); } *p = '/'; @@ -1130,6 +1116,8 @@ tdbio_dump_record( TRUSTREC *rec, FILE *fp ) fputs(", expired", fp ); if( rec->r.dir.dirflags & DIRF_REVOKED ) fputs(", revoked", fp ); + if( rec->r.dir.dirflags & DIRF_NEWKEYS ) + fputs(", newkeys", fp ); } putc('\n', fp); break; diff --git a/g10/tdbio.h b/g10/tdbio.h index 4eabed91c..a2e5404f6 100644 --- a/g10/tdbio.h +++ b/g10/tdbio.h @@ -1,5 +1,5 @@ /* tdbio.h - Trust database I/O functions - * Copyright (C) 1998 Free Software Foundation, Inc. + * Copyright (C) 1998, 1999, 2000 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -52,6 +52,7 @@ /* one uid with a selfsignature or an revocation */ #define DIRF_EXPIRED 4 /* the complete key has expired */ #define DIRF_REVOKED 8 /* the complete key has been revoked */ +#define DIRF_NEWKEYS 128 /* new keys are available: we can check the sigs */ #define KEYF_CHECKED 1 /* This key has been checked */ #define KEYF_VALID 2 /* This is a valid (sub)key */ @@ -121,7 +122,7 @@ struct trust_record { } uid; struct { /* preference record */ ulong lid; /* point back to the directory record */ - /* or 0 for a glocal pref record */ + /* or 0 for a global pref record */ ulong next; /* points to next pref record */ byte data[ITEMS_PER_PREF_RECORD]; } pref; diff --git a/g10/textfilter.c b/g10/textfilter.c index 06f85dc64..a360ffccb 100644 --- a/g10/textfilter.c +++ b/g10/textfilter.c @@ -1,5 +1,5 @@ /* textfilter.c - * Copyright (C) 1998,1999 Free Software Foundation, Inc. + * Copyright (C) 1998, 1999, 2000 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -31,7 +31,13 @@ #include "util.h" #include "filter.h" #include "i18n.h" +#include "options.h" +#ifdef HAVE_DOSISH_SYSTEM + #define LF "\r\n" +#else + #define LF "\n" +#endif #define MAX_LINELEN 19995 /* a little bit smaller than in armor.c */ /* to make sure that a warning is displayed while */ @@ -151,6 +157,9 @@ copy_clearsig_text( IOBUF out, IOBUF inp, GCRY_MD_HD md, int truncated = 0; int pending_lf = 0; + if( !opt.pgp2_workarounds ) + pgp2mode = 0; + if( !escape_dash ) escape_from = 0; @@ -183,12 +192,37 @@ copy_clearsig_text( IOBUF out, IOBUF inp, GCRY_MD_HD md, iobuf_put( out, '-' ); iobuf_put( out, ' ' ); } + + #if 0 /*defined(HAVE_DOSISH_SYSTEM)*/ + /* We don't use this anymore because my interpretation of rfc2440 7.1 + * is that there is no conversion needed. If one decides to + * clearsign a unix file on a DOS box he will get a mixed line endings. + * If at some point it turns out, that a conversion is a nice feature + * we can make an option out of it. + */ + /* make sure the lines do end in CR,LF */ + if( n > 1 && ( (buffer[n-2] == '\r' && buffer[n-1] == '\n' ) + || (buffer[n-2] == '\n' && buffer[n-1] == '\r'))) { + iobuf_write( out, buffer, n-2 ); + iobuf_put( out, '\r'); + iobuf_put( out, '\n'); + } + else if( n && buffer[n-1] == '\n' ) { + iobuf_write( out, buffer, n-1 ); + iobuf_put( out, '\r'); + iobuf_put( out, '\n'); + } + else + iobuf_write( out, buffer, n ); + + #else iobuf_write( out, buffer, n ); + #endif } /* at eof */ if( !pending_lf ) { /* make sure that the file ends with a LF */ - iobuf_put( out, '\n'); + iobuf_writestr( out, LF ); if( !escape_dash ) gcry_md_putc( md, '\n' ); } diff --git a/g10/trustdb.c b/g10/trustdb.c index 610b3b355..f75ccc52f 100644 --- a/g10/trustdb.c +++ b/g10/trustdb.c @@ -1,5 +1,5 @@ /* trustdb.c - * Copyright (C) 1998, 1999 Free Software Foundation, Inc. + * Copyright (C) 1998, 1999, 2000 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -110,7 +110,6 @@ static int alloced_tns; static int max_alloced_tns; - static LOCAL_ID_TABLE new_lid_table(void); static int ins_lid_table_item( LOCAL_ID_TABLE tbl, ulong lid, unsigned flag ); static int qry_lid_table_flag( LOCAL_ID_TABLE tbl, ulong lid, unsigned *flag ); @@ -125,13 +124,22 @@ static int do_check( TRUSTREC *drec, unsigned *trustlevel, unsigned *retflgs); static int get_dir_record( PKT_public_key *pk, TRUSTREC *rec ); static int do_update_trust_record( KBNODE keyblock, TRUSTREC *drec, - int recheck, int *modified ); -static int check_trust_record( TRUSTREC *drec ); + int sigs_only, int *modified ); +static int check_trust_record( TRUSTREC *drec, int sigs_only ); +static void mark_fresh_keys(void); /* a table used to keep track of ultimately trusted keys * which are the ones from our secrings and the trusted keys */ static LOCAL_ID_TABLE ultikey_table; + +/* a table to keep track of newly importted keys. This one is + * create by the insert_trust_record function and from time to time + * used to verify key signature which have been done with these new keys */ +static LOCAL_ID_TABLE fresh_imported_keys; +static int fresh_imported_keys_count; +#define FRESH_KEY_CHECK_THRESHOLD 200 + /* list of unused lid items and tables */ static LOCAL_ID_TABLE unused_lid_tables; static struct local_id_item *unused_lid_items; @@ -245,6 +253,27 @@ release_lid_table( LOCAL_ID_TABLE tbl ) } #endif + +/**************** + * Remove all items from a LID table + */ +static void +clear_lid_table( LOCAL_ID_TABLE tbl ) +{ + struct local_id_item *a, *a2; + int i; + + for(i=0; i < 16; i++ ) { + for(a=tbl->items[i]; a; a = a2 ) { + a2 = a->next; + a->next = unused_lid_items; + unused_lid_items = a; + } + tbl->items[i] = NULL; + } +} + + /**************** * Add a new item to the table or return 1 if we already have this item */ @@ -454,7 +483,7 @@ verify_own_keys(void) if( DBG_TRUST ) log_debug("key %08lX: checking secret key\n", (ulong)keyid[1] ); - if( is_secret_key_protected( sk ) < 1 ) + if( !opt.quiet && is_secret_key_protected( sk ) < 1 ) log_info(_("NOTE: secret key %08lX is NOT protected.\n"), (ulong)keyid[1] ); @@ -572,6 +601,18 @@ init_trustdb() +/**************** + * This function should be called in certain cases to sync the internal state + * of the trustdb with the file image. Currently it is needed after + * a sequence of insert_trust_record() calls. + */ +void +sync_trustdb() +{ + if( fresh_imported_keys && fresh_imported_keys_count ) + mark_fresh_keys(); +} + /*********************************************** @@ -652,7 +693,7 @@ print_path( int pathlen, TN ME .........., FILE *fp, ulong highlight ) p = get_user_id( keyid, &n ); putc(' ', fp); putc('\"', fp); - print_utf8_string( fp, p, n > 40? 40:n, 0 ); + print_utf8_string( fp, p, n > 40? 40:n ); putc('\"', fp); gcry_free(p); putc('\n', fp ); @@ -683,8 +724,14 @@ print_uid_from_keyblock( FILE *fp, KBNODE keyblock, ulong urecno ) if( node->pkt->pkttype == PKT_USER_ID ) { PKT_user_id *uidpkt = node->pkt->pkt.user_id; - gcry_md_hash_buffer( GCRY_MD_RMD160, uhash, - uidpkt->name, uidpkt->len ); + if( uidpkt->photo ) { + gcry_md_hash_buffer( GCRY_MD_RMD160, uhash, + uidpkt->photo, uidpkt->photolen ); + } + else { + gcry_md_hash_buffer( GCRY_MD_RMD160, uhash, + uidpkt->name, uidpkt->len ); + } if( !memcmp( uhash, urec.r.uid.namehash, 20 ) ) { print_string( fp, uidpkt->name, uidpkt->len, ':' ); return; @@ -1001,7 +1048,7 @@ check_uidsigs( KBNODE keyblock, KBNODE keynode, u32 *mainkid, ulong lid, (ulong)mainkid[1], lid ); assert(keynode->pkt->pkttype == PKT_USER_ID ); uid = keynode->pkt->pkt.user_id; - print_utf8_string( log_stream(), uid->name, uid->len ); + print_string( log_stream(), uid->name, uid->len, '\"' ); fputs("\"\n", log_stream()); } @@ -1083,17 +1130,17 @@ check_uidsigs( KBNODE keyblock, KBNODE keynode, u32 *mainkid, ulong lid, static unsigned int check_sig_record( KBNODE keyblock, KBNODE signode, ulong siglid, int sigidx, u32 *keyid, ulong lid, - u32 *r_expire ) + u32 *r_expiretime, int *mod_down, int *mod_up ) { PKT_signature *sig = signode->pkt->pkt.signature; unsigned int sigflag = 0; TRUSTREC tmp; - int revocation=0, rc; + int revocation=0, expired=0, rc; if( DBG_TRUST ) log_debug("check_sig_record: %08lX.%lu %lu[%d]\n", (ulong)keyid[1], lid, siglid, sigidx ); - *r_expire = 0; + *r_expiretime = 0; if( (sig->sig_class&~3) == 0x10 ) /* regular certification */ ; else if( sig->sig_class == 0x30 ) /* cert revocation */ @@ -1104,7 +1151,8 @@ check_sig_record( KBNODE keyblock, KBNODE signode, read_record( siglid, &tmp, 0 ); if( tmp.rectype == RECTYPE_DIR ) { /* the public key is in the trustdb: check sig */ - rc = check_key_signature2( keyblock, signode, NULL, r_expire ); + rc = check_key_signature2( keyblock, signode, NULL, + r_expiretime, &expired ); if( !rc ) { /* valid signature */ if( opt.verbose ) log_info("sig %08lX.%lu/%lu[%d]/%08lX: %s\n", @@ -1113,18 +1161,25 @@ check_sig_record( KBNODE keyblock, KBNODE signode, revocation? _("Valid certificate revocation") : _("Good certificate") ); sigflag |= SIGF_CHECKED | SIGF_VALID; + if( expired ) { + sigflag |= SIGF_EXPIRED; + /* We have to reset the expiretime, so that this signature + * does not get checked over and over due to the reached + * expiretime */ + *r_expiretime = 0; + } if( revocation ) { sigflag |= SIGF_REVOKED; - /**mod_down = 1;*/ + *mod_down = 1; } else - /**mod_up = 1*/; + *mod_up = 1; } else if( rc == GPGERR_NO_PUBKEY ) { /* This may happen if the key is still in the trustdb * but not available in the keystorage */ sigflag |= SIGF_NOPUBKEY; - /**mod_down = 1;*/ + *mod_down = 1; if( revocation ) sigflag |= SIGF_REVOKED; } @@ -1138,7 +1193,7 @@ check_sig_record( KBNODE keyblock, KBNODE signode, sigflag |= SIGF_CHECKED; if( revocation ) { sigflag |= SIGF_REVOKED; - /**mod_down = 1;*/ + *mod_down = 1; } } } @@ -1169,14 +1224,15 @@ check_sig_record( KBNODE keyblock, KBNODE signode, */ static ulong make_sig_records( KBNODE keyblock, KBNODE uidnode, - ulong lid, u32 *mainkid, u32 *min_expire ) + ulong lid, u32 *mainkid, u32 *min_expire, + int *mod_down, int *mod_up ) { TRUSTREC *srecs, **s_end, *s=NULL, *s2; KBNODE node; PKT_signature *sig; ulong sigrecno, siglid; int i, sigidx = 0; - u32 expire; + u32 expiretime; srecs = NULL; s_end = &srecs; for( node=uidnode->next; node; node = node->next ) { @@ -1191,6 +1247,12 @@ make_sig_records( KBNODE keyblock, KBNODE uidnode, siglid = find_or_create_lid( sig ); /* smash dups */ + /* FIXME: Here we have a problem: + * We can't distinguish between a certification and a certification + * revocation without looking at class of the signature - we have + * to see how we can store the sigclass in the sigrecord.. + * Argg- I hope I can get rid of this ugly trustdb ASAP. + */ for( s2 = s; s2 ; s2 = s2->next ) { for(i=0; i < sigidx; i++ ) { if( s2->r.sig.sig[i].lid == siglid ) @@ -1219,7 +1281,8 @@ make_sig_records( KBNODE keyblock, KBNODE uidnode, s->r.sig.sig[sigidx].lid = siglid; s->r.sig.sig[sigidx].flag= check_sig_record( keyblock, node, siglid, sigidx, - mainkid, lid, &expire ); + mainkid, lid, &expiretime, + mod_down, mod_up ); sigidx++; if( sigidx == SIGS_PER_RECORD ) { @@ -1229,8 +1292,8 @@ make_sig_records( KBNODE keyblock, KBNODE uidnode, sigidx = 0; } /* keep track of signers pk expire time */ - if( expire && (!*min_expire || *min_expire > expire ) ) - *min_expire = expire; + if( expiretime && (!*min_expire || *min_expire > expiretime ) ) + *min_expire = expiretime; } if( sigidx ) { s->recnum = tdbio_new_recnum(); @@ -1320,7 +1383,8 @@ make_pref_record( PKT_signature *sig, ulong lid ) static ulong -make_uid_records( KBNODE keyblock, ulong lid, u32 *keyid, u32 *min_expire ) +make_uid_records( KBNODE keyblock, ulong lid, u32 *keyid, u32 *min_expire, + int *mod_down, int *mod_up ) { TRUSTREC *urecs, **uend, *u, *u2; KBNODE node; @@ -1335,7 +1399,14 @@ make_uid_records( KBNODE keyblock, ulong lid, u32 *keyid, u32 *min_expire ) if( node->pkt->pkttype != PKT_USER_ID ) continue; uid = node->pkt->pkt.user_id; - gcry_md_hash_buffer( GCRY_MD_RMD160, uidhash, uid->name, uid->len ); + if( uid->photo ) { + gcry_md_hash_buffer( GCRY_MD_RMD160, uidhash, + uid->photo, uid->photolen ); + } + else { + gcry_md_hash_buffer( GCRY_MD_RMD160, uidhash, + uid->name, uid->len ); + } /* create the uid record */ u = gcry_xcalloc( 1, sizeof *u ); @@ -1352,9 +1423,21 @@ make_uid_records( KBNODE keyblock, ulong lid, u32 *keyid, u32 *min_expire ) && (u->r.uid.uidflags & UIDF_VALID) ) { u->r.uid.prefrec = bestsig? make_pref_record( bestsig, lid ) : 0; } + + /* the next test is really bad because we should modify + * out modification timestamps only if we really have a change. + * But because we are deleting the uid records first it is somewhat + * difficult to track those changes. fixme */ + if( !( u->r.uid.uidflags & UIDF_VALID ) + || ( u->r.uid.uidflags & UIDF_REVOKED ) ) + *mod_down=1; + else + *mod_up=1; + /* create the list of signatures */ u->r.uid.siglist = make_sig_records( keyblock, node, - lid, keyid, min_expire ); + lid, keyid, min_expire, + mod_down, mod_up ); } uidrecno = urecs? urecs->recnum : 0; @@ -1381,6 +1464,8 @@ update_trust_record( KBNODE keyblock, int recheck, int *modified ) TRUSTREC drec; int rc; + /* NOTE: We don't need recheck anymore, but this might chnage again in + * the future */ if( opt.dry_run ) return 0; if( modified ) @@ -1391,26 +1476,27 @@ update_trust_record( KBNODE keyblock, int recheck, int *modified ) if( rc ) return rc; - rc = do_update_trust_record( keyblock, &drec, recheck, modified ); + rc = do_update_trust_record( keyblock, &drec, 0, modified ); return rc; } /**************** - * Same as update_trust_record, but tghis functions expects the dir record. - * On exit the dirrecord will reflect any changes made. + * Same as update_trust_record, but this functions expects the dir record. + * On exit the dir record will reflect any changes made. + * With sigs_only set only foreign key signatures are checked. */ static int do_update_trust_record( KBNODE keyblock, TRUSTREC *drec, - int recheck, int *modified ) + int sigs_only, int *modified ) { PKT_public_key *primary_pk; TRUSTREC krec, urec, prec, helprec; int i, rc = 0; u32 keyid[2]; /* keyid of primary key */ -/* int mod_up = 0; - int mod_down = 0; */ + int mod_up = 0; + int mod_down = 0; ulong recno, r2; - u32 expire; + u32 expiretime; primary_pk = find_kbnode( keyblock, PKT_PUBLIC_KEY )->pkt->pkt.public_key; if( !primary_pk->local_id ) @@ -1425,7 +1511,7 @@ do_update_trust_record( KBNODE keyblock, TRUSTREC *drec, if( rc ) return rc; - /* delete the old stuff */ + /* delete the old stuff FIXME: implementend sigs_only */ for( recno=drec->r.dir.keylist; recno; recno = krec.r.key.next ) { read_record( recno, &krec, RECTYPE_KEY ); delete_record( recno ); @@ -1448,22 +1534,13 @@ do_update_trust_record( KBNODE keyblock, TRUSTREC *drec, /* insert new stuff */ drec->r.dir.dirflags &= ~DIRF_REVOKED; + drec->r.dir.dirflags &= ~DIRF_NEWKEYS; drec->r.dir.keylist = make_key_records( keyblock, drec->recnum, keyid, &i ); if( i ) /* primary key has been revoked */ - drec->r.dir.dirflags &= DIRF_REVOKED; - expire = 0; + drec->r.dir.dirflags |= DIRF_REVOKED; + expiretime = 0; drec->r.dir.uidlist = make_uid_records( keyblock, drec->recnum, keyid, - &expire ); - #if 0 - if( orig_uidflags != urec.r.uid.uidflags ) { - write_record( &urec ); - if( !( urec.r.uid.uidflags & UIDF_VALID ) - || ( urec.r.uid.uidflags & UIDF_REVOKED ) ) - *mod_down=1; - else - *mod_up=1; /*(maybe a new user id)*/ - #endif - + &expiretime, &mod_down, &mod_up ); if( rc ) rc = tdbio_cancel_transaction(); else { @@ -1471,9 +1548,9 @@ do_update_trust_record( KBNODE keyblock, TRUSTREC *drec, *modified = 1; drec->r.dir.dirflags |= DIRF_CHECKED; drec->r.dir.valcheck = 0; - drec->r.dir.checkat = expire; + drec->r.dir.checkat = expiretime; write_record( drec ); - /*tdbio_write_modify_stamp( mod_up, mod_down );*/ + tdbio_write_modify_stamp( mod_up, mod_down ); rc = tdbio_end_transaction(); } return rc; @@ -1538,16 +1615,28 @@ insert_trust_record( KBNODE keyblock ) } } + /* mark tdb as modified upwards */ tdbio_write_modify_stamp( 1, 0 ); /* and put all the other stuff into the keydb */ - rc = do_update_trust_record( keyblock, &dirrec, 1, NULL ); + rc = do_update_trust_record( keyblock, &dirrec, 0, NULL ); do_sync(); + + /* keep track of new keys */ + if( !fresh_imported_keys ) + fresh_imported_keys = new_lid_table(); + ins_lid_table_item( fresh_imported_keys, pk->local_id, 0 ); + if( ++fresh_imported_keys_count > FRESH_KEY_CHECK_THRESHOLD ) + mark_fresh_keys(); + return rc; } + + + /**************** * Insert a trust record indentified by a PK into the TrustDB */ @@ -1585,7 +1674,7 @@ insert_trust_record_by_pk( PKT_public_key *pk ) * Currently we only do an update_trust_record. */ static int -check_trust_record( TRUSTREC *drec ) +check_trust_record( TRUSTREC *drec, int sigs_only ) { KBNODE keyblock; int modified, rc; @@ -1597,7 +1686,7 @@ check_trust_record( TRUSTREC *drec ) return rc; } - rc = do_update_trust_record( keyblock, drec, 0, &modified ); + rc = do_update_trust_record( keyblock, drec, sigs_only, &modified ); release_kbnode( keyblock ); return rc; @@ -1674,7 +1763,7 @@ update_trustdb() /**************** - * Do all required check in the trustdb. This function walks over all + * Do all required checks in the trustdb. This function walks over all * records in the trustdb and does scheduled processing. */ void @@ -1682,7 +1771,7 @@ check_trustdb( const char *username ) { TRUSTREC rec; ulong recnum; - ulong count=0, upd_count=0, err_count=0, skip_count=0; + ulong count=0, upd_count=0, err_count=0, skip_count=0, sigonly_count=0; ulong current_time = make_timestamp(); if( username ) @@ -1691,15 +1780,25 @@ check_trustdb( const char *username ) init_trustdb(); for(recnum=0; !tdbio_read_record( recnum, &rec, 0); recnum++ ) { + int sigs_only; + if( rec.rectype != RECTYPE_DIR ) continue; /* we only want the dir records */ if( count && !(count % 100) && !opt.quiet ) log_info(_("%lu keys so far processed\n"), count); count++; - if( !rec.r.dir.checkat || rec.r.dir.checkat > current_time ) { - skip_count++; - continue; /* not scheduled for checking */ + sigs_only = 0; + + if( !(rec.r.dir.dirflags & DIRF_CHECKED) ) + ; + else if( !rec.r.dir.checkat || rec.r.dir.checkat > current_time ) { + if( !(rec.r.dir.dirflags & DIRF_NEWKEYS) ) { + skip_count++; + continue; /* not scheduled for checking */ + } + sigs_only = 1; /* new public keys - check them */ + sigonly_count++; } if( !rec.r.dir.keylist ) { @@ -1708,11 +1807,12 @@ check_trustdb( const char *username ) continue; } - check_trust_record( &rec ); - + check_trust_record( &rec, sigs_only ); } log_info(_("%lu keys processed\n"), count); + if( sigonly_count ) + log_info(_("\t%lu due to new pubkeys\n"), sigonly_count); if( skip_count ) log_info(_("\t%lu keys skipped\n"), skip_count); if( err_count ) @@ -1772,8 +1872,12 @@ build_cert_tree( ulong lid, int depth, int max_depth, TN helproot ) return NULL; } - if( dirrec.r.dir.checkat && dirrec.r.dir.checkat <= make_timestamp() ) - check_trust_record( &dirrec ); + if( dirrec.r.dir.checkat && dirrec.r.dir.checkat <= make_timestamp() ) { + check_trust_record( &dirrec, 0 ); + } + else if( (dirrec.r.dir.dirflags & DIRF_NEWKEYS) ) { + check_trust_record( &dirrec, 1 ); + } keynode->n.k.ownertrust = dirrec.r.dir.ownertrust & TRUST_MASK; @@ -1924,10 +2028,10 @@ propagate_validity( TN root, TN node, int (*add_fnc)(ulong), unsigned *retflgs ) } /* loop over all user ids */ - for( ur=node->list; ur && max_validity < TRUST_FULLY; ur = ur->next ) { + for( ur=node->list; ur && max_validity <= TRUST_FULLY; ur = ur->next ) { assert( ur->is_uid ); /* loop over all signators */ - for(kr=ur->list; kr && max_validity < TRUST_FULLY; kr = kr->next ) { + for(kr=ur->list; kr && max_validity <= TRUST_FULLY; kr = kr->next ) { if( propagate_validity( root, kr, add_fnc, retflgs ) ) return -1; /* quit */ if( kr->n.k.validity == TRUST_ULTIMATE ) { @@ -2001,8 +2105,12 @@ verify_key( int max_depth, TRUSTREC *drec, const char *namehash, if( !tree ) return TRUST_UNDEFINED; pv_result = propagate_validity( tree, tree, add_fnc, retflgs ); - if( namehash ) { + if( namehash && tree->n.k.validity != TRUST_ULTIMATE ) { /* find the matching user id. + * We don't do this here if the key is ultimately trusted; in + * this case there will be no lids for the user IDs and frankly + * it does not make sense to compare by the name if we do + * have the secret key. * fixme: the way we handle this is too inefficient */ TN ur; TRUSTREC rec; @@ -2075,6 +2183,7 @@ do_check( TRUSTREC *dr, unsigned *validity, } else if( !add_fnc && tdbio_db_matches_options() + /* FIXME, TODO: This comparision is WRONG ! */ && dr->r.dir.valcheck > tdbio_read_modify_stamp( (dr->r.dir.validity < TRUST_FULLY) ) && dr->r.dir.validity ) @@ -2240,10 +2349,16 @@ check_trust( PKT_public_key *pk, unsigned *r_trustlevel, log_info(_("key %08lX.%lu: created in future " "(time warp or clock problem)\n"), (ulong)keyid[1], pk->local_id ); - return GPGERR_TIME_CONFLICT; + if( !opt.ignore_time_conflict ) + return GPGERR_TIME_CONFLICT; } - if( rec.r.dir.checkat && rec.r.dir.checkat <= cur_time ) - check_trust_record( &rec ); + + if( !(rec.r.dir.dirflags & DIRF_CHECKED) ) + check_trust_record( &rec, 0 ); + else if( rec.r.dir.checkat && rec.r.dir.checkat <= cur_time ) + check_trust_record( &rec, 0 ); + else if( (rec.r.dir.dirflags & DIRF_NEWKEYS) ) + check_trust_record( &rec, 1 ); if( pk->expiredate && pk->expiredate <= cur_time ) { log_info(_("key %08lX.%lu: expired at %s\n"), @@ -2299,6 +2414,51 @@ check_trust( PKT_public_key *pk, unsigned *r_trustlevel, } +/**************** + * scan the whole trustdb and mark all signature records whose keys + * are freshly imported. + */ +static void +mark_fresh_keys() +{ + TRUSTREC dirrec, rec; + ulong recnum, lid; + int i; + + memset( &dirrec, 0, sizeof dirrec ); + + for(recnum=0; !tdbio_read_record( recnum, &rec, 0); recnum++ ) { + if( rec.rectype != RECTYPE_SIG ) + continue; + /* if we have already have the dir record, we can check it now */ + if( dirrec.recnum == rec.r.sig.lid + && (dirrec.r.dir.dirflags & DIRF_NEWKEYS) ) + continue; /* flag is already set */ + + for(i=0; i < SIGS_PER_RECORD; i++ ) { + if( !(lid=rec.r.sig.sig[i].lid) ) + continue; /* skip deleted sigs */ + if( !(rec.r.sig.sig[i].flag & SIGF_CHECKED) ) + continue; /* skip checked signatures */ + if( qry_lid_table_flag( fresh_imported_keys, lid, NULL ) ) + continue; /* not in the list of new keys */ + read_record( rec.r.sig.lid, &dirrec, RECTYPE_DIR ); + if( !(dirrec.r.dir.dirflags & DIRF_NEWKEYS) ) { + dirrec.r.dir.dirflags |= DIRF_NEWKEYS; + write_record( &dirrec ); + } + break; + } + } + + do_sync(); + + clear_lid_table( fresh_imported_keys ); + fresh_imported_keys_count = 0; +} + + + int query_trust_info( PKT_public_key *pk, const byte *namehash ) { @@ -2532,7 +2692,7 @@ enum_cert_paths_print( void **context, FILE *fp, /* * Return an allocated buffer with the preference values for * the key with LID and the userid which is identified by the - * HAMEHASH or the firstone if namehash is NULL. ret_n receives + * HAMEHASH or the first one if namehash is NULL. ret_n receives * the length of the allocated buffer. Structure of the buffer is * a repeated sequences of 2 bytes; where the first byte describes the * type of the preference and the second one the value. The constants diff --git a/g10/trustdb.h b/g10/trustdb.h index 86351c1eb..1279edb0f 100644 --- a/g10/trustdb.h +++ b/g10/trustdb.h @@ -1,5 +1,5 @@ /* trustdb.h - Trust database - * Copyright (C) 1998 Free Software Foundation, Inc. + * Copyright (C) 1998, 1999, 2000 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -49,6 +49,7 @@ void check_trustdb( const char *username ); void update_trustdb( void ); int setup_trustdb( int level, const char *dbname ); void init_trustdb( void ); +void sync_trustdb( void ); int check_trust( PKT_public_key *pk, unsigned *r_trustlevel, const byte* nh, int (*add_fnc)(ulong), unsigned *retflgs ); int query_trust_info( PKT_public_key *pk, const byte *nh ); diff --git a/g10/verify.c b/g10/verify.c index 5fdf99338..f3f9a36eb 100644 --- a/g10/verify.c +++ b/g10/verify.c @@ -1,5 +1,5 @@ /* verify.c - verify signed data - * Copyright (C) 1998 Free Software Foundation, Inc. + * Copyright (C) 1998, 1999, 2000 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -25,14 +25,15 @@ #include #include +#include #include "options.h" #include "packet.h" #include "errors.h" #include "iobuf.h" #include "keydb.h" -#include #include "util.h" #include "main.h" +#include "status.h" #include "filter.h" #include "ttyio.h" #include "i18n.h" @@ -58,6 +59,7 @@ verify_signatures( int nfiles, char **files ) int i, rc; STRLIST sl; + memset( &afx, 0, sizeof afx); sigfile = nfiles? *files : NULL; /* open the signature file */ @@ -67,6 +69,51 @@ verify_signatures( int nfiles, char **files ) return GPGERR_OPEN_FILE; } + if( !opt.no_armor && use_armor_filter( fp ) ) + iobuf_push_filter( fp, armor_filter, &afx ); + + sl = NULL; + for(i=1 ; i < nfiles; i++ ) + add_to_strlist( &sl, files[i] ); + rc = proc_signature_packets( NULL, fp, sl, sigfile ); + free_strlist(sl); + iobuf_close(fp); + if( afx.no_openpgp_data && rc == -1 ) { + log_error(_("the signature could not be verified.\n" + "Please remember that the signature file (.sig or .asc)\n" + "should be the first file given on the command line.\n") ); + rc = 0; + } + + return rc; +} + + +static void +print_file_status( int status, const char *name, int what ) +{ + char *p = gcry_xmalloc(strlen(name)+10); + sprintf(p, "%d %s", what, name ); + write_status_text( status, p ); + gcry_free(p); +} + + +static int +verify_one_file( const char *name ) +{ + IOBUF fp; + armor_filter_context_t afx; + int rc; + + print_file_status( STATUS_FILE_START, name, 1 ); + fp = iobuf_open(name); + if( !fp ) { + print_file_status( STATUS_FILE_ERROR, name, 1 ); + log_error(_("can't open `%s'\n"), print_fname_stdin(name)); + return GPGERR_OPEN_FILE; + } + if( !opt.no_armor ) { if( use_armor_filter( fp ) ) { memset( &afx, 0, sizeof afx); @@ -74,14 +121,44 @@ verify_signatures( int nfiles, char **files ) } } - sl = NULL; - for(i=1 ; i < nfiles; i++ ) - add_to_strlist( &sl, files[i] ); - rc = proc_signature_packets( NULL, fp, sl, sigfile ); - free_strlist(sl); + rc = proc_signature_packets( NULL, fp, NULL, name ); iobuf_close(fp); + write_status( STATUS_FILE_DONE ); return rc; } +/**************** + * Verify each file given in the files array or read the names of the + * files from stdin. + * Note: This function can not handle detached signatures. + */ +int +verify_files( int nfiles, char **files ) +{ + int i; + if( !nfiles ) { /* read the filenames from stdin */ + char line[2048]; + unsigned int lno = 0; + + while( fgets(line, DIM(line), stdin) ) { + lno++; + if( !*line || line[strlen(line)-1] != '\n' ) { + log_error(_("input line %u too long or missing LF\n"), lno ); + return GPGERR_GENERAL; + } + /* This code does not work on MSDOS but how cares there are + * also no script languages available. We don't strip any + * spaces, so that we can process nearly all filenames */ + line[strlen(line)-1] = 0; + verify_one_file( line ); + } + + } + else { /* take filenames from the array */ + for(i=0; i < nfiles; i++ ) + verify_one_file( files[i] ); + } + return 0; +} diff --git a/include/ChangeLog b/include/ChangeLog index dc8e6fbc1..addfc24a2 100644 --- a/include/ChangeLog +++ b/include/ChangeLog @@ -1,3 +1,13 @@ +Fri Jul 14 19:38:23 CEST 2000 Werner Koch + + * http.h (HTTP_FLAG_TRY_PROXY): new. + + * error.h (G10ERR_NOT_PROCESSED): New. + + * iobuf.h (IOBUFCTRL_CANCEL): New. + + * types.h (HAVE_U64_TYPEDEF): Defined depending on configure test. + Thu Jan 27 18:00:44 CET 2000 Werner Koch * Changed all "g10_"/"GPG_" prefixes to "gpg_"/"GPG_". diff --git a/include/errors.h b/include/errors.h index cb60e7dae..9fbf9320e 100644 --- a/include/errors.h +++ b/include/errors.h @@ -1,5 +1,5 @@ -/* errors.h - erro code - * Copyright (C) 1998 Free Software Foundation, Inc. +/* errors.h - error codes fro GnuPG + * Copyright (C) 1998, 1999, 2000 Free Software Foundation, Inc. * * This file is part of GNUPG. * @@ -74,6 +74,7 @@ #define GPGERR_UNKNOWN_HOST 149 #define GPGERR_SELFTEST_FAILED 50 #define GPGERR_NOT_ENCRYPTED 151 +#define GPGERR_NOT_PROCESSED 152 #ifndef HAVE_STRERROR char *strerror( int n ); diff --git a/include/http.h b/include/http.h index e644a1b11..a1d70406f 100644 --- a/include/http.h +++ b/include/http.h @@ -49,6 +49,10 @@ typedef enum { HTTP_REQ_POST = 3 } HTTP_REQ_TYPE; +enum { /* put flag values into an enum, so that gdb can display them */ + HTTP_FLAG_TRY_PROXY = 1 +}; + struct http_context { int initialized; unsigned int status_code; @@ -60,7 +64,8 @@ struct http_context { PARSED_URI uri; HTTP_REQ_TYPE req_type; byte *buffer; /* line buffer */ - unsigned buffer_size; + unsigned int buffer_size; + unsigned int flags; }; typedef struct http_context *HTTP_HD; diff --git a/include/iobuf.h b/include/iobuf.h index cbbb74133..7a7d825e6 100644 --- a/include/iobuf.h +++ b/include/iobuf.h @@ -32,6 +32,7 @@ #define IOBUFCTRL_UNDERFLOW 3 #define IOBUFCTRL_FLUSH 4 #define IOBUFCTRL_DESC 5 +#define IOBUFCTRL_CANCEL 6 #define IOBUFCTRL_USER 16 typedef struct iobuf_struct *IOBUF; diff --git a/include/types.h b/include/types.h index 6e12c3406..baece0bca 100644 --- a/include/types.h +++ b/include/types.h @@ -83,6 +83,11 @@ #define HAVE_U32_TYPEDEF #endif +/**************** + * Warning: Some systems segfault when this u64 typedef and + * the dummy code in cipher/md.c is not available. Examples are + * Solaris and IRIX. + */ #ifndef HAVE_U64_TYPEDEF #undef u64 /* maybe there is a macro with this name */ #if SIZEOF_UNSIGNED_INT == 8 @@ -91,7 +96,7 @@ #elif SIZEOF_UNSIGNED_LONG == 8 typedef unsigned long u64; #define HAVE_U64_TYPEDEF - #elif __GNUC__ >= 2 || defined(__SUNPRO_C) + #elif SIZEOF_UNSIGNED_LONG_LONG == 8 typedef unsigned long long u64; #define HAVE_U64_TYPEDEF #endif diff --git a/include/util.h b/include/util.h index a95b42c7b..375204e78 100644 --- a/include/util.h +++ b/include/util.h @@ -40,6 +40,7 @@ void gpg_log_print_prefix(const char *text); void log_set_name( const char *name ); const char *log_get_name(void); void log_set_pid( int pid ); +void log_inc_errorcount(void); int log_get_errorcount( int clear ); void gpg_log_hexdump( const char *text, const char *buf, size_t len ); diff --git a/jnlib/ChangeLog b/jnlib/ChangeLog index a41901bf4..5bb8faf3b 100644 --- a/jnlib/ChangeLog +++ b/jnlib/ChangeLog @@ -1,3 +1,9 @@ +Fri Jul 14 19:38:23 CEST 2000 Werner Koch + + * argparse.c (default_strusage): Changed year of default copyright. + + * dotlock.c (disable_dotlock): New. + Mon Jan 24 13:04:28 CET 2000 Werner Koch * README: New. diff --git a/jnlib/argparse.c b/jnlib/argparse.c index 3f778053d..6293d3eb3 100644 --- a/jnlib/argparse.c +++ b/jnlib/argparse.c @@ -1,5 +1,5 @@ /* [argparse.c wk 17.06.97] Argument Parser for option handling - * Copyright (C) 1998,1999 Free Software Foundation, Inc. + * Copyright (C) 1998, 1999, 2000 Free Software Foundation, Inc. * This file is part of GnuPG. * * GnuPG is free software; you can redistribute it and/or modify @@ -899,7 +899,7 @@ strusage( int level ) switch( level ) { case 11: p = "foo"; break; case 13: p = "0.0"; break; - case 14: p = "Copyright (C) 1999 Free Software Foundation, Inc."; break; + case 14: p = "Copyright (C) 2000 Free Software Foundation, Inc."; break; case 15: p = "This program comes with ABSOLUTELY NO WARRANTY.\n" "This is free software, and you are welcome to redistribute it\n" diff --git a/jnlib/dotlock.c b/jnlib/dotlock.c index 8e61f7a03..29ab65dff 100644 --- a/jnlib/dotlock.c +++ b/jnlib/dotlock.c @@ -1,5 +1,5 @@ /* dotlock.c - dotfile locking - * Copyright (C) 1998,2000 Free Software Foundation, Inc. + * Copyright (C) 1998, 2000 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -42,14 +42,22 @@ struct dotlock_handle { char *tname; /* name of lockfile template */ char *lockname; /* name of the real lockfile */ int locked; /* lock status */ + int disable; /* locking */ }; static DOTLOCK all_lockfiles; +static int never_lock; static int read_lockfile( const char *name ); static void remove_lockfiles(void); +void +disable_dotlock(void) +{ + never_lock = 1; +} + /**************** * Create a lockfile with the given name and return an object of * type DOTLOCK which may be used later to actually do the lock. @@ -88,6 +96,16 @@ create_dotlock( const char *file_to_lock ) return NULL; h = jnlib_xcalloc( 1, sizeof *h ); + if( never_lock ) { + h->disable = 1; + #ifdef _REENTRANT + /* fixme: aquire mutex on all_lockfiles */ + #endif + h->next = all_lockfiles; + all_lockfiles = h; + return h; + } + #ifndef HAVE_DOSISH_SYSTEM sprintf( pidstr, "%10d\n", (int)getpid() ); /* fixme: add the hostname to the second line (FQDN or IP addr?) */ @@ -191,6 +209,10 @@ make_dotlock( DOTLOCK h, long timeout ) const char *maybe_dead=""; int backoff=0; + if( h->disable ) { + return 0; + } + if( h->locked ) { log_debug("oops, `%s' is already locked\n", h->lockname ); return 0; @@ -259,6 +281,10 @@ release_dotlock( DOTLOCK h ) #else int pid; + if( h->disable ) { + return 0; + } + if( !h->locked ) { log_debug("oops, `%s' is not locked\n", h->lockname ); return 0; @@ -333,11 +359,13 @@ remove_lockfiles() while( h ) { h2 = h->next; - if( h->locked ) - unlink( h->lockname ); - unlink(h->tname); - jnlib_free(h->tname); - jnlib_free(h->lockname); + if( !h->disable ) { + if( h->locked ) + unlink( h->lockname ); + unlink(h->tname); + jnlib_free(h->tname); + jnlib_free(h->lockname); + } jnlib_free(h); h = h2; } diff --git a/jnlib/dotlock.h b/jnlib/dotlock.h index d54219e23..74ac876fa 100644 --- a/jnlib/dotlock.h +++ b/jnlib/dotlock.h @@ -24,6 +24,7 @@ struct dotlock_handle; typedef struct dotlock_handle *DOTLOCK; +void disable_dotlock(void); DOTLOCK create_dotlock( const char *file_to_lock ); int make_dotlock( DOTLOCK h, long timeout ); int release_dotlock( DOTLOCK h ); diff --git a/util/ChangeLog b/util/ChangeLog index b42bc37e5..d1eba0291 100644 --- a/util/ChangeLog +++ b/util/ChangeLog @@ -1,3 +1,28 @@ +Fri Jul 14 19:38:23 CEST 2000 Werner Koch + + * iobuf.c (iobuf_cancel): Broadcast the new Cancel message to all + filters. Fix for MSDOS. + + * miscutil.c (asctimestamp): Fix for possible buffer overflow by + a large system returned date format string. + + * logger.c (log_inc_errorcount): New. + + * w32reg.c: New. + + * simple-gettext.c: Use the Registry to locate the mo file. + + * http.c (send_request): Add support for proxys; suggested by + Walter Hofmann. + (http_open_document): Pass flags to http_open. + + * ttyio.c (do_get): Replaced #if __MINGW32__ by #ifdef because + gcc 2.95.1 assigns a floating point value (0.2) to this macro, + which in turn can't be used in an expression. + * ttyio.c: Simulate termios with termios. By Dave Dykstra. + * ttyio.c (tty_print_utf8_string): Oops. + * ttyio.c (tty_print_utf8_string2): New to allow a max output size. + Thu Jan 27 18:00:44 CET 2000 Werner Koch * Changed all "g10_"/"GPG_" prefixes to "gpg_"/"GPG_". diff --git a/util/Makefile.am b/util/Makefile.am index 7668de61e..6247f1f2e 100644 --- a/util/Makefile.am +++ b/util/Makefile.am @@ -8,7 +8,7 @@ noinst_LTLIBRARIES = libutil.la libutil_la_LDFLAGS = libutil_la_SOURCES = logger.c fileutil.c miscutil.c strgutil.c \ ttyio.c errors.c iobuf.c \ - http.c simple-gettext.c + http.c simple-gettext.c w32reg.c http-test: http.c diff --git a/util/http.c b/util/http.c index 173c17116..81bf91ded 100644 --- a/util/http.c +++ b/util/http.c @@ -1,5 +1,5 @@ /* http.c - HTTP protocol handler - * Copyright (C) 1999 Free Software Foundation, Inc. + * Copyright (C) 1999, 2000 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -26,7 +26,7 @@ #include #include -#ifndef HAVE_DOSISH_SYSTEM +#ifndef HAVE_DOSISH_SYSTEM /* fixme: add support W32 sockets */ #include #include @@ -76,7 +76,7 @@ http_open( HTTP_HD hd, HTTP_REQ_TYPE reqtype, const char *url, { int rc; - if( flags || !(reqtype == HTTP_REQ_GET || reqtype == HTTP_REQ_POST) ) + if( !(reqtype == HTTP_REQ_GET || reqtype == HTTP_REQ_POST) ) return GPGERR_INV_ARG; /* initialize the handle */ @@ -84,6 +84,7 @@ http_open( HTTP_HD hd, HTTP_REQ_TYPE reqtype, const char *url, hd->sock = -1; hd->initialized = 1; hd->req_type = reqtype; + hd->flags = flags; rc = parse_uri( &hd->uri, url ); if( !rc ) { @@ -150,10 +151,7 @@ http_open_document( HTTP_HD hd, const char *document, unsigned int flags ) { int rc; - if( flags ) - return GPGERR_INV_ARG; - - rc = http_open( hd, HTTP_REQ_GET, document, 0 ); + rc = http_open( hd, HTTP_REQ_GET, document, flags ); if( rc ) return rc; @@ -429,21 +427,47 @@ send_request( HTTP_HD hd ) byte *request, *p; ushort port; int rc; + const char *http_proxy = NULL; server = *hd->uri->host? hd->uri->host : "localhost"; port = hd->uri->port? hd->uri->port : 80; - hd->sock = connect_server( server, port ); + if( (hd->flags & HTTP_FLAG_TRY_PROXY) + && (http_proxy = getenv( "http_proxy" )) ) { + PARSED_URI uri; + + rc = parse_uri( &uri, http_proxy ); + if (rc) { + log_error("invalid $http_proxy: %s\n", gpg_errstr(rc)); + release_parsed_uri( uri ); + return GPGERR_NETWORK; + } + hd->sock = connect_server( *uri->host? uri->host : "localhost", + uri->port? uri->port : 80 ); + release_parsed_uri( uri ); + } + else + hd->sock = connect_server( server, port ); + if( hd->sock == -1 ) return GPGERR_NETWORK; p = build_rel_path( hd->uri ); request = gcry_xmalloc( strlen(p) + 20 ); - sprintf( request, "%s %s%s HTTP/1.0\r\n", + if( http_proxy ) { + sprintf( request, "%s http://%s:%hu%s%s HTTP/1.0\r\n", + hd->req_type == HTTP_REQ_GET ? "GET" : + hd->req_type == HTTP_REQ_HEAD? "HEAD": + hd->req_type == HTTP_REQ_POST? "POST": "OOPS", + server, port, *p == '/'? "":"/", p ); + } + else { + sprintf( request, "%s %s%s HTTP/1.0\r\n", hd->req_type == HTTP_REQ_GET ? "GET" : hd->req_type == HTTP_REQ_HEAD? "HEAD": hd->req_type == HTTP_REQ_POST? "POST": "OOPS", *p == '/'? "":"/", p ); + } gcry_free(p); rc = write_server( hd->sock, request, strlen(request) ); diff --git a/util/iobuf.c b/util/iobuf.c index ec5cfefbd..d4900f416 100644 --- a/util/iobuf.c +++ b/util/iobuf.c @@ -1,5 +1,5 @@ /* iobuf.c - file handling - * Copyright (C) 1998, 1999 Free Software Foundation, Inc. + * Copyright (C) 1998, 1999, 2000 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -81,6 +81,8 @@ static int underflow(IOBUF a); * IOBUFCTRL_FLUSH: called by iobuf_flush() to write out the collected stuff. * *RET_LAN is the number of bytes in BUF. * + * IOBUFCTRL_CANCEL: send to all filters on behalf of iobuf_cancel. The + * filter may take appropriate action on this message. */ static int file_filter(void *opaque, int control, IOBUF chain, byte *buf, size_t *ret_len) @@ -494,19 +496,47 @@ iobuf_close( IOBUF a ) return rc; } + int iobuf_cancel( IOBUF a ) { const char *s; + IOBUF a2; + int rc; + #ifdef HAVE_DOSISH_SYSTEM + char *remove_name = NULL; + #endif if( a && a->use == 2 ) { s = iobuf_get_real_fname(a); - if( s && *s ) - remove(s); /* remove the file. Fixme: this will fail for MSDOZE*/ - } /* because the file is still open */ - return iobuf_close(a); -} + if( s && *s ) { + #ifdef HAVE_DOSISH_SYSTEM + remove_name = m_strdup ( s ); + #else + remove(s); + #endif + } + } + /* send a cancel message to all filters */ + for( a2 = a; a2 ; a2 = a2->chain ) { + size_t dummy; + if( a2->filter ) + a2->filter( a2->filter_ov, IOBUFCTRL_CANCEL, a2->chain, + NULL, &dummy ); + } + + rc = iobuf_close(a); + #ifdef HAVE_DOSISH_SYSTEM + if ( remove_name ) { + /* Argg, MSDOS does not allow to remove open files. So + * we have to do it here */ + remove ( remove_name ); + m_free ( remove_name ); + } + #endif + return rc; +} /**************** * create a temporary iobuf, which can be used to collect stuff diff --git a/util/logger.c b/util/logger.c index 83cba8c22..bb0b89e8b 100644 --- a/util/logger.c +++ b/util/logger.c @@ -1,5 +1,5 @@ /* logger.c - log functions - * Copyright (C) 1998 Free Software Foundation, Inc. + * Copyright (C) 1998, 2000 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -102,6 +102,12 @@ log_get_errorcount( int clear) return n; } +void +log_inc_errorcount() +{ + errorcount++; +} + void gpg_log_print_prefix(const char *text) diff --git a/util/miscutil.c b/util/miscutil.c index 2cf0db0e2..c1b8fa076 100644 --- a/util/miscutil.c +++ b/util/miscutil.c @@ -1,5 +1,5 @@ /* miscutil.c - miscellaneous utilities - * Copyright (C) 1998, 1999 Free Software Foundation, Inc. + * Copyright (C) 1998, 1999, 2000 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -148,7 +148,7 @@ asctimestamp( u32 stamp ) tp = localtime( &atime ); #ifdef HAVE_STRFTIME #if defined(HAVE_NL_LANGINFO) - mem2str( fmt, nl_langinfo(D_T_FMT), DIM(fmt) ); + mem2str( fmt, nl_langinfo(D_T_FMT), DIM(fmt)-3 ); if( strstr( fmt, "%Z" ) == NULL ) strcat( fmt, " %Z"); strftime( buffer, DIM(buffer)-1, fmt, tp ); diff --git a/util/simple-gettext.c b/util/simple-gettext.c index a128aff3c..80dbbfe0e 100644 --- a/util/simple-gettext.c +++ b/util/simple-gettext.c @@ -217,13 +217,6 @@ load_domain( const char *filename ) free( domain ); return NULL; } - /* Currently we have only a Latin-1 to IBM850 translation, so - * we simply mark everything mapped if we don't have this codepage. */ - { - const char *s = get_native_charset(); - if( !s || strcmp(s, "ibm850") - memset( domain->mapped, 1, domain->nstrings ); - } return domain; } @@ -251,16 +244,19 @@ set_gettext_file( const char *filename ) /* absolute path - use it as is */ domain = load_domain( filename ); } - else { /* relative path - append ".mo" and get DIR from env */ + else { /* relative path - append ".mo" and get DIR from the Registry */ char *buf = NULL; - const char *s; + char *dir; - s = getenv("MINGW32_NLS_DIR"); - if( s && (buf=malloc(strlen(s)+strlen(filename)+1+3+1)) ) { - strcpy(stpcpy(stpcpy(stpcpy( buf, s),"/"), filename),".mo"); + dir = read_w32_registry_string( NULL, + "Control Panel\\Mingw32\\NLS", + "MODir" ); + if( dir && (buf=malloc(strlen(dir)+strlen(filename)+1+3+1)) ) { + strcpy(stpcpy(stpcpy(stpcpy( buf, dir),"/"), filename),".mo"); domain = load_domain( buf ); free(buf); } + free(dir); } if( !domain ) return -1; @@ -286,7 +282,7 @@ get_string( struct loaded_domain *domain, u32 idx ) byte *pp; domain->mapped[idx] = 1; - /* currently we only support Latin-1 to CP 850 */ + /* we assume Latin1 -> CP 850 for now */ for( pp=p; *pp; pp++ ) { if( (*pp & 0x80) ) { switch( *pp ) { diff --git a/util/strgutil.c b/util/strgutil.c index ea3ddc29c..a8abf7ba8 100644 --- a/util/strgutil.c +++ b/util/strgutil.c @@ -1,5 +1,5 @@ /* strgutil.c - string utilities - * Copyright (C) 1998 Free Software Foundation, Inc. + * Copyright (C) 1998, 2000 Free Software Foundation, Inc. * * This file is part of GnuPG. * diff --git a/util/ttyio.c b/util/ttyio.c index 527d64832..6aaff000c 100644 --- a/util/ttyio.c +++ b/util/ttyio.c @@ -1,5 +1,5 @@ /* ttyio.c - tty i/O functions - * Copyright (C) 1998 Free Software Foundation, Inc. + * Copyright (C) 1998, 2000 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -26,6 +26,16 @@ #include #ifdef HAVE_TCGETATTR #include +#else + #ifdef HAVE_TERMIO_H + /* simulate termios with termio */ + #include + #define termios termio + #define tcsetattr ioctl + #define TCSAFLUSH TCSETAF + #define tcgetattr(A,B) ioctl(A,TCGETA,B) + #define HAVE_TCGETATTR + #endif #endif #ifdef __MINGW32__ /* use the odd Win32 functions */ #include @@ -237,7 +247,7 @@ tty_print_string( byte *p, size_t n ) } void -tty_print_utf8_string( byte *p, size_t n ) +tty_print_utf8_string2( byte *p, size_t n, size_t max_n ) { size_t i; char *buf; @@ -252,14 +262,28 @@ tty_print_utf8_string( byte *p, size_t n ) } if( i < n ) { buf = utf8_to_native( p, n ); + if( strlen( buf ) > max_n ) { + buf[max_n] = 0; + } + /*(utf8 conversion already does the control character quoting)*/ tty_printf("%s", buf ); gcry_free( buf ); } - else + else { + if( n > max_n ) { + n = max_n; + } tty_print_string( p, n ); + } } +void +tty_print_utf8_string( byte *p, size_t n ) +{ + tty_print_utf8_string2( p, n, n ); +} +