diff --git a/NEWS b/NEWS index 15d5d9e8d..688069e78 100644 --- a/NEWS +++ b/NEWS @@ -4,6 +4,8 @@ * A bunch of changes to the key validation code. + * --list-trust-path now has an optional --with-colons format. + Noteworthy changes in version 0.9.4 ----------------------------------- diff --git a/TODO b/TODO index 9833173c9..0e35c6c8f 100644 --- a/TODO +++ b/TODO @@ -1,17 +1,14 @@ - * Replace --trusted-keys by a local certificate (which does not get - exported). - * add some status output put for signing and encryption. - replace the puc in primegen with some kind of status-fd outputs. + replace the putc in primegen with some kind of status-fd outputs. * Finish the EGD module. - * Implement 256 bit key Twofish (wait until the 2nd AES conference). + * Implement 256 bit key Twofish. - * Check revocation and expire stuff. [I'm currently working on this.] + * Check revocation and expire stuff. - * Check calculation of key validity. [I'm currently working on this.] + * Check calculation of key validity. * See why we always get this "Hmmm public key lost" @@ -25,17 +22,11 @@ * when decryptiong multiple key: print a warning only if no usable pubkey encrypt package was found. Extension: display a list of all recipients. -* an ERRSIG argument like - ERRSIG , where is the id of the missing public key - or a new keyword - PUBLIC_MISSING -* a status line complaining about a missing secret key like - SECRET_MISSING , where is the id of the missing secret key -* a status line complaining about a bad passphrase like - BADPASS - - + * Add NO_PUBKEY and NO_SECKEY status lines. + * Add more NODATA status lines + * gpg --keyserver wwwkeys.us.pgp.net --importserver 0x12345678 + (or --importserver warner@lothar.com, etc) Nice to have diff --git a/acinclude.m4 b/acinclude.m4 index 2349da629..11ba6fda4 100644 --- a/acinclude.m4 +++ b/acinclude.m4 @@ -343,4 +343,256 @@ define(GNUPG_CHECK_MLOCK, ]) +################################################################ + +# GNUPG_PROG_NM - find the path to a BSD-compatible name lister +AC_DEFUN(GNUPG_PROG_NM, +[AC_MSG_CHECKING([for BSD-compatible nm]) +AC_CACHE_VAL(ac_cv_path_NM, +[if test -n "$NM"; then + # Let the user override the test. + ac_cv_path_NM="$NM" +else + IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS="${IFS}:" + for ac_dir in /usr/ucb /usr/ccs/bin $PATH /bin; do + test -z "$ac_dir" && ac_dir=. + if test -f $ac_dir/nm; then + # Check to see if the nm accepts a BSD-compat flag. + # Adding the `sed 1q' prevents false positives on HP-UX, which says: + # nm: unknown option "B" ignored + if ($ac_dir/nm -B /dev/null 2>&1 | sed '1q'; exit 0) | egrep /dev/null >/dev/null; then + ac_cv_path_NM="$ac_dir/nm -B" + elif ($ac_dir/nm -p /dev/null 2>&1 | sed '1q'; exit 0) | egrep /dev/null >/dev/null; then + ac_cv_path_NM="$ac_dir/nm -p" + else + ac_cv_path_NM="$ac_dir/nm" + fi + break + fi + done + IFS="$ac_save_ifs" + test -z "$ac_cv_path_NM" && ac_cv_path_NM=nm +fi]) +NM="$ac_cv_path_NM" +AC_MSG_RESULT([$NM]) +AC_SUBST(NM) +]) + +# GNUPG_SYS_NM_PARSE - Check for command ro grab the raw symbol name followed +# by C symbol name from nm. +AC_DEFUN(GNUPG_SYS_NM_PARSE, +[AC_REQUIRE([AC_CANONICAL_HOST])dnl +AC_REQUIRE([GNUPG_PROG_NM])dnl +# Check for command to grab the raw symbol name followed by C symbol from nm. +AC_MSG_CHECKING([command to parse $NM output]) +AC_CACHE_VAL(ac_cv_sys_global_symbol_pipe, +[# These are sane defaults that work on at least a few old systems. +# {They come from Ultrix. What could be older than Ultrix?!! ;)} + +changequote(,)dnl +# Character class describing NM global symbol codes. +ac_symcode='[BCDEGRSTU]' + +# Regexp to match symbols that can be accessed directly from C. +ac_sympat='\([_A-Za-z][_A-Za-z0-9]*\)' + +# Transform the above into a raw symbol and a C symbol. +ac_symxfrm='\1 \1' + +# Define system-specific variables. +case "$host_os" in +aix*) + ac_symcode='[BCDTU]' + ;; +sunos* | cygwin32* | mingw32*) + ac_sympat='_\([_A-Za-z][_A-Za-z0-9]*\)' + ac_symxfrm='_\1 \1' + ;; +irix*) + # Cannot use undefined symbols on IRIX because inlined functions mess us up. + ac_symcode='[BCDEGRST]' + ;; +solaris*) + ac_symcode='[BDTU]' + ;; +esac + +# If we're using GNU nm, then use its standard symbol codes. +if $NM -V 2>&1 | egrep '(GNU|with BFD)' > /dev/null; then + ac_symcode='[ABCDGISTUW]' +fi + +case "$host_os" in +cygwin32* | mingw32*) + # We do not want undefined symbols on cygwin32. The user must + # arrange to define them via -l arguments. + ac_symcode='[ABCDGISTW]' + ;; +esac +changequote([,])dnl + +# Write the raw and C identifiers. +ac_cv_sys_global_symbol_pipe="sed -n -e 's/^.* $ac_symcode $ac_sympat$/$ac_symxfrm/p'" + +# Check to see that the pipe works correctly. +ac_pipe_works=no +cat > conftest.$ac_ext < $ac_nlist) && test -s "$ac_nlist"; then + + # Try sorting and uniquifying the output. + if sort "$ac_nlist" | uniq > "$ac_nlist"T; then + mv -f "$ac_nlist"T "$ac_nlist" + ac_wcout=`wc "$ac_nlist" 2>/dev/null` +changequote(,)dnl + ac_count=`echo "X$ac_wcout" | sed -e 's,^X,,' -e 's/^[ ]*\([0-9][0-9]*\).*$/\1/'` +changequote([,])dnl + (test "$ac_count" -ge 0) 2>/dev/null || ac_count=-1 + else + rm -f "$ac_nlist"T + ac_count=-1 + fi + + # Make sure that we snagged all the symbols we need. + if egrep ' nm_test_var$' "$ac_nlist" >/dev/null; then + if egrep ' nm_test_func$' "$ac_nlist" >/dev/null; then + cat < conftest.c +#ifdef __cplusplus +extern "C" { +#endif + +EOF + # Now generate the symbol file. + sed 's/^.* \(.*\)$/extern char \1;/' < "$ac_nlist" >> conftest.c + + cat <> conftest.c +#if defined (__STDC__) && __STDC__ +# define __ptr_t void * +#else +# define __ptr_t char * +#endif + +/* The number of symbols in dld_preloaded_symbols, -1 if unsorted. */ +int dld_preloaded_symbol_count = $ac_count; + +/* The mapping between symbol names and symbols. */ +struct { + char *name; + __ptr_t address; +} +changequote(,)dnl +dld_preloaded_symbols[] = +changequote([,])dnl +{ +EOF + sed 's/^\(.*\) \(.*\)$/ {"\1", (__ptr_t) \&\2},/' < "$ac_nlist" >> conftest.c + cat <<\EOF >> conftest.c + {0, (__ptr_t) 0} +}; + +#ifdef __cplusplus +} +#endif +EOF + # Now try linking the two files. + mv conftest.$ac_objext conftestm.$ac_objext + ac_save_LIBS="$LIBS" + ac_save_CFLAGS="$CFLAGS" + LIBS="conftestm.$ac_objext" + CFLAGS="$CFLAGS$no_builtin_flag" + if AC_TRY_EVAL(ac_link) && test -s conftest; then + ac_pipe_works=yes + else + echo "configure: failed program was:" >&AC_FD_CC + cat conftest.c >&AC_FD_CC + fi + LIBS="$ac_save_LIBS" + CFLAGS="$ac_save_CFLAGS" + else + echo "cannot find nm_test_func in $ac_nlist" >&AC_FD_CC + fi + else + echo "cannot find nm_test_var in $ac_nlist" >&AC_FD_CC + fi + else + echo "cannot run $ac_cv_sys_global_symbol_pipe" >&AC_FD_CC + fi +else + echo "$progname: failed program was:" >&AC_FD_CC + cat conftest.c >&AC_FD_CC +fi +rm -rf conftest* + +# Do not use the global_symbol_pipe unless it works. +test "$ac_pipe_works" = yes || ac_cv_sys_global_symbol_pipe= +]) + +ac_result=yes +if test -z "$ac_cv_sys_global_symbol_pipe"; then + ac_result=no +fi +AC_MSG_RESULT($ac_result) +]) + +# GNUPG_SYS_LIBTOOL_CYGWIN32 - find tools needed on cygwin32 +AC_DEFUN(GNUPG_SYS_LIBTOOL_CYGWIN32, +[AC_CHECK_TOOL(DLLTOOL, dlltool, false) +AC_CHECK_TOOL(AS, as, false) +]) + +# GNUPG_SYS_SYMBOL_UNDERSCORE - does the compiler prefix global symbols +# with an underscore? +AC_DEFUN(GNUPG_SYS_SYMBOL_UNDERSCORE, +[AC_REQUIRE([GNUPG_PROG_NM])dnl +AC_REQUIRE([GNUPG_SYS_NM_PARSE])dnl +AC_MSG_CHECKING([for _ prefix in compiled symbols]) +AC_CACHE_VAL(ac_cv_sys_symbol_underscore, +[ac_cv_sys_symbol_underscore=no +cat > conftest.$ac_ext < $ac_nlist) && test -s "$ac_nlist"; then + # See whether the symbols have a leading underscore. + if egrep '^_nm_test_func' "$ac_nlist" >/dev/null; then + ac_cv_sys_symbol_underscore=yes + else + if egrep '^nm_test_func ' "$ac_nlist" >/dev/null; then + : + else + echo "configure: cannot find nm_test_func in $ac_nlist" >&AC_FD_CC + fi + fi + else + echo "configure: cannot run $ac_cv_sys_global_symbol_pipe" >&AC_FD_CC + fi +else + echo "configure: failed program was:" >&AC_FD_CC + cat conftest.c >&AC_FD_CC +fi +rm -rf conftest* +]) +AC_MSG_RESULT($ac_cv_sys_symbol_underscore) +if test x$ac_cv_sys_symbol_underscore = xyes; then + AC_DEFINE(WITH_SYMBOL_UNDERSCORE,1, + [define if compiled symbols have a leading underscore]) +fi +]) + + dnl *-*wedit:notab*-* Please keep this as the last line. diff --git a/checks/ChangeLog b/checks/ChangeLog index 27397c0e4..3217d4145 100644 --- a/checks/ChangeLog +++ b/checks/ChangeLog @@ -1,3 +1,7 @@ +Wed Mar 17 13:09:03 CET 1999 Werner Koch + + * mds.test: replaced the "echo -n" + Mon Mar 8 20:47:17 CET 1999 Werner Koch * pubdemo.asc, secdemo.asc: New. diff --git a/checks/genkey1024.test b/checks/genkey1024.test index ac1868e3d..bd79bcb89 100755 --- a/checks/genkey1024.test +++ b/checks/genkey1024.test @@ -13,7 +13,7 @@ fi LANG= LANGUAGE= -expect - </dev/null +expect - </dev/null #set timeout -1 set timeout 8 match_max 100000 @@ -89,10 +89,11 @@ Enter passphrase: " { sleep 1; send -- "abc\r" } expect { -ex "\r \rRepeat passphrase: " { sleep 1; send -- "abc\r" } timeout { exit 1 } } +set timeout 600 expect { -ex "\r \rWe need to generate a lot of random bytes. It is a good idea to perform\r some other action (work in another window, move the mouse, utilize the\r -network and the disks) during the prime generation; this gives the random\r +the disks) during the prime generation; this gives the random\r number generator a better chance to gain enough entropy.\r" {} timeout { exit 1 } } set timeout 600 diff --git a/checks/mds.test b/checks/mds.test index 2d04812d4..63bec7c05 100755 --- a/checks/mds.test +++ b/checks/mds.test @@ -13,7 +13,7 @@ test_one () { failed="" #info Checking message digests -echo -n "" | $srcdir/run-gpgm -v --print-mds >y +cat /dev/null | $srcdir/run-gpgm -v --print-mds >y test_one "MD5" "D41D8CD98F00B204E9800998ECF8427E" test_one "SHA1" "DA39A3EE5E6B4B0D3255BFEF95601890AFD80709" test_one "RMD160" "9C1185A5C5E9FC54612808977EE8F548B2258D31" @@ -25,7 +25,7 @@ fi [ "$failed" != "" ] && error "$failed failed for empty string" -echo -n "abcdefghijklmnopqrstuvwxyz" | $srcdir/run-gpgm --print-mds >y +/bin/echo "abcdefghijklmnopqrstuvwxyz\c" | $srcdir/run-gpgm --print-mds >y test_one "MD5" "C3FCD3D76192E4007DFB496CCA67E13B" test_one "SHA1" "32D10C7B8CF96570CA04CE37F2A19D84240D3A89" test_one "RMD160" "F71C27109C692C1B56BBDCEB5B9D2865B3708DBC" diff --git a/cipher/ChangeLog b/cipher/ChangeLog index a9a720f26..3ac7d31c6 100644 --- a/cipher/ChangeLog +++ b/cipher/ChangeLog @@ -1,3 +1,8 @@ +Wed Mar 17 13:09:03 CET 1999 Werner Koch + + * rndegd.c (do_read): New. + (gather_random): Changed the implementation. + Mon Mar 8 20:47:17 CET 1999 Werner Koch * dynload.c (DLSYM_NEEDS_UNDERSCORE): Renamed. diff --git a/cipher/rndegd.c b/cipher/rndegd.c index 6cdc4dda7..0777ff859 100644 --- a/cipher/rndegd.c +++ b/cipher/rndegd.c @@ -34,6 +34,7 @@ #include "util.h" #include "ttyio.h" #include "dynload.h" +#include "cipher.h" #ifdef IS_MODULE #define _(a) (a) @@ -64,18 +65,47 @@ do_write( int fd, void *buf, size_t nbytes ) return 0; } +static int +do_read( int fd, void *buf, size_t nbytes ) +{ + int n, nread = 0; + do { + do { + n = read(fd, (char*)buf + nread, nbytes ); + } while( n == -1 && errno == EINTR ); + if( n == -1 ) + return -1; + nread += n; + } while( nread < nbytes ); + return nbytes; +} + + +/* fixme: level 1 is not yet handled */ static int gather_random( void (*add)(const void*, size_t, int), int requester, size_t length, int level ) { static int fd = -1; int n; - int warn=0; byte buffer[256+2]; + int nbytes; + int do_restart = 0; + if( !length ) + return 0; + + + restart: + if( do_restart ) { + if( fd != -1 ) { + close( fd ); + fd = -1; + } + } if( fd == -1 ) { - const char *name = "/tmp/entropy"; + char *name = make_filename( g10_opt_homedir, "entropy", NULL ); struct sockaddr_un addr; int addr_len; @@ -92,73 +122,63 @@ gather_random( void (*add)(const void*, size_t, int), int requester, if( connect( fd, (struct sockaddr*)&addr, addr_len) == -1 ) g10_log_fatal("can't connect to `%s': %s\n", name, strerror(errno) ); + m_free(name); + } + do_restart = 0; + + nbytes = length < 255? length : 255; + /* first time we do it with a non blocking request */ + buffer[0] = 1; /* non blocking */ + buffer[1] = nbytes; + if( do_write( fd, buffer, 2 ) == -1 ) + g10_log_fatal("can't write to the EGD: %s\n", strerror(errno) ); + n = do_read( fd, buffer, 1 ); + if( n == -1 ) { + g10_log_error("read error on EGD: %s\n", strerror(errno)); + do_restart = 1; + goto restart; + } + if( !n ) { + g10_log_error("bad EGD reply: too short\n"); + do_restart = 1; + goto restart; + } + if( n > 1 ) { + n--; + (*add)( buffer+1, n, requester ); + length -= n; } - + if( length ) { + #ifdef IS_MODULE + fprintf( stderr, + #else + tty_printf( + #endif + _("Please wait, entropy is being gathered. Do some work if it would\n" + "keep you from getting bored, because it will improve the quality\n" + "of the entropy.\n") ); + } while( length ) { - fd_set rfds; - struct timeval tv; - int rc; - int nbytes; - int cmd; - nbytes = length < 255? length : 255; - /* send request */ - cmd = level >= 2 ? 2 : 1; - buffer[0] = cmd; + + buffer[0] = 2; /* blocking */ buffer[1] = nbytes; if( do_write( fd, buffer, 2 ) == -1 ) g10_log_fatal("can't write to the EGD: %s\n", strerror(errno) ); - /* wait on reply */ - FD_ZERO(&rfds); - FD_SET(fd, &rfds); - tv.tv_sec = 3; - tv.tv_usec = 0; - if( !(rc=select(fd+1, &rfds, NULL, NULL, &tv)) ) { - if( !warn ) - #ifdef IS_MODULE - fprintf( stderr, - #else - tty_printf( - #endif - _( -"\n" -"Not enough random bytes available. Please do some other work to give\n" -"the OS a chance to collect more entropy! (Need %d more bytes)\n"), length ); - warn = 1; - continue; - } - else if( rc == -1 ) { - g10_log_error("select error on EGD: %s\n", strerror(errno)); - continue; - } - - /* collect reply */ - do { - n = read(fd, buffer, nbytes+2 ); - } while( n == -1 && errno == EINTR ); - /* process reply */ - if( n == -1 ) + n = do_read( fd, buffer, nbytes ); + if( n == -1 ) { g10_log_error("read error on EGD: %s\n", strerror(errno)); - else if( cmd == 2 && n != nbytes ) { + do_restart = 1; + goto restart; + } + if( n != nbytes ) { g10_log_error("bad EGD reply: too short %d/%d\n", nbytes, n ); + do_restart = 1; + goto restart; } - else if( cmd == 2 ) { - (*add)( buffer, n, requester ); - length -= n; - } - else if( !n ) - g10_log_error("bad EGD reply: too short\n"); - else if( buffer[0] != n-1 ) - g10_log_error("bad EGD reply: count mismatch %d/%d\n", - n-1, buffer[0] ); - else if( n==1 ) - g10_log_info("no data from EGD\n"); - else { - n -= 1; - (*add)( buffer+1, n, requester ); - length -= n; - } + (*add)( buffer, n, requester ); + length -= n; } memset(buffer, 0, sizeof(buffer) ); diff --git a/configure.in b/configure.in index 1e71eb7bc..e4760fbd5 100644 --- a/configure.in +++ b/configure.in @@ -28,11 +28,11 @@ dnl dnl Check for random module options dnl dnl Fixme: get the list of available modules from MODULES_IN_CIPHER -dnl and check agiants this list +dnl and check against this list AC_MSG_CHECKING([which static random module to use]) AC_ARG_ENABLE(static-rnd, - [ --enable-static-rnd=[egd|unix|linux|nonde] ], + [ --enable-static-rnd=[egd|unix|linux|none] ], [use_static_rnd=$enableval], [use_static_rnd=default] ) if test "$use_static_rnd" = no; then @@ -169,7 +169,7 @@ case "${target}" in esac AC_SUBST(MPI_OPT_FLAGS) -AM_SYS_SYMBOL_UNDERSCORE +GNUPG_SYS_SYMBOL_UNDERSCORE GNUPG_CHECK_PIC GNUPG_CHECK_RDYNAMIC if test "$NO_PIC" = yes; then diff --git a/doc/DETAILS b/doc/DETAILS index 81b9dce5d..fb26c2b16 100644 --- a/doc/DETAILS +++ b/doc/DETAILS @@ -54,10 +54,9 @@ more arguments in future versions. BADSIG The signature with the keyid has not been verified okay. - ERRSIG + ERRSIG It was not possible to check the signature. This may be caused by a missing public key or an unsupported algorithm. - No argument yet. VALIDSIG The signature with the keyid is good. This is the same @@ -72,6 +71,13 @@ more arguments in future versions. unique ids - others may yield duplicated ones when they have been created in the same second. + ENC_TO + The message is encrypted to this keyid. + + NODATA + No data has been found. Codes for what are: + 1 - No armored data. + TRUST_UNDEFINED TRUST_NEVER TRUST_MARGINAL @@ -98,10 +104,16 @@ more arguments in future versions. SHM_GET SHM_GET_BOOL SHM_GET_HIDDEN - NEED_PASSPHRASE - [Needs documentation] + NEED_PASSPHRASE + Issued whenever a passphrase is needed. + BAD_PASSPHRASE + The supplied passphrase was wrong + + NO_PUBKEY + NO_SECKEY + The key is not available Key generation @@ -282,31 +294,6 @@ Record type 8: (shadow directory record) -Record type 9: (cache record) NOT USED --------------- - Used to bind the trustDB to the concrete instance of keyblock in - a pubring. This is used to cache information. - - 1 byte value 9 - 1 byte reserved - 1 u32 Local-Id. - 8 bytes keyid of the primary key (needed?) - 1 byte cache-is-valid the following stuff is only - valid if this is set. - 1 byte reserved - 20 bytes rmd160 hash value over the complete keyblock - This is used to detect any changes of the keyblock with all - CTBs and lengths headers. Calculation is easy if the keyblock - is obtained from a keyserver: simply create the hash from all - received data bytes. - - 1 byte number of untrusted signatures. - 1 byte number of marginal trusted signatures. - 1 byte number of fully trusted signatures. - (255 is stored for all values greater than 254) - 1 byte Trustlevel (see trustdb.h) - - Record Type 10 (hash table) -------------- Due to the fact that we use fingerprints to lookup keys, we can diff --git a/g10/ChangeLog b/g10/ChangeLog index 2e682f353..ea1133cc1 100644 --- a/g10/ChangeLog +++ b/g10/ChangeLog @@ -1,3 +1,31 @@ +Wed Mar 17 13:09:03 CET 1999 Werner Koch + + * trustdb.c (check_trust): add new arg add_fnc and changed all callers. + (do_check): Ditto. + (verify_key): Ditto. + (propagate_validity): Use the new add_fnc arg. + (print_user_id): Add the FILE arg. + (propagate_ownertrust): New. + * pkclist.c (add_ownertrust_cb): New and changed the add_ownertrust + logic. + + * getkey.c (get_keyblock_bylid): New. + * trustdb.c (print_uid_from_keyblock): New. + (dump_tn_tree_with_colons): New. + (list_trust_path): Add colon print mode. + + * trustdb.c (insert_trust_record): Always use the primary key. + + * encode.c (encode_simple): Added text_mode filter (Rémi Guyomarch) + (encode_crypt): Ditto. + + * mainproc.c (proc_pubkey_enc): Added status ENC_TO. + * armor.c (armor_filter): Added status NODATA. + * passphrase.c (passphrase_to_dek): Always print NEED_PASSPHRASE + * seckey_cert.c (check_secret_key): Added BAD_PASS status. + + * g10.c (main): Set g10_opt_homedir. + Sun Mar 14 19:34:36 CET 1999 Werner Koch * keygen.c (do_create): Changed wording of the note (Hugh Daniel) diff --git a/g10/armor.c b/g10/armor.c index 1a3e6e5ba..a6daf7110 100644 --- a/g10/armor.c +++ b/g10/armor.c @@ -989,8 +989,10 @@ armor_filter( void *opaque, int control, iobuf_writestr(a, tail_strings[afx->what] ); iobuf_writestr(a, "-----\n"); } - else if( !afx->any_data && !afx->inp_bypass ) + else if( !afx->any_data && !afx->inp_bypass ) { log_error(_("no valid OpenPGP data found.\n")); + write_status_text( STATUS_NODATA, "1" ); + } if( afx->truncated ) log_info(_("invalid armor: line longer than %d characters\n"), MAX_LINELEN ); diff --git a/g10/encode.c b/g10/encode.c index aba251a20..825a4c98a 100644 --- a/g10/encode.c +++ b/g10/encode.c @@ -76,11 +76,13 @@ encode_simple( const char *filename, int mode ) cipher_filter_context_t cfx; armor_filter_context_t afx; compress_filter_context_t zfx; + text_filter_context_t tfx; int do_compress = opt.compress && !opt.rfc1991; memset( &cfx, 0, sizeof cfx); memset( &afx, 0, sizeof afx); memset( &zfx, 0, sizeof zfx); + memset( &tfx, 0, sizeof tfx); init_packet(&pkt); /* prepare iobufs */ @@ -90,6 +92,9 @@ encode_simple( const char *filename, int mode ) return G10ERR_OPEN_FILE; } + if( opt.textmode ) + iobuf_push_filter( inp, text_filter, &tfx ); + cfx.dek = NULL; if( mode ) { s2k = m_alloc_clear( sizeof *s2k ); @@ -151,19 +156,19 @@ encode_simple( const char *filename, int mode ) pt->namelen = 0; } /* pgp5 has problems to decrypt symmetrically encrypted data from - * GnuPOG if the filelength is in the inner packet. It works + * GnuPG if the filelength is in the inner packet. It works * when only partial length headers are use. Until we have * tracked this problem down. We use this temporary fix * (fixme: remove the && !mode ) */ - if( filename && !mode ) { + if( filename && !opt.textmode && !mode ) { if( !(filesize = iobuf_get_filelength(inp)) ) log_info(_("%s: WARNING: empty file\n"), filename ); } else filesize = 0; /* stdin */ pt->timestamp = make_timestamp(); - pt->mode = 'b'; + pt->mode = opt.textmode? 't' : 'b'; pt->len = filesize; pt->buf = inp; pkt.pkttype = PKT_PLAINTEXT; @@ -206,12 +211,14 @@ encode_crypt( const char *filename, STRLIST remusr ) cipher_filter_context_t cfx; armor_filter_context_t afx; compress_filter_context_t zfx; + text_filter_context_t tfx; PK_LIST pk_list; int do_compress = opt.compress && !opt.rfc1991; memset( &cfx, 0, sizeof cfx); memset( &afx, 0, sizeof afx); memset( &zfx, 0, sizeof zfx); + memset( &tfx, 0, sizeof tfx); init_packet(&pkt); if( (rc=build_pk_list( remusr, &pk_list, PUBKEY_USAGE_ENC)) ) @@ -227,6 +234,9 @@ encode_crypt( const char *filename, STRLIST remusr ) else if( opt.verbose ) log_info(_("reading from `%s'\n"), filename? filename: "[stdin]"); + if( opt.textmode ) + iobuf_push_filter( inp, text_filter, &tfx ); + if( (rc = open_outfile( filename, opt.armor? 1:0, &out )) ) goto leave; @@ -270,14 +280,14 @@ encode_crypt( const char *filename, STRLIST remusr ) pt = m_alloc( sizeof *pt - 1 ); pt->namelen = 0; } - if( filename ) { + if( filename && !opt.textmode ) { if( !(filesize = iobuf_get_filelength(inp)) ) log_info(_("%s: WARNING: empty file\n"), filename ); } else filesize = 0; /* stdin */ pt->timestamp = make_timestamp(); - pt->mode = 'b'; + pt->mode = opt.textmode ? 't' : 'b'; pt->len = filesize; pt->new_ctb = !pt->len && !opt.rfc1991; pt->buf = inp; diff --git a/g10/g10.c b/g10/g10.c index 06d660e7b..c78cb34e9 100644 --- a/g10/g10.c +++ b/g10/g10.c @@ -824,6 +824,7 @@ main( int argc, char **argv ) secmem_set_flags( secmem_get_flags() & ~2 ); /* resume warnings */ set_debug(); + g10_opt_homedir = opt.homedir; /* must do this after dropping setuid, because string_to... * may try to load an module */ diff --git a/g10/getkey.c b/g10/getkey.c index 424590ed2..4d16ecd84 100644 --- a/g10/getkey.c +++ b/g10/getkey.c @@ -31,6 +31,7 @@ #include "keydb.h" #include "options.h" #include "main.h" +#include "trustdb.h" #include "i18n.h" #define MAX_UNK_CACHE_ENTRIES 1000 /* we use a linked list - so I guess @@ -832,6 +833,34 @@ get_keyblock_byfprint( KBNODE *ret_keyblock, const byte *fprint, +/**************** + * Search for a key with the given lid and return the complete keyblock + */ +int +get_keyblock_bylid( KBNODE *ret_keyblock, ulong lid ) +{ + int rc; + PKT_public_key *pk = m_alloc_clear( sizeof *pk ); + struct getkey_ctx_s ctx; + u32 kid[2]; + + if( keyid_from_lid( lid, kid ) ) + kid[0] = kid[1] = 0; + memset( &ctx, 0, sizeof ctx ); + ctx.not_allocated = 1; + ctx.nitems = 1; + 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 ); + get_pubkey_end( &ctx ); + + free_public_key( pk ); + return rc; +} + + + /**************** diff --git a/g10/keydb.h b/g10/keydb.h index 43862d2cb..88bdccf0d 100644 --- a/g10/keydb.h +++ b/g10/keydb.h @@ -144,6 +144,7 @@ int get_pubkey_byfprint( PKT_public_key *pk, const byte *fprint, size_t fprint_len ); int get_keyblock_byfprint( KBNODE *ret_keyblock, const byte *fprint, size_t fprint_len ); +int get_keyblock_bylid( KBNODE *ret_keyblock, ulong lid ); int seckey_available( u32 *keyid ); int get_seckey_byname( PKT_secret_key *sk, const char *name, int unlock ); int get_seckey_bynames( GETKEY_CTX *rx, PKT_secret_key *sk, diff --git a/g10/mainproc.c b/g10/mainproc.c index c41e72352..dd91fcc5e 100644 --- a/g10/mainproc.c +++ b/g10/mainproc.c @@ -182,6 +182,14 @@ proc_pubkey_enc( CTX c, PACKET *pkt ) * function to check it. */ if( opt.verbose ) log_info(_("public key is %08lX\n"), (ulong)enc->keyid[1] ); + + if( is_status_enabled() ) { + char buf[50]; + sprintf(buf, "%08lX%08lX", (ulong)enc->keyid[0], (ulong)enc->keyid[1]); + write_status_text( STATUS_ENC_TO, buf ); + } + + if( is_ELGAMAL(enc->pubkey_algo) || enc->pubkey_algo == PUBKEY_ALGO_DSA || is_RSA(enc->pubkey_algo) ) { @@ -914,7 +922,11 @@ check_sig_and_print( CTX c, KBNODE node ) g10_exit(1); } else { - write_status( STATUS_ERRSIG ); + char buf[50]; + sprintf(buf, "%08lX%08lX %d", + (ulong)sig->keyid[0], (ulong)sig->keyid[1], + sig->pubkey_algo ); + write_status_text( STATUS_ERRSIG, buf ); log_error(_("Can't check signature: %s\n"), g10_errstr(rc) ); } return rc; diff --git a/g10/passphrase.c b/g10/passphrase.c index 785d21df5..6da528015 100644 --- a/g10/passphrase.c +++ b/g10/passphrase.c @@ -133,6 +133,16 @@ passphrase_to_dek( u32 *keyid, int cipher_algo, STRING2KEY *s2k, int mode ) :DEFAULT_DIGEST_ALGO; } + if( keyid && !next_pw && is_status_enabled() ) { + char buf[50]; + sprintf( buf, "%08lX%08lX", (ulong)keyid[0], (ulong)keyid[1] ); + if( keyid[2] && keyid[3] && keyid[0] != keyid[2] + && keyid[1] != keyid[3] ) + sprintf( buf+strlen(buf), " %08lX%08lX", + (ulong)keyid[2], (ulong)keyid[3] ); + write_status_text( STATUS_NEED_PASSPHRASE, buf ); + } + if( keyid && !opt.batch && !next_pw ) { PKT_public_key *pk = m_alloc_clear( sizeof *pk ); size_t n; @@ -159,15 +169,6 @@ passphrase_to_dek( u32 *keyid, int cipher_algo, STRING2KEY *s2k, int mode ) tty_printf("\n"); free_public_key( pk ); } - else if( keyid && !next_pw ) { - char buf[50]; - sprintf( buf, "%08lX%08lX", (ulong)keyid[0], (ulong)keyid[1] ); - if( keyid[2] && keyid[3] && keyid[0] != keyid[2] - && keyid[1] != keyid[3] ) - sprintf( buf+strlen(buf), " %08lX%08lX", - (ulong)keyid[2], (ulong)keyid[3] ); - write_status_text( STATUS_NEED_PASSPHRASE, buf ); - } if( next_pw ) { pw = next_pw; diff --git a/g10/pkclist.c b/g10/pkclist.c index fffaef821..57c36cee2 100644 --- a/g10/pkclist.c +++ b/g10/pkclist.c @@ -107,8 +107,8 @@ show_paths( ulong lid, int only_first ) /**************** * Returns true if an ownertrust has changed. */ -int -edit_ownertrust( ulong lid, int mode ) +static int +do_edit_ownertrust( ulong lid, int mode, unsigned *new_trust ) { char *p; int rc; @@ -117,6 +117,7 @@ edit_ownertrust( ulong lid, int mode ) PKT_public_key *pk ; int changed=0; int quit=0; + int show=0; rc = keyid_from_lid( lid, keyid ); if( rc ) { @@ -177,14 +178,15 @@ edit_ownertrust( ulong lid, int mode ) case '4': trust = TRUST_FULLY ; break; default: BUG(); } - if( !update_ownertrust( lid, trust ) ) - changed++; + *new_trust = trust; + changed = 1; break; } else if( *p == ans[0] || *p == ans[1] ) { tty_printf(_( "Certificates leading to an ultimately trusted key:\n")); - show_paths( lid, 1 ); + show = 1; + break; } else if( mode && (*p == ans[2] || *p == ans[3] || *p == CONTROL_D ) ) { break ; /* back to the menu */ @@ -197,73 +199,71 @@ edit_ownertrust( ulong lid, int mode ) } m_free(p); m_free(pk); - return quit? -1 : changed; + return show? -2: quit? -1 : changed; } +int +edit_ownertrust( ulong lid, int mode ) +{ + unsigned trust; + + for(;;) { + switch( do_edit_ownertrust( lid, mode, &trust ) ) { + case -1: + return 0; + case -2: + show_paths( lid, 1 ); + break; + case 1: + if( !update_ownertrust( lid, trust ) ) + return 1; + return 0; + default: + return 0; + } + } +} + +static int +add_ownertrust_cb( ulong lid ) +{ + unsigned trust; + int rc = do_edit_ownertrust( lid, 0, &trust ); + + if( rc == 1 ) + return trust & TRUST_MASK; + return rc > 0? 0 : rc; +} + /**************** * Try to add some more owner trusts (interactive) * This function presents all the signator in a certificate - * chain who have no trust value assigned. + * chain who have no ownertrust value assigned. * Returns: -1 if no ownertrust were added. */ static int -add_ownertrust( PKT_public_key *pk, int *quit ) +add_ownertrust( PKT_public_key *pk, int *quit, unsigned *trustlevel ) { int rc; - void *context = NULL; - ulong lid; - unsigned otrust, validity; - int any=0, changed=0, any_undefined=0; + unsigned flags = 0; *quit = 0; + *trustlevel = 0; tty_printf( _("Could not find a valid trust path to the key. Let's see whether we\n" "can assign some missing owner trust values.\n\n")); - rc = query_trust_record( pk ); - if( rc ) { - log_error("Ooops: not in trustdb\n"); - return -1; - } + rc = check_trust( pk, trustlevel, NULL, add_ownertrust_cb, &flags ); - lid = pk->local_id; - while( enum_cert_paths( &context, &lid, &otrust, &validity ) != -1 ) { - if( lid == pk->local_id ) - continue; - any=1; - if( changed ) { - /* because enum_cert_paths() makes a snapshop of the - * trust paths, the otrust and validity are not anymore - * valid after changing an entry - we have to reread - * those values from then on - */ - otrust = get_ownertrust( lid ); - /* fixme: and the validity? */ - } - if( otrust == TRUST_UNDEFINED ) { - any_undefined=1; - enum_cert_paths_print( &context, NULL, changed, lid ); - tty_printf("\n"); - rc = edit_ownertrust( lid, 0 ); - if( rc == -1 ) { - *quit = 1; - break; - } - else if( rc > 0 ) - changed = 1; - } - } - enum_cert_paths( &context, NULL, NULL, NULL ); /* release context */ - - if( !any ) + if( !(flags & 1) ) tty_printf(_("No path leading to one of our keys found.\n\n") ); - else if( !any_undefined ) + else if( !(flags & 2) ) tty_printf(_("No certificates with undefined trust found.\n\n") ); - else if( !changed ) + else if( !(flags & 4) ) tty_printf(_("No trust values changed.\n\n") ); - return changed? 0:-1; + return (flags & 4)? 0:-1; } /**************** @@ -274,7 +274,9 @@ static int do_we_trust( PKT_public_key *pk, int trustlevel ) { int rc; + int did_add = 0; + retry: if( (trustlevel & TRUST_FLAG_REVOKED) ) { log_info(_("key %08lX: key has been revoked!\n"), (ulong)keyid_from_pk( pk, NULL) ); @@ -295,7 +297,7 @@ do_we_trust( PKT_public_key *pk, int trustlevel ) g10_errstr(rc) ); return 0; /* no */ } - rc = check_trust( pk, &trustlevel, NULL ); + rc = check_trust( pk, &trustlevel, NULL, NULL, NULL ); if( rc ) log_fatal("trust check after insert failed: %s\n", g10_errstr(rc) ); @@ -317,14 +319,10 @@ do_we_trust( PKT_public_key *pk, int trustlevel ) else { int quit; - rc = add_ownertrust( pk, &quit ); - if( !rc && !quit ) { - rc = check_trust( pk, &trustlevel, NULL ); - if( rc ) - log_fatal("trust check after add_ownertrust failed: %s\n", - g10_errstr(rc) ); - /* fixme: this is recursive; we should unroll it */ - return do_we_trust( pk, trustlevel ); + rc = add_ownertrust( pk, &quit, &trustlevel ); + if( !rc && !did_add && !quit ) { + did_add = 1; + goto retry; } } return 0; @@ -353,10 +351,6 @@ do_we_trust( PKT_public_key *pk, int trustlevel ) default: BUG(); } - - /* Eventuell fragen falls der trustlevel nicht ausreichend ist */ - - return 1; /* yes */ } @@ -419,7 +413,7 @@ check_signatures_trust( PKT_signature *sig ) { PKT_public_key *pk = m_alloc_clear( sizeof *pk ); int trustlevel; - int dont_try = 0; + int did_add = 0; int rc=0; rc = get_pubkey( pk, sig->keyid ); @@ -429,13 +423,13 @@ check_signatures_trust( PKT_signature *sig ) goto leave; } - retry: - rc = check_trust( pk, &trustlevel, NULL ); + rc = check_trust( pk, &trustlevel, NULL, NULL, NULL ); if( rc ) { log_error("check trust failed: %s\n", g10_errstr(rc)); goto leave; } + retry: if( (trustlevel & TRUST_FLAG_REVOKED) ) { write_status( STATUS_KEYREVOKED ); log_info(_("WARNING: This key has been revoked by its owner!\n")); @@ -451,7 +445,7 @@ check_signatures_trust( PKT_signature *sig ) g10_errstr(rc) ); goto leave; } - rc = check_trust( pk, &trustlevel, NULL ); + rc = check_trust( pk, &trustlevel, NULL, NULL, NULL ); if( rc ) log_fatal("trust check after insert failed: %s\n", g10_errstr(rc) ); @@ -464,7 +458,7 @@ check_signatures_trust( PKT_signature *sig ) break; case TRUST_UNDEFINED: - if( dont_try || opt.batch || opt.answer_no ) { + if( did_add || opt.batch || opt.answer_no ) { write_status( STATUS_TRUST_UNDEFINED ); log_info(_( "WARNING: This key is not certified with a trusted signature!\n")); @@ -474,9 +468,9 @@ check_signatures_trust( PKT_signature *sig ) } else { int quit; - rc = add_ownertrust( pk, &quit ); + rc = add_ownertrust( pk, &quit, &trustlevel ); if( rc || quit ) { - dont_try = 1; + did_add = 1; rc = 0; } goto retry; @@ -591,7 +585,7 @@ build_pk_list( STRLIST remusr, PK_LIST *ret_pk_list, unsigned use ) else if( !(rc=check_pubkey_algo2(pk->pubkey_algo, use)) ) { int trustlevel; - rc = check_trust( pk, &trustlevel, NULL ); + rc = check_trust( pk, &trustlevel, NULL, NULL, NULL ); if( rc ) { log_error("error checking pk of `%s': %s\n", answer, g10_errstr(rc) ); @@ -630,7 +624,7 @@ build_pk_list( STRLIST remusr, PK_LIST *ret_pk_list, unsigned use ) else if( !(rc=check_pubkey_algo2(pk->pubkey_algo, use )) ) { int trustlevel; - rc = check_trust( pk, &trustlevel, NULL ); + rc = check_trust( pk, &trustlevel, NULL, NULL, NULL ); if( rc ) { free_public_key( pk ); pk = NULL; log_error(_("%s: error checking key: %s\n"), diff --git a/g10/seckey-cert.c b/g10/seckey-cert.c index 661f1da43..7f0d41e33 100644 --- a/g10/seckey-cert.c +++ b/g10/seckey-cert.c @@ -32,6 +32,7 @@ #include "main.h" #include "options.h" #include "i18n.h" +#include "status.h" static int @@ -175,6 +176,14 @@ check_secret_key( PKT_secret_key *sk, int n ) if( i ) log_info(_("Invalid passphrase; please try again ...\n")); rc = do_check( sk ); + if( rc == G10ERR_BAD_PASS && is_status_enabled() ) { + u32 kid[2]; + char buf[50]; + + keyid_from_sk( sk, kid ); + sprintf(buf, "%08lX%08lX", (ulong)kid[0], (ulong)kid[1]); + write_status_text( STATUS_BAD_PASSPHRASE, buf ); + } if( have_static_passphrase() ) break; } diff --git a/g10/status.c b/g10/status.c index e34dda3d9..1b6dc1095 100644 --- a/g10/status.c +++ b/g10/status.c @@ -99,6 +99,11 @@ write_status_text ( int no, const char *text) case STATUS_NEED_PASSPHRASE: s = "NEED_PASSPHRASE\n"; break; case STATUS_VALIDSIG : s = "VALIDSIG\n"; break; case STATUS_SIG_ID : s = "SIG_ID\n"; break; + case STATUS_ENC_TO : s = "ENC_TO\n"; break; + case STATUS_NODATA : s = "NODATA\n"; break; + case STATUS_BAD_PASSPHRASE : s = "BAD_PASSPHRASE\n"; break; + case STATUS_NO_PUBKEY : s = "NO_PUBKEY\n"; break; + case STATUS_NO_SECKEY : s = "NO_SECKEY\n"; break; default: s = "?\n"; break; } diff --git a/g10/status.h b/g10/status.h index 734c29983..7beb32123 100644 --- a/g10/status.h +++ b/g10/status.h @@ -50,6 +50,11 @@ #define STATUS_NEED_PASSPHRASE 20 #define STATUS_VALIDSIG 21 #define STATUS_SIG_ID 22 +#define STATUS_ENC_TO 23 +#define STATUS_NODATA 24 +#define STATUS_BAD_PASSPHRASE 25 +#define STATUS_NO_PUBKEY 26 +#define STATUS_NO_SECKEY 27 /*-- status.c --*/ void set_status_fd ( int fd ); diff --git a/g10/tdbio.c b/g10/tdbio.c index 22592ae93..9bcb6aa14 100644 --- a/g10/tdbio.c +++ b/g10/tdbio.c @@ -633,12 +633,15 @@ tdbio_read_modify_stamp( int modify_down ) } void -tdbio_write_modify_stamp( int down, int up ) +tdbio_write_modify_stamp( int up, int down ) { TRUSTREC vr; int rc; ulong stamp; + if( !(up || down) ) + return; + rc = tdbio_read_record( 0, &vr, RECTYPE_VER ); if( rc ) log_fatal( _("%s: error reading version record: %s\n"), @@ -651,7 +654,7 @@ tdbio_write_modify_stamp( int down, int up ) vr.r.ver.mod_up = stamp; rc = tdbio_write_record( &vr ); - if( !rc ) + if( rc ) log_fatal( _("%s: error writing version record: %s\n"), db_name, g10_errstr(rc) ); } diff --git a/g10/tdbio.h b/g10/tdbio.h index 9c97dd108..ca9d23e8b 100644 --- a/g10/tdbio.h +++ b/g10/tdbio.h @@ -176,7 +176,7 @@ int tdbio_read_record( ulong recnum, TRUSTREC *rec, int expected ); int tdbio_write_record( TRUSTREC *rec ); int tdbio_db_matches_options(void); ulong tdbio_read_modify_stamp( int modify_down ); -void tdbio_write_modify_stamp( int down, int up ); +void tdbio_write_modify_stamp( int up, int down ); int tdbio_is_dirty(void); int tdbio_sync(void); int tdbio_begin_transaction(void); diff --git a/g10/trustdb.c b/g10/trustdb.c index 557b4cc51..88e06c315 100644 --- a/g10/trustdb.c +++ b/g10/trustdb.c @@ -41,6 +41,7 @@ #include "main.h" #include "i18n.h" #include "tdbio.h" +#include "ttyio.h" #if MAX_FINGERPRINT_LEN > 20 #error Must change structure of trustdb @@ -115,16 +116,20 @@ static int ins_lid_table_item( LOCAL_ID_TABLE tbl, ulong lid, unsigned flag ); static int qry_lid_table_flag( LOCAL_ID_TABLE tbl, ulong lid, unsigned *flag ); -static void propagate_validity( TN node ); +static int propagate_validity( TN root, TN node, + int (*add_fnc)(ulong), unsigned *retflgs ); -static void print_user_id( const char *text, u32 *keyid ); -static int do_check( TRUSTREC *drec, unsigned *trustlevel, const char *nhash); +static void print_user_id( FILE *fp, const char *text, u32 *keyid ); +static int do_check( TRUSTREC *drec, unsigned *trustlevel, + const char *nhash, int (*add_fnc)(ulong), + unsigned *retflgs); static int get_dir_record( PKT_public_key *pk, TRUSTREC *rec ); static void upd_pref_record( TRUSTREC *urec, u32 *keyid, PKT_signature *sig ); static void upd_cert_record( KBNODE keyblock, KBNODE signode, u32 *keyid, TRUSTREC *drec, RECNO_LIST *recno_list, int recheck, - TRUSTREC *urec, const byte *uidhash, int revoked ); + TRUSTREC *urec, const byte *uidhash, int revoked, + int *mod_up, int *mod_down ); /* a table used to keep track of ultimately trusted keys * which are the ones from our secrings and the trusted keys */ @@ -375,6 +380,7 @@ keyid_from_lid( ulong lid, u32 *keyid ) int rc; init_trustdb(); + keyid[0] = keyid[1] = 0; rc = tdbio_read_record( lid, &rec, 0 ); if( rc ) { log_error(_("error reading dir record for LID %lu: %s\n"), @@ -622,20 +628,23 @@ init_trustdb() ************* Print helpers **************** ***********************************************/ static void -print_user_id( const char *text, u32 *keyid ) +print_user_id( FILE *fp, const char *text, u32 *keyid ) { char *p; size_t n; p = get_user_id( keyid, &n ); - if( *text ) { - fputs( text, stdout); - putchar(' '); + if( fp ) { + fprintf( fp, "%s \"", text ); + print_string( fp, p, n, 0 ); + putc('\"', fp); + putc('\n', fp); + } + else { + tty_printf( "%s \"", text ); + tty_print_string( p, n ); + tty_printf( "\"\n" ); } - putchar('\"'); - print_string( stdout, p, n, 0 ); - putchar('\"'); - putchar('\n'); m_free(p); } @@ -699,35 +708,122 @@ print_path( int pathlen, TN ME .........., FILE *fp, ulong highlight ) static void -print_default_uid( ulong lid ) +print_default_uid( FILE *fp, ulong lid ) { u32 keyid[2]; if( !keyid_from_lid( lid, keyid ) ) - print_user_id( "", keyid ); + print_user_id( fp, "", keyid ); } static void -dump_tn_tree( int indent, TN tree ) +print_uid_from_keyblock( FILE *fp, KBNODE keyblock, ulong urecno ) +{ + TRUSTREC urec; + KBNODE node; + byte uhash[20]; + + read_record( urecno, &urec, RECTYPE_UID ); + for( node=keyblock; node; node = node->next ) { + if( node->pkt->pkttype == PKT_USER_ID ) { + PKT_user_id *uidpkt = node->pkt->pkt.user_id; + + rmd160_hash_buffer( uhash, uidpkt->name, uidpkt->len ); + if( !memcmp( uhash, urec.r.uid.namehash, 20 ) ) { + print_string( fp, uidpkt->name, uidpkt->len, ':' ); + return; + } + } + } + + fputs("[?]", fp ); +} + + + +static void +dump_tn_tree( FILE *fp, int level, TN tree ) { TN kr, ur; for( kr=tree; kr; kr = kr->next ) { - printf("%*s", indent*4, "" ); - printf("K%lu(ot=%d,val=%d) ", kr->lid, - kr->n.k.ownertrust, - kr->n.k.validity ); - print_default_uid( kr->lid ); - for( ur=kr->list; ur; ur = ur->next ) { - printf("%*s ", indent*4, "" ); - printf("U%lu(mc=%d,fc=%d,val=%d)\n", ur->lid, - ur->n.u.marginal_count, - ur->n.u.fully_count, - ur->n.u.validity - ); - dump_tn_tree( indent+1, ur->list ); + if( fp ) { + fprintf( fp, "%*s", level*4, "" ); + fprintf( fp, "K%lu(ot=%d,val=%d) ", kr->lid, + kr->n.k.ownertrust, + kr->n.k.validity ); } + else { + tty_printf("%*s", level*4, "" ); + tty_printf("K%lu(ot=%d,val=%d) ", kr->lid, + kr->n.k.ownertrust, + kr->n.k.validity ); + } + print_default_uid( fp, kr->lid ); + for( ur=kr->list; ur; ur = ur->next ) { + if( fp ) { + fprintf(fp, "%*s ", level*4, "" ); + fprintf(fp, "U%lu(mc=%d,fc=%d,val=%d)\n", ur->lid, + ur->n.u.marginal_count, + ur->n.u.fully_count, + ur->n.u.validity + ); + } + else { + tty_printf("%*s ", level*4, "" ); + tty_printf("U%lu(mc=%d,fc=%d,val=%d)\n", ur->lid, + ur->n.u.marginal_count, + ur->n.u.fully_count, + ur->n.u.validity + ); + } + dump_tn_tree( fp, level+1, ur->list ); + } + } +} + +/**************** + * Special version of dump_tn_tree, which prints it colon delimited. + * Format: + * level:keyid:type:recno:ot:val:mc:cc:name: + * With TYPE = U for a user ID + * K for a key + * The RECNO is either the one of the dir record or the one of the uid record. + * OT is the the usual trust letter and only availabel on K lines. + * VAL is the calcualted validity + * MC is the marginal trust counter and only available on U lines + * CC is the same for the complete count + * NAME ist the username and only printed on U lines + */ +static void +dump_tn_tree_with_colons( int level, TN tree ) +{ + TN kr, ur; + + for( kr=tree; kr; kr = kr->next ) { + KBNODE kb = NULL; + u32 kid[2]; + + keyid_from_lid( kr->lid, kid ); + get_keyblock_bylid( &kb, kr->lid ); + + printf( "%d:%08lX%08lX:K:%lu:%c:%c::::\n", + level, (ulong)kid[0], (ulong)kid[1], kr->lid, + trust_letter( kr->n.k.ownertrust ), + trust_letter( kr->n.k.validity ) ); + for( ur=kr->list; ur; ur = ur->next ) { + printf( "%d:%08lX%08lX:U:%lu::%c:%d:%d:", + level, (ulong)kid[0], (ulong)kid[1], ur->lid, + trust_letter( kr->n.u.validity ), + ur->n.u.marginal_count, + ur->n.u.fully_count ); + print_uid_from_keyblock( stdout, kb, ur->lid ); + putchar(':'); + putchar('\n'); + dump_tn_tree_with_colons( level+1, ur->list ); + } + release_kbnode( kb ); } } @@ -851,6 +947,7 @@ check_hint_sig( ulong lid, KBNODE keyblock, u32 *keyid, byte *uidrec_hash, * Process a hintlist. * Fixme: this list is not anymore anchored to another * record, so it should be put elsewehere in case of an error + * FIXME: add mod_up/down handling */ static void process_hintlist( ulong hintlist, ulong hint_owner ) @@ -1184,7 +1281,8 @@ upd_key_record( KBNODE keyblock, KBNODE keynode, u32 *keyid, */ static void upd_uid_record( KBNODE keyblock, KBNODE uidnode, u32 *keyid, - TRUSTREC *drec, RECNO_LIST *recno_list, int recheck ) + TRUSTREC *drec, RECNO_LIST *recno_list, + int recheck, int *mod_up, int *mod_down ) { ulong lid = drec->recnum; PKT_user_id *uid = uidnode->pkt->pkt.user_id; @@ -1327,10 +1425,10 @@ upd_uid_record( KBNODE keyblock, KBNODE uidnode, u32 *keyid, write_record( &urec ); if( !( urec.r.uid.uidflags & UIDF_VALID ) || ( urec.r.uid.uidflags & UIDF_REVOKED ) ) - ; /*FIXME: mark as modified down */ + *mod_down=1; else - ; /*FIXME: mark as modified up (maybe a new uuser id)*/ - + *mod_up=1; /*(maybe a new user id)*/ + /* Hmmm, did we catch changed expiration dates? */ } } /* end check self-signatures */ @@ -1362,11 +1460,11 @@ upd_uid_record( KBNODE keyblock, KBNODE uidnode, u32 *keyid, if( (sig->sig_class&~3) == 0x10 ) { /* regular certification */ upd_cert_record( keyblock, node, keyid, drec, recno_list, - recheck, &urec, uidhash, 0 ); + recheck, &urec, uidhash, 0, mod_up, mod_down ); } else if( sig->sig_class == 0x30 ) { /* cert revocation */ upd_cert_record( keyblock, node, keyid, drec, recno_list, - recheck, &urec, uidhash, 1 ); + recheck, &urec, uidhash, 1, mod_up, mod_down ); } } /* end check certificates */ @@ -1490,11 +1588,11 @@ upd_pref_record( TRUSTREC *urec, u32 *keyid, PKT_signature *sig ) } -/* FIXME: add logic to set the modify_{down,up} */ static void upd_cert_record( KBNODE keyblock, KBNODE signode, u32 *keyid, TRUSTREC *drec, RECNO_LIST *recno_list, int recheck, - TRUSTREC *urec, const byte *uidhash, int revoked ) + TRUSTREC *urec, const byte *uidhash, int revoked, + int *mod_up, int *mod_down ) { /* We simply insert the signature into the sig records but * avoid duplicate ones. We do not check them here because @@ -1577,8 +1675,12 @@ upd_cert_record( KBNODE keyblock, KBNODE signode, u32 *keyid, revoked? _("Valid certificate revocation") : _("Good certificate") ); rec.r.sig.sig[i].flag = SIGF_CHECKED | SIGF_VALID; - if( revoked ) /* we are investigating revocations */ + if( revoked ) { /* we are investigating revocations */ rec.r.sig.sig[i].flag |= SIGF_REVOKED; + *mod_down = 1; + } + else + *mod_up = 1; } else if( rc == G10ERR_NO_PUBKEY ) { /* This may happen if the key is still in the trustdb @@ -1589,6 +1691,7 @@ upd_cert_record( KBNODE keyblock, KBNODE signode, u32 *keyid, uidhash[19], (ulong)sig->keyid[1], _("public key not anymore available") ); rec.r.sig.sig[i].flag = SIGF_NOPUBKEY; + *mod_down = 1; if( revoked ) rec.r.sig.sig[i].flag |= SIGF_REVOKED; } @@ -1600,8 +1703,10 @@ upd_cert_record( KBNODE keyblock, KBNODE signode, u32 *keyid, : _("Invalid certificate"), g10_errstr(rc)); rec.r.sig.sig[i].flag = SIGF_CHECKED; - if( revoked ) + if( revoked ) { rec.r.sig.sig[i].flag |= SIGF_REVOKED; + *mod_down = 1; + } } rec.dirty = 1; } @@ -1662,8 +1767,12 @@ upd_cert_record( KBNODE keyblock, KBNODE signode, u32 *keyid, : _("Good certificate") ); newlid = pk_lid; /* this is the pk of the signature */ newflag = SIGF_CHECKED | SIGF_VALID; - if( revoked ) + if( revoked ) { newflag |= SIGF_REVOKED; + *mod_down = 1; + } + else + *mod_up = 1; } else if( rc == G10ERR_NO_PUBKEY ) { if( opt.verbose > 1 || DBG_TRUST ) @@ -1686,6 +1795,7 @@ upd_cert_record( KBNODE keyblock, KBNODE signode, u32 *keyid, newflag = SIGF_CHECKED; if( revoked ) newflag |= SIGF_REVOKED; + *mod_down = 1; } if( delrec.recnum ) { /* we can reuse an unused slot */ @@ -1730,6 +1840,8 @@ update_trust_record( KBNODE keyblock, int recheck, int *modified ) int rc = 0; u32 keyid[2]; /* keyid of primary key */ ulong recno, lastrecno; + int mod_up = 0; + int mod_down = 0; RECNO_LIST recno_list = NULL; /* list of verified records */ /* fixme: replace recno_list by a lookup on node->recno */ @@ -1767,7 +1879,7 @@ update_trust_record( KBNODE keyblock, int recheck, int *modified ) for( node=keyblock; node; node = node->next ) { if( node->pkt->pkttype == PKT_USER_ID ) upd_uid_record( keyblock, node, keyid, - &drec, &recno_list, recheck ); + &drec, &recno_list, recheck, &mod_up, &mod_down ); } /* delete keyrecords from the trustdb which are not anymore used */ @@ -1842,6 +1954,7 @@ update_trust_record( KBNODE keyblock, int recheck, int *modified ) drec.r.dir.dirflags |= DIRF_CHECKED; drec.r.dir.valcheck = 0; write_record( &drec ); + tdbio_write_modify_stamp( mod_up, mod_down ); rc = tdbio_end_transaction(); } rel_recno_list( &recno_list ); @@ -1854,7 +1967,7 @@ update_trust_record( KBNODE keyblock, int recheck, int *modified ) * This function assumes that the record does not yet exist. */ int -insert_trust_record( PKT_public_key *pk ) +insert_trust_record( PKT_public_key *orig_pk ) { TRUSTREC dirrec; TRUSTREC shadow; @@ -1864,6 +1977,7 @@ insert_trust_record( PKT_public_key *pk ) size_t fingerlen; int rc = 0; ulong hintlist = 0; + PKT_public_key *pk; if( opt.dry_run ) @@ -1871,7 +1985,7 @@ insert_trust_record( PKT_public_key *pk ) init_trustdb(); - fingerprint_from_pk( pk, fingerprint, &fingerlen ); + fingerprint_from_pk( orig_pk, fingerprint, &fingerlen ); /* fixme: assert that we do not have this record. * we can do this by searching for the primary keyid @@ -1883,6 +1997,10 @@ insert_trust_record( PKT_public_key *pk ) * to the primary one which has the user ids etc.) */ + if( orig_pk->local_id ) + log_debug("insert_trust_record with pk->local_id=%lu (1)\n", + orig_pk->local_id ); + /* get the keyblock which has the key */ rc = get_keyblock_byfprint( &keyblock, fingerprint, fingerlen ); if( rc ) { /* that should never happen */ @@ -1891,32 +2009,18 @@ insert_trust_record( PKT_public_key *pk ) goto leave; } + /* make sure that we use the primary key */ + pk = find_kbnode( keyblock, PKT_PUBLIC_KEY )->pkt->pkt.public_key; + if( pk->local_id ) { - log_debug("insert_trust_reord with pk->local_id=%lu\n", pk->local_id ); + orig_pk->local_id = pk->local_id; + log_debug("insert_trust_record with pk->local_id=%lu (2)\n", + pk->local_id ); rc = update_trust_record( keyblock, 1, NULL ); release_kbnode( keyblock ); return rc; } - /* check that we used the primary key (we are little bit paranoid) */ - { PKT_public_key *a_pk; - u32 akid[2], bkid[2]; - - node = find_kbnode( keyblock, PKT_PUBLIC_KEY ); - a_pk = node->pkt->pkt.public_key; - - /* we can't use cmp_public_keys here because some parts (expiredate) - * might not be set in pk <--- but why (fixme) */ - keyid_from_pk( a_pk, akid ); - keyid_from_pk( pk, bkid ); - - if( akid[0] != bkid[0] || akid[1] != bkid[1] ) { - log_error(_("did not use primary key for insert_trust_record()\n")); - rc = G10ERR_GENERAL; - goto leave; - } - } - /* We have to look for a shadow dir record which must be reused * as the dir record. And: check all signatures which are listed * in the hintlist of the shadow dir record. @@ -1942,6 +2046,7 @@ insert_trust_record( PKT_public_key *pk ) /* out the LID into the keyblock */ pk->local_id = dirrec.r.dir.lid; + orig_pk->local_id = dirrec.r.dir.lid; for( node=keyblock; node; node = node->next ) { if( node->pkt->pkttype == PKT_PUBLIC_KEY || node->pkt->pkttype == PKT_PUBLIC_SUBKEY ) { @@ -1954,7 +2059,8 @@ insert_trust_record( PKT_public_key *pk ) } } - /* FIXME: mark tdb as modified upwards */ + /* mark tdb as modified upwards */ + tdbio_write_modify_stamp( 1, 0 ); /* and put all the other stuff into the keydb */ rc = update_trust_record( keyblock, 1, NULL ); @@ -2100,9 +2206,57 @@ build_cert_tree( ulong lid, int depth, int max_depth, TN helproot ) } - static void -propagate_validity( TN node ) +upd_one_ownertrust( ulong lid, unsigned new_trust, unsigned *retflgs ) +{ + TRUSTREC rec; + + read_record( lid, &rec, RECTYPE_DIR ); + if( DBG_TRUST ) + log_debug("upd_one_ownertrust of %lu from %u to %u\n", + lid, (unsigned)rec.r.dir.ownertrust, new_trust ); + if( retflgs ) { + if( new_trust > rec.r.dir.ownertrust ) + *retflgs |= 16; /* modified up */ + else + *retflgs |= 32; /* modified down */ + } + rec.r.dir.ownertrust = new_trust; + write_record( &rec ); +} + +/**************** + * Update the ownertrust in the complete tree. + */ +static void +propagate_ownertrust( TN kr, ulong lid, unsigned trust ) +{ + TN ur; + + for( ; kr; kr = kr->next ) { + if( kr->lid == lid ) + kr->n.k.ownertrust = trust; + for( ur=kr->list; ur; ur = ur->next ) + propagate_ownertrust( ur->list, lid, trust ); + } +} + +/**************** + * Calculate the validity of all keys in the tree and especially + * the one of the top key. If add_fnc is not NULL, it is used to + * ask for missing ownertrust values (but only if this will help + * us to increase the validity. + * add_fnc is expected to take the LID of the key under question + * and return a ownertrust value or an error: positive values + * are assumed to be the new ownertrust value; a 0 does mean no change, + * a -1 is a request to cancel this validation procedure, a -2 requests + * a listing of the sub-tree using the tty functions. + * + * + * Returns: 0 = okay + */ +static int +propagate_validity( TN root, TN node, int (*add_fnc)(ulong), unsigned *retflgs ) { TN kr, ur; int max_validity = 0; @@ -2112,7 +2266,9 @@ propagate_validity( TN node ) /* this is one of our keys */ assert( !node->list ); /* it should be a leaf */ node->n.k.validity = TRUST_ULTIMATE; - return; + if( retflgs ) + *retflgs |= 1; /* found a path to an ultimately trusted key */ + return 0; } /* loop over all user ids */ @@ -2120,11 +2276,39 @@ propagate_validity( TN node ) assert( ur->is_uid ); /* loop over all signators */ for(kr=ur->list; kr; kr = kr->next ) { - propagate_validity( kr ); + if( propagate_validity( root, kr, add_fnc, retflgs ) ) + return -1; /* quit */ if( kr->n.k.validity == TRUST_ULTIMATE ) { ur->n.u.fully_count = opt.completes_needed; } else if( kr->n.k.validity == TRUST_FULLY ) { + if( add_fnc && !kr->n.k.ownertrust ) { + int rc; + + if( retflgs ) + *retflgs |= 2; /* found key with undefined ownertrust*/ + do { + rc = add_fnc( kr->lid ); + switch( rc ) { + case TRUST_NEVER: + case TRUST_MARGINAL: + case TRUST_FULLY: + propagate_ownertrust( root, kr->lid, rc ); + upd_one_ownertrust( kr->lid, rc, retflgs ); + if( retflgs ) + *retflgs |= 4; /* changed */ + break; + case -1: + return -1; /* cancel */ + case -2: + dump_tn_tree( NULL, 0, kr ); + tty_printf("\n"); + break; + default: + break; + } + } while( rc == -2 ); + } if( kr->n.k.ownertrust == TRUST_FULLY ) ur->n.u.fully_count++; else if( kr->n.k.ownertrust == TRUST_MARGINAL ) @@ -2145,6 +2329,7 @@ propagate_validity( TN node ) } node->n.k.validity = max_validity; + return 0; } @@ -2155,15 +2340,17 @@ propagate_validity( TN node ) * checking all key signatures up to a some depth. */ static int -verify_key( int max_depth, TRUSTREC *drec, const char *namehash ) +verify_key( int max_depth, TRUSTREC *drec, const char *namehash, + int (*add_fnc)(ulong), unsigned *retflgs ) { TN tree; int keytrust; + int pv_result; tree = build_cert_tree( drec->r.dir.lid, 0, opt.max_cert_depth, NULL ); if( !tree ) return TRUST_UNDEFINED; - propagate_validity( tree ); + pv_result = propagate_validity( tree, tree, add_fnc, retflgs ); if( namehash ) { /* find the matching user id. * fixme: the way we handle this is too inefficient */ @@ -2183,7 +2370,8 @@ verify_key( int max_depth, TRUSTREC *drec, const char *namehash ) keytrust = tree->n.k.validity; /* update the cached validity values */ - if( keytrust >= TRUST_UNDEFINED + if( !pv_result + && keytrust >= TRUST_UNDEFINED && tdbio_db_matches_options() && ( !drec->r.dir.valcheck || drec->r.dir.validity != keytrust ) ) { TN ur; @@ -2213,7 +2401,8 @@ verify_key( int max_depth, TRUSTREC *drec, const char *namehash ) * but nothing more is known. */ static int -do_check( TRUSTREC *dr, unsigned *validity, const char *namehash ) +do_check( TRUSTREC *dr, unsigned *validity, + const char *namehash, int (*add_fnc)(ulong), unsigned *retflgs ) { if( !dr->r.dir.keylist ) { log_error(_("Ooops, no keys\n")); @@ -2224,22 +2413,39 @@ do_check( TRUSTREC *dr, unsigned *validity, const char *namehash ) return G10ERR_TRUSTDB; } + if( retflgs ) + *retflgs &= ~(16|32); /* reset the 2 special flags */ + if( namehash ) { /* Fixme: use the cache */ - *validity = verify_key( opt.max_cert_depth, dr, namehash ); + *validity = verify_key( opt.max_cert_depth, dr, namehash, + add_fnc, retflgs ); } - else if( tdbio_db_matches_options() + else if( !add_fnc + && tdbio_db_matches_options() && dr->r.dir.valcheck > tdbio_read_modify_stamp( (dr->r.dir.validity < TRUST_FULLY) ) && dr->r.dir.validity ) *validity = dr->r.dir.validity; else - *validity = verify_key( opt.max_cert_depth, dr, NULL ); + *validity = verify_key( opt.max_cert_depth, dr, NULL, + add_fnc, retflgs ); + + if( !(*validity & TRUST_MASK) ) + *validity = TRUST_UNDEFINED; if( dr->r.dir.dirflags & DIRF_REVOKED ) *validity |= TRUST_FLAG_REVOKED; + /* If we have changed some ownertrusts, set the trustdb timestamps + * and do a sync */ + if( retflgs && (*retflgs & (16|32)) ) { + tdbio_write_modify_stamp( (*retflgs & 16), (*retflgs & 32) ); + do_sync(); + } + + return 0; } @@ -2256,6 +2462,9 @@ update_ownertrust( ulong lid, unsigned new_trust ) init_trustdb(); read_record( lid, &rec, RECTYPE_DIR ); + if( DBG_TRUST ) + log_debug("update_ownertrust of %lu from %u to %u\n", + lid, (unsigned)rec.r.dir.ownertrust, new_trust ); rec.r.dir.ownertrust = new_trust; write_record( &rec ); do_sync(); @@ -2512,7 +2721,8 @@ query_trust_record( PKT_public_key *pk ) * is not necessary to check this if we use a local pubring. Hmmmm. */ int -check_trust( PKT_public_key *pk, unsigned *r_trustlevel, const byte *namehash ) +check_trust( PKT_public_key *pk, unsigned *r_trustlevel, + const byte *namehash, int (*add_fnc)(ulong), unsigned *retflgs ) { TRUSTREC rec; unsigned trustlevel = TRUST_UNKNOWN; @@ -2562,7 +2772,7 @@ check_trust( PKT_public_key *pk, unsigned *r_trustlevel, const byte *namehash ) trustlevel = TRUST_EXPIRED; } else { - rc = do_check( &rec, &trustlevel, namehash ); + rc = do_check( &rec, &trustlevel, namehash, add_fnc, retflgs ); if( rc ) { log_error(_("key %08lX.%lu: trust check failed: %s\n"), (ulong)keyid[1], pk->local_id, g10_errstr(rc)); @@ -2586,7 +2796,7 @@ query_trust_info( PKT_public_key *pk, const byte *namehash ) int c; init_trustdb(); - if( check_trust( pk, &trustlevel, namehash ) ) + if( check_trust( pk, &trustlevel, namehash, NULL, NULL ) ) return '?'; if( trustlevel & TRUST_FLAG_REVOKED ) return 'r'; @@ -2656,12 +2866,15 @@ list_trust_path( const char *username ) tree = build_cert_tree( lid, 0, opt.max_cert_depth, NULL ); if( tree ) - propagate_validity( tree ); - dump_tn_tree( 0, tree ); - printf("(alloced tns=%d max=%d)\n", alloced_tns, max_alloced_tns ); + propagate_validity( tree, tree, NULL, NULL ); + if( opt.with_colons ) + dump_tn_tree_with_colons( 0, tree ); + else + dump_tn_tree( stdout, 0, tree ); + /*printf("(alloced tns=%d max=%d)\n", alloced_tns, max_alloced_tns );*/ release_tn_tree( tree ); - printf("Ownertrust=%c Validity=%c\n", get_ownertrust_info( lid ), - query_trust_info( pk, NULL ) ); + /*printf("Ownertrust=%c Validity=%c\n", get_ownertrust_info( lid ), + query_trust_info( pk, NULL ) ); */ free_public_key( pk ); diff --git a/g10/trustdb.h b/g10/trustdb.h index dba9e5caf..64ccaae5d 100644 --- a/g10/trustdb.h +++ b/g10/trustdb.h @@ -47,7 +47,8 @@ void check_trustdb( const char *username ); void update_trustdb( void ); int setup_trustdb( int level, const char *dbname ); void init_trustdb( void ); -int check_trust( PKT_public_key *pk, unsigned *r_trustlevel, const byte* nh ); +int check_trust( PKT_public_key *pk, unsigned *r_trustlevel, + const byte* nh, int (*add_fnc)(ulong), unsigned *retflgs ); int query_trust_info( PKT_public_key *pk, const byte *nh ); int enum_cert_paths( void **context, ulong *lid, unsigned *ownertrust, unsigned *validity ); diff --git a/include/cipher.h b/include/cipher.h index 8e7012fa3..3c4edc7ce 100644 --- a/include/cipher.h +++ b/include/cipher.h @@ -88,6 +88,7 @@ typedef struct { int g10c_debug_mode; int g10_opt_verbose; +const char *g10_opt_homedir; /*-- dynload.c --*/ void register_cipher_extension( const char *mainpgm, const char *fname );