diff options
author | Werner Koch <wk@gnupg.org> | 2000-09-18 16:35:34 +0200 |
---|---|---|
committer | Werner Koch <wk@gnupg.org> | 2000-09-18 16:35:34 +0200 |
commit | 986d928ce2a561b04fda7730de6a94f9b1e703d6 (patch) | |
tree | 9d815bcf427ee76f678153f4b000d8843816a2bd | |
parent | See ChangeLog: Tue Aug 22 14:31:15 CEST 2000 Werner Koch (diff) | |
download | gnupg2-986d928ce2a561b04fda7730de6a94f9b1e703d6.tar.xz gnupg2-986d928ce2a561b04fda7730de6a94f9b1e703d6.zip |
See ChangeLog: Mon Sep 18 16:35:45 CEST 2000 Werner Koch
46 files changed, 1755 insertions, 827 deletions
@@ -1,3 +1,20 @@ +Mon Sep 18 16:35:45 CEST 2000 Werner Koch <wk@openit.de> + + * acinclude.m4 (GNUPG_CHECK_MLOCK): Removed that silly mkdir(). + + * configure.in: Changes to allow for Solaris random device. + By Nils Ellmenreich. + (--with-egd-socket): New. + + * configure.in (GNUPG_HOMEDIR): New. + + * configure.in: Check for fstat64 and fopen64 + + * acinclude.m4 (GNUPG_CHECK_FAQPROG): New. + * configure.in: Test for this. + + * configure.in (DYNLINK_MOD_CFLAGS): Fix by David Champion. + Tue Aug 22 14:31:15 CEST 2000 Werner Koch <wk@openit.de> Version 1.1.1 @@ -1,8 +1,38 @@ -Noteworthy changes in version 1.1.1 ------------------------------------ +Noteworthy changes in the current CVS HEAD +------------------------------------------ THIS IS A DEVELOPMENT VERSION; see README-alpha + * Fixed problems with piping to/from other MS-Windows software + + * Expiration time of the primary key can be changed again. + + * Revoked user IDs are now marked in the output of --list-key + + * New options --merge-only and --try-all-secrets. + + * New configuration option --with-egd-socket. + + * The --trusted-key option is back after it left us with 0.9.5 + + * RSA is supported. Key generation does not yet work but will come + soon. + + * CAST5 and SHA-1 are now the default algorithms to protect the key + and for symmetric-only encryption. This should solve a couple + of compatibility problems because the old algorithms are optional + according to RFC2440 + + * Twofish and MDC enhanced encryption is now used. PGP 7 supports + this. Older versions of GnuPG don't support it, so they should be + upgraded to at least 1.0.2 + + + + +Noteworthy changes in version 1.1.1 +----------------------------------- + * Add gpg-agent. * Removed option --emulate-checksum-bug @@ -1 +1,2 @@ -1.1.1
\ No newline at end of file +1.1.1a + diff --git a/acconfig.h b/acconfig.h index a2c91283f..3c1d32e25 100644 --- a/acconfig.h +++ b/acconfig.h @@ -74,6 +74,8 @@ #undef NAME_OF_DEV_URANDOM /* Linux has an ioctl */ #undef HAVE_DEV_RANDOM_IOCTL +/* see cipher/rndegd.c */ +#undef EGD_SOCKET_NAME #undef USE_DYNAMIC_LINKING diff --git a/acinclude.m4 b/acinclude.m4 index 9f8bfd01d..7291c1dff 100644 --- a/acinclude.m4 +++ b/acinclude.m4 @@ -67,6 +67,32 @@ AC_DEFUN(GNUPG_CHECK_GNUMAKE, fi ]) +dnl GNUPG_CHECK_FAQPROG +dnl +AC_DEFUN(GNUPG_CHECK_FAQPROG, + [ AC_MSG_CHECKING(for faqprog.pl) + if faqprog.pl -V 2>/dev/null | grep '^faqprog.pl ' >/dev/null 2>&1; then + working_faqprog=yes + FAQPROG="faqprog.pl" + else + working_faqprog=no + FAQPROG=": " + fi + AC_MSG_RESULT($working_faqprog) + AC_SUBST(FAQPROG) + AM_CONDITIONAL(WORKING_FAQPROG, test "$working_faqprog" = "yes" ) + + if test $working_faqprog = no; then + AC_MSG_WARN([[ +*** +*** It seems that the faqprog.pl program is not installed. +*** Unless you do not change the source of the FAQs it is not required. +*** The working version of this utility should be available at: +*** ftp://ftp.gnupg.org/pub/gcrypt/contrib/faqprog.pl +***]]) + fi + ]) + dnl GNUPG_LINK_FILES( SRC, DEST ) @@ -358,7 +384,7 @@ define(GNUPG_CHECK_MLOCK, #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 diff --git a/cipher/ChangeLog b/cipher/ChangeLog index e37e3c351..2e4ebe53e 100644 --- a/cipher/ChangeLog +++ b/cipher/ChangeLog @@ -1,3 +1,21 @@ +Mon Sep 18 16:35:45 CEST 2000 Werner Koch <wk@openit.de> + + * rndlinux.c (open_device): Loose random device checking. + By Nils Ellmenreich. + + * random.c (fast_random_poll): Check ENOSYS for getrusage. + * rndunix.c: Add 2 sources for QNX. By Sam Roberts. + + * pubkey.c (gcry_pk_algo_info): Add GCRYCTL_GET_ALGO_USAGE. + + * rsa.c: Changed the comment about the patent. + (secret): Speed up by using the CRT. For a 2k keys this + is about 3 times faster. + (stronger_key_check): New but unused code to check the secret key. + * Makefile.am: Included rsa.[ch]. + * pubkey.c: Enabled RSA support. + (pubkey_get_npkey): Removed RSA workaround. + Mon Jul 31 10:04:47 CEST 2000 Werner Koch <wk@openit.de> * pubkey.c: Replaced all gcry_sexp_{car,cdr}_{data,mpi} by the new diff --git a/cipher/Makefile.am b/cipher/Makefile.am index 2ed370f05..26de92520 100644 --- a/cipher/Makefile.am +++ b/cipher/Makefile.am @@ -50,6 +50,7 @@ libcipher_la_SOURCES = cipher.c \ rmd.h \ dsa.h \ dsa.c \ + rsa.c rsa.h \ smallprime.c \ construct.c diff --git a/cipher/pubkey.c b/cipher/pubkey.c index bbf592bb4..3dab336d7 100644 --- a/cipher/pubkey.c +++ b/cipher/pubkey.c @@ -30,9 +30,7 @@ #include "cipher.h" #include "elgamal.h" #include "dsa.h" -#if 0 #include "rsa.h" -#endif #include "dynload.h" /* FIXME: use set_lasterr() */ @@ -196,7 +194,6 @@ 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, @@ -248,7 +245,6 @@ setup_pubkey_table(void) if( !pubkey_table[i].name ) BUG(); i++; - #endif for( ; i < TABLE_SIZE; i++ ) pubkey_table[i].name = NULL; @@ -433,8 +429,6 @@ pubkey_get_npkey( int algo ) if( pubkey_table[i].algo == algo ) return pubkey_table[i].npkey; } while( load_pubkey_modules() ); - if( is_RSA(algo) ) /* special hack, so that we are able to */ - return 2; /* see the RSA keyids */ return 0; } @@ -450,8 +444,6 @@ pubkey_get_nskey( int algo ) if( pubkey_table[i].algo == algo ) return pubkey_table[i].nskey; } while( load_pubkey_modules() ); - if( is_RSA(algo) ) /* special hack, so that we are able to */ - return 6; /* see the RSA keyids */ return 0; } @@ -467,8 +459,6 @@ pubkey_get_nsig( int algo ) if( pubkey_table[i].algo == algo ) return pubkey_table[i].nsig; } while( load_pubkey_modules() ); - if( is_RSA(algo) ) /* special hack, so that we are able to */ - return 1; /* see the RSA keyids */ return 0; } @@ -484,8 +474,6 @@ pubkey_get_nenc( int algo ) if( pubkey_table[i].algo == algo ) return pubkey_table[i].nenc; } while( load_pubkey_modules() ); - if( is_RSA(algo) ) /* special hack, so that we are able to */ - return 1; /* see the RSA keyids */ return 0; } @@ -1509,6 +1497,11 @@ gcry_pk_ctl( int cmd, void *buffer, size_t buflen) * Buffer must be NULL, nbytes may have the address of a variable * with the required usage of the algorithm. It may be 0 for don't * care or a combination of the GCRY_PK_USAGE_xxx flags; + * GCRYCTL_GET_ALGO_USAGE: + * Return the usage glafs for the give algo. An invalid alog + * does return 0. Disabled algos are ignored here becuase we + * only want to know whether the algo is at all capable of + * the usage. * * On error the value -1 is returned and the error reason may be * retrieved by gcry_errno(). @@ -1535,6 +1528,15 @@ gcry_pk_algo_info( int algo, int what, void *buffer, size_t *nbytes) } break; + case GCRYCTL_GET_ALGO_USAGE: + do { + int i; + for(i=0; pubkey_table[i].name; i++ ) + if( pubkey_table[i].algo == algo ) + return pubkey_table[i].use; + } while( load_pubkey_modules() ); + return 0; + case GCRYCTL_GET_ALGO_NPKEY: return pubkey_get_npkey( algo ); case GCRYCTL_GET_ALGO_NSKEY: return pubkey_get_nskey( algo ); case GCRYCTL_GET_ALGO_NSIGN: return pubkey_get_nsig( algo ); diff --git a/cipher/random.c b/cipher/random.c index 0258f5dd0..6f31b5625 100644 --- a/cipher/random.c +++ b/cipher/random.c @@ -604,7 +604,9 @@ fast_random_poll() #endif #else { struct rusage buf; - if( getrusage( RUSAGE_SELF, &buf ) ) + /* QNX/Neutrino does return ENOSYS - so we just ignore it and + * add whatever is in buf */ + if( getrusage( RUSAGE_SELF, &buf ) && errno != ENOSYS ) BUG(); add_randomness( &buf, sizeof buf, 1 ); memset( &buf, 0, sizeof buf ); diff --git a/cipher/rndlinux.c b/cipher/rndlinux.c index bca596fd1..c23269644 100644 --- a/cipher/rndlinux.c +++ b/cipher/rndlinux.c @@ -63,7 +63,7 @@ get_entropy_count( int fd ) #endif /**************** - * Used to open the Linux and xBSD /dev/random devices + * Used to open the /dev/random devices (Linux, xBSD, Solaris (if it exists), ...) */ static int open_device( const char *name, int minor ) @@ -76,8 +76,9 @@ open_device( const char *name, int minor ) g10_log_fatal("can't open %s: %s\n", name, strerror(errno) ); if( fstat( fd, &sb ) ) g10_log_fatal("stat() off %s failed: %s\n", name, strerror(errno) ); - if( !S_ISCHR(sb.st_mode) ) - g10_log_fatal("invalid random device!\n" ); + /* Don't check device type for better portability */ + /* if( (!S_ISCHR(sb.st_mode)) && (!S_ISFIFO(sb.st_mode)) ) + g10_log_fatal("invalid random device!\n" ); */ return fd; } diff --git a/cipher/rndunix.c b/cipher/rndunix.c index 99a416ea7..6c8e680b2 100644 --- a/cipher/rndunix.c +++ b/cipher/rndunix.c @@ -244,6 +244,7 @@ static struct RI { { "/usr/ucb/ps", "aux", SC(0.3), NULL, 0, 0, 0, 1 }, { "/usr/bin/ps", "aux", SC(0.3), NULL, 0, 0, 0, 1 }, { "/bin/ps", "aux", SC(0.3), NULL, 0, 0, 0, 0 }, + { "/bin/ps", "-A", SC(0.3), NULL, 0, 0, 0, 0 }, /*QNX*/ { "/usr/bin/ipcs", "-a", SC(0.5), NULL, 0, 0, 0, 1 }, { "/bin/ipcs", "-a", SC(0.5), NULL, 0, 0, 0, 0 }, /* Unreliable source, depends on system usage */ @@ -292,6 +293,10 @@ static struct RI { /* This is a complex and screwball program. Some systems have things * like rX_dmn, x = integer, for RAID systems, but the statistics are * pretty dodgy */ +#ifdef __QNXNTO__ + { "/bin/pidin", "-F%A%B%c%d%E%I%J%K%m%M%n%N%p%P%S%s%T", SC(0.3), + NULL, 0, 0, 0, 0 }, +#endif #if 0 /* The following aren't enabled since they're somewhat slow and not very * unpredictable, however they give an indication of the sort of sources diff --git a/cipher/rsa.c b/cipher/rsa.c index 5d852cd88..2bb451002 100644 --- a/cipher/rsa.c +++ b/cipher/rsa.c @@ -1,10 +1,6 @@ /* 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. * @@ -23,11 +19,16 @@ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA */ +/* This code uses an algorithm protected by U.S. Patent #4,405,829 + which expires on September 20, 2000. The patent holder placed that + patent into the public domain on Sep 6th, 2000. +*/ + #include <config.h> #include <stdio.h> #include <stdlib.h> #include <string.h> -#include "util.h" +#include "g10lib.h" #include "mpi.h" #include "cipher.h" #include "rsa.h" @@ -68,7 +69,7 @@ test_keys( RSA_secret_key *sk, unsigned nbits ) pk.e = sk->e; { char *p = get_random_bits( nbits, 0, 0 ); mpi_set_buffer( test, p, (nbits+7)/8, 0 ); - m_free(p); + g10_free(p); } public( out1, test, &pk ); @@ -200,22 +201,111 @@ public(MPI output, MPI input, RSA_public_key *pkey ) mpi_powm( output, input, pkey->e, pkey->n ); } +#if 0 +static void +stronger_key_check ( RSA_secret_key *skey ) +{ + MPI t = mpi_alloc_secure ( 0 ); + MPI t1 = mpi_alloc_secure ( 0 ); + MPI t2 = mpi_alloc_secure ( 0 ); + MPI phi = mpi_alloc_secure ( 0 ); + + /* check that n == p * q */ + mpi_mul( t, skey->p, skey->q); + if (mpi_cmp( t, skey->n) ) + log_info ( "RSA Oops: n != p * q\n" ); + + /* check that p is less than q */ + if( mpi_cmp( skey->p, skey->q ) > 0 ) + log_info ("RSA Oops: p >= q\n"); + + + /* check that e divides neither p-1 nor q-1 */ + mpi_sub_ui(t, skey->p, 1 ); + mpi_fdiv_r(t, t, skey->e ); + if ( !mpi_cmp_ui( t, 0) ) + log_info ( "RSA Oops: e divides p-1\n" ); + mpi_sub_ui(t, skey->q, 1 ); + mpi_fdiv_r(t, t, skey->e ); + if ( !mpi_cmp_ui( t, 0) ) + log_info ( "RSA Oops: e divides q-1\n" ); + + /* check that d is correct */ + mpi_sub_ui( t1, skey->p, 1 ); + mpi_sub_ui( t2, skey->q, 1 ); + mpi_mul( phi, t1, t2 ); + mpi_gcd(t, t1, t2); + mpi_fdiv_q(t, phi, t); + mpi_invm(t, skey->e, t ); + if ( mpi_cmp(t, skey->d ) ) + log_info ( "RSA Oops: d is wrong\n"); + + /* check for crrectness of u */ + mpi_invm(t, skey->p, skey->q ); + if ( mpi_cmp(t, skey->u ) ) + log_info ( "RSA Oops: u is wrong\n"); + + log_info ( "RSA secret key check finished\n"); + + mpi_free (t); + mpi_free (t1); + mpi_free (t2); + mpi_free (phi); +} +#endif + + + /**************** * 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. + * Or faster: * - * FIXME: We should better use the Chinese Remainder Theorem + * m1 = c ^ (d mod (p-1)) mod p + * m2 = c ^ (d mod (q-1)) mod q + * h = u * (m2 - m1) mod q + * m = m1 + h * p + * + * Where m is OUTPUT, c is INPUT and d,n,p,q,u are elements of SKEY. */ static void secret(MPI output, MPI input, RSA_secret_key *skey ) { + #if 0 mpi_powm( output, input, skey->d, skey->n ); + #else + MPI m1 = mpi_alloc_secure( mpi_get_nlimbs(skey->n)+1 ); + MPI m2 = mpi_alloc_secure( mpi_get_nlimbs(skey->n)+1 ); + MPI h = mpi_alloc_secure( mpi_get_nlimbs(skey->n)+1 ); + + /* m1 = c ^ (d mod (p-1)) mod p */ + mpi_sub_ui( h, skey->p, 1 ); + mpi_fdiv_r( h, skey->d, h ); + mpi_powm( m1, input, h, skey->p ); + /* m2 = c ^ (d mod (q-1)) mod q */ + mpi_sub_ui( h, skey->q, 1 ); + mpi_fdiv_r( h, skey->d, h ); + mpi_powm( m2, input, h, skey->q ); + /* h = u * ( m2 - m1 ) mod q */ + mpi_sub( h, m2, m1 ); + if ( mpi_is_neg( h ) ) + mpi_add ( h, h, skey->q ); + mpi_mulm( h, skey->u, h, skey->q ); + /* m = m2 + h * p */ + mpi_mul ( h, h, skey->p ); + mpi_add ( output, m1, h ); + /* ready */ + + mpi_free ( h ); + mpi_free ( m1 ); + mpi_free ( m2 ); + #endif } + /********************************************* ************** interface ****************** *********************************************/ @@ -226,7 +316,7 @@ rsa_generate( int algo, unsigned nbits, MPI *skey, MPI **retfactors ) RSA_secret_key sk; if( !is_RSA(algo) ) - return G10ERR_PUBKEY_ALGO; + return GCRYERR_INV_PK_ALGO; generate( &sk, nbits ); skey[0] = sk.n; @@ -236,7 +326,7 @@ rsa_generate( int algo, unsigned nbits, MPI *skey, MPI **retfactors ) skey[4] = sk.q; skey[5] = sk.u; /* make an empty list of factors */ - *retfactors = m_alloc_clear( 1 * sizeof **retfactors ); + *retfactors = g10_xcalloc( 1, sizeof **retfactors ); return 0; } @@ -247,7 +337,7 @@ rsa_check_secret_key( int algo, MPI *skey ) RSA_secret_key sk; if( !is_RSA(algo) ) - return G10ERR_PUBKEY_ALGO; + return GCRYERR_INV_PK_ALGO; sk.n = skey[0]; sk.e = skey[1]; @@ -256,7 +346,7 @@ rsa_check_secret_key( int algo, MPI *skey ) sk.q = skey[4]; sk.u = skey[5]; if( !check_secret_key( &sk ) ) - return G10ERR_BAD_SECKEY; + return GCRYERR_INV_PK_ALGO; return 0; } @@ -269,7 +359,7 @@ rsa_encrypt( int algo, MPI *resarr, MPI data, MPI *pkey ) RSA_public_key pk; if( algo != 1 && algo != 2 ) - return G10ERR_PUBKEY_ALGO; + return GCRYERR_INV_PK_ALGO; pk.n = pkey[0]; pk.e = pkey[1]; @@ -284,7 +374,7 @@ rsa_decrypt( int algo, MPI *result, MPI *data, MPI *skey ) RSA_secret_key sk; if( algo != 1 && algo != 2 ) - return G10ERR_PUBKEY_ALGO; + return GCRYERR_INV_PK_ALGO; sk.n = skey[0]; sk.e = skey[1]; @@ -303,7 +393,7 @@ rsa_sign( int algo, MPI *resarr, MPI data, MPI *skey ) RSA_secret_key sk; if( algo != 1 && algo != 3 ) - return G10ERR_PUBKEY_ALGO; + return GCRYERR_INV_PK_ALGO; sk.n = skey[0]; sk.e = skey[1]; @@ -326,13 +416,13 @@ rsa_verify( int algo, MPI hash, MPI *data, MPI *pkey, int rc; if( algo != 1 && algo != 3 ) - return G10ERR_PUBKEY_ALGO; + return GCRYERR_INV_PK_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; + rc = mpi_cmp( result, hash )? GCRYERR_BAD_SIGNATURE:0; mpi_free(result); return rc; @@ -366,10 +456,16 @@ rsa_get_info( int algo, *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"; + case 1: *usage = GCRY_PK_USAGE_SIGN | GCRY_PK_USAGE_ENCR; return "RSA"; + case 2: *usage = GCRY_PK_USAGE_ENCR; return "RSA-E"; + case 3: *usage = GCRY_PK_USAGE_SIGN; return "RSA-S"; default:*usage = 0; return NULL; } } + + + + + + diff --git a/configure.in b/configure.in index 65b6c8471..38d2c94db 100644 --- a/configure.in +++ b/configure.in @@ -48,6 +48,11 @@ case "$use_static_rnd" in ;; esac +AC_ARG_WITH(egd-socket, + [ --with-egd-socket=NAME Use NAME for the EGD socket)], + egd_socket_name="$withval", egd_socket_name="" ) +AC_DEFINE_UNQUOTED(EGD_SOCKET_NAME, "$egd_socket_name") + dnl @@ -164,6 +169,7 @@ AC_ISC_POSIX AC_PROG_INSTALL AC_PROG_AWK GPH_PROG_DOCBOOK +GNUPG_CHECK_FAQPROG dnl @@ -271,19 +277,30 @@ case "${target}" in *-openbsd*) NAME_OF_DEV_RANDOM="/dev/srandom" NAME_OF_DEV_URANDOM="/dev/urandom" - DYNLINK_MOD_CFLAGS="-shared -rdynamic -fpic -Wl,-Bshareable -Wl,-x" + DYNLINK_MOD_CFLAGS="-shared -rdynamic $CFLAGS_PIC -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" + DYNLINK_MOD_CFLAGS="-shared -rdynamic $CFLAGS_PIC -Wl,-Bshareable -Wl,-x" + ;; + + *-solaris*) + NAME_OF_DEV_RANDOM="/dev/random" + NAME_OF_DEV_URANDOM="/dev/random" + DYNLINK_MOD_CFLAGS="-shared $CFLAGS_PIC" ;; *) NAME_OF_DEV_RANDOM="/dev/random" NAME_OF_DEV_URANDOM="/dev/urandom" - DYNLINK_MOD_CFLAGS="-shared $CFLAGS_PIC" + # -shared is a gcc-ism. Find pic flags from GNUPG_CHECK_PIC. + if test -n "$GCC" ; then + DYNLINK_MOD_CFLAGS="-shared $CFLAGS_PIC" + else + DYNLINK_MOD_CFLAGS="$CFLAGS_PIC" + fi ;; esac AC_DEFINE_UNQUOTED(NAME_OF_DEV_RANDOM, "$NAME_OF_DEV_RANDOM") @@ -429,7 +446,7 @@ AC_FUNC_VPRINTF AC_CHECK_FUNCS(strerror stpcpy strlwr stricmp tcgetattr rand strtoul mmap) 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) +AC_CHECK_FUNCS(waitpid wait4 sigaction sigprocmask fopen64 fstat64) GNUPG_CHECK_MLOCK GNUPG_FUNC_MKDIR_TAKES_ONE_ARG @@ -473,7 +490,7 @@ dnl check whether we have a random device dnl if test "$try_dev_random" = yes ; then AC_CACHE_CHECK(for random device, ac_cv_have_dev_random, - [if test -c "$NAME_OF_DEV_RANDOM" && test -c "$NAME_OF_DEV_URANDOM" ; then + [if test -r "$NAME_OF_DEV_RANDOM" && test -r "$NAME_OF_DEV_URANDOM" ; then ac_cv_have_dev_random=yes; else ac_cv_have_dev_random=no; fi]) if test "$ac_cv_have_dev_random" = yes; then AC_DEFINE(HAVE_DEV_RANDOM) @@ -740,10 +757,16 @@ cat >gnupg-defs.tmp <<G10EOF #define GNUPG_LOCALEDIR "c:/lib/gnupg/locale" #define GNUPG_LIBDIR "c:/lib/gnupg" #define GNUPG_DATADIR "c:/lib/gnupg" + #define GNUPG_HOMEDIR "c:/gnupg-test" #else #define GNUPG_LOCALEDIR "${prefix}/${DATADIRNAME}/locale" #define GNUPG_LIBDIR "${libdir}/gnupg" #define GNUPG_DATADIR "${datadir}/gnupg" + #ifdef __VMS + #define GNUPG_HOMEDIR "/SYS\$LOGIN/gnupg" + #else + #define GNUPG_HOMEDIR "~/.gnupg-test" + #endif #endif G10EOF if cmp -s gnupg-defs.h gnupg-defs.tmp 2>/dev/null; then diff --git a/g10/ChangeLog b/g10/ChangeLog index 74cf0d400..7cb0f17b7 100644 --- a/g10/ChangeLog +++ b/g10/ChangeLog @@ -1,3 +1,81 @@ +Mon Sep 18 16:35:45 CEST 2000 Werner Koch <wk@openit.de> + + * parse-packet.c (dump_sig_subpkt): Dump key flags. + (parse_one_sig_subpkt,can_handle_critical): Add KeyFlags support. + * build-packet.c (build_sig_subpkt): Ditto. + + * g10.c: New option --allow-freeform-uid. By Jeroen C. van Gelderen. + * keygen.c (ask_user_id): Implemented here. + + * parse-packet.c (dump_sig_subpkt): Print info about the ARR. + + * openfile.c (overwrite_filep): Always return okay if the file is + called /dev/null. + (make_outfile_name): Add ".sign" to the list of know extensions. + (open_sigfile): Ditto. + + * getkey.c: Large parts rewritten to have a better sub key selection + and handle some meta information from signatures more correctly. + (get_primary_seckey): Removed. + * seckey_cert.c (do_check): Set main keyid from the data in the sk. + * free-packet.c (copy_public_parts_to_secret_key): New. + * sig-check.c (check_key_signature2): Enabled shortcut for already + checked signatures. + * keydb.h: New macros IS_xxx_SIG, IS_xxx_REV. + * misc.c (openpgp_pk_algo_usage): New. + * packet.h: New field req_uage and do not use pubkey_usage anymore + to request a specific usage. Changed at all places. + * keyid.c (keyid_from_sk): Cache the keyid in the sk + + * passphrase.c (hash_passphrase): Removed funny assert. Reported by + David Mathog. + + * keyedit.c (keyedit_menu): Allow "debug" on secret keys. + + * keygen.c (keygen_add_std_prefs): Changed order of preferences to + twofish, cast5, blowfish. + + * gpg.c: The --trusted-key option is back. + * trustdb.c (verify_own_key): Handle this option. + (add_ultimate_key): Moved stuff from verify_own_key to this new func. + (register_trusted_key): New. + + * openfile.c (try_make_homedir): Changes for non-Posix systems. + * gpg.c (main): Take the default homedir from macro. + + * encode.c (encode_simple, encode_crypt): Fix for large files. + * sign.c (sign_file): Ditto. + + * gpg.c (main): Don't set --quite along with --no-tty. By Frank Tobin. + + * misc.c (disable_core_dump): Don't display a warning here but a return + a status value and ... + * gpg.c (main): ...print warning here. Suggested by Sam Roberts. + + * misc.c (print_pubkey_algo_note): Do not print the RSA notice. + * sig-check.c (do_signature_check): Do not emit the RSA status message. + * pubkey-enc.c (get_session_key): Ditto. + + * ringedit.c (cmp_seckey): Fix for v4 RSA keys. + * seckey-cert.c (do_check): Workaround for PGP 7 bug. + + * pkclist.c (algo_available): Removed hack to disable Twofish. + + * gpg.c (main): Default S2K algorithms are now SHA1 and CAST5 - this + should solve a lot of compatibility problems with other OpenPGP + apps because those algorithms are SHOULD and not optional. The old + way to force it was by using the --openpgp option whith the drawback + that this would disable a couple of workarounds for PGP. + + * gpg.c: New option --merge-only. Suggested by Brendan O'Dea. + * import.c (import_one): Implemented it here. + (import_secret_one): Ditto. + (print_stats): and give some stats. + + * gpg.c: New option --try-all-secrets on suggestion from + Matthias Urlichs. + * pubkey-enc.c (get_session_key): Quite easy to implement here. + Mon Aug 21 17:59:17 CEST 2000 Werner Koch <wk@openit.de> * gpg.c: New option --use-agent diff --git a/g10/build-packet.c b/g10/build-packet.c index 878158917..7291f74b2 100644 --- a/g10/build-packet.c +++ b/g10/build-packet.c @@ -701,6 +701,7 @@ build_sig_subpkt( PKT_signature *sig, sigsubpkttype_t type, case SIGSUBPKT_NOTATION: case SIGSUBPKT_POLICY: case SIGSUBPKT_REVOC_REASON: + case SIGSUBPKT_KEY_FLAGS: hashed = 1; break; default: hashed = 0; break; } diff --git a/g10/encode.c b/g10/encode.c index a0048379f..a817f9094 100644 --- a/g10/encode.c +++ b/g10/encode.c @@ -220,6 +220,10 @@ encode_simple( const char *filename, int mode ) if( filename && !opt.textmode && !mode ) { if( !(filesize = iobuf_get_filelength(inp)) ) log_info(_("%s: WARNING: empty file\n"), filename ); + /* we can't yet encode the length of very large files, + * so we switch to partial length encoding in this case */ + if ( filesize >= IOBUF_FILELENGTH_LIMIT ) + filesize = 0; } else filesize = opt.set_filesize ? opt.set_filesize : 0; /* stdin */ @@ -368,6 +372,10 @@ encode_crypt( const char *filename, STRLIST remusr ) if( filename && !opt.textmode ) { if( !(filesize = iobuf_get_filelength(inp)) ) log_info(_("%s: WARNING: empty file\n"), filename ); + /* we can't yet encode the length of very large files, + * so we switch to partial lengthn encoding in this case */ + if ( filesize >= IOBUF_FILELENGTH_LIMIT ) + filesize = 0; } else filesize = opt.set_filesize ? opt.set_filesize : 0; /* stdin */ diff --git a/g10/free-packet.c b/g10/free-packet.c index 1d9aacff6..2cc4e25e9 100644 --- a/g10/free-packet.c +++ b/g10/free-packet.c @@ -139,6 +139,32 @@ copy_public_key( PKT_public_key *d, PKT_public_key *s ) return copy_public_key_new_namehash( d, s, NULL ); } + +/**************** + * Replace all common parts of a sk by the one from the public key. + * This is a hack and a better solution will be to just store the real secret + * parts somewhere and don't duplicate all the other stuff. + */ +void +copy_public_parts_to_secret_key( PKT_public_key *pk, PKT_secret_key *sk ) +{ + sk->expiredate = pk->expiredate; + sk->pubkey_algo = pk->pubkey_algo; + sk->pubkey_usage= pk->pubkey_usage; + sk->created = pk->created; + sk->req_usage = pk->req_usage; + sk->req_algo = pk->req_algo; + sk->has_expired = pk->has_expired; + sk->is_revoked = pk->is_revoked; + sk->is_valid = pk->is_valid; + sk->main_keyid[0]= pk->main_keyid[0]; + sk->main_keyid[1]= pk->main_keyid[1]; + sk->keyid[0] = pk->keyid[0]; + sk->keyid[1] = pk->keyid[1]; +} + + + PKT_signature * copy_signature( PKT_signature *d, PKT_signature *s ) { @@ -450,3 +476,7 @@ cmp_user_ids( PKT_user_id *a, PKT_user_id *b ) } + + + + diff --git a/g10/getkey.c b/g10/getkey.c index e9b4a231a..17dc6fafb 100644 --- a/g10/getkey.c +++ b/g10/getkey.c @@ -35,9 +35,12 @@ #include "trustdb.h" #include "i18n.h" + +#if 0 #define MAX_UNK_CACHE_ENTRIES 1000 /* we use a linked list - so I guess * this is a reasonable limit */ #define MAX_PK_CACHE_ENTRIES 50 +#endif #define MAX_UID_CACHE_ENTRIES 50 /* A map of the all characters valid used for word_match() @@ -99,7 +102,10 @@ struct getkey_ctx_s { int primary; KBNODE keyblock; KBPOS kbpos; + KBNODE found_key; /* pointer into some keyblock */ int last_rc; + int req_usage; + int req_algo; ulong count; int not_allocated; int nitems; @@ -119,13 +125,13 @@ static struct { } lkup_stats[21]; #endif +typedef struct keyid_list { + struct keyid_list *next; + u32 keyid[2]; +} *keyid_list_t; #if MAX_UNK_CACHE_ENTRIES - typedef struct keyid_list { - struct keyid_list *next; - u32 keyid[2]; - } *keyid_list_t; static keyid_list_t unknown_keyids; static int unk_cache_entries; /* number of entries in unknown keys cache */ static int unk_cache_disabled; @@ -147,7 +153,7 @@ static struct { #endif typedef struct user_id_db { struct user_id_db *next; - u32 keyid[2]; + keyid_list_t keyids; int len; char name[1]; } *user_id_db_t; @@ -157,9 +163,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 ); +static int lookup( GETKEY_CTX ctx, KBNODE *ret_kb, int secmode ); #if 0 @@ -222,34 +226,96 @@ cache_public_key( PKT_public_key *pk ) #endif } +/* + * Return the user ID from the given keyblock. + * We use the primary uid flag which has been set by the merge_selfsigs + * function. The returned value is only valid as long as then given + * keyblock is not changed + */ +static const char * +get_primary_uid ( KBNODE keyblock, size_t *uidlen ) +{ + KBNODE k; + + for (k=keyblock; k; k=k->next ) { + if ( k->pkt->pkttype == PKT_USER_ID + && k->pkt->pkt.user_id->is_primary ) { + *uidlen = k->pkt->pkt.user_id->len; + return k->pkt->pkt.user_id->name; + } + } + *uidlen = 12; + return "[No user ID]"; +} + + +static void +release_keyid_list ( keyid_list_t k ) +{ + while ( k ) { + keyid_list_t k2 = k->next; + gcry_free (k); + k = k2; + } +} /**************** * Store the association of keyid and userid + * Feed only public keys to this function. */ void -cache_user_id( PKT_user_id *uid, u32 *keyid ) +cache_user_id( KBNODE keyblock ) { user_id_db_t r; + const char *uid; + size_t uidlen; + keyid_list_t keyids = NULL; + KBNODE k; - for(r=user_id_db; r; r = r->next ) - if( r->keyid[0] == keyid[0] && r->keyid[1] == keyid[1] ) { - if( DBG_CACHE ) - log_debug("cache_user_id: already in cache\n"); - return; - } + for (k=keyblock; k; k = k->next ) { + if ( k->pkt->pkttype == PKT_PUBLIC_KEY + || k->pkt->pkttype == PKT_PUBLIC_SUBKEY ) { + keyid_list_t a = gcry_xcalloc ( 1, sizeof *a ); + /* Hmmm: For a long list of keyids it might be an advantage + * to append the keys */ + keyid_from_pk( k->pkt->pkt.public_key, a->keyid ); + /* first check for duplicates */ + for(r=user_id_db; r; r = r->next ) { + keyid_list_t b = r->keyids; + for ( b = r->keyids; b; b = b->next ) { + if( b->keyid[0] == a->keyid[0] + && b->keyid[1] == a->keyid[1] ) { + if( DBG_CACHE ) + log_debug("cache_user_id: already in cache\n"); + release_keyid_list ( keyids ); + gcry_free ( a ); + return; + } + } + } + /* now put it into the cache */ + a->next = keyids; + keyids = a; + } + } + if ( !keyids ) + BUG (); /* No key no fun */ + + + uid = get_primary_uid ( keyblock, &uidlen ); if( uid_cache_entries >= MAX_UID_CACHE_ENTRIES ) { /* fixme: use another algorithm to free some cache slots */ r = user_id_db; user_id_db = r->next; + release_keyid_list ( r->keyids ); gcry_free(r); uid_cache_entries--; } - r = gcry_xmalloc( sizeof *r + uid->len-1 ); - r->keyid[0] = keyid[0]; - r->keyid[1] = keyid[1]; - r->len = uid->len; - memcpy(r->name, uid->name, r->len); + r = gcry_xmalloc( sizeof *r + uidlen-1 ); + r->keyids = keyids; + r->len = uidlen; + memcpy(r->name, uid, r->len); r->next = user_id_db; user_id_db = r; uid_cache_entries++; @@ -288,6 +354,31 @@ getkey_disable_caches() } +static void +pk_from_block ( GETKEY_CTX ctx, + PKT_public_key *pk, KBNODE keyblock, const char *namehash ) +{ + KBNODE a = ctx->found_key ? ctx->found_key : keyblock; + + assert ( a->pkt->pkttype == PKT_PUBLIC_KEY + || a->pkt->pkttype == PKT_PUBLIC_SUBKEY ); + + copy_public_key_new_namehash( pk, a->pkt->pkt.public_key, namehash); +} + +static void +sk_from_block ( GETKEY_CTX ctx, + PKT_secret_key *sk, KBNODE keyblock ) +{ + KBNODE a = ctx->found_key ? ctx->found_key : keyblock; + + assert ( a->pkt->pkttype == PKT_SECRET_KEY + || a->pkt->pkttype == PKT_SECRET_SUBKEY ); + + copy_secret_key( sk, a->pkt->pkt.secret_key); +} + + /**************** * Get a public key and store it into the allocated pk * can be called with PK set to NULL to just read it into some @@ -329,14 +420,21 @@ get_pubkey( PKT_public_key *pk, u32 *keyid ) /* do a lookup */ { struct getkey_ctx_s ctx; + KBNODE kb = NULL; memset( &ctx, 0, sizeof ctx ); ctx.not_allocated = 1; ctx.nitems = 1; ctx.items[0].mode = 11; ctx.items[0].keyid[0] = keyid[0]; ctx.items[0].keyid[1] = keyid[1]; - rc = lookup_pk( &ctx, pk, NULL ); + ctx.req_algo = pk->req_algo; + ctx.req_usage = pk->req_usage; + rc = lookup( &ctx, &kb, 0 ); + if ( !rc ) { + pk_from_block ( &ctx, pk, kb, NULL ); + } get_pubkey_end( &ctx ); + release_kbnode ( kb ); } if( !rc ) goto leave; @@ -374,7 +472,6 @@ get_pubkey( PKT_public_key *pk, u32 *keyid ) KBNODE get_pubkeyblock( u32 *keyid ) { - PKT_public_key *pk = gcry_xcalloc( 1, sizeof *pk ); struct getkey_ctx_s ctx; int rc = 0; KBNODE keyblock = NULL; @@ -385,8 +482,7 @@ get_pubkeyblock( u32 *keyid ) ctx.items[0].mode = 11; ctx.items[0].keyid[0] = keyid[0]; ctx.items[0].keyid[1] = keyid[1]; - rc = lookup_pk( &ctx, pk, &keyblock ); - free_public_key(pk); + rc = lookup( &ctx, &keyblock, 0 ); get_pubkey_end( &ctx ); return rc ? NULL : keyblock; @@ -403,6 +499,7 @@ get_seckey( PKT_secret_key *sk, u32 *keyid ) { int rc; struct getkey_ctx_s ctx; + KBNODE kb = NULL; memset( &ctx, 0, sizeof ctx ); ctx.not_allocated = 1; @@ -410,8 +507,15 @@ get_seckey( PKT_secret_key *sk, u32 *keyid ) ctx.items[0].mode = 11; ctx.items[0].keyid[0] = keyid[0]; ctx.items[0].keyid[1] = keyid[1]; - rc = lookup_sk( &ctx, sk, NULL ); + ctx.req_algo = sk->req_algo; + ctx.req_usage = sk->req_usage; + rc = lookup( &ctx, &kb, 1 ); + if ( !rc ) { + sk_from_block ( &ctx, sk, kb ); + } get_seckey_end( &ctx ); + release_kbnode ( kb ); + if( !rc ) { /* check the secret key (this may prompt for a passprase to * unlock the secret key @@ -424,30 +528,6 @@ get_seckey( PKT_secret_key *sk, u32 *keyid ) /**************** - * Get the primary secret key and store it into sk - * Note: This function does not unprotect the key! - */ -int -get_primary_seckey( PKT_secret_key *sk, u32 *keyid ) -{ - struct getkey_ctx_s ctx; - int rc; - - memset( &ctx, 0, sizeof ctx ); - ctx.not_allocated = 1; - ctx.primary = 1; - ctx.nitems = 1; - ctx.items[0].mode = 11; - ctx.items[0].keyid[0] = keyid[0]; - ctx.items[0].keyid[1] = keyid[1]; - rc = lookup_sk( &ctx, sk, NULL ); - get_seckey_end( &ctx ); - return rc; -} - - - -/**************** * Check whether the secret key is available * Returns: 0 := key is available * GPGERR_NO_SECKEY := not availabe @@ -457,18 +537,17 @@ seckey_available( u32 *keyid ) { int rc; struct getkey_ctx_s ctx; - PKT_secret_key *sk; + KBNODE kb = NULL; - sk = gcry_xcalloc( 1, sizeof *sk ); memset( &ctx, 0, sizeof ctx ); ctx.not_allocated = 1; ctx.nitems = 1; ctx.items[0].mode = 11; ctx.items[0].keyid[0] = keyid[0]; ctx.items[0].keyid[1] = keyid[1]; - rc = lookup_sk( &ctx, sk, NULL ); + rc = lookup( &ctx, &kb, 1 ); get_seckey_end( &ctx ); - free_secret_key( sk ); + release_kbnode ( kb ); return rc; } @@ -612,7 +691,8 @@ classify_user_id( const char *name, u32 *keyid, byte *fprint, hexlength = 0; /* a hex number, but really were not. */ } - if (hexlength == 8 || (!hexprefix && hexlength == 9 && *s == '0')){ + if (hexlength == 8 + || (!hexprefix && hexlength == 9 && *s == '0')){ /* short keyid */ if (hexlength == 9) s++; @@ -622,8 +702,8 @@ classify_user_id( const char *name, u32 *keyid, byte *fprint, } mode = 10; } - else if (hexlength == 16 || (!hexprefix && hexlength == 17 - && *s == '0')) { + else if (hexlength == 16 + || (!hexprefix && hexlength == 17 && *s == '0')) { /* complete keyid */ char buf[9]; if (hexlength == 17) @@ -701,10 +781,10 @@ key_byname( GETKEY_CTX *retctx, STRLIST namelist, int n; STRLIST r; GETKEY_CTX ctx; - + KBNODE help_kb = NULL; + if( retctx ) /* reset the returned context in case of error */ *retctx = NULL; - assert( !pk ^ !sk ); /* build the search context */ /* Performance hint: Use a static buffer if there is only one name */ @@ -732,10 +812,25 @@ key_byname( GETKEY_CTX *retctx, STRLIST namelist, /* and call the lookup function */ ctx->primary = 1; /* we want to look for the primary key only */ - if( sk ) - rc = lookup_sk( ctx, sk, ret_kb ); - else - rc = lookup_pk( ctx, pk, ret_kb ); + + if ( !ret_kb ) + ret_kb = &help_kb; + + if( sk ) { + rc = lookup( ctx, ret_kb, 1 ); + if ( !rc && sk ) { + sk_from_block ( ctx, sk, *ret_kb ); + } + } + else { + + rc = lookup( ctx, ret_kb, 0 ); + if ( !rc && pk ) { + pk_from_block ( ctx, pk, *ret_kb, NULL /* FIXME need to get the namehash*/ ); + } + } + + release_kbnode ( help_kb ); if( retctx ) /* caller wants the context */ *retctx = ctx; @@ -758,16 +853,7 @@ get_pubkey_byname( GETKEY_CTX *retctx, PKT_public_key *pk, STRLIST namelist = NULL; add_to_strlist( &namelist, name ); - - if( !pk ) { - /* Performance Hint: key_byname should not need a pk here */ - pk = gcry_xcalloc( 1, sizeof *pk ); - rc = key_byname( retctx, namelist, pk, NULL, ret_keyblock ); - free_public_key( pk ); - } - else - rc = key_byname( retctx, namelist, pk, NULL, ret_keyblock ); - + rc = key_byname( retctx, namelist, pk, NULL, ret_keyblock ); free_strlist( namelist ); return rc; } @@ -776,18 +862,7 @@ int get_pubkey_bynames( GETKEY_CTX *retctx, PKT_public_key *pk, STRLIST names, KBNODE *ret_keyblock ) { - int rc; - - if( !pk ) { - /* Performance Hint: key_byname should not need a pk here */ - pk = gcry_xcalloc( 1, sizeof *pk ); - rc = key_byname( retctx, names, pk, NULL, ret_keyblock ); - free_public_key( pk ); - } - else - rc = key_byname( retctx, names, pk, NULL, ret_keyblock ); - - return rc; + return key_byname( retctx, names, pk, NULL, ret_keyblock ); } int @@ -795,14 +870,10 @@ get_pubkey_next( GETKEY_CTX ctx, PKT_public_key *pk, KBNODE *ret_keyblock ) { int rc; - if( !pk ) { - /* Performance Hint: lookup_read should not need a pk in this case */ - pk = gcry_xcalloc( 1, sizeof *pk ); - rc = lookup_pk( ctx, pk, ret_keyblock ); - free_public_key( pk ); - } - else - rc = lookup_pk( ctx, pk, ret_keyblock ); + rc = lookup( ctx, ret_keyblock, 0 ); + if ( !rc && pk && ret_keyblock ) + pk_from_block ( ctx, pk, *ret_keyblock, NULL ); + return rc; } @@ -824,18 +895,24 @@ get_pubkey_end( GETKEY_CTX ctx ) * Search for a key with the given fingerprint. */ int -get_pubkey_byfprint( PKT_public_key *pk, const byte *fprint, size_t fprint_len) +get_pubkey_byfprint( PKT_public_key *pk, + const byte *fprint, size_t fprint_len) { int rc; if( fprint_len == 20 || fprint_len == 16 ) { struct getkey_ctx_s ctx; + KBNODE kb = NULL; + memset( &ctx, 0, sizeof ctx ); ctx.not_allocated = 1; ctx.nitems = 1; ctx.items[0].mode = fprint_len; memcpy( ctx.items[0].fprint, fprint, fprint_len ); - rc = lookup_pk( &ctx, pk, NULL ); + rc = lookup( &ctx, &kb, 0 ); + if (!rc && pk ) + pk_from_block ( &ctx, pk, kb, NULL ); + release_kbnode ( kb ); get_pubkey_end( &ctx ); } else @@ -852,35 +929,33 @@ get_keyblock_byfprint( KBNODE *ret_keyblock, const byte *fprint, size_t fprint_len ) { int rc; - PKT_public_key *pk = gcry_xcalloc( 1, sizeof *pk ); if( fprint_len == 20 || fprint_len == 16 ) { struct getkey_ctx_s ctx; + memset( &ctx, 0, sizeof ctx ); ctx.not_allocated = 1; ctx.nitems = 1; ctx.items[0].mode = fprint_len; memcpy( ctx.items[0].fprint, fprint, fprint_len ); - rc = lookup_pk( &ctx, pk, ret_keyblock ); + rc = lookup( &ctx, ret_keyblock, 0 ); get_pubkey_end( &ctx ); } else rc = GPGERR_GENERAL; /* Oops */ - free_public_key( pk ); return rc; } /**************** - * Search for a key with the given lid and return the complete keyblock + * Search for a key with the given lid and return the entire keyblock */ int get_keyblock_bylid( KBNODE *ret_keyblock, ulong lid ) { int rc; - PKT_public_key *pk = gcry_xcalloc( 1, sizeof *pk ); struct getkey_ctx_s ctx; u32 kid[2]; @@ -892,10 +967,9 @@ get_keyblock_bylid( KBNODE *ret_keyblock, ulong lid ) ctx.items[0].mode = 12; ctx.items[0].keyid[0] = kid[0]; ctx.items[0].keyid[1] = kid[1]; - rc = lookup_pk( &ctx, pk, ret_keyblock ); + rc = lookup( &ctx, ret_keyblock, 0 ); get_pubkey_end( &ctx ); - free_public_key( pk ); return rc; } @@ -919,13 +993,17 @@ get_seckey_byname( PKT_secret_key *sk, const char *name, int unprotect ) } else if( !name ) { /* use the first one as default key */ struct getkey_ctx_s ctx; + KBNODE kb = NULL; memset( &ctx, 0, sizeof ctx ); ctx.not_allocated = 1; ctx.primary = 1; ctx.nitems = 1; ctx.items[0].mode = 15; - rc = lookup_sk( &ctx, sk, NULL ); + rc = lookup( &ctx, &kb, 1 ); + if (!rc && sk ) + sk_from_block ( &ctx, sk, kb ); + release_kbnode ( kb ); get_seckey_end( &ctx ); } else { @@ -945,18 +1023,7 @@ int get_seckey_bynames( GETKEY_CTX *retctx, PKT_secret_key *sk, STRLIST names, KBNODE *ret_keyblock ) { - int rc; - - if( !sk ) { - /* Performance Hint: key_byname should not need a sk here */ - sk = gcry_xcalloc_secure( 1, sizeof *sk ); - rc = key_byname( retctx, names, NULL, sk, ret_keyblock ); - free_secret_key( sk ); - } - else - rc = key_byname( retctx, names, NULL, sk, ret_keyblock ); - - return rc; + return key_byname( retctx, names, NULL, sk, ret_keyblock ); } @@ -965,32 +1032,24 @@ get_seckey_next( GETKEY_CTX ctx, PKT_secret_key *sk, KBNODE *ret_keyblock ) { int rc; - if( !sk ) { - /* Performance Hint: lookup_read should not need a pk in this case */ - sk = gcry_xcalloc_secure( 1, sizeof *sk ); - rc = lookup_sk( ctx, sk, ret_keyblock ); - free_secret_key( sk ); - } - else - rc = lookup_sk( ctx, sk, ret_keyblock ); + rc = lookup( ctx, ret_keyblock, 1 ); + if ( !rc && sk && ret_keyblock ) + sk_from_block ( ctx, sk, *ret_keyblock ); + return rc; } void get_seckey_end( GETKEY_CTX ctx ) { - if( ctx ) { - int n; - - enum_keyblocks( 2, &ctx->kbpos, NULL ); /* close */ - for(n=0; n < ctx->nitems; n++ ) - gcry_free( ctx->items[n].namebuf ); - if( !ctx->not_allocated ) - gcry_free( ctx ); - } + get_pubkey_end( ctx ); } + +/******************************************************* + ************** compare functions ********************** + *******************************************************/ /**************** * Do a word match (original user id starts with a '+'). @@ -1130,73 +1189,15 @@ compare_name( const char *uid, size_t uidlen, const char *name, int mode ) -/**************** - * Assume that knode points to a public key packet and keyblock is - * the entire keyblock. This function adds all relevant information from - * a selfsignature to the public key. - */ - -static void -merge_one_pk_and_selfsig( KBNODE keyblock, KBNODE knode, - PKT_public_key *orig_pk ) -{ - PKT_public_key *pk = knode->pkt->pkt.public_key; - PKT_signature *sig; - KBNODE k; - u32 kid[2]; - u32 sigdate = 0; - - assert( knode->pkt->pkttype == PKT_PUBLIC_KEY - || knode->pkt->pkttype == PKT_PUBLIC_SUBKEY ); - - if( pk->version < 4 ) - return; /* this is only needed for version >=4 packets */ - - - /* find the selfsignature */ - if( knode->pkt->pkttype == PKT_PUBLIC_SUBKEY ) { - k = find_kbnode( keyblock, PKT_PUBLIC_KEY ); - if( !k ) - BUG(); /* keyblock without primary key!!! */ - keyid_from_pk( k->pkt->pkt.public_key, kid ); - } - else - keyid_from_pk( pk, kid ); - - for(k=knode->next; k; k = k->next ) { - if( k->pkt->pkttype == PKT_SIGNATURE - && (sig=k->pkt->pkt.signature)->sig_class >= 0x10 - && sig->sig_class <= 0x30 - && sig->keyid[0] == kid[0] - && sig->keyid[1] == kid[1] - && sig->version > 3 ) { - /* okay this is a self-signature which can be used. - * We use the latest self-signature. - * FIXME: We should only use this if the 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? pk->timestamp + buffer_to_u32(p):0; - /* use the latest self signature */ - if( sig->timestamp > sigdate ) { - pk->expiredate = ed; - orig_pk->expiredate = ed; - sigdate = sig->timestamp; - } - /* fixme: add usage etc. to pk */ - } - else if( k->pkt->pkttype == PKT_PUBLIC_SUBKEY ) - break; /* stop here */ - } -} - + +/************************************************ + ************* Merging stuff ******************** + ************************************************/ /**************** * merge all selfsignatures with the keys. + * FIXME: replace this at least for the public key parts + * by merge_selfsigs */ void merge_keys_and_selfsig( KBNODE keyblock ) @@ -1218,7 +1219,7 @@ merge_keys_and_selfsig( KBNODE keyblock ) keyid_from_pk( pk, kid ); else if( !pk->expiredate ) { /* and subkey */ /* insert the expiration date here */ - pk->expiredate = subkeys_expiretime( k, kid ); + /*FIXME!!! pk->expiredate = subkeys_expiretime( k, kid );*/ } sigdate = 0; } @@ -1266,510 +1267,850 @@ merge_keys_and_selfsig( KBNODE keyblock ) } -static KBNODE -find_by_name( KBNODE keyblock, PKT_public_key *pk, const char *name, - int mode, byte *namehash, int *use_namehash ) +static void +fixup_uidnode ( KBNODE uidnode, KBNODE signode ) { - KBNODE k, kk; + PKT_user_id *uid = uidnode->pkt->pkt.user_id; + PKT_signature *sig = signode->pkt->pkt.signature; + const byte *p; + size_t n; + + uid->created = 0; /* not created == invalid */ + if ( !signode ) + return; /* no self-signature */ + if ( IS_UID_REV ( sig ) ) + return; /* has been revoked */ + + uid->created = sig->timestamp; /* this one is okay */ + + + /* store the key flags in the helper variable for later processing */ + uid->help_key_usage = 0; + p = parse_sig_subpkt ( sig->hashed_data, SIGSUBPKT_KEY_FLAGS, &n ); + if ( p && n ) { + /* first octet of the keyflags */ + if ( (*p & 3) ) + uid->help_key_usage |= GCRY_PK_USAGE_SIGN; + if ( (*p & 12) ) + uid->help_key_usage |= GCRY_PK_USAGE_ENCR; + } - for(k=keyblock; k; k = k->next ) { - if( k->pkt->pkttype == PKT_USER_ID - && !compare_name( k->pkt->pkt.user_id->name, - k->pkt->pkt.user_id->len, name, mode)) { - /* we found a matching name, look for the key */ - for(kk=keyblock; kk; kk = kk->next ) { - if( ( kk->pkt->pkttype == PKT_PUBLIC_KEY - || kk->pkt->pkttype == PKT_PUBLIC_SUBKEY ) - && ( !pk->pubkey_algo - || pk->pubkey_algo - == kk->pkt->pkt.public_key->pubkey_algo) - && ( !pk->pubkey_usage - || !openpgp_pk_test_algo( - kk->pkt->pkt.public_key->pubkey_algo, - pk->pubkey_usage )) - ) - break; - } - if( kk ) { - u32 aki[2]; - keyid_from_pk( kk->pkt->pkt.public_key, aki ); - cache_user_id( k->pkt->pkt.user_id, aki ); - 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; - } - else if( is_RSA(pk->pubkey_algo) ) - log_error(_("RSA key cannot be used in this version\n")); - else - log_error(_("No key for user ID\n")); - } + /* ditto or the key expiration */ + uid->help_key_expire = 0; + p = parse_sig_subpkt ( sig->hashed_data, SIGSUBPKT_KEY_EXPIRE, NULL); + if ( p ) { + uid->help_key_expire = sig->timestamp + buffer_to_u32(p); } - return NULL; + + /* Set the primary user ID flag - we will later wipe out some + * of them to only have one in out keyblock */ + uid->is_primary = 0; + p = parse_sig_subpkt ( sig->hashed_data, SIGSUBPKT_PRIMARY_UID, NULL ); + if ( p && *p ) + uid->is_primary = 1; + /* We could also query this from the unhashed area if it is not in + * the hased area and then later try to decide which is the better + * there should be no security problem with this. + * For now we only look at the hashed one. + */ } -static KBNODE -find_by_name_sk( KBNODE keyblock, PKT_secret_key *sk, const char *name, - int mode ) +static void +merge_selfsigs_main( KBNODE keyblock, int *r_revoked ) { - KBNODE k, kk; + PKT_public_key *pk = NULL; + KBNODE k; + u32 kid[2]; + u32 sigdate = 0, uiddate=0, uiddate2; + KBNODE signode, uidnode, uidnode2; + u32 curtime = make_timestamp (); + unsigned int key_usage = 0; + u32 key_expire = 0; + int key_expire_seen = 0; + + *r_revoked = 0; + if ( keyblock->pkt->pkttype != PKT_PUBLIC_KEY ) + BUG (); + pk = keyblock->pkt->pkt.public_key; + pk->created = 0; + keyid_from_pk( pk, kid ); + pk->main_keyid[0] = kid[0]; + pk->main_keyid[1] = kid[1]; + + if ( pk->version < 4 ) + return; /* nothing to do for old keys FIXME: This is wrong!!!!*/ + + /* first pass: find the latest direct key self-signature. + * We assume that the newest one overrides all others + */ + signode = NULL; + sigdate = 0; /* helper to find the latest signature */ + for(k=keyblock; k && k->pkt->pkttype != PKT_PUBLIC_SUBKEY; k = k->next ) { + if ( k->pkt->pkttype == PKT_SIGNATURE ) { + PKT_signature *sig = k->pkt->pkt.signature; + if ( sig->keyid[0] == kid[0] && sig->keyid[1]==kid[1] ) { + if ( check_key_signature( keyblock, k, NULL ) ) + ; /* signature did not verify */ + else if ( IS_KEY_REV (sig) ){ + /* key has been revoked - there is no way to override + * such a revocation, so we can stop now. + * we can't cope with expiration times for revocations + * here because we have to assumethat an attacker can + * generate all kinds of signatures. + */ + *r_revoked = 1; + return; + } + else if ( IS_KEY_SIG (sig) && sig->timestamp >= sigdate ) { + const byte *p; + + p = parse_sig_subpkt( sig->hashed_data, + SIGSUBPKT_SIG_EXPIRE, NULL ); + if ( p && (sig->timestamp + buffer_to_u32(p)) >= curtime ) + ; /* signature has expired - ignore it */ + else { + sigdate = sig->timestamp; + signode = k; + } + } + } + } + } - for(k=keyblock; k; k = k->next ) { - if( k->pkt->pkttype == PKT_USER_ID - && !compare_name( k->pkt->pkt.user_id->name, - k->pkt->pkt.user_id->len, name, mode)) { - /* we found a matching name, look for the key */ - for(kk=keyblock; kk; kk = kk->next ) { - if( ( kk->pkt->pkttype == PKT_SECRET_KEY - || kk->pkt->pkttype == PKT_SECRET_SUBKEY ) - && ( !sk->pubkey_algo - || sk->pubkey_algo - == kk->pkt->pkt.secret_key->pubkey_algo) - && ( !sk->pubkey_usage - || !openpgp_pk_test_algo( - kk->pkt->pkt.secret_key->pubkey_algo, - sk->pubkey_usage )) - ) - break; - } - if( kk ) { - u32 aki[2]; - keyid_from_sk( kk->pkt->pkt.secret_key, aki ); - cache_user_id( k->pkt->pkt.user_id, aki ); - return kk; - } - else if( is_RSA(sk->pubkey_algo) ) - log_error(_("RSA key cannot be used in this version\n")); - else - log_error(_("No key for user ID\n")); - } + if ( signode ) { + /* some information from a direct key signature take precedence + * over the same information given in UID sigs. + */ + PKT_signature *sig = signode->pkt->pkt.signature; + const byte *p; + size_t n; + + p = parse_sig_subpkt ( sig->hashed_data, SIGSUBPKT_KEY_FLAGS, &n ); + if ( p && n ) { + /* first octet of the keyflags */ + if ( (*p & 3) ) + key_usage |= GCRY_PK_USAGE_SIGN; + if ( (*p & 12) ) + key_usage |= GCRY_PK_USAGE_ENCR; + } + + p = parse_sig_subpkt ( sig->hashed_data, SIGSUBPKT_KEY_EXPIRE, NULL); + if ( p ) { + key_expire = sig->timestamp + buffer_to_u32(p); + key_expire_seen = 1; + } + + /* and set the created field */ + pk->created = sigdate; + /* and mark that key as valid: one direct key signature should + * render a key as valid */ + pk->is_valid = 1; } - return NULL; + + + /* second pass: look at the self-signature of all user IDs */ + signode = uidnode = NULL; + sigdate = 0; /* helper to find the latest signature in one user ID */ + uiddate = 0; /* and over of all user IDs */ + for(k=keyblock; k && k->pkt->pkttype != PKT_PUBLIC_SUBKEY; k = k->next ) { + if ( k->pkt->pkttype == PKT_USER_ID + || k->pkt->pkttype == PKT_PHOTO_ID ) { + if ( uidnode ) + fixup_uidnode ( uidnode, signode ); + uidnode = k; + signode = NULL; + if ( sigdate > uiddate ) + uiddate = sigdate; + sigdate = 0; + } + else if ( k->pkt->pkttype == PKT_SIGNATURE && uidnode ) { + PKT_signature *sig = k->pkt->pkt.signature; + if ( sig->keyid[0] == kid[0] && sig->keyid[1]==kid[1] ) { + if ( check_key_signature( keyblock, k, NULL ) ) + ; /* signature did not verify */ + else if ( IS_UID_SIG (sig) || IS_UID_REV (sig)) { + /* Note: we allow to invalidated cert revocations + * by a newer signature. An attacker can't use this + * because a key should be revoced with a key revocation. + * The reason why we have to allow for that is that at + * one time an email address may become invalid but later + * the same email address may become valid again (hired, + * fired, hired again). + */ + const byte *p; + + p = parse_sig_subpkt( sig->hashed_data, + SIGSUBPKT_SIG_EXPIRE, NULL ); + if ( p && (sig->timestamp + buffer_to_u32(p)) >= curtime ) + ; /* signature/revocation has expired - ignore it */ + else { + sigdate = sig->timestamp; + signode = k; + } + } + } + } + } + if ( uidnode ) { + fixup_uidnode ( uidnode, signode ); + pk->is_valid = 1; + } + if ( sigdate > uiddate ) + uiddate = sigdate; + /* if we do not have a direct key signature, take the key creation date + * from the latest user ID. Hmmm, another possibilty would be to take + * it from the latest primary user ID - but we don't implement it for + * now */ + if ( !pk->created ) + pk->created = uiddate; + if ( !pk->created ) { + /* oops, still no creation date: use the timestamp */ + if (DBG_CACHE) + log_debug( "merge_selfsigs_main: " + "using timestamp as creation date\n"); + pk->created = pk->timestamp; + } + + /* Now that we had a look at all user IDs we can now get some information + * from those user IDs. + */ + + if ( !key_usage ) { + /* find the latest user ID with key flags set */ + uiddate = 0; /* helper to find the latest user ID */ + for(k=keyblock; k && k->pkt->pkttype != PKT_PUBLIC_SUBKEY; + k = k->next ) { + if ( k->pkt->pkttype == PKT_USER_ID + || k->pkt->pkttype == PKT_PHOTO_ID ) { + PKT_user_id *uid = k->pkt->pkt.user_id; + if ( uid->help_key_usage && uid->created > uiddate ) { + key_usage = uid->help_key_usage; + uiddate = uid->created; + } + } + } + } + if ( !key_usage ) { /* no key flags at all: get it from the algo */ + key_usage = openpgp_pk_algo_usage ( pk->pubkey_algo ); + } + else { /* check that the usage matches the usage as given by the algo */ + int x = openpgp_pk_algo_usage ( pk->pubkey_algo ); + if ( x ) /* mask it down to the actual allowed usage */ + key_usage &= x; + } + pk->pubkey_usage = key_usage; + + + if ( !key_expire_seen ) { + /* find the latest valid user ID with a key expiration set + * Note, that this may be a diferent one from the above because + * some user IDs may have no expiration date set */ + uiddate = 0; + for(k=keyblock; k && k->pkt->pkttype != PKT_PUBLIC_SUBKEY; + k = k->next ) { + if ( k->pkt->pkttype == PKT_USER_ID + || k->pkt->pkttype == PKT_PHOTO_ID ) { + PKT_user_id *uid = k->pkt->pkt.user_id; + if ( uid->help_key_expire && uid->created > uiddate ) { + key_expire = uid->help_key_expire; + uiddate = uid->created; + } + } + } + } + if ( key_expire >= curtime ) + pk->has_expired = key_expire; + /* FIXME: we should see how to get rid of the expiretime fields */ + + + /* and now find the real primary user ID and delete all others */ + uiddate = uiddate2 = 0; + uidnode = uidnode2 = NULL; + for(k=keyblock; k && k->pkt->pkttype != PKT_PUBLIC_SUBKEY; k = k->next ) { + if ( k->pkt->pkttype == PKT_USER_ID + || k->pkt->pkttype == PKT_PHOTO_ID ) { + PKT_user_id *uid = k->pkt->pkt.user_id; + if ( uid->is_primary && uid->created > uiddate ) { + uiddate = uid->created; + uidnode = k; + } + if ( !uid->is_primary && uid->created > uiddate2 ) { + uiddate2 = uid->created; + uidnode2 = k; + } + } + } + if ( uidnode ) { + for(k=keyblock; k && k->pkt->pkttype != PKT_PUBLIC_SUBKEY; + k = k->next ) { + if ( k->pkt->pkttype == PKT_USER_ID + || k->pkt->pkttype == PKT_PHOTO_ID ) { + PKT_user_id *uid = k->pkt->pkt.user_id; + if ( k != uidnode ) + uid->is_primary = 0; + } + } + } + else if( uidnode2 ) { + /* none is flagged primary - use the latest user ID we have */ + uidnode2->pkt->pkt.user_id->is_primary = 1; + } + } -static KBNODE -find_by_keyid( KBNODE keyblock, PKT_public_key *pk, u32 *keyid, int mode ) +static void +merge_selfsigs_subkey( KBNODE keyblock, KBNODE subnode ) { + PKT_public_key *mainpk = NULL, *subpk = NULL; + PKT_signature *sig; KBNODE k; + u32 mainkid[2]; + u32 sigdate = 0; + KBNODE signode; + u32 curtime = make_timestamp (); + unsigned int key_usage = 0; + u32 key_expire = 0; + const byte *p; + size_t n; + + if ( subnode->pkt->pkttype != PKT_PUBLIC_SUBKEY ) + BUG (); + mainpk = keyblock->pkt->pkt.public_key; + if ( mainpk->version < 4 ) + return; /* (actually this should never happen) */ + keyid_from_pk( mainpk, mainkid ); + subpk = subnode->pkt->pkt.public_key; + subpk->is_valid = 0; + subpk->main_keyid[0] = mainpk->main_keyid[0]; + subpk->main_keyid[1] = mainpk->main_keyid[1]; + if ( subpk->version < 4 ) + return; /* there are no v3 subkeys */ + + /* find the latest key binding self-signature. */ + signode = NULL; + sigdate = 0; /* helper to find the latest signature */ + for(k=subnode->next; k && k->pkt->pkttype != PKT_PUBLIC_SUBKEY; + k = k->next ) { + if ( k->pkt->pkttype == PKT_SIGNATURE ) { + sig = k->pkt->pkt.signature; + if ( sig->keyid[0] == mainkid[0] && sig->keyid[1]==mainkid[1] ) { + if ( check_key_signature( keyblock, k, NULL ) ) + ; /* signature did not verify */ + else if ( IS_SUBKEY_REV (sig) ) { + /* key has been revoked - given the fact that it is easy + * to create a new subkey, it does not make sense to + * revive a revoked key. So we can stop here. + */ + subpk->is_revoked = 1; + return; + } + else if ( IS_SUBKEY_SIG (sig) && sig->timestamp >= sigdate ) { + p = parse_sig_subpkt( sig->hashed_data, + SIGSUBPKT_SIG_EXPIRE, NULL ); + if ( p && (sig->timestamp + buffer_to_u32(p)) >= curtime ) + ; /* signature has expired - ignore it */ + else { + sigdate = sig->timestamp; + signode = k; + } + } + } + } + } - if( DBG_CACHE ) - log_debug("lookup keyid=%08lx%08lx req_algo=%d mode=%d\n", - (ulong)keyid[0], (ulong)keyid[1], pk->pubkey_algo, mode ); + if ( !signode ) { + subpk->created = subpk->timestamp; + return; /* no valid key binding */ + } - for(k=keyblock; k; k = k->next ) { - if( k->pkt->pkttype == PKT_PUBLIC_KEY - || k->pkt->pkttype == PKT_PUBLIC_SUBKEY ) { - u32 aki[2]; - keyid_from_pk( k->pkt->pkt.public_key, aki ); - if( DBG_CACHE ) - log_debug(" aki=%08lx%08lx algo=%d\n", - (ulong)aki[0], (ulong)aki[1], - k->pkt->pkt.public_key->pubkey_algo ); - - if( aki[1] == keyid[1] - && ( mode == 10 || aki[0] == keyid[0] ) - && ( !pk->pubkey_algo - || pk->pubkey_algo - == k->pkt->pkt.public_key->pubkey_algo) ){ - KBNODE kk; - /* cache the userid */ - for(kk=keyblock; kk; kk = kk->next ) - if( kk->pkt->pkttype == PKT_USER_ID ) - break; - if( kk ) - cache_user_id( kk->pkt->pkt.user_id, aki ); - else - log_error(_("No user ID for key\n")); - return k; /* found */ - } - } + subpk->is_valid = 1; + subpk->created = sigdate; + sig = signode->pkt->pkt.signature; + + p = parse_sig_subpkt ( sig->hashed_data, SIGSUBPKT_KEY_FLAGS, &n ); + if ( p && n ) { + /* first octet of the keyflags */ + if ( (*p & 3) ) + key_usage |= GCRY_PK_USAGE_SIGN; + if ( (*p & 12) ) + key_usage |= GCRY_PK_USAGE_ENCR; } - return NULL; + if ( !key_usage ) { /* no key flags at all: get it from the algo */ + key_usage = openpgp_pk_algo_usage ( subpk->pubkey_algo ); + } + else { /* check that the usage matches the usage as given by the algo */ + int x = openpgp_pk_algo_usage ( subpk->pubkey_algo ); + if ( x ) /* mask it down to the actual allowed usage */ + key_usage &= x; + } + subpk->pubkey_usage = key_usage; + + p = parse_sig_subpkt ( sig->hashed_data, SIGSUBPKT_KEY_EXPIRE, NULL); + if ( p ) + key_expire = sig->timestamp + buffer_to_u32(p); + else + key_expire = 0; + subpk->has_expired = key_expire >= curtime? key_expire : 0; } -static KBNODE -find_by_keyid_sk( KBNODE keyblock, PKT_secret_key *sk, u32 *keyid, int mode ) + + +/* + * Merge information from the self-signatures with the key, so that + * we can later use them more easy. + * The function works by first applying the self signatures to the + * primary key and the to each subkey. + * Here are the rules we use to decide which inormation from which + * self-signature is used: + * We check all self signatures or validity and ignore all invalid signatures. + * All signatures are then ordered by their creation date .... + * For the primary key: + * FIXME the docs + */ +static void +merge_selfsigs( KBNODE keyblock ) { KBNODE k; + int revoked; + PKT_public_key *main_pk; + + if ( keyblock->pkt->pkttype != PKT_PUBLIC_KEY ) + BUG (); + + merge_selfsigs_main ( keyblock, &revoked ); + main_pk = keyblock->pkt->pkt.public_key; + if ( revoked ) { + /* if the primary key has been revoked we better set the revoke + * flag on that key and all subkeys */ + for(k=keyblock; k; k = k->next ) { + if ( k->pkt->pkttype == PKT_PUBLIC_KEY + || k->pkt->pkttype == PKT_PUBLIC_SUBKEY ) { + PKT_public_key *pk = k->pkt->pkt.public_key; + pk->is_revoked = 1; + pk->main_keyid[0] = main_pk->main_keyid[0]; + pk->main_keyid[1] = main_pk->main_keyid[1]; + } + } + return; + } - if( DBG_CACHE ) - log_debug("lookup_sk keyid=%08lx%08lx req_algo=%d mode=%d\n", - (ulong)keyid[0], (ulong)keyid[1], sk->pubkey_algo, mode ); - + /* now merge in the data from each of the subkeys */ for(k=keyblock; k; k = k->next ) { - if( k->pkt->pkttype == PKT_SECRET_KEY - || k->pkt->pkttype == PKT_SECRET_SUBKEY ) { - u32 aki[2]; - keyid_from_sk( k->pkt->pkt.secret_key, aki ); - if( DBG_CACHE ) - log_debug(" aki=%08lx%08lx algo=%d\n", - (ulong)aki[0], (ulong)aki[1], - k->pkt->pkt.secret_key->pubkey_algo ); - - if( aki[1] == keyid[1] - && ( mode == 10 || aki[0] == keyid[0] ) - && ( !sk->pubkey_algo - || sk->pubkey_algo - == k->pkt->pkt.secret_key->pubkey_algo) ){ - KBNODE kk; - /* cache the userid */ - for(kk=keyblock; kk; kk = kk->next ) - if( kk->pkt->pkttype == PKT_USER_ID ) - break; - if( kk ) - cache_user_id( kk->pkt->pkt.user_id, aki ); - else - log_error(_("No user ID for key\n")); - return k; /* found */ - } - } + if ( k->pkt->pkttype == PKT_PUBLIC_SUBKEY ) { + merge_selfsigs_subkey ( keyblock, k ); + } } - return NULL; } -static KBNODE -find_first( KBNODE keyblock, PKT_public_key *pk ) +/* + * Merge the secret keys from secblock into the pubblock thereby + * replacing the public (sub)keys with their secret counterparts Hmmm: + * It might be better to get away from the concept of entire secret + * keys at all and have a way to store just the real secret parts + * from the key. + */ +static void +merge_public_with_secret ( KBNODE pubblock, KBNODE secblock ) { - KBNODE k; + KBNODE pub; + int deleting = 0; + int any_deleted = 0; + + assert ( pubblock->pkt->pkttype == PKT_PUBLIC_KEY ); + assert ( secblock->pkt->pkttype == PKT_SECRET_KEY ); + + for (pub=pubblock; pub; pub = pub->next ) { + if ( pub->pkt->pkttype == PKT_PUBLIC_KEY ) { + PKT_public_key *pk = pub->pkt->pkt.public_key; + PKT_secret_key *sk = secblock->pkt->pkt.secret_key; + assert ( pub == pubblock ); /* only in the first node */ + /* there is nothing to compare in this case, so just replace + * some information */ + copy_public_parts_to_secret_key ( pk, sk ); + free_public_key ( pk ); + pub->pkt->pkttype = PKT_SECRET_KEY; + pub->pkt->pkt.secret_key = copy_secret_key (NULL, sk); + } + else if ( pub->pkt->pkttype == PKT_PUBLIC_SUBKEY ) { + KBNODE sec; + PKT_public_key *pk = pub->pkt->pkt.public_key; + + deleting = 0; + /* this is more complicated: it may happen that the sequence + * of the subkeys dosn't match, so we have to find the + * appropriate secret key */ + for (sec=secblock->next; sec; sec = sec->next ) { + if ( sec->pkt->pkttype == PKT_SECRET_SUBKEY ) { + PKT_secret_key *sk = sec->pkt->pkt.secret_key; + if ( !cmp_public_secret_key ( pk, sk ) ) { + copy_public_parts_to_secret_key ( pk, sk ); + free_public_key ( pk ); + pub->pkt->pkttype = PKT_SECRET_KEY; + pub->pkt->pkt.secret_key = copy_secret_key (NULL, sk); + break; + } + } + } + if ( !sec ) { + log_error ( "no corresponding secret subkey " + "for public subkey - removing\n" ); + /* better remove the public subkey in this case */ + delete_kbnode ( pub ); + deleting = 1; + any_deleted = 1; + } + } + else if ( deleting ) { + delete_kbnode (pub); + } + } - for(k=keyblock; k; k = k->next ) { - if( k->pkt->pkttype == PKT_PUBLIC_KEY - || k->pkt->pkttype == PKT_PUBLIC_SUBKEY ) - { - if( !pk->pubkey_algo - || pk->pubkey_algo == k->pkt->pkt.public_key->pubkey_algo ) - return k; - } + if ( any_deleted ) { + /* because we have not deleted the root node, we don't need to + * update the pubblock */ + pub = pubblock; + commit_kbnode ( &pubblock ); + assert ( pub == pubblock ); } - return NULL; } -static KBNODE -find_first_sk( KBNODE keyblock, PKT_secret_key *sk ) + + + +/************************************************ + ************* Find stuff *********************** + ************************************************/ + +static int +find_by_name( KBNODE keyblock, const char *name, + int mode, byte *namehash ) { KBNODE k; for(k=keyblock; k; k = k->next ) { - if( k->pkt->pkttype == PKT_SECRET_KEY - || k->pkt->pkttype == PKT_SECRET_SUBKEY ) - { - if( !sk->pubkey_algo - || sk->pubkey_algo == k->pkt->pkt.secret_key->pubkey_algo ) - return k; - } + if( k->pkt->pkttype == PKT_USER_ID + && !compare_name( k->pkt->pkt.user_id->name, + k->pkt->pkt.user_id->len, name, mode)) { + /* we found a matching name, look for the key */ + if( k->pkt->pkt.user_id->photo ) { + /* oops: this can never happen */ + 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 ); + } + return 1; + } } - return NULL; + + return 0; } + static KBNODE -find_by_fpr( KBNODE keyblock, PKT_public_key *pk, const char *name, int mode ) +find_by_keyid( KBNODE keyblock, u32 *keyid, int mode ) { KBNODE k; for(k=keyblock; k; k = k->next ) { if( k->pkt->pkttype == PKT_PUBLIC_KEY || k->pkt->pkttype == PKT_PUBLIC_SUBKEY ) { - byte afp[MAX_FINGERPRINT_LEN]; - size_t an; - - fingerprint_from_pk(k->pkt->pkt.public_key, afp, &an ); - - if( DBG_CACHE ) { - u32 aki[2]; - keyid_from_pk( k->pkt->pkt.public_key, aki ); - log_debug(" aki=%08lx%08lx algo=%d mode=%d an=%u\n", - (ulong)aki[0], (ulong)aki[1], - k->pkt->pkt.public_key->pubkey_algo, mode, - (unsigned)an ); + u32 aki[2]; + keyid_from_pk( k->pkt->pkt.public_key, aki ); + if( aki[1] == keyid[1] && ( mode == 10 || aki[0] == keyid[0] ) ) { + return k; /* found */ } - - if( an == mode - && !memcmp( afp, name, an) - && ( !pk->pubkey_algo - || pk->pubkey_algo == k->pkt->pkt.public_key->pubkey_algo) ) - return k; } } return NULL; } + + static KBNODE -find_by_fpr_sk( KBNODE keyblock, PKT_secret_key *sk, - const char *name, int mode ) +find_by_fpr( KBNODE keyblock, const char *name, int mode ) { KBNODE k; for(k=keyblock; k; k = k->next ) { - if( k->pkt->pkttype == PKT_SECRET_KEY - || k->pkt->pkttype == PKT_SECRET_SUBKEY ) { + if( k->pkt->pkttype == PKT_PUBLIC_KEY + || k->pkt->pkttype == PKT_PUBLIC_SUBKEY ) { byte afp[MAX_FINGERPRINT_LEN]; size_t an; - fingerprint_from_sk(k->pkt->pkt.secret_key, afp, &an ); - - if( DBG_CACHE ) { - u32 aki[2]; - keyid_from_sk( k->pkt->pkt.secret_key, aki ); - log_debug(" aki=%08lx%08lx algo=%d mode=%d an=%u\n", - (ulong)aki[0], (ulong)aki[1], - k->pkt->pkt.secret_key->pubkey_algo, mode, - (unsigned)an ); - } - - if( an == mode - && !memcmp( afp, name, an) - && ( !sk->pubkey_algo - || sk->pubkey_algo == k->pkt->pkt.secret_key->pubkey_algo) ) - return k; + fingerprint_from_pk(k->pkt->pkt.public_key, afp, &an ); + if( an == mode && !memcmp( afp, name, an) ) { + return k; + } } } return NULL; } -/**************** - * 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 +/* See see whether the key fits + * our requirements and in case we do not + * request a the primary key, we should select + * a suitable subkey. + * FIXME: Check against PGP 7 whether we still need a kludge + * to favor type 16 keys over type 20 keys when type 20 + * has not been explitely requested. + * Returns: True when a suitable key has been found. + * + * We have to distinguish four cases: + * 1. No usage and no primary key requested + * Examples for this case are that we have a keyID to be used + * for decrytion or verification. + * 2. No usage but primary key requested + * This is the case for all functions which work on an + * entire keyblock, e.g. for editing or listing + * 3. Usage and primary key requested + * FXME + * 4. Usage but no primary key requested + * FIXME + * FIXME: Tell what is going to happen here and something about the rationale + * */ + static int -has_expired( KBNODE node, u32 *mainkid, u32 cur_time ) +finish_lookup( GETKEY_CTX ctx, KBNODE foundk ) { - u32 expires = subkeys_expiretime( node, mainkid ); - return expires && expires <= cur_time; -} + KBNODE keyblock = ctx->keyblock; + KBNODE k; + #define USAGE_MASK (GCRY_PK_USAGE_SIGN|GCRY_PK_USAGE_ENCR) + unsigned int req_usage = ( ctx->req_usage & USAGE_MASK ); + u32 latest_date; + KBNODE latest_key; -static void -finish_lookup( KBNODE keyblock, PKT_public_key *pk, KBNODE k, byte *namehash, - int use_namehash, int primary ) -{ - assert( k->pkt->pkttype == PKT_PUBLIC_KEY - || k->pkt->pkttype == PKT_PUBLIC_SUBKEY ); + assert( !foundk || foundk->pkt->pkttype == PKT_PUBLIC_KEY + || foundk->pkt->pkttype == PKT_PUBLIC_SUBKEY ); assert( keyblock->pkt->pkttype == PKT_PUBLIC_KEY ); - if( primary && !pk->pubkey_usage ) { - copy_public_key_new_namehash( pk, keyblock->pkt->pkt.public_key, - use_namehash? namehash:NULL); - merge_one_pk_and_selfsig( keyblock, keyblock, pk ); + + ctx->found_key = NULL; + + if ( DBG_CACHE ) + log_debug( "finish_lookup: checking %s (req_usage=%x)\n", + foundk? "one key":"all keys", req_usage); + + latest_date = 0; + latest_key = NULL; + /* We do check the subkeys only if we either have requested a specific + * usage or have not requested to get the primary key. */ + if ( (req_usage || !ctx->primary) + && (!foundk || foundk->pkt->pkttype == PKT_PUBLIC_SUBKEY) ) { + KBNODE nextk; + /* either start a loop or check just this one subkey */ + for (k=foundk?foundk:keyblock; k; k = nextk ) { + PKT_public_key *pk; + nextk = k->next; + if ( k->pkt->pkttype != PKT_PUBLIC_SUBKEY ) + continue; + if ( foundk ) + nextk = NULL; /* what a hack */ + pk = k->pkt->pkt.public_key; + if ( !pk->is_valid ) { + if (DBG_CACHE) + log_debug( "\tsubkey not valid\n"); + continue; + } + if ( pk->is_revoked ) { + if (DBG_CACHE) + log_debug( "\tsubkey has been revoked\n"); + continue; + } + if ( pk->has_expired ) { + if (DBG_CACHE) + log_debug( "\tsubkey has expired\n"); + continue; + } + + if ( req_usage && + !((pk->pubkey_usage&USAGE_MASK) & req_usage) ) { + if (DBG_CACHE) + log_debug( "\tusage does not match: want=%x have=%x\n", + req_usage, pk->pubkey_usage ); + continue; + } + + if (DBG_CACHE) + log_debug( "\tconsidering key created %lu\n", + (ulong)pk->created); + if ( pk->created > latest_date ) { + latest_date = pk->created; + latest_key = k; + } + } } - else { - if( primary && pk->pubkey_usage - && openpgp_pk_test_algo( k->pkt->pkt.public_key->pubkey_algo, - 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: - * try to use a type 16 subkey instead */ - if( pk->pubkey_usage == GCRY_PK_USAGE_ENCR ) { - for( k = save_k; k; k = k->next ) { - if( k->pkt->pkttype == PKT_PUBLIC_SUBKEY - && k->pkt->pkt.public_key->pubkey_algo - == GCRY_PK_ELG_E - && !openpgp_pk_test_algo( - k->pkt->pkt.public_key->pubkey_algo, - pk->pubkey_usage ) - && !has_expired(k, mainkid, cur_time) ) - break; - } - } - if( !k ) { - for(k = save_k ; k; k = k->next ) { - if( k->pkt->pkttype == PKT_PUBLIC_SUBKEY - && !openpgp_pk_test_algo( - k->pkt->pkt.public_key->pubkey_algo, - pk->pubkey_usage ) - && ( pk->pubkey_usage != GCRY_PK_USAGE_ENCR - || !has_expired( k, mainkid, cur_time ) ) - ) - break; - } - } - if( !k ) - k = save_k; - else - log_info(_("using secondary key %08lX " - "instead of primary key %08lX\n"), - (ulong)keyid_from_pk( k->pkt->pkt.public_key, NULL), - (ulong)keyid_from_pk( save_k->pkt->pkt.public_key, NULL) - ); - } - - copy_public_key_new_namehash( pk, k->pkt->pkt.public_key, - use_namehash? namehash:NULL); - merge_one_pk_and_selfsig( keyblock, k, pk ); + if ( !latest_key ) { + PKT_public_key *pk; + if (DBG_CACHE && !foundk ) + log_debug( "\tno suitable subkeys found - trying primary\n"); + pk = keyblock->pkt->pkt.public_key; + if ( !pk->is_valid ) { + if (DBG_CACHE) + log_debug( "\tprimary key not valid\n"); + } + else if ( pk->is_revoked ) { + if (DBG_CACHE) + log_debug( "\tprimary key has been revoked\n"); + } + else if ( pk->has_expired ) { + if (DBG_CACHE) + log_debug( "\tprimary key has expired\n"); + } + else if ( req_usage + && !((pk->pubkey_usage&USAGE_MASK) & req_usage) ) { + if (DBG_CACHE) + log_debug( "\tusage does not match: want=%x have=%x\n", + req_usage, pk->pubkey_usage ); + } + else { /* okay */ + if (DBG_CACHE) + log_debug( "\tprimary key may be used\n"); + latest_key = keyblock; + latest_date = pk->created; + } } -} - -static void -finish_lookup_sk( KBNODE keyblock, PKT_secret_key *sk, KBNODE k, int primary ) -{ - assert( k->pkt->pkttype == PKT_SECRET_KEY - || k->pkt->pkttype == PKT_SECRET_SUBKEY ); - assert( keyblock->pkt->pkttype == PKT_SECRET_KEY ); - if( primary && !sk->pubkey_usage ) { - copy_secret_key( sk, keyblock->pkt->pkt.secret_key ); + + if ( !latest_key ) { + if (DBG_CACHE) + log_debug("\tno suitable key found - giving up\n"); + return 0; } - else { - if( primary && sk->pubkey_usage - && openpgp_pk_test_algo( k->pkt->pkt.secret_key->pubkey_algo, - sk->pubkey_usage ) == GPGERR_WR_PUBKEY_ALGO ) { - /* if the usage is not correct, try to use a subkey */ - KBNODE save_k = k; - - k = NULL; - /* kludge for pgp 5: which doesn't accept type 20: - * try to use a type 16 subkey instead */ - if( sk->pubkey_usage == GCRY_PK_USAGE_ENCR ) { - for( k = save_k; k; k = k->next ) { - if( k->pkt->pkttype == PKT_SECRET_SUBKEY - && k->pkt->pkt.secret_key->pubkey_algo - == GCRY_PK_ELG_E - && !openpgp_pk_test_algo( - k->pkt->pkt.secret_key->pubkey_algo, - sk->pubkey_usage ) ) - break; - } - } - if( !k ) { - for(k = save_k ; k; k = k->next ) { - if( k->pkt->pkttype == PKT_SECRET_SUBKEY - && !openpgp_pk_test_algo( - k->pkt->pkt.secret_key->pubkey_algo, - sk->pubkey_usage ) ) - break; - } - } - if( !k ) - k = save_k; - else - log_info(_("using secondary key %08lX " - "instead of primary key %08lX\n"), - (ulong)keyid_from_sk( k->pkt->pkt.secret_key, NULL), - (ulong)keyid_from_sk( save_k->pkt->pkt.secret_key, NULL) - ); - } + if (DBG_CACHE) + log_debug( "\tusing key created %lu\n", (ulong)latest_date ); + + ctx->found_key = latest_key; - copy_secret_key( sk, k->pkt->pkt.secret_key ); + if ( latest_key != keyblock ) { + log_info(_("using secondary key %08lX " + "instead of primary key %08lX\n"), + (ulong)keyid_from_pk( latest_key->pkt->pkt.public_key, NULL), + (ulong)keyid_from_pk( keyblock->pkt->pkt.public_key, NULL) ); } -} + cache_user_id( keyblock ); + + return 1; /* found */ +} + static int -lookup_pk( GETKEY_CTX ctx, PKT_public_key *pk, KBNODE *ret_keyblock ) +lookup( GETKEY_CTX ctx, KBNODE *ret_keyblock, int secmode ) { int rc; - KBNODE k; int oldmode = set_packet_list_mode(0); byte namehash[20]; int use_namehash=0; + KBNODE secblock = NULL; /* helper */ if( !ctx->count ) /* first time */ - rc = enum_keyblocks( 0, &ctx->kbpos, &ctx->keyblock ); + rc = enum_keyblocks( secmode?5:0, &ctx->kbpos, &ctx->keyblock ); else rc = 0; if( !rc ) { while( !(rc = enum_keyblocks( 1, &ctx->kbpos, &ctx->keyblock )) ) { int n; getkey_item_t *item; - /* fixme: we don't enum the complete keyblock, but - * use the first match and then continue with the next keyblock - */ + + if ( secmode ) { + /* find the correspondig public key and use this + * this one for the selection process */ + u32 aki[2]; + KBNODE k = ctx->keyblock; + + if ( k->pkt->pkttype != PKT_SECRET_KEY ) + BUG(); + keyid_from_sk( k->pkt->pkt.secret_key, aki ); + k = get_pubkeyblock( aki ); + if( !k ) { + log_info(_("key %08lX: secret key without public key " + "- skipped\n"), (ulong)aki[1] ); + goto skip; + } + secblock = ctx->keyblock; + ctx->keyblock = k; + } + + /* loop over all the user ids we want to look for */ item = ctx->items; for(n=0; n < ctx->nitems; n++, item++ ) { - if( item->mode < 10 ) - k = find_by_name( ctx->keyblock, pk, - item->name, item->mode, - namehash, &use_namehash ); - else if( item->mode >= 10 && item->mode <= 12 ) - k = find_by_keyid( ctx->keyblock, pk, + KBNODE k = NULL; + int found = 0; + + if( item->mode < 10 ) { + found = find_by_name( ctx->keyblock, + item->name, item->mode, + namehash ); + use_namehash = found; + } + else if( item->mode >= 10 && item->mode <= 12 ) { + k = find_by_keyid( ctx->keyblock, item->keyid, item->mode ); - else if( item->mode == 15 ) - k = find_first( ctx->keyblock, pk ); - else if( item->mode == 16 || item->mode == 20 ) - k = find_by_fpr( ctx->keyblock, pk, + found = !!k; + } + else if( item->mode == 15 ) { + found = 1; + } + else if( item->mode == 16 || item->mode == 20 ) { + k = find_by_fpr( ctx->keyblock, item->fprint, item->mode ); + found = !!k; + } else BUG(); - if( k ) { - finish_lookup( ctx->keyblock, pk, k, namehash, - use_namehash, ctx->primary ); - goto found; + if( found ) { + /* this keyblock looks fine - do further investigation */ + merge_selfsigs ( ctx->keyblock ); + if ( finish_lookup( ctx, k ) ) { + if ( secmode ) { + merge_public_with_secret ( ctx->keyblock, + secblock); + release_kbnode (secblock); + secblock = NULL; + } + goto found; + } } } + skip: + /* release resources and try the next keyblock */ + if ( secmode ) { + release_kbnode( secblock ); + secblock = NULL; + } release_kbnode( ctx->keyblock ); ctx->keyblock = NULL; } - found: ; + found: + ; } if( rc && rc != -1 ) log_error("enum_keyblocks failed: %s\n", gpg_errstr(rc)); if( !rc ) { - if( ret_keyblock ) { - *ret_keyblock = ctx->keyblock; - ctx->keyblock = NULL; - } + *ret_keyblock = ctx->keyblock; /* return the keyblock */ + ctx->keyblock = NULL; } else if( rc == -1 ) - rc = GPGERR_NO_PUBKEY; + rc = secmode ? GPGERR_NO_SECKEY : GPGERR_NO_PUBKEY; + if ( secmode ) { + release_kbnode( secblock ); + secblock = NULL; + } release_kbnode( ctx->keyblock ); ctx->keyblock = NULL; set_packet_list_mode(oldmode); @@ -1786,7 +2127,7 @@ lookup_pk( GETKEY_CTX ctx, PKT_public_key *pk, KBNODE *ret_keyblock ) lkup_stats[ctx->mode].any = 1; if( !rc ) lkup_stats[ctx->mode].okay_count++; - else if ( rc == GPGERR_NO_PUBKEY ) + else if ( rc == GPGERR_NO_PUBKEY || rc == GPGERR_NO_SECKEY ) lkup_stats[ctx->mode].nokey_count++; else lkup_stats[ctx->mode].error_count++; @@ -1800,75 +2141,14 @@ lookup_pk( GETKEY_CTX ctx, PKT_public_key *pk, KBNODE *ret_keyblock ) -static int -lookup_sk( GETKEY_CTX ctx, PKT_secret_key *sk, KBNODE *ret_keyblock ) -{ - int rc; - KBNODE k; - int oldmode = set_packet_list_mode(0); - - if( !ctx->count ) /* first time */ - rc = enum_keyblocks( 5, &ctx->kbpos, &ctx->keyblock ); - else - rc = 0; - if( !rc ) { - while( !(rc = enum_keyblocks( 1, &ctx->kbpos, &ctx->keyblock )) ) { - int n; - getkey_item_t *item; - /* fixme: we don't enum the complete keyblock, but - * use the first match and then continue with the next keyblock - */ - /* loop over all the user ids we want to look for */ - item = ctx->items; - for(n=0; n < ctx->nitems; n++, item++ ) { - if( item->mode < 10 ) - k = find_by_name_sk( ctx->keyblock, sk, - item->name, item->mode ); - else if( item->mode >= 10 && item->mode <= 12 ) - k = find_by_keyid_sk( ctx->keyblock, sk, - item->keyid, item->mode ); - else if( item->mode == 15 ) - k = find_first_sk( ctx->keyblock, sk ); - else if( item->mode == 16 || item->mode == 20 ) - k = find_by_fpr_sk( ctx->keyblock, sk, - item->fprint, item->mode ); - else - BUG(); - if( k ) { - finish_lookup_sk( ctx->keyblock, sk, k, ctx->primary ); - goto found; - } - } - release_kbnode( ctx->keyblock ); - ctx->keyblock = NULL; - } - found: ; - } - if( rc && rc != -1 ) - log_error("enum_keyblocks failed: %s\n", gpg_errstr(rc)); - - if( !rc ) { - if( ret_keyblock ) { - *ret_keyblock = ctx->keyblock; - ctx->keyblock = NULL; - } - } - else if( rc == -1 ) - rc = GPGERR_NO_SECKEY; - - release_kbnode( ctx->keyblock ); - ctx->keyblock = NULL; - set_packet_list_mode(oldmode); - - ctx->last_rc = rc; - ctx->count++; - return rc; -} - /**************** - * fixme: replace by the generic function + * FIXME: Replace by the generic function + * It does not work as it is right now - it is used at + * 2 places: a) to get the key for an anonyous recipient + * b) to get the ultimately trusted keys. + * The a) usage might have some problems. * * Enumerate all primary secret keys. Caller must use these procedure: * 1) create a void pointer and initialize it to NULL @@ -1943,6 +2223,11 @@ enum_secret_keys( void **context, PKT_secret_key *sk, int with_subkeys ) } + +/********************************************* + *********** user ID printing helpers ******* + *********************************************/ + /**************** * Return a string with a printable representation of the user_id. * this string must be freed by m_free. @@ -1955,12 +2240,17 @@ get_user_id_string( u32 *keyid ) int pass=0; /* try it two times; second pass reads from key resources */ do { - for(r=user_id_db; r; r = r->next ) - if( r->keyid[0] == keyid[0] && r->keyid[1] == keyid[1] ) { - p = gcry_xmalloc( r->len + 10 ); - sprintf(p, "%08lX %.*s", (ulong)keyid[1], r->len, r->name ); - return p; - } + for(r=user_id_db; r; r = r->next ) { + keyid_list_t a; + for (a=r->keyids; a; a= a->next ) { + if( a->keyid[0] == keyid[0] && a->keyid[1] == keyid[1] ) { + p = gcry_xmalloc( r->len + 10 ); + sprintf(p, "%08lX %.*s", + (ulong)keyid[1], r->len, r->name ); + return p; + } + } + } } while( ++pass < 2 && !get_pubkey( NULL, keyid ) ); p = gcry_xmalloc( 15 ); sprintf(p, "%08lX [?]", (ulong)keyid[1] ); @@ -1987,13 +2277,18 @@ get_long_user_id_string( u32 *keyid ) int pass=0; /* try it two times; second pass reads from key resources */ do { - for(r=user_id_db; r; r = r->next ) - if( r->keyid[0] == keyid[0] && r->keyid[1] == keyid[1] ) { - p = gcry_xmalloc( r->len + 20 ); - sprintf(p, "%08lX%08lX %.*s", - (ulong)keyid[0], (ulong)keyid[1], r->len, r->name ); - return p; - } + for(r=user_id_db; r; r = r->next ) { + keyid_list_t a; + for (a=r->keyids; a; a= a->next ) { + if( a->keyid[0] == keyid[0] && a->keyid[1] == keyid[1] ) { + p = gcry_xmalloc( r->len + 20 ); + sprintf(p, "%08lX%08lX %.*s", + (ulong)keyid[0], (ulong)keyid[1], + r->len, r->name ); + return p; + } + } + } } while( ++pass < 2 && !get_pubkey( NULL, keyid ) ); p = gcry_xmalloc( 25 ); sprintf(p, "%08lX%08lX [?]", (ulong)keyid[0], (ulong)keyid[1] ); @@ -2009,13 +2304,17 @@ get_user_id( u32 *keyid, size_t *rn ) /* try it two times; second pass reads from key resources */ do { - for(r=user_id_db; r; r = r->next ) - if( r->keyid[0] == keyid[0] && r->keyid[1] == keyid[1] ) { - p = gcry_xmalloc( r->len ); - memcpy(p, r->name, r->len ); - *rn = r->len; - return p; - } + for(r=user_id_db; r; r = r->next ) { + keyid_list_t a; + for (a=r->keyids; a; a= a->next ) { + if( a->keyid[0] == keyid[0] && a->keyid[1] == keyid[1] ) { + p = gcry_xmalloc( r->len ); + memcpy(p, r->name, r->len ); + *rn = r->len; + return p; + } + } + } } while( ++pass < 2 && !get_pubkey( NULL, keyid ) ); p = gcry_xstrdup( _("[User id not found]") ); *rn = strlen(p); @@ -179,6 +179,7 @@ enum cmd_and_opt_values { aNull = 0, oDisableCipherAlgo, oDisablePubkeyAlgo, oAllowNonSelfsignedUID, + oAllowFreeformUID, oNoLiteral, oSetFilesize, oHonorHttpProxy, @@ -188,7 +189,9 @@ enum cmd_and_opt_values { aNull = 0, oNoRandomSeedFile, oNoAutoKeyRetrieve, oUseAgent, - oEmu3DESS2KBug, /* will be removed in 1.1 */ + oMergeOnly, + oTryAllSecrets, + oTrustedKey, oEmuMDEncodeBug, aTest }; @@ -290,6 +293,7 @@ static ARGPARSE_OPTS opts[] = { { oCompletesNeeded, "completes-needed", 1, "@"}, { oMarginalsNeeded, "marginals-needed", 1, "@"}, { oMaxCertDepth, "max-cert-depth", 1, "@" }, + { oTrustedKey, "trusted-key", 2, N_("|KEYID|ulimately trust this key")}, { oLoadExtension, "load-extension" ,2, N_("|FILE|load extension module FILE")}, { oRFC1991, "rfc1991", 0, N_("emulate the mode described in RFC1991")}, { oOpenPGP, "openpgp", 0, N_("set all packet, cipher and digest options to OpenPGP behavior")}, @@ -362,6 +366,7 @@ static ARGPARSE_OPTS opts[] = { { oDisableCipherAlgo, "disable-cipher-algo", 2, "@" }, { oDisablePubkeyAlgo, "disable-pubkey-algo", 2, "@" }, { oAllowNonSelfsignedUID, "allow-non-selfsigned-uid", 0, "@" }, + { oAllowFreeformUID, "allow-freeform-uid", 0, "@" }, { oNoLiteral, "no-literal", 0, "@" }, { oSetFilesize, "set-filesize", 20, "@" }, { oHonorHttpProxy,"honor-http-proxy", 0, "@" }, @@ -370,7 +375,8 @@ static ARGPARSE_OPTS opts[] = { { oIgnoreTimeConflict, "ignore-time-conflict", 0, "@" }, { oNoRandomSeedFile, "no-random-seed-file", 0, "@" }, { oNoAutoKeyRetrieve, "no-auto-key-retrieve", 0, "@" }, - { oEmu3DESS2KBug, "emulate-3des-s2k-bug", 0, "@"}, + { oMergeOnly, "merge-only", 0, "@" }, + { oTryAllSecrets, "try-all-secrets", 0, "@" }, { oEmuMDEncodeBug, "emulate-md-encode-bug", 0, "@"}, {0} }; @@ -601,6 +607,7 @@ main( int argc, char **argv ) char **orig_argv; const char *fname; char *username; + int may_coredump; STRLIST sl, remusr= NULL, locusr=NULL; STRLIST nrings=NULL, sec_nrings=NULL; armor_filter_context_t afx; @@ -642,7 +649,7 @@ main( int argc, char **argv ) } gcry_control( GCRYCTL_USE_SECURE_RNDPOOL ); - disable_core_dumps(); + may_coredump = disable_core_dumps(); init_signals(); create_dotlock(NULL); /* register locking cleanup */ i18n_init(); @@ -653,8 +660,8 @@ main( int argc, char **argv ) opt.def_digest_algo = 0; opt.def_compress_algo = 2; 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; opt.completes_needed = 1; opt.marginals_needed = 3; opt.max_cert_depth = 5; @@ -666,11 +673,7 @@ main( int argc, char **argv ) opt.homedir = getenv("GNUPGHOME"); #endif if( !opt.homedir || !*opt.homedir ) { - #ifdef HAVE_DRIVE_LETTERS - opt.homedir = "c:/gnupg-test"; - #else - opt.homedir = "~/.gnupg-test"; - #endif + opt.homedir = GNUPG_HOMEDIR; } /* check whether we have a config file on the commandline */ @@ -801,7 +804,7 @@ main( int argc, char **argv ) case oArmor: opt.armor = 1; opt.no_armor=0; break; case oOutput: opt.outfile = pargs.r.ret_str; break; case oQuiet: opt.quiet = 1; break; - case oNoTTY: opt.quiet = 1; tty_no_terminal(1); break; + case oNoTTY: tty_no_terminal(1); break; case oDryRun: opt.dry_run = 1; break; case oInteractive: opt.interactive = 1; break; case oVerbose: @@ -894,7 +897,6 @@ main( int argc, char **argv ) 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: @@ -965,6 +967,7 @@ main( int argc, char **argv ) } break; case oAllowNonSelfsignedUID: opt.allow_non_selfsigned_uid = 1; break; + case oAllowFreeformUID: opt.allow_freeform_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; @@ -973,6 +976,9 @@ main( int argc, char **argv ) case oIgnoreTimeConflict: opt.ignore_time_conflict = 1; break; case oNoRandomSeedFile: use_random_seed = 0; break; case oNoAutoKeyRetrieve: opt.auto_key_retrieve = 0; break; + case oMergeOnly: opt.merge_only = 1; break; + case oTryAllSecrets: opt.try_all_secrets = 1; break; + case oTrustedKey: register_trusted_key( pargs.r.ret_str ); break; default : pargs.err = configfp? 1:2; break; } @@ -1001,6 +1007,10 @@ main( int argc, char **argv ) log_info("used in a production environment or with production keys!\n"); } #endif + + if( may_coredump && !opt.quiet ) + log_info(_("WARNING: program may create a core file!\n")); + if (opt.no_literal) { log_info(_("NOTE: %s is not for normal use!\n"), "--no-literal"); if (opt.textmode) @@ -1406,9 +1416,13 @@ main( int argc, char **argv ) case aPrimegen: - { int mode = argc < 2 ? 0 : atoi(*argv); + { + #if 1 + log_error( "command is currently not implemented\n"); + #else + /* FIXME: disabled until we have an API to create primes */ + int mode = argc < 2 ? 0 : atoi(*argv); - #if 0 /* FIXME: disabled until we have an API to create primes */ if( mode == 1 && argc == 2 ) { mpi_print( stdout, generate_public_prime( atoi(argv[1]) ), 1); } @@ -1435,9 +1449,9 @@ main( int argc, char **argv ) mpi_release(g); } else - #endif wrong_args("--gen-prime mode bits [qbits] "); putchar('\n'); + #endif } break; diff --git a/g10/import.c b/g10/import.c index 578082577..94b83f080 100644 --- a/g10/import.c +++ b/g10/import.c @@ -50,6 +50,7 @@ static struct { ulong secret_read; ulong secret_imported; ulong secret_dups; + ulong skipped_new_keys; } stats; @@ -201,6 +202,9 @@ print_stats() { if( !opt.quiet ) { log_info(_("Total number processed: %lu\n"), stats.count ); + if( stats.skipped_new_keys ) + log_info(_(" skipped new keys: %lu\n"), + stats.skipped_new_keys ); if( stats.no_user_id ) log_info(_(" w/o user IDs: %lu\n"), stats.no_user_id ); if( stats.imported || stats.imported_rsa ) { @@ -413,6 +417,13 @@ import_one( const char *fname, KBNODE keyblock, int fast ) log_error( _("key %08lX: public key not found: %s\n"), (ulong)keyid[1], gpg_errstr(rc)); } + else if ( rc && opt.merge_only ) { + if( opt.verbose ) + log_info( _("key %08lX: new key - skipped\n"), (ulong)keyid[1] ); + rc = 0; + fast = 1; /* so that we don't get into the trustdb update */ + stats.skipped_new_keys++; + } else if( rc ) { /* insert this key */ /* get default resource */ if( get_keyblock_handle( NULL, 0, &kbpos ) ) { @@ -583,7 +594,7 @@ import_secret_one( const char *fname, KBNODE keyblock ) /* do we have this key already in one of our secrings ? */ rc = seckey_available( keyid ); - if( rc == GPGERR_NO_SECKEY ) { /* simply insert this key */ + if( rc == GPGERR_NO_SECKEY && !opt.merge_only ) { /* simply insert this key */ /* get default resource */ if( get_keyblock_handle( NULL, 1, &kbpos ) ) { log_error("no default secret keyring\n"); diff --git a/g10/keydb.h b/g10/keydb.h index 75cb3d4cc..830a7db3c 100644 --- a/g10/keydb.h +++ b/g10/keydb.h @@ -31,6 +31,15 @@ #define MAX_FINGERPRINT_LEN 20 +#define IS_KEY_SIG(s) ((s)->sig_class == 0x1f) +#define IS_UID_SIG(s) (((s)->sig_class & ~3) == 0x10) +#define IS_SUBKEY_SIG(s) ((s)->sig_class == 0x18) +#define IS_KEY_REV(s) ((s)->sig_class == 0x20) +#define IS_UID_REV(s) ((s)->sig_class == 0x30) +#define IS_SUBKEY_REV(s) ((s)->sig_class == 0x28) + + + struct getkey_ctx_s; typedef struct getkey_ctx_s *GETKEY_CTX; diff --git a/g10/keyedit.c b/g10/keyedit.c index daf4fb41d..b7e836711 100644 --- a/g10/keyedit.c +++ b/g10/keyedit.c @@ -592,7 +592,7 @@ keyedit_menu( const char *username, STRLIST locusr, STRLIST commands, { 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_("debug") , cmdDEBUG , 0,0,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") }, diff --git a/g10/keygen.c b/g10/keygen.c index 8e29090c3..fc3b2cf8c 100644 --- a/g10/keygen.c +++ b/g10/keygen.c @@ -136,8 +136,8 @@ keygen_add_std_prefs( PKT_signature *sig, void *opaque ) keygen_add_key_expire( sig, opaque ); buf[0] = GCRY_CIPHER_TWOFISH; - buf[1] = GCRY_CIPHER_BLOWFISH; - buf[2] = GCRY_CIPHER_CAST5; + buf[1] = GCRY_CIPHER_CAST5; + buf[2] = GCRY_CIPHER_BLOWFISH; build_sig_subpkt( sig, SIGSUBPKT_PREF_SYM, buf, 3 ); buf[0] = GCRY_MD_RMD160; @@ -889,7 +889,11 @@ ask_user_id( int mode ) aname = cpr_get("keygen.name",_("Real name: ")); trim_spaces(aname); cpr_kill_prompt(); - if( strpbrk( aname, "<([])>" ) ) + + if( opt.allow_freeform_uid ) + break; + + if( strpbrk( aname, "<>" ) ) tty_printf(_("Invalid character in name\n")); else if( isdigit(*aname) ) tty_printf(_("Name may not start with a digit\n")); diff --git a/g10/keyid.c b/g10/keyid.c index 14406b60b..fb652e7b2 100644 --- a/g10/keyid.c +++ b/g10/keyid.c @@ -166,11 +166,17 @@ keyid_from_sk( PKT_secret_key *sk, u32 *keyid ) if( !keyid ) keyid = dummy_keyid; - if( sk->version < 4 && is_RSA(sk->pubkey_algo) ) { + if( sk->keyid[0] || sk->keyid[1] ) { + keyid[0] = sk->keyid[0]; + keyid[1] = sk->keyid[1]; + } + else if( sk->version < 4 && is_RSA(sk->pubkey_algo) ) { if( pubkey_get_npkey(sk->pubkey_algo) ) v3_keyid( sk->skey[0], keyid ); /* take n */ else keyid[0] = keyid[1] = 0; + sk->keyid[0] = keyid[0]; + sk->keyid[1] = keyid[1]; } else { const byte *dp; @@ -180,6 +186,8 @@ keyid_from_sk( PKT_secret_key *sk, u32 *keyid ) keyid[0] = dp[12] << 24 | dp[13] << 16 | dp[14] << 8 | dp[15] ; keyid[1] = dp[16] << 24 | dp[17] << 16 | dp[18] << 8 | dp[19] ; gcry_md_close(md); + sk->keyid[0] = keyid[0]; + sk->keyid[1] = keyid[1]; } return keyid[1]; diff --git a/g10/main.h b/g10/main.h index 8e2436c70..d199c5484 100644 --- a/g10/main.h +++ b/g10/main.h @@ -51,7 +51,7 @@ char *make_radix64_string( const byte *data, size_t len ); /*-- misc.c --*/ void trap_unaligned(void); -void disable_core_dumps(void); +int disable_core_dumps(void); u16 checksum_u16( unsigned n ); u16 checksum( byte *p, unsigned n ); u16 checksum_mpi( MPI a ); @@ -65,6 +65,7 @@ int mpi_print( FILE *fp, MPI a, int mode ); int openpgp_cipher_test_algo( int algo ); int openpgp_pk_test_algo( int algo, unsigned int usage_flags ); +int openpgp_pk_algo_usage ( int algo ); int openpgp_md_test_algo( int algo ); int pubkey_get_npkey( int algo ); diff --git a/g10/misc.c b/g10/misc.c index 44a6f76b1..a62a04766 100644 --- a/g10/misc.c +++ b/g10/misc.c @@ -68,22 +68,24 @@ trap_unaligned(void) #endif -void + +int disable_core_dumps() { - #ifndef HAVE_DOSISH_SYSTEM + #ifdef HAVE_DOSISH_SYSTEM + return 0; + #else #ifdef HAVE_SETRLIMIT struct rlimit limit; limit.rlim_cur = 0; limit.rlim_max = 0; if( !setrlimit( RLIMIT_CORE, &limit ) ) - return; - if( errno != EINVAL ) + return 0; + if( errno != EINVAL && errno != ENOSYS ) log_fatal(_("can't disable core dumps: %s\n"), strerror(errno) ); #endif - if( !opt.quiet ) - log_info(_("WARNING: program may create a core file!\n")); + return 1; #endif } @@ -318,15 +320,6 @@ print_pubkey_algo_note( int algo ) { if( algo >= 100 && algo <= 110 ) no_exp_algo(); - else if( is_RSA( algo ) ) { - static int did_note = 0; - - if( !did_note ) { - did_note = 1; - log_info(_("RSA keys are deprecated; please consider " - "creating a new key and use this key in the future\n")); - } - } } void @@ -362,7 +355,7 @@ print_digest_algo_note( int algo ) /**************** * Wrapper around the libgcrypt function with addional checks on - * openPGP contrainst for the algo ID. + * openPGP contraints for the algo ID. */ int openpgp_cipher_test_algo( int algo ) @@ -382,6 +375,40 @@ openpgp_pk_test_algo( int algo, unsigned int usage_flags ) return gcry_pk_algo_info( algo, GCRYCTL_TEST_ALGO, NULL, &n ); } +int +openpgp_pk_algo_usage ( int algo ) +{ + int usage = 0; + + /* some are hardwired */ + switch ( algo ) { + case GCRY_PK_RSA: + usage = GCRY_PK_USAGE_SIGN | GCRY_PK_USAGE_ENCR; + break; + case GCRY_PK_RSA_E: + usage = GCRY_PK_USAGE_ENCR; + break; + case GCRY_PK_RSA_S: + usage = GCRY_PK_USAGE_SIGN; + break; + case GCRY_PK_ELG_E: + usage = GCRY_PK_USAGE_ENCR; + break; + case GCRY_PK_DSA: + usage = GCRY_PK_USAGE_SIGN; + break; + case GCRY_PK_ELG: + usage = GCRY_PK_USAGE_SIGN | GCRY_PK_USAGE_ENCR; + break; + default: + usage = gcry_pk_algo_info ( algo, GCRYCTL_GET_ALGO_USAGE, + NULL, NULL); + } + return usage; + +} + + int openpgp_md_test_algo( int algo ) diff --git a/g10/openfile.c b/g10/openfile.c index 47dca0e76..340cfd6fa 100644 --- a/g10/openfile.c +++ b/g10/openfile.c @@ -70,6 +70,11 @@ overwrite_filep( const char *fname ) if( access( fname, F_OK ) ) return 1; /* does not exist */ +#ifndef HAVE_DOSISH_SYSTEM + if ( !strcmp ( fname, "/dev/null" ) ) + return 1; /* does not do any harm */ +#endif + /* fixme: add some backup stuff in case of overwrite */ if( opt.answer_yes ) return 1; @@ -105,6 +110,12 @@ make_outfile_name( const char *iname ) buf[n-4] = 0; return buf; } + else if( n > 5 && !CMP_FILENAME(iname+n-5,".sign") ) { + char *buf = gcry_xstrdup( iname ); + buf[n-5] = 0; + return buf; + } + log_info(_("%s: unknown suffix\n"), iname ); return NULL; @@ -241,6 +252,7 @@ open_sigfile( const char *iname ) if( iname && !(*iname == '-' && !iname[1]) ) { len = strlen(iname); if( len > 4 && ( !strcmp(iname + len - 4, ".sig") + || ( len > 5 && !strcmp(iname + len - 5, ".sign") ) || !strcmp(iname + len - 4, ".asc")) ) { char *buf; buf = gcry_xstrdup(iname); @@ -305,10 +317,24 @@ copy_options_file( const char *destdir ) void try_make_homedir( const char *fname ) { + const char *defhome = GNUPG_HOMEDIR; + + /* Create the directory only if the supplied directory name + * is the same as the default one. This way we avoid to create + * arbitrary directories when a non-default homedirectory is used. + * To cope with HOME, we do compare only the suffix if we see that + * the default homedir does start with a tilde. + */ if( opt.dry_run ) return; - if( strlen(fname) >= 7 - && !strcmp(fname+strlen(fname)-7, "/.gnupg" ) ) { + + if ( ( *defhome == '~' + && ( strlen(fname) >= strlen (defhome+1) + && !strcmp(fname+strlen(defhome+1)-strlen(defhome+1), + defhome+1 ) )) + || ( *defhome != '~' + && !compare_filenames( fname, defhome ) ) + ) { if( mkdir( fname, S_IRUSR|S_IWUSR|S_IXUSR ) ) log_fatal( _("%s: can't create directory: %s\n"), fname, strerror(errno) ); @@ -321,3 +347,5 @@ try_make_homedir( const char *fname ) } } + + diff --git a/g10/options.h b/g10/options.h index 218e97694..b7ef09fe7 100644 --- a/g10/options.h +++ b/g10/options.h @@ -84,6 +84,7 @@ struct { const char *set_policy_url; int use_embedded_filename; int allow_non_selfsigned_uid; + int allow_freeform_uid; int no_literal; ulong set_filesize; int honor_http_proxy; @@ -92,6 +93,8 @@ struct { int command_fd; int auto_key_retrieve; int use_agent; + int merge_only; + int try_all_secrets; } opt; diff --git a/g10/packet.h b/g10/packet.h index f6da15e17..03fa2ca6e 100644 --- a/g10/packet.h +++ b/g10/packet.h @@ -128,8 +128,15 @@ typedef struct { byte hdrbytes; /* number of header bytes */ byte version; byte pubkey_algo; /* algorithm used for public key scheme */ - byte pubkey_usage; /* for now only used to pass it to getkey() */ + byte pubkey_usage; /* the actual allowed usage as set by getkey() */ + u32 created; /* according to the self-signature */ + byte req_usage; /* hack to pass a request to getkey() */ + byte req_algo; /* Ditto */ + u32 has_expired; /* set to the expiration date if expired */ + int is_revoked; /* key has been revoked */ + int is_valid; /* key (especially subkey) is valid */ ulong local_id; /* internal use, valid if > 0 */ + u32 main_keyid[2]; /* keyid of the primary key */ u32 keyid[2]; /* calculated by keyid_from_pk() */ byte *namehash; /* if != NULL: found by this name */ MPI pkey[GNUPG_MAX_NPKEY]; @@ -142,6 +149,14 @@ typedef struct { byte version; byte pubkey_algo; /* algorithm used for public key scheme */ byte pubkey_usage; + u32 created; /* according to the self-signature */ + byte req_usage; + byte req_algo; + u32 has_expired; /* set to the expiration date if expired */ + int is_revoked; /* key has been revoked */ + int is_valid; /* key (especially subkey) is valid */ + u32 main_keyid[2]; /* keyid of the primary key */ + u32 keyid[2]; byte is_primary; byte is_protected; /* The secret info is protected and must */ /* be decrypted before use, the protected */ @@ -169,6 +184,10 @@ typedef struct { 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 */ + int help_key_usage; + u32 help_key_expire; + int is_primary; + u32 created; /* according to the self-signature */ char name[1]; } PKT_user_id; @@ -322,6 +341,8 @@ PKT_public_key *copy_public_key( PKT_public_key *d, PKT_public_key *s ); PKT_public_key *copy_public_key_new_namehash( PKT_public_key *d, PKT_public_key *s, const byte *namehash ); +void copy_public_parts_to_secret_key( PKT_public_key *pk, + PKT_secret_key *sk ); PKT_secret_key *copy_secret_key( PKT_secret_key *d, PKT_secret_key *s ); PKT_signature *copy_signature( PKT_signature *d, PKT_signature *s ); PKT_user_id *copy_user_id( PKT_user_id *d, PKT_user_id *s ); diff --git a/g10/parse-packet.c b/g10/parse-packet.c index 867f93d1d..3cabe77b1 100644 --- a/g10/parse-packet.c +++ b/g10/parse-packet.c @@ -700,6 +700,17 @@ dump_sig_subpkt( int hashed, int type, int critical, const char *p=NULL; int i; + /* The CERT has warning out with explains how to use GNUPG to + * detect the ARRs - we print our old message here when it is a faked + * ARR and add an additional notice */ + if ( type == SIGSUBPKT_ARR && !hashed ) { + printf("\tsubpkt %d len %u (additional recipient request)\n" + "WARNING: PGP versions > 5.0 and < 6.5.8 will automagically " + "encrypt to this key and thereby reveal the plaintext to " + "the owner of this ARR key. Detailed info follows:\n", + type, (unsigned)length ); + } + printf("\t%s%ssubpkt %d len %u (", /*)*/ critical ? "critical ":"", hashed ? "hashed ":"", type, (unsigned)length ); @@ -737,9 +748,6 @@ dump_sig_subpkt( int hashed, int type, int critical, printf("key expires after %s", strtimevalue( buffer_to_u32(buffer) ) ); break; - case SIGSUBPKT_ARR: - p = "additional recipient request"; - break; case SIGSUBPKT_PREF_SYM: fputs("pref-sym-algos:", stdout ); for( i=0; i < length; i++ ) @@ -809,8 +817,10 @@ dump_sig_subpkt( int hashed, int type, int critical, print_string( stdout, buffer, length, ')' ); break; case SIGSUBPKT_KEY_FLAGS: - p = "key flags"; - break; + fputs ( "key flags:", stdout ); + for( i=0; i < length; i++ ) + printf(" %02X", buffer[i] ); + break; case SIGSUBPKT_SIGNERS_UID: p = "signer's user ID"; break; @@ -821,6 +831,16 @@ dump_sig_subpkt( int hashed, int type, int critical, p = ")"; } break; + case SIGSUBPKT_ARR: + fputs("Big Brother's key (ignored): ", stdout ); + if( length < 22 ) + p = "[too short]"; + else { + printf("c=%02x a=%d f=", buffer[0], buffer[1] ); + for( i=2; i < length; i++ ) + printf("%02X", buffer[i] ); + } + break; case SIGSUBPKT_PRIV_ADD_SIG: p = "signs additional user ID"; break; @@ -846,6 +866,8 @@ parse_one_sig_subpkt( const byte *buffer, size_t n, int type ) if( n < 4 ) break; return 0; + case SIGSUBPKT_KEY_FLAGS: + return 0; case SIGSUBPKT_EXPORTABLE: if( !n ) break; @@ -867,6 +889,10 @@ parse_one_sig_subpkt( const byte *buffer, size_t n, int type ) case SIGSUBPKT_PREF_COMPR: case SIGSUBPKT_POLICY: return 0; + case SIGSUBPKT_PRIMARY_UID: + if ( n != 1 ) + break; + return 0; case SIGSUBPKT_PRIV_ADD_SIG: /* because we use private data, we check the GNUPG marker */ if( n < 24 ) @@ -897,6 +923,7 @@ can_handle_critical( const byte *buffer, size_t n, int type ) case SIGSUBPKT_PREF_SYM: case SIGSUBPKT_PREF_HASH: case SIGSUBPKT_PREF_COMPR: + case SIGSUBPKT_KEY_FLAGS: return 1; case SIGSUBPKT_POLICY: /* Is it enough to show the policy? */ @@ -1288,7 +1315,8 @@ parse_key( IOBUF inp, int pkttype, unsigned long pktlen, sk->version = version; sk->is_primary = pkttype == PKT_SECRET_KEY; sk->pubkey_algo = algorithm; - sk->pubkey_usage = 0; /* not yet used */ + sk->req_usage = 0; + sk->pubkey_usage = 0; /* will be set by getkey functions */ } else { PKT_public_key *pk = pkt->pkt.public_key; @@ -1298,7 +1326,9 @@ parse_key( IOBUF inp, int pkttype, unsigned long pktlen, pk->hdrbytes = hdrlen; pk->version = version; pk->pubkey_algo = algorithm; - pk->pubkey_usage = 0; /* not yet used */ + pk->req_usage = 0; + pk->pubkey_usage = 0; /* will be set bey getkey functions */ + pk->is_revoked = 0; pk->keyid[0] = 0; pk->keyid[1] = 0; } diff --git a/g10/passphrase.c b/g10/passphrase.c index 97ed3d189..6b06df72e 100644 --- a/g10/passphrase.c +++ b/g10/passphrase.c @@ -640,7 +640,6 @@ hash_passphrase( DEK *dek, char *pw, STRING2KEY *s2k, int create ) else { gcry_md_write( md, s2k->salt, 8 ); count -= 8; - assert( count >= 0 ); gcry_md_write( md, pw, count ); } } diff --git a/g10/pkclist.c b/g10/pkclist.c index 8e61ffdd0..d827ce653 100644 --- a/g10/pkclist.c +++ b/g10/pkclist.c @@ -814,13 +814,12 @@ build_pk_list( STRLIST remusr, PK_LIST *ret_pk_list, unsigned use ) any_recipients = 1; else if( (use & GCRY_PK_USAGE_ENCR) && !opt.no_encrypt_to ) { pk = gcry_xcalloc( 1, sizeof *pk ); - pk->pubkey_usage = use; + pk->req_usage = use; if( (rc = get_pubkey_byname( NULL, pk, rov->d, NULL )) ) { free_public_key( pk ); pk = NULL; log_error(_("%s: skipped: %s\n"), rov->d, gpg_errstr(rc) ); } else if( !(rc=openpgp_pk_test_algo(pk->pubkey_algo, use )) ) { - /* Skip the actual key if the key is already present * in the list */ if (key_present_in_pk_list(pk_list, pk) == 0) { @@ -871,7 +870,7 @@ build_pk_list( STRLIST remusr, PK_LIST *ret_pk_list, unsigned use ) if( pk ) free_public_key( pk ); pk = gcry_xcalloc( 1, sizeof *pk ); - pk->pubkey_usage = use; + pk->req_usage = use; rc = get_pubkey_byname( NULL, pk, answer, NULL ); if( rc ) tty_printf(_("No such user ID.\n")); @@ -937,7 +936,7 @@ build_pk_list( STRLIST remusr, PK_LIST *ret_pk_list, unsigned use ) } else if( !any_recipients && (def_rec = default_recipient()) ) { pk = gcry_xcalloc( 1, sizeof *pk ); - pk->pubkey_usage = use; + pk->req_usage = use; rc = get_pubkey_byname( NULL, pk, def_rec, NULL ); if( rc ) log_error(_("unknown default recipient `%s'\n"), def_rec ); @@ -962,7 +961,7 @@ build_pk_list( STRLIST remusr, PK_LIST *ret_pk_list, unsigned use ) continue; /* encrypt-to keys are already handled */ pk = gcry_xcalloc( 1, sizeof *pk ); - pk->pubkey_usage = use; + pk->req_usage = use; if( (rc = get_pubkey_byname( NULL, pk, remusr->d, NULL )) ) { free_public_key( pk ); pk = NULL; log_error(_("%s: skipped: %s\n"), remusr->d, gpg_errstr(rc) ); @@ -1033,8 +1032,6 @@ static int algo_available( int preftype, int algo ) { if( preftype == PREFTYPE_SYM ) { - if( algo == GCRY_CIPHER_TWOFISH ) - return 0; /* we don't want to generate Twofish messages for now*/ return algo && !openpgp_cipher_test_algo( algo ); } else if( preftype == PREFTYPE_HASH ) { diff --git a/g10/pubkey-enc.c b/g10/pubkey-enc.c index e6cbec4a0..646aca017 100644 --- a/g10/pubkey-enc.c +++ b/g10/pubkey-enc.c @@ -106,14 +106,11 @@ get_session_key( PKT_pubkey_enc *k, DEK *dek ) PKT_secret_key *sk = NULL; int rc; - if( is_RSA(k->pubkey_algo) ) /* warn about that */ - write_status(STATUS_RSA_OR_IDEA); - rc = openpgp_pk_test_algo( k->pubkey_algo, 0 ); if( rc ) goto leave; - if( k->keyid[0] || k->keyid[1] ) { + if( (k->keyid[0] || k->keyid[1]) && !opt.try_all_secrets ) { sk = gcry_xcalloc( 1, sizeof *sk ); sk->pubkey_algo = k->pubkey_algo; /* we want a pubkey with this algo*/ if( !(rc = get_seckey( sk, k->keyid )) ) diff --git a/g10/ringedit.c b/g10/ringedit.c index 3a29654d2..311cf41fc 100644 --- a/g10/ringedit.c +++ b/g10/ringedit.c @@ -1114,7 +1114,13 @@ cmp_seckey( PKT_secret_key *req_sk, PKT_secret_key *sk ) n = pubkey_get_nskey( req_sk->pubkey_algo ); for(i=0; i < n; i++ ) { - if( mpi_cmp( req_sk->skey[i], sk->skey[i] ) ) + /* Note: because v4 protected keys have nothing in the + * mpis except for the first one, we skip all NULL MPIs. + * This might not be always correct in cases where the both + * keys do not match in their secret parts but we can ignore that + * because the need for this function is quite ugly. */ + if( req_sk->skey[1] && sk->skey[i] + && mpi_cmp( req_sk->skey[i], sk->skey[i] ) ) return -1; } return 0; diff --git a/g10/seckey-cert.c b/g10/seckey-cert.c index 342dde06c..0be514eb3 100644 --- a/g10/seckey-cert.c +++ b/g10/seckey-cert.c @@ -100,10 +100,8 @@ do_check( PKT_secret_key *sk ) keyid_from_sk( sk, keyid ); keyid[2] = keyid[3] = 0; if( !sk->is_primary ) { - PKT_secret_key *sk2 = gcry_xcalloc( 1, sizeof *sk2 ); - if( !get_primary_seckey( sk2, keyid ) ) - keyid_from_sk( sk2, keyid+2 ); - free_secret_key( sk2 ); + keyid[2] = sk->main_keyid[0]; + keyid[3] = sk->main_keyid[1]; } dek = passphrase_to_dek( keyid, sk->pubkey_algo, sk->protect.algo, &sk->protect.s2k, 0 ); @@ -128,11 +126,14 @@ do_check( PKT_secret_key *sk ) size_t ndata; unsigned int ndatabits; byte *p, *data; + u16 csumc = 0; i = pubkey_get_npkey(sk->pubkey_algo); assert( gcry_mpi_get_flag( sk->skey[i], GCRYMPI_FLAG_OPAQUE ) ); p = gcry_mpi_get_opaque( sk->skey[i], &ndatabits ); ndata = (ndatabits+7)/8; + if ( ndata > 1 ) + csumc = p[ndata-2] << 8 | p[ndata-1]; data = gcry_xmalloc_secure( ndata ); gcry_cipher_decrypt( cipher_hd, data, ndata, p, ndata ); mpi_release( sk->skey[i] ); sk->skey[i] = NULL ; @@ -145,6 +146,10 @@ do_check( PKT_secret_key *sk ) else { csum = checksum( data, ndata-2); sk->csum = data[ndata-2] << 8 | data[ndata-1]; + if ( sk->csum != csum ) { + /* This is a PGP 7.0.0 workaround */ + sk->csum = csumc; /* take the encrypted one */ + } } /* must check it here otherwise the mpi_read_xx would fail * because the length may have an arbitrary value */ @@ -321,8 +326,6 @@ protect_secret_key( PKT_secret_key *sk, DEK *dek ) #warning FIXME: replace set/get buffer if( sk->version >= 4 ) { - /* 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]; diff --git a/g10/sig-check.c b/g10/sig-check.c index 85c8bfbc1..a3946a1e0 100644 --- a/g10/sig-check.c +++ b/g10/sig-check.c @@ -134,9 +134,6 @@ do_signature_check( PKT_signature *sig, GCRY_MD_HD digest, PKT_public_key *pk = gcry_xcalloc( 1, sizeof *pk ); int rc=0; - if( is_RSA(sig->pubkey_algo) ) - write_status(STATUS_RSA_OR_IDEA); - *r_expiredate = 0; if( get_pubkey( pk, sig->keyid ) ) rc = GPGERR_NO_PUBKEY; @@ -451,10 +448,18 @@ check_key_signature2( KBNODE root, KBNODE node, int *is_selfsig, sig = node->pkt->pkt.signature; algo = sig->digest_algo; - #if 0 /* I am not sure whether this is a good thing to do */ - if( sig->flags.checked ) + #if 0 + if( sig->flags.checked ) { log_debug("check_key_signature: already checked: %s\n", sig->flags.valid? "good":"bad" ); + if ( sig->flags.valid ) + return 0; /* shortcut already checked signatures */ + /* FIXME: We should also do this with bad signatures but here we + * have to distinguish between several reasons; e.g. for a missing + * public key. the key may now be available. + * For now we simply don't shortcut bad signatures + */ + } #endif if( (rc=openpgp_md_test_algo(algo)) ) diff --git a/g10/sign.c b/g10/sign.c index d52010a4c..b53444b7c 100644 --- a/g10/sign.c +++ b/g10/sign.c @@ -485,6 +485,10 @@ sign_file( STRLIST filenames, int detached, STRLIST locusr, if( fname ) { if( !(filesize = iobuf_get_filelength(inp)) ) log_info(_("WARNING: `%s' is an empty file\n"), fname ); + /* we can't yet encode the length of very large files, + * so we switch to partial length encoding in this case */ + if ( filesize >= IOBUF_FILELENGTH_LIMIT ) + filesize = 0; /* because the text_filter modifies the length of the * data, it is not possible to know the used length diff --git a/g10/skclist.c b/g10/skclist.c index 8fcb22aba..5c6d6fbd7 100644 --- a/g10/skclist.c +++ b/g10/skclist.c @@ -59,7 +59,7 @@ build_sk_list( STRLIST locusr, SK_LIST *ret_sk_list, int unlock, PKT_secret_key *sk; sk = gcry_xcalloc( 1, sizeof *sk ); - sk->pubkey_usage = use; + sk->req_usage = use; if( (rc = get_seckey_byname( sk, NULL, unlock )) ) { free_secret_key( sk ); sk = NULL; log_error("no default secret key: %s\n", gpg_errstr(rc) ); @@ -90,7 +90,7 @@ build_sk_list( STRLIST locusr, SK_LIST *ret_sk_list, int unlock, PKT_secret_key *sk; sk = gcry_xcalloc( 1, sizeof *sk ); - sk->pubkey_usage = use; + sk->req_usage = use; if( (rc = get_seckey_byname( sk, locusr->d, unlock )) ) { free_secret_key( sk ); sk = NULL; log_error(_("skipped `%s': %s\n"), locusr->d, gpg_errstr(rc) ); diff --git a/g10/trustdb.c b/g10/trustdb.c index f75ccc52f..f3fcd4649 100644 --- a/g10/trustdb.c +++ b/g10/trustdb.c @@ -109,6 +109,7 @@ static TN used_tns; static int alloced_tns; static int max_alloced_tns; +static struct keyid_list *trusted_key_list; static LOCAL_ID_TABLE new_lid_table(void); static int ins_lid_table_item( LOCAL_ID_TABLE tbl, ulong lid, unsigned flag ); @@ -463,6 +464,64 @@ lid_from_keyid_no_sdir( u32 *keyid ) ************* Initialization **************** ***********************************************/ +void +register_trusted_key( const char *string ) +{ + u32 keyid[2]; + struct keyid_list *r; + + if( classify_user_id( string, keyid, NULL, NULL, NULL ) != 11 ) { + log_error(_("'%s' is not a valid long keyID\n"), string ); + return; + } + + for( r = trusted_key_list; r; r = r->next ) + if( r->keyid[0] == keyid[0] && r->keyid[1] == keyid[1] ) + return; + r = gcry_xmalloc( sizeof *r ); + r->keyid[0] = keyid[0]; + r->keyid[1] = keyid[1]; + r->next = trusted_key_list; + trusted_key_list = r; +} + + + +static void +add_ultimate_key( PKT_public_key *pk, u32 *keyid ) +{ + int rc; + + /* first make sure that the pubkey is in the trustdb */ + rc = query_trust_record( pk ); + if( rc == -1 && opt.dry_run ) + return; + if( rc == -1 ) { /* put it into the trustdb */ + rc = insert_trust_record_by_pk( pk ); + if( rc ) { + log_error(_("key %08lX: can't put it into the trustdb\n"), + (ulong)keyid[1] ); + return; + } + } + else if( rc ) { + log_error(_("key %08lX: query record failed\n"), (ulong)keyid[1] ); + return; + } + + if( DBG_TRUST ) + log_debug("key %08lX.%lu: stored into ultikey_table\n", + (ulong)keyid[1], pk->local_id ); + + if( ins_lid_table_item( ultikey_table, pk->local_id, 0 ) ) + log_error(_("key %08lX: already in trusted key table\n"), + (ulong)keyid[1]); + else if( opt.verbose > 1 ) + log_info(_("key %08lX: accepted as trusted key.\n"), + (ulong)keyid[1]); + +} + /**************** * Verify that all our public keys are in the trustdb. */ @@ -474,7 +533,27 @@ verify_own_keys(void) PKT_secret_key *sk = gcry_xcalloc( 1, sizeof *sk ); PKT_public_key *pk = gcry_xcalloc( 1, sizeof *pk ); u32 keyid[2]; - + struct keyid_list *kl; + + + /* put the trusted keys into the ultikey table */ + for( kl = trusted_key_list; kl; kl = kl->next ) { + keyid[0] = kl->keyid[0]; + keyid[1] = kl->keyid[1]; + /* get the public key */ + memset( pk, 0, sizeof *pk ); + rc = get_pubkey( pk, keyid ); + if( rc ) { + log_info(_("key %08lX: no public key for trusted key - skipped\n"), + (ulong)keyid[1] ); + } + else { + add_ultimate_key( pk, keyid ); + release_public_key_parts( pk ); + } + } + + /* And now add all secret keys to the ultikey table */ while( !(rc=enum_secret_keys( &enum_context, sk, 0 ) ) ) { int have_pk = 0; @@ -487,6 +566,10 @@ verify_own_keys(void) log_info(_("NOTE: secret key %08lX is NOT protected.\n"), (ulong)keyid[1] ); + for( kl = trusted_key_list; kl; kl = kl->next ) { + if( kl->keyid[0] == keyid[0] && kl->keyid[1] == keyid[1] ) + goto skip; /* already in trusted key table */ + } /* see whether we can access the public key of this secret key */ memset( pk, 0, sizeof *pk ); @@ -504,33 +587,8 @@ verify_own_keys(void) goto skip; } - /* make sure that the pubkey is in the trustdb */ - rc = query_trust_record( pk ); - if( rc == -1 && opt.dry_run ) - goto skip; - if( rc == -1 ) { /* put it into the trustdb */ - rc = insert_trust_record_by_pk( pk ); - if( rc ) { - log_error(_("key %08lX: can't put it into the trustdb\n"), - (ulong)keyid[1] ); - goto skip; - } - } - else if( rc ) { - log_error(_("key %08lX: query record failed\n"), (ulong)keyid[1] ); - goto skip; + add_ultimate_key( pk, keyid ); - } - - if( DBG_TRUST ) - log_debug("key %08lX.%lu: stored into ultikey_table\n", - (ulong)keyid[1], pk->local_id ); - if( ins_lid_table_item( ultikey_table, pk->local_id, 0 ) ) - log_error(_("key %08lX: already in trusted key table\n"), - (ulong)keyid[1]); - else if( opt.verbose > 1 ) - log_info(_("key %08lX: accepted as trusted key.\n"), - (ulong)keyid[1]); skip: release_secret_key_parts( sk ); if( have_pk ) @@ -541,6 +599,15 @@ verify_own_keys(void) else rc = 0; + /* release the trusted keyid table */ + { struct keyid_list *kl2; + for( kl = trusted_key_list; kl; kl = kl2 ) { + kl2 = kl->next; + gcry_free( kl ); + } + trusted_key_list = NULL; + } + enum_secret_keys( &enum_context, NULL, 0 ); /* free context */ free_secret_key( sk ); free_public_key( pk ); @@ -548,6 +615,8 @@ verify_own_keys(void) } + + /**************** * Perform some checks over the trustdb * level 0: only open the db diff --git a/include/iobuf.h b/include/iobuf.h index 7a7d825e6..2f35f9af9 100644 --- a/include/iobuf.h +++ b/include/iobuf.h @@ -116,6 +116,7 @@ size_t iobuf_temp_to_buffer( IOBUF a, byte *buffer, size_t buflen ); void iobuf_unget_and_close_temp( IOBUF a, IOBUF temp ); u32 iobuf_get_filelength( IOBUF a ); +#define IOBUF_FILELENGTH_LIMIT 0xffffffff const char *iobuf_get_real_fname( IOBUF a ); const char *iobuf_get_fname( IOBUF a ); diff --git a/tools/ChangeLog b/tools/ChangeLog index 79bba89f2..136caf56b 100644 --- a/tools/ChangeLog +++ b/tools/ChangeLog @@ -1,3 +1,8 @@ +Mon Sep 18 16:35:45 CEST 2000 Werner Koch <wk@openit.de> + + * ring-a-party: substr starts at offset 1 not 0. Many thanks to Mike + for finding this bug. Flush the last key. + Mon Jul 17 16:35:47 CEST 2000 Werner Koch <wk@> * mail-signed-keys: New. diff --git a/tools/ring-a-party b/tools/ring-a-party index 561b51336..7cf05d78d 100755 --- a/tools/ring-a-party +++ b/tools/ring-a-party @@ -31,6 +31,9 @@ BEGIN { FS=":" page = 0; now = strftime("%b %d %H:%M %Y"); } +END { + if (any) myflush(); +} $1 == "pub" { if( any ) myflush(); uidcount = 0; @@ -84,7 +87,7 @@ function printfpr16( s ) printf "f16 Fingerprint16 ="; for(i=0; i < 16; i++ ) { if( i == 8 ) printf " "; - printf " %s", substr( s, i*2, 2 ); + printf " %s", substr( s, i*2+1, 2 ); } printf "\n" } @@ -94,10 +97,13 @@ function printfpr20( s ) printf "f20 Fingerprint20 ="; for(i=0; i < 10; i++ ) { if( i == 5 ) printf " "; - printf " %s", substr( s, i*4, 4 ); + printf " %s", substr( s, i*4+1, 4 ); } printf "\n" } ' | tee a.pub | gpg --print-mds + + + diff --git a/util/ChangeLog b/util/ChangeLog index 956c2085d..20e9bda3e 100644 --- a/util/ChangeLog +++ b/util/ChangeLog @@ -1,3 +1,15 @@ +Mon Sep 18 16:35:45 CEST 2000 Werner Koch <wk@openit.de> + + * strgutil.c (utf8_to_native): Fixed null ptr problem. By + Giampaolo Tomassoni. + + * iobuf.c: Use fopen64 insead of fopen when available. + (iobuf_get_filelength): Use fstat64 when available but return + 2^32-1 if the file is larger than this value. + + * miscutil.c (answer_is_yes_no_quit): Swapped order of yes/no test + so that no is returned for an empty input. By David Champion. + Fri Aug 18 14:27:14 CEST 2000 Werner Koch <wk@openit.de> * logger.c (log_set_file): Allow to set the file by name. diff --git a/util/iobuf.c b/util/iobuf.c index f7035ae4c..5f70694c2 100644 --- a/util/iobuf.c +++ b/util/iobuf.c @@ -36,6 +36,14 @@ #include "util.h" #include "iobuf.h" + +#if defined (HAVE_FOPEN64) && defined (HAVE_FSTAT64) + #define fopen(a,b) fopen64 ((a),(b)) + #define fstat(a,b) fstat64 ((a),(b)) +#endif + + + typedef struct { FILE *fp; /* open file handle */ int print_only_name; /* flags indicating that fname is not a real file*/ @@ -1312,25 +1320,40 @@ iobuf_set_limit( IOBUF a, unsigned long nlimit ) u32 iobuf_get_filelength( IOBUF a ) { +#if defined (HAVE_FOPEN64) && defined (HAVE_FSTAT64) + struct stat64 st; +#else struct stat st; +#endif if( a->directfp ) { FILE *fp = a->directfp; - if( !fstat(fileno(fp), &st) ) - return st.st_size; + if( !fstat(fileno(fp), &st) ) { + #if defined (HAVE_FOPEN64) && defined (HAVE_FSTAT64) + if( st.st_size >= IOBUF_FILELENGTH_LIMIT ) + return IOBUF_FILELENGTH_LIMIT; + #endif + return (u32)st.st_size; + } log_error("fstat() failed: %s\n", strerror(errno) ); return 0; } + /* Hmmm: file_filter may have already been removed */ for( ; a; a = a->chain ) if( !a->chain && a->filter == file_filter ) { file_filter_ctx_t *b = a->filter_ov; FILE *fp = b->fp; - if( !fstat(fileno(fp), &st) ) + if( !fstat(fileno(fp), &st) ) { + #if defined (HAVE_FOPEN64) && defined (HAVE_FSTAT64) + if( st.st_size >= IOBUF_FILELENGTH_LIMIT ) + return IOBUF_FILELENGTH_LIMIT; + #endif return st.st_size; + } log_error("fstat() failed: %s\n", strerror(errno) ); break; } diff --git a/util/miscutil.c b/util/miscutil.c index c1b8fa076..9b4a8379f 100644 --- a/util/miscutil.c +++ b/util/miscutil.c @@ -311,16 +311,16 @@ answer_is_yes_no_quit( const char *s ) char *short_no = _("nN"); char *short_quit = _("qQ"); - if( !stricmp(s, long_yes ) ) - return 1; if( !stricmp(s, long_no ) ) return 0; + if( !stricmp(s, long_yes ) ) + return 1; if( !stricmp(s, long_quit ) ) return -1; - if( strchr( short_yes, *s ) && !s[1] ) - return 1; if( strchr( short_no, *s ) && !s[1] ) return 0; + if( strchr( short_yes, *s ) && !s[1] ) + return 1; if( strchr( short_quit, *s ) && !s[1] ) return -1; if( !stricmp(s, "yes" ) ) diff --git a/util/strgutil.c b/util/strgutil.c index a8abf7ba8..89722f8e4 100644 --- a/util/strgutil.c +++ b/util/strgutil.c @@ -389,7 +389,8 @@ utf8_to_native( const char *string, size_t length ) case 0 : n++; if( p ) *p++ = '0'; break; default: n += 3; sprintf( p, "x%02x", *s ); - p += 3; + if ( p ) + p += 3; break; } } @@ -496,3 +497,7 @@ utf8_to_native( const char *string, size_t length ) } + + + + |