diff options
author | Werner Koch <wk@gnupg.org> | 2006-04-19 13:26:11 +0200 |
---|---|---|
committer | Werner Koch <wk@gnupg.org> | 2006-04-19 13:26:11 +0200 |
commit | 29b23dea9731e8f258211bc6fd733d205c18e2a8 (patch) | |
tree | ff2a0f66bda0c1f050e8fa00cbf610e18b91c9f7 | |
parent | 2006-04-14 Marcus Brinkmann <marcus@g10code.de> (diff) | |
download | gnupg2-29b23dea9731e8f258211bc6fd733d205c18e2a8.tar.xz gnupg2-29b23dea9731e8f258211bc6fd733d205c18e2a8.zip |
Merged with gpg 1.4.3 code.
The gpg part does not yet build.
90 files changed, 17949 insertions, 10511 deletions
@@ -1,3 +1,12 @@ +2006-04-18 Werner Koch <wk@g10code.com> + + * configure.ac (PK_UID_CACHE_SIZE): New. + +2006-04-07 Werner Koch <wk@g10code.com> + + * configure.ac: Use new method to include the SVN revison. Now it + is the actual global revision number. + 2005-12-20 Werner Koch <wk@g10code.com> Released 1.9.20. @@ -1,12 +1,14 @@ Noteworthy changes in version 1.9.21 ------------------------------------------------- + * [scdaemon] New command APDU. + * [scdaemon] Support for keypads of some readers. Tested only with SPR532. New option --disable-keypad. - * Support for CardMan 4040 PCMCIA reader. + * [scdaemon] Support for CardMan 4040 PCMCIA reader. - * Cards are not anymore reseted at the end of a connection. + * [scdaemon] Cards are not anymore reseted at the end of a connection. * [gpgsm] Kludge to allow use of Bundesnetzagentur issued certificates. diff --git a/common/ChangeLog b/common/ChangeLog index 4a184006a..54bce4538 100644 --- a/common/ChangeLog +++ b/common/ChangeLog @@ -1,3 +1,8 @@ +2006-04-18 Werner Koch <wk@g10code.com> + + * homedir.c (w32_shgetfolderpath): New. Taken from gpg 1.4.3. + (default_homedir): Use it. + 2005-10-08 Marcus Brinkmann <marcus@g10code.de> * signal.c (get_signal_name): Check value of HAVE_DECL_SYS_SIGLIST diff --git a/common/homedir.c b/common/homedir.c index ab5b1d270..a118cbac1 100644 --- a/common/homedir.c +++ b/common/homedir.c @@ -1,5 +1,5 @@ /* homedir.c - Setup the home directory. - * Copyright (C) 2004 Free Software Foundation, Inc. + * Copyright (C) 2004, 2006 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -41,6 +41,47 @@ #include "util.h" #include "sysutils.h" + +/* This is a helper function to load a Windows function from either of + one DLLs. */ +#ifdef HAVE_W32_SYSTEM +static HRESULT +w32_shgetfolderpath (HWND a, int b, HANDLE c, DWORD d, LPSTR e) +{ + static int initialized; + static HRESULT (WINAPI * func)(HWND,int,HANDLE,DWORD,LPSTR); + + if (!initialized) + { + static char *dllnames[] = { "shell32.dll", "shfolder.dll", NULL }; + void *handle; + int i; + + initialized = 1; + + for (i=0, handle = NULL; !handle && dllnames[i]; i++) + { + handle = dlopen (dllnames[i], RTLD_LAZY); + if (handle) + { + func = dlsym (handle, "SHGetFolderPathA"); + if (!func) + { + dlclose (handle); + handle = NULL; + } + } + } + } + + if (func) + return func (a,b,c,d,e); + else + return -1; +} +#endif /*HAVE_W32_SYSTEM*/ + + /* Set up the default home directory. The usual --homedir option should be parsed later. */ const char * @@ -56,15 +97,15 @@ default_homedir (void) { char path[MAX_PATH]; - /* fixme: It might be better to use LOCAL_APPDATA because this - is defined as "non roaming" and thus more likely to be kept + /* It might be better to use LOCAL_APPDATA because this is + defined as "non roaming" and thus more likely to be kept locally. For private keys this is desired. However, given that many users copy private keys anyway forth and back, - using a system roaming serives might be better than to let + using a system roaming services might be better than to let them do it manually. A security conscious user will anyway use the registry entry to have better control. */ - if (SHGetFolderPath(NULL, CSIDL_APPDATA|CSIDL_FLAG_CREATE, - NULL, 0, path) >= 0) + if (w32_shgetfolderpath (NULL, CSIDL_APPDATA|CSIDL_FLAG_CREATE, + NULL, 0, path) >= 0) { char *tmp = xmalloc (strlen (path) + 6 +1); strcpy (stpcpy (tmp, path), "\\gnupg"); diff --git a/common/iobuf.h b/common/iobuf.h index b991717c2..def0a6506 100644 --- a/common/iobuf.h +++ b/common/iobuf.h @@ -36,6 +36,7 @@ #define IOBUFCTRL_USER 16 typedef struct iobuf_struct *iobuf_t; +typedef struct iobuf_struct *IOBUF; /* Compatibility with gpg 1.4. */ /* fixme: we should hide most of this stuff */ struct iobuf_struct diff --git a/common/util.h b/common/util.h index 1ced59b67..68f5222b5 100644 --- a/common/util.h +++ b/common/util.h @@ -59,6 +59,10 @@ #define xrealloc(a,b) gcry_xrealloc ((a),(b)) #define xstrdup(a) gcry_xstrdup ((a)) +/* For compatibility with gpg 1.4 we also define these: */ +#define xmalloc_clear(a) gcry_xcalloc (1, (a)) +#define xmalloc_secure_clear(a) gcry_xcalloc_secure (1, (a)) + /* A type to hold the ISO time. Note that this this is the same as the the KSBA type ksba_isotime_t. */ @@ -133,7 +137,7 @@ int cmp_simple_canon_sexp (const unsigned char *a, const unsigned char *b); unsigned char *make_simple_sexp_from_hexstr (const char *line, size_t *nscanned); -/*-- homedir. c --*/ +/*-- homedir.c --*/ const char *default_homedir (void); diff --git a/configure.ac b/configure.ac index 9b0f04cb4..53cbc38fc 100644 --- a/configure.ac +++ b/configure.ac @@ -23,11 +23,16 @@ AC_PREREQ(2.52) min_automake_version="1.9.3" # Remember to change the version number immediately *after* a release. -# Uncomment the my_iscvs macro for non-released code. -m4_define(my_version, [1.9.21]) -m4_define(my_iscvs, yes) -AC_INIT([gnupg], my_version[]m4_ifdef([my_iscvs], [-cvs[]m4_translit( - [$Revision$],[Ra-z $:])]), [gnupg-devel@gnupg.org]) +# Set my_issvn to "yes" for non-released code. Remember to run an +# "svn up" and "autogen.sh" right before creating a distribution. +m4_define([my_version], [1.9.21]) +m4_define([my_issvn], [yes]) + + +m4_define([svn_revision], m4_esyscmd([echo -n $((svn info 2>/dev/null \ + || echo 'Revision: 0')|sed -n '/^Revision:/ {s/[^0-9]//gp;q}')])) +AC_INIT([gnupg], my_version[]m4_if(my_issvn,[yes],[-svn[]svn_revision]), + [gnupg-devel@gnupg.org]) # Set development_version to yes if the minor number is odd or you # feel that the default check for a development version is not # sufficient. @@ -219,6 +224,34 @@ if test "$use_exec" = yes ; then AC_MSG_RESULT($enableval) fi + +dnl +dnl Check for the key/uid cache size. This can't be zero, but can be +dnl pretty small on embedded systems. +dnl +AC_MSG_CHECKING([for the size of the key and uid cache]) +AC_ARG_ENABLE(key-cache, + AC_HELP_STRING([--enable-key-cache=SIZE],[Set key cache to SIZE (default 4096)]),,enableval=4096) + +if test "$enableval" = "no"; then + enableval=5 +elif test "$enableval" = "yes" || test "$enableval" = ""; then + enableval=4096 +fi + +changequote(,)dnl +key_cache_size=`echo "$enableval" | sed 's/[A-Za-z]//g'` +changequote([,])dnl + +if test "$enableval" != "$key_cache_size" || test "$key_cache_size" -lt 5; then + AC_MSG_ERROR([invalid key-cache size]) +fi + +AC_MSG_RESULT($key_cache_size) +AC_DEFINE_UNQUOTED(PK_UID_CACHE_SIZE,$key_cache_size,[Size of the key and UID caches]) + + + dnl dnl Check whether we want to use Linux capabilities dnl diff --git a/g10/ChangeLog b/g10/ChangeLog index 0ae73b535..6259bdc20 100644 --- a/g10/ChangeLog +++ b/g10/ChangeLog @@ -1,3 +1,33 @@ +2006-04-18 Werner Koch <wk@g10code.com> + + * tdbio.c (open_db, migrate_from_v2): Removed feature to migration + from old trustdb version 2. + + * gpg.c, mainproc.c: Removed pipemode feature. + + * status.c: Removed shared memory coprocess stuff + + Merged with current gpg 1.4.3 code. + + * keygen.c, keyid.c, misc.c, openfile.c, verify.c, trustdb.c + * textfilter.c, tdbio.c, tdbdump.c, status.c, skclist.c, signal.c + * sign.c, sig-check.c, seskey.c, seckey-cert.c, revoke.c + * pubkey-enc.c, progress.c, plaintext.c, pkclist.c, photoid.c + * passphrase.c, parse-packet.c, mdfilter.c, mainproc.c + * keyserver.c, keyring.c, keylist.c, keyedit.c, keydb.c, kbnode.c + * import.c, getkey.c, gpgv.c, helptext.c, free-packet.c + * build-packet.c, cipher.c, compress.c, dearmor.c, decrypt.c + * delkey.c, encr-data.c, encode.c, exec.c, export.c + * gpg.c, armor.c: Updated from gnupg-1.4.3 and merged back gcry and + gnupg-1.9 related changes. + * trustdb.h, tdbio.h, status.h, photoid.h, packet.h, options.h + * main.h, keyserver-internal.h, keyring.h, keydb.h, filter.h + * exec.h: Ditto. + * global.h: Removed after merging constants with gpg.h. + * comment.c, pipemode.c: Removed. + * card-util.c: Updated from gnupg-1.4.3. + * compress-bz2.c: New. + 2005-06-15 Werner Koch <wk@g10code.com> * g10.c (print_hashline, add_group): Fixes for signed/unsigned @@ -9007,7 +9037,8 @@ Thu Feb 12 22:24:42 1998 Werner Koch (wk@frodo) * pubkey-enc.c (get_session_key): rewritten - Copyright 1998,1999,2000,2001,2002,2003 Free Software Foundation, Inc. + Copyright 1998,1999,2000,2001,2002,2003,2004,2005, + 2006 Free Software Foundation, Inc. This file is free software; as a special exception the author gives unlimited permission to copy and/or distribute it, with or without diff --git a/g10/Makefile.am b/g10/Makefile.am index f371dab4a..1deacb9f8 100644 --- a/g10/Makefile.am +++ b/g10/Makefile.am @@ -1,5 +1,5 @@ # Copyright (C) 1998, 1999, 2000, 2001, 2002, -# 2003 Free Software Foundation, Inc. +# 2003, 2006 Free Software Foundation, Inc. # # This file is part of GnuPG. # @@ -26,16 +26,17 @@ AM_CPPFLAGS = -I$(top_srcdir)/gl -I$(top_srcdir)/common \ include $(top_srcdir)/am/cmacros.am -AM_CFLAGS = $(LIBGCRYPT_CFLAGS) +AM_CFLAGS = $(LIBGCRYPT_CFLAGS) -Wno-pointer-sign needed_libs = ../gl/libgnu.a ../common/libcommon.a ../jnlib/libjnlib.a bin_PROGRAMS = gpg2 gpgv2 common_source = \ - global.h gpg.h \ + gpg.h \ build-packet.c \ compress.c \ + compress-bz2.c \ filter.h \ free-packet.c \ getkey.c \ @@ -55,7 +56,6 @@ common_source = \ keyid.c \ packet.h \ parse-packet.c \ - comment.c \ status.c \ status.h \ plaintext.c \ @@ -63,7 +63,7 @@ common_source = \ keylist.c \ pkglue.c pkglue.h -gpg2_SOURCES = g10.c \ +gpg2_SOURCES = gpg.c \ $(common_source) \ pkclist.c \ skclist.c \ @@ -88,7 +88,6 @@ gpg2_SOURCES = g10.c \ tdbio.h \ delkey.c \ keygen.c \ - pipemode.c \ helptext.c \ keyserver.c \ keyserver-internal.h \ diff --git a/g10/armor.c b/g10/armor.c index 121ec3a09..a154c5cfe 100644 --- a/g10/armor.c +++ b/g10/armor.c @@ -1,6 +1,6 @@ /* armor.c - Armor flter - * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003 - * Free Software Foundation, Inc. + * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, + * 2005 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -16,7 +16,8 @@ * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. */ #include <config.h> @@ -30,7 +31,6 @@ #include "gpg.h" #include "errors.h" #include "iobuf.h" -#include "memory.h" #include "util.h" #include "filter.h" #include "packet.h" @@ -39,12 +39,6 @@ #include "status.h" #include "i18n.h" -#ifdef HAVE_DOSISH_SYSTEM -#define LF "\r\n" -#else -#define LF "\n" -#endif - #define MAX_LINELEN 20000 #define CRCINIT 0xB704CE @@ -120,7 +114,6 @@ static char *tail_strings[] = { }; - static void initialize(void) { @@ -193,7 +186,7 @@ is_armored( const byte *buf ) * filter to do further processing. */ int -use_armor_filter( iobuf_t a ) +use_armor_filter( IOBUF a ) { byte buf[1]; int n; @@ -292,17 +285,24 @@ is_armor_header( byte *line, unsigned len ) save_p = p; p += 5; - /* Some mail programs on Windows seem to add spaces to the end of - the line. This becomes strict if --openpgp is set. */ - - if(!RFC2440) - while(*p==' ') + /* Some Windows environments seem to add whitespace to the end of + the line, so we strip it here. This becomes strict if + --rfc2440 is set since 2440 reads "The header lines, therefore, + MUST start at the beginning of a line, and MUST NOT have text + following them on the same line." It is unclear whether "text" + refers to all text or just non-whitespace text. */ + + if(RFC2440) + { + if( *p == '\r' ) + p++; + if( *p == '\n' ) + p++; + } + else + while(*p==' ' || *p=='\r' || *p=='\n' || *p=='\t') p++; - if( *p == '\r' ) - p++; - if( *p == '\n' ) - p++; if( *p ) return -1; /* garbage after dashes */ save_c = *save_p; *save_p = 0; @@ -334,21 +334,35 @@ parse_header_line( armor_filter_context_t *afx, byte *line, unsigned int len ) int hashes=0; unsigned int len2; - len2 = length_sans_trailing_ws( line, len ); + len2 = check_trailing_ws( line, len ); if( !len2 ) { afx->buffer_pos = len2; /* (it is not the fine way to do it here) */ return 0; /* WS only: same as empty line */ } - len = len2; - line[len2] = 0; + + /* + This is fussy. The spec says that a header line is delimited + with a colon-space pair. This means that a line such as + "Comment: " (with nothing else) is actually legal as an empty + string comment. However, email and cut-and-paste being what it + is, that trailing space may go away. Therefore, we accept empty + headers delimited with only a colon. --rfc2440, as always, + makes this strict and enforces the colon-space pair. -dms + */ p = strchr( line, ':'); - if( !p || !p[1] ) { + if( !p || (RFC2440 && p[1]!=' ') + || (!RFC2440 && p[1]!=' ' && p[1]!='\n' && p[1]!='\r')) + { log_error(_("invalid armor header: ")); print_string( stderr, line, len, 0 ); putc('\n', stderr); return -1; - } + } + + /* Chop off the whitespace we detected before */ + len=len2; + line[len2]='\0'; if( opt.verbose ) { log_info(_("armor header: ")); @@ -373,7 +387,7 @@ parse_header_line( armor_filter_context_t *afx, byte *line, unsigned int len ) /* figure out whether the data is armored or not */ static int -check_input( armor_filter_context_t *afx, iobuf_t a ) +check_input( armor_filter_context_t *afx, IOBUF a ) { int rc = 0; int i; @@ -415,7 +429,7 @@ check_input( armor_filter_context_t *afx, iobuf_t a ) if( hdr_line == BEGIN_SIGNED_MSG_IDX ) { if( afx->in_cleartext ) { log_error(_("nested clear text signatures\n")); - rc = GPG_ERR_INV_ARMOR; + rc = gpg_error (GPG_ERR_INV_ARMOR); } afx->in_cleartext = 1; } @@ -431,9 +445,9 @@ check_input( armor_filter_context_t *afx, iobuf_t a ) } while( !maxlen ); } - /* parse the header lines */ + /* Parse the header lines. */ while(len) { - /* read the next line (skip all truncated lines) */ + /* Read the next line (skip all truncated lines). */ do { maxlen = MAX_LINELEN; afx->buffer_len = iobuf_read_line( a, &afx->buffer, @@ -444,8 +458,8 @@ check_input( armor_filter_context_t *afx, iobuf_t a ) i = parse_header_line( afx, line, len ); if( i <= 0 ) { - if( i ) - rc = GPG_ERR_INV_ARMOR; + if (i && RFC2440) + rc = G10ERR_INVALID_ARMOR; break; } } @@ -465,7 +479,8 @@ check_input( armor_filter_context_t *afx, iobuf_t a ) return rc; } - +#define PARTIAL_CHUNK 512 +#define PARTIAL_POW 9 /**************** * Fake a literal data packet and wait for the next armor line @@ -473,7 +488,7 @@ check_input( armor_filter_context_t *afx, iobuf_t a ) * not implemented/checked. */ static int -fake_packet( armor_filter_context_t *afx, iobuf_t a, +fake_packet( armor_filter_context_t *afx, IOBUF a, size_t *retn, byte *buf, size_t size ) { int rc = 0; @@ -481,19 +496,31 @@ fake_packet( armor_filter_context_t *afx, iobuf_t a, int lastline = 0; unsigned maxlen, n; byte *p; + byte tempbuf[PARTIAL_CHUNK]; + size_t tempbuf_len=0; - len = 2; /* reserve 2 bytes for the length header */ - size -= 2; /* and 2 for the terminating header */ - while( !rc && len < size ) { + while( !rc && size-len>=(PARTIAL_CHUNK+1)) { /* copy what we have in the line buffer */ if( afx->faked == 1 ) afx->faked++; /* skip the first (empty) line */ - else { - while( len < size && afx->buffer_pos < afx->buffer_len ) - buf[len++] = afx->buffer[afx->buffer_pos++]; - if( len >= size ) + else + { + /* It's full, so write this partial chunk */ + if(tempbuf_len==PARTIAL_CHUNK) + { + buf[len++]=0xE0+PARTIAL_POW; + memcpy(&buf[len],tempbuf,PARTIAL_CHUNK); + len+=PARTIAL_CHUNK; + tempbuf_len=0; continue; - } + } + + while( tempbuf_len < PARTIAL_CHUNK + && afx->buffer_pos < afx->buffer_len ) + tempbuf[tempbuf_len++] = afx->buffer[afx->buffer_pos++]; + if( tempbuf_len==PARTIAL_CHUNK ) + continue; + } /* read the next line */ maxlen = MAX_LINELEN; @@ -506,15 +533,64 @@ fake_packet( armor_filter_context_t *afx, iobuf_t a, } if( !maxlen ) afx->truncated++; - if( !afx->not_dash_escaped ) { - int crlf; - p = afx->buffer; - n = afx->buffer_len; - crlf = n > 1 && p[n-2] == '\r' && p[n-1]=='\n'; + + p = afx->buffer; + n = afx->buffer_len; + + /* Armor header or dash-escaped line? */ + if(p[0]=='-') + { + /* 2440bis-10: When reversing dash-escaping, an + implementation MUST strip the string "- " if it occurs + at the beginning of a line, and SHOULD warn on "-" and + any character other than a space at the beginning of a + line. */ + + if(p[1]==' ' && !afx->not_dash_escaped) + { + /* It's a dash-escaped line, so skip over the + escape. */ + afx->buffer_pos = 2; + } + else if(p[1]=='-' && p[2]=='-' && p[3]=='-' && p[4]=='-') + { + /* Five dashes in a row mean it's probably armor + header. */ + int type = is_armor_header( p, n ); + if( afx->not_dash_escaped && type != BEGIN_SIGNATURE ) + ; /* this is okay */ + else + { + if( type != BEGIN_SIGNATURE ) + { + log_info(_("unexpected armor: ")); + print_string( stderr, p, n, 0 ); + putc('\n', stderr); + } + + lastline = 1; + rc = -1; + } + } + else if(!afx->not_dash_escaped) + { + /* Bad dash-escaping. */ + log_info(_("invalid dash escaped line: ")); + print_string( stderr, p, n, 0 ); + putc('\n', stderr); + } + } + + /* Now handle the end-of-line canonicalization */ + if( !afx->not_dash_escaped ) + { + int crlf = n > 1 && p[n-2] == '\r' && p[n-1]=='\n'; /* PGP2 does not treat a tab as white space character */ - afx->buffer_len = trim_trailing_chars( p, n, - afx->pgp2mode ? " \r\n" : " \t\r\n"); + afx->buffer_len= + trim_trailing_chars( &p[afx->buffer_pos], n-afx->buffer_pos, + afx->pgp2mode ? " \r\n" : " \t\r\n"); + afx->buffer_len+=afx->buffer_pos; /* the buffer is always allocated with enough space to append * the removed [CR], LF and a Nul * The reason for this complicated procedure is to keep at least @@ -526,48 +602,23 @@ fake_packet( armor_filter_context_t *afx, iobuf_t a, * faked packet could do the job). */ if( crlf ) - afx->buffer[afx->buffer_len++] = '\r'; + afx->buffer[afx->buffer_len++] = '\r'; afx->buffer[afx->buffer_len++] = '\n'; - afx->buffer[afx->buffer_len] = 0; - } - p = afx->buffer; - n = afx->buffer_len; - - if( n > 2 && *p == '-' ) { - /* check for dash escaped or armor header */ - if( p[1] == ' ' && !afx->not_dash_escaped ) { - /* issue a warning if it is not regular encoded */ - if( p[2] != '-' && !( n > 6 && !memcmp(p+2, "From ", 5))) { - log_info(_("invalid dash escaped line: ")); - print_string( stderr, p, n, 0 ); - putc('\n', stderr); - } - afx->buffer_pos = 2; /* skip */ - } - else if( n >= 15 && p[1] == '-' && p[2] == '-' && p[3] == '-' ) { - int type = is_armor_header( p, n ); - if( afx->not_dash_escaped && type != BEGIN_SIGNATURE ) - ; /* this is okay */ - else { - if( type != BEGIN_SIGNATURE ) { - log_info(_("unexpected armor:")); - print_string( stderr, p, n, 0 ); - putc('\n', stderr); - } - lastline = 1; - rc = -1; - } - } - } + afx->buffer[afx->buffer_len] = '\0'; + } } - buf[0] = (len-2) >> 8; - buf[1] = (len-2); if( lastline ) { /* write last (ending) length header */ - if( buf[0] || buf[1] ) { /* only if we have some text */ - buf[len++] = 0; - buf[len++] = 0; - } + if(tempbuf_len<192) + buf[len++]=tempbuf_len; + else + { + buf[len++]=((tempbuf_len-192)/256) + 192; + buf[len++]=(tempbuf_len-192) % 256; + } + memcpy(&buf[len],tempbuf,tempbuf_len); + len+=tempbuf_len; + rc = 0; afx->faked = 0; afx->in_cleartext = 0; @@ -609,15 +660,15 @@ fake_packet( armor_filter_context_t *afx, iobuf_t a, static int invalid_crc(void) { - if ( opt.ignore_crc_error ) - return 0; - log_inc_errorcount(); - return GPG_ERR_INV_ARMOR; + if ( opt.ignore_crc_error ) + return 0; + log_inc_errorcount(); + return gpg_error (GPG_ERR_INV_ARMOR); } static int -radix64_read( armor_filter_context_t *afx, iobuf_t a, size_t *retn, +radix64_read( armor_filter_context_t *afx, IOBUF a, size_t *retn, byte *buf, size_t size ) { byte val; @@ -676,7 +727,7 @@ radix64_read( armor_filter_context_t *afx, iobuf_t a, size_t *retn, break; } else if( (c = asctobin[(c2=c)]) == 255 ) { - log_error(_("invalid radix64 character %02x skipped\n"), c2); + log_error(_("invalid radix64 character %02X skipped\n"), c2); continue; } switch(idx) { @@ -755,13 +806,17 @@ radix64_read( armor_filter_context_t *afx, iobuf_t a, size_t *retn, if( c == -1 ) { log_info(_("premature eof (in CRC)\n")); rc = invalid_crc(); - } + } + else if( idx == 0 ) { + /* No CRC at all is legal ("MAY") */ + rc=0; + } else if( idx != 4 ) { log_info(_("malformed CRC\n")); rc = invalid_crc(); } else if( mycrc != afx->crc ) { - log_info (_("CRC error; %06lx - %06lx\n"), + log_info (_("CRC error; %06lX - %06lX\n"), (ulong)afx->crc, (ulong)mycrc); rc = invalid_crc(); } @@ -781,12 +836,12 @@ radix64_read( armor_filter_context_t *afx, iobuf_t a, size_t *retn, if( rc == -1 ) rc = 0; else if( rc == 2 ) { - log_error(_("premature eof (in Trailer)\n")); - rc = GPG_ERR_INV_ARMOR; + log_error(_("premature eof (in trailer)\n")); + rc = G10ERR_INVALID_ARMOR; } else { log_error(_("error in trailer line\n")); - rc = GPG_ERR_INV_ARMOR; + rc = G10ERR_INVALID_ARMOR; } #endif } @@ -805,7 +860,7 @@ radix64_read( armor_filter_context_t *afx, iobuf_t a, size_t *retn, */ int armor_filter( void *opaque, int control, - iobuf_t a, byte *buf, size_t *ret_len) + IOBUF a, byte *buf, size_t *ret_len) { size_t size = *ret_len; armor_filter_context_t *afx = opaque; @@ -843,9 +898,10 @@ armor_filter( void *opaque, int control, *ret_len = n; } else if( control == IOBUFCTRL_UNDERFLOW ) { - /* We need some space for the faked packet. The minmum required - * size is ~18 + length of the session marker */ - if( size < 50 ) + /* We need some space for the faked packet. The minmum + * required size is the PARTIAL_CHUNK size plus a byte for the + * length itself */ + if( size < PARTIAL_CHUNK+1 ) BUG(); /* supplied buffer too short */ if( afx->faked ) @@ -882,7 +938,7 @@ armor_filter( void *opaque, int control, afx->pgp2mode = 1; } n=0; - /* first a gpg control packet */ + /* First a gpg control packet... */ buf[n++] = 0xff; /* new format, type 63, 1 length byte */ n++; /* see below */ memcpy(buf+n, sesmark, sesmarklen ); n+= sesmarklen; @@ -902,12 +958,16 @@ armor_filter( void *opaque, int control, buf[n++] = DIGEST_ALGO_SHA512; buf[1] = n - 2; - /* followed by a plaintext packet */ - buf[n++] = 0xaf; /* old packet format, type 11, var length */ - buf[n++] = 0; /* set the length header */ - buf[n++] = 6; + /* ...followed by an invented plaintext packet. + Amusingly enough, this packet is not compliant with + 2440 as the initial partial length is less than 512 + bytes. Of course, we'll accept it anyway ;) */ + + buf[n++] = 0xCB; /* new packet format, type 11 */ + buf[n++] = 0xE1; /* 2^1 == 2 bytes */ buf[n++] = 't'; /* canonical text mode */ buf[n++] = 0; /* namelength */ + buf[n++] = 0xE2; /* 2^2 == 4 more bytes */ memset(buf+n, 0, 4); /* timestamp */ n += 4; } @@ -926,35 +986,39 @@ armor_filter( void *opaque, int control, else if( control == IOBUFCTRL_FLUSH && !afx->cancel ) { if( !afx->status ) { /* write the header line */ const char *s; - STRLIST comment = opt.comments; + STRLIST comment=opt.comments; if( afx->what >= DIM(head_strings) ) log_bug("afx->what=%d", afx->what); iobuf_writestr(a, "-----"); iobuf_writestr(a, head_strings[afx->what] ); - iobuf_writestr(a, "-----" LF ); + iobuf_writestr(a, "-----" ); + iobuf_writestr(a,afx->eol); if( !opt.no_version ) + { iobuf_writestr(a, "Version: GnuPG v" VERSION " (" - PRINTABLE_OS_NAME ")" LF ); + PRINTABLE_OS_NAME ")" ); + iobuf_writestr(a,afx->eol); + } - /* Write the comment string. */ - for(s=comment? comment->d:NULL; comment; - comment=comment->next,s=comment->d) + /* write the comment strings */ + for(s=comment->d;comment;comment=comment->next,s=comment->d) { iobuf_writestr(a, "Comment: " ); - for ( ; *s; s++ ) - { + for( ; *s; s++ ) + { if( *s == '\n' ) - iobuf_writestr(a, "\\n" ); + iobuf_writestr(a, "\\n" ); else if( *s == '\r' ) - iobuf_writestr(a, "\\r" ); + iobuf_writestr(a, "\\r" ); else if( *s == '\v' ) - iobuf_writestr(a, "\\v" ); + iobuf_writestr(a, "\\v" ); else - iobuf_put(a, *s ); - } - iobuf_writestr(a, LF ); - } + iobuf_put(a, *s ); + } + + iobuf_writestr(a,afx->eol); + } if ( afx->hdrlines ) { for ( s = afx->hdrlines; *s; s++ ) { @@ -965,7 +1029,8 @@ armor_filter( void *opaque, int control, iobuf_put(a, *s ); } } - iobuf_writestr(a, LF ); + + iobuf_writestr(a,afx->eol); afx->status++; afx->idx = 0; afx->idx2 = 0; @@ -994,10 +1059,11 @@ armor_filter( void *opaque, int control, iobuf_put(a, c); c = bintoasc[radbuf[2]&077]; iobuf_put(a, c); - if( ++idx2 >= (64/4) ) { /* pgp doesn't like 72 here */ - iobuf_writestr(a, LF ); + if( ++idx2 >= (64/4) ) + { /* pgp doesn't like 72 here */ + iobuf_writestr(a,afx->eol); idx2=0; - } + } } } for(i=0; i < idx; i++ ) @@ -1006,10 +1072,23 @@ armor_filter( void *opaque, int control, afx->idx2 = idx2; afx->crc = crc; } - else if( control == IOBUFCTRL_INIT ) { + else if( control == IOBUFCTRL_INIT ) + { if( !is_initialized ) - initialize(); - } + initialize(); + + /* Figure out what we're using for line endings if the caller + didn't specify. */ + if(afx->eol[0]==0) + { +#ifdef HAVE_DOSISH_SYSTEM + afx->eol[0]='\r'; + afx->eol[1]='\n'; +#else + afx->eol[0]='\n'; +#endif + } + } else if( control == IOBUFCTRL_CANCEL ) { afx->cancel = 1; } @@ -1038,14 +1117,15 @@ armor_filter( void *opaque, int control, iobuf_put(a, c); iobuf_put(a, '='); } - if( ++idx2 >= (64/4) ) { /* pgp doesn't like 72 here */ - iobuf_writestr(a, LF ); + if( ++idx2 >= (64/4) ) + { /* pgp doesn't like 72 here */ + iobuf_writestr(a,afx->eol); idx2=0; - } + } } /* may need a linefeed */ if( idx2 ) - iobuf_writestr(a, LF ); + iobuf_writestr(a,afx->eol); /* write the CRC */ iobuf_put(a, '='); radbuf[0] = crc >>16; @@ -1059,13 +1139,14 @@ armor_filter( void *opaque, int control, iobuf_put(a, c); c = bintoasc[radbuf[2]&077]; iobuf_put(a, c); - iobuf_writestr(a, LF ); + iobuf_writestr(a,afx->eol); /* and the the trailer */ if( afx->what >= DIM(tail_strings) ) log_bug("afx->what=%d", afx->what); iobuf_writestr(a, "-----"); iobuf_writestr(a, tail_strings[afx->what] ); - iobuf_writestr(a, "-----" LF ); + iobuf_writestr(a, "-----" ); + iobuf_writestr(a,afx->eol); } else if( !afx->any_data && !afx->inp_bypass ) { log_error(_("no valid OpenPGP data found.\n")); @@ -1079,7 +1160,7 @@ armor_filter( void *opaque, int control, if( afx->qp_detected ) log_error(_("quoted printable character in armor - " "probably a buggy MTA has been used\n") ); - xfree ( afx->buffer ); + xfree( afx->buffer ); afx->buffer = NULL; } else if( control == IOBUFCTRL_DESC ) @@ -1096,7 +1177,7 @@ make_radix64_string( const byte *data, size_t len ) { char *buffer, *p; - buffer = p = xmalloc ( (len+2)/3*4 + 1 ); + buffer = p = xmalloc( (len+2)/3*4 + 1 ); for( ; len >= 3 ; len -= 3, data += 3 ) { *p++ = bintoasc[(data[0] >> 2) & 077]; *p++ = bintoasc[(((data[0] <<4)&060)|((data[1] >> 4)&017))&077]; @@ -1156,7 +1237,7 @@ unarmor_pump_new (void) if( !is_initialized ) initialize(); - x = xcalloc (1,sizeof *x); + x = xmalloc_clear (sizeof *x); return x; } @@ -1253,7 +1334,7 @@ unarmor_pump (UnarmorPump x, int c) { int c2; if( (c = asctobin[(c2=c)]) == 255 ) { - log_error(_("invalid radix64 character %02x skipped\n"), c2); + log_error(_("invalid radix64 character %02X skipped\n"), c2); break; } } @@ -1290,7 +1371,7 @@ unarmor_pump (UnarmorPump x, int c) if( (c = asctobin[c]) == 255 ) { rval = -1; /* ready */ if( x->crc != x->mycrc ) { - log_info (_("CRC error; %06lx - %06lx\n"), + log_info (_("CRC error; %06lX - %06lX\n"), (ulong)x->crc, (ulong)x->mycrc); if ( invalid_crc() ) rval = -3; diff --git a/g10/build-packet.c b/g10/build-packet.c index d2c538477..cbf037483 100644 --- a/g10/build-packet.c +++ b/g10/build-packet.c @@ -1,6 +1,6 @@ /* build-packet.c - assemble packets and write them - * Copyright (C) 1998, 1999, 2000, 2001, 2002, - * 2003 Free Software Foundation, Inc. + * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, + * 2006 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -16,7 +16,8 @@ * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. */ #include <config.h> @@ -24,40 +25,38 @@ #include <stdlib.h> #include <string.h> #include <assert.h> +#include <ctype.h> #include "gpg.h" #include "packet.h" #include "errors.h" #include "iobuf.h" -#include "mpi.h" #include "util.h" #include "cipher.h" -#include "memory.h" +#include "i18n.h" #include "options.h" - -static int do_comment( iobuf_t out, int ctb, PKT_comment *rem ); -static int do_user_id( iobuf_t out, int ctb, PKT_user_id *uid ); -static int do_public_key( iobuf_t out, int ctb, PKT_public_key *pk ); -static int do_secret_key( iobuf_t out, int ctb, PKT_secret_key *pk ); -static int do_symkey_enc( iobuf_t out, int ctb, PKT_symkey_enc *enc ); -static int do_pubkey_enc( iobuf_t out, int ctb, PKT_pubkey_enc *enc ); +static int do_user_id( IOBUF out, int ctb, PKT_user_id *uid ); +static int do_public_key( IOBUF out, int ctb, PKT_public_key *pk ); +static int do_secret_key( IOBUF out, int ctb, PKT_secret_key *pk ); +static int do_symkey_enc( IOBUF out, int ctb, PKT_symkey_enc *enc ); +static int do_pubkey_enc( IOBUF out, int ctb, PKT_pubkey_enc *enc ); static u32 calc_plaintext( PKT_plaintext *pt ); -static int do_plaintext( iobuf_t out, int ctb, PKT_plaintext *pt ); -static int do_encrypted( iobuf_t out, int ctb, PKT_encrypted *ed ); -static int do_encrypted_mdc( iobuf_t out, int ctb, PKT_encrypted *ed ); -static int do_compressed( iobuf_t out, int ctb, PKT_compressed *cd ); -static int do_signature( iobuf_t out, int ctb, PKT_signature *sig ); -static int do_onepass_sig( iobuf_t out, int ctb, PKT_onepass_sig *ops ); +static int do_plaintext( IOBUF out, int ctb, PKT_plaintext *pt ); +static int do_encrypted( IOBUF out, int ctb, PKT_encrypted *ed ); +static int do_encrypted_mdc( IOBUF out, int ctb, PKT_encrypted *ed ); +static int do_compressed( IOBUF out, int ctb, PKT_compressed *cd ); +static int do_signature( IOBUF out, int ctb, PKT_signature *sig ); +static int do_onepass_sig( IOBUF out, int ctb, PKT_onepass_sig *ops ); static int calc_header_length( u32 len, int new_ctb ); -static int write_16(iobuf_t inp, u16 a); -static int write_32(iobuf_t inp, u32 a); -static int write_header( iobuf_t out, int ctb, u32 len ); -static int write_sign_packet_header( iobuf_t out, int ctb, u32 len ); -static int write_header2( iobuf_t out, int ctb, u32 len, int hdrlen, int blkmode ); -static int write_new_header( iobuf_t out, int ctb, u32 len, int hdrlen ); -static int write_version( iobuf_t out, int ctb ); +static int write_16(IOBUF inp, u16 a); +static int write_32(IOBUF inp, u32 a); +static int write_header( IOBUF out, int ctb, u32 len ); +static int write_sign_packet_header( IOBUF out, int ctb, u32 len ); +static int write_header2( IOBUF out, int ctb, u32 len, int hdrlen ); +static int write_new_header( IOBUF out, int ctb, u32 len, int hdrlen ); +static int write_version( IOBUF out, int ctb ); /**************** * Build a packet and write it to INP @@ -66,7 +65,7 @@ static int write_version( iobuf_t out, int ctb ); * Note: Caller must free the packet */ int -build_packet( iobuf_t out, PACKET *pkt ) +build_packet( IOBUF out, PACKET *pkt ) { int new_ctb=0, rc=0, ctb; int pkttype; @@ -75,30 +74,38 @@ build_packet( iobuf_t out, PACKET *pkt ) log_debug("build_packet() type=%d\n", pkt->pkttype ); assert( pkt->pkt.generic ); - switch( (pkttype = pkt->pkttype) ) { - case PKT_OLD_COMMENT: pkttype = pkt->pkttype = PKT_COMMENT; break; + switch( (pkttype = pkt->pkttype) ) + { case PKT_PLAINTEXT: new_ctb = pkt->pkt.plaintext->new_ctb; break; case PKT_ENCRYPTED: case PKT_ENCRYPTED_MDC: new_ctb = pkt->pkt.encrypted->new_ctb; break; case PKT_COMPRESSED:new_ctb = pkt->pkt.compressed->new_ctb; break; case PKT_USER_ID: - if( pkt->pkt.user_id->attrib_data ) - pkttype = PKT_ATTRIBUTE; - break; + if( pkt->pkt.user_id->attrib_data ) + pkttype = PKT_ATTRIBUTE; + break; default: break; - } + } if( new_ctb || pkttype > 15 ) /* new format */ ctb = 0xc0 | (pkttype & 0x3f); else ctb = 0x80 | ((pkttype & 15)<<2); - switch( pkttype ) { + switch( pkttype ) + { case PKT_ATTRIBUTE: case PKT_USER_ID: rc = do_user_id( out, ctb, pkt->pkt.user_id ); break; + case PKT_OLD_COMMENT: case PKT_COMMENT: - rc = do_comment( out, ctb, pkt->pkt.comment ); + /* + Ignore these. Theoretically, this will never be called as + we have no way to output comment packets any longer, but + just in case there is some code path that would end up + outputting a comment that was written before comments were + dropped (in the public key?) this is a no-op. + */ break; case PKT_PUBLIC_SUBKEY: case PKT_PUBLIC_KEY: @@ -138,11 +145,32 @@ build_packet( iobuf_t out, PACKET *pkt ) default: log_bug("invalid packet type in build_packet()\n"); break; - } + } return rc; } + +/* + * Write the mpi A to OUT. + */ +static int +mpi_write (iobuf_t out, gcry_mpi_t a) +{ + char buffer[(MAX_EXTERN_MPI_BITS+7)/8]; + size_t nbytes; + int rc; + + nbytes = (MAX_EXTERN_MPI_BITS+7)/8; + rc = gcry_mpi_print (GCRYMPI_FMT_PGP, buffer, nbytes, &nbytes, a ); + if( !rc ) + rc = iobuf_write( out, buffer, nbytes ); + + return rc; +} + + + /**************** * calculate the length of a packet described by PKT */ @@ -180,56 +208,42 @@ calc_packet_length( PACKET *pkt ) } static void -write_fake_data( iobuf_t out, gcry_mpi_t a ) +write_fake_data (IOBUF out, gcry_mpi_t a) { - if( a ) { - unsigned int n; - void *p; - - assert( gcry_mpi_get_flag (a, GCRYMPI_FLAG_OPAQUE)); - p = gcry_mpi_get_opaque (a, &n); - iobuf_write (out, p, (n+7)/8); - } -} - - -static int -do_comment (iobuf_t out, int ctb, PKT_comment *rem) -{ - int rc = 0; - - if (opt.sk_comments) + if (a) { - write_header(out, ctb, rem->len); - rc = iobuf_write( out, rem->data, rem->len ); + unsigned int n; + void *p; + + p = gcry_mpi_get_opaque ( a, &n ); + iobuf_write (out, p, (n+7)/8 ); } - return rc; } static int -do_user_id( iobuf_t out, int ctb, PKT_user_id *uid ) +do_user_id( IOBUF out, int ctb, PKT_user_id *uid ) { - int rc; + int rc; - if (uid->attrib_data) - { - write_header (out, ctb, uid->attrib_len); - rc = iobuf_write (out, uid->attrib_data, uid->attrib_len ); - } - else - { - write_header (out, ctb, uid->len); - rc = iobuf_write (out, uid->name, uid->len ); - } - return rc; + if( uid->attrib_data ) + { + write_header(out, ctb, uid->attrib_len); + rc = iobuf_write( out, uid->attrib_data, uid->attrib_len ); + } + else + { + write_header2( out, ctb, uid->len, 2 ); + rc = iobuf_write( out, uid->name, uid->len ); + } + return 0; } static int -do_public_key( iobuf_t out, int ctb, PKT_public_key *pk ) +do_public_key( IOBUF out, int ctb, PKT_public_key *pk ) { int rc = 0; int n, i; - iobuf_t a = iobuf_temp(); + IOBUF a = iobuf_temp(); if( !pk->version ) iobuf_put( a, 3 ); @@ -251,99 +265,20 @@ do_public_key( iobuf_t out, int ctb, PKT_public_key *pk ) for(i=0; i < n; i++ ) mpi_write(a, pk->pkey[i] ); - write_header2(out, ctb, iobuf_get_temp_length(a), pk->hdrbytes, 1 ); - rc = iobuf_write_temp (out, a); + write_header2(out, ctb, iobuf_get_temp_length(a), pk->hdrbytes); + rc = iobuf_write_temp( out, a ); iobuf_close(a); return rc; } -/**************** - * Make a hash value from the public key certificate - */ -void -hash_public_key( MD_HANDLE md, PKT_public_key *pk ) -{ - PACKET pkt; - int rc = 0; - int ctb; - ulong pktlen; - int c; - iobuf_t a = iobuf_temp(); -#if 0 - FILE *fp = fopen("dump.pk", "a"); - int i=0; - - fprintf(fp, "\nHashing PK (v%d):\n", pk->version); -#endif - - /* build the packet */ - init_packet(&pkt); - pkt.pkttype = PKT_PUBLIC_KEY; - pkt.pkt.public_key = pk; - if( (rc = build_packet( a, &pkt )) ) - log_fatal("build public_key for hashing failed: %s\n", gpg_strerror (rc)); - - if( !(pk->version == 3 && pk->pubkey_algo == 16) ) { - /* skip the constructed header but don't do this for our very old - * v3 ElG keys */ - ctb = iobuf_get_noeof(a); - pktlen = 0; - if( (ctb & 0x40) ) { - c = iobuf_get_noeof(a); - if( c < 192 ) - pktlen = c; - else if( c < 224 ) { - pktlen = (c - 192) * 256; - c = iobuf_get_noeof(a); - pktlen += c + 192; - } - else if( c == 255 ) { - pktlen = iobuf_get_noeof(a) << 24; - pktlen |= iobuf_get_noeof(a) << 16; - pktlen |= iobuf_get_noeof(a) << 8; - pktlen |= iobuf_get_noeof(a); - } - } - else { - int lenbytes = ((ctb&3)==3)? 0 : (1<<(ctb & 3)); - for( ; lenbytes; lenbytes-- ) { - pktlen <<= 8; - pktlen |= iobuf_get_noeof(a); - } - } - /* hash a header */ - gcry_md_putc ( md, 0x99 ); - pktlen &= 0xffff; /* can't handle longer packets */ - gcry_md_putc ( md, pktlen >> 8 ); - gcry_md_putc ( md, pktlen & 0xff ); - } - /* hash the packet body */ - while( (c=iobuf_get(a)) != -1 ) { -#if 0 - fprintf( fp," %02x", c ); - if( (++i == 24) ) { - putc('\n', fp); - i=0; - } -#endif - gcry_md_putc ( md, c ); - } -#if 0 - putc('\n', fp); - fclose(fp); -#endif - iobuf_cancel(a); -} - - static int -do_secret_key( iobuf_t out, int ctb, PKT_secret_key *sk ) +do_secret_key( IOBUF out, int ctb, PKT_secret_key *sk ) { int rc = 0; int i, nskey, npkey; - iobuf_t a = iobuf_temp(); /* build in a self-enlarging buffer */ + IOBUF a = iobuf_temp(); /* build in a self-enlarging buffer */ /* Write the version number - if none is specified, use 3 */ if( !sk->version ) @@ -371,7 +306,7 @@ do_secret_key( iobuf_t out, int ctb, PKT_secret_key *sk ) /* If we don't have any public parameters - which is the case if we don't know the algorithm used - the parameters are stored as - one blob in a faked (opaque) gcry_mpi_t */ + one blob in a faked (opaque) MPI */ if( !npkey ) { write_fake_data( a, sk->skey[0] ); goto leave; @@ -415,9 +350,9 @@ do_secret_key( iobuf_t out, int ctb, PKT_secret_key *sk ) if( sk->protect.s2k.mode == 3 ) iobuf_put(a, sk->protect.s2k.count ); - /* For our special modes 1001 and 1002 we do not need an IV */ - if( sk->protect.s2k.mode != 1001 - && sk->protect.s2k.mode != 1002 ) + /* For out special modes 1001, 1002 we do not need an IV */ + if( sk->protect.s2k.mode != 1001 + && sk->protect.s2k.mode != 1002 ) iobuf_write(a, sk->protect.iv, sk->protect.ivlen ); } } @@ -437,19 +372,21 @@ do_secret_key( iobuf_t out, int ctb, PKT_secret_key *sk ) else if( sk->is_protected && sk->version >= 4 ) { /* The secret key is protected - write it out as it is */ byte *p; - assert( gcry_mpi_get_flag( sk->skey[npkey], GCRYMPI_FLAG_OPAQUE ) ); - p = gcry_mpi_get_opaque( sk->skey[npkey], &i ); - iobuf_write(a, p, (i+7)/8 ); + unsigned int ndatabits; + + assert (gcry_mpi_get_flag (sk->skey[npkey], GCRYMPI_FLAG_OPAQUE)); + p = gcry_mpi_get_opaque (sk->skey[npkey], &ndatabits ); + iobuf_write (a, p, (ndatabits+7)/8 ); } else if( sk->is_protected ) { - /* The secret key is protected the old v4 way. */ + /* The secret key is protected te old v4 way. */ for( ; i < nskey; i++ ) { byte *p; - size_t n; + unsigned int ndatabits; - assert( gcry_mpi_get_flag (sk->skey[i], GCRYMPI_FLAG_OPAQUE)); - p = gcry_mpi_get_opaque( sk->skey[i], &n ); - iobuf_write (a, p, (n+7)/8); + assert (gcry_mpi_get_flag (sk->skey[i], GCRYMPI_FLAG_OPAQUE)); + p = gcry_mpi_get_opaque (sk->skey[i], &ndatabits); + iobuf_write (a, p, (ndatabits+7)/8); } write_16(a, sk->csum ); } @@ -463,19 +400,19 @@ do_secret_key( iobuf_t out, int ctb, PKT_secret_key *sk ) leave: /* Build the header of the packet - which we must do after writing all the other stuff, so that we know the length of the packet */ - write_header2(out, ctb, iobuf_get_temp_length(a), sk->hdrbytes, 1 ); + write_header2(out, ctb, iobuf_get_temp_length(a), sk->hdrbytes); /* And finally write it out the real stream */ - rc = iobuf_write_temp (out, a ); + rc = iobuf_write_temp( out, a ); iobuf_close(a); /* close the remporary buffer */ return rc; } static int -do_symkey_enc( iobuf_t out, int ctb, PKT_symkey_enc *enc ) +do_symkey_enc( IOBUF out, int ctb, PKT_symkey_enc *enc ) { int rc = 0; - iobuf_t a = iobuf_temp(); + IOBUF a = iobuf_temp(); assert( enc->version == 4 ); switch( enc->s2k.mode ) { @@ -495,21 +432,19 @@ do_symkey_enc( iobuf_t out, int ctb, PKT_symkey_enc *enc ) iobuf_write(a, enc->seskey, enc->seskeylen ); write_header(out, ctb, iobuf_get_temp_length(a) ); - rc = iobuf_write_temp (out, a); + rc = iobuf_write_temp( out, a ); iobuf_close(a); return rc; } - - static int -do_pubkey_enc( iobuf_t out, int ctb, PKT_pubkey_enc *enc ) +do_pubkey_enc( IOBUF out, int ctb, PKT_pubkey_enc *enc ) { int rc = 0; int n, i; - iobuf_t a = iobuf_temp(); + IOBUF a = iobuf_temp(); write_version( a, ctb ); if( enc->throw_keyid ) { @@ -528,55 +463,56 @@ do_pubkey_enc( iobuf_t out, int ctb, PKT_pubkey_enc *enc ) mpi_write(a, enc->data[i] ); write_header(out, ctb, iobuf_get_temp_length(a) ); - rc = iobuf_write_temp (out, a); + rc = iobuf_write_temp( out, a ); iobuf_close(a); return rc; } - - static u32 calc_plaintext( PKT_plaintext *pt ) { - return pt->len? (1 + 1 + pt->namelen + 4 + pt->len) : 0; + /* Truncate namelen to the maximum 255 characters. Note this means + that a function that calls build_packet with an illegal literal + packet will get it back legalized. */ + + if(pt->namelen>255) + pt->namelen=255; + + return pt->len? (1 + 1 + pt->namelen + 4 + pt->len) : 0; } static int -do_plaintext( iobuf_t out, int ctb, PKT_plaintext *pt ) +do_plaintext( IOBUF out, int ctb, PKT_plaintext *pt ) { int i, rc = 0; u32 n; byte buf[1000]; /* this buffer has the plaintext! */ int nbytes; - /* Truncate namelen to the maximum 255 characters. This does mean - that a function that calls build_packet with an illegal literal - packet will get it back legalized. */ - if(pt->namelen>255) - pt->namelen=255; - write_header(out, ctb, calc_plaintext( pt ) ); iobuf_put(out, pt->mode ); iobuf_put(out, pt->namelen ); for(i=0; i < pt->namelen; i++ ) iobuf_put(out, pt->name[i] ); - rc = write_32 (out, pt->timestamp); + rc = write_32(out, pt->timestamp ); + if (rc) + return rc; n = 0; while( (nbytes=iobuf_read(pt->buf, buf, 1000)) != -1 ) { - rc = iobuf_write(out, buf, nbytes); - if (rc) - break; - n += nbytes; + rc = iobuf_write (out, buf, nbytes); + if (rc) + break; + n += nbytes; } wipememory(buf,1000); /* burn the buffer */ - if( !pt->len ) - iobuf_set_block_mode(out, 0 ); /* write end marker */ - else if( n != pt->len ) - log_error("do_plaintext(): wrote %lu bytes but expected %lu bytes\n", - (ulong)n, (ulong)pt->len ); + if( (ctb&0x40) && !pt->len ) + iobuf_set_partial_block_mode(out, 0 ); /* turn off partial */ + if( pt->len && n != pt->len ) + log_error("do_plaintext(): wrote %lu bytes but expected %lu bytes\n", + (ulong)n, (ulong)pt->len ); return rc; } @@ -584,7 +520,7 @@ do_plaintext( iobuf_t out, int ctb, PKT_plaintext *pt ) static int -do_encrypted( iobuf_t out, int ctb, PKT_encrypted *ed ) +do_encrypted( IOBUF out, int ctb, PKT_encrypted *ed ) { int rc = 0; u32 n; @@ -598,7 +534,7 @@ do_encrypted( iobuf_t out, int ctb, PKT_encrypted *ed ) } static int -do_encrypted_mdc( iobuf_t out, int ctb, PKT_encrypted *ed ) +do_encrypted_mdc( IOBUF out, int ctb, PKT_encrypted *ed ) { int rc = 0; u32 n; @@ -617,7 +553,7 @@ do_encrypted_mdc( iobuf_t out, int ctb, PKT_encrypted *ed ) static int -do_compressed( iobuf_t out, int ctb, PKT_compressed *cd ) +do_compressed( IOBUF out, int ctb, PKT_compressed *cd ) { int rc = 0; @@ -626,7 +562,7 @@ do_compressed( iobuf_t out, int ctb, PKT_compressed *cd ) set, CTB is already formatted as new style and write_header2 does create a partial length encoding using new the new style. */ - write_header2(out, ctb, 0, 0, 0 ); + write_header2(out, ctb, 0, 0); iobuf_put(out, cd->algorithm ); /* This is all. The caller has to write the real data */ @@ -734,6 +670,7 @@ build_sig_subpkt (PKT_signature *sig, sigsubpkttype_t type, case SIGSUBPKT_NOTATION: case SIGSUBPKT_POLICY: case SIGSUBPKT_REV_KEY: + case SIGSUBPKT_SIGNATURE: /* we do allow multiple subpackets */ break; @@ -803,18 +740,20 @@ build_sig_subpkt (PKT_signature *sig, sigsubpkttype_t type, else nlen = 1; /* just a 1 byte length header */ - switch( type ) { + switch( type ) + { /* The issuer being unhashed is a historical oddity. It should work equally as well hashed. Of course, if even an unhashed issuer is tampered with, it makes it awfully hard to verify the sig... */ case SIGSUBPKT_ISSUER: + case SIGSUBPKT_SIGNATURE: hashed = 0; break; default: hashed = 1; break; - } + } if( critical ) type |= SIGSUBPKT_FLAG_CRITICAL; @@ -966,12 +905,179 @@ build_attribute_subpkt(PKT_user_id *uid,byte type, uid->attrib_len+=idx+headerlen+buflen; } +struct notation * +string_to_notation(const char *string,int is_utf8) +{ + const char *s; + int saw_at=0; + struct notation *notation; + + notation=xmalloc_clear(sizeof(*notation)); + + if(*string=='-') + { + notation->flags.ignore=1; + string++; + } + + if(*string=='!') + { + notation->flags.critical=1; + string++; + } + + /* If and when the IETF assigns some official name tags, we'll have + to add them here. */ + + for( s=string ; *s != '='; s++ ) + { + if( *s=='@') + saw_at++; + + /* -notationname is legal without an = sign */ + if(!*s && notation->flags.ignore) + break; + + if( !*s || !isascii (*s) || (!isgraph(*s) && !isspace(*s)) ) + { + log_error(_("a notation name must have only printable characters" + " or spaces, and end with an '='\n") ); + goto fail; + } + } + + notation->name=xmalloc((s-string)+1); + strncpy(notation->name,string,s-string); + notation->name[s-string]='\0'; + + if(!saw_at && !opt.expert) + { + log_error(_("a user notation name must contain the '@' character\n")); + goto fail; + } + + if (saw_at > 1) + { + log_error(_("a notation name must not contain more than" + " one '@' character\n")); + goto fail; + } + + if(*s) + { + const char *i=s+1; + int highbit=0; + + /* we only support printable text - therefore we enforce the use + of only printable characters (an empty value is valid) */ + for(s++; *s ; s++ ) + { + if ( !isascii (*s) ) + highbit=1; + else if (iscntrl(*s)) + { + log_error(_("a notation value must not use any" + " control characters\n")); + goto fail; + } + } + + if(!highbit || is_utf8) + notation->value=xstrdup(i); + else + notation->value=native_to_utf8(i); + } + + return notation; + + fail: + free_notation(notation); + return NULL; +} + +struct notation * +sig_to_notation(PKT_signature *sig) +{ + const byte *p; + size_t len; + int seq=0,crit; + struct notation *list=NULL; + + while((p=enum_sig_subpkt(sig->hashed,SIGSUBPKT_NOTATION,&len,&seq,&crit))) + { + int n1,n2; + struct notation *n=NULL; + + if(len<8) + { + log_info(_("WARNING: invalid notation data found\n")); + continue; + } + + n1=(p[4]<<8)|p[5]; + n2=(p[6]<<8)|p[7]; + + if(8+n1+n2!=len) + { + log_info(_("WARNING: invalid notation data found\n")); + continue; + } + + n=xmalloc_clear(sizeof(*n)); + n->name=xmalloc(n1+1); + + memcpy(n->name,&p[8],n1); + n->name[n1]='\0'; + + if(p[0]&0x80) + { + n->value=xmalloc(n2+1); + memcpy(n->value,&p[8+n1],n2); + n->value[n2]='\0'; + } + else + { + n->bdat=xmalloc(n2); + n->blen=n2; + memcpy(n->bdat,&p[8+n1],n2); + + n->value=xmalloc(2+strlen(_("not human readable"))+2+1); + strcpy(n->value,"[ "); + strcat(n->value,_("not human readable")); + strcat(n->value," ]"); + } + + n->flags.critical=crit; + + n->next=list; + list=n; + } + + return list; +} + +void +free_notation(struct notation *notation) +{ + while(notation) + { + struct notation *n=notation; + + xfree(n->name); + xfree(n->value); + xfree(n->altvalue); + xfree(n->bdat); + notation=n->next; + xfree(n); + } +} + static int -do_signature( iobuf_t out, int ctb, PKT_signature *sig ) +do_signature( IOBUF out, int ctb, PKT_signature *sig ) { int rc = 0; int n, i; - iobuf_t a = iobuf_temp(); + IOBUF a = iobuf_temp(); if( !sig->version ) iobuf_put( a, 3 ); @@ -1013,7 +1119,7 @@ do_signature( iobuf_t out, int ctb, PKT_signature *sig ) write_sign_packet_header(out, ctb, iobuf_get_temp_length(a) ); else write_header(out, ctb, iobuf_get_temp_length(a) ); - rc = iobuf_write_temp (out, a); + rc = iobuf_write_temp( out, a ); iobuf_close(a); return rc; @@ -1021,10 +1127,10 @@ do_signature( iobuf_t out, int ctb, PKT_signature *sig ) static int -do_onepass_sig( iobuf_t out, int ctb, PKT_onepass_sig *ops ) +do_onepass_sig( IOBUF out, int ctb, PKT_onepass_sig *ops ) { int rc = 0; - iobuf_t a = iobuf_temp(); + IOBUF a = iobuf_temp(); write_version( a, ctb ); iobuf_put(a, ops->sig_class ); @@ -1035,7 +1141,7 @@ do_onepass_sig( iobuf_t out, int ctb, PKT_onepass_sig *ops ) iobuf_put(a, ops->last ); write_header(out, ctb, iobuf_get_temp_length(a) ); - rc = iobuf_write_temp (out, a); + rc = iobuf_write_temp( out, a ); iobuf_close(a); return rc; @@ -1043,19 +1149,21 @@ do_onepass_sig( iobuf_t out, int ctb, PKT_onepass_sig *ops ) static int -write_16(iobuf_t out, u16 a) +write_16(IOBUF out, u16 a) { iobuf_put(out, a>>8); - return iobuf_put(out,a); + if( iobuf_put(out,a) ) + return -1; + return 0; } static int -write_32(iobuf_t out, u32 a) +write_32(IOBUF out, u32 a) { iobuf_put(out, a>> 24); iobuf_put(out, a>> 16); iobuf_put(out, a>> 8); - return iobuf_put (out, a); + return iobuf_put(out, a); } @@ -1088,14 +1196,14 @@ calc_header_length( u32 len, int new_ctb ) * Write the CTB and the packet length */ static int -write_header( iobuf_t out, int ctb, u32 len ) +write_header( IOBUF out, int ctb, u32 len ) { - return write_header2( out, ctb, len, 0, 1 ); + return write_header2( out, ctb, len, 0 ); } static int -write_sign_packet_header( iobuf_t out, int ctb, u32 len ) +write_sign_packet_header( IOBUF out, int ctb, u32 len ) { /* work around a bug in the pgp read function for signature packets, * which are not correctly coded and silently assume at some @@ -1106,57 +1214,66 @@ write_sign_packet_header( iobuf_t out, int ctb, u32 len ) } /**************** - * if HDRLEN is > 0, try to build a header of this length. - * we need this, so that we can hash packets without reading them again. + * If HDRLEN is > 0, try to build a header of this length. We need + * this so that we can hash packets without reading them again. If + * len is 0, write a partial or indeterminate length header, unless + * hdrlen is specified in which case write an actual zero length + * (using the specified hdrlen). */ static int -write_header2( iobuf_t out, int ctb, u32 len, int hdrlen, int blkmode ) +write_header2( IOBUF out, int ctb, u32 len, int hdrlen ) { - if( ctb & 0x40 ) - return write_new_header( out, ctb, len, hdrlen ); - - if( hdrlen ) { - if( !len ) - ctb |= 3; - else if( hdrlen == 2 && len < 256 ) - ; - else if( hdrlen == 3 && len < 65536 ) - ctb |= 1; - else - ctb |= 2; - } - else { - if( !len ) - ctb |= 3; - else if( len < 256 ) - ; - else if( len < 65536 ) - ctb |= 1; - else - ctb |= 2; + if( ctb & 0x40 ) + return write_new_header( out, ctb, len, hdrlen ); + + if( hdrlen ) + { + if( hdrlen == 2 && len < 256 ) + ; + else if( hdrlen == 3 && len < 65536 ) + ctb |= 1; + else + ctb |= 2; } - if( iobuf_put(out, ctb ) ) - return -1; - if( !len ) { - if( blkmode ) - iobuf_set_block_mode(out, 8196 ); + else + { + if( !len ) + ctb |= 3; + else if( len < 256 ) + ; + else if( len < 65536 ) + ctb |= 1; + else + ctb |= 2; } - else { - if( ctb & 2 ) { - iobuf_put(out, len >> 24 ); - iobuf_put(out, len >> 16 ); - } - if( ctb & 3 ) - iobuf_put(out, len >> 8 ); - if( iobuf_put(out, len ) ) + + if( iobuf_put(out, ctb ) ) + return -1; + + if( len || hdrlen ) + { + if( ctb & 2 ) + { + if(iobuf_put(out, len >> 24 )) + return -1; + if(iobuf_put(out, len >> 16 )) return -1; + } + + if( ctb & 3 ) + if(iobuf_put(out, len >> 8 )) + return -1; + + if( iobuf_put(out, len ) ) + return -1; } - return 0; + + return 0; } static int -write_new_header( iobuf_t out, int ctb, u32 len, int hdrlen ) +write_new_header( IOBUF out, int ctb, u32 len, int hdrlen ) { if( hdrlen ) log_bug("can't cope with hdrlen yet\n"); @@ -1195,7 +1312,7 @@ write_new_header( iobuf_t out, int ctb, u32 len, int hdrlen ) } static int -write_version( iobuf_t out, int ctb ) +write_version( IOBUF out, int ctb ) { if( iobuf_put( out, 3 ) ) return -1; diff --git a/g10/call-agent.c b/g10/call-agent.c index 9c7f8409b..31c43cf13 100644 --- a/g10/call-agent.c +++ b/g10/call-agent.c @@ -18,8 +18,8 @@ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA */ -#if 0 /* lety Emacs display a red warning */ -#error fixme: this shares a lof of code with the file in ../sm +#if 0 /* let Emacs display a red warning */ +#error fixme: this shares a lot of code with the file in ../sm #endif #include <config.h> diff --git a/g10/card-util.c b/g10/card-util.c index 1ff57ade5..0c8365405 100644 --- a/g10/card-util.c +++ b/g10/card-util.c @@ -1,5 +1,5 @@ /* card-util.c - Utility functions for the OpenPGP card. - * Copyright (C) 2003 Free Software Foundation, Inc. + * Copyright (C) 2003, 2004, 2005 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -15,7 +15,8 @@ * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. */ #include <config.h> @@ -27,7 +28,7 @@ #if GNUPG_MAJOR_VERSION != 1 #include "gpg.h" -#endif +#endif /*GNUPG_MAJOR_VERSION != 1*/ #include "util.h" #include "i18n.h" #include "ttyio.h" @@ -36,10 +37,14 @@ #include "main.h" #include "keyserver-internal.h" #if GNUPG_MAJOR_VERSION == 1 +#ifdef HAVE_LIBREADLINE +#include <stdio.h> +#include <readline/readline.h> +#endif /*HAVE_LIBREADLINE*/ #include "cardglue.h" -#else +#else /*GNUPG_MAJOR_VERSION!=1*/ #include "call-agent.h" -#endif +#endif /*GNUPG_MAJOR_VERSION!=1*/ #define CONTROL_D ('D' - 'A' + 1) @@ -63,21 +68,25 @@ change_pin (int chvno, int allow_admin) log_info (_("OpenPGP card no. %s detected\n"), info.serialno? info.serialno : "[none]"); - agent_release_card_info (&info); + agent_clear_pin_cache (info.serialno); if (opt.batch) { - log_error (_("sorry, can't do this in batch mode\n")); + agent_release_card_info (&info); + log_error (_("can't do this in batch mode\n")); return; } if(!allow_admin) { - rc = agent_scd_change_pin (1); + rc = agent_scd_change_pin (1, info.serialno); if (rc) tty_printf ("Error changing the PIN: %s\n", gpg_strerror (rc)); else - tty_printf ("PIN changed.\n"); + { + write_status (STATUS_SC_OP_SUCCESS); + tty_printf ("PIN changed.\n"); + } } else for (;;) @@ -99,33 +108,44 @@ change_pin (int chvno, int allow_admin) rc = 0; if (*answer == '1') { - rc = agent_scd_change_pin (1); + rc = agent_scd_change_pin (1, info.serialno); if (rc) tty_printf ("Error changing the PIN: %s\n", gpg_strerror (rc)); else - tty_printf ("PIN changed.\n"); + { + write_status (STATUS_SC_OP_SUCCESS); + tty_printf ("PIN changed.\n"); + } } else if (*answer == '2') { - rc = agent_scd_change_pin (101); + rc = agent_scd_change_pin (101, info.serialno); if (rc) tty_printf ("Error unblocking the PIN: %s\n", gpg_strerror (rc)); else - tty_printf ("PIN unblocked and new PIN set.\n"); - } + { + write_status (STATUS_SC_OP_SUCCESS); + tty_printf ("PIN unblocked and new PIN set.\n"); + } + } else if (*answer == '3') { - rc = agent_scd_change_pin (3); + rc = agent_scd_change_pin (3, info.serialno); if (rc) tty_printf ("Error changing the PIN: %s\n", gpg_strerror (rc)); else - tty_printf ("PIN changed.\n"); + { + write_status (STATUS_SC_OP_SUCCESS); + tty_printf ("PIN changed.\n"); + } } else if (*answer == 'q' || *answer == 'Q') { break; } } + + agent_release_card_info (&info); } static const char * @@ -137,6 +157,8 @@ get_manufacturer (unsigned int no) case 0: case 0xffff: return "test card"; case 0x0001: return "PPC Card Systems"; + case 0x0002: return "Prism"; + case 0x0003: return "OpenFortress"; default: return "unknown"; } } @@ -270,6 +292,8 @@ card_status (FILE *fp, char *serialno, size_t serialnobuflen) PKT_public_key *pk = xcalloc (1, sizeof *pk); int rc; unsigned int uval; + const unsigned char *thefpr; + int i; if (serialno && serialnobuflen) *serialno = 0; @@ -346,6 +370,17 @@ card_status (FILE *fp, char *serialno, size_t serialnobuflen) info.chvretry[0], info.chvretry[1], info.chvretry[2]); fprintf (fp, "sigcount:%lu:::\n", info.sig_counter); + for (i=0; i < 4; i++) + { + if (info.private_do[i]) + { + fprintf (fp, "private_do:%d:", i+1); + print_string (fp, info.private_do[i], + strlen (info.private_do[i]), ':'); + fputs (":\n", fp); + } + } + fputs ("cafpr:", fp); print_sha1_fpr_colon (fp, info.cafpr1valid? info.cafpr1:NULL); print_sha1_fpr_colon (fp, info.cafpr2valid? info.cafpr2:NULL); @@ -356,7 +391,9 @@ card_status (FILE *fp, char *serialno, size_t serialnobuflen) print_sha1_fpr_colon (fp, info.fpr2valid? info.fpr2:NULL); print_sha1_fpr_colon (fp, info.fpr3valid? info.fpr3:NULL); putc ('\n', fp); - + fprintf (fp, "fprtime:%lu:%lu:%lu:\n", + (unsigned long)info.fpr1time, (unsigned long)info.fpr2time, + (unsigned long)info.fpr3time); } else { @@ -377,6 +414,14 @@ card_status (FILE *fp, char *serialno, size_t serialnobuflen) info.disp_sex == 2? _("female") : _("unspecified")); print_name (fp, "URL of public key : ", info.pubkey_url); print_name (fp, "Login data .......: ", info.login_data); + if (info.private_do[0]) + print_name (fp, "Private DO 1 .....: ", info.private_do[0]); + if (info.private_do[1]) + print_name (fp, "Private DO 2 .....: ", info.private_do[1]); + if (info.private_do[2]) + print_name (fp, "Private DO 3 .....: ", info.private_do[2]); + if (info.private_do[3]) + print_name (fp, "Private DO 4 .....: ", info.private_do[3]); if (info.cafpr1valid) { tty_fprintf (fp, "CA fingerprint %d .:", 1); @@ -401,13 +446,48 @@ card_status (FILE *fp, char *serialno, size_t serialnobuflen) tty_fprintf (fp, "Signature counter : %lu\n", info.sig_counter); tty_fprintf (fp, "Signature key ....:"); print_sha1_fpr (fp, info.fpr1valid? info.fpr1:NULL); + if (info.fpr1valid && info.fpr1time) + tty_fprintf (fp, " created ....: %s\n", + isotimestamp (info.fpr1time)); tty_fprintf (fp, "Encryption key....:"); print_sha1_fpr (fp, info.fpr2valid? info.fpr2:NULL); + if (info.fpr2valid && info.fpr2time) + tty_fprintf (fp, " created ....: %s\n", + isotimestamp (info.fpr2time)); tty_fprintf (fp, "Authentication key:"); print_sha1_fpr (fp, info.fpr3valid? info.fpr3:NULL); + if (info.fpr3valid && info.fpr3time) + tty_fprintf (fp, " created ....: %s\n", + isotimestamp (info.fpr3time)); tty_fprintf (fp, "General key info..: "); - if (info.fpr1valid && !get_pubkey_byfprint (pk, info.fpr1, 20)) - print_pubkey_info (fp, pk); + + thefpr = (info.fpr1valid? info.fpr1 : info.fpr2valid? info.fpr2 : + info.fpr3valid? info.fpr3 : NULL); + if ( thefpr && !get_pubkey_byfprint (pk, thefpr, 20)) + { + KBNODE keyblock = NULL; + + print_pubkey_info (fp, pk); + + if ( !get_seckeyblock_byfprint (&keyblock, thefpr, 20) ) + print_card_key_info (fp, keyblock); + else if ( !get_keyblock_byfprint (&keyblock, thefpr, 20) ) + { + release_kbnode (keyblock); + keyblock = NULL; + + if (!auto_create_card_key_stub (info.serialno, + info.fpr1valid? info.fpr1:NULL, + info.fpr2valid? info.fpr2:NULL, + info.fpr3valid? info.fpr3:NULL)) + { + if ( !get_seckeyblock_byfprint (&keyblock, thefpr, 20) ) + print_card_key_info (fp, keyblock); + } + } + + release_kbnode (keyblock); + } else tty_fprintf (fp, "[none]\n"); } @@ -483,8 +563,7 @@ change_name (void) return -1; } - log_debug ("setting Name to `%s'\n", isoname); - rc = agent_scd_setattr ("DISP-NAME", isoname, strlen (isoname) ); + rc = agent_scd_setattr ("DISP-NAME", isoname, strlen (isoname), NULL ); if (rc) log_error ("error setting Name: %s\n", gpg_strerror (rc)); @@ -513,13 +592,16 @@ change_url (void) return -1; } - rc = agent_scd_setattr ("PUBKEY-URL", url, strlen (url) ); + rc = agent_scd_setattr ("PUBKEY-URL", url, strlen (url), NULL ); if (rc) log_error ("error setting URL: %s\n", gpg_strerror (rc)); xfree (url); return rc; } + +/* Fetch the key from the URL given on the card or try to get it from + the default keyserver. */ static int fetch_url(void) { @@ -532,7 +614,7 @@ fetch_url(void) rc=agent_scd_getattr("PUBKEY-URL",&info); if(rc) log_error("error retrieving URL from card: %s\n",gpg_strerror(rc)); - else if(info.pubkey_url) + else { struct keyserver_spec *spec=NULL; @@ -540,9 +622,9 @@ fetch_url(void) if(rc) log_error("error retrieving key fingerprint from card: %s\n", gpg_strerror(rc)); - else + else if (info.pubkey_url && *info.pubkey_url) { - spec=parse_keyserver_uri(info.pubkey_url,0,NULL,0); + spec=parse_keyserver_uri(info.pubkey_url,1,NULL,0); if(spec && info.fpr1valid) { /* This is not perfectly right. Currently, all card @@ -556,9 +638,11 @@ fetch_url(void) free_keyserver_spec(spec); } } + else if (info.fpr1valid) + { + rc = keyserver_import_fprint (info.fpr1, 20, opt.keyserver); + } } - else - log_error("no URL set on card\n"); return rc; #else @@ -624,7 +708,7 @@ change_login (const char *args) return -1; } - rc = agent_scd_setattr ("LOGIN-DATA", data, n ); + rc = agent_scd_setattr ("LOGIN-DATA", data, n, NULL ); if (rc) log_error ("error setting login data: %s\n", gpg_strerror (rc)); xfree (data); @@ -632,6 +716,75 @@ change_login (const char *args) } static int +change_private_do (const char *args, int nr) +{ + char do_name[] = "PRIVATE-DO-X"; + char *data; + int n; + int rc; + + assert (nr >= 1 && nr <= 4); + do_name[11] = '0' + nr; + + if (args && (args = strchr (args, '<'))) /* Read it from a file */ + { + FILE *fp; + + /* Fixme: Factor this duplicated code out. */ + for (args++; spacep (args); args++) + ; + fp = fopen (args, "rb"); +#if GNUPG_MAJOR_VERSION == 1 + if (fp && is_secured_file (fileno (fp))) + { + fclose (fp); + fp = NULL; + errno = EPERM; + } +#endif + if (!fp) + { + tty_printf (_("can't open `%s': %s\n"), args, strerror (errno)); + return -1; + } + + data = xmalloc (254); + n = fread (data, 1, 254, fp); + fclose (fp); + if (n < 0) + { + tty_printf (_("error reading `%s': %s\n"), args, strerror (errno)); + xfree (data); + return -1; + } + } + else + { + data = cpr_get ("cardedit.change_private_do", + _("Private DO data: ")); + if (!data) + return -1; + trim_spaces (data); + cpr_kill_prompt (); + n = strlen (data); + } + + if (n > 254 ) + { + tty_printf (_("Error: Private DO too long " + "(limit is %d characters).\n"), 254); + xfree (data); + return -1; + } + + rc = agent_scd_setattr (do_name, data, n, NULL ); + if (rc) + log_error ("error setting private DO: %s\n", gpg_strerror (rc)); + xfree (data); + return rc; +} + +static int change_lang (void) { char *data, *p; @@ -660,7 +813,7 @@ change_lang (void) return -1; } - rc = agent_scd_setattr ("DISP-LANG", data, strlen (data) ); + rc = agent_scd_setattr ("DISP-LANG", data, strlen (data), NULL ); if (rc) log_error ("error setting lang: %s\n", gpg_strerror (rc)); xfree (data); @@ -695,7 +848,7 @@ change_sex (void) return -1; } - rc = agent_scd_setattr ("DISP-SEX", str, 1 ); + rc = agent_scd_setattr ("DISP-SEX", str, 1, NULL ); if (rc) log_error ("error setting sex: %s\n", gpg_strerror (rc)); xfree (data); @@ -740,7 +893,7 @@ change_cafpr (int fprno) rc = agent_scd_setattr (fprno==1?"CA-FPR-1": fprno==2?"CA-FPR-2": - fprno==3?"CA-FPR-3":"x", fpr, 20 ); + fprno==3?"CA-FPR-3":"x", fpr, 20, NULL ); if (rc) log_error ("error setting cafpr: %s\n", gpg_strerror (rc)); return rc; @@ -765,7 +918,7 @@ toggle_forcesig (void) newstate = !info.chv1_cached; agent_release_card_info (&info); - rc = agent_scd_setattr ("CHV-STATUS-1", newstate? "\x01":"", 1); + rc = agent_scd_setattr ("CHV-STATUS-1", newstate? "\x01":"", 1, NULL); if (rc) log_error ("error toggling signature PIN flag: %s\n", gpg_strerror (rc)); } @@ -803,12 +956,14 @@ check_pin_for_key_operation (struct agent_card_info_s *info, int *forced_chv1) { int rc = 0; + agent_clear_pin_cache (info->serialno); + *forced_chv1 = !info->chv1_cached; if (*forced_chv1) { /* Switch of the forced mode so that during key generation we don't get bothered with PIN queries for each self-signature. */ - rc = agent_scd_setattr ("CHV-STATUS-1", "\x01", 1); + rc = agent_scd_setattr ("CHV-STATUS-1", "\x01", 1, info->serialno); if (rc) { log_error ("error clearing forced signature PIN flag: %s\n", @@ -836,7 +991,7 @@ restore_forced_chv1 (int *forced_chv1) if (*forced_chv1) { /* Switch back to forced state. */ - rc = agent_scd_setattr ("CHV-STATUS-1", "", 1); + rc = agent_scd_setattr ("CHV-STATUS-1", "", 1, NULL); if (rc) { log_error ("error setting forced signature PIN flag: %s\n", @@ -900,7 +1055,7 @@ generate_card_keys (const char *serialno) want_backup=answer_is_yes_no_default(answer,1); cpr_kill_prompt(); - m_free(answer); + xfree(answer); } #else want_backup = cpr_get_answer_is_yes @@ -949,7 +1104,7 @@ generate_card_keys (const char *serialno) } -/* This fucntion is used by the key edit menu to generate an arbitrary +/* This function is used by the key edit menu to generate an arbitrary subkey. */ int card_generate_subkey (KBNODE pub_keyblock, KBNODE sec_keyblock) @@ -1007,9 +1162,10 @@ card_generate_subkey (KBNODE pub_keyblock, KBNODE sec_keyblock) } -/* Store the subkey at NODE into the smartcard and modify NODE to - carry the serrialno stuff instead of the actual secret key - parameters. */ +/* Store the key at NODE into the smartcard and modify NODE to + carry the serialno stuff instead of the actual secret key + parameters. USE is the usage for that key; 0 means any + usage. */ int card_store_subkey (KBNODE node, int use) { @@ -1140,49 +1296,101 @@ card_store_subkey (KBNODE node, int use) } -/* Menu to edit all user changeable values on an OpenPGP card. Only - Key creation is not handled here. */ -void -card_edit (STRLIST commands) -{ - enum cmdids { + +/* Data used by the command parser. This needs to be outside of the + function scope to allow readline based command completion. */ +enum cmdids + { cmdNOP = 0, - cmdQUIT, cmdADMIN, cmdHELP, cmdLIST, cmdDEBUG, + cmdQUIT, cmdADMIN, cmdHELP, cmdLIST, cmdDEBUG, cmdVERIFY, cmdNAME, cmdURL, cmdFETCH, cmdLOGIN, cmdLANG, cmdSEX, cmdCAFPR, - cmdFORCESIG, cmdGENERATE, cmdPASSWD, + cmdFORCESIG, cmdGENERATE, cmdPASSWD, cmdPRIVATEDO, cmdINVCMD }; - static struct { - const char *name; - enum cmdids id; - int admin_only; - const char *desc; - } cmds[] = { - { N_("quit") , cmdQUIT , 0, N_("quit this menu") }, - { N_("q") , cmdQUIT , 0, NULL }, - { N_("admin") , cmdADMIN , 0, N_("show admin commands") }, - { N_("help") , cmdHELP , 0, N_("show this help") }, - { "?" , cmdHELP , 0, NULL }, - { N_("list") , cmdLIST , 0, N_("list all available data") }, - { N_("l") , cmdLIST , 0, NULL }, - { N_("debug") , cmdDEBUG , 0, NULL }, - { N_("name") , cmdNAME , 1, N_("change card holder's name") }, - { N_("url") , cmdURL , 1, N_("change URL to retrieve key") }, - { N_("fetch") , cmdFETCH , 0, - N_("fetch the key specified in the card URL") }, - { N_("login") , cmdLOGIN , 1, N_("change the login name") }, - { N_("lang") , cmdLANG , 1, N_("change the language preferences") }, - { N_("sex") , cmdSEX , 1, N_("change card holder's sex") }, - { N_("cafpr"), cmdCAFPR, 1, N_("change a CA fingerprint") }, - { N_("forcesig"), - cmdFORCESIG, 1, N_("toggle the signature force PIN flag") }, - { N_("generate"), - cmdGENERATE, 1, N_("generate new keys") }, - { N_("passwd"), cmdPASSWD, 0, N_("menu to change or unblock the PIN") }, +static struct +{ + const char *name; + enum cmdids id; + int admin_only; + const char *desc; +} cmds[] = + { + { "quit" , cmdQUIT , 0, N_("quit this menu")}, + { "q" , cmdQUIT , 0, NULL }, + { "admin" , cmdADMIN , 0, N_("show admin commands")}, + { "help" , cmdHELP , 0, N_("show this help")}, + { "?" , cmdHELP , 0, NULL }, + { "list" , cmdLIST , 0, N_("list all available data")}, + { "l" , cmdLIST , 0, NULL }, + { "debug" , cmdDEBUG , 0, NULL }, + { "name" , cmdNAME , 1, N_("change card holder's name")}, + { "url" , cmdURL , 1, N_("change URL to retrieve key")}, + { "fetch" , cmdFETCH , 0, N_("fetch the key specified in the card URL")}, + { "login" , cmdLOGIN , 1, N_("change the login name")}, + { "lang" , cmdLANG , 1, N_("change the language preferences")}, + { "sex" , cmdSEX , 1, N_("change card holder's sex")}, + { "cafpr" , cmdCAFPR , 1, N_("change a CA fingerprint")}, + { "forcesig", cmdFORCESIG, 1, N_("toggle the signature force PIN flag")}, + { "generate", cmdGENERATE, 1, N_("generate new keys")}, + { "passwd" , cmdPASSWD, 0, N_("menu to change or unblock the PIN")}, + { "verify" , cmdVERIFY, 0, N_("verify the PIN and list all data")}, + /* Note, that we do not announce this command yet. */ + { "privatedo", cmdPRIVATEDO, 0, NULL }, { NULL, cmdINVCMD, 0, NULL } }; - + + +#if GNUPG_MAJOR_VERSION == 1 && defined (HAVE_LIBREADLINE) + +/* These two functions are used by readline for command completion. */ + +static char * +command_generator(const char *text,int state) +{ + static int list_index,len; + const char *name; + + /* If this is a new word to complete, initialize now. This includes + saving the length of TEXT for efficiency, and initializing the + index variable to 0. */ + if(!state) + { + list_index=0; + len=strlen(text); + } + + /* Return the next partial match */ + while((name=cmds[list_index].name)) + { + /* Only complete commands that have help text */ + if(cmds[list_index++].desc && strncmp(name,text,len)==0) + return strdup(name); + } + + return NULL; +} + +static char ** +card_edit_completion(const char *text, int start, int end) +{ + /* If we are at the start of a line, we try and command-complete. + If not, just do nothing for now. */ + + if(start==0) + return rl_completion_matches(text,command_generator); + + rl_attempted_completion_over=1; + + return NULL; +} +#endif /* GNUPG_MAJOR_VERSION == 1 && HAVE_LIBREADLINE */ + +/* Menu to edit all user changeable values on an OpenPGP card. Only + Key creation is not handled here. */ +void +card_edit (STRLIST commands) +{ enum cmdids cmd = cmdNOP; int have_commands = !!commands; int redisplay = 1; @@ -1195,7 +1403,7 @@ card_edit (STRLIST commands) ; else if (opt.batch && !have_commands) { - log_error(_("can't do that in batchmode\n")); + log_error(_("can't do this in batch mode\n")); goto leave; } @@ -1243,8 +1451,14 @@ card_edit (STRLIST commands) if (!have_commands) { +#if GNUPG_MAJOR_VERSION == 1 + tty_enable_completion (card_edit_completion); +#endif answer = cpr_get_no_help("cardedit.prompt", _("Command> ")); cpr_kill_prompt(); +#if GNUPG_MAJOR_VERSION == 1 + tty_disable_completion (); +#endif } trim_spaces(answer); } @@ -1292,9 +1506,33 @@ card_edit (STRLIST commands) break; case cmdADMIN: - allow_admin=!allow_admin; + if ( !strcmp (arg_string, "on") ) + allow_admin = 1; + else if ( !strcmp (arg_string, "off") ) + allow_admin = 0; + else if ( !strcmp (arg_string, "verify") ) + { + /* Force verification of the Admin Command. However, + this is only done if the retry counter is at initial + state. */ + char *tmp = xmalloc (strlen (serialnobuf) + 6 + 1); + strcpy (stpcpy (tmp, serialnobuf), "[CHV3]"); + allow_admin = !agent_scd_checkpin (tmp); + xfree (tmp); + } + else /* Toggle. */ + allow_admin=!allow_admin; + if(allow_admin) + tty_printf(_("Admin commands are allowed\n")); + else + tty_printf(_("Admin commands are not allowed\n")); break; + case cmdVERIFY: + agent_scd_checkpin (serialnobuf); + redisplay = 1; + break; + case cmdLIST: redisplay = 1; break; @@ -1331,6 +1569,14 @@ card_edit (STRLIST commands) change_cafpr (arg_number); break; + case cmdPRIVATEDO: + if ( arg_number < 1 || arg_number > 4 ) + tty_printf ("usage: privatedo N\n" + " 1 <= N <= 4\n"); + else + change_private_do (arg_string, arg_number); + break; + case cmdFORCESIG: toggle_forcesig (); break; diff --git a/g10/cipher.c b/g10/cipher.c index 3d51a874a..ff1080495 100644 --- a/g10/cipher.c +++ b/g10/cipher.c @@ -1,5 +1,6 @@ /* cipher.c - En-/De-ciphering filter - * Copyright (C) 1998, 1999, 2000, 2001, 2003 Free Software Foundation, Inc. + * Copyright (C) 1998, 1999, 2000, 2001, 2003, + * 2006 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -15,7 +16,8 @@ * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. */ #include <config.h> @@ -28,7 +30,6 @@ #include "gpg.h" #include "errors.h" #include "iobuf.h" -#include "memory.h" #include "util.h" #include "filter.h" #include "packet.h" @@ -41,17 +42,17 @@ static void -write_header( cipher_filter_context_t *cfx, iobuf_t a ) +write_header( cipher_filter_context_t *cfx, IOBUF a ) { + gcry_error_t err; PACKET pkt; PKT_encrypted ed; byte temp[18]; unsigned int blocksize; unsigned int nprefix; - gpg_error_t rc; - blocksize = gcry_cipher_get_algo_blklen ( cfx->dek->algo ); - if( blocksize < 8 || blocksize > 16 ) + blocksize = gcry_cipher_algo_blklen (cfx->dek->algo); + if ( blocksize < 8 || blocksize > 16 ) log_fatal("unsupported blocksize %u\n", blocksize ); memset( &ed, 0, sizeof ed ); @@ -60,9 +61,9 @@ write_header( cipher_filter_context_t *cfx, iobuf_t a ) ed.new_ctb = !ed.len && !RFC1991; if( cfx->dek->use_mdc ) { ed.mdc_method = DIGEST_ALGO_SHA1; - gcry_md_open (&cfx->mdc_hash, GCRY_MD_SHA1, 0 ); + gcry_md_open (&cfx->mdc_hash, DIGEST_ALGO_SHA1, 0); if ( DBG_HASHING ) - gcry_md_start_debug ( cfx->mdc_hash, "creatmdc" ); + gcry_md_start_debug (cfx->mdc_hash, "creatmdc"); } { @@ -78,28 +79,31 @@ write_header( cipher_filter_context_t *cfx, iobuf_t a ) if( build_packet( a, &pkt )) log_bug("build_packet(ENCR_DATA) failed\n"); nprefix = blocksize; - gcry_randomize ( temp, nprefix, GCRY_STRONG_RANDOM); + gcry_randomize (temp, nprefix, GCRY_STRONG_RANDOM ); temp[nprefix] = temp[nprefix-2]; temp[nprefix+1] = temp[nprefix-1]; print_cipher_algo_note( cfx->dek->algo ); - rc = gcry_cipher_open (&cfx->cipher_hd, cfx->dek->algo, - GCRY_CIPHER_MODE_CFB, - GCRY_CIPHER_SECURE - | ((cfx->dek->use_mdc || cfx->dek->algo >= 100) ? - 0 : GCRY_CIPHER_ENABLE_SYNC)); + err = gcry_cipher_open (&cfx->cipher_hd, + cfx->dek->algo, + GCRY_CIPHER_MODE_CFB, + (GCRY_CIPHER_SECURE + | ((cfx->dek->use_mdc || cfx->dek->algo >= 100)? + 0 : GCRY_CIPHER_ENABLE_SYNC)); if (rc) { - /* we should never get an error here cause we already checked, that - * the algorithm is available. */ + /* We should never get an error here cause we already checked, + * that the algorithm is available. */ BUG(); } + + /* log_hexdump( "thekey", cfx->dek->key, cfx->dek->keylen );*/ gcry_cipher_setkey( cfx->cipher_hd, cfx->dek->key, cfx->dek->keylen ); gcry_cipher_setiv( cfx->cipher_hd, NULL, 0 ); /* log_hexdump( "prefix", temp, nprefix+2 ); */ - if( cfx->mdc_hash ) /* hash the "IV" */ - gcry_md_write( cfx->mdc_hash, temp, nprefix+2 ); - gcry_cipher_encrypt( cfx->cipher_hd, temp, nprefix+2, NULL, 0); - gcry_cipher_sync( cfx->cipher_hd ); + if (cfx->mdc_hash) /* Hash the "IV". */ + gcry_md_write (cfx->mdc_hash, temp, nprefix+2 ); + gcry_cipher_encrypt (cfx->cipher_hd, temp, nprefix+2, NULL, 0); + gcry_cipher_sync (cfx->cipher_hd); iobuf_write(a, temp, nprefix+2); cfx->header=1; } @@ -111,7 +115,7 @@ write_header( cipher_filter_context_t *cfx, iobuf_t a ) */ int cipher_filter( void *opaque, int control, - iobuf_t a, byte *buf, size_t *ret_len) + IOBUF a, byte *buf, size_t *ret_len) { size_t size = *ret_len; cipher_filter_context_t *cfx = opaque; @@ -125,32 +129,31 @@ cipher_filter( void *opaque, int control, if( !cfx->header ) { write_header( cfx, a ); } - if( cfx->mdc_hash ) - gcry_md_write( cfx->mdc_hash, buf, size ); - gcry_cipher_encrypt( cfx->cipher_hd, buf, size, NULL, 0); + if (cfx->mdc_hash) + gcry_md_write (cfx->mdc_hash, buf, size); + gcry_cipher_encrypt (cfx->cipher_hd, buf, size, NULL, 0); rc = iobuf_write( a, buf, size ); } else if( control == IOBUFCTRL_FREE ) { if( cfx->mdc_hash ) { byte *hash; - int hashlen = gcry_md_get_algo_dlen (gcry_md_get_algo ( - cfx->mdc_hash)); + int hashlen = gcry_md_get_algo_dlen (gcry_md_get_algo + (cfx->mdc_hash)); byte temp[22]; assert( hashlen == 20 ); /* we must hash the prefix of the MDC packet here */ temp[0] = 0xd3; temp[1] = 0x14; - gcry_md_putc ( cfx->mdc_hash, temp[0] ); - gcry_md_putc ( cfx->mdc_hash, temp[1] ); + gcry_md_putc (cfx->mdc_hash, temp[0]); + gcry_md_putc (cfx->mdc_hash, temp[1]); - gcry_md_final ( cfx->mdc_hash ); - hash = gcry_md_read ( cfx->mdc_hash, 0 ); + gcry_md_final (cfx->mdc_hash); + hash = gcry_md_read (cfx->mdc_hash, 0); memcpy(temp+2, hash, 20); - gcry_cipher_encrypt( cfx->cipher_hd, temp, 22, NULL, 0 ); - gcry_md_close ( cfx->mdc_hash ); cfx->mdc_hash = NULL; - rc = iobuf_write( a, temp, 22 ); - if (rc) + gcry_cipher_encrypt (cfx->cipher_hd, temp, 22, NULL, 0); + gcry_md_close (cfx->mdc_hash); cfx->mdc_hash = NULL; + if( iobuf_write( a, temp, 22 ) ) log_error("writing MDC packet failed\n" ); } gcry_cipher_close (cfx->cipher_hd); @@ -160,6 +163,3 @@ cipher_filter( void *opaque, int control, } return rc; } - - - diff --git a/g10/compress.c b/g10/compress.c index 7dce1790a..030a4c1d1 100644 --- a/g10/compress.c +++ b/g10/compress.c @@ -1,6 +1,6 @@ /* compress.c - compress filter * Copyright (C) 1998, 1999, 2000, 2001, 2002, - * 2003 Free Software Foundation, Inc. + * 2003, 2006 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -16,9 +16,16 @@ * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. */ +/* Note that the code in compress-bz2.c is nearly identical to the + code here, so if you fix a bug here, look there to see if a + matching bug needs to be fixed. I tried to have one set of + functions that could do ZIP, ZLIB, and BZIP2, but it became + dangerously unreadable with #ifdefs and if(algo) -dshaw */ + #include <config.h> #include <stdio.h> #include <stdlib.h> @@ -27,43 +34,42 @@ #include <assert.h> #include <errno.h> #include <zlib.h> -#ifdef __riscos__ +#if defined(__riscos__) && defined(USE_ZLIBRISCOS) # include "zlib-riscos.h" -#endif +#endif #include "gpg.h" #include "util.h" -#include "memory.h" #include "packet.h" #include "filter.h" #include "main.h" #include "options.h" +int compress_filter_bz2( void *opaque, int control, + IOBUF a, byte *buf, size_t *ret_len); + static void init_compress( compress_filter_context_t *zfx, z_stream *zs ) { int rc; int level; -#ifdef __riscos__ +#if defined(__riscos__) && defined(USE_ZLIBRISCOS) static int zlib_initialized = 0; if (!zlib_initialized) zlib_initialized = riscos_load_module("ZLib", zlib_path, 1); #endif - if( opt.compress >= 0 && opt.compress <= 9 ) - level = opt.compress; - else if( opt.compress == -1 ) + if( opt.compress_level >= 1 && opt.compress_level <= 9 ) + level = opt.compress_level; + else if( opt.compress_level == -1 ) level = Z_DEFAULT_COMPRESSION; - else if( opt.compress == 10 ) /* remove this ! */ - level = 0; else { log_error("invalid compression level; using default level\n"); level = Z_DEFAULT_COMPRESSION; } - if( (rc = zfx->algo == 1? deflateInit2( zs, level, Z_DEFLATED, -13, 8, Z_DEFAULT_STRATEGY) : deflateInit( zs, level ) @@ -75,13 +81,13 @@ init_compress( compress_filter_context_t *zfx, z_stream *zs ) } zfx->outbufsize = 8192; - zfx->outbuf = xmalloc ( zfx->outbufsize ); + zfx->outbuf = xmalloc( zfx->outbufsize ); } static int -do_compress( compress_filter_context_t *zfx, z_stream *zs, int flush, iobuf_t a ) +do_compress( compress_filter_context_t *zfx, z_stream *zs, int flush, IOBUF a ) { - gpg_error_t rc; + int rc; int zrc; unsigned n; @@ -111,12 +117,10 @@ do_compress( compress_filter_context_t *zfx, z_stream *zs, int flush, iobuf_t a (unsigned)zs->avail_in, (unsigned)zs->avail_out, (unsigned)n, zrc ); - rc = iobuf_write (a, zfx->outbuf, n); - if (rc) - { + if( (rc=iobuf_write( a, zfx->outbuf, n )) ) { log_debug("deflate: iobuf_write failed\n"); return rc; - } + } } while( zs->avail_in || (flush == Z_FINISH && zrc != Z_STREAM_END) ); return 0; } @@ -145,13 +149,13 @@ init_uncompress( compress_filter_context_t *zfx, z_stream *zs ) } zfx->inbufsize = 2048; - zfx->inbuf = xmalloc ( zfx->inbufsize ); + zfx->inbuf = xmalloc( zfx->inbufsize ); zs->avail_in = 0; } static int do_uncompress( compress_filter_context_t *zfx, z_stream *zs, - iobuf_t a, size_t *ret_len ) + IOBUF a, size_t *ret_len ) { int zrc; int rc=0; @@ -213,9 +217,9 @@ do_uncompress( compress_filter_context_t *zfx, z_stream *zs, return rc; } -int +static int compress_filter( void *opaque, int control, - iobuf_t a, byte *buf, size_t *ret_len) + IOBUF a, byte *buf, size_t *ret_len) { size_t size = *ret_len; compress_filter_context_t *zfx = opaque; @@ -224,7 +228,7 @@ compress_filter( void *opaque, int control, if( control == IOBUFCTRL_UNDERFLOW ) { if( !zfx->status ) { - zs = zfx->opaque = xcalloc (1, sizeof *zs ); + zs = zfx->opaque = xmalloc_clear( sizeof *zs ); init_uncompress( zfx, zs ); zfx->status = 1; } @@ -242,10 +246,8 @@ compress_filter( void *opaque, int control, if( !zfx->status ) { PACKET pkt; PKT_compressed cd; - - if( !zfx->algo ) - zfx->algo = DEFAULT_COMPRESS_ALGO; - if( zfx->algo != 1 && zfx->algo != 2 ) + if(zfx->algo != COMPRESS_ALGO_ZIP + && zfx->algo != COMPRESS_ALGO_ZLIB) BUG(); memset( &cd, 0, sizeof cd ); cd.len = 0; @@ -255,7 +257,7 @@ compress_filter( void *opaque, int control, pkt.pkt.compressed = &cd; if( build_packet( a, &pkt )) log_bug("build_packet(PKT_COMPRESSED) failed\n"); - zs = zfx->opaque = xcalloc (1, sizeof *zs ); + zs = zfx->opaque = xmalloc_clear( sizeof *zs ); init_compress( zfx, zs ); zfx->status = 2; } @@ -271,9 +273,9 @@ compress_filter( void *opaque, int control, else if( control == IOBUFCTRL_FREE ) { if( zfx->status == 1 ) { inflateEnd(zs); - xfree (zs); + xfree(zs); zfx->opaque = NULL; - xfree (zfx->outbuf); zfx->outbuf = NULL; + xfree(zfx->outbuf); zfx->outbuf = NULL; } else if( zfx->status == 2 ) { #ifndef __riscos__ @@ -284,9 +286,9 @@ compress_filter( void *opaque, int control, zs->avail_in = 0; do_compress( zfx, zs, Z_FINISH, a ); deflateEnd(zs); - xfree (zs); + xfree(zs); zfx->opaque = NULL; - xfree (zfx->outbuf); zfx->outbuf = NULL; + xfree(zfx->outbuf); zfx->outbuf = NULL; } if (zfx->release) zfx->release (zfx); @@ -308,17 +310,17 @@ release_context (compress_filter_context_t *ctx) */ int handle_compressed( void *procctx, PKT_compressed *cd, - int (*callback)(iobuf_t, void *), void *passthru ) + int (*callback)(IOBUF, void *), void *passthru ) { compress_filter_context_t *cfx; int rc; - if( cd->algorithm < 1 || cd->algorithm > 2 ) - return GPG_ERR_COMPR_ALGO; - cfx = xcalloc (1,sizeof *cfx); - cfx->algo = cd->algorithm; + if(check_compress_algo(cd->algorithm)) + return G10ERR_COMPR_ALGO; + cfx = xmalloc_clear (sizeof *cfx); cfx->release = release_context; - iobuf_push_filter( cd->buf, compress_filter, cfx ); + cfx->algo = cd->algorithm; + push_compress_filter(cd->buf,cfx,cd->algorithm); if( callback ) rc = callback(cd->buf, passthru ); else @@ -327,3 +329,38 @@ handle_compressed( void *procctx, PKT_compressed *cd, return rc; } +void +push_compress_filter(IOBUF out,compress_filter_context_t *zfx,int algo) +{ + push_compress_filter2(out,zfx,algo,0); +} + +void +push_compress_filter2(IOBUF out,compress_filter_context_t *zfx, + int algo,int rel) +{ + if(algo>=0) + zfx->algo=algo; + else + zfx->algo=DEFAULT_COMPRESS_ALGO; + + switch(zfx->algo) + { + case COMPRESS_ALGO_NONE: + break; + + case COMPRESS_ALGO_ZIP: + case COMPRESS_ALGO_ZLIB: + iobuf_push_filter2(out,compress_filter,zfx,rel); + break; + +#ifdef HAVE_BZIP2 + case COMPRESS_ALGO_BZIP2: + iobuf_push_filter2(out,compress_filter_bz2,zfx,rel); + break; +#endif + + default: + BUG(); + } +} diff --git a/g10/dearmor.c b/g10/dearmor.c index 4f9fa2db7..dc9a22fad 100644 --- a/g10/dearmor.c +++ b/g10/dearmor.c @@ -1,5 +1,5 @@ /* dearmor.c - Armor utility - * Copyright (C) 1998, 1999, 2000, 2001, 2003 Free Software Foundation, Inc. + * Copyright (C) 1998, 1999, 2000, 2001 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -15,7 +15,8 @@ * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. */ #include <config.h> @@ -28,13 +29,12 @@ #include "gpg.h" #include "errors.h" #include "iobuf.h" -#include "memory.h" #include "util.h" #include "filter.h" #include "packet.h" #include "options.h" #include "main.h" - +#include "i18n.h" /**************** * Take an armor file and write it out without armor @@ -43,17 +43,24 @@ int dearmor_file( const char *fname ) { armor_filter_context_t afx; - iobuf_t inp = NULL, out = NULL; + IOBUF inp = NULL, out = NULL; int rc = 0; int c; memset( &afx, 0, sizeof afx); /* prepare iobufs */ - if( !(inp = iobuf_open(fname)) ) { - rc = gpg_error_from_errno (errno); - log_error("can't open %s: %s\n", fname? fname: "[stdin]", + inp = iobuf_open(fname); + if (inp && is_secured_file (iobuf_get_fd (inp))) + { + iobuf_close (inp); + inp = NULL; + errno = EPERM; + } + if (!inp) { + log_error(_("can't open `%s': %s\n"), fname? fname: "[stdin]", strerror(errno) ); + rc = G10ERR_OPEN_FILE; goto leave; } @@ -85,17 +92,24 @@ int enarmor_file( const char *fname ) { armor_filter_context_t afx; - iobuf_t inp = NULL, out = NULL; + IOBUF inp = NULL, out = NULL; int rc = 0; int c; memset( &afx, 0, sizeof afx); /* prepare iobufs */ - if( !(inp = iobuf_open(fname)) ) { - rc = gpg_error_from_errno (errno); - log_error("can't open %s: %s\n", fname? fname: "[stdin]", - strerror(errno) ); + inp = iobuf_open(fname); + if (inp && is_secured_file (iobuf_get_fd (inp))) + { + iobuf_close (inp); + inp = NULL; + errno = EPERM; + } + if (!inp) { + log_error(_("can't open `%s': %s\n"), fname? fname: "[stdin]", + strerror(errno) ); + rc = G10ERR_OPEN_FILE; goto leave; } diff --git a/g10/decrypt.c b/g10/decrypt.c index 98a270cfb..9a37283c1 100644 --- a/g10/decrypt.c +++ b/g10/decrypt.c @@ -1,5 +1,6 @@ /* decrypt.c - verify signed data - * Copyright (C) 1998,1999,2000,2001,2002,2003 Free Software Foundation, Inc. + * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003 + * 2004 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -15,7 +16,8 @@ * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. */ #include <config.h> @@ -25,12 +27,12 @@ #include <errno.h> #include <assert.h> +#include "gpg.h" #include "options.h" #include "packet.h" #include "errors.h" #include "iobuf.h" #include "keydb.h" -#include "memory.h" #include "util.h" #include "main.h" #include "status.h" @@ -49,17 +51,24 @@ int decrypt_message( const char *filename ) { - iobuf_t fp; + IOBUF fp; armor_filter_context_t afx; progress_filter_context_t pfx; int rc; int no_out=0; - /* open the message file */ + /* Open the message file. */ fp = iobuf_open(filename); + if (fp && is_secured_file (iobuf_get_fd (fp))) + { + iobuf_close (fp); + fp = NULL; + errno = EPERM; + } if( !fp ) { rc = gpg_error_from_errno (errno); - log_error(_("can't open `%s'\n"), print_fname_stdin(filename)); + log_error (_("can't open `%s': %s\n"), print_fname_stdin(filename), + gpg_strerror (rc)); return rc; } @@ -84,13 +93,14 @@ decrypt_message( const char *filename ) } void -decrypt_messages(int nfiles, char **files) +decrypt_messages(int nfiles, char *files[]) { - iobuf_t fp; + IOBUF fp; armor_filter_context_t afx; progress_filter_context_t pfx; char *p, *output = NULL; - int rc = 0; + int rc=0,use_stdin=0; + unsigned int lno=0; if (opt.outfile) { @@ -99,20 +109,61 @@ decrypt_messages(int nfiles, char **files) } - while (nfiles--) + if(!nfiles) + use_stdin=1; + + for(;;) { - print_file_status(STATUS_FILE_START, *files, 3); - output = make_outfile_name(*files); + char line[2048]; + char *filename=NULL; + + if(use_stdin) + { + if(fgets(line, DIM(line), stdin)) + { + lno++; + if (!*line || line[strlen(line)-1] != '\n') + log_error("input line %u too long or missing LF\n", lno); + else + { + line[strlen(line)-1] = '\0'; + filename=line; + } + } + } + else + { + if(nfiles) + { + filename=*files; + nfiles--; + files++; + } + } + + if(filename==NULL) + break; + + print_file_status(STATUS_FILE_START, filename, 3); + output = make_outfile_name(filename); if (!output) goto next_file; - fp = iobuf_open(*files); + fp = iobuf_open(filename); + if (fp) + iobuf_ioctl (fp,3,1,NULL); /* disable fd caching */ + if (fp && is_secured_file (iobuf_get_fd (fp))) + { + iobuf_close (fp); + fp = NULL; + errno = EPERM; + } if (!fp) { - log_error(_("can't open `%s'\n"), print_fname_stdin(*files)); + log_error(_("can't open `%s'\n"), print_fname_stdin(filename)); goto next_file; } - handle_progress (&pfx, fp, *files); + handle_progress (&pfx, fp, filename); if (!opt.no_armor) { @@ -125,8 +176,8 @@ decrypt_messages(int nfiles, char **files) rc = proc_packets(NULL, fp); iobuf_close(fp); if (rc) - log_error("%s: decryption failed: %s\n", print_fname_stdin(*files), - gpg_strerror (rc)); + log_error("%s: decryption failed: %s\n", print_fname_stdin(filename), + g10_errstr(rc)); p = get_last_passphrase(); set_next_passphrase(p); xfree (p); @@ -134,9 +185,8 @@ decrypt_messages(int nfiles, char **files) next_file: /* Note that we emit file_done even after an error. */ write_status( STATUS_FILE_DONE ); - xfree (output); - files++; + xfree(output); } + set_next_passphrase(NULL); } - diff --git a/g10/delkey.c b/g10/delkey.c index 6263dec47..bb8108754 100644 --- a/g10/delkey.c +++ b/g10/delkey.c @@ -1,5 +1,6 @@ /* delkey.c - delete keys - * Copyright (C) 1998, 1999, 2000, 2001, 2002 Free Software Foundation, Inc. + * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2004, + * 2005, 2006 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -15,7 +16,8 @@ * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. */ #include <config.h> @@ -26,12 +28,12 @@ #include <assert.h> #include <ctype.h> +#include "gpg.h" #include "options.h" #include "packet.h" #include "errors.h" #include "iobuf.h" #include "keydb.h" -#include "memory.h" #include "util.h" #include "main.h" #include "trustdb.h" @@ -47,7 +49,7 @@ * key can't be deleted for that reason. */ static int -do_delete_key( const char *username, int secret, int *r_sec_avail ) +do_delete_key( const char *username, int secret, int force, int *r_sec_avail ) { int rc = 0; KBNODE keyblock = NULL; @@ -68,9 +70,9 @@ do_delete_key( const char *username, int secret, int *r_sec_avail ) exactmatch = (desc.mode == KEYDB_SEARCH_MODE_FPR || desc.mode == KEYDB_SEARCH_MODE_FPR16 || desc.mode == KEYDB_SEARCH_MODE_FPR20); - rc = desc.mode? keydb_search (hd, &desc, 1):GPG_ERR_INV_USER_ID; + rc = desc.mode? keydb_search (hd, &desc, 1):G10ERR_INV_USER_ID; if (rc) { - log_error (_("key `%s' not found: %s\n"), username, gpg_strerror (rc)); + log_error (_("key \"%s\" not found: %s\n"), username, g10_errstr (rc)); write_status_text( STATUS_DELETE_PROBLEM, "1" ); goto leave; } @@ -78,7 +80,7 @@ do_delete_key( const char *username, int secret, int *r_sec_avail ) /* read the keyblock */ rc = keydb_get_keyblock (hd, &keyblock ); if (rc) { - log_error (_("error reading keyblock: %s\n"), gpg_strerror (rc) ); + log_error (_("error reading keyblock: %s\n"), g10_errstr(rc) ); goto leave; } @@ -86,29 +88,36 @@ do_delete_key( const char *username, int secret, int *r_sec_avail ) node = find_kbnode( keyblock, secret? PKT_SECRET_KEY:PKT_PUBLIC_KEY ); if( !node ) { log_error("Oops; key not found anymore!\n"); - rc = GPG_ERR_GENERAL; + rc = G10ERR_GENERAL; goto leave; } - if( secret ) { + if( secret ) + { sk = node->pkt->pkt.secret_key; keyid_from_sk( sk, keyid ); - } - else { + } + else + { + /* public */ pk = node->pkt->pkt.public_key; keyid_from_pk( pk, keyid ); - rc = seckey_available( keyid ); - if( !rc ) { - *r_sec_avail = 1; - rc = -1; - goto leave; - } - else if( rc != GPG_ERR_NO_SECKEY ) { - log_error("%s: get secret key: %s\n", username, gpg_strerror (rc) ); - } - else - rc = 0; - } + + if(!force) + { + rc = seckey_available( keyid ); + if( !rc ) + { + *r_sec_avail = 1; + rc = -1; + goto leave; + } + else if( rc != G10ERR_NO_SECKEY ) + log_error("%s: get secret key: %s\n", username, g10_errstr(rc) ); + else + rc = 0; + } + } if( rc ) rc = 0; @@ -116,26 +125,26 @@ do_delete_key( const char *username, int secret, int *r_sec_avail ) okay++; else if( opt.batch && secret ) { - log_error(_("can't do that in batchmode\n")); + log_error(_("can't do this in batch mode\n")); log_info (_("(unless you specify the key by fingerprint)\n")); } else if( opt.batch && opt.answer_yes ) okay++; else if( opt.batch ) { - log_error(_("can't do that in batchmode without \"--yes\"\n")); + log_error(_("can't do this in batch mode without \"--yes\"\n")); log_info (_("(unless you specify the key by fingerprint)\n")); } else { if( secret ) print_seckey_info( sk ); else - print_pubkey_info (NULL, pk ); + print_pubkey_info(NULL, pk ); tty_printf( "\n" ); yes = cpr_get_answer_is_yes( secret? "delete_key.secret.okay" : "delete_key.okay", - _("Delete this key from the keyring? ")); + _("Delete this key from the keyring? (y/N) ")); if( !cpr_enabled() && secret && yes ) { /* I think it is not required to check a passphrase; if * the user is so stupid as to let others access his secret keyring @@ -143,7 +152,7 @@ do_delete_key( const char *username, int secret, int *r_sec_avail ) * basic texts about security. */ yes = cpr_get_answer_is_yes("delete_key.secret.okay", - _("This is a secret key! - really delete? ")); + _("This is a secret key! - really delete? (y/N) ")); } if( yes ) okay++; @@ -153,7 +162,7 @@ do_delete_key( const char *username, int secret, int *r_sec_avail ) if( okay ) { rc = keydb_delete_keyblock (hd); if (rc) { - log_error (_("deleting keyblock failed: %s\n"), gpg_strerror (rc) ); + log_error (_("deleting keyblock failed: %s\n"), g10_errstr(rc) ); goto leave; } @@ -179,15 +188,18 @@ do_delete_key( const char *username, int secret, int *r_sec_avail ) int delete_keys( STRLIST names, int secret, int allow_both ) { - int rc, avail; + int rc, avail, force=(!allow_both && !secret && opt.expert); + + /* Force allows us to delete a public key even if a secret key + exists. */ for(;names;names=names->next) { - rc = do_delete_key (names->d, secret, &avail ); + rc = do_delete_key (names->d, secret, force, &avail ); if ( rc && avail ) { if ( allow_both ) { - rc = do_delete_key (names->d, 1, &avail ); + rc = do_delete_key (names->d, 1, 0, &avail ); if ( !rc ) - rc = do_delete_key (names->d, 0, &avail ); + rc = do_delete_key (names->d, 0, 0, &avail ); } else { log_error(_( @@ -200,7 +212,7 @@ delete_keys( STRLIST names, int secret, int allow_both ) } if(rc) { - log_error("%s: delete key failed: %s\n", names->d, gpg_strerror (rc) ); + log_error("%s: delete key failed: %s\n", names->d, g10_errstr(rc) ); return rc; } } diff --git a/g10/encode.c b/g10/encode.c index 7794bdb7c..57f2272dd 100644 --- a/g10/encode.c +++ b/g10/encode.c @@ -1,6 +1,6 @@ /* encode.c - encode data - * Copyright (C) 1998, 1999, 2000, 2001, 2002, - * 2003 Free Software Foundation, Inc. + * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, + * 2005 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -16,7 +16,8 @@ * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. */ #include <config.h> @@ -32,7 +33,6 @@ #include "errors.h" #include "iobuf.h" #include "keydb.h" -#include "memory.h" #include "util.h" #include "main.h" #include "filter.h" @@ -42,10 +42,8 @@ #include "pkglue.h" -static int encode_simple( const char *filename, int mode, int compat ); -static int write_pubkey_enc_from_list( PK_LIST pk_list, DEK *dek, iobuf_t out ); - - +static int encode_simple( const char *filename, int mode, int use_seskey ); +static int write_pubkey_enc_from_list( PK_LIST pk_list, DEK *dek, IOBUF out ); /**************** * Encode FILENAME with only the symmetric cipher. Take input from @@ -54,17 +52,7 @@ static int write_pubkey_enc_from_list( PK_LIST pk_list, DEK *dek, iobuf_t out ); int encode_symmetric( const char *filename ) { - int compat = 1; - -#if 0 - /* We don't want to use it because older gnupg version can't - handle it and we can presume that a lot of scripts are running - with the expert mode set. Some time in the future we might - want to allow for it. */ - if ( opt.expert ) - compat = 0; /* PGP knows how to handle this mode. */ -#endif - return encode_simple( filename, 1, compat ); + return encode_simple( filename, 1, 0 ); } /**************** @@ -74,69 +62,62 @@ encode_symmetric( const char *filename ) int encode_store( const char *filename ) { - return encode_simple( filename, 0, 1 ); + return encode_simple( filename, 0, 0 ); } + static void -encode_sesskey (DEK * dek, DEK ** ret_dek, byte * enckey) +encode_seskey( DEK *dek, DEK **seskey, byte *enckey ) { - CIPHER_HANDLE hd; - DEK * c; - byte buf[33]; + gcry_cipher_hd_t hd; + byte buf[33]; - assert (dek->keylen < 32); - - c = xcalloc (1, sizeof *c); - c->keylen = dek->keylen; - c->algo = dek->algo; - make_session_key (c); - /*log_hexdump ("thekey", c->key, c->keylen);*/ - - /* the encrypted session key is prefixed with a one-octet algorithm id */ - buf[0] = c->algo; - memcpy (buf + 1, c->key, c->keylen); + assert ( dek->keylen <= 32 ); + if(!*seskey) + { + *seskey=xmalloc_clear(sizeof(DEK)); + (*seskey)->keylen=dek->keylen; + (*seskey)->algo=dek->algo; + make_session_key(*seskey); + /*log_hexdump( "thekey", c->key, c->keylen );*/ + } + + /* The encrypted session key is prefixed with a one-octet algorithm id. */ + buf[0] = (*seskey)->algo; + memcpy( buf + 1, (*seskey)->key, (*seskey)->keylen ); - /* due to the fact that we use only checked values, consider each - failure as fatal. */ - if (gcry_cipher_open (&hd, dek->algo, GCRY_CIPHER_MODE_CFB, 1)) - BUG(); - if (gcry_cipher_setkey (hd, dek->key, dek->keylen)) - BUG(); - gcry_cipher_setiv (hd, NULL, 0); - gcry_cipher_encrypt (hd, buf, c->keylen + 1, NULL, 0); - gcry_cipher_close (hd); - - memcpy (enckey, buf, c->keylen + 1); - wipememory (buf, sizeof buf); /* burn key */ - *ret_dek = c; + /* We only pass already checked values to the following fucntion, + thus we consider any failure as fatal. */ + if (gcry_cipher_open (&hd, dek->algo, GCRY_CIPHER_MODE_CFB, 1)) + BUG (); + if (gcry_cipher_setkey (hd, dek->key, dek->keylen)) + BUG (); + gry_cipher_setiv (hd, NULL, 0); + gcry_cipher_encrypt (hd, buf, (*seskey)->keylen + 1, NULL, 0); + gcry_cipher_close (hd); + + memcpy( enckey, buf, (*seskey)->keylen + 1 ); + wipememory( buf, sizeof buf ); /* burn key */ } /* We try very hard to use a MDC */ static int -use_mdc (PK_LIST pk_list,int algo) +use_mdc(PK_LIST pk_list,int algo) { - byte cipher_algid[4] = { - CIPHER_ALGO_AES, - CIPHER_ALGO_AES192, - CIPHER_ALGO_AES256, - CIPHER_ALGO_TWOFISH - }; - int i; - /* RFC-1991 and 2440 don't have MDC */ if(RFC1991 || RFC2440) return 0; - + /* --force-mdc overrides --disable-mdc */ - if (opt.force_mdc) + if(opt.force_mdc) return 1; - if (opt.disable_mdc) + if(opt.disable_mdc) return 0; /* Do the keys really support MDC? */ - if (select_mdc_from_pklist (pk_list)) + if(select_mdc_from_pklist(pk_list)) return 1; /* The keys don't support MDC, so now we do a bit of a hack - if any @@ -144,26 +125,40 @@ use_mdc (PK_LIST pk_list,int algo) can handle a MDC. This is valid for PGP 7, which can handle MDCs though it will not generate them. 2440bis allows this, by the way. */ - for (i=0; i < DIM (cipher_algid); i++) - { - if (select_algo_from_prefs (pk_list, PREFTYPE_SYM, cipher_algid[i], - NULL) == cipher_algid[i]) - return 1; - } + + if(select_algo_from_prefs(pk_list,PREFTYPE_SYM, + CIPHER_ALGO_AES,NULL)==CIPHER_ALGO_AES) + return 1; + + if(select_algo_from_prefs(pk_list,PREFTYPE_SYM, + CIPHER_ALGO_AES192,NULL)==CIPHER_ALGO_AES192) + return 1; + + if(select_algo_from_prefs(pk_list,PREFTYPE_SYM, + CIPHER_ALGO_AES256,NULL)==CIPHER_ALGO_AES256) + return 1; + + if(select_algo_from_prefs(pk_list,PREFTYPE_SYM, + CIPHER_ALGO_TWOFISH,NULL)==CIPHER_ALGO_TWOFISH) + return 1; /* Last try. Use MDC for the modern ciphers. */ + if (gcry_cipher_get_algo_blklen (algo) != 8) return 1; return 0; /* No MDC */ } +/* We don't want to use use_seskey yet because older gnupg versions + can't handle it, and there isn't really any point unless we're + making a message that can be decrypted by a public key or + passphrase. */ static int -encode_simple( const char *filename, int mode, int compat ) +encode_simple( const char *filename, int mode, int use_seskey ) { - iobuf_t inp, out; + IOBUF inp, out; PACKET pkt; - DEK *dek = NULL; PKT_plaintext *pt = NULL; STRING2KEY *s2k = NULL; byte enckey[33]; @@ -175,7 +170,7 @@ encode_simple( const char *filename, int mode, int compat ) compress_filter_context_t zfx; text_filter_context_t tfx; progress_filter_context_t pfx; - int do_compress = opt.compress && !RFC1991; + int do_compress = !RFC1991 && default_compress_algo(); memset( &cfx, 0, sizeof cfx); memset( &afx, 0, sizeof afx); @@ -184,10 +179,19 @@ encode_simple( const char *filename, int mode, int compat ) init_packet(&pkt); /* prepare iobufs */ - if( !(inp = iobuf_open(filename)) ) { + inp = iobuf_open(filename); + if (inp) + iobuf_ioctl (inp,3,1,NULL); /* disable fd caching */ + if (inp && is_secured_file (iobuf_get_fd (inp))) + { + iobuf_close (inp); + inp = NULL; + errno = EPERM; + } + if( !inp ) { rc = gpg_error_from_errno (errno); - log_error(_("%s: can't open: %s\n"), filename? filename: "[stdin]", - strerror(errno) ); + log_error(_("can't open `%s': %s\n"), filename? filename: "[stdin]" + strerror(errno) ); return rc; } @@ -199,42 +203,49 @@ encode_simple( const char *filename, int mode, int compat ) /* Due the the fact that we use don't use an IV to encrypt the session key we can't use the new mode with RFC1991 because it has no S2K salt. RFC1991 always uses simple S2K. */ - if ( RFC1991 && !compat ) - compat = 1; + if ( RFC1991 && use_seskey ) + use_seskey = 0; cfx.dek = NULL; if( mode ) { - s2k = xcalloc (1, sizeof *s2k ); + s2k = xmalloc_clear( sizeof *s2k ); s2k->mode = RFC1991? 0:opt.s2k_mode; - s2k->hash_algo = opt.s2k_digest_algo; + s2k->hash_algo=S2K_DIGEST_ALGO; cfx.dek = passphrase_to_dek( NULL, 0, default_cipher_algo(), s2k, 2, NULL, NULL); if( !cfx.dek || !cfx.dek->keylen ) { - rc = gpg_error (GPG_ERR_INV_PASSPHRASE); - xfree (cfx.dek); - xfree (s2k); + rc = gpg_error (GPG_ERR_INV_PASSPHRASE); + xfree(cfx.dek); + xfree(s2k); iobuf_close(inp); - log_error(_("error creating passphrase: %s\n"), gpg_strerror (rc) ); + log_error(_("error creating passphrase: %s\n"), gpg_strerror (rc)); return rc; } - if (!compat && s2k->mode != 1 && s2k->mode != 3) { - compat = 1; + if (use_seskey && s2k->mode != 1 && s2k->mode != 3) { + use_seskey = 0; log_info (_("can't use a symmetric ESK packet " "due to the S2K mode\n")); } - if ( !compat ) { - seskeylen = gcry_cipher_get_algo_keylen (default_cipher_algo()); - encode_sesskey( cfx.dek, &dek, enckey ); - xfree (cfx.dek); cfx.dek = dek; - } + if ( use_seskey ) + { + DEK *dek = NULL; + + seskeylen = gcry_cipher_get_algo_keylen (default_cipher_algo ()); + encode_seskey( cfx.dek, &dek, enckey ); + xfree( cfx.dek ); cfx.dek = dek; + } + + if(opt.verbose) + log_info(_("using cipher %s\n"), + gcry_cipher_algo_name (cfx.dek->algo)); cfx.dek->use_mdc=use_mdc(NULL,cfx.dek->algo); } - if (opt.compress == -1 && cfx.dek && cfx.dek->use_mdc && - is_file_compressed(filename, &rc)) + if (do_compress && cfx.dek && cfx.dek->use_mdc + && is_file_compressed(filename, &rc)) { if (opt.verbose) log_info(_("`%s' already compressed\n"), filename); @@ -243,52 +254,43 @@ encode_simple( const char *filename, int mode, int compat ) if( rc || (rc = open_outfile( filename, opt.armor? 1:0, &out )) ) { iobuf_cancel(inp); - xfree (cfx.dek); - xfree (s2k); + xfree(cfx.dek); + xfree(s2k); return rc; } if( opt.armor ) iobuf_push_filter( out, armor_filter, &afx ); -#ifdef ENABLE_COMMENT_PACKETS - else { - write_comment( out, "#created by GNUPG v" VERSION " (" - PRINTABLE_OS_NAME ")"); - if( opt.comment_string ) - write_comment( out, opt.comment_string ); - } -#endif + if( s2k && !RFC1991 ) { - PKT_symkey_enc *enc = xcalloc (1, sizeof *enc + seskeylen + 1 ); + PKT_symkey_enc *enc = xmalloc_clear( sizeof *enc + seskeylen + 1 ); enc->version = 4; enc->cipher_algo = cfx.dek->algo; enc->s2k = *s2k; - if ( !compat && seskeylen ) { + if ( use_seskey && seskeylen ) { enc->seskeylen = seskeylen + 1; /* algo id */ memcpy( enc->seskey, enckey, seskeylen + 1 ); } pkt.pkttype = PKT_SYMKEY_ENC; pkt.pkt.symkey_enc = enc; if( (rc = build_packet( out, &pkt )) ) - log_error("build symkey packet failed: %s\n", gpg_strerror (rc) ); - xfree (enc); + log_error("build symkey packet failed: %s\n", g10_errstr(rc) ); + xfree(enc); } if (!opt.no_literal) { /* setup the inner packet */ if( filename || opt.set_filename ) { - char *s = make_basename ( opt.set_filename ? opt.set_filename - : filename - /* for riscos? - .iobuf_get_real_fname( inp ) */ - ); - pt = xmalloc ( sizeof *pt + strlen(s) - 1 ); + char *s = make_basename( opt.set_filename ? opt.set_filename + : filename, + iobuf_get_real_fname( inp ) ); + pt = xmalloc( sizeof *pt + strlen(s) - 1 ); pt->namelen = strlen(s); memcpy(pt->name, s, pt->namelen ); - xfree (s); + xfree(s); } else { /* no filename */ - pt = xmalloc ( sizeof *pt - 1 ); + pt = xmalloc( sizeof *pt - 1 ); pt->namelen = 0; } } @@ -304,12 +306,14 @@ encode_simple( const char *filename, int mode, int compat ) either partial length or fixed length with the new style messages. */ - if (filename && *filename && !(*filename == '-' && !filename[1]) - && !opt.textmode ) { + if ( !iobuf_is_pipe_filename (filename) && *filename && !opt.textmode ) + { off_t tmpsize; + int overflow; - if ( !(tmpsize = iobuf_get_filelength(inp)) ) - log_info(_("%s: WARNING: empty file\n"), filename ); + if ( !(tmpsize = iobuf_get_filelength(inp, &overflow)) + && !overflow ) + log_info(_("WARNING: `%s' is an empty file\n"), filename ); /* We can't encode the length of very large files because OpenPGP uses only 32 bit for file sizes. So if the the size of a file is larger than 2^32 minus some bytes for @@ -318,9 +322,9 @@ encode_simple( const char *filename, int mode, int compat ) filesize = tmpsize; else filesize = 0; - } + } else - filesize = opt.set_filesize ? opt.set_filesize : 0; /* stdin */ + filesize = opt.set_filesize ? opt.set_filesize : 0; /* stdin */ if (!opt.no_literal) { pt->timestamp = make_timestamp(); @@ -347,14 +351,13 @@ encode_simple( const char *filename, int mode, int compat ) { if (cfx.dek && cfx.dek->use_mdc) zfx.new_ctb = 1; - zfx.algo=default_compress_algo(); - iobuf_push_filter( out, compress_filter, &zfx ); + push_compress_filter(out,&zfx,default_compress_algo()); } /* do the work */ if (!opt.no_literal) { if( (rc = build_packet( out, &pkt )) ) - log_error("build_packet failed: %s\n", gpg_strerror (rc) ); + log_error("build_packet failed: %s\n", g10_errstr(rc) ); } else { /* user requested not to create a literal packet, @@ -362,8 +365,9 @@ encode_simple( const char *filename, int mode, int compat ) byte copy_buffer[4096]; int bytes_copied; while ((bytes_copied = iobuf_read(inp, copy_buffer, 4096)) != -1) - if ( (rc=iobuf_write(out, copy_buffer, bytes_copied))) { - log_error("copying input to output failed: %s\n", gpg_strerror (rc) ); + if (iobuf_write(out, copy_buffer, bytes_copied) == -1) { + rc = G10ERR_WRITE_FILE; + log_error("copying input to output failed: %s\n", g10_errstr(rc) ); break; } wipememory(copy_buffer, 4096); /* burn buffer */ @@ -381,21 +385,70 @@ encode_simple( const char *filename, int mode, int compat ) if (pt) pt->buf = NULL; free_packet(&pkt); - xfree (cfx.dek); - xfree (s2k); + xfree(cfx.dek); + xfree(s2k); return rc; } +int +setup_symkey(STRING2KEY **symkey_s2k,DEK **symkey_dek) +{ + *symkey_s2k=xmalloc_clear(sizeof(STRING2KEY)); + (*symkey_s2k)->mode = opt.s2k_mode; + (*symkey_s2k)->hash_algo = S2K_DIGEST_ALGO; + + *symkey_dek=passphrase_to_dek(NULL,0,opt.s2k_cipher_algo, + *symkey_s2k,2,NULL,NULL); + if(!*symkey_dek || !(*symkey_dek)->keylen) + { + xfree(*symkey_dek); + xfree(*symkey_s2k); + return G10ERR_PASSPHRASE; + } + + return 0; +} + +static int +write_symkey_enc(STRING2KEY *symkey_s2k,DEK *symkey_dek,DEK *dek,IOBUF out) +{ + int rc,seskeylen=cipher_get_keylen(dek->algo)/8; + + PKT_symkey_enc *enc; + byte enckey[33]; + PACKET pkt; + + enc=xmalloc_clear(sizeof(PKT_symkey_enc)+seskeylen+1); + encode_seskey(symkey_dek,&dek,enckey); + + enc->version = 4; + enc->cipher_algo = opt.s2k_cipher_algo; + enc->s2k = *symkey_s2k; + enc->seskeylen = seskeylen + 1; /* algo id */ + memcpy( enc->seskey, enckey, seskeylen + 1 ); + + pkt.pkttype = PKT_SYMKEY_ENC; + pkt.pkt.symkey_enc = enc; + + if((rc=build_packet(out,&pkt))) + log_error("build symkey_enc packet failed: %s\n",g10_errstr(rc)); + + xfree(enc); + return rc; +} + /**************** * Encrypt the file with the given userids (or ask if none * is supplied). */ int -encode_crypt( const char *filename, STRLIST remusr ) +encode_crypt( const char *filename, STRLIST remusr, int use_symkey ) { - iobuf_t inp = NULL, out = NULL; + IOBUF inp = NULL, out = NULL; PACKET pkt; PKT_plaintext *pt = NULL; + DEK *symkey_dek = NULL; + STRING2KEY *symkey_s2k = NULL; int rc = 0, rc2 = 0; u32 filesize; cipher_filter_context_t cfx; @@ -404,8 +457,7 @@ encode_crypt( const char *filename, STRLIST remusr ) text_filter_context_t tfx; progress_filter_context_t pfx; PK_LIST pk_list,work_list; - int do_compress = opt.compress && !RFC1991; - + int do_compress = opt.compress_algo && !RFC1991; memset( &cfx, 0, sizeof cfx); memset( &afx, 0, sizeof afx); @@ -413,6 +465,10 @@ encode_crypt( const char *filename, STRLIST remusr ) memset( &tfx, 0, sizeof tfx); init_packet(&pkt); + if(use_symkey + && (rc=setup_symkey(&symkey_s2k,&symkey_dek))) + return rc; + if( (rc=build_pk_list( remusr, &pk_list, PUBKEY_USAGE_ENC)) ) return rc; @@ -429,10 +485,20 @@ encode_crypt( const char *filename, STRLIST remusr ) } /* prepare iobufs */ - if( !(inp = iobuf_open(filename)) ) { + inp = iobuf_open(filename); + if (inp) + iobuf_ioctl (inp,3,1,NULL); /* disable fd caching */ + if (inp && is_secured_file (iobuf_get_fd (inp))) + { + iobuf_close (inp); + inp = NULL; + errno = EPERM; + } + if( !inp ) { rc = gpg_error_from_errno (errno); - log_error(_("can't open %s: %s\n"), filename? filename: "[stdin]", - strerror(errno) ); + log_error(_("can't open `%s': %s\n"), + filename? filename: "[stdin]", + gpg_strerror (rc) ); goto leave; } else if( opt.verbose ) @@ -446,19 +512,11 @@ encode_crypt( const char *filename, STRLIST remusr ) if( (rc = open_outfile( filename, opt.armor? 1:0, &out )) ) goto leave; - if( opt.armor ) iobuf_push_filter( out, armor_filter, &afx ); -#ifdef ENABLE_COMMENT_PACKETS - else { - write_comment( out, "#created by GNUPG v" VERSION " (" - PRINTABLE_OS_NAME ")"); - if( opt.comment_string ) - write_comment( out, opt.comment_string ); - } -#endif + /* create a session key */ - cfx.dek = xcalloc_secure (1, sizeof *cfx.dek); + cfx.dek = xmalloc_secure_clear (sizeof *cfx.dek); if( !opt.def_cipher_algo ) { /* try to get it from the prefs */ cfx.dek->algo = select_algo_from_prefs(pk_list,PREFTYPE_SYM,-1,NULL); /* The only way select_algo_from_prefs can fail here is when @@ -482,8 +540,8 @@ encode_crypt( const char *filename, STRLIST remusr ) if(!opt.expert && select_algo_from_prefs(pk_list,PREFTYPE_SYM, opt.def_cipher_algo,NULL)!=opt.def_cipher_algo) - log_info(_("forcing symmetric cipher %s (%d) " - "violates recipient preferences\n"), + log_info(_("WARNING: forcing symmetric cipher %s (%d)" + " violates recipient preferences\n"), gcry_cipher_algo_name (opt.def_cipher_algo), opt.def_cipher_algo); @@ -497,8 +555,7 @@ encode_crypt( const char *filename, STRLIST remusr ) not have a MDC to give some protection against chosen ciphertext attacks. */ - if (opt.compress == -1 && cfx.dek->use_mdc && - is_file_compressed(filename, &rc2) ) + if (do_compress && cfx.dek->use_mdc && is_file_compressed(filename, &rc2) ) { if (opt.verbose) log_info(_("`%s' already compressed\n"), filename); @@ -518,40 +575,49 @@ encode_crypt( const char *filename, STRLIST remusr ) if( rc ) goto leave; + /* We put the passphrase (if any) after any public keys as this + seems to be the most useful on the recipient side - there is no + point in prompting a user for a passphrase if they have the + secret key needed to decrypt. */ + if(use_symkey && (rc=write_symkey_enc(symkey_s2k,symkey_dek,cfx.dek,out))) + goto leave; + if (!opt.no_literal) { /* setup the inner packet */ if( filename || opt.set_filename ) { char *s = make_basename( opt.set_filename ? opt.set_filename - : filename - /* ,iobuf_get_real_fname( inp )*/ ); - pt = xmalloc ( sizeof *pt + strlen(s) - 1 ); + : filename, + iobuf_get_real_fname( inp ) ); + pt = xmalloc( sizeof *pt + strlen(s) - 1 ); pt->namelen = strlen(s); memcpy(pt->name, s, pt->namelen ); - xfree (s); + xfree(s); } else { /* no filename */ - pt = xmalloc ( sizeof *pt - 1 ); + pt = xmalloc( sizeof *pt - 1 ); pt->namelen = 0; } } - if (filename && *filename && !(*filename == '-' && !filename[1]) - && !opt.textmode ) { + if (!iobuf_is_pipe_filename (filename) && *filename && !opt.textmode ) + { off_t tmpsize; + int overflow; - if ( !(tmpsize = iobuf_get_filelength(inp)) ) - log_info(_("%s: WARNING: empty file\n"), filename ); + if ( !(tmpsize = iobuf_get_filelength(inp, &overflow)) + && !overflow ) + log_info(_("WARNING: `%s' is an empty file\n"), filename ); /* We can't encode the length of very large files because OpenPGP uses only 32 bit for file sizes. So if the the size of a file is larger than 2^32 minus some bytes for packet headers, we switch to partial length encoding. */ - if ( tmpsize < (IOBUF_FILELENGTH_LIMIT - 65536) ) + if (tmpsize < (IOBUF_FILELENGTH_LIMIT - 65536) ) filesize = tmpsize; else filesize = 0; - } + } else - filesize = opt.set_filesize ? opt.set_filesize : 0; /* stdin */ + filesize = opt.set_filesize ? opt.set_filesize : 0; /* stdin */ if (!opt.no_literal) { pt->timestamp = make_timestamp(); @@ -571,7 +637,7 @@ encode_crypt( const char *filename, STRLIST remusr ) /* register the compress filter */ if( do_compress ) { - int compr_algo = opt.def_compress_algo; + int compr_algo = opt.compress_algo; if(compr_algo==-1) { @@ -584,8 +650,8 @@ encode_crypt( const char *filename, STRLIST remusr ) else if(!opt.expert && select_algo_from_prefs(pk_list,PREFTYPE_ZIP, compr_algo,NULL)!=compr_algo) - log_info(_("forcing compression algorithm %s (%d) " - "violates recipient preferences\n"), + log_info(_("WARNING: forcing compression algorithm %s (%d)" + " violates recipient preferences\n"), compress_algo_to_string(compr_algo),compr_algo); /* algo 0 means no compression */ @@ -593,15 +659,14 @@ encode_crypt( const char *filename, STRLIST remusr ) { if (cfx.dek && cfx.dek->use_mdc) zfx.new_ctb = 1; - zfx.algo = compr_algo; - iobuf_push_filter( out, compress_filter, &zfx ); + push_compress_filter(out,&zfx,compr_algo); } } /* do the work */ if (!opt.no_literal) { if( (rc = build_packet( out, &pkt )) ) - log_error("build_packet failed: %s\n", gpg_strerror (rc) ); + log_error("build_packet failed: %s\n", g10_errstr(rc) ); } else { /* user requested not to create a literal packet, so we copy @@ -609,9 +674,10 @@ encode_crypt( const char *filename, STRLIST remusr ) byte copy_buffer[4096]; int bytes_copied; while ((bytes_copied = iobuf_read(inp, copy_buffer, 4096)) != -1) - if ((rc=iobuf_write(out, copy_buffer, bytes_copied))) { + if (iobuf_write(out, copy_buffer, bytes_copied) == -1) { + rc = G10ERR_WRITE_FILE; log_error("copying input to output failed: %s\n", - gpg_strerror (rc) ); + g10_errstr(rc) ); break; } wipememory(copy_buffer, 4096); /* burn buffer */ @@ -629,7 +695,9 @@ encode_crypt( const char *filename, STRLIST remusr ) if( pt ) pt->buf = NULL; free_packet(&pkt); - xfree (cfx.dek); + xfree(cfx.dek); + xfree(symkey_dek); + xfree(symkey_s2k); release_pk_list( pk_list ); return rc; } @@ -642,7 +710,7 @@ encode_crypt( const char *filename, STRLIST remusr ) */ int encrypt_filter( void *opaque, int control, - iobuf_t a, byte *buf, size_t *ret_len) + IOBUF a, byte *buf, size_t *ret_len) { size_t size = *ret_len; encrypt_filter_context_t *efx = opaque; @@ -653,7 +721,7 @@ encrypt_filter( void *opaque, int control, } else if( control == IOBUFCTRL_FLUSH ) { /* encrypt */ if( !efx->header_okay ) { - efx->cfx.dek = xcalloc_secure (1, sizeof *efx->cfx.dek ); + efx->cfx.dek = xmalloc_secure_clear( sizeof *efx->cfx.dek ); if( !opt.def_cipher_algo ) { /* try to get it from the prefs */ efx->cfx.dek->algo = @@ -688,6 +756,14 @@ encrypt_filter( void *opaque, int control, if( rc ) return rc; + if(efx->symkey_s2k && efx->symkey_dek) + { + rc=write_symkey_enc(efx->symkey_s2k,efx->symkey_dek, + efx->cfx.dek,a); + if(rc) + return rc; + } + iobuf_push_filter( a, cipher_filter, &efx->cfx ); efx->header_okay = 1; @@ -695,8 +771,11 @@ encrypt_filter( void *opaque, int control, rc = iobuf_write( a, buf, size ); } - else if( control == IOBUFCTRL_FREE ) { - } + else if( control == IOBUFCTRL_FREE ) + { + xfree(efx->symkey_dek); + xfree(efx->symkey_s2k); + } else if( control == IOBUFCTRL_DESC ) { *(char**)buf = "encrypt_filter"; } @@ -708,7 +787,7 @@ encrypt_filter( void *opaque, int control, * Write pubkey-enc packets from the list of PKs to OUT. */ static int -write_pubkey_enc_from_list( PK_LIST pk_list, DEK *dek, iobuf_t out ) +write_pubkey_enc_from_list( PK_LIST pk_list, DEK *dek, IOBUF out ) { PACKET pkt; PKT_public_key *pk; @@ -716,12 +795,12 @@ write_pubkey_enc_from_list( PK_LIST pk_list, DEK *dek, iobuf_t out ) int rc; for( ; pk_list; pk_list = pk_list->next ) { - gcry_mpi_t frame; + MPI frame; pk = pk_list->pk; print_pubkey_algo_note( pk->pubkey_algo ); - enc = xcalloc (1, sizeof *enc ); + enc = xmalloc_clear( sizeof *enc ); enc->pubkey_algo = pk->pubkey_algo; keyid_from_pk( pk, enc->keyid ); enc->throw_keyid = (opt.throw_keyid || (pk_list->flags&1)); @@ -742,23 +821,24 @@ write_pubkey_enc_from_list( PK_LIST pk_list, DEK *dek, iobuf_t out ) * algorithm number PK->PUBKEY_ALGO and pass it to pubkey_encrypt * which returns the encrypted value in the array ENC->DATA. * This array has a size which depends on the used algorithm - * (e.g. 2 for ElGamal). We don't need frame anymore because we + * (e.g. 2 for Elgamal). We don't need frame anymore because we * have everything now in enc->data which is the passed to * build_packet() */ - frame = encode_session_key( dek, pubkey_nbits( pk->pubkey_algo, - pk->pkey ) ); - rc = pk_encrypt( pk->pubkey_algo, enc->data, frame, pk->pkey ); - gcry_mpi_release ( frame ); + frame = encode_session_key (dek, pubkey_nbits (pk->pubkey_algo, + pk->pkey) ); + rc = pk_encrypt (pk->pubkey_algo, enc->data, frame, pk->pkey); + gcry_mpi_release (frame); if( rc ) - log_error("pubkey_encrypt failed: %s\n", gpg_strerror (rc) ); + log_error ("pubkey_encrypt failed: %s\n", gpg_strerror (rc) ); else { if( opt.verbose ) { - char *ustr = get_user_id_string_printable (enc->keyid); + char *ustr = get_user_id_string_native (enc->keyid); log_info(_("%s/%s encrypted for: \"%s\"\n"), - gcry_pk_algo_name (enc->pubkey_algo), - gcry_cipher_algo_name (dek->algo), ustr ); - xfree (ustr); + gcry_pk_algo_name (enc->pubkey_algo), + gcry_cipher_algo_name (dek->algo), + ustr ); + xfree(ustr); } /* and write it */ init_packet(&pkt); @@ -766,7 +846,7 @@ write_pubkey_enc_from_list( PK_LIST pk_list, DEK *dek, iobuf_t out ) pkt.pkt.pubkey_enc = enc; rc = build_packet( out, &pkt ); if( rc ) - log_error("build_packet(pubkey_enc) failed: %s\n", gpg_strerror (rc)); + log_error("build_packet(pubkey_enc) failed: %s\n", g10_errstr(rc)); } free_pubkey_enc(enc); if( rc ) @@ -800,9 +880,9 @@ encode_crypt_files(int nfiles, char **files, STRLIST remusr) } line[strlen(line)-1] = '\0'; print_file_status(STATUS_FILE_START, line, 2); - if ( (rc = encode_crypt(line, remusr)) ) - log_error("%s: encryption failed: %s\n", - print_fname_stdin(line), gpg_strerror (rc) ); + if ( (rc = encode_crypt(line, remusr, 0)) ) + log_error("encryption of `%s' failed: %s\n", + print_fname_stdin(line), g10_errstr(rc) ); write_status( STATUS_FILE_DONE ); } } @@ -811,9 +891,9 @@ encode_crypt_files(int nfiles, char **files, STRLIST remusr) while (nfiles--) { print_file_status(STATUS_FILE_START, *files, 2); - if ( (rc = encode_crypt(*files, remusr)) ) - log_error("%s: encryption failed: %s\n", - print_fname_stdin(*files), gpg_strerror (rc) ); + if ( (rc = encode_crypt(*files, remusr, 0)) ) + log_error("encryption of `%s' failed: %s\n", + print_fname_stdin(*files), g10_errstr(rc) ); write_status( STATUS_FILE_DONE ); files++; } diff --git a/g10/encr-data.c b/g10/encr-data.c index 074408404..cf2e43da7 100644 --- a/g10/encr-data.c +++ b/g10/encr-data.c @@ -1,5 +1,6 @@ /* encr-data.c - process an encrypted data packet - * Copyright (C) 1998, 1999, 2000, 2001, 2003 Free Software Foundation, Inc. + * Copyright (C) 1998, 1999, 2000, 2001, 2005, + * 2006 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -15,7 +16,8 @@ * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. */ #include <config.h> @@ -26,25 +28,24 @@ #include "gpg.h" #include "util.h" -#include "memory.h" #include "packet.h" -#include "mpi.h" #include "cipher.h" #include "options.h" #include "i18n.h" -static int mdc_decode_filter( void *opaque, int control, iobuf_t a, +static int mdc_decode_filter( void *opaque, int control, IOBUF a, byte *buf, size_t *ret_len); -static int decode_filter( void *opaque, int control, iobuf_t a, +static int decode_filter( void *opaque, int control, IOBUF a, byte *buf, size_t *ret_len); -typedef struct { - CIPHER_HANDLE cipher_hd; - MD_HANDLE mdc_hash; - char defer[20]; - int defer_filled; - int eof_seen; +typedef struct +{ + gcry_cipher_hd_t cipher_hd; + gcry_md_hd_t mdc_hash; + char defer[20]; + int defer_filled; + int eof_seen; } decode_filter_ctx_t; @@ -70,7 +71,8 @@ decrypt_data( void *procctx, PKT_encrypted *ed, DEK *dek ) log_info(_("encrypted with unknown algorithm %d\n"), dek->algo ); dek->algo_info_printed = 1; } - if( (rc=openpgp_cipher_test_algo(dek->algo)) ) + rc = openpgp_cipher_test_algo (dek->algo); + if (rc) goto leave; blocksize = gcry_cipher_get_algo_blklen (dek->algo); if( !blocksize || blocksize > 16 ) @@ -80,31 +82,39 @@ decrypt_data( void *procctx, PKT_encrypted *ed, DEK *dek ) BUG(); if( ed->mdc_method ) { - gcry_md_open (&dfx.mdc_hash, ed->mdc_method, 0 ); + if (gcry_md_open (&dfx.mdc_hash, ed->mdc_method, 0 )) + BUG (); if ( DBG_HASHING ) gcry_md_start_debug (dfx.mdc_hash, "checkmdc"); } + rc = gcry_cipher_open (&dfx.cipher_hd, dek->algo, GCRY_CIPHER_MODE_CFB, - GCRY_CIPHER_SECURE - | ((ed->mdc_method || dek->algo >= 100)? - 0 : GCRY_CIPHER_ENABLE_SYNC) ); - if (rc) - { - /* we should never get an error here cause we already - * checked, that the algorithm is available. What about a - * flag to let the function die in this case? */ - BUG(); - } + (GCRY_CIPHER_SECURE + | ((ed->mdc_method || dek->algo >= 100)? + 0 : GCRY_CIPHER_ENABLE_SYNC))); + if (rc) + { + /* We should never get an error here cause we already checked + * that the algorithm is available. */ + BUG(); + } + + /* log_hexdump( "thekey", dek->key, dek->keylen );*/ rc = gcry_cipher_setkey (dfx.cipher_hd, dek->key, dek->keylen); - if( gpg_err_code (rc) == GPG_ERR_WEAK_KEY ) - log_info(_("WARNING: message was encrypted with " - "a weak key in the symmetric cipher.\n")); - else if( rc ) { - log_error("key setup failed: %s\n", gpg_strerror (rc) ); + if ( gpg_err_code (rc) == GPG_ERR_WEAK_KEY ) + { + log_info(_("WARNING: message was encrypted with" + " a weak key in the symmetric cipher.\n")); + rc=0; + } + else if( rc ) + { + log_error("key setup failed: %s\n", g10_errstr(rc) ); goto leave; - } + + } if (!ed->buf) { log_error(_("problem handling encrypted packet\n")); goto leave; @@ -112,7 +122,7 @@ decrypt_data( void *procctx, PKT_encrypted *ed, DEK *dek ) gcry_cipher_setiv (dfx.cipher_hd, NULL, 0); - if (ed->len) { + if( ed->len ) { for(i=0; i < (nprefix+2) && ed->len; i++, ed->len-- ) { if( (c=iobuf_get(ed->buf)) == -1 ) break; @@ -127,17 +137,20 @@ decrypt_data( void *procctx, PKT_encrypted *ed, DEK *dek ) else temp[i] = c; } - gcry_cipher_decrypt( dfx.cipher_hd, temp, nprefix+2, NULL, 0); - gcry_cipher_sync( dfx.cipher_hd ); + + gcry_cipher_decrypt (dfx.cipher_hd, temp, nprefix+2, NULL, 0); + gcry_cipher_sync (dfx.cipher_hd); p = temp; /* log_hexdump( "prefix", temp, nprefix+2 ); */ - if( p[nprefix-2] != p[nprefix] || p[nprefix-1] != p[nprefix+1] ) { + if(dek->symmetric + && (p[nprefix-2] != p[nprefix] || p[nprefix-1] != p[nprefix+1]) ) + { rc = GPG_ERR_BAD_KEY; goto leave; - } + } if( dfx.mdc_hash ) - gcry_md_write( dfx.mdc_hash, temp, nprefix+2 ); + gcry_md_write (dfx.mdc_hash, temp, nprefix+2); if( ed->mdc_method ) iobuf_push_filter( ed->buf, mdc_decode_filter, &dfx ); @@ -152,18 +165,18 @@ decrypt_data( void *procctx, PKT_encrypted *ed, DEK *dek ) int datalen = gcry_md_get_algo_dlen (ed->mdc_method); gcry_cipher_decrypt (dfx.cipher_hd, dfx.defer, 20, NULL, 0); - gcry_md_final ( dfx.mdc_hash ); - if( datalen != 20 - || memcmp(gcry_md_read ( dfx.mdc_hash, 0 ), dfx.defer, datalen) ) - rc = gpg_error (GPG_ERR_BAD_SIGNATURE); - /*log_hexdump("MDC calculated:", gcry_md_read ( dfx.mdc_hash, 0), datalen);*/ + gcry_md_final (dfx.mdc_hash); + if (datalen != 20 + || memcmp (gcry_md_read( dfx.mdc_hash, 0 ), dfx.defer, datalen) ) + rc = gpg_error (GPG_ERR_BAD_SIGNATURE); + /*log_hexdump("MDC calculated:", md_read( dfx.mdc_hash, 0), datalen);*/ /*log_hexdump("MDC message :", dfx.defer, 20);*/ } leave: - gcry_cipher_close(dfx.cipher_hd); - gcry_md_close ( dfx.mdc_hash ); + gcry_cipher_close (dfx.cipher_hd); + gcry_md_close (dfx.mdc_hash); return rc; } @@ -171,7 +184,7 @@ decrypt_data( void *procctx, PKT_encrypted *ed, DEK *dek ) /* I think we should merge this with cipher_filter */ static int -mdc_decode_filter( void *opaque, int control, iobuf_t a, +mdc_decode_filter( void *opaque, int control, IOBUF a, byte *buf, size_t *ret_len) { decode_filter_ctx_t *dfx = opaque; @@ -229,8 +242,8 @@ mdc_decode_filter( void *opaque, int control, iobuf_t a, } if( n ) { - gcry_cipher_decrypt( dfx->cipher_hd, buf, n, NULL, 0); - gcry_md_write( dfx->mdc_hash, buf, n ); + gcry_cipher_decrypt (dfx->cipher_hd, buf, n, NULL, 0); + gcry_md_write (dfx->mdc_hash, buf, n); } else { assert( dfx->eof_seen ); @@ -245,7 +258,7 @@ mdc_decode_filter( void *opaque, int control, iobuf_t a, } static int -decode_filter( void *opaque, int control, iobuf_t a, byte *buf, size_t *ret_len) +decode_filter( void *opaque, int control, IOBUF a, byte *buf, size_t *ret_len) { decode_filter_ctx_t *fc = opaque; size_t n, size = *ret_len; @@ -256,7 +269,7 @@ decode_filter( void *opaque, int control, iobuf_t a, byte *buf, size_t *ret_len) n = iobuf_read( a, buf, size ); if( n == -1 ) n = 0; if( n ) - gcry_cipher_decrypt( fc->cipher_hd, buf, n, NULL, 0); + gcry_cipher_decrypt (fc->cipher_hd, buf, n, NULL, 0); else rc = -1; /* eof */ *ret_len = n; diff --git a/g10/exec.c b/g10/exec.c index b1fc2c70f..839964b1d 100644 --- a/g10/exec.c +++ b/g10/exec.c @@ -1,5 +1,5 @@ /* exec.c - generic call-a-program code - * Copyright (C) 2001, 2002, 2003 Free Software Foundation, Inc. + * Copyright (C) 2001, 2002, 2003, 2004, 2005 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -15,7 +15,8 @@ * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. */ #include <config.h> @@ -34,12 +35,13 @@ #include <unistd.h> #include <string.h> #include <errno.h> + +#include "gpg.h" #include "options.h" -#include "memory.h" #include "i18n.h" #include "iobuf.h" #include "util.h" -#include "mkdtemp.h" +#include "mkdtemp.h" /* From gnulib. */ #include "exec.h" #ifdef NO_EXEC @@ -47,12 +49,12 @@ int exec_write(struct exec_info **info,const char *program, const char *args_in,const char *name,int writeonly,int binary) { log_error(_("no remote program execution supported\n")); - return GPG_ERR_GENERAL; + return G10ERR_GENERAL; } -int exec_read(struct exec_info *info) { return GPG_ERR_GENERAL; } -int exec_finish(struct exec_info *info) { return GPG_ERR_GENERAL; } -int set_exec_path(const char *path,int method) { return GPG_ERR_GENERAL; } +int exec_read(struct exec_info *info) { return G10ERR_GENERAL; } +int exec_finish(struct exec_info *info) { return G10ERR_GENERAL; } +int set_exec_path(const char *path) { return G10ERR_GENERAL; } #else /* ! NO_EXEC */ @@ -60,7 +62,7 @@ int set_exec_path(const char *path,int method) { return GPG_ERR_GENERAL; } /* This is a nicer system() for windows that waits for programs to return before returning control to the caller. I hate helpful computers. */ -static int win_system(const char *command) +static int w32_system(const char *command) { PROCESS_INFORMATION pi; STARTUPINFO si; @@ -68,7 +70,7 @@ static int win_system(const char *command) /* We must use a copy of the command as CreateProcess modifies this argument. */ - string=xstrdup (command); + string=xstrdup(command); memset(&pi,0,sizeof(pi)); memset(&si,0,sizeof(si)); @@ -82,42 +84,30 @@ static int win_system(const char *command) CloseHandle(pi.hProcess); CloseHandle(pi.hThread); - xfree (string); + xfree(string); return 0; } #endif -/* method==0 to replace current $PATH, and 1 to append to current - $PATH. */ -int set_exec_path(const char *path,int method) +/* Replaces current $PATH */ +int set_exec_path(const char *path) { - char *p,*curpath=NULL; - size_t curlen=0; - - if(method==1 && (curpath=getenv("PATH"))) - curlen=strlen(curpath)+1; + char *p; - p=xmalloc (5+curlen+strlen(path)+1); + p=xmalloc(5+strlen(path)+1); strcpy(p,"PATH="); - - if(curpath) - { - strcat(p,curpath); - strcat(p,":"); - } - strcat(p,path); if(DBG_EXTPROG) - log_debug("set_exec_path method %d: %s\n",method,p); + log_debug("set_exec_path: %s\n",p); /* Notice that path is never freed. That is intentional due to the way putenv() works. This leaks a few bytes if we call set_exec_path multiple times. */ if(putenv(p)!=0) - return GPG_ERR_GENERAL; + return G10ERR_GENERAL; else return 0; } @@ -128,16 +118,16 @@ static int make_tempdir(struct exec_info *info) char *tmp=opt.temp_dir,*namein=info->name,*nameout; if(!namein) - namein=info->binary?"tempin" EXTSEP_S "bin":"tempin" EXTSEP_S "txt"; + namein=info->flags.binary?"tempin" EXTSEP_S "bin":"tempin" EXTSEP_S "txt"; - nameout=info->binary?"tempout" EXTSEP_S "bin":"tempout" EXTSEP_S "txt"; + nameout=info->flags.binary?"tempout" EXTSEP_S "bin":"tempout" EXTSEP_S "txt"; /* Make up the temp dir and files in case we need them */ if(tmp==NULL) { #if defined (_WIN32) - tmp=xmalloc (256); + tmp=xmalloc(256); if(GetTempPath(256,tmp)==0) strcpy(tmp,"c:\\windows\\temp"); else @@ -169,12 +159,12 @@ static int make_tempdir(struct exec_info *info) #endif } - info->tempdir=xmalloc (strlen(tmp)+strlen(DIRSEP_S)+10+1); + info->tempdir=xmalloc(strlen(tmp)+strlen(DIRSEP_S)+10+1); sprintf(info->tempdir,"%s" DIRSEP_S "gpg-XXXXXX",tmp); #if defined (_WIN32) - xfree (tmp); + xfree(tmp); #endif if(mkdtemp(info->tempdir)==NULL) @@ -182,21 +172,21 @@ static int make_tempdir(struct exec_info *info) info->tempdir,strerror(errno)); else { - info->madedir=1; + info->flags.madedir=1; - info->tempfile_in=xmalloc (strlen(info->tempdir)+ + info->tempfile_in=xmalloc(strlen(info->tempdir)+ strlen(DIRSEP_S)+strlen(namein)+1); sprintf(info->tempfile_in,"%s" DIRSEP_S "%s",info->tempdir,namein); - if(!info->writeonly) + if(!info->flags.writeonly) { - info->tempfile_out=xmalloc (strlen(info->tempdir)+ + info->tempfile_out=xmalloc(strlen(info->tempdir)+ strlen(DIRSEP_S)+strlen(nameout)+1); sprintf(info->tempfile_out,"%s" DIRSEP_S "%s",info->tempdir,nameout); } } - return info->madedir?0:GPG_ERR_GENERAL; + return info->flags.madedir?0:G10ERR_GENERAL; } /* Expands %i and %o in the args to the full temp files within the @@ -206,14 +196,14 @@ static int expand_args(struct exec_info *info,const char *args_in) const char *ch=args_in; unsigned int size,len; - info->use_temp_files=0; - info->keep_temp_files=0; + info->flags.use_temp_files=0; + info->flags.keep_temp_files=0; if(DBG_EXTPROG) log_debug("expanding string \"%s\"\n",args_in); size=100; - info->command=xmalloc (size); + info->command=xmalloc(size); len=0; info->command[0]='\0'; @@ -228,31 +218,31 @@ static int expand_args(struct exec_info *info,const char *args_in) switch(*ch) { case 'O': - info->keep_temp_files=1; + info->flags.keep_temp_files=1; /* fall through */ case 'o': /* out */ - if(!info->madedir) + if(!info->flags.madedir) { if(make_tempdir(info)) goto fail; } append=info->tempfile_out; - info->use_temp_files=1; + info->flags.use_temp_files=1; break; case 'I': - info->keep_temp_files=1; + info->flags.keep_temp_files=1; /* fall through */ case 'i': /* in */ - if(!info->madedir) + if(!info->flags.madedir) { if(make_tempdir(info)) goto fail; } append=info->tempfile_in; - info->use_temp_files=1; + info->flags.use_temp_files=1; break; case '%': @@ -293,17 +283,17 @@ static int expand_args(struct exec_info *info,const char *args_in) } if(DBG_EXTPROG) - log_debug("args expanded to \"%s\", use %d, keep %d\n", - info->command,info->use_temp_files,info->keep_temp_files); + log_debug("args expanded to \"%s\", use %u, keep %u\n",info->command, + info->flags.use_temp_files,info->flags.keep_temp_files); return 0; fail: - xfree (info->command); + xfree(info->command); info->command=NULL; - return GPG_ERR_GENERAL; + return G10ERR_GENERAL; } /* Either handles the tempfile creation, or the fork/exec. If it @@ -315,7 +305,7 @@ static int expand_args(struct exec_info *info,const char *args_in) int exec_write(struct exec_info **info,const char *program, const char *args_in,const char *name,int writeonly,int binary) { - int ret=GPG_ERR_GENERAL; + int ret=G10ERR_GENERAL; if(opt.exec_disable && !opt.no_perm_warn) { @@ -335,22 +325,22 @@ int exec_write(struct exec_info **info,const char *program, if(program==NULL && args_in==NULL) BUG(); - *info=xcalloc (1,sizeof(struct exec_info)); + *info=xmalloc_clear(sizeof(struct exec_info)); if(name) - (*info)->name=xstrdup (name); - (*info)->binary=binary; - (*info)->writeonly=writeonly; + (*info)->name=xstrdup(name); + (*info)->flags.binary=binary; + (*info)->flags.writeonly=writeonly; /* Expand the args, if any */ if(args_in && expand_args(*info,args_in)) goto fail; #ifdef EXEC_TEMPFILE_ONLY - if(!(*info)->use_temp_files) + if(!(*info)->flags.use_temp_files) { - log_error(_("this platform requires temp files when calling external " - "programs\n")); + log_error(_("this platform requires temporary files when calling" + " external programs\n")); goto fail; } @@ -358,7 +348,7 @@ int exec_write(struct exec_info **info,const char *program, /* If there are no args, or there are args, but no temp files, we can use fork/exec/pipe */ - if(args_in==NULL || (*info)->use_temp_files==0) + if(args_in==NULL || (*info)->flags.use_temp_files==0) { int to[2],from[2]; @@ -392,7 +382,7 @@ int exec_write(struct exec_info **info,const char *program, /* If the program isn't going to respond back, they get to keep their stdout/stderr */ - if(!(*info)->writeonly) + if(!(*info)->flags.writeonly) { /* implied close of STDERR */ if(dup2(STDOUT_FILENO,STDERR_FILENO)==-1) @@ -426,10 +416,12 @@ int exec_write(struct exec_info **info,const char *program, /* If we get this far the exec failed. Clean up and return. */ - log_error(_("unable to execute %s \"%s\": %s\n"), - args_in==NULL?"program":"shell", - args_in==NULL?program:shell, - strerror(errno)); + if(args_in==NULL) + log_error(_("unable to execute program `%s': %s\n"), + program,strerror(errno)); + else + log_error(_("unable to execute shell `%s': %s\n"), + shell,strerror(errno)); /* This mimics the POSIX sh behavior - 127 means "not found" from the shell. */ @@ -446,8 +438,8 @@ int exec_write(struct exec_info **info,const char *program, (*info)->tochild=fdopen(to[1],binary?"wb":"w"); if((*info)->tochild==NULL) { - ret = gpg_error_from_errno (errno); close(to[1]); + ret=G10ERR_WRITE_FILE; goto fail; } @@ -456,8 +448,8 @@ int exec_write(struct exec_info **info,const char *program, (*info)->fromchild=iobuf_fdopen(from[0],"r"); if((*info)->fromchild==NULL) { - ret = gpg_error_from_errno (errno); close(from[0]); + ret=G10ERR_READ_FILE; goto fail; } @@ -472,12 +464,18 @@ int exec_write(struct exec_info **info,const char *program, log_debug("using temp file `%s'\n",(*info)->tempfile_in); /* It's not fork/exec/pipe, so create a temp file */ - (*info)->tochild=fopen((*info)->tempfile_in,binary?"wb":"w"); + if( is_secured_filename ((*info)->tempfile_in) ) + { + (*info)->tochild = NULL; + errno = EPERM; + } + else + (*info)->tochild=fopen((*info)->tempfile_in,binary?"wb":"w"); if((*info)->tochild==NULL) { - ret = gpg_error_from_errno (errno); log_error(_("can't create `%s': %s\n"), (*info)->tempfile_in,strerror(errno)); + ret=G10ERR_WRITE_FILE; goto fail; } @@ -489,18 +487,18 @@ int exec_write(struct exec_info **info,const char *program, int exec_read(struct exec_info *info) { - int ret=GPG_ERR_GENERAL; + int ret=G10ERR_GENERAL; fclose(info->tochild); info->tochild=NULL; - if(info->use_temp_files) + if(info->flags.use_temp_files) { if(DBG_EXTPROG) log_debug("system() command is %s\n",info->command); #if defined (_WIN32) - info->progreturn=win_system(info->command); + info->progreturn=w32_system(info->command); #else info->progreturn=system(info->command); #endif @@ -537,14 +535,21 @@ int exec_read(struct exec_info *info) goto fail; } - if(!info->writeonly) + if(!info->flags.writeonly) { info->fromchild=iobuf_open(info->tempfile_out); + if (info->fromchild + && is_secured_file (iobuf_get_fd (info->fromchild))) + { + iobuf_close (info->fromchild); + info->fromchild = NULL; + errno = EPERM; + } if(info->fromchild==NULL) { - ret = gpg_error_from_errno (errno); log_error(_("unable to read external program response: %s\n"), strerror(errno)); + ret=G10ERR_READ_FILE; goto fail; } @@ -583,7 +588,7 @@ int exec_finish(struct exec_info *info) } #endif - if(info->madedir && !info->keep_temp_files) + if(info->flags.madedir && !info->flags.keep_temp_files) { if(info->tempfile_in) { @@ -604,12 +609,12 @@ int exec_finish(struct exec_info *info) info->tempdir,strerror(errno)); } - xfree (info->command); - xfree (info->name); - xfree (info->tempdir); - xfree (info->tempfile_in); - xfree (info->tempfile_out); - xfree (info); + xfree(info->command); + xfree(info->name); + xfree(info->tempdir); + xfree(info->tempfile_in); + xfree(info->tempfile_out); + xfree(info); return ret; } diff --git a/g10/exec.h b/g10/exec.h index eda406894..66d13c72b 100644 --- a/g10/exec.h +++ b/g10/exec.h @@ -1,5 +1,5 @@ /* exec.h - * Copyright (C) 2001, 2002, 2003 Free Software Foundation, Inc. + * Copyright (C) 2001, 2002, 2005 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -15,7 +15,8 @@ * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. */ #ifndef _EXEC_H_ @@ -23,11 +24,20 @@ #include <unistd.h> #include <stdio.h> + #include "../common/iobuf.h" struct exec_info { - int progreturn,binary,writeonly,madedir,use_temp_files,keep_temp_files; + int progreturn; + struct + { + unsigned int binary:1; + unsigned int writeonly:1; + unsigned int madedir:1; + unsigned int use_temp_files:1; + unsigned int keep_temp_files:1; + } flags; pid_t child; FILE *tochild; iobuf_t fromchild; @@ -38,6 +48,6 @@ int exec_write(struct exec_info **info,const char *program, const char *args_in,const char *name,int writeonly,int binary); int exec_read(struct exec_info *info); int exec_finish(struct exec_info *info); -int set_exec_path(const char *path,int method); +int set_exec_path(const char *path); #endif /* !_EXEC_H_ */ diff --git a/g10/export.c b/g10/export.c index 43d1b21ed..495079602 100644 --- a/g10/export.c +++ b/g10/export.c @@ -1,6 +1,6 @@ /* export.c - * Copyright (C) 1998, 1999, 2000, 2001, 2002, - * 2003 Free Software Foundation, Inc. + * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, + * 2005 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -16,7 +16,8 @@ * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. */ #include <config.h> @@ -26,38 +27,68 @@ #include <errno.h> #include <assert.h> +#include "gpg.h" #include "options.h" #include "packet.h" #include "errors.h" #include "keydb.h" -#include "memory.h" #include "util.h" #include "main.h" #include "i18n.h" +#include "trustdb.h" + + +/* An object to keep track of subkeys. */ +struct subkey_list_s +{ + struct subkey_list_s *next; + u32 kid[2]; +}; +typedef struct subkey_list_s *subkey_list_t; + static int do_export( STRLIST users, int secret, unsigned int options ); -static int do_export_stream( iobuf_t out, STRLIST users, int secret, +static int do_export_stream( IOBUF out, STRLIST users, int secret, KBNODE *keyblock_out, unsigned int options, int *any ); static int build_sexp (iobuf_t out, PACKET *pkt, int *indent); + int -parse_export_options(char *str,unsigned int *options) +parse_export_options(char *str,unsigned int *options,int noisy) { struct parse_options export_opts[]= { - {"include-non-rfc",EXPORT_INCLUDE_NON_RFC}, - {"include-local-sigs",EXPORT_INCLUDE_LOCAL_SIGS}, - {"include-attributes",EXPORT_INCLUDE_ATTRIBUTES}, - {"include-sensitive-revkeys",EXPORT_INCLUDE_SENSITIVE_REVKEYS}, - {"sexp-format",EXPORT_SEXP_FORMAT}, - {NULL,0} + {"export-local-sigs",EXPORT_LOCAL_SIGS,NULL, + N_("export signatures that are marked as local-only")}, + {"export-attributes",EXPORT_ATTRIBUTES,NULL, + N_("export attribute user IDs (generally photo IDs)")}, + {"export-sensitive-revkeys",EXPORT_SENSITIVE_REVKEYS,NULL, + N_("export revocation keys marked as \"sensitive\"")}, + {"export-reset-subkey-passwd",EXPORT_RESET_SUBKEY_PASSWD,NULL, + N_("remove the passphrase from exported subkeys")}, + {"export-clean",EXPORT_CLEAN,NULL, + N_("remove unusable parts from key during export")}, + {"export-minimal",EXPORT_MINIMAL|EXPORT_CLEAN,NULL, + N_("remove as much as possible from key during export")}, + {"export-sexp-format",EXPORT_SEXP_FORMAT, NULL, + N_("export keys in an S-expression based format")}, + /* Aliases for backward compatibility */ + {"include-local-sigs",EXPORT_LOCAL_SIGS,NULL,NULL}, + {"include-attributes",EXPORT_ATTRIBUTES,NULL,NULL}, + {"include-sensitive-revkeys",EXPORT_SENSITIVE_REVKEYS,NULL,NULL}, + /* dummy */ + {"export-unusable-sigs",0,NULL,NULL}, + {"export-clean-sigs",0,NULL,NULL}, + {"export-clean-uids",0,NULL,NULL}, + {NULL,0,NULL,NULL} /* add tags for include revoked and disabled? */ }; - return parse_options(str,options,export_opts); + return parse_options(str,options,export_opts,noisy); } + /**************** * Export the public keys (to standard out or --output). * Depending on opt.armor the output is armored. @@ -74,7 +105,7 @@ export_pubkeys( STRLIST users, unsigned int options ) * been exported */ int -export_pubkeys_stream( iobuf_t out, STRLIST users, +export_pubkeys_stream( IOBUF out, STRLIST users, KBNODE *keyblock_out, unsigned int options ) { int any, rc; @@ -90,7 +121,7 @@ export_seckeys( STRLIST users ) { /* Use only relevant options for the secret key. */ unsigned int options = (opt.export_options & EXPORT_SEXP_FORMAT); - return do_export (users, 1, options); + return do_export( users, 1, options ); } int @@ -98,37 +129,38 @@ export_secsubkeys( STRLIST users ) { /* Use only relevant options for the secret key. */ unsigned int options = (opt.export_options & EXPORT_SEXP_FORMAT); - return do_export( users, 2, options); + return do_export( users, 2, options ); } static int -do_export (STRLIST users, int secret, unsigned int options) +do_export( STRLIST users, int secret, unsigned int options ) { - iobuf_t out = NULL; + IOBUF out = NULL; int any, rc; armor_filter_context_t afx; compress_filter_context_t zfx; - memset (&afx, 0, sizeof afx); - memset (&zfx, 0, sizeof zfx); + memset( &afx, 0, sizeof afx); + memset( &zfx, 0, sizeof zfx); - rc = open_outfile (NULL, 0, &out); + rc = open_outfile( NULL, 0, &out ); if (rc) return rc; if (!(options & EXPORT_SEXP_FORMAT)) { - if (opt.armor) + if ( opt.armor ) { afx.what = secret?5:1; - iobuf_push_filter( out, armor_filter, &afx ); + iobuf_push_filter ( out, armor_filter, &afx ); } - if (opt.compress_keys && opt.compress) - iobuf_push_filter( out, compress_filter, &zfx ); + if ( opt.compress_keys ) + push_compress_filter (out,&zfx,default_compress_algo()); } - rc = do_export_stream (out, users, secret, NULL, options, &any ); - if (rc || !any) + rc = do_export_stream ( out, users, secret, NULL, options, &any ); + + if ( rc || !any ) iobuf_cancel (out); else iobuf_close (out); @@ -136,11 +168,129 @@ do_export (STRLIST users, int secret, unsigned int options) } + +/* Release an entire subkey list. */ +static void +release_subkey_list (subkey_list_t list) +{ + while (list) + { + subkey_list_t tmp = list->next;; + xfree (list); + list = tmp; + } +} + + +/* Returns true if NODE is a subkey and contained in LIST. */ +static int +subkey_in_list_p (subkey_list_t list, KBNODE node) +{ + if (node->pkt->pkttype == PKT_PUBLIC_SUBKEY + || node->pkt->pkttype == PKT_SECRET_SUBKEY ) + { + u32 kid[2]; + + if (node->pkt->pkttype == PKT_PUBLIC_SUBKEY) + keyid_from_pk (node->pkt->pkt.public_key, kid); + else + keyid_from_sk (node->pkt->pkt.secret_key, kid); + + for (; list; list = list->next) + if (list->kid[0] == kid[0] && list->kid[1] == kid[1]) + return 1; + } + return 0; +} + +/* Allocate a new subkey list item from NODE. */ +static subkey_list_t +new_subkey_list_item (KBNODE node) +{ + subkey_list_t list = xcalloc (1, sizeof *list); + + if (node->pkt->pkttype == PKT_PUBLIC_SUBKEY) + keyid_from_pk (node->pkt->pkt.public_key, list->kid); + else if (node->pkt->pkttype == PKT_SECRET_SUBKEY) + keyid_from_sk (node->pkt->pkt.secret_key, list->kid); + + return list; +} + + +/* Helper function to check whether the subkey at NODE actually + matches the description at DESC. The function returns true if the + key under question has been specified by an exact specification + (keyID or fingerprint) and does match the one at NODE. It is + assumed that the packet at NODE is either a public or secret + subkey. */ +static int +exact_subkey_match_p (KEYDB_SEARCH_DESC *desc, KBNODE node) +{ + u32 kid[2]; + byte fpr[MAX_FINGERPRINT_LEN]; + size_t fprlen; + int result = 0; + + switch(desc->mode) + { + case KEYDB_SEARCH_MODE_SHORT_KID: + case KEYDB_SEARCH_MODE_LONG_KID: + if (node->pkt->pkttype == PKT_PUBLIC_SUBKEY) + keyid_from_pk (node->pkt->pkt.public_key, kid); + else + keyid_from_sk (node->pkt->pkt.secret_key, kid); + break; + + case KEYDB_SEARCH_MODE_FPR16: + case KEYDB_SEARCH_MODE_FPR20: + case KEYDB_SEARCH_MODE_FPR: + if (node->pkt->pkttype == PKT_PUBLIC_SUBKEY) + fingerprint_from_pk (node->pkt->pkt.public_key, fpr,&fprlen); + else + fingerprint_from_sk (node->pkt->pkt.secret_key, fpr,&fprlen); + break; + + default: + break; + } + + switch(desc->mode) + { + case KEYDB_SEARCH_MODE_SHORT_KID: + if (desc->u.kid[1] == kid[1]) + result = 1; + break; + + case KEYDB_SEARCH_MODE_LONG_KID: + if (desc->u.kid[0] == kid[0] && desc->u.kid[1] == kid[1]) + result = 1; + break; + + case KEYDB_SEARCH_MODE_FPR16: + if (!memcmp (desc->u.fpr, fpr, 16)) + result = 1; + break; + + case KEYDB_SEARCH_MODE_FPR20: + case KEYDB_SEARCH_MODE_FPR: + if (!memcmp (desc->u.fpr, fpr, 20)) + result = 1; + break; + + default: + break; + } + + return result; +} + + /* If keyblock_out is non-NULL, AND the exit code is zero, then it contains a pointer to the first keyblock found and exported. No other keyblocks are exported. The caller must free it. */ static int -do_export_stream( iobuf_t out, STRLIST users, int secret, +do_export_stream( IOBUF out, STRLIST users, int secret, KBNODE *keyblock_out, unsigned int options, int *any ) { int rc = 0; @@ -149,6 +299,7 @@ do_export_stream( iobuf_t out, STRLIST users, int secret, KBNODE kbctx, node; size_t ndesc, descindex; KEYDB_SEARCH_DESC *desc = NULL; + subkey_list_t subkey_list = NULL; /* Track alreay processed subkeys. */ KEYDB_HANDLE kdbhd; STRLIST sl; int indent = 0; @@ -159,7 +310,7 @@ do_export_stream( iobuf_t out, STRLIST users, int secret, if (!users) { ndesc = 1; - desc = xcalloc (1, ndesc * sizeof *desc); + desc = xcalloc ( ndesc, sizeof *desc ); desc[0].mode = KEYDB_SEARCH_MODE_FIRST; } else { @@ -171,11 +322,11 @@ do_export_stream( iobuf_t out, STRLIST users, int secret, if (classify_user_id (sl->d, desc+ndesc)) ndesc++; else - log_error (_("key `%s' not found: %s\n"), - sl->d, gpg_strerror (GPG_ERR_INV_USER_ID)); + log_error (_("key \"%s\" not found: %s\n"), + sl->d, g10_errstr (G10ERR_INV_USER_ID)); } - /* it would be nice to see which of the given users did + /* It would be nice to see which of the given users did actually match one in the keyring. To implement this we need to have a found flag for each entry in desc and to set this we must check all those entries after a match to mark @@ -183,6 +334,14 @@ do_export_stream( iobuf_t out, STRLIST users, int secret, do this we need an extra flag to enable this feature so */ } +#ifdef ENABLE_SELINUX_HACKS + if (secret) { + log_error (_("exporting secret keys not allowed\n")); + rc = G10ERR_GENERAL; + goto leave; + } +#endif + while (!(rc = keydb_search2 (kdbhd, desc, ndesc, &descindex))) { int sha1_warned=0,skip_until_subkey=0; u32 sk_keyid[2]; @@ -190,49 +349,59 @@ do_export_stream( iobuf_t out, STRLIST users, int secret, if (!users) desc[0].mode = KEYDB_SEARCH_MODE_NEXT; - /* read the keyblock */ + /* Read the keyblock. */ rc = keydb_get_keyblock (kdbhd, &keyblock ); if( rc ) { - log_error (_("error reading keyblock: %s\n"), gpg_strerror (rc) ); + log_error (_("error reading keyblock: %s\n"), g10_errstr(rc) ); goto leave; } - /* do not export keys which are incompatible with rfc2440 */ - if( !(options&EXPORT_INCLUDE_NON_RFC) && - (node = find_kbnode( keyblock, PKT_PUBLIC_KEY )) ) { - PKT_public_key *pk = node->pkt->pkt.public_key; - if( pk->version == 3 && pk->pubkey_algo > 3 ) { - log_info(_("key %08lX: not a rfc2440 key - skipped\n"), - (ulong)keyid_from_pk( pk, NULL) ); - continue; - } - } - - node=find_kbnode( keyblock, PKT_SECRET_KEY ); - if(node) + if((node=find_kbnode(keyblock,PKT_SECRET_KEY))) { PKT_secret_key *sk=node->pkt->pkt.secret_key; keyid_from_sk(sk,sk_keyid); - /* we can't apply GNU mode 1001 on an unprotected key */ + /* We can't apply GNU mode 1001 on an unprotected key. */ if( secret == 2 && !sk->is_protected ) { - log_info(_("key %08lX: not protected - skipped\n"), - (ulong)sk_keyid[1]); + log_info(_("key %s: not protected - skipped\n"), + keystr(sk_keyid)); continue; } - /* no v3 keys with GNU mode 1001 */ + /* No v3 keys with GNU mode 1001. */ if( secret == 2 && sk->version == 3 ) { - log_info(_("key %08lX: PGP 2.x style key - skipped\n"), - (ulong)sk_keyid[1]); + log_info(_("key %s: PGP 2.x style key - skipped\n"), + keystr(sk_keyid)); continue; } + + /* It does not make sense to export a key with a primary + key on card using a non-key stub. We simply skip those + keys when used with --export-secret-subkeys. */ + if (secret == 2 && sk->is_protected + && sk->protect.s2k.mode == 1002 ) + { + log_info(_("key %s: key material on-card - skipped\n"), + keystr(sk_keyid)); + continue; + } + } + else + { + /* It's a public key export, so do the cleaning if + requested. Note that both export-clean and + export-minimal only apply to UID sigs (0x10, 0x11, + 0x12, and 0x13). A designated revocation is never + stripped, even with export-minimal set. */ + + if(options&EXPORT_CLEAN) + clean_key(keyblock,opt.verbose,options&EXPORT_MINIMAL,NULL,NULL); } - /* and write it */ + /* And write it. */ for( kbctx=NULL; (node = walk_kbnode( keyblock, &kbctx, 0 )); ) { if( skip_until_subkey ) { @@ -243,104 +412,92 @@ do_export_stream( iobuf_t out, STRLIST users, int secret, continue; } - /* don't export any comment packets but those in the - * secret keyring */ - if( !secret && node->pkt->pkttype == PKT_COMMENT ) - continue; + /* We used to use comment packets, but not any longer. In + case we still have comments on a key, strip them here + before we call build_packet(). */ + if( node->pkt->pkttype == PKT_COMMENT ) + continue; - /* make sure that ring_trust packets never get exported */ + /* Make sure that ring_trust packets never get exported. */ if (node->pkt->pkttype == PKT_RING_TRUST) continue; /* If exact is set, then we only export what was requested (plus the primary key, if the user didn't specifically - request it) */ + request it). */ if(desc[descindex].exact && (node->pkt->pkttype==PKT_PUBLIC_SUBKEY || node->pkt->pkttype==PKT_SECRET_SUBKEY)) { - u32 kid[2]; - byte fpr[MAX_FINGERPRINT_LEN]; - size_t fprlen; - - switch(desc[descindex].mode) - { - case KEYDB_SEARCH_MODE_SHORT_KID: - case KEYDB_SEARCH_MODE_LONG_KID: - if(node->pkt->pkttype==PKT_PUBLIC_SUBKEY) - keyid_from_pk(node->pkt->pkt.public_key,kid); - else - keyid_from_sk(node->pkt->pkt.secret_key,kid); - break; - - case KEYDB_SEARCH_MODE_FPR16: - case KEYDB_SEARCH_MODE_FPR20: - case KEYDB_SEARCH_MODE_FPR: - if(node->pkt->pkttype==PKT_PUBLIC_SUBKEY) - fingerprint_from_pk(node->pkt->pkt.public_key, - fpr,&fprlen); - else - fingerprint_from_sk(node->pkt->pkt.secret_key, - fpr,&fprlen); - break; - - default: - break; - } - - switch(desc[descindex].mode) - { - case KEYDB_SEARCH_MODE_SHORT_KID: - if (desc[descindex].u.kid[1] != kid[1]) - skip_until_subkey=1; - break; - case KEYDB_SEARCH_MODE_LONG_KID: - if (desc[descindex].u.kid[0] != kid[0] - || desc[descindex].u.kid[1] != kid[1]) - skip_until_subkey=1; - break; - case KEYDB_SEARCH_MODE_FPR16: - if (memcmp (desc[descindex].u.fpr, fpr, 16)) - skip_until_subkey=1; - break; - case KEYDB_SEARCH_MODE_FPR20: - case KEYDB_SEARCH_MODE_FPR: - if (memcmp (desc[descindex].u.fpr, fpr, 20)) - skip_until_subkey=1; - break; - default: - break; - } + if (!exact_subkey_match_p (desc+descindex, node)) + { + /* Before skipping this subkey, check whether any + other description wants an exact match on a + subkey and include that subkey into the output + too. Need to add this subkey to a list so that + it won't get processed a second time. + + So the first step here is to check that list and + skip in any case if the key is in that list. + + We need this whole mess because the import + function is not able to merge secret keys and + thus it is useless to output them as two + separate keys and have import merge them. */ + if (subkey_in_list_p (subkey_list, node)) + skip_until_subkey = 1; /* Already processed this one. */ + else + { + size_t j; + + for (j=0; j < ndesc; j++) + if (j != descindex && desc[j].exact + && exact_subkey_match_p (desc+j, node)) + break; + if (!(j < ndesc)) + skip_until_subkey = 1; /* No other one matching. */ + } + } if(skip_until_subkey) continue; + + /* Mark this one as processed. */ + { + subkey_list_t tmp = new_subkey_list_item (node); + tmp->next = subkey_list; + subkey_list = tmp; + } } - if( node->pkt->pkttype == PKT_SIGNATURE ) { - /* do not export packets which are marked as not exportable */ - if( !(options&EXPORT_INCLUDE_LOCAL_SIGS) && - !node->pkt->pkt.signature->flags.exportable ) - continue; /* not exportable */ - - /* Do not export packets with a "sensitive" revocation - key unless the user wants us to. Note that we do - export these when issuing the actual revocation (see - revoke.c). */ - if( !(options&EXPORT_INCLUDE_SENSITIVE_REVKEYS) && - node->pkt->pkt.signature->revkey ) { - int i; - - for(i=0;i<node->pkt->pkt.signature->numrevkeys;i++) - if(node->pkt->pkt.signature->revkey[i]->class & 0x40) - break; - - if(i<node->pkt->pkt.signature->numrevkeys) - continue; + if(node->pkt->pkttype==PKT_SIGNATURE) + { + /* do not export packets which are marked as not + exportable */ + if(!(options&EXPORT_LOCAL_SIGS) + && !node->pkt->pkt.signature->flags.exportable) + continue; /* not exportable */ + + /* Do not export packets with a "sensitive" revocation + key unless the user wants us to. Note that we do + export these when issuing the actual revocation + (see revoke.c). */ + if(!(options&EXPORT_SENSITIVE_REVKEYS) + && node->pkt->pkt.signature->revkey) + { + int i; + + for(i=0;i<node->pkt->pkt.signature->numrevkeys;i++) + if(node->pkt->pkt.signature->revkey[i]->class & 0x40) + break; + + if(i<node->pkt->pkt.signature->numrevkeys) + continue; + } } - } /* Don't export attribs? */ - if( !(options&EXPORT_INCLUDE_ATTRIBUTES) && + if( !(options&EXPORT_ATTRIBUTES) && node->pkt->pkttype == PKT_USER_ID && node->pkt->pkt.user_id->attrib_data ) { /* Skip until we get to something that is not an attrib @@ -352,8 +509,9 @@ do_export_stream( iobuf_t out, STRLIST users, int secret, continue; } - if( secret == 2 && node->pkt->pkttype == PKT_SECRET_KEY ) { - /* we don't want to export the secret parts of the + if( secret == 2 && node->pkt->pkttype == PKT_SECRET_KEY ) + { + /* We don't want to export the secret parts of the * primary key, this is done by using GNU protection mode 1001 */ int save_mode = node->pkt->pkt.secret_key->protect.s2k.mode; @@ -363,42 +521,91 @@ do_export_stream( iobuf_t out, STRLIST users, int secret, else rc = build_packet (out, node->pkt); node->pkt->pkt.secret_key->protect.s2k.mode = save_mode; - } - else { - /* Warn the user if the secret key or any of the secret - subkeys are protected with SHA1 and we have - simple_sk_checksum set. */ - if(!sha1_warned && opt.simple_sk_checksum && - (node->pkt->pkttype==PKT_SECRET_KEY || - node->pkt->pkttype==PKT_SECRET_SUBKEY) && - node->pkt->pkt.secret_key->protect.sha1chk) - { - /* I hope this warning doesn't confuse people. */ - log_info(_("WARNING: secret key %08lX does not have a " - "simple SK checksum\n"),(ulong)sk_keyid[1]); - - sha1_warned=1; - } + } + else if (secret == 2 && node->pkt->pkttype == PKT_SECRET_SUBKEY + && (opt.export_options&EXPORT_RESET_SUBKEY_PASSWD)) + { + /* If the subkey is protected reset the passphrase to + export an unprotected subkey. This feature is + useful in cases of a subkey copied to an unattended + machine where a passphrase is not required. */ + PKT_secret_key *sk_save, *sk; + + sk_save = node->pkt->pkt.secret_key; + sk = copy_secret_key (NULL, sk_save); + node->pkt->pkt.secret_key = sk; + + log_info (_("about to export an unprotected subkey\n")); + switch (is_secret_key_protected (sk)) + { + case -1: + rc = G10ERR_PUBKEY_ALGO; + break; + case 0: + break; + default: + if (sk->protect.s2k.mode == 1001) + ; /* No secret parts. */ + else if( sk->protect.s2k.mode == 1002 ) + ; /* Card key stub. */ + else + { + rc = check_secret_key( sk, 0 ); + } + break; + } + if (rc) + { + node->pkt->pkt.secret_key = sk_save; + free_secret_key (sk); + log_error (_("failed to unprotect the subkey: %s\n"), + g10_errstr (rc)); + goto leave; + } + + rc = build_packet (out, node->pkt); + + node->pkt->pkt.secret_key = sk_save; + free_secret_key (sk); + } + else + { + /* Warn the user if the secret key or any of the secret + subkeys are protected with SHA1 and we have + simple_sk_checksum set. */ + if(!sha1_warned && opt.simple_sk_checksum && + (node->pkt->pkttype==PKT_SECRET_KEY || + node->pkt->pkttype==PKT_SECRET_SUBKEY) && + node->pkt->pkt.secret_key->protect.sha1chk) + { + /* I hope this warning doesn't confuse people. */ + log_info(_("WARNING: secret key %s does not have a " + "simple SK checksum\n"),keystr(sk_keyid)); + + sha1_warned=1; + } if ((options&EXPORT_SEXP_FORMAT)) rc = build_sexp (out, node->pkt, &indent); else rc = build_packet (out, node->pkt); - } + } if( rc ) { log_error("build_packet(%d) failed: %s\n", - node->pkt->pkttype, gpg_strerror (rc) ); + node->pkt->pkttype, g10_errstr(rc) ); + rc = G10ERR_WRITE_FILE; goto leave; } } + if ((options&EXPORT_SEXP_FORMAT) && indent) { for (; indent; indent--) iobuf_put (out, ')'); iobuf_put (out, '\n'); } - + ++*any; if(keyblock_out) { @@ -416,7 +623,8 @@ do_export_stream( iobuf_t out, STRLIST users, int secret, rc = 0; leave: - xfree (desc); + release_subkey_list (subkey_list); + xfree(desc); keydb_release (kdbhd); if(rc || keyblock_out==NULL) release_kbnode( keyblock ); @@ -426,6 +634,7 @@ do_export_stream( iobuf_t out, STRLIST users, int secret, } + static int write_sexp_line (iobuf_t out, int *indent, const char *text) { @@ -524,8 +733,8 @@ build_sexp_seckey (iobuf_t out, PACKET *pkt, int *indent) } -/* For some packet types we write them in a S-Exp like format. This is - still EXPERIMENTAL and subject to change. */ +/* For some packet types we write them in a S-expression format. This + is still EXPERIMENTAL and subject to change. */ static int build_sexp (iobuf_t out, PACKET *pkt, int *indent) { diff --git a/g10/filter.h b/g10/filter.h index 12c5cebed..3b4e73963 100644 --- a/g10/filter.h +++ b/g10/filter.h @@ -1,5 +1,6 @@ /* filter.h - * Copyright (C) 1998, 1999, 2000, 2001 Free Software Foundation, Inc. + * Copyright (C) 1998, 1999, 2000, 2001, 2003, + * 2005 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -15,18 +16,18 @@ * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. */ #ifndef G10_FILTER_H #define G10_FILTER_H #include "types.h" #include "cipher.h" -#include "iobuf.h" typedef struct { - MD_HANDLE md; /* catch all */ - MD_HANDLE md2; /* if we want to calculate an alternate hash */ + gcry_md_hd_t md; /* catch all */ + gcry_md_hd_t md2; /* if we want to calculate an alternate hash */ size_t maxbuf_size; } md_filter_context_t; @@ -49,6 +50,10 @@ typedef struct { int truncated; /* number of truncated lines */ int qp_detected; int pgp2mode; + byte eol[3]; /* The end of line characters as a + zero-terminated string. Defaults + (eol[0]=='\0') to whatever the local + platform uses. */ byte *buffer; /* malloced buffer */ unsigned buffer_size; /* and size of this buffer */ @@ -87,9 +92,9 @@ typedef struct compress_filter_context_s compress_filter_context_t; typedef struct { DEK *dek; u32 datalen; - CIPHER_HANDLE cipher_hd; + gcry_cipher_hd_t cipher_hd; int header; - MD_HANDLE mdc_hash; + gcry_md_hd_t mdc_hash; byte enchash[20]; int create_mdc; /* flag will be set by the cipher filter */ } cipher_filter_context_t; @@ -104,7 +109,7 @@ typedef struct { int truncated; /* number of truncated lines */ int not_dash_escaped; int escape_from; - MD_HANDLE md; + gcry_md_hd_t md; int pending_lf; int pending_esc; } text_filter_context_t; @@ -121,35 +126,36 @@ typedef struct { /* encrypt_filter_context_t defined in main.h */ /*-- mdfilter.c --*/ -int md_filter( void *opaque, int control, iobuf_t a, byte *buf, size_t *ret_len); +int md_filter( void *opaque, int control, IOBUF a, byte *buf, size_t *ret_len); void free_md_filter_context( md_filter_context_t *mfx ); /*-- armor.c --*/ -int use_armor_filter( iobuf_t a ); +int use_armor_filter( IOBUF a ); int armor_filter( void *opaque, int control, - iobuf_t chain, byte *buf, size_t *ret_len); + IOBUF chain, byte *buf, size_t *ret_len); UnarmorPump unarmor_pump_new (void); void unarmor_pump_release (UnarmorPump x); int unarmor_pump (UnarmorPump x, int c); /*-- compress.c --*/ -int compress_filter( void *opaque, int control, - iobuf_t chain, byte *buf, size_t *ret_len); +void push_compress_filter(IOBUF out,compress_filter_context_t *zfx,int algo); +void push_compress_filter2(IOBUF out,compress_filter_context_t *zfx, + int algo,int rel); /*-- cipher.c --*/ int cipher_filter( void *opaque, int control, - iobuf_t chain, byte *buf, size_t *ret_len); + IOBUF chain, byte *buf, size_t *ret_len); /*-- textfilter.c --*/ int text_filter( void *opaque, int control, - iobuf_t chain, byte *buf, size_t *ret_len); -int copy_clearsig_text( iobuf_t out, iobuf_t inp, MD_HANDLE md, - int escape_dash, int escape_from, int pgp2mode ); + IOBUF chain, byte *buf, size_t *ret_len); +int copy_clearsig_text (IOBUF out, IOBUF inp, gcry_md_hd_t md, + int escape_dash, int escape_from, int pgp2mode); /*-- progress.c --*/ int progress_filter (void *opaque, int control, - iobuf_t a, byte *buf, size_t *ret_len); + IOBUF a, byte *buf, size_t *ret_len); void handle_progress (progress_filter_context_t *pfx, - iobuf_t inp, const char *name); + IOBUF inp, const char *name); #endif /*G10_FILTER_H*/ diff --git a/g10/free-packet.c b/g10/free-packet.c index 7ced327f5..8aab06370 100644 --- a/g10/free-packet.c +++ b/g10/free-packet.c @@ -1,6 +1,6 @@ /* free-packet.c - cleanup stuff for packets - * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003 - * Free Software Foundation, Inc. + * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, + * 2005 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -16,7 +16,8 @@ * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. */ #include <config.h> @@ -25,18 +26,18 @@ #include <string.h> #include <assert.h> +#include "gpg.h" #include "packet.h" -#include "iobuf.h" -#include "mpi.h" +#include "../common/iobuf.h" #include "util.h" #include "cipher.h" -#include "memory.h" -#include "options.h" +#include "options.h" + void free_symkey_enc( PKT_symkey_enc *enc ) { - xfree (enc); + xfree(enc); } void @@ -45,10 +46,10 @@ free_pubkey_enc( PKT_pubkey_enc *enc ) int n, i; n = pubkey_get_nenc( enc->pubkey_algo ); if( !n ) - mpi_release (enc->data[0]); + mpi_release(enc->data[0]); for(i=0; i < n; i++ ) - mpi_release ( enc->data[i] ); - xfree (enc); + mpi_release( enc->data[i] ); + xfree(enc); } void @@ -58,14 +59,21 @@ free_seckey_enc( PKT_signature *sig ) n = pubkey_get_nsig( sig->pubkey_algo ); if( !n ) - mpi_release (sig->data[0]); + mpi_release(sig->data[0]); for(i=0; i < n; i++ ) - mpi_release ( sig->data[i] ); - - xfree (sig->revkey); - xfree (sig->hashed); - xfree (sig->unhashed); - xfree (sig); + mpi_release( sig->data[i] ); + + xfree(sig->revkey); + xfree(sig->hashed); + xfree(sig->unhashed); + + if (sig->pka_info) + { + xfree (sig->pka_info->uri); + xfree (sig->pka_info); + } + + xfree(sig); } @@ -75,9 +83,9 @@ release_public_key_parts( PKT_public_key *pk ) int n, i; n = pubkey_get_npkey( pk->pubkey_algo ); if( !n ) - mpi_release (pk->pkey[0]); + mpi_release(pk->pkey[0]); for(i=0; i < n; i++ ) { - mpi_release ( pk->pkey[i] ); + mpi_release( pk->pkey[i] ); pk->pkey[i] = NULL; } if (pk->prefs) { @@ -89,7 +97,7 @@ release_public_key_parts( PKT_public_key *pk ) pk->user_id = NULL; } if (pk->revkey) { - xfree (pk->revkey); + xfree(pk->revkey); pk->revkey=NULL; pk->numrevkeys=0; } @@ -100,7 +108,7 @@ void free_public_key( PKT_public_key *pk ) { release_public_key_parts( pk ); - xfree (pk); + xfree(pk); } @@ -150,7 +158,7 @@ copy_public_key ( PKT_public_key *d, PKT_public_key *s) int n, i; if( !d ) - d = xmalloc (sizeof *d); + d = xmalloc(sizeof *d); memcpy( d, s, sizeof *d ); d->user_id = scopy_user_id (s->user_id); d->prefs = copy_prefs (s->prefs); @@ -164,7 +172,7 @@ copy_public_key ( PKT_public_key *d, PKT_public_key *s) if( !s->revkey && s->numrevkeys ) BUG(); if( s->numrevkeys ) { - d->revkey = xmalloc (sizeof(struct revocation_key)*s->numrevkeys); + d->revkey = xmalloc(sizeof(struct revocation_key)*s->numrevkeys); memcpy(d->revkey,s->revkey,sizeof(struct revocation_key)*s->numrevkeys); } else @@ -194,13 +202,28 @@ copy_public_parts_to_secret_key( PKT_public_key *pk, PKT_secret_key *sk ) sk->keyid[1] = pk->keyid[1]; } + +static pka_info_t * +cp_pka_info (const pka_info_t *s) +{ + pka_info_t *d = xmalloc (sizeof *s + strlen (s->email)); + + d->valid = s->valid; + d->checked = s->checked; + d->uri = s->uri? xstrdup (s->uri):NULL; + memcpy (d->fpr, s->fpr, sizeof s->fpr); + strcpy (d->email, s->email); + return d; +} + + PKT_signature * copy_signature( PKT_signature *d, PKT_signature *s ) { int n, i; if( !d ) - d = xmalloc (sizeof *d); + d = xmalloc(sizeof *d); memcpy( d, s, sizeof *d ); n = pubkey_get_nsig( s->pubkey_algo ); if( !n ) @@ -209,6 +232,7 @@ copy_signature( PKT_signature *d, PKT_signature *s ) for(i=0; i < n; i++ ) d->data[i] = mpi_copy( s->data[i] ); } + d->pka_info = s->pka_info? cp_pka_info (s->pka_info) : NULL; d->hashed = cp_subpktarea (s->hashed); d->unhashed = cp_subpktarea (s->unhashed); if(s->numrevkeys) @@ -241,9 +265,9 @@ release_secret_key_parts( PKT_secret_key *sk ) n = pubkey_get_nskey( sk->pubkey_algo ); if( !n ) - mpi_release (sk->skey[0]); + mpi_release(sk->skey[0]); for(i=0; i < n; i++ ) { - mpi_release ( sk->skey[i] ); + mpi_release( sk->skey[i] ); sk->skey[i] = NULL; } } @@ -252,7 +276,7 @@ void free_secret_key( PKT_secret_key *sk ) { release_secret_key_parts( sk ); - xfree (sk); + xfree(sk); } PKT_secret_key * @@ -261,29 +285,32 @@ copy_secret_key( PKT_secret_key *d, PKT_secret_key *s ) int n, i; if( !d ) - d = xmalloc (sizeof *d); + d = xmalloc_secure(sizeof *d); + else + release_secret_key_parts (d); memcpy( d, s, sizeof *d ); n = pubkey_get_nskey( s->pubkey_algo ); if( !n ) - d->skey[0] = mpi_copy(s->skey[0]); + d->skey[0] = mpi_copy(s->skey[0]); else { for(i=0; i < n; i++ ) - d->skey[i] = mpi_copy( s->skey[i] ); + d->skey[i] = mpi_copy( s->skey[i] ); } + return d; } void free_comment( PKT_comment *rem ) { - xfree (rem); + xfree(rem); } void free_attributes(PKT_user_id *uid) { - xfree (uid->attribs); - xfree (uid->attrib_data); + xfree(uid->attribs); + xfree(uid->attrib_data); uid->attribs=NULL; uid->attrib_data=NULL; @@ -312,14 +339,14 @@ free_compressed( PKT_compressed *zd ) while( iobuf_read( zd->buf, NULL, 1<<30 ) != -1 ) ; } - xfree (zd); + xfree(zd); } void free_encrypted( PKT_encrypted *ed ) { if( ed->buf ) { /* have to skip some bytes */ - if( iobuf_in_block_mode(ed->buf) ) { + if( ed->is_partial ) { while( iobuf_read( ed->buf, NULL, 1<<30 ) != -1 ) ; } @@ -333,7 +360,7 @@ free_encrypted( PKT_encrypted *ed ) } } } - xfree (ed); + xfree(ed); } @@ -341,7 +368,7 @@ void free_plaintext( PKT_plaintext *pt ) { if( pt->buf ) { /* have to skip some bytes */ - if( iobuf_in_block_mode(pt->buf) ) { + if( pt->is_partial ) { while( iobuf_read( pt->buf, NULL, 1<<30 ) != -1 ) ; } @@ -355,7 +382,7 @@ free_plaintext( PKT_plaintext *pt ) } } } - xfree (pt); + xfree(pt); } /**************** @@ -405,7 +432,7 @@ free_packet( PACKET *pkt ) free_plaintext( pkt->pkt.plaintext ); break; default: - xfree ( pkt->pkt.generic ); + xfree( pkt->pkt.generic ); break; } pkt->pkt.generic = NULL; diff --git a/g10/getkey.c b/g10/getkey.c index f51b8f2df..acd992c21 100644 --- a/g10/getkey.c +++ b/g10/getkey.c @@ -1,6 +1,6 @@ /* getkey.c - Get a key from the database - * Copyright (C) 1998, 1999, 2000, 2001, 2002, - * 2003 Free Software Foundation, Inc. + * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, + * 2006 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -16,7 +16,8 @@ * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. */ #include <config.h> @@ -29,22 +30,21 @@ #include "gpg.h" #include "util.h" #include "packet.h" -#include "memory.h" #include "iobuf.h" #include "keydb.h" #include "options.h" #include "main.h" #include "trustdb.h" #include "i18n.h" +#include "keyserver-internal.h" -#define MAX_PK_CACHE_ENTRIES 200 -#define MAX_UID_CACHE_ENTRIES 200 +#define MAX_PK_CACHE_ENTRIES PK_UID_CACHE_SIZE +#define MAX_UID_CACHE_ENTRIES PK_UID_CACHE_SIZE #if MAX_PK_CACHE_ENTRIES < 2 #error We need the cache for key creation #endif - struct getkey_ctx_s { int exact; KBNODE keyblock; @@ -154,7 +154,7 @@ cache_public_key( PKT_public_key *pk ) return; } pk_cache_entries++; - ce = xmalloc ( sizeof *ce ); + ce = xmalloc( sizeof *ce ); ce->next = pk_cache; pk_cache = ce; ce->pk = copy_public_key( NULL, pk ); @@ -164,6 +164,21 @@ cache_public_key( PKT_public_key *pk ) } +/* Return a const utf-8 string with the text "[User ID not found]". + This fucntion is required so that we don't need to switch gettext's + encoding temporary. */ +static const char * +user_id_not_found_utf8 (void) +{ + static char *text; + + if (!text) + text = native_to_utf8 (_("[User ID not found]")); + return text; +} + + + /* * Return the user ID from the given keyblock. * We use the primary uid flag which has been set by the merge_selfsigs @@ -184,9 +199,7 @@ get_primary_uid ( KBNODE keyblock, size_t *uidlen ) return k->pkt->pkt.user_id->name; } } - /* fixme: returning translatable constants instead of a user ID is - * not good because they are probably not utf-8 encoded. */ - s = _("[User id not found]"); + s = user_id_not_found_utf8 (); *uidlen = strlen (s); return s; } @@ -218,7 +231,7 @@ cache_user_id( KBNODE keyblock ) for (k=keyblock; k; k = k->next ) { if ( k->pkt->pkttype == PKT_PUBLIC_KEY || k->pkt->pkttype == PKT_PUBLIC_SUBKEY ) { - keyid_list_t a = xcalloc (1, sizeof *a ); + keyid_list_t a = xmalloc_clear ( 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 ); @@ -252,10 +265,10 @@ cache_user_id( KBNODE keyblock ) r = user_id_db; user_id_db = r->next; release_keyid_list ( r->keyids ); - xfree (r); + xfree(r); uid_cache_entries--; } - r = xmalloc ( sizeof *r + uidlen-1 ); + r = xmalloc( sizeof *r + uidlen-1 ); r->keyids = keyids; r->len = uidlen; memcpy(r->name, uid, r->len); @@ -275,7 +288,7 @@ getkey_disable_caches() for( ce = pk_cache; ce; ce = ce2 ) { ce2 = ce->next; free_public_key( ce->pk ); - xfree ( ce ); + xfree( ce ); } pk_cache_disabled=1; pk_cache_entries = 0; @@ -322,20 +335,25 @@ get_pubkey( PKT_public_key *pk, u32 *keyid ) int rc = 0; #if MAX_PK_CACHE_ENTRIES - { /* Try to get it from the cache */ + if(pk) + { + /* Try to get it from the cache. We don't do this when pk is + NULL as it does not guarantee that the user IDs are + cached. */ pk_cache_entry_t ce; - for( ce = pk_cache; ce; ce = ce->next ) { - if( ce->keyid[0] == keyid[0] && ce->keyid[1] == keyid[1] ) { - if( pk ) - copy_public_key( pk, ce->pk ); + for( ce = pk_cache; ce; ce = ce->next ) + { + if( ce->keyid[0] == keyid[0] && ce->keyid[1] == keyid[1] ) + { + copy_public_key( pk, ce->pk ); return 0; - } - } - } + } + } + } #endif /* more init stuff */ if( !pk ) { - pk = xcalloc (1, sizeof *pk ); + pk = xmalloc_clear( sizeof *pk ); internal++; } @@ -363,7 +381,7 @@ get_pubkey( PKT_public_key *pk, u32 *keyid ) if( !rc ) goto leave; - rc = GPG_ERR_NO_PUBKEY; + rc = G10ERR_NO_PUBKEY; leave: if( !rc ) @@ -376,13 +394,15 @@ get_pubkey( PKT_public_key *pk, u32 *keyid ) /* Get a public key and store it into the allocated pk. This function differs from get_pubkey() in that it does not do a check of the key - to avoid recursion. It should be used only in very certain cases. */ + to avoid recursion. It should be used only in very certain cases. + It will only retrieve primary keys. */ int get_pubkey_fast (PKT_public_key *pk, u32 *keyid) { int rc = 0; KEYDB_HANDLE hd; KBNODE keyblock; + u32 pkid[2]; assert (pk); #if MAX_PK_CACHE_ENTRIES @@ -406,29 +426,34 @@ get_pubkey_fast (PKT_public_key *pk, u32 *keyid) if (rc == -1) { keydb_release (hd); - return GPG_ERR_NO_PUBKEY; + return G10ERR_NO_PUBKEY; } rc = keydb_get_keyblock (hd, &keyblock); keydb_release (hd); if (rc) { - log_error ("keydb_get_keyblock failed: %s\n", gpg_strerror (rc)); - return GPG_ERR_NO_PUBKEY; + log_error ("keydb_get_keyblock failed: %s\n", g10_errstr(rc)); + return G10ERR_NO_PUBKEY; } - + assert ( keyblock->pkt->pkttype == PKT_PUBLIC_KEY || keyblock->pkt->pkttype == PKT_PUBLIC_SUBKEY ); - copy_public_key (pk, keyblock->pkt->pkt.public_key ); + + keyid_from_pk(keyblock->pkt->pkt.public_key,pkid); + if(keyid[0]==pkid[0] && keyid[1]==pkid[1]) + copy_public_key (pk, keyblock->pkt->pkt.public_key ); + else + rc=G10ERR_NO_PUBKEY; + release_kbnode (keyblock); /* Not caching key here since it won't have all of the fields properly set. */ - return 0; + return rc; } - KBNODE get_pubkeyblock( u32 *keyid ) { @@ -496,7 +521,7 @@ get_seckey( PKT_secret_key *sk, u32 *keyid ) * check and does not tell us whether the secret key is valid. It * merely tells other whether there is some secret key. * Returns: 0 := key is available - * GPG_ERR_NO_SECKEY := not availabe + * G10ERR_NO_SECKEY := not availabe */ int seckey_available( u32 *keyid ) @@ -506,7 +531,7 @@ seckey_available( u32 *keyid ) rc = keydb_search_kid (hd, keyid); if ( rc == -1 ) - rc = GPG_ERR_NO_SECKEY; + rc = G10ERR_NO_SECKEY; keydb_release (hd); return rc; } @@ -579,11 +604,13 @@ classify_user_id( const char *name, KEYDB_SEARCH_DESC *desc ) case 0: /* empty string is an error */ return 0; +#if 0 case '.': /* an email address, compare from end */ mode = KEYDB_SEARCH_MODE_MAILEND; s++; desc->u.name = s; break; +#endif case '<': /* an email address */ mode = KEYDB_SEARCH_MODE_MAIL; @@ -608,11 +635,13 @@ classify_user_id( const char *name, KEYDB_SEARCH_DESC *desc ) desc->u.name = s; break; +#if 0 case '+': /* compare individual words */ mode = KEYDB_SEARCH_MODE_WORDS; s++; desc->u.name = s; break; +#endif case '#': /* local user id */ return 0; /* This is now obsolete and van't not be used anymore*/ @@ -653,7 +682,7 @@ classify_user_id( const char *name, KEYDB_SEARCH_DESC *desc ) } /* check if a hexadecimal number is terminated by EOS or blank */ - if (hexlength && s[hexlength] && !spacep (s+hexlength)) { + if (hexlength && s[hexlength] && !spacep(s+hexlength)) { if (hexprefix) /* a "0x" prefix without correct */ return 0; /* termination is an error */ else /* The first chars looked like */ @@ -728,39 +757,60 @@ classify_user_id( const char *name, KEYDB_SEARCH_DESC *desc ) static int -skip_disabled(void *dummy,u32 *keyid) +skip_unusable(void *dummy,u32 *keyid,PKT_user_id *uid) { - int rc,disabled=0; - PKT_public_key *pk=xcalloc (1,sizeof(PKT_public_key)); + int unusable=0; + KBNODE keyblock; - rc = get_pubkey(pk, keyid); - if(rc) + keyblock=get_pubkeyblock(keyid); + if(!keyblock) { - log_error("error checking disabled status of %08lX: %s\n", - (ulong)keyid[1],gpg_strerror (rc)); + log_error("error checking usability status of %s\n",keystr(keyid)); goto leave; } - - disabled=pk_is_disabled(pk); + + /* Is the user ID in question revoked/expired? */ + if(uid) + { + KBNODE node; + + for(node=keyblock;node;node=node->next) + { + if(node->pkt->pkttype==PKT_USER_ID) + { + if(cmp_user_ids(uid,node->pkt->pkt.user_id)==0 + && (node->pkt->pkt.user_id->is_revoked + || node->pkt->pkt.user_id->is_expired)) + { + unusable=1; + break; + } + } + } + } + + if(!unusable) + unusable=pk_is_disabled(keyblock->pkt->pkt.public_key); leave: - free_public_key(pk); - return disabled; + release_kbnode(keyblock); + return unusable; } /**************** * Try to get the pubkey by the userid. This function looks for the - * first pubkey certificate which has the given name in a user_id. - * if pk/sk has the pubkey algo set, the function will only return - * a pubkey with that algo. - * The caller should provide storage for either the pk or the sk. - * If ret_kb is not NULL the function will return the keyblock there. + * first pubkey certificate which has the given name in a user_id. if + * pk/sk has the pubkey algo set, the function will only return a + * pubkey with that algo. If namelist is NULL, the first key is + * returned. The caller should provide storage for either the pk or + * the sk. If ret_kb is not NULL the function will return the + * keyblock there. */ static int key_byname( GETKEY_CTX *retctx, STRLIST namelist, PKT_public_key *pk, PKT_secret_key *sk, - int secmode, int include_disabled, + int secmode, int include_unusable, KBNODE *ret_kb, KEYDB_HANDLE *ret_kdbhd ) { int rc = 0; @@ -777,29 +827,43 @@ key_byname( GETKEY_CTX *retctx, STRLIST namelist, if (ret_kdbhd) *ret_kdbhd = NULL; - /* build the search context */ - for(n=0, r=namelist; r; r = r->next ) - n++; - ctx = xcalloc (1,sizeof *ctx + (n-1)*sizeof ctx->items ); - ctx->nitems = n; + if(!namelist) + { + ctx = xmalloc_clear (sizeof *ctx); + ctx->nitems = 1; + ctx->items[0].mode=KEYDB_SEARCH_MODE_FIRST; + if(!include_unusable) + ctx->items[0].skipfnc=skip_unusable; + } + else + { + /* build the search context */ + for(n=0, r=namelist; r; r = r->next ) + n++; - for(n=0, r=namelist; r; r = r->next, n++ ) { - classify_user_id (r->d, &ctx->items[n]); + ctx = xmalloc_clear (sizeof *ctx + (n-1)*sizeof ctx->items ); + ctx->nitems = n; + + for(n=0, r=namelist; r; r = r->next, n++ ) + { + classify_user_id (r->d, &ctx->items[n]); - if (ctx->items[n].exact) - ctx->exact = 1; - if (!ctx->items[n].mode) { - xfree (ctx); - return GPG_ERR_INV_USER_ID; - } - if(!include_disabled - && ctx->items[n].mode!=KEYDB_SEARCH_MODE_SHORT_KID - && ctx->items[n].mode!=KEYDB_SEARCH_MODE_LONG_KID - && ctx->items[n].mode!=KEYDB_SEARCH_MODE_FPR16 - && ctx->items[n].mode!=KEYDB_SEARCH_MODE_FPR20 - && ctx->items[n].mode!=KEYDB_SEARCH_MODE_FPR) - ctx->items[n].skipfnc=skip_disabled; - } + if (ctx->items[n].exact) + ctx->exact = 1; + if (!ctx->items[n].mode) + { + xfree (ctx); + return G10ERR_INV_USER_ID; + } + if(!include_unusable + && ctx->items[n].mode!=KEYDB_SEARCH_MODE_SHORT_KID + && ctx->items[n].mode!=KEYDB_SEARCH_MODE_LONG_KID + && ctx->items[n].mode!=KEYDB_SEARCH_MODE_FPR16 + && ctx->items[n].mode!=KEYDB_SEARCH_MODE_FPR20 + && ctx->items[n].mode!=KEYDB_SEARCH_MODE_FPR) + ctx->items[n].skipfnc=skip_unusable; + } + } ctx->kr_handle = keydb_new (secmode); if ( !ret_kb ) @@ -841,24 +905,141 @@ key_byname( GETKEY_CTX *retctx, STRLIST namelist, return rc; } -/* - * Find a public key from NAME and returh the keyblock or the key. - * If ret_kdb is not NULL, the KEYDB handle used to locate this keyblock is - * returned and the caller is responsible for closing it. - */ + + +/* Find a public key from NAME and return the keyblock or the key. If + ret_kdb is not NULL, the KEYDB handle used to locate this keyblock + is returned and the caller is responsible for closing it. If a key + was not found and NAME is a valid RFC822 mailbox and PKA retrieval + has been enabled, we try to import the pkea via the PKA + mechanism. */ int get_pubkey_byname (PKT_public_key *pk, const char *name, KBNODE *ret_keyblock, - KEYDB_HANDLE *ret_kdbhd, int include_disabled ) + KEYDB_HANDLE *ret_kdbhd, int include_unusable ) { - int rc; - STRLIST namelist = NULL; + int rc; + STRLIST namelist = NULL; - add_to_strlist( &namelist, name ); - rc = key_byname( NULL, namelist, pk, NULL, 0, - include_disabled, ret_keyblock, ret_kdbhd); - free_strlist( namelist ); - return rc; + add_to_strlist( &namelist, name ); + + rc = key_byname( NULL, namelist, pk, NULL, 0, + include_unusable, ret_keyblock, ret_kdbhd); + + /* If the requested name resembles a valid mailbox and automatic + retrieval has been enabled, we try to import the key. */ + + if (rc == G10ERR_NO_PUBKEY && is_valid_mailbox(name)) + { + struct akl *akl; + + for(akl=opt.auto_key_locate;akl;akl=akl->next) + { + unsigned char *fpr; + size_t fpr_len; + + switch(akl->type) + { + case AKL_CERT: + glo_ctrl.in_auto_key_retrieve++; + rc=keyserver_import_cert(name,&fpr,&fpr_len); + glo_ctrl.in_auto_key_retrieve--; + + if(rc==0) + log_info(_("automatically retrieved `%s' via %s\n"), + name,"DNS CERT"); + break; + + case AKL_PKA: + glo_ctrl.in_auto_key_retrieve++; + rc=keyserver_import_pka(name,&fpr,&fpr_len); + glo_ctrl.in_auto_key_retrieve--; + + if(rc==0) + log_info(_("automatically retrieved `%s' via %s\n"), + name,"PKA"); + break; + + case AKL_LDAP: + glo_ctrl.in_auto_key_retrieve++; + rc=keyserver_import_ldap(name,&fpr,&fpr_len); + glo_ctrl.in_auto_key_retrieve--; + + if(rc==0) + log_info(_("automatically retrieved `%s' via %s\n"), + name,"LDAP"); + break; + + case AKL_KEYSERVER: + /* Strictly speaking, we don't need to only use a valid + mailbox for the getname search, but it helps cut down + on the problem of searching for something like "john" + and getting a whole lot of keys back. */ + if(opt.keyserver) + { + glo_ctrl.in_auto_key_retrieve++; + rc=keyserver_import_name(name,&fpr,&fpr_len,opt.keyserver); + glo_ctrl.in_auto_key_retrieve--; + + if(rc==0) + log_info(_("automatically retrieved `%s' via %s\n"), + name,opt.keyserver->uri); + } + break; + + case AKL_SPEC: + { + struct keyserver_spec *keyserver; + + keyserver=keyserver_match(akl->spec); + glo_ctrl.in_auto_key_retrieve++; + rc=keyserver_import_name(name,&fpr,&fpr_len,keyserver); + glo_ctrl.in_auto_key_retrieve--; + + if(rc==0) + log_info(_("automatically retrieved `%s' via %s\n"), + name,akl->spec->uri); + } + break; + } + + /* Use the fingerprint of the key that we actually fetched. + This helps prevent problems where the key that we fetched + doesn't have the same name that we used to fetch it. In + the case of CERT and PKA, this is an actual security + requirement as the URL might point to a key put in by an + attacker. By forcing the use of the fingerprint, we + won't use the attacker's key here. */ + if(rc==0 && fpr) + { + int i; + char fpr_string[MAX_FINGERPRINT_LEN*2+1]; + + assert(fpr_len<=MAX_FINGERPRINT_LEN); + + free_strlist(namelist); + namelist=NULL; + + for(i=0;i<fpr_len;i++) + sprintf(fpr_string+2*i,"%02X",fpr[i]); + + if(opt.verbose) + log_info("auto-key-locate found fingerprint %s\n",fpr_string); + + add_to_strlist( &namelist, fpr_string ); + + xfree(fpr); + } + + rc = key_byname( NULL, namelist, pk, NULL, 0, + include_unusable, ret_keyblock, ret_kdbhd); + if(rc!=G10ERR_NO_PUBKEY) + break; + } + } + + free_strlist( namelist ); + return rc; } int @@ -880,7 +1061,6 @@ get_pubkey_next( GETKEY_CTX ctx, PKT_public_key *pk, KBNODE *ret_keyblock ) return rc; } - void get_pubkey_end( GETKEY_CTX ctx ) { @@ -888,17 +1068,15 @@ get_pubkey_end( GETKEY_CTX ctx ) memset (&ctx->kbpos, 0, sizeof ctx->kbpos); keydb_release (ctx->kr_handle); if( !ctx->not_allocated ) - xfree ( ctx ); + xfree( ctx ); } } - - /**************** * Search for a key with the given fingerprint. * FIXME: - * We should replace this with the _byname function. This can be done + * We should replace this with the _byname function. Thiscsan be done * by creating a userID conforming to the unified fingerprint style. */ int @@ -926,7 +1104,7 @@ get_pubkey_byfprint( PKT_public_key *pk, get_pubkey_end( &ctx ); } else - rc = GPG_ERR_GENERAL; /* Oops */ + rc = G10ERR_GENERAL; /* Oops */ return rc; } @@ -956,14 +1134,14 @@ get_pubkey_byfprint_fast (PKT_public_key *pk, if (rc == -1) { keydb_release (hd); - return GPG_ERR_NO_PUBKEY; + return G10ERR_NO_PUBKEY; } rc = keydb_get_keyblock (hd, &keyblock); keydb_release (hd); if (rc) { - log_error ("keydb_get_keyblock failed: %s\n", gpg_strerror (rc)); - return GPG_ERR_NO_PUBKEY; + log_error ("keydb_get_keyblock failed: %s\n", g10_errstr(rc)); + return G10ERR_NO_PUBKEY; } assert ( keyblock->pkt->pkttype == PKT_PUBLIC_KEY @@ -1002,7 +1180,7 @@ get_keyblock_byfprint( KBNODE *ret_keyblock, const byte *fprint, get_pubkey_end( &ctx ); } else - rc = GPG_ERR_GENERAL; /* Oops */ + rc = G10ERR_GENERAL; /* Oops */ return rc; } @@ -1014,44 +1192,31 @@ get_keyblock_byfprint( KBNODE *ret_keyblock, const byte *fprint, */ static int get_seckey_byname2( GETKEY_CTX *retctx, - PKT_secret_key *sk, const char *name, int unprotect, - KBNODE *retblock ) + PKT_secret_key *sk, const char *name, int unprotect, + KBNODE *retblock ) { - STRLIST namelist = NULL; - int rc; + STRLIST namelist = NULL; + int rc,include_unusable=1; - if( !name && opt.def_secret_key && *opt.def_secret_key ) { - add_to_strlist( &namelist, opt.def_secret_key ); - rc = key_byname( retctx, namelist, NULL, sk, 1, 1, retblock, NULL ); - } - else if( !name ) { /* use the first one as default key */ - struct getkey_ctx_s ctx; - KBNODE kb = NULL; + /* If we have no name, try to use the default secret key. If we + have no default, we'll use the first usable one. */ - assert (!retctx ); /* do we need this at all */ - assert (!retblock); - memset( &ctx, 0, sizeof ctx ); - ctx.not_allocated = 1; - ctx.kr_handle = keydb_new (1); - ctx.nitems = 1; - ctx.items[0].mode = KEYDB_SEARCH_MODE_FIRST; - rc = lookup( &ctx, &kb, 1 ); - if (!rc && sk ) - sk_from_block ( &ctx, sk, kb ); - release_kbnode ( kb ); - get_seckey_end( &ctx ); - } - else { - add_to_strlist( &namelist, name ); - rc = key_byname( retctx, namelist, NULL, sk, 1, 1, retblock, NULL ); - } + if( !name && opt.def_secret_key && *opt.def_secret_key ) + add_to_strlist( &namelist, opt.def_secret_key ); + else if(name) + add_to_strlist( &namelist, name ); + else + include_unusable=0; - free_strlist( namelist ); + rc = key_byname( retctx, namelist, NULL, sk, 1, include_unusable, + retblock, NULL ); - if( !rc && unprotect ) - rc = check_secret_key( sk, 0 ); + free_strlist( namelist ); - return rc; + if( !rc && unprotect ) + rc = check_secret_key( sk, 0 ); + + return rc; } int @@ -1117,13 +1282,41 @@ get_seckey_byfprint( PKT_secret_key *sk, if (!rc && sk ) sk_from_block ( &ctx, sk, kb ); release_kbnode ( kb ); - get_pubkey_end( &ctx ); + get_seckey_end( &ctx ); } else - rc = GPG_ERR_GENERAL; /* Oops */ + rc = G10ERR_GENERAL; /* Oops */ return rc; } + +/* Search for a secret key with the given fingerprint and return the + complete keyblock which may have more than only this key. */ +int +get_seckeyblock_byfprint (KBNODE *ret_keyblock, const byte *fprint, + size_t fprint_len ) +{ + int rc; + struct getkey_ctx_s ctx; + + if (fprint_len != 20 && fprint_len == 16) + return G10ERR_GENERAL; /* Oops */ + + memset (&ctx, 0, sizeof ctx); + ctx.not_allocated = 1; + ctx.kr_handle = keydb_new (1); + ctx.nitems = 1; + ctx.items[0].mode = (fprint_len==16 + ? KEYDB_SEARCH_MODE_FPR16 + : KEYDB_SEARCH_MODE_FPR20); + memcpy (ctx.items[0].u.fpr, fprint, fprint_len); + rc = lookup (&ctx, ret_keyblock, 1); + get_seckey_end (&ctx); + + return rc; +} + + /************************************************ ************* Merging stuff ******************** @@ -1220,6 +1413,59 @@ merge_keys_and_selfsig( KBNODE keyblock ) } } +static int +parse_key_usage(PKT_signature *sig) +{ + int key_usage=0; + const byte *p; + size_t n; + byte flags; + + p=parse_sig_subpkt(sig->hashed,SIGSUBPKT_KEY_FLAGS,&n); + if(p && n) + { + /* first octet of the keyflags */ + flags=*p; + + if(flags & 1) + { + key_usage |= PUBKEY_USAGE_CERT; + flags&=~1; + } + + if(flags & 2) + { + key_usage |= PUBKEY_USAGE_SIG; + flags&=~2; + } + + /* We do not distinguish between encrypting communications and + encrypting storage. */ + if(flags & (0x04|0x08)) + { + key_usage |= PUBKEY_USAGE_ENC; + flags&=~(0x04|0x08); + } + + if(flags & 0x20) + { + key_usage |= PUBKEY_USAGE_AUTH; + flags&=~0x20; + } + + if(flags) + key_usage |= PUBKEY_USAGE_UNKNOWN; + } + + /* We set PUBKEY_USAGE_UNKNOWN to indicate that this key has a + capability that we do not handle. This serves to distinguish + between a zero key usage which we handle as the default + capabilities for that algorithm, and a usage that we do not + handle. */ + + return key_usage; +} + /* * Apply information from SIGNODE (which is the valid self-signature * associated with that UID) to the UIDNODE: @@ -1238,32 +1484,28 @@ fixup_uidnode ( KBNODE uidnode, KBNODE signode, u32 keycreated ) const byte *p, *sym, *hash, *zip; size_t n, nsym, nhash, nzip; + sig->flags.chosen_selfsig = 1; /* we chose this one */ uid->created = 0; /* not created == invalid */ if ( IS_UID_REV ( sig ) ) { uid->is_revoked = 1; return; /* has been revoked */ } + uid->expiredate = sig->expiredate; + + if(sig->flags.expired) + { + uid->is_expired = 1; + return; /* has expired */ + } + uid->created = sig->timestamp; /* this one is okay */ uid->selfsigversion = sig->version; /* If we got this far, it's not expired :) */ uid->is_expired = 0; - uid->expiredate = sig->expiredate; /* store the key flags in the helper variable for later processing */ - uid->help_key_usage = 0; - p = parse_sig_subpkt (sig->hashed, SIGSUBPKT_KEY_FLAGS, &n ); - if ( p && n ) { - /* first octet of the keyflags */ - if ( (*p & 0x03) ) - uid->help_key_usage |= PUBKEY_USAGE_SIG; - if ( (*p & 0x0c) ) - uid->help_key_usage |= PUBKEY_USAGE_ENC; - /* Note: we do not set the CERT flag here because it can be assumed - * that thre is no real policy to set it. */ - if ( (*p & 0x20) ) - uid->help_key_usage |= PUBKEY_USAGE_AUTH; - } + uid->help_key_usage=parse_key_usage(sig); /* ditto or the key expiration */ uid->help_key_expire = 0; @@ -1318,20 +1560,29 @@ fixup_uidnode ( KBNODE uidnode, KBNODE signode, u32 keycreated ) } /* see whether we have the MDC feature */ - uid->mdc_feature = 0; + uid->flags.mdc = 0; p = parse_sig_subpkt (sig->hashed, SIGSUBPKT_FEATURES, &n); if (p && n && (p[0] & 0x01)) - uid->mdc_feature = 1; + uid->flags.mdc = 1; /* and the keyserver modify flag */ - uid->ks_modify = 1; + uid->flags.ks_modify = 1; p = parse_sig_subpkt (sig->hashed, SIGSUBPKT_KS_FLAGS, &n); if (p && n && (p[0] & 0x80)) - uid->ks_modify = 0; + uid->flags.ks_modify = 0; +} + +static void +sig_to_revoke_info(PKT_signature *sig,struct revoke_info *rinfo) +{ + rinfo->date = sig->timestamp; + rinfo->algo = sig->pubkey_algo; + rinfo->keyid[0] = sig->keyid[0]; + rinfo->keyid[1] = sig->keyid[1]; } static void -merge_selfsigs_main( KBNODE keyblock, int *r_revoked ) +merge_selfsigs_main(KBNODE keyblock, int *r_revoked, struct revoke_info *rinfo) { PKT_public_key *pk = NULL; KBNODE k; @@ -1346,6 +1597,8 @@ merge_selfsigs_main( KBNODE keyblock, int *r_revoked ) byte sigversion = 0; *r_revoked = 0; + memset(rinfo,0,sizeof(*rinfo)); + if ( keyblock->pkt->pkttype != PKT_PUBLIC_KEY ) BUG (); pk = keyblock->pkt->pkt.public_key; @@ -1368,7 +1621,7 @@ merge_selfsigs_main( KBNODE keyblock, int *r_revoked ) */ /* In case this key was already merged */ - xfree (pk->revkey); + xfree(pk->revkey); pk->revkey=NULL; pk->numrevkeys=0; @@ -1391,6 +1644,7 @@ merge_selfsigs_main( KBNODE keyblock, int *r_revoked ) * that key. */ *r_revoked = 1; + sig_to_revoke_info(sig,rinfo); } else if ( IS_KEY_SIG (sig) ) { /* Add any revocation keys onto the pk. This is @@ -1459,42 +1713,34 @@ merge_selfsigs_main( KBNODE keyblock, int *r_revoked ) pk->numrevkeys*sizeof(struct revocation_key)); } - if ( signode ) { + 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, SIGSUBPKT_KEY_FLAGS, &n ); - if ( p && n ) { - /* first octet of the keyflags */ - if ( (*p & 0x03) ) - key_usage |= PUBKEY_USAGE_SIG; - if ( (*p & 0x0c) ) - key_usage |= PUBKEY_USAGE_ENC; - if ( (*p & 0x20) ) - key_usage |= PUBKEY_USAGE_AUTH; - } + + key_usage=parse_key_usage(sig); p = parse_sig_subpkt (sig->hashed, SIGSUBPKT_KEY_EXPIRE, NULL); - if ( p ) { - key_expire = keytimestamp + buffer_to_u32(p); - key_expire_seen = 1; - } + if ( p ) + { + key_expire = keytimestamp + buffer_to_u32(p); + key_expire_seen = 1; + } /* mark that key as valid: one direct key signature should * render a key as valid */ pk->is_valid = 1; - } + } /* pass 1.5: look for key revocation signatures that were not made by the key (i.e. did a revocation key issue a revocation for us?). Only bother to do this if there is a revocation key in - the first place. */ + the first place and we're not revoked already. */ - if(pk->revkey) + if(!*r_revoked && pk->revkey) for(k=keyblock; k && k->pkt->pkttype != PKT_USER_ID; k = k->next ) { if ( k->pkt->pkttype == PKT_SIGNATURE ) @@ -1504,15 +1750,26 @@ merge_selfsigs_main( KBNODE keyblock, int *r_revoked ) if(IS_KEY_REV(sig) && (sig->keyid[0]!=kid[0] || sig->keyid[1]!=kid[1])) { - /* Failure here means the sig did not verify, is was - not issued by a revocation key, or a revocation - key loop was broken. */ + int rc=check_revocation_keys(pk,sig); + if(rc==0) + { + *r_revoked=2; + sig_to_revoke_info(sig,rinfo); + /* don't continue checking since we can't be any + more revoked than this */ + break; + } + else if(rc==G10ERR_NO_PUBKEY) + pk->maybe_revoked=1; - if(check_revocation_keys(pk,sig)==0) - *r_revoked=1; + /* A failure here means the sig did not verify, was + not issued by a revocation key, or a revocation + key loop was broken. If a revocation key isn't + findable, however, the key might be revoked and + we don't know it. */ - /* In the future handle subkey and cert revocations? - PGP doesn't, but it's in 2440. */ + /* TODO: In the future handle subkey and cert + revocations? PGP doesn't, but it's in 2440. */ } } } @@ -1537,7 +1794,8 @@ merge_selfsigs_main( KBNODE keyblock, int *r_revoked ) if ( check_key_signature( keyblock, k, NULL ) ) ; /* signature did not verify */ else if ( (IS_UID_SIG (sig) || IS_UID_REV (sig)) - && sig->timestamp >= sigdate ) { + && sig->timestamp >= sigdate ) + { /* Note: we allow to invalidate cert revocations * by a newer signature. An attacker can't use this * because a key should be revoced with a key revocation. @@ -1546,20 +1804,13 @@ merge_selfsigs_main( KBNODE keyblock, int *r_revoked ) * the same email address may become valid again (hired, * fired, hired again). */ - if(sig->flags.expired) { - /* Expired uids don't get to be primary unless - they are the only uid there is. */ - uidnode->pkt->pkt.user_id->is_primary=0; - uidnode->pkt->pkt.user_id->is_expired=1; - uidnode->pkt->pkt.user_id->expiredate=sig->expiredate; - } - else { - sigdate = sig->timestamp; - signode = k; - if( sig->version > sigversion ) - sigversion = sig->version; - } - } + + sigdate = sig->timestamp; + signode = k; + signode->pkt->pkt.signature->flags.chosen_selfsig=0; + if( sig->version > sigversion ) + sigversion = sig->version; + } } } } @@ -1573,10 +1824,8 @@ merge_selfsigs_main( KBNODE keyblock, int *r_revoked ) if(!pk->is_valid && opt.allow_non_selfsigned_uid) { if(opt.verbose) - log_info(_("Invalid key %08lX made valid by " - "--allow-non-selfsigned-uid\n"), - (ulong)keyid_from_pk(pk,NULL)); - + log_info(_("Invalid key %s made valid by" + " --allow-non-selfsigned-uid\n"),keystr_from_pk(pk)); pk->is_valid = 1; } @@ -1598,7 +1847,7 @@ merge_selfsigs_main( KBNODE keyblock, int *r_revoked ) { PKT_public_key *ultimate_pk; - ultimate_pk=xcalloc (1,sizeof(*ultimate_pk)); + ultimate_pk=xmalloc_clear(sizeof(*ultimate_pk)); /* We don't want to use the full get_pubkey to avoid infinite recursion in certain cases. @@ -1608,7 +1857,7 @@ merge_selfsigs_main( KBNODE keyblock, int *r_revoked ) ultimate trust flag. */ if(get_pubkey_fast(ultimate_pk,sig->keyid)==0 && check_key_signature2(keyblock,k,ultimate_pk, - NULL, NULL, NULL, NULL)==0 + NULL,NULL,NULL,NULL)==0 && get_ownertrust(ultimate_pk)==TRUST_ULTIMATE) { free_public_key(ultimate_pk); @@ -1659,7 +1908,9 @@ merge_selfsigs_main( KBNODE keyblock, int *r_revoked ) if ( x ) /* mask it down to the actual allowed usage */ key_usage &= x; } - pk->pubkey_usage = key_usage; + + /* Whatever happens, it's a primary key, so it can certify. */ + pk->pubkey_usage = key_usage|PUBKEY_USAGE_CERT; if ( !key_expire_seen ) { /* find the latest valid user ID with a key expiration set @@ -1799,7 +2050,6 @@ merge_selfsigs_subkey( KBNODE keyblock, KBNODE subnode ) u32 keytimestamp = 0; u32 key_expire = 0; const byte *p; - size_t n; if ( subnode->pkt->pkttype != PKT_PUBLIC_SUBKEY ) BUG (); @@ -1834,47 +2084,47 @@ merge_selfsigs_subkey( KBNODE keyblock, KBNODE subnode ) problem is in the distribution. Plus, PGP (7) does this the same way. */ subpk->is_revoked = 1; + sig_to_revoke_info(sig,&subpk->revoked); /* although we could stop now, we continue to * figure out other information like the old expiration * time */ } - else if ( IS_SUBKEY_SIG (sig) && sig->timestamp >= sigdate ) { + else if ( IS_SUBKEY_SIG (sig) && sig->timestamp >= sigdate ) + { if(sig->flags.expired) - ; /* signature has expired - ignore it */ - else { + ; /* signature has expired - ignore it */ + else + { sigdate = sig->timestamp; signode = k; - } - } + signode->pkt->pkt.signature->flags.chosen_selfsig=0; + } + } } } } - if ( !signode ) { - return; /* no valid key binding */ - } + /* no valid key binding */ + if ( !signode ) + return; - subpk->is_valid = 1; sig = signode->pkt->pkt.signature; - - p = parse_sig_subpkt (sig->hashed, SIGSUBPKT_KEY_FLAGS, &n ); - if ( p && n ) { - /* first octet of the keyflags */ - if ( (*p & 0x03) ) - key_usage |= PUBKEY_USAGE_SIG; - if ( (*p & 0x0c) ) - key_usage |= PUBKEY_USAGE_ENC; - if ( (*p & 0x20) ) - key_usage |= PUBKEY_USAGE_AUTH; - } - if ( !key_usage ) { /* no key flags at all: get it from the algo */ + sig->flags.chosen_selfsig=1; /* so we know which selfsig we chose later */ + + key_usage=parse_key_usage(sig); + 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 */ + } + 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; - } + key_usage &= x; + } + subpk->pubkey_usage = key_usage; p = parse_sig_subpkt (sig->hashed, SIGSUBPKT_KEY_EXPIRE, NULL); @@ -1884,8 +2134,56 @@ merge_selfsigs_subkey( KBNODE keyblock, KBNODE subnode ) key_expire = 0; subpk->has_expired = key_expire >= curtime? 0 : key_expire; subpk->expiredate = key_expire; -} + /* algo doesn't exist */ + if(openpgp_pk_test_algo(subpk->pubkey_algo)) + return; + + subpk->is_valid = 1; + + /* Find the first 0x19 embedded signature on our self-sig. */ + if(subpk->backsig==0) + { + int seq=0; + size_t n; + + /* We do this while() since there may be other embedded + signatures in the future. We only want 0x19 here. */ + while((p=enum_sig_subpkt(sig->hashed, + SIGSUBPKT_SIGNATURE,&n,&seq,NULL))) + if(n>3 && ((p[0]==3 && p[2]==0x19) || (p[0]==4 && p[1]==0x19))) + break; + + if(p==NULL) + { + seq=0; + /* It is safe to have this in the unhashed area since the + 0x19 is located on the selfsig for convenience, not + security. */ + while((p=enum_sig_subpkt(sig->unhashed,SIGSUBPKT_SIGNATURE, + &n,&seq,NULL))) + if(n>3 && ((p[0]==3 && p[2]==0x19) || (p[0]==4 && p[1]==0x19))) + break; + } + + if(p) + { + PKT_signature *backsig=xmalloc_clear(sizeof(PKT_signature)); + IOBUF backsig_buf=iobuf_temp_with_content(p,n); + + if(parse_signature(backsig_buf,PKT_SIGNATURE,n,backsig)==0) + { + if(check_backsig(mainpk,subpk,backsig)==0) + subpk->backsig=2; + else + subpk->backsig=1; + } + + iobuf_close(backsig_buf); + free_seckey_enc(backsig); + } + } +} /* @@ -1905,6 +2203,7 @@ merge_selfsigs( KBNODE keyblock ) { KBNODE k; int revoked; + struct revoke_info rinfo; PKT_public_key *main_pk; prefitem_t *prefs; int mdc_feature; @@ -1921,7 +2220,7 @@ merge_selfsigs( KBNODE keyblock ) BUG (); } - merge_selfsigs_main ( keyblock, &revoked ); + merge_selfsigs_main ( keyblock, &revoked, &rinfo ); /* now merge in the data from each of the subkeys */ for(k=keyblock; k; k = k->next ) { @@ -1941,8 +2240,11 @@ merge_selfsigs( KBNODE keyblock ) PKT_public_key *pk = k->pkt->pkt.public_key; if(!main_pk->is_valid) pk->is_valid = 0; - if(revoked) - pk->is_revoked = 1; + if(revoked && !pk->is_revoked) + { + pk->is_revoked = revoked; + memcpy(&pk->revoked,&rinfo,sizeof(rinfo)); + } if(main_pk->has_expired) pk->has_expired = main_pk->has_expired; } @@ -1966,7 +2268,7 @@ merge_selfsigs( KBNODE keyblock ) && !k->pkt->pkt.user_id->attrib_data && k->pkt->pkt.user_id->is_primary) { prefs = k->pkt->pkt.user_id->prefs; - mdc_feature = k->pkt->pkt.user_id->mdc_feature; + mdc_feature = k->pkt->pkt.user_id->flags.mdc; break; } } @@ -2076,14 +2378,14 @@ premerge_public_with_secret ( KBNODE pubblock, KBNODE secblock ) KBNODE next, ll; if (opt.verbose) - log_info ( _("no secret subkey " - "for public subkey %08lX - ignoring\n"), - (ulong)keyid_from_pk (pk,NULL) ); + log_info (_("no secret subkey" + " for public subkey %s - ignoring\n"), + keystr_from_pk (pk)); /* we have to remove the subkey in this case */ assert ( last ); /* find the next subkey */ for (next=pub->next,ll=pub; - next && pub->pkt->pkttype != PKT_PUBLIC_SUBKEY; + next && next->pkt->pkttype != PKT_PUBLIC_SUBKEY; ll = next, next = next->next ) ; /* make new link */ @@ -2141,7 +2443,7 @@ finish_lookup (GETKEY_CTX ctx) KBNODE k; KBNODE foundk = NULL; PKT_user_id *foundu = NULL; -#define USAGE_MASK (PUBKEY_USAGE_SIG|PUBKEY_USAGE_ENC) +#define USAGE_MASK (PUBKEY_USAGE_SIG|PUBKEY_USAGE_ENC|PUBKEY_USAGE_CERT) unsigned int req_usage = ( ctx->req_usage & USAGE_MASK ); /* Request the primary if we're certifying another key, and also if signing data while --pgp6 or --pgp7 is on since pgp 6 and 7 @@ -2303,12 +2605,14 @@ finish_lookup (GETKEY_CTX ctx) ctx->found_key = latest_key; - if (latest_key != keyblock && opt.verbose) { - 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) ); - } + if (latest_key != keyblock && opt.verbose) + { + char *tempkeystr= + xstrdup(keystr_from_pk(latest_key->pkt->pkt.public_key)); + log_info(_("using subkey %s instead of primary key %s\n"), + tempkeystr, keystr_from_pk(keyblock->pkt->pkt.public_key)); + xfree(tempkeystr); + } cache_user_id( keyblock ); @@ -2333,7 +2637,7 @@ lookup( GETKEY_CTX ctx, KBNODE *ret_keyblock, int secmode ) rc = keydb_get_keyblock (ctx->kr_handle, &ctx->keyblock); if (rc) { - log_error ("keydb_get_keyblock failed: %s\n", gpg_strerror (rc)); + log_error ("keydb_get_keyblock failed: %s\n", g10_errstr(rc)); rc = 0; goto skip; } @@ -2349,12 +2653,13 @@ lookup( GETKEY_CTX ctx, KBNODE *ret_keyblock, int secmode ) keyid_from_sk (k->pkt->pkt.secret_key, aki); k = get_pubkeyblock (aki); - if( !k ) { + if( !k ) + { if (!opt.quiet) - log_info(_("key %08lX: secret key without public key " - "- skipped\n"), (ulong)aki[1] ); + log_info(_("key %s: secret key without public key" + " - skipped\n"), keystr(aki)); goto skip; - } + } secblock = ctx->keyblock; ctx->keyblock = k; @@ -2390,16 +2695,16 @@ lookup( GETKEY_CTX ctx, KBNODE *ret_keyblock, int secmode ) found: if( rc && rc != -1 ) - log_error("keydb_search failed: %s\n", gpg_strerror (rc)); + log_error("keydb_search failed: %s\n", g10_errstr(rc)); if( !rc ) { *ret_keyblock = ctx->keyblock; /* return the keyblock */ ctx->keyblock = NULL; } else if (rc == -1 && no_suitable_key) - rc = secmode ? GPG_ERR_UNUSABLE_SECKEY : GPG_ERR_UNUSABLE_PUBKEY; + rc = secmode ? G10ERR_UNU_SECKEY : G10ERR_UNU_PUBKEY; else if( rc == -1 ) - rc = secmode ? GPG_ERR_NO_SECKEY : GPG_ERR_NO_PUBKEY; + rc = secmode ? G10ERR_NO_SECKEY : G10ERR_NO_PUBKEY; if ( secmode ) { release_kbnode( secblock ); @@ -2449,7 +2754,7 @@ enum_secret_keys( void **context, PKT_secret_key *sk, if( !c ) { /* make a new context */ - c = xcalloc (1, sizeof *c ); + c = xmalloc_clear( sizeof *c ); *context = c; c->hd = keydb_new (1); c->first = 1; @@ -2460,7 +2765,7 @@ enum_secret_keys( void **context, PKT_secret_key *sk, if( !sk ) { /* free the context */ keydb_release (c->hd); release_kbnode (c->keyblock); - xfree ( c ); + xfree( c ); *context = NULL; return 0; } @@ -2507,43 +2812,44 @@ enum_secret_keys( void **context, PKT_secret_key *sk, /**************** * Return a string with a printable representation of the user_id. - * this string must be freed by m_free. + * this string must be freed by xfree. */ char* get_user_id_string( u32 *keyid ) { - user_id_db_t r; - char *p; - int pass=0; - /* try it two times; second pass reads from key resources */ - do { - for(r=user_id_db; r; r = r->next ) { - keyid_list_t a; - for (a=r->keyids; a; a= a->next ) { - if( a->keyid[0] == keyid[0] && a->keyid[1] == keyid[1] ) { - p = xmalloc ( r->len + 10 ); - sprintf(p, "%08lX %.*s", - (ulong)keyid[1], r->len, r->name ); - return p; - } - } + user_id_db_t r; + char *p; + int pass=0; + /* try it two times; second pass reads from key resources */ + do + { + for(r=user_id_db; r; r = r->next ) + { + keyid_list_t a; + for (a=r->keyids; a; a= a->next ) + { + if( a->keyid[0] == keyid[0] && a->keyid[1] == keyid[1] ) + { + p = xmalloc( keystrlen() + 1 + r->len + 1 ); + sprintf(p, "%s %.*s", keystr(keyid), r->len, r->name ); + return p; + } + } } } while( ++pass < 2 && !get_pubkey( NULL, keyid ) ); - p = xmalloc ( 15 ); - sprintf(p, "%08lX [?]", (ulong)keyid[1] ); - return p; + p = xmalloc( keystrlen() + 5 ); + sprintf(p, "%s [?]", keystr(keyid)); + return p; } char* -get_user_id_string_printable ( u32 *keyid ) +get_user_id_string_native ( u32 *keyid ) { - char *p = get_user_id_string( keyid ); - char *p2 = utf8_to_native( p, strlen(p), 0 ); - xfree (p); - p = make_printable_string (p2, strlen (p2), 0); - xfree (p2); - return p; + char *p = get_user_id_string( keyid ); + char *p2 = utf8_to_native( p, strlen(p), 0 ); + xfree(p); + return p2; } @@ -2559,7 +2865,7 @@ get_long_user_id_string( u32 *keyid ) keyid_list_t a; for (a=r->keyids; a; a= a->next ) { if( a->keyid[0] == keyid[0] && a->keyid[1] == keyid[1] ) { - p = xmalloc ( r->len + 20 ); + p = xmalloc( r->len + 20 ); sprintf(p, "%08lX%08lX %.*s", (ulong)keyid[0], (ulong)keyid[1], r->len, r->name ); @@ -2568,7 +2874,7 @@ get_long_user_id_string( u32 *keyid ) } } } while( ++pass < 2 && !get_pubkey( NULL, keyid ) ); - p = xmalloc ( 25 ); + p = xmalloc( 25 ); sprintf(p, "%08lX%08lX [?]", (ulong)keyid[0], (ulong)keyid[1] ); return p; } @@ -2586,7 +2892,7 @@ get_user_id( u32 *keyid, size_t *rn ) keyid_list_t a; for (a=r->keyids; a; a= a->next ) { if( a->keyid[0] == keyid[0] && a->keyid[1] == keyid[1] ) { - p = xmalloc ( r->len ); + p = xmalloc( r->len ); memcpy(p, r->name, r->len ); *rn = r->len; return p; @@ -2594,21 +2900,19 @@ get_user_id( u32 *keyid, size_t *rn ) } } } while( ++pass < 2 && !get_pubkey( NULL, keyid ) ); - p = xstrdup ( _("[User id not found]") ); + p = xstrdup( user_id_not_found_utf8 () ); *rn = strlen(p); return p; } char* -get_user_id_printable( u32 *keyid ) +get_user_id_native( u32 *keyid ) { - size_t rn; - char *p = get_user_id( keyid, &rn ); - char *p2 = utf8_to_native( p, rn, 0 ); - xfree (p); - p = make_printable_string (p2, strlen (p2), 0); - xfree (p2); - return p; + size_t rn; + char *p = get_user_id( keyid, &rn ); + char *p2 = utf8_to_native( p, rn, 0 ); + xfree(p); + return p2; } KEYDB_HANDLE @@ -2616,3 +2920,85 @@ get_ctx_handle(GETKEY_CTX ctx) { return ctx->kr_handle; } + +static void +free_akl(struct akl *akl) +{ + if(akl->spec) + free_keyserver_spec(akl->spec); + + xfree(akl); +} + +void +release_akl(void) +{ + while(opt.auto_key_locate) + { + struct akl *akl2=opt.auto_key_locate; + opt.auto_key_locate=opt.auto_key_locate->next; + free_akl(akl2); + } +} + +int +parse_auto_key_locate(char *options) +{ + char *tok; + + while((tok=optsep(&options))) + { + struct akl *akl,*last; + int dupe=0; + + if(tok[0]=='\0') + continue; + + akl=xmalloc_clear(sizeof(*akl)); + + if(ascii_strcasecmp(tok,"ldap")==0) + akl->type=AKL_LDAP; + else if(ascii_strcasecmp(tok,"keyserver")==0) + akl->type=AKL_KEYSERVER; +#ifdef USE_DNS_CERT + else if(ascii_strcasecmp(tok,"cert")==0) + akl->type=AKL_CERT; +#endif +#ifdef USE_DNS_PKA + else if(ascii_strcasecmp(tok,"pka")==0) + akl->type=AKL_PKA; +#endif + else if((akl->spec=parse_keyserver_uri(tok,1,NULL,0))) + akl->type=AKL_SPEC; + else + { + free_akl(akl); + return 0; + } + + /* We must maintain the order the user gave us */ + for(last=opt.auto_key_locate;last && last->next;last=last->next) + { + /* Check for duplicates */ + if(last && last->type==akl->type + && (akl->type!=AKL_SPEC + || (akl->type==AKL_SPEC + && strcmp(last->spec->uri,akl->spec->uri)==0))) + { + dupe=1; + free_akl(akl); + break; + } + } + + if(!dupe) + { + if(last) + last->next=akl; + else + opt.auto_key_locate=akl; + } + } + + return 1; +} diff --git a/g10/global.h b/g10/global.h deleted file mode 100644 index d1c554dce..000000000 --- a/g10/global.h +++ /dev/null @@ -1,31 +0,0 @@ -/* global.h - Local typedefs and constants - * Copyright (C) 2001 Free Software Foundation, Inc. - * - * This file is part of GnuPG. - * - * GnuPG is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * GnuPG is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA - */ - -#ifndef GPG_GLOBAL_H -#define GPG_GLOBAL_H - -#define MAX_FINGERPRINT_LEN 20 - -typedef struct kbnode_struct *KBNODE; -typedef struct keydb_search_desc KEYDB_SEARCH_DESC; - -#include "gpg.h" - -#endif /*GPG_GLOBAL_H*/ @@ -1,6 +1,6 @@ -/* g10.c - The GnuPG utility (main for gpg) - * Copyright (C) 1998,1999,2000,2001,2002,2003 - * 2004 Free Software Foundation, Inc. +/* gpg.c - The GnuPG utility (main for gpg) + * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, + * 2006 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -16,7 +16,8 @@ * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. */ #include <config.h> @@ -33,19 +34,21 @@ #ifdef HAVE_STAT #include <sys/stat.h> /* for stat() */ #endif +#include <fcntl.h> #include <assuan.h> +#ifdef HAVE_W32_SYSTEM +#include <windows.h> +#endif #define INCLUDED_BY_MAIN_MODULE 1 #include "gpg.h" #include "packet.h" -#include "iobuf.h" -#include "memory.h" +#include "../common/iobuf.h" #include "util.h" #include "main.h" #include "options.h" #include "keydb.h" #include "trustdb.h" -#include "mpi.h" #include "cipher.h" #include "filter.h" #include "ttyio.h" @@ -54,15 +57,28 @@ #include "keyserver-internal.h" #include "exec.h" -enum cmd_and_opt_values { aNull = 0, + +#if defined(HAVE_DOSISH_SYSTEM) || defined(__CYGWIN__) +#define MY_O_BINARY O_BINARY +#ifndef S_IRGRP +# define S_IRGRP 0 +# define S_IWGRP 0 +#endif +#else +#define MY_O_BINARY 0 +#endif + + +enum cmd_and_opt_values + { + aNull = 0, oArmor = 'a', aDetachedSign = 'b', aSym = 'c', aDecrypt = 'd', aEncr = 'e', oInteractive = 'i', - aListKeys = 'k', - aListSecretKeys = 'K', + oKOption = 'k', oDryRun = 'n', oOutput = 'o', oQuiet = 'q', @@ -70,42 +86,49 @@ enum cmd_and_opt_values { aNull = 0, oHiddenRecipient = 'R', aSign = 's', oTextmodeShort= 't', - oUser = 'u', + oLocalUser = 'u', oVerbose = 'v', oCompress = 'z', oSetNotation = 'N', + aListSecretKeys = 'K', oBatch = 500, - aGPGConfList, + oMaxOutput, oSigNotation, oCertNotation, oShowNotation, oNoShowNotation, aEncrFiles, - aDecryptFiles, + aEncrSym, + aDecryptFiles, aClearsign, aStore, aKeygen, aSignEncr, + aSignEncrSym, aSignSym, aSignKey, aLSignKey, - aNRSignKey, - aNRLSignKey, + aListConfig, + aGPGConfList, aListPackets, aEditKey, aDeleteKeys, aDeleteSecretKeys, aDeleteSecretAndPublicKeys, + aKMode, + aKModeC, aImport, aFastImport, aVerify, aVerifyFiles, + aListKeys, aListSigs, aSendKeys, aRecvKeys, aSearchKeys, + aRefreshKeys, + aFetchKeys, aExport, - aExportAll, aExportSecret, aExportSecretSub, aCheckKeys, @@ -125,9 +148,7 @@ enum cmd_and_opt_values { aNull = 0, aDeArmor, aEnArmor, aGenRandom, - aPipeMode, aRebuildKeydbCaches, - aRefreshKeys, aCardStatus, aCardEdit, aChangePIN, @@ -136,15 +157,20 @@ enum cmd_and_opt_values { aNull = 0, oNoTextmode, oExpert, oNoExpert, + oDefSigExpire, oAskSigExpire, oNoAskSigExpire, + oDefCertExpire, oAskCertExpire, oNoAskCertExpire, + oDefCertLevel, + oMinCertLevel, + oAskCertLevel, + oNoAskCertLevel, oFingerprint, oWithFingerprint, oAnswerYes, oAnswerNo, - oDefCertCheckLevel, oKeyring, oPrimaryKeyring, oSecretKeyring, @@ -157,16 +183,11 @@ enum cmd_and_opt_values { aNull = 0, oDebug, oDebugLevel, oDebugAll, + oDebugCCIDDriver, oStatusFD, -#ifdef __riscos__ oStatusFile, -#endif /* __riscos__ */ oAttributeFD, -#ifdef __riscos__ oAttributeFile, -#endif /* __riscos__ */ - oSKComments, - oNoSKComments, oEmitVersion, oNoEmitVersion, oCompletesNeeded, @@ -181,22 +202,26 @@ enum cmd_and_opt_values { aNull = 0, oPGP6, oPGP7, oPGP8, + oRFC2440Text, + oNoRFC2440Text, oCipherAlgo, oDigestAlgo, oCertDigestAlgo, oCompressAlgo, + oCompressLevel, + oBZ2CompressLevel, + oBZ2DecompressLowmem, + oPasswd, oPasswdFD, -#ifdef __riscos__ oPasswdFile, -#endif /* __riscos__ */ oCommandFD, -#ifdef __riscos__ oCommandFile, -#endif /* __riscos__ */ oQuickRandom, oNoVerbose, oTrustDBName, oNoSecmemWarn, + oRequireSecmem, + oNoRequireSecmem, oNoPermissionWarn, oNoMDCWarn, oNoArmor, @@ -214,7 +239,6 @@ enum cmd_and_opt_values { aNull = 0, oAlwaysTrust, oTrustModel, oForceOwnertrust, - oEmuChecksumBug, oSetFilename, oForYourEyesOnly, oNoForYourEyesOnly, @@ -225,11 +249,12 @@ enum cmd_and_opt_values { aNull = 0, oNoShowPolicyURL, oSigKeyserverURL, oUseEmbeddedFilename, + oNoUseEmbeddedFilename, oComment, oDefaultComment, oNoComments, - oThrowKeyid, - oNoThrowKeyid, + oThrowKeyids, + oNoThrowKeyids, oShowPhotos, oNoShowPhotos, oPhotoViewer, @@ -245,7 +270,7 @@ enum cmd_and_opt_values { aNull = 0, oS2KDigest, oS2KCipher, oSimpleSKChecksum, - oCharset, + oDisplayCharset, oNotDashEscaped, oEscapeFrom, oNoEscapeFrom, @@ -263,11 +288,8 @@ enum cmd_and_opt_values { aNull = 0, oEncryptTo, oHiddenEncryptTo, oNoEncryptTo, - oLogFile, oLoggerFD, -#ifdef __riscos__ oLoggerFile, -#endif /* __riscos__ */ oUtf8Strings, oNoUtf8Strings, oDisableCipherAlgo, @@ -309,7 +331,6 @@ enum cmd_and_opt_values { aNull = 0, oPersonalCipherPreferences, oPersonalDigestPreferences, oPersonalCompressPreferences, - oEmuMDEncodeBug, oAgentProgram, oDisplay, oTTYname, @@ -317,13 +338,25 @@ enum cmd_and_opt_values { aNull = 0, oLCctype, oLCmessages, oGroup, + oUnGroup, + oNoGroups, oStrict, oNoStrict, oMangleDosFilenames, oNoMangleDosFilenames, - oEnableProgressFilter, + oEnableProgressFilter, oMultifile, -aTest }; + oKeyidFormat, + oExitOnStatusWriteError, + oLimitCardInsertTries, + oRequireCrossCert, + oNoRequireCrossCert, + oAutoKeyLocate, + oNoAutoKeyLocate, + oAllowMultisigVerification, + + oNoop + }; static ARGPARSE_OPTS opts[] = { @@ -331,7 +364,7 @@ static ARGPARSE_OPTS opts[] = { { 300, NULL, 0, N_("@Commands:\n ") }, { aSign, "sign", 256, N_("|[file]|make a signature")}, - { aClearsign, "clearsign", 256, N_("|[file]|make a clear text signature") }, + { aClearsign, "clearsign", 256, N_("|[file]|make a clear text signature")}, { aDetachedSign, "detach-sign", 256, N_("make a detached signature")}, { aEncr, "encrypt", 256, N_("encrypt data")}, { aEncrFiles, "encrypt-files", 256, "@"}, @@ -353,8 +386,6 @@ static ARGPARSE_OPTS opts[] = { N_("remove keys from the secret keyring")}, { aSignKey, "sign-key" ,256, N_("sign a key")}, { aLSignKey, "lsign-key" ,256, N_("sign a key locally")}, - { aNRSignKey, "nrsign-key" ,256, "@"}, - { aNRLSignKey, "nrlsign-key" ,256, "@"}, { aEditKey, "edit-key" ,256, N_("sign or edit a key")}, { aGenRevoke, "gen-revoke",256, N_("generate a revocation certificate")}, { aDesigRevoke, "desig-revoke",256, "@" }, @@ -365,29 +396,32 @@ static ARGPARSE_OPTS opts[] = { N_("search for keys on a key server") }, { aRefreshKeys, "refresh-keys", 256, N_("update all keys from a keyserver")}, - { aExportAll, "export-all" , 256, "@" }, + { aFetchKeys, "fetch-keys" , 256, "@" }, { aExportSecret, "export-secret-keys" , 256, "@" }, { aExportSecretSub, "export-secret-subkeys" , 256, "@" }, { aImport, "import", 256 , N_("import/merge keys")}, { aFastImport, "fast-import", 256 , "@"}, +#ifdef ENABLE_CARD_SUPPORT { aCardStatus, "card-status", 256, N_("print the card status")}, { aCardEdit, "card-edit", 256, N_("change data on a card")}, { aChangePIN, "change-pin", 256, N_("change a card's PIN")}, - +#endif + { aListConfig, "list-config", 256, "@"}, + { aGPGConfList, "gpgconf-list", 256, "@" }, { aListPackets, "list-packets",256, "@"}, { aExportOwnerTrust, "export-ownertrust", 256, "@"}, { aImportOwnerTrust, "import-ownertrust", 256, "@"}, - { aUpdateTrustDB, "update-trustdb",0 , N_("update the trust database")}, - { aCheckTrustDB, "check-trustdb",0 , "@"}, - { aFixTrustDB, "fix-trustdb",0 , N_("fix a corrupted trust database")}, - { aDeArmor, "dearmor", 256, "@" }, - { aDeArmor, "dearmour", 256, "@" }, - { aEnArmor, "enarmor", 256, "@" }, - { aEnArmor, "enarmour", 256, "@" }, + { aUpdateTrustDB, + "update-trustdb",0 , N_("update the trust database")}, + { aCheckTrustDB, "check-trustdb", 0, "@"}, + { aFixTrustDB, "fix-trustdb", 0, "@"}, + { aDeArmor, "dearmor", 256, "@"}, + { aDeArmor, "dearmour", 256, "@"}, + { aEnArmor, "enarmor", 256, "@"}, + { aEnArmor, "enarmour", 256, "@"}, { aPrintMD, "print-md" , 256, N_("|algo [files]|print message digests")}, { aPrimegen, "gen-prime" , 256, "@" }, { aGenRandom, "gen-random" , 256, "@" }, - { aGPGConfList, "gpgconf-list", 256, "@" }, { 301, NULL, 0, N_("@\nOptions:\n ") }, @@ -396,107 +430,117 @@ static ARGPARSE_OPTS opts[] = { { oRecipient, "recipient", 2, N_("|NAME|encrypt for NAME")}, { oHiddenRecipient, "hidden-recipient", 2, "@" }, { oRecipient, "remote-user", 2, "@"}, /* old option name */ - { oDefRecipient, "default-recipient" ,2, "@" }, - { oDefRecipientSelf, "default-recipient-self" ,0, "@" }, + { oDefRecipient, "default-recipient", 2, "@"}, + { oDefRecipientSelf, "default-recipient-self", 0, "@"}, { oNoDefRecipient, "no-default-recipient", 0, "@" }, { oTempDir, "temp-directory", 2, "@" }, { oExecPath, "exec-path", 2, "@" }, { oEncryptTo, "encrypt-to", 2, "@" }, { oHiddenEncryptTo, "hidden-encrypt-to", 2, "@" }, { oNoEncryptTo, "no-encrypt-to", 0, "@" }, - { oUser, "local-user",2, N_("use this user-id to sign or decrypt")}, - { oCompress, NULL, 1, N_("|N|set compress level N (0 disables)") }, + { oLocalUser, "local-user",2, N_("use this user-id to sign or decrypt")}, + { oCompress, NULL, 1, N_("|N|set compress level N (0 disables)") }, + { oCompressLevel, "compress-level", 1, "@" }, + { oBZ2CompressLevel, "bzip2-compress-level", 1, "@" }, + { oBZ2DecompressLowmem, "bzip2-decompress-lowmem", 0, "@" }, { oTextmodeShort, NULL, 0, "@"}, { oTextmode, "textmode", 0, N_("use canonical text mode")}, { oNoTextmode, "no-textmode", 0, "@"}, { oExpert, "expert", 0, "@"}, { oNoExpert, "no-expert", 0, "@"}, + { oDefSigExpire, "default-sig-expire", 2, "@"}, { oAskSigExpire, "ask-sig-expire", 0, "@"}, { oNoAskSigExpire, "no-ask-sig-expire", 0, "@"}, + { oDefCertExpire, "default-cert-expire", 2, "@"}, { oAskCertExpire, "ask-cert-expire", 0, "@"}, { oNoAskCertExpire, "no-ask-cert-expire", 0, "@"}, + { oDefCertLevel, "default-cert-level", 1, "@"}, + { oMinCertLevel, "min-cert-level", 1, "@"}, + { oAskCertLevel, "ask-cert-level", 0, "@"}, + { oNoAskCertLevel, "no-ask-cert-level", 0, "@"}, { oOutput, "output", 2, N_("use as output file")}, + { oMaxOutput, "max-output", 16|4, "@" }, { oVerbose, "verbose", 0, N_("verbose") }, - { oQuiet, "quiet", 0, "@" }, - { oNoTTY, "no-tty", 0, "@" }, - { oLogFile, "log-file" ,2, "@" }, - { oForceV3Sigs, "force-v3-sigs", 0, "@" }, - { oNoForceV3Sigs, "no-force-v3-sigs", 0, "@" }, - { oForceV4Certs, "force-v4-certs", 0, "@" }, - { oNoForceV4Certs, "no-force-v4-certs", 0, "@" }, - { oForceMDC, "force-mdc", 0, "@" }, + { oQuiet, "quiet", 0, "@"}, + { oNoTTY, "no-tty", 0, "@"}, + { oForceV3Sigs, "force-v3-sigs", 0, "@"}, + { oNoForceV3Sigs, "no-force-v3-sigs", 0, "@"}, + { oForceV4Certs, "force-v4-certs", 0, "@"}, + { oNoForceV4Certs, "no-force-v4-certs", 0, "@"}, + { oForceMDC, "force-mdc", 0, "@"}, { oNoForceMDC, "no-force-mdc", 0, "@" }, - { oDisableMDC, "disable-mdc", 0, "@" }, + { oDisableMDC, "disable-mdc", 0, "@"}, { oNoDisableMDC, "no-disable-mdc", 0, "@" }, { oDryRun, "dry-run", 0, N_("do not make any changes") }, { oInteractive, "interactive", 0, N_("prompt before overwriting") }, { oUseAgent, "use-agent",0, "@"}, { oNoUseAgent, "no-use-agent",0, "@"}, { oGpgAgentInfo, "gpg-agent-info",2, "@"}, - { oBatch, "batch", 0, "@"}, - { oAnswerYes, "yes", 0, "@"}, - { oAnswerNo, "no", 0, "@"}, - { oKeyring, "keyring" , 2, "@"}, + { oBatch, "batch", 0, "@"}, + { oAnswerYes, "yes", 0, "@"}, + { oAnswerNo, "no", 0, "@"}, + { oKeyring, "keyring", 2, "@"}, { oPrimaryKeyring, "primary-keyring",2, "@" }, - { oSecretKeyring, "secret-keyring" ,2, "@"}, + { oSecretKeyring, "secret-keyring", 2, "@"}, { oShowKeyring, "show-keyring", 0, "@"}, - { oDefaultKey, "default-key" , 2, "@"}, - { oKeyServer, "keyserver", 2, "@"}, + { oDefaultKey, "default-key", 2, "@"}, + { oKeyServer, "keyserver", 2, "@"}, { oKeyServerOptions, "keyserver-options",2,"@"}, { oImportOptions, "import-options",2,"@"}, { oExportOptions, "export-options",2,"@"}, { oListOptions, "list-options",2,"@"}, { oVerifyOptions, "verify-options",2,"@"}, - { oCharset, "charset" , 2, "@" }, - { oOptions, "options" , 2, "@"}, - + { oDisplayCharset, "display-charset", 2, "@"}, + { oDisplayCharset, "charset", 2, "@"}, + { oOptions, "options", 2, "@"}, { oDebug, "debug" ,4|16, "@"}, { oDebugLevel, "debug-level" ,2, "@"}, { oDebugAll, "debug-all" ,0, "@"}, - { oStatusFD, "status-fd" ,1, "@" }, -#ifdef __riscos__ - { oStatusFile, "status-file" ,2, "@" }, -#endif /* __riscos__ */ + { oStatusFD, "status-fd" ,1, "@"}, + { oStatusFile, "status-file" ,2, "@"}, { oAttributeFD, "attribute-fd" ,1, "@" }, -#ifdef __riscos__ { oAttributeFile, "attribute-file" ,2, "@" }, -#endif /* __riscos__ */ - { oNoSKComments, "no-sk-comments", 0, "@"}, - { oSKComments, "sk-comments", 0, "@"}, + { oNoop, "sk-comments", 0, "@"}, + { oNoop, "no-sk-comments", 0, "@"}, { oCompletesNeeded, "completes-needed", 1, "@"}, { oMarginalsNeeded, "marginals-needed", 1, "@"}, { oMaxCertDepth, "max-cert-depth", 1, "@" }, { oTrustedKey, "trusted-key", 2, "@"}, - { oLoadExtension, "load-extension" ,2, "@"}, + { oLoadExtension, "load-extension", 2, "@"}, { oGnuPG, "gnupg", 0, "@"}, { oGnuPG, "no-pgp2", 0, "@"}, { oGnuPG, "no-pgp6", 0, "@"}, { oGnuPG, "no-pgp7", 0, "@"}, { oGnuPG, "no-pgp8", 0, "@"}, { oRFC1991, "rfc1991", 0, "@"}, - { oRFC2440, "rfc2440", 0, "@"}, + { oRFC2440, "rfc2440", 0, "@" }, { oOpenPGP, "openpgp", 0, N_("use strict OpenPGP behavior")}, { oPGP2, "pgp2", 0, N_("generate PGP 2.x compatible messages")}, { oPGP6, "pgp6", 0, "@"}, { oPGP7, "pgp7", 0, "@"}, { oPGP8, "pgp8", 0, "@"}, - { oS2KMode, "s2k-mode", 1, "@"}, - { oS2KDigest, "s2k-digest-algo",2, "@"}, - { oS2KCipher, "s2k-cipher-algo",2, "@"}, + { oRFC2440Text, "rfc2440-text", 0, "@"}, + { oNoRFC2440Text, "no-rfc2440-text", 0, "@"}, + { oS2KMode, "s2k-mode", 1, "@"}, + { oS2KDigest, "s2k-digest-algo", 2, "@"}, + { oS2KCipher, "s2k-cipher-algo", 2, "@"}, { oSimpleSKChecksum, "simple-sk-checksum", 0, "@"}, - { oCipherAlgo, "cipher-algo", 2 , "@"}, - { oDigestAlgo, "digest-algo", 2 , "@"}, + { oCipherAlgo, "cipher-algo", 2, "@"}, + { oDigestAlgo, "digest-algo", 2, "@"}, { oCertDigestAlgo, "cert-digest-algo", 2 , "@" }, - { oCompressAlgo,"compress-algo",2, "@"}, - { oThrowKeyid, "throw-keyid", 0, "@"}, - { oNoThrowKeyid, "no-throw-keyid", 0, "@" }, + { oCompressAlgo,"compress-algo", 2, "@"}, + { oCompressAlgo, "compression-algo", 2, "@"}, /* Alias */ + { oThrowKeyids, "throw-keyid", 0, "@"}, + { oThrowKeyids, "throw-keyids", 0, "@"}, + { oNoThrowKeyids, "no-throw-keyid", 0, "@" }, + { oNoThrowKeyids, "no-throw-keyids", 0, "@" }, { oShowPhotos, "show-photos", 0, "@" }, { oNoShowPhotos, "no-show-photos", 0, "@" }, { oPhotoViewer, "photo-viewer", 2, "@" }, { oSetNotation, "set-notation", 2, "@" }, { oSetNotation, "notation-data", 2, "@" }, /* Alias */ - { oSigNotation, "sig-notation", 2, "@" }, - { oCertNotation, "cert-notation", 2, "@" }, + { oSigNotation, "sig-notation", 2, "@" }, + { oCertNotation, "cert-notation", 2, "@" }, { 302, NULL, 0, N_( "@\n(See the man page for a complete listing of all commands and options)\n" @@ -511,24 +555,22 @@ static ARGPARSE_OPTS opts[] = { /* hidden options */ { aListOwnerTrust, "list-ownertrust", 256, "@"}, /* deprecated */ - { oCompressAlgo, "compression-algo", 1, "@"}, /* alias */ { aPrintMDs, "print-mds" , 256, "@"}, /* old */ { aListTrustDB, "list-trustdb",0 , "@"}, /* Not yet used */ /* { aListTrustPath, "list-trust-path",0, "@"}, */ - { aPipeMode, "pipemode", 0, "@" }, + { oKOption, NULL, 0, "@"}, + { oPasswd, "passphrase",2, "@" }, { oPasswdFD, "passphrase-fd",1, "@" }, -#ifdef __riscos__ { oPasswdFile, "passphrase-file",2, "@" }, -#endif /* __riscos__ */ { oCommandFD, "command-fd",1, "@" }, -#ifdef __riscos__ { oCommandFile, "command-file",2, "@" }, -#endif /* __riscos__ */ { oQuickRandom, "quick-random", 0, "@"}, { oNoVerbose, "no-verbose", 0, "@"}, { oTrustDBName, "trustdb-name", 2, "@" }, - { oNoSecmemWarn, "no-secmem-warning", 0, "@" }, /* used only by regression tests */ + { oNoSecmemWarn, "no-secmem-warning", 0, "@" }, + { oRequireSecmem,"require-secmem", 0, "@" }, + { oNoRequireSecmem,"no-require-secmem", 0, "@" }, { oNoPermissionWarn, "no-permission-warning", 0, "@" }, { oNoMDCWarn, "no-mdc-warning", 0, "@" }, { oNoArmor, "no-armor", 0, "@"}, @@ -546,11 +588,10 @@ static ARGPARSE_OPTS opts[] = { { oSkipVerify, "skip-verify",0, "@" }, { oCompressKeys, "compress-keys",0, "@"}, { oCompressSigs, "compress-sigs",0, "@"}, - { oDefCertCheckLevel, "default-cert-check-level", 1, "@"}, + { oDefCertLevel, "default-cert-check-level", 1, "@"}, /* Old option */ { oAlwaysTrust, "always-trust", 0, "@"}, { oTrustModel, "trust-model", 2, "@"}, { oForceOwnertrust, "force-ownertrust", 2, "@"}, - { oEmuChecksumBug, "emulate-checksum-bug", 0, "@"}, { oSetFilename, "set-filename", 2, "@" }, { oForYourEyesOnly, "for-your-eyes-only", 0, "@" }, { oNoForYourEyesOnly, "no-for-your-eyes-only", 0, "@" }, @@ -559,9 +600,9 @@ static ARGPARSE_OPTS opts[] = { { oCertPolicyURL, "cert-policy-url", 2, "@" }, { oShowPolicyURL, "show-policy-url", 0, "@" }, { oNoShowPolicyURL, "no-show-policy-url", 0, "@" }, + { oSigKeyserverURL, "sig-keyserver-url", 2, "@" }, { oShowNotation, "show-notation", 0, "@" }, { oNoShowNotation, "no-show-notation", 0, "@" }, - { oSigKeyserverURL, "sig-keyserver-url", 2, "@" }, { oComment, "comment", 2, "@" }, { oDefaultComment, "default-comment", 0, "@" }, { oNoComments, "no-comments", 0, "@" }, @@ -575,10 +616,9 @@ static ARGPARSE_OPTS opts[] = { { oLockMultiple, "lock-multiple", 0, "@" }, { oLockNever, "lock-never", 0, "@" }, { oLoggerFD, "logger-fd",1, "@" }, -#ifdef __riscos__ - { oLoggerFile, "logger-file",2, "@" }, -#endif /* __riscos__ */ + { oLoggerFile, "log-file",2, "@" }, { oUseEmbeddedFilename, "use-embedded-filename", 0, "@" }, + { oNoUseEmbeddedFilename, "no-use-embedded-filename", 0, "@" }, { oUtf8Strings, "utf8-strings", 0, "@" }, { oNoUtf8Strings, "no-utf8-strings", 0, "@" }, { oWithFingerprint, "with-fingerprint", 0, "@" }, @@ -619,7 +659,11 @@ static ARGPARSE_OPTS opts[] = { { oPersonalCipherPreferences, "personal-cipher-preferences", 2, "@"}, { oPersonalDigestPreferences, "personal-digest-preferences", 2, "@"}, { oPersonalCompressPreferences, "personal-compress-preferences", 2, "@"}, - { oEmuMDEncodeBug, "emulate-md-encode-bug", 0, "@"}, + /* Aliases. I constantly mistype these, and assume other people + do as well. */ + { oPersonalCipherPreferences, "personal-cipher-prefs", 2, "@"}, + { oPersonalDigestPreferences, "personal-digest-prefs", 2, "@"}, + { oPersonalCompressPreferences, "personal-compress-prefs", 2, "@"}, { oAgentProgram, "agent-program", 2 , "@" }, { oDisplay, "display", 2, "@" }, { oTTYname, "ttyname", 2, "@" }, @@ -627,14 +671,41 @@ static ARGPARSE_OPTS opts[] = { { oLCctype, "lc-ctype", 2, "@" }, { oLCmessages, "lc-messages", 2, "@" }, { oGroup, "group", 2, "@" }, + { oUnGroup, "ungroup", 2, "@" }, + { oNoGroups, "no-groups", 0, "@" }, { oStrict, "strict", 0, "@" }, { oNoStrict, "no-strict", 0, "@" }, { oMangleDosFilenames, "mangle-dos-filenames", 0, "@" }, { oNoMangleDosFilenames, "no-mangle-dos-filenames", 0, "@" }, { oEnableProgressFilter, "enable-progress-filter", 0, "@" }, { oMultifile, "multifile", 0, "@" }, -{0} }; - + { oKeyidFormat, "keyid-format", 2, "@" }, + { oExitOnStatusWriteError, "exit-on-status-write-error", 0, "@" }, + { oLimitCardInsertTries, "limit-card-insert-tries", 1, "@"}, + + { oAllowMultisigVerification, "allow-multisig-verification", 0, "@"}, + + /* These two are aliases to help users of the PGP command line + product use gpg with minimal pain. Many commands are common + already as they seem to have borrowed commands from us. Now + I'm returning the favor. */ + { oLocalUser, "sign-with", 2, "@" }, + { oRecipient, "user", 2, "@" }, + { oRequireCrossCert, "require-backsigs", 0, "@"}, + { oRequireCrossCert, "require-cross-certification", 0, "@"}, + { oNoRequireCrossCert, "no-require-backsigs", 0, "@"}, + { oNoRequireCrossCert, "no-require-cross-certification", 0, "@"}, + { oAutoKeyLocate, "auto-key-locate", 2, "@"}, + { oNoAutoKeyLocate, "no-auto-key-locate", 0, "@"}, + {0,NULL,0,NULL} +}; + + +#ifdef ENABLE_SELINUX_HACKS +#define ALWAYS_ADD_KEYRINGS 1 +#else +#define ALWAYS_ADD_KEYRINGS 0 +#endif int g10_errors_seen = 0; @@ -652,16 +723,6 @@ static void add_policy_url( const char *string, int which ); static void add_keyserver_url( const char *string, int which ); static void emergency_cleanup (void); -#ifdef __riscos__ -RISCOS_GLOBAL_STATICS("GnuPG Heap") -#endif /* __riscos__ */ - -static int -pk_test_algo (int algo) -{ - return openpgp_pk_test_algo (algo, 0); -} - static const char * my_strusage( int level ) @@ -676,6 +737,19 @@ my_strusage( int level ) case 19: p = _("Please report bugs to <gnupg-bugs@gnu.org>.\n"); break; + +#ifdef IS_DEVELOPMENT_VERSION + case 20: + p="NOTE: THIS IS A DEVELOPMENT VERSION!"; + break; + case 21: + p="It is only intended for test purposes and should NOT be"; + break; + case 22: + p="used in a production environment or with production keys!"; + break; +#endif + case 1: case 40: p = _("Usage: gpg [options] [files] (-h for help)"); @@ -694,27 +768,31 @@ my_strusage( int level ) #endif /* __riscos__ */ case 33: p = _("\nSupported algorithms:\n"); break; case 34: - if( !pubkeys ) - pubkeys = build_list(_("Pubkey: "), 0, gcry_pk_algo_name, - pk_test_algo ); + if (!pubkeys) + pubkeys = build_list (_("Pubkey: "), 0, + gcry_pk_algo_name, + openpgp_pk_test_algo ); p = pubkeys; break; case 35: if( !ciphers ) - ciphers = build_list(_("Cipher: "), 'S', gcry_cipher_algo_name, + ciphers = build_list(_("Cipher: "), 'S', + gcry_cipher_algo_name, openpgp_cipher_test_algo ); p = ciphers; break; case 36: if( !digests ) - digests = build_list(_("Hash: "), 'H', gcry_md_algo_name, - openpgp_md_test_algo ); + digests = build_list(_("Hash: "), 'H', + gcry_md_algo_name, + openpgp_md_test_algo ); p = digests; break; case 37: if( !zips ) - zips = build_list(_("Compression: "),'Z',compress_algo_to_string, - check_compress_algo); + zips = build_list(_("Compression: "),'Z', + compress_algo_to_string, + check_compress_algo); p = zips; break; @@ -733,13 +811,13 @@ build_list( const char *text, char letter, size_t n=strlen(text)+2; char *list, *p, *line=NULL; - if( maybe_setuid ) - gcry_control (GCRYCTL_INIT_SECMEM, 0, 0); /* drop setuid */ + if (maybe_setuid) + gcry_control (GCRYCTL_INIT_SECMEM, 0, 0); /* Drop setuid. */ for(i=0; i <= 110; i++ ) if( !chkf(i) && (s=mapf(i)) ) n += strlen(s) + 7 + 2; - list = xmalloc ( 21 + n ); *list = 0; + list = xmalloc( 21 + n ); *list = 0; for(p=NULL, i=0; i <= 110; i++ ) { if( !chkf(i) && (s=mapf(i)) ) { if( !p ) { @@ -752,7 +830,7 @@ build_list( const char *text, char letter, if(strlen(line)>60) { int spaces=strlen(text); - list = xrealloc(list,n+spaces+1); + list=xrealloc(list,n+spaces+1); /* realloc could move the block, so find the end again */ p=list; while(*p) @@ -783,12 +861,12 @@ static void i18n_init(void) { #ifdef USE_SIMPLE_GETTEXT - set_gettext_file( PACKAGE_GT ); + set_gettext_file (PACKAGE_GT, "Software\\GNU\\GnuPG"); #else #ifdef ENABLE_NLS - setlocale( LC_ALL, "" ); - bindtextdomain( PACKAGE_GT, LOCALEDIR ); - textdomain( PACKAGE_GT ); + setlocale (LC_ALL, ""); + bindtextdomain (PACKAGE_GT, LOCALEDIR); + textdomain (PACKAGE_GT); #endif #endif } @@ -803,42 +881,15 @@ wrong_args( const char *text) } -static void -log_set_strict (int yesno) -{ - /* FIXME-XXX*/ -} - static char * make_username( const char *string ) { - char *p; - if( utf8_strings ) - p = xstrdup (string); - else - p = native_to_utf8( string ); - return p; -} - - -/* - * same as add_to_strlist() but if is_utf8 is *not* set a conversion - * to UTF8 is done - */ -static STRLIST -add_to_strlist2 ( STRLIST *list, const char *string, int is_utf8) -{ - STRLIST sl; - - if (is_utf8) - sl = add_to_strlist( list, string ); - else - { - char *p = native_to_utf8( string ); - sl = add_to_strlist( list, p ); - xfree( p ); - } - return sl; + char *p; + if( utf8_strings ) + p = xstrdup(string); + else + p = native_to_utf8( string ); + return p; } @@ -878,9 +929,11 @@ set_debug (const char *level) gcry_control (GCRYCTL_SET_DEBUG_FLAGS, 1); if (opt.debug & DBG_IOBUF_VALUE ) iobuf_debug_mode = 1; + gcry_control (GCRYCTL_SET_VERBOSITY, (int)opt.verbose); } + /* We need the home directory also in some other directories, so make sure that both variables are always in sync. */ static void @@ -888,10 +941,80 @@ set_homedir (const char *dir) { if (!dir) dir = ""; - g10_opt_homedir = opt.homedir = dir; + opt.homedir = dir; } +/* We set the screen dimensions for UI purposes. Do not allow screens + smaller than 80x24 for the sake of simplicity. */ +static void +set_screen_dimensions(void) +{ +#ifndef HAVE_W32_SYSTEM + char *str; + + str=getenv("COLUMNS"); + if(str) + opt.screen_columns=atoi(str); + + str=getenv("LINES"); + if(str) + opt.screen_lines=atoi(str); +#endif + + if(opt.screen_columns<80 || opt.screen_columns>255) + opt.screen_columns=80; + + if(opt.screen_lines<24 || opt.screen_lines>255) + opt.screen_lines=24; +} + + +/* Helper to open a file FNAME either for reading or writing to be + used with --status-file etc functions. Not generally useful but it + avoids the riscos specific functions and well some Windows people + might like it too. Prints an error message and returns -1 on + error. On success the file descriptor is returned. */ +static int +open_info_file (const char *fname, int for_write) +{ +#ifdef __riscos__ + return riscos_fdopenfile (fname, for_write); +#elif defined (ENABLE_SELINUX_HACKS) + /* We can't allow these even when testing for a secured filename + because files to be secured might not yet been secured. This is + similar to the option file but in that case it is unlikely that + sensitive information may be retrieved by means of error + messages. */ + return -1; +#else + int fd; + +/* if (is_secured_filename (fname)) */ +/* { */ +/* fd = -1; */ +/* errno = EPERM; */ +/* } */ +/* else */ +/* { */ + do + { + if (for_write) + fd = open (fname, O_CREAT | O_TRUNC | O_WRONLY, + S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP); + else + fd = open (fname, O_RDONLY | MY_O_BINARY); + } + while (fd == -1 && errno == EINTR); +/* } */ + if ( fd == -1) + log_error ( for_write? _("can't create `%s': %s\n") + : _("can't open `%s': %s\n"), fname, strerror(errno)); + + return fd; +#endif +} + static void set_cmd( enum cmd_and_opt_values *ret_cmd, enum cmd_and_opt_values new_cmd ) { @@ -907,6 +1030,18 @@ set_cmd( enum cmd_and_opt_values *ret_cmd, enum cmd_and_opt_values new_cmd ) cmd = aSignSym; else if( cmd == aSym && new_cmd == aSign ) cmd = aSignSym; + else if( cmd == aSym && new_cmd == aEncr ) + cmd = aEncrSym; + else if( cmd == aEncr && new_cmd == aSym ) + cmd = aEncrSym; + else if( cmd == aKMode && new_cmd == aSym ) + cmd = aKModeC; + else if (cmd == aSignEncr && new_cmd == aSym) + cmd = aSignEncrSym; + else if (cmd == aSignSym && new_cmd == aEncr) + cmd = aSignEncrSym; + else if (cmd == aEncrSym && new_cmd == aSign) + cmd = aSignEncrSym; else if( ( cmd == aSign && new_cmd == aClearsign ) || ( cmd == aClearsign && new_cmd == aSign ) ) cmd = aClearsign; @@ -919,47 +1054,79 @@ set_cmd( enum cmd_and_opt_values *ret_cmd, enum cmd_and_opt_values new_cmd ) } -static void add_group(char *string) +static void +add_group(char *string) { char *name,*value; struct groupitem *item; - STRLIST values=NULL; /* Break off the group name */ name=strsep(&string,"="); if(string==NULL) { - log_error(_("no = sign found in group definition \"%s\"\n"),name); + log_error(_("no = sign found in group definition `%s'\n"),name); return; } - trim_trailing_ws((unsigned char *)name,strlen(name)); + trim_trailing_ws(name,strlen(name)); + + /* Does this group already exist? */ + for(item=opt.grouplist;item;item=item->next) + if(strcasecmp(item->name,name)==0) + break; + + if(!item) + { + item=xmalloc(sizeof(struct groupitem)); + item->name=name; + item->next=opt.grouplist; + item->values=NULL; + opt.grouplist=item; + } /* Break apart the values */ while ((value= strsep(&string," \t"))) { if (*value) - add_to_strlist2 (&values,value,utf8_strings); + add_to_strlist2(&item->values,value,utf8_strings); } +} + + +static void +rm_group(char *name) +{ + struct groupitem *item,*last=NULL; - item=xmalloc (sizeof(struct groupitem)); - item->name=name; - item->values=values; - item->next=opt.grouplist; + trim_trailing_ws(name,strlen(name)); + + for(item=opt.grouplist;item;last=item,item=item->next) + { + if(strcasecmp(item->name,name)==0) + { + if(last) + last->next=item->next; + else + opt.grouplist=item->next; - opt.grouplist=item; + free_strlist(item->values); + xfree(item); + break; + } + } } + /* We need to check three things. 0) The homedir. It must be x00, a directory, and owned by the user. - 1) The options file. Okay unless it or its containing directory is - group or other writable or not owned by us. disable exec in this - case. + 1) The options/gpg.conf file. Okay unless it or its containing + directory is group or other writable or not owned by us. Disable + exec in this case. - 2) Extensions. Same as #2. + 2) Extensions. Same as #1. Returns true if the item is unsafe. */ static int @@ -986,7 +1153,7 @@ check_permissions(const char *path,int item) tmppath=make_filename(GNUPG_LIBDIR,path,NULL); } else - tmppath=xstrdup (path); + tmppath=xstrdup(path); /* If the item is located in the homedir, but isn't the homedir, don't continue if we already checked the homedir itself. This is @@ -1019,7 +1186,7 @@ check_permissions(const char *path,int item) goto end; } - xfree (dir); + xfree(dir); /* Assume failure */ ret=1; @@ -1094,55 +1261,55 @@ check_permissions(const char *path,int item) if(own) { if(item==0) - log_info(_("WARNING: unsafe ownership on " - "homedir \"%s\"\n"),tmppath); + log_info(_("WARNING: unsafe ownership on" + " homedir `%s'\n"),tmppath); else if(item==1) - log_info(_("WARNING: unsafe ownership on " - "configuration file \"%s\"\n"),tmppath); + log_info(_("WARNING: unsafe ownership on" + " configuration file `%s'\n"),tmppath); else - log_info(_("WARNING: unsafe ownership on " - "extension \"%s\"\n"),tmppath); + log_info(_("WARNING: unsafe ownership on" + " extension `%s'\n"),tmppath); } if(perm) { if(item==0) - log_info(_("WARNING: unsafe permissions on " - "homedir \"%s\"\n"),tmppath); + log_info(_("WARNING: unsafe permissions on" + " homedir `%s'\n"),tmppath); else if(item==1) - log_info(_("WARNING: unsafe permissions on " - "configuration file \"%s\"\n"),tmppath); + log_info(_("WARNING: unsafe permissions on" + " configuration file `%s'\n"),tmppath); else - log_info(_("WARNING: unsafe permissions on " - "extension \"%s\"\n"),tmppath); + log_info(_("WARNING: unsafe permissions on" + " extension `%s'\n"),tmppath); } if(enc_dir_own) { if(item==0) - log_info(_("WARNING: unsafe enclosing directory ownership on " - "homedir \"%s\"\n"),tmppath); + log_info(_("WARNING: unsafe enclosing directory ownership on" + " homedir `%s'\n"),tmppath); else if(item==1) - log_info(_("WARNING: unsafe enclosing directory ownership on " - "configuration file \"%s\"\n"),tmppath); + log_info(_("WARNING: unsafe enclosing directory ownership on" + " configuration file `%s'\n"),tmppath); else - log_info(_("WARNING: unsafe enclosing directory ownership on " - "extension \"%s\"\n"),tmppath); + log_info(_("WARNING: unsafe enclosing directory ownership on" + " extension `%s'\n"),tmppath); } if(enc_dir_perm) { if(item==0) - log_info(_("WARNING: unsafe enclosing directory permissions on " - "homedir \"%s\"\n"),tmppath); + log_info(_("WARNING: unsafe enclosing directory permissions on" + " homedir `%s'\n"),tmppath); else if(item==1) - log_info(_("WARNING: unsafe enclosing directory permissions on " - "configuration file \"%s\"\n"),tmppath); + log_info(_("WARNING: unsafe enclosing directory permissions on" + " configuration file `%s'\n"),tmppath); else - log_info(_("WARNING: unsafe enclosing directory permissions on " - "extension \"%s\"\n"),tmppath); + log_info(_("WARNING: unsafe enclosing directory permissions on" + " extension `%s'\n"),tmppath); } } end: - xfree (tmppath); + xfree(tmppath); if(homedir) homedir_cache=ret; @@ -1154,11 +1321,326 @@ check_permissions(const char *path,int item) return 0; } + +static void +print_algo_numbers(int (*checker)(int)) +{ + int i,first=1; + + for(i=0;i<=110;i++) + { + if(!checker(i)) + { + if(first) + first=0; + else + printf(";"); + printf("%d",i); + } + } +} + + +/* In the future, we can do all sorts of interesting configuration + output here. For now, just give "group" as the Enigmail folks need + it, and pubkey, cipher, hash, and compress as they may be useful + for frontends. */ +static void +list_config(char *items) +{ + int show_all=(items==NULL); + char *name=NULL; + + if(!opt.with_colons) + return; + + while(show_all || (name=strsep(&items," "))) + { + int any=0; + + if(show_all || ascii_strcasecmp(name,"group")==0) + { + struct groupitem *iter; + + for(iter=opt.grouplist;iter;iter=iter->next) + { + STRLIST sl; + + printf("cfg:group:"); + print_string(stdout,iter->name,strlen(iter->name),':'); + printf(":"); + + for(sl=iter->values;sl;sl=sl->next) + { + print_string2(stdout,sl->d,strlen(sl->d),':',';'); + if(sl->next) + printf(";"); + } + + printf("\n"); + } + + any=1; + } + + if(show_all || ascii_strcasecmp(name,"version")==0) + { + printf("cfg:version:"); + print_string(stdout,VERSION,strlen(VERSION),':'); + printf("\n"); + any=1; + } + + if(show_all || ascii_strcasecmp(name,"pubkey")==0) + { + printf("cfg:pubkey:"); + print_algo_numbers (openpgp_pk_test_algo); + printf("\n"); + any=1; + } + + if(show_all || ascii_strcasecmp(name,"cipher")==0) + { + printf("cfg:cipher:"); + print_algo_numbers(openpgp_cipher_test_algo); + printf("\n"); + any=1; + } + + if(show_all + || ascii_strcasecmp(name,"digest")==0 + || ascii_strcasecmp(name,"hash")==0) + { + printf("cfg:digest:"); + print_algo_numbers(openpgp_md_test_algo); + printf("\n"); + any=1; + } + + if(show_all || ascii_strcasecmp(name,"compress")==0) + { + printf("cfg:compress:"); + print_algo_numbers(check_compress_algo); + printf("\n"); + any=1; + } + + if(show_all || ascii_strcasecmp(name,"ccid-reader-id")==0) + { +#if defined(ENABLE_CARD_SUPPORT) && defined(HAVE_LIBUSB) + char *p, *p2, *list = ccid_get_reader_list (); + + for (p=list; p && (p2 = strchr (p, '\n')); p = p2+1) + { + *p2 = 0; + printf("cfg:ccid-reader-id:%s\n", p); + } + free (list); +#endif + any=1; + } + + if(show_all) + break; + + if(!any) + log_error(_("unknown configuration item `%s'\n"),name); + } +} + + +/* List options and default values in the GPG Conf format. This is a + new tool distributed with gnupg 1.9.x but we also want some limited + support in older gpg versions. The output is the name of the + configuration file and a list of options available for editing by + gpgconf. */ +static void +gpgconf_list (const char *configfile) +{ + /* The following definitions are taken from gnupg/tools/gpgconf-comp.c. */ +#define GC_OPT_FLAG_NONE 0UL +#define GC_OPT_FLAG_DEFAULT (1UL << 4) + + printf ("gpgconf-gpg.conf:%lu:\"%s\n", + GC_OPT_FLAG_DEFAULT,configfile?configfile:"/dev/null"); + printf ("verbose:%lu:\n", GC_OPT_FLAG_NONE); + printf ("quiet:%lu:\n", GC_OPT_FLAG_NONE); + printf ("keyserver:%lu:\n", GC_OPT_FLAG_NONE); + printf ("reader-port:%lu:\n", GC_OPT_FLAG_NONE); +} + + +static int +parse_subpacket_list(char *list) +{ + char *tok; + byte subpackets[128],i; + int count=0; + + if(!list) + { + /* No arguments means all subpackets */ + memset(subpackets+1,1,sizeof(subpackets)-1); + count=127; + } + else + { + memset(subpackets,0,sizeof(subpackets)); + + /* Merge with earlier copy */ + if(opt.show_subpackets) + { + byte *in; + + for(in=opt.show_subpackets;*in;in++) + { + if(*in>127 || *in<1) + BUG(); + + if(!subpackets[*in]) + count++; + subpackets[*in]=1; + } + } + + while((tok=strsep(&list," ,"))) + { + if(!*tok) + continue; + + i=atoi(tok); + if(i>127 || i<1) + return 0; + + if(!subpackets[i]) + count++; + subpackets[i]=1; + } + } + + xfree(opt.show_subpackets); + opt.show_subpackets=xmalloc(count+1); + opt.show_subpackets[count--]=0; + + for(i=1;i<128 && count>=0;i++) + if(subpackets[i]) + opt.show_subpackets[count--]=i; + + return 1; +} + + +static int +parse_list_options(char *str) +{ + char *subpackets=""; /* something that isn't NULL */ + struct parse_options lopts[]= + { + {"show-photos",LIST_SHOW_PHOTOS,NULL, + N_("display photo IDs during key listings")}, + {"show-policy-urls",LIST_SHOW_POLICY_URLS,NULL, + N_("show policy URLs during signature listings")}, + {"show-notations",LIST_SHOW_NOTATIONS,NULL, + N_("show all notations during signature listings")}, + {"show-std-notations",LIST_SHOW_STD_NOTATIONS,NULL, + N_("show IETF standard notations during signature listings")}, + {"show-standard-notations",LIST_SHOW_STD_NOTATIONS,NULL, + NULL}, + {"show-user-notations",LIST_SHOW_USER_NOTATIONS,NULL, + N_("show user-supplied notations during signature listings")}, + {"show-keyserver-urls",LIST_SHOW_KEYSERVER_URLS,NULL, + N_("show preferred keyserver URLs during signature listings")}, + {"show-uid-validity",LIST_SHOW_UID_VALIDITY,NULL, + N_("show user ID validity during key listings")}, + {"show-unusable-uids",LIST_SHOW_UNUSABLE_UIDS,NULL, + N_("show revoked and expired user IDs in key listings")}, + {"show-unusable-subkeys",LIST_SHOW_UNUSABLE_SUBKEYS,NULL, + N_("show revoked and expired subkeys in key listings")}, + {"show-keyring",LIST_SHOW_KEYRING,NULL, + N_("show the keyring name in key listings")}, + {"show-sig-expire",LIST_SHOW_SIG_EXPIRE,NULL, + N_("show expiration dates during signature listings")}, + {"show-sig-subpackets",LIST_SHOW_SIG_SUBPACKETS,NULL, + NULL}, + {NULL,0,NULL,NULL} + }; + + /* C99 allows for non-constant initializers, but we'd like to + compile everywhere, so fill in the show-sig-subpackets argument + here. Note that if the parse_options array changes, we'll have + to change the subscript here. */ + lopts[12].value=&subpackets; + + if(parse_options(str,&opt.list_options,lopts,1)) + { + if(opt.list_options&LIST_SHOW_SIG_SUBPACKETS) + { + /* Unset so users can pass multiple lists in. */ + opt.list_options&=~LIST_SHOW_SIG_SUBPACKETS; + if(!parse_subpacket_list(subpackets)) + return 0; + } + else if(subpackets==NULL && opt.show_subpackets) + { + /* User did 'no-show-subpackets' */ + xfree(opt.show_subpackets); + opt.show_subpackets=NULL; + } + + return 1; + } + else + return 0; +} + + +/* Collapses argc/argv into a single string that must be freed */ +static char * +collapse_args(int argc,char *argv[]) +{ + char *str=NULL; + int i,first=1,len=0; + + for(i=0;i<argc;i++) + { + len+=strlen(argv[i])+2; + str=xrealloc(str,len); + if(first) + { + str[0]='\0'; + first=0; + } + else + strcat(str," "); + + strcat(str,argv[i]); + } + + return str; +} + +static void +parse_trust_model(const char *model) +{ + if(ascii_strcasecmp(model,"pgp")==0) + opt.trust_model=TM_PGP; + else if(ascii_strcasecmp(model,"classic")==0) + opt.trust_model=TM_CLASSIC; + else if(ascii_strcasecmp(model,"always")==0) + opt.trust_model=TM_ALWAYS; + else if(ascii_strcasecmp(model,"direct")==0) + opt.trust_model=TM_DIRECT; + else if(ascii_strcasecmp(model,"auto")==0) + opt.trust_model=TM_AUTO; + else + log_error("unknown trust model `%s'\n",model); +} + int -main( int argc, char **argv ) +main (int argc, char **argv ) { ARGPARSE_ARGS pargs; - iobuf_t a; + IOBUF a; int rc=0; int orig_argc; char **orig_argv; @@ -1171,7 +1653,7 @@ main( int argc, char **argv ) int detached_sig = 0; FILE *configfp = NULL; char *configname = NULL; - const char *config_filename = NULL; + char *save_configname = NULL; unsigned configlineno; int parse_debug = 0; int default_config = 1; @@ -1185,7 +1667,7 @@ main( int argc, char **argv ) const char *trustdb_name = NULL; char *def_cipher_string = NULL; char *def_digest_string = NULL; - char *def_compress_string = NULL; + char *compress_algo_string = NULL; char *cert_digest_string = NULL; char *s2k_cipher_string = NULL; char *s2k_digest_string = NULL; @@ -1197,47 +1679,53 @@ main( int argc, char **argv ) int pwfd = -1; int with_fpr = 0; /* make an option out of --fingerprint */ int any_explicit_recipient = 0; + int require_secmem=0,got_secmem=0; #ifdef __riscos__ - riscos_global_defaults(); opt.lock_once = 1; #endif /* __riscos__ */ + + /* Please note that we may running SUID(ROOT), so be very CAREFUL + when adding any stuff between here and the call to + secmem_init() somewhere after the option parsing. */ + trap_unaligned(); set_strusage (my_strusage); gcry_control (GCRYCTL_SUSPEND_SECMEM_WARN); /* We don't need any locking in libgcrypt unless we use any kind of threading. */ gcry_control (GCRYCTL_DISABLE_INTERNAL_LOCKING); - /* Please note that we may running SUID(ROOT), so be very CAREFUL - * when adding any stuff between here and the call to - * secmem_init() somewhere after the option parsing - */ log_set_prefix ("gpg", 1); - /* check that the libraries are suitable. Do it here because the - option parse may need services of the library */ + + /* Check that the libraries are suitable. Do it right here because the + option parsing may need services of the library. */ if (!gcry_check_version (NEED_LIBGCRYPT_VERSION) ) { - log_fatal( _("libgcrypt is too old (need %s, have %s)\n"), - NEED_LIBGCRYPT_VERSION, gcry_check_version (NULL) ); + log_fatal ( _("libgcrypt is too old (need %s, have %s)\n"), + NEED_LIBGCRYPT_VERSION, gcry_check_version (NULL) ); } + /* Put random number into secure memory */ gcry_control (GCRYCTL_USE_SECURE_RNDPOOL); may_coredump = disable_core_dumps(); + gnupg_init_signals (0, emergency_cleanup); - create_dotlock (NULL); /* register locking cleanup */ + + create_dotlock(NULL); /* Register locking cleanup. */ + i18n_init(); opt.command_fd = -1; /* no command fd */ - opt.compress = -1; /* defaults to standard compress level */ + opt.compress_level = -1; /* defaults to standard compress level */ + opt.bz2_compress_level = -1; /* defaults to standard compress level */ /* note: if you change these lines, look at oOpenPGP */ opt.def_cipher_algo = 0; opt.def_digest_algo = 0; opt.cert_digest_algo = 0; - opt.def_compress_algo = -1; + opt.compress_algo = -1; /* defaults to DEFAULT_COMPRESS_ALGO */ opt.s2k_mode = 3; /* iterated+salted */ - opt.s2k_digest_algo = DIGEST_ALGO_SHA1; #ifdef USE_CAST5 opt.s2k_cipher_algo = CIPHER_ALGO_CAST5; #else @@ -1250,23 +1738,24 @@ main( int argc, char **argv ) opt.force_v3_sigs = 1; opt.escape_from = 1; opt.import_options=IMPORT_SK2PK; - opt.export_options= - EXPORT_INCLUDE_NON_RFC|EXPORT_INCLUDE_ATTRIBUTES; + opt.export_options=EXPORT_ATTRIBUTES; opt.keyserver_options.import_options=IMPORT_REPAIR_PKS_SUBKEY_BUG; - opt.keyserver_options.export_options= - EXPORT_INCLUDE_NON_RFC|EXPORT_INCLUDE_ATTRIBUTES; - opt.keyserver_options.include_subkeys=1; - opt.keyserver_options.include_revoked=1; - opt.keyserver_options.try_dns_srv=1; + opt.keyserver_options.export_options=EXPORT_ATTRIBUTES; + opt.keyserver_options.options= + KEYSERVER_HONOR_KEYSERVER_URL|KEYSERVER_HONOR_PKA_RECORD; opt.verify_options= - VERIFY_SHOW_POLICY|VERIFY_SHOW_NOTATION|VERIFY_SHOW_KEYSERVER; + VERIFY_SHOW_POLICY_URLS|VERIFY_SHOW_STD_NOTATIONS|VERIFY_SHOW_KEYSERVER_URLS; opt.trust_model=TM_AUTO; - opt.mangle_dos_filenames = 1; - opt.use_agent = 1; - + opt.mangle_dos_filenames=0; + opt.min_cert_level=2; + set_screen_dimensions(); + opt.keyid_format=KF_SHORT; + opt.rfc2440_text=1; + opt.def_sig_expire="0"; + opt.def_cert_expire="0"; set_homedir ( default_homedir () ); - /* Check whether we have a config file on the commandline */ + /* Check whether we have a config file on the command line. */ orig_argc = argc; orig_argv = argv; pargs.argc = &argc; @@ -1304,7 +1793,13 @@ main( int argc, char **argv ) char *d, *buf = xmalloc (strlen (opt.homedir)+1); const char *s = opt.homedir; for (d=buf,s=opt.homedir; *s; s++) + { *d++ = *s == '\\'? '/': *s; +#ifdef HAVE_W32_SYSTEM + if (s[1] && IsDBCSLeadByte (*s)) + *d++ = *++s; +#endif + } *d = 0; set_homedir (buf); } @@ -1312,19 +1807,28 @@ main( int argc, char **argv ) /* Initialize the secure memory. */ gcry_control (GCRYCTL_INIT_SECMEM, 32768, 0); + got_secmem = 1; /* FIXME: gcry_control should return an indicator. */ +#if defined(HAVE_GETUID) && defined(HAVE_GETEUID) + /* There should be no way to get to this spot while still carrying + setuid privs. Just in case, bomb out if we are. */ + if(getuid()!=geteuid()) + BUG(); +#endif maybe_setuid = 0; + /* Okay, we are now working under our real uid */ /* malloc hooks go here ... */ assuan_set_malloc_hooks (gcry_malloc, gcry_realloc, gcry_free); + set_native_charset (NULL); /* Try to auto set the character set */ /* Try for a version specific config file first */ if( default_config ) { - char *name = xstrdup ("gpg" EXTSEP_S "conf-" SAFE_VERSION); - char *ver = name + strlen("gpg" EXTSEP_S "conf-"); + char *name=xstrdup("gpg" EXTSEP_S "conf-" SAFE_VERSION); + char *ver=&name[strlen("gpg" EXTSEP_S "conf-")]; do { @@ -1332,22 +1836,25 @@ main( int argc, char **argv ) { char *tok; - xfree (configname); + xfree(configname); configname=NULL; - if((tok=strrchr (ver,SAFE_VERSION_DASH))) + if((tok=strrchr(ver,SAFE_VERSION_DASH))) *tok='\0'; - else if((tok=strrchr (ver,SAFE_VERSION_DOT))) + else if((tok=strrchr(ver,SAFE_VERSION_DOT))) *tok='\0'; else break; } - configname = make_filename (opt.homedir, name, NULL); + configname = make_filename(opt.homedir,name,NULL); } - while ( access(configname,R_OK) ); + while(access(configname,R_OK)); + xfree(name); + if(!configname) + configname=make_filename(opt.homedir, "gpg" EXTSEP_S "conf", NULL ); if (!access (configname, R_OK)) { /* Print a warning when both config files are present. */ char *p = make_filename(opt.homedir, "options", NULL ); @@ -1385,6 +1892,12 @@ main( int argc, char **argv ) configlineno = 0; configfp = fopen( configname, "r" ); + if (configfp && is_secured_file (fileno (configfp))) + { + fclose (configfp); + configfp = NULL; + errno = EPERM; + } if( !configfp ) { if( default_config ) { if( parse_debug ) @@ -1396,7 +1909,7 @@ main( int argc, char **argv ) configname, strerror(errno) ); g10_exit(2); } - xfree (configname); configname = NULL; + xfree(configname); configname = NULL; } if( parse_debug && configname ) log_info(_("reading options from `%s'\n"), configname ); @@ -1404,24 +1917,32 @@ main( int argc, char **argv ) } while( optfile_parse( configfp, configname, &configlineno, - &pargs, opts) ) { - switch( pargs.r_opt ) { - case aCheckKeys: set_cmd( &cmd, aCheckKeys); break; - case aListPackets: set_cmd( &cmd, aListPackets); break; - case aImport: set_cmd( &cmd, aImport); break; - case aFastImport: set_cmd( &cmd, aFastImport); break; - case aSendKeys: set_cmd( &cmd, aSendKeys); break; - case aRecvKeys: set_cmd( &cmd, aRecvKeys); break; - case aSearchKeys: set_cmd( &cmd, aSearchKeys); break; - case aRefreshKeys: set_cmd( &cmd, aRefreshKeys); break; - case aExport: set_cmd( &cmd, aExport); break; - case aExportAll: set_cmd( &cmd, aExportAll); break; + &pargs, opts) ) + { + switch( pargs.r_opt ) + { + case aCheckKeys: + case aListConfig: + case aGPGConfList: + case aListPackets: + case aImport: + case aFastImport: + case aSendKeys: + case aRecvKeys: + case aSearchKeys: + case aRefreshKeys: + case aFetchKeys: + case aExport: + set_cmd (&cmd, pargs.r_opt); + break; case aListKeys: set_cmd( &cmd, aListKeys); break; case aListSigs: set_cmd( &cmd, aListSigs); break; case aExportSecret: set_cmd( &cmd, aExportSecret); break; case aExportSecretSub: set_cmd( &cmd, aExportSecretSub); break; - case aDeleteSecretKeys: set_cmd( &cmd, aDeleteSecretKeys); - greeting=1; break; + case aDeleteSecretKeys: + set_cmd( &cmd, aDeleteSecretKeys); + greeting=1; + break; case aDeleteSecretAndPublicKeys: set_cmd( &cmd, aDeleteSecretAndPublicKeys); greeting=1; @@ -1431,7 +1952,7 @@ main( int argc, char **argv ) case aDetachedSign: detached_sig = 1; set_cmd( &cmd, aSign ); break; case aSym: set_cmd( &cmd, aSym); break; - case aDecryptFiles: multifile=1; /* fall through */ + case aDecryptFiles: multifile=1; /* fall through */ case aDecrypt: set_cmd( &cmd, aDecrypt); break; case aEncrFiles: multifile=1; /* fall through */ @@ -1444,14 +1965,11 @@ main( int argc, char **argv ) case aKeygen: set_cmd( &cmd, aKeygen); greeting=1; break; case aSignKey: set_cmd( &cmd, aSignKey); break; case aLSignKey: set_cmd( &cmd, aLSignKey); break; - case aNRSignKey: set_cmd( &cmd, aNRSignKey); break; - case aNRLSignKey: set_cmd( &cmd, aNRLSignKey); break; case aStore: set_cmd( &cmd, aStore); break; case aEditKey: set_cmd( &cmd, aEditKey); greeting=1; break; case aClearsign: set_cmd( &cmd, aClearsign); break; case aGenRevoke: set_cmd( &cmd, aGenRevoke); break; case aDesigRevoke: set_cmd( &cmd, aDesigRevoke); break; - case aPrimegen: set_cmd( &cmd, aPrimegen); break; case aGenRandom: set_cmd( &cmd, aGenRandom); break; case aPrintMD: set_cmd( &cmd, aPrintMD); break; @@ -1468,28 +1986,23 @@ main( int argc, char **argv ) "--list-ownertrust","--export-ownertrust",""); case aExportOwnerTrust: set_cmd( &cmd, aExportOwnerTrust); break; case aImportOwnerTrust: set_cmd( &cmd, aImportOwnerTrust); break; - case aPipeMode: set_cmd( &cmd, aPipeMode); break; case aRebuildKeydbCaches: set_cmd( &cmd, aRebuildKeydbCaches); break; - case aCardStatus: set_cmd (&cmd, aCardStatus); break; - case aCardEdit: set_cmd (&cmd, aCardEdit); break; - case aChangePIN: set_cmd (&cmd, aChangePIN); break; - case aGPGConfList: - set_cmd (&cmd, aGPGConfList); - nogreeting = 1; - break; - case oArmor: opt.armor = 1; opt.no_armor=0; break; case oOutput: opt.outfile = pargs.r.ret_str; break; + case oMaxOutput: opt.max_output = pargs.r.ret_ulong; break; case oQuiet: opt.quiet = 1; break; case oNoTTY: tty_no_terminal(1); break; case oDryRun: opt.dry_run = 1; break; case oInteractive: opt.interactive = 1; break; - case oVerbose: g10_opt_verbose++; - opt.verbose++; opt.list_sigs=1; break; + case oVerbose: + opt.verbose++; + gcry_control (GCRYCTL_SET_VERBOSITY, (int)opt.verbose); + opt.list_options|=LIST_SHOW_UNUSABLE_UIDS; + opt.list_options|=LIST_SHOW_UNUSABLE_SUBKEYS; + break; + case oKOption: set_cmd( &cmd, aKMode ); break; - case oLogFile: logfile = pargs.r.ret_str; break; - case oBatch: opt.batch = 1; nogreeting = 1; break; case oUseAgent: #ifndef __riscos__ @@ -1508,58 +2021,60 @@ main( int argc, char **argv ) sl=append_to_strlist( &nrings, pargs.r.ret_str); sl->flags=2; break; - case oShowKeyring: opt.list_options|=LIST_SHOW_KEYRING; break; + case oShowKeyring: + deprecated_warning(configname,configlineno,"--show-keyring", + "--list-options ","show-keyring"); + opt.list_options|=LIST_SHOW_KEYRING; + break; + case oDebug: opt.debug |= pargs.r.ret_ulong; break; case oDebugAll: opt.debug = ~0; break; case oDebugLevel: debug_level = pargs.r.ret_str; break; + case oStatusFD: set_status_fd( iobuf_translate_file_handle (pargs.r.ret_int, 1) ); break; -#ifdef __riscos__ case oStatusFile: - set_status_fd( iobuf_translate_file_handle ( riscos_fdopenfile (pargs.r.ret_str, 1), 1) ); + set_status_fd ( open_info_file (pargs.r.ret_str, 1) ); break; -#endif /* __riscos__ */ case oAttributeFD: set_attrib_fd(iobuf_translate_file_handle (pargs.r.ret_int, 1)); break; -#ifdef __riscos__ case oAttributeFile: - set_attrib_fd(iobuf_translate_file_handle ( riscos_fdopenfile (pargs.r.ret_str, 1), 1) ); + set_attrib_fd ( open_info_file (pargs.r.ret_str, 1) ); break; -#endif /* __riscos__ */ case oLoggerFD: log_set_fd (iobuf_translate_file_handle (pargs.r.ret_int, 1)); break; -#ifdef __riscos__ - case oLoggerFile: - log_set_logfile( NULL, - iobuf_translate_file_handle ( riscos_fdopenfile (pargs.r.ret_str, 1), 1) ); + case oLoggerFile: + logfile = pargs.r.ret_str; break; -#endif /* __riscos__ */ + case oWithFingerprint: opt.with_fingerprint = 1; with_fpr=1; /*fall thru*/ case oFingerprint: opt.fingerprint++; break; - case oSecretKeyring: append_to_strlist( &sec_nrings, pargs.r.ret_str); break; + case oSecretKeyring: + append_to_strlist( &sec_nrings, pargs.r.ret_str); + break; case oOptions: /* config files may not be nested (silently ignore them) */ if( !configfp ) { - xfree (configname); - configname = xstrdup (pargs.r.ret_str); + xfree(configname); + configname = xstrdup(pargs.r.ret_str); goto next_pass; } break; case oNoArmor: opt.no_armor=1; opt.armor=0; break; case oNoDefKeyring: default_keyring = 0; break; - case oDefCertCheckLevel: opt.def_cert_check_level=pargs.r.ret_int; break; case oNoGreeting: nogreeting = 1; break; - case oNoVerbose: g10_opt_verbose = 0; - opt.verbose = 0; opt.list_sigs=0; break; - /* disabled for now: - case oQuickRandom: quick_random_gen(1); break; */ - case oSKComments: opt.sk_comments=1; break; - case oNoSKComments: opt.sk_comments=0; break; + case oNoVerbose: + opt.verbose = 0; + gcry_control (GCRYCTL_SET_VERBOSITY, (int)opt.verbose); + opt.list_sigs=0; + break; + /* Disabled for now: + case oQuickRandom: quick_random_gen(1); break;*/ case oEmitVersion: opt.no_version=0; break; case oNoEmitVersion: opt.no_version=1; break; case oCompletesNeeded: opt.completes_needed = pargs.r.ret_int; break; @@ -1572,11 +2087,11 @@ main( int argc, char **argv ) opt.def_recipient = make_username(pargs.r.ret_str); break; case oDefRecipientSelf: - xfree (opt.def_recipient); opt.def_recipient = NULL; + xfree(opt.def_recipient); opt.def_recipient = NULL; opt.def_recipient_self = 1; break; case oNoDefRecipient: - xfree (opt.def_recipient); opt.def_recipient = NULL; + xfree(opt.def_recipient); opt.def_recipient = NULL; opt.def_recipient_self = 0; break; case oNoOptions: opt.no_homedir_creation = 1; break; /* no-options */ @@ -1593,16 +2108,7 @@ main( int argc, char **argv ) time. */ case oAlwaysTrust: opt.trust_model=TM_ALWAYS; break; case oTrustModel: - if(ascii_strcasecmp(pargs.r.ret_str,"pgp")==0) - opt.trust_model=TM_PGP; - else if(ascii_strcasecmp(pargs.r.ret_str,"classic")==0) - opt.trust_model=TM_CLASSIC; - else if(ascii_strcasecmp(pargs.r.ret_str,"always")==0) - opt.trust_model=TM_ALWAYS; - else if(ascii_strcasecmp(pargs.r.ret_str,"auto")==0) - opt.trust_model=TM_AUTO; - else - log_error("unknown trust model \"%s\"\n",pargs.r.ret_str); + parse_trust_model(pargs.r.ret_str); break; case oForceOwnertrust: log_info(_("NOTE: %s is not for normal use!\n"), @@ -1610,7 +2116,7 @@ main( int argc, char **argv ) opt.force_ownertrust=string_to_trust_value(pargs.r.ret_str); if(opt.force_ownertrust==-1) { - log_error("invalid ownertrust \"%s\"\n",pargs.r.ret_str); + log_error("invalid ownertrust `%s'\n",pargs.r.ret_str); opt.force_ownertrust=0; } break; @@ -1618,8 +2124,8 @@ main( int argc, char **argv ) #ifndef __riscos__ #if defined(USE_DYNAMIC_LINKING) || defined(_WIN32) if(check_permissions(pargs.r.ret_str,2)) - log_info(_("cipher extension \"%s\" not loaded due to " - "unsafe permissions\n"),pargs.r.ret_str); + log_info(_("cipher extension `%s' not loaded due to" + " unsafe permissions\n"),pargs.r.ret_str); else register_cipher_extension(orig_argc? *orig_argv:NULL, pargs.r.ret_str); @@ -1633,23 +2139,24 @@ main( int argc, char **argv ) opt.force_v4_certs = 0; opt.escape_from = 1; break; - case oRFC2440: case oOpenPGP: - /* TODO: When 2440bis becomes a RFC, these may need - changing. */ + case oRFC2440: + /* TODO: When 2440bis becomes a RFC, set new values for + oOpenPGP. */ + opt.rfc2440_text=1; opt.compliance = CO_RFC2440; opt.allow_non_selfsigned_uid = 1; opt.allow_freeform_uid = 1; opt.pgp2_workarounds = 0; opt.escape_from = 0; opt.force_v3_sigs = 0; - opt.compress_keys = 0; /* not mandated but we do it */ + opt.compress_keys = 0; /* not mandated, but we do it */ opt.compress_sigs = 0; /* ditto. */ opt.not_dash_escaped = 0; opt.def_cipher_algo = 0; opt.def_digest_algo = 0; opt.cert_digest_algo = 0; - opt.def_compress_algo = -1; + opt.compress_algo = -1; opt.s2k_mode = 3; /* iterated+salted */ opt.s2k_digest_algo = DIGEST_ALGO_SHA1; opt.s2k_cipher_algo = CIPHER_ALGO_3DES; @@ -1659,8 +2166,9 @@ main( int argc, char **argv ) case oPGP7: opt.compliance = CO_PGP7; break; case oPGP8: opt.compliance = CO_PGP8; break; case oGnuPG: opt.compliance = CO_GNUPG; break; - case oEmuMDEncodeBug: opt.emulate_bugs |= EMUBUG_MDENCODE; break; case oCompressSigs: opt.compress_sigs = 1; break; + case oRFC2440Text: opt.rfc2440_text=1; break; + case oNoRFC2440Text: opt.rfc2440_text=0; break; case oSetFilename: opt.set_filename = pargs.r.ret_str; break; case oForYourEyesOnly: eyes_only = 1; break; case oNoForYourEyesOnly: eyes_only = 0; break; @@ -1671,17 +2179,28 @@ main( int argc, char **argv ) case oSigPolicyURL: add_policy_url(pargs.r.ret_str,0); break; case oCertPolicyURL: add_policy_url(pargs.r.ret_str,1); break; case oShowPolicyURL: - opt.list_options|=LIST_SHOW_POLICY; - opt.verify_options|=VERIFY_SHOW_POLICY; + deprecated_warning(configname,configlineno,"--show-policy-url", + "--list-options ","show-policy-urls"); + deprecated_warning(configname,configlineno,"--show-policy-url", + "--verify-options ","show-policy-urls"); + opt.list_options|=LIST_SHOW_POLICY_URLS; + opt.verify_options|=VERIFY_SHOW_POLICY_URLS; break; case oNoShowPolicyURL: - opt.list_options&=~LIST_SHOW_POLICY; - opt.verify_options&=~VERIFY_SHOW_POLICY; + deprecated_warning(configname,configlineno,"--no-show-policy-url", + "--list-options ","no-show-policy-urls"); + deprecated_warning(configname,configlineno,"--no-show-policy-url", + "--verify-options ","no-show-policy-urls"); + opt.list_options&=~LIST_SHOW_POLICY_URLS; + opt.verify_options&=~VERIFY_SHOW_POLICY_URLS; break; case oSigKeyserverURL: add_keyserver_url(pargs.r.ret_str,0); break; case oUseEmbeddedFilename: opt.use_embedded_filename = 1; break; - - case oComment: add_to_strlist(&opt.comments,pargs.r.ret_str); break; + case oNoUseEmbeddedFilename: opt.use_embedded_filename = 0; break; + case oComment: + if(pargs.r.ret_str[0]) + append_to_strlist(&opt.comments,pargs.r.ret_str); + break; case oDefaultComment: deprecated_warning(configname,configlineno, "--default-comment","--no-comments",""); @@ -1690,14 +2209,21 @@ main( int argc, char **argv ) free_strlist(opt.comments); opt.comments=NULL; break; - - case oThrowKeyid: opt.throw_keyid = 1; break; - case oNoThrowKeyid: opt.throw_keyid = 0; break; - case oShowPhotos: + case oThrowKeyids: opt.throw_keyid = 1; break; + case oNoThrowKeyids: opt.throw_keyid = 0; break; + case oShowPhotos: + deprecated_warning(configname,configlineno,"--show-photos", + "--list-options ","show-photos"); + deprecated_warning(configname,configlineno,"--show-photos", + "--verify-options ","show-photos"); opt.list_options|=LIST_SHOW_PHOTOS; opt.verify_options|=VERIFY_SHOW_PHOTOS; break; case oNoShowPhotos: + deprecated_warning(configname,configlineno,"--no-show-photos", + "--list-options ","no-show-photos"); + deprecated_warning(configname,configlineno,"--no-show-photos", + "--verify-options ","no-show-photos"); opt.list_options&=~LIST_SHOW_PHOTOS; opt.verify_options&=~VERIFY_SHOW_PHOTOS; break; @@ -1711,8 +2237,8 @@ main( int argc, char **argv ) case oDisableMDC: opt.disable_mdc = 1; break; case oNoDisableMDC: opt.disable_mdc = 0; break; case oS2KMode: opt.s2k_mode = pargs.r.ret_int; break; - case oS2KDigest: s2k_digest_string = xstrdup (pargs.r.ret_str); break; - case oS2KCipher: s2k_cipher_string = xstrdup (pargs.r.ret_str); break; + case oS2KDigest: s2k_digest_string = xstrdup(pargs.r.ret_str); break; + case oS2KCipher: s2k_cipher_string = xstrdup(pargs.r.ret_str); break; case oSimpleSKChecksum: opt.simple_sk_checksum = 1; break; case oNoEncryptTo: opt.no_encrypt_to = 1; break; case oEncryptTo: /* store the recipient in the second list */ @@ -1737,33 +2263,66 @@ main( int argc, char **argv ) case oNoTextmode: opt.textmode=0; break; case oExpert: opt.expert = 1; break; case oNoExpert: opt.expert = 0; break; + case oDefSigExpire: + if(*pargs.r.ret_str!='\0') + { + if(parse_expire_string(pargs.r.ret_str)==(u32)-1) + log_error(_("`%s' is not a valid signature expiration\n"), + pargs.r.ret_str); + else + opt.def_sig_expire=pargs.r.ret_str; + } + break; case oAskSigExpire: opt.ask_sig_expire = 1; break; case oNoAskSigExpire: opt.ask_sig_expire = 0; break; + case oDefCertExpire: + if(*pargs.r.ret_str!='\0') + { + if(parse_expire_string(pargs.r.ret_str)==(u32)-1) + log_error(_("`%s' is not a valid signature expiration\n"), + pargs.r.ret_str); + else + opt.def_cert_expire=pargs.r.ret_str; + } + break; case oAskCertExpire: opt.ask_cert_expire = 1; break; case oNoAskCertExpire: opt.ask_cert_expire = 0; break; - case oUser: /* store the local users */ + case oDefCertLevel: opt.def_cert_level=pargs.r.ret_int; break; + case oMinCertLevel: opt.min_cert_level=pargs.r.ret_int; break; + case oAskCertLevel: opt.ask_cert_level = 1; break; + case oNoAskCertLevel: opt.ask_cert_level = 0; break; + case oLocalUser: /* store the local users */ add_to_strlist2( &locusr, pargs.r.ret_str, utf8_strings ); break; - case oCompress: opt.compress = pargs.r.ret_int; break; + case oCompress: + /* this is the -z command line option */ + opt.compress_level = opt.bz2_compress_level = pargs.r.ret_int; + break; + case oCompressLevel: opt.compress_level = pargs.r.ret_int; break; + case oBZ2CompressLevel: opt.bz2_compress_level = pargs.r.ret_int; break; + case oBZ2DecompressLowmem: opt.bz2_decompress_lowmem=1; break; + case oPasswd: + set_passphrase_from_string(pargs.r.ret_str); + break; case oPasswdFD: pwfd = iobuf_translate_file_handle (pargs.r.ret_int, 0); opt.use_agent = 0; break; -#ifdef __riscos__ case oPasswdFile: - pwfd = iobuf_translate_file_handle ( riscos_fdopenfile (pargs.r.ret_str, 0), 0); + pwfd = open_info_file (pargs.r.ret_str, 0); break; -#endif /* __riscos__ */ case oCommandFD: opt.command_fd = iobuf_translate_file_handle (pargs.r.ret_int, 0); break; -#ifdef __riscos__ case oCommandFile: - opt.command_fd = iobuf_translate_file_handle ( riscos_fdopenfile (pargs.r.ret_str, 0), 0); + opt.command_fd = open_info_file (pargs.r.ret_str, 0); + break; + case oCipherAlgo: + def_cipher_string = xstrdup(pargs.r.ret_str); + break; + case oDigestAlgo: + def_digest_string = xstrdup(pargs.r.ret_str); break; -#endif /* __riscos__ */ - case oCipherAlgo: def_cipher_string = xstrdup (pargs.r.ret_str); break; - case oDigestAlgo: def_digest_string = xstrdup (pargs.r.ret_str); break; case oCompressAlgo: /* If it is all digits, stick a Z in front of it for later. This is for backwards compatibility with @@ -1772,7 +2331,7 @@ main( int argc, char **argv ) char *pt=pargs.r.ret_str; while(*pt) { - if(!isdigit(*pt)) + if (!isascii (*pt) || !isdigit (*pt)) break; pt++; @@ -1780,30 +2339,32 @@ main( int argc, char **argv ) if(*pt=='\0') { - def_compress_string=xmalloc (strlen(pargs.r.ret_str)+2); - strcpy(def_compress_string,"Z"); - strcat(def_compress_string,pargs.r.ret_str); + compress_algo_string=xmalloc(strlen(pargs.r.ret_str)+2); + strcpy(compress_algo_string,"Z"); + strcat(compress_algo_string,pargs.r.ret_str); } else - def_compress_string = xstrdup (pargs.r.ret_str); + compress_algo_string = xstrdup(pargs.r.ret_str); } break; - case oCertDigestAlgo: cert_digest_string = xstrdup (pargs.r.ret_str); break; - case oNoSecmemWarn: - gcry_control (GCRYCTL_DISABLE_SECMEM_WARN); - break; + case oCertDigestAlgo: cert_digest_string = xstrdup(pargs.r.ret_str); break; + case oNoSecmemWarn: secmem_set_flags( secmem_get_flags() | 1 ); break; + case oRequireSecmem: require_secmem=1; break; + case oNoRequireSecmem: require_secmem=0; break; case oNoPermissionWarn: opt.no_perm_warn=1; break; case oNoMDCWarn: opt.no_mdc_warn=1; break; - case oCharset: + case oDisplayCharset: if( set_native_charset( pargs.r.ret_str ) ) - log_error(_("%s is not a valid character set\n"), - pargs.r.ret_str); + log_error(_("`%s' is not a valid character set\n"), + pargs.r.ret_str); break; case oNotDashEscaped: opt.not_dash_escaped = 1; break; case oEscapeFrom: opt.escape_from = 1; break; case oNoEscapeFrom: opt.escape_from = 0; break; case oLockOnce: opt.lock_once = 1; break; - case oLockNever: disable_dotlock(); break; + case oLockNever: + disable_dotlock (); + break; case oLockMultiple: #ifndef __riscos__ opt.lock_once = 0; @@ -1812,15 +2373,31 @@ main( int argc, char **argv ) #endif /* __riscos__ */ break; case oKeyServer: - opt.keyserver_uri=xstrdup (pargs.r.ret_str); - if(parse_keyserver_uri(pargs.r.ret_str,configname,configlineno)) - log_error(_("could not parse keyserver URI\n")); + { + struct keyserver_spec *keyserver; + keyserver=parse_keyserver_uri(pargs.r.ret_str,0, + configname,configlineno); + if(!keyserver) + log_error(_("could not parse keyserver URL\n")); + else + { + keyserver->next=opt.keyserver; + opt.keyserver=keyserver; + } + } break; case oKeyServerOptions: - parse_keyserver_options(pargs.r.ret_str); + if(!parse_keyserver_options(pargs.r.ret_str)) + { + if(configname) + log_error(_("%s:%d: invalid keyserver options\n"), + configname,configlineno); + else + log_error(_("invalid keyserver options\n")); + } break; case oImportOptions: - if(!parse_import_options(pargs.r.ret_str,&opt.import_options)) + if(!parse_import_options(pargs.r.ret_str,&opt.import_options,1)) { if(configname) log_error(_("%s:%d: invalid import options\n"), @@ -1830,7 +2407,7 @@ main( int argc, char **argv ) } break; case oExportOptions: - if(!parse_export_options(pargs.r.ret_str,&opt.export_options)) + if(!parse_export_options(pargs.r.ret_str,&opt.export_options,1)) { if(configname) log_error(_("%s:%d: invalid export options\n"), @@ -1840,44 +2417,45 @@ main( int argc, char **argv ) } break; case oListOptions: - { - struct parse_options lopts[]= - { - {"show-photos",LIST_SHOW_PHOTOS}, - {"show-policy-url",LIST_SHOW_POLICY}, - {"show-notation",LIST_SHOW_NOTATION}, - {"show-keyserver-url",LIST_SHOW_KEYSERVER}, - {"show-validity",LIST_SHOW_VALIDITY}, - {"show-long-keyid",LIST_SHOW_LONG_KEYID}, - {"show-keyring",LIST_SHOW_KEYRING}, - {"show-sig-expire",LIST_SHOW_SIG_EXPIRE}, - {NULL,0} - }; - - if(!parse_options(pargs.r.ret_str,&opt.list_options,lopts)) - { - if(configname) - log_error(_("%s:%d: invalid list options\n"), - configname,configlineno); - else - log_error(_("invalid list options\n")); - } - } + if(!parse_list_options(pargs.r.ret_str)) + { + if(configname) + log_error(_("%s:%d: invalid list options\n"), + configname,configlineno); + else + log_error(_("invalid list options\n")); + } break; case oVerifyOptions: { struct parse_options vopts[]= { - {"show-photos",VERIFY_SHOW_PHOTOS}, - {"show-policy-url",VERIFY_SHOW_POLICY}, - {"show-notation",VERIFY_SHOW_NOTATION}, - {"show-keyserver-url",VERIFY_SHOW_KEYSERVER}, - {"show-validity",VERIFY_SHOW_VALIDITY}, - {"show-long-keyid",VERIFY_SHOW_LONG_KEYID}, - {NULL,0} + {"show-photos",VERIFY_SHOW_PHOTOS,NULL, + N_("display photo IDs during signature verification")}, + {"show-policy-urls",VERIFY_SHOW_POLICY_URLS,NULL, + N_("show policy URLs during signature verification")}, + {"show-notations",VERIFY_SHOW_NOTATIONS,NULL, + N_("show all notations during signature verification")}, + {"show-std-notations",VERIFY_SHOW_STD_NOTATIONS,NULL, + N_("show IETF standard notations during signature verification")}, + {"show-standard-notations",VERIFY_SHOW_STD_NOTATIONS,NULL, + NULL}, + {"show-user-notations",VERIFY_SHOW_USER_NOTATIONS,NULL, + N_("show user-supplied notations during signature verification")}, + {"show-keyserver-urls",VERIFY_SHOW_KEYSERVER_URLS,NULL, + N_("show preferred keyserver URLs during signature verification")}, + {"show-uid-validity",VERIFY_SHOW_UID_VALIDITY,NULL, + N_("show user ID validity during signature verification")}, + {"show-unusable-uids",VERIFY_SHOW_UNUSABLE_UIDS,NULL, + N_("show revoked and expired user IDs in signature verification")}, + {"pka-lookups",VERIFY_PKA_LOOKUPS,NULL, + N_("validate signatures with PKA data")}, + {"pka-trust-increase",VERIFY_PKA_TRUST_INCREASE,NULL, + N_("elevate the trust of signatures with valid PKA data")}, + {NULL,0,NULL,NULL} }; - if(!parse_options(pargs.r.ret_str,&opt.verify_options,vopts)) + if(!parse_options(pargs.r.ret_str,&opt.verify_options,vopts,1)) { if(configname) log_error(_("%s:%d: invalid verify options\n"), @@ -1889,7 +2467,7 @@ main( int argc, char **argv ) break; case oTempDir: opt.temp_dir=pargs.r.ret_str; break; case oExecPath: - if(set_exec_path(pargs.r.ret_str,0)) + if(set_exec_path(pargs.r.ret_str)) log_error(_("unable to set exec-path to %s\n"),pargs.r.ret_str); else opt.exec_path_set=1; @@ -1901,27 +2479,33 @@ main( int argc, char **argv ) case oSigNotation: add_notation_data( pargs.r.ret_str, 0 ); break; case oCertNotation: add_notation_data( pargs.r.ret_str, 1 ); break; case oShowNotation: - opt.list_options|=LIST_SHOW_NOTATION; - opt.verify_options|=VERIFY_SHOW_NOTATION; + deprecated_warning(configname,configlineno,"--show-notation", + "--list-options ","show-notations"); + deprecated_warning(configname,configlineno,"--show-notation", + "--verify-options ","show-notations"); + opt.list_options|=LIST_SHOW_NOTATIONS; + opt.verify_options|=VERIFY_SHOW_NOTATIONS; break; case oNoShowNotation: - opt.list_options&=~LIST_SHOW_NOTATION; - opt.verify_options&=~VERIFY_SHOW_NOTATION; + deprecated_warning(configname,configlineno,"--no-show-notation", + "--list-options ","no-show-notations"); + deprecated_warning(configname,configlineno,"--no-show-notation", + "--verify-options ","no-show-notations"); + opt.list_options&=~LIST_SHOW_NOTATIONS; + opt.verify_options&=~VERIFY_SHOW_NOTATIONS; break; case oUtf8Strings: utf8_strings = 1; break; case oNoUtf8Strings: utf8_strings = 0; break; case oDisableCipherAlgo: { int algo = gcry_cipher_map_name (pargs.r.ret_str); - gcry_cipher_ctl (NULL, GCRYCTL_DISABLE_ALGO, - &algo, sizeof algo); + gcry_cipher_ctl (NULL, GCRYCTL_DISABLE_ALGO, &algo, sizeof algo); } break; case oDisablePubkeyAlgo: { int algo = gcry_pk_map_name (pargs.r.ret_str); - gcry_pk_ctl (GCRYCTL_DISABLE_ALGO, - &algo, sizeof algo ); + gcry_pk_ctl (GCRYCTL_DISABLE_ALGO, &algo, sizeof algo); } break; case oNoSigCache: opt.no_sig_cache = 1; break; @@ -1933,11 +2517,10 @@ main( int argc, char **argv ) case oNoLiteral: opt.no_literal = 1; break; case oSetFilesize: opt.set_filesize = pargs.r.ret_ulong; break; case oHonorHttpProxy: - opt.keyserver_options.honor_http_proxy = 1; + add_to_strlist(&opt.keyserver_options.other,"http-proxy"); deprecated_warning(configname,configlineno, "--honor-http-proxy", - "--keyserver-options ", - "honor-http-proxy"); + "--keyserver-options ","http-proxy"); break; case oFastListMode: opt.fast_list_mode = 1; break; case oFixedListMode: opt.fixed_list_mode = 1; break; @@ -1949,8 +2532,11 @@ main( int argc, char **argv ) case oNoRandomSeedFile: use_random_seed = 0; break; case oAutoKeyRetrieve: case oNoAutoKeyRetrieve: - opt.keyserver_options.auto_key_retrieve= - (pargs.r_opt==oAutoKeyRetrieve); + if(pargs.r_opt==oAutoKeyRetrieve) + opt.keyserver_options.options|=KEYSERVER_AUTO_KEY_RETRIEVE; + else + opt.keyserver_options.options&=~KEYSERVER_AUTO_KEY_RETRIEVE; + deprecated_warning(configname,configlineno, pargs.r_opt==oAutoKeyRetrieve?"--auto-key-retrieve": "--no-auto-key-retrieve","--keyserver-options ", @@ -1961,7 +2547,11 @@ main( int argc, char **argv ) case oOverrideSessionKey: opt.override_session_key = pargs.r.ret_str; break; - case oMergeOnly: opt.merge_only = 1; break; + case oMergeOnly: + deprecated_warning(configname,configlineno,"--merge-only", + "--import-options ","merge-only"); + opt.import_options|=IMPORT_MERGE_ONLY; + break; case oAllowSecretKeyImport: /* obsolete */ break; case oTryAllSecrets: opt.try_all_secrets = 1; break; case oTrustedKey: register_trusted_key( pargs.r.ret_str ); break; @@ -1991,30 +2581,95 @@ main( int argc, char **argv ) case oLCctype: opt.lc_ctype = pargs.r.ret_str; break; case oLCmessages: opt.lc_messages = pargs.r.ret_str; break; case oGroup: add_group(pargs.r.ret_str); break; + case oUnGroup: rm_group(pargs.r.ret_str); break; + case oNoGroups: + while(opt.grouplist) + { + struct groupitem *iter=opt.grouplist; + free_strlist(iter->values); + opt.grouplist=opt.grouplist->next; + xfree(iter); + } + break; case oStrict: opt.strict=1; log_set_strict(1); break; case oNoStrict: opt.strict=0; log_set_strict(0); break; - case oMangleDosFilenames: opt.mangle_dos_filenames = 1; break; case oNoMangleDosFilenames: opt.mangle_dos_filenames = 0; break; - case oEnableProgressFilter: opt.enable_progress_filter = 1; break; - case oMultifile: multifile=1; break; + case oMultifile: multifile=1; break; + case oKeyidFormat: + if(ascii_strcasecmp(pargs.r.ret_str,"short")==0) + opt.keyid_format=KF_SHORT; + else if(ascii_strcasecmp(pargs.r.ret_str,"long")==0) + opt.keyid_format=KF_LONG; + else if(ascii_strcasecmp(pargs.r.ret_str,"0xshort")==0) + opt.keyid_format=KF_0xSHORT; + else if(ascii_strcasecmp(pargs.r.ret_str,"0xlong")==0) + opt.keyid_format=KF_0xLONG; + else + log_error("unknown keyid-format `%s'\n",pargs.r.ret_str); + break; + + case oExitOnStatusWriteError: + opt.exit_on_status_write_error = 1; + break; + + case oLimitCardInsertTries: + opt.limit_card_insert_tries = pargs.r.ret_int; + break; + + case oRequireCrossCert: opt.flags.require_cross_cert=1; break; + case oNoRequireCrossCert: opt.flags.require_cross_cert=0; break; + + case oAutoKeyLocate: + if(!parse_auto_key_locate(pargs.r.ret_str)) + { + if(configname) + log_error(_("%s:%d: invalid auto-key-locate list\n"), + configname,configlineno); + else + log_error(_("invalid auto-key-locate list\n")); + } + break; + case oNoAutoKeyLocate: + release_akl(); + break; + + case oAllowMultisigVerification: + opt.allow_multisig_verification = 1; + break; + + case oNoop: break; default : pargs.err = configfp? 1:2; break; - } - } + } + } + if( configfp ) { fclose( configfp ); configfp = NULL; - config_filename = configname; /* Keep a copy of the config - file name. */ - configname = NULL; + /* Remember the first config file name. */ + if (!save_configname) + save_configname = configname; + else + xfree(configname); + configname = NULL; goto next_pass; } - xfree ( configname ); configname = NULL; + xfree( configname ); configname = NULL; if( log_get_errorcount(0) ) g10_exit(2); + + /* The command --gpgconf-list is pretty simple and may be called + directly after the option parsing. */ + if (cmd == aGPGConfList) + { + gpgconf_list (save_configname); + g10_exit (0); + } + xfree (save_configname); + if( nogreeting ) greeting = 0; @@ -2024,20 +2679,23 @@ main( int argc, char **argv ) fprintf(stderr, "%s\n", strusage(15) ); } #ifdef IS_DEVELOPMENT_VERSION - if( !opt.batch ) { - log_info("NOTE: THIS IS A DEVELOPMENT VERSION!\n"); - log_info("It is only intended for test purposes and should NOT be\n"); - log_info("used in a production environment or with production keys!\n"); - } + if( !opt.batch ) + { + const char *s; + + if((s=strusage(20))) + log_info("%s\n",s); + if((s=strusage(21))) + log_info("%s\n",s); + if((s=strusage(22))) + log_info("%s\n",s); + } #endif - log_info ("WARNING: This version of gpg is not very matured and\n"); - log_info ("WARNING: only intended for testing. Please keep using\n"); - log_info ("WARNING: gpg 1.2.x, 1.3.x or 1.4.x for OpenPGP\n"); - - /* FIXME: We should use the lggging to a file only in server mode; - however we have not yet implemetyed that thus we try to get - away with --batch as indication for logging to file required. */ + /* FIXME: We should use logging to a file only in server mode; + however we have not yet implemetyed that. Thus we try to get + away with --batch as indication for logging to file + required. */ if (logfile && opt.batch) { log_set_file (logfile); @@ -2069,12 +2727,21 @@ main( int argc, char **argv ) "--no-literal" ); } + if (opt.set_filesize) log_info(_("NOTE: %s is not for normal use!\n"), "--set-filesize"); if( opt.batch ) tty_batchmode( 1 ); gcry_control (GCRYCTL_RESUME_SECMEM_WARN); + + if(require_secmem && !got_secmem) + { + log_info(_("will not run with insecure memory due to %s\n"), + "--require-secmem"); + g10_exit(2); + } + set_debug (debug_level); /* Do these after the switch(), so they can override settings. */ @@ -2107,7 +2774,7 @@ main( int argc, char **argv ) preference, but those have their own error messages). */ - if(openpgp_cipher_test_algo (CIPHER_ALGO_IDEA)) + if (openpgp_cipher_test_algo(CIPHER_ALGO_IDEA)) { log_info(_("encrypting a message in --pgp2 mode requires " "the IDEA cipher\n")); @@ -2119,8 +2786,8 @@ main( int argc, char **argv ) /* This only sets IDEA for symmetric encryption since it is set via select_algo_from_prefs for pk encryption. */ - xfree (def_cipher_string); - def_cipher_string = xstrdup ("idea"); + xfree(def_cipher_string); + def_cipher_string = xstrdup("idea"); } /* PGP2 can't handle the output from the textmode @@ -2137,27 +2804,26 @@ main( int argc, char **argv ) else { opt.force_v4_certs = 0; - opt.sk_comments = 0; opt.escape_from = 1; opt.force_v3_sigs = 1; opt.pgp2_workarounds = 1; opt.ask_sig_expire = 0; opt.ask_cert_expire = 0; - xfree (def_digest_string); - def_digest_string = xstrdup ("md5"); - opt.def_compress_algo = 1; + xfree(def_digest_string); + def_digest_string = xstrdup("md5"); + xfree(s2k_digest_string); + s2k_digest_string = xstrdup("md5"); + opt.compress_algo = COMPRESS_ALGO_ZIP; } } else if(PGP6) { - opt.sk_comments=0; opt.escape_from=1; opt.force_v3_sigs=1; opt.ask_sig_expire=0; } else if(PGP7) { - opt.sk_comments=0; opt.escape_from=1; opt.force_v3_sigs=1; opt.ask_sig_expire=0; @@ -2167,54 +2833,57 @@ main( int argc, char **argv ) opt.escape_from=1; } - /* must do this after dropping setuid, because string_to... - * may try to load an module */ + if( def_cipher_string ) { opt.def_cipher_algo = gcry_cipher_map_name (def_cipher_string); if(opt.def_cipher_algo==0 && (ascii_strcasecmp(def_cipher_string,"idea")==0 || ascii_strcasecmp(def_cipher_string,"s1")==0)) idea_cipher_warn(1); - xfree (def_cipher_string); def_cipher_string = NULL; - if( openpgp_cipher_test_algo (opt.def_cipher_algo) ) + xfree(def_cipher_string); def_cipher_string = NULL; + if ( openpgp_cipher_test_algo (opt.def_cipher_algo) ) log_error(_("selected cipher algorithm is invalid\n")); } if( def_digest_string ) { opt.def_digest_algo = gcry_md_map_name (def_digest_string); - xfree (def_digest_string); def_digest_string = NULL; - if( openpgp_md_test_algo (opt.def_digest_algo) ) + xfree(def_digest_string); def_digest_string = NULL; + if ( openpgp_md_test_algo (opt.def_digest_algo) ) log_error(_("selected digest algorithm is invalid\n")); } - if( def_compress_string ) { - opt.def_compress_algo = string_to_compress_algo(def_compress_string); - xfree (def_compress_string); def_compress_string = NULL; - if( check_compress_algo(opt.def_compress_algo) ) - log_error(_("selected compression algorithm is invalid\n")); + if( compress_algo_string ) { + opt.compress_algo = string_to_compress_algo(compress_algo_string); + xfree(compress_algo_string); compress_algo_string = NULL; + if( check_compress_algo(opt.compress_algo) ) + log_error(_("selected compression algorithm is invalid\n")); } if( cert_digest_string ) { opt.cert_digest_algo = gcry_md_map_name (cert_digest_string); - xfree (cert_digest_string); cert_digest_string = NULL; - if( openpgp_md_test_algo(opt.cert_digest_algo) ) - log_error(_("selected certification digest algorithm is invalid\n")); + xfree(cert_digest_string); cert_digest_string = NULL; + if (openpgp_md_test_algo(opt.cert_digest_algo)) + log_error(_("selected certification digest algorithm is invalid\n")); } if( s2k_cipher_string ) { opt.s2k_cipher_algo = gcry_cipher_map_name (s2k_cipher_string); - xfree (s2k_cipher_string); s2k_cipher_string = NULL; - if( openpgp_cipher_test_algo (opt.s2k_cipher_algo) ) - log_error(_("selected cipher algorithm is invalid\n")); + xfree(s2k_cipher_string); s2k_cipher_string = NULL; + if (openpgp_cipher_test_algo (opt.s2k_cipher_algo)) + log_error(_("selected cipher algorithm is invalid\n")); } if( s2k_digest_string ) { opt.s2k_digest_algo = gcry_md_map_name (s2k_digest_string); - xfree (s2k_digest_string); s2k_digest_string = NULL; - if( openpgp_md_test_algo (opt.s2k_digest_algo) ) - log_error(_("selected digest algorithm is invalid\n")); + xfree(s2k_digest_string); s2k_digest_string = NULL; + if (openpgp_md_test_algo(opt.s2k_digest_algo)) + log_error(_("selected digest algorithm is invalid\n")); } if( opt.completes_needed < 1 ) - log_error(_("completes-needed must be greater than 0\n")); + log_error(_("completes-needed must be greater than 0\n")); if( opt.marginals_needed < 2 ) - log_error(_("marginals-needed must be greater than 1\n")); + log_error(_("marginals-needed must be greater than 1\n")); if( opt.max_cert_depth < 1 || opt.max_cert_depth > 255 ) - log_error(_("max-cert-depth must be in range 1 to 255\n")); + log_error(_("max-cert-depth must be in the range from 1 to 255\n")); + if(opt.def_cert_level<0 || opt.def_cert_level>3) + log_error(_("invalid default-cert-level; must be 0, 1, 2, or 3\n")); + if( opt.min_cert_level < 1 || opt.min_cert_level > 3 ) + log_error(_("invalid min-cert-level; must be 1, 2, or 3\n")); switch( opt.s2k_mode ) { case 0: log_info(_("NOTE: simple S2K mode (0) is strongly discouraged\n")); @@ -2224,16 +2893,14 @@ main( int argc, char **argv ) log_error(_("invalid S2K mode; must be 0, 1 or 3\n")); } - if(opt.def_cert_check_level<0 || opt.def_cert_check_level>3) - log_error(_("invalid default-check-level; must be 0, 1, 2, or 3\n")); - /* This isn't actually needed, but does serve to error out if the string is invalid. */ if(opt.def_preference_list && keygen_set_std_prefs(opt.def_preference_list,0)) log_error(_("invalid default preferences\n")); - /* We provide defaults for the personal digest list */ + /* We provide defaults for the personal digest list. This is + SHA-1. */ if(!pers_digest_list) pers_digest_list="h2"; @@ -2253,7 +2920,7 @@ main( int argc, char **argv ) if(multifile) { char *cmdname; - + switch(cmd) { case aSign: @@ -2268,6 +2935,9 @@ main( int argc, char **argv ) case aSym: cmdname="--symmetric"; break; + case aEncrSym: + cmdname="--symmetric --encrypt"; + break; case aStore: cmdname="--store"; break; @@ -2283,6 +2953,9 @@ main( int argc, char **argv ) if( log_get_errorcount(0) ) g10_exit(2); + if(opt.compress_level==0) + opt.compress_algo=COMPRESS_ALGO_NONE; + /* Check our chosen algorithms against the list of legal algorithms. */ @@ -2291,48 +2964,48 @@ main( int argc, char **argv ) const char *badalg=NULL; preftype_t badtype=PREFTYPE_NONE; - if (opt.def_cipher_algo - && !algo_available (PREFTYPE_SYM,opt.def_cipher_algo,NULL)) + if(opt.def_cipher_algo + && !algo_available(PREFTYPE_SYM,opt.def_cipher_algo,NULL)) { badalg = gcry_cipher_algo_name (opt.def_cipher_algo); badtype = PREFTYPE_SYM; } - else if (opt.def_digest_algo - && !algo_available (PREFTYPE_HASH,opt.def_digest_algo,NULL)) + else if(opt.def_digest_algo + && !algo_available(PREFTYPE_HASH,opt.def_digest_algo,NULL)) { badalg = gcry_md_algo_name (opt.def_digest_algo); badtype = PREFTYPE_HASH; } - else if (opt.cert_digest_algo - && !algo_available (PREFTYPE_HASH,opt.cert_digest_algo,NULL)) + else if(opt.cert_digest_algo + && !algo_available(PREFTYPE_HASH,opt.cert_digest_algo,NULL)) { badalg = gcry_md_algo_name (opt.cert_digest_algo); badtype = PREFTYPE_HASH; } - else if (opt.def_compress_algo!=-1 - && !algo_available (PREFTYPE_ZIP,opt.def_compress_algo,NULL)) + else if(opt.compress_algo!=-1 + && !algo_available(PREFTYPE_ZIP,opt.compress_algo,NULL)) { - badalg = compress_algo_to_string (opt.def_compress_algo); + badalg = compress_algo_to_string(opt.compress_algo); badtype = PREFTYPE_ZIP; } - if (badalg) + if(badalg) { switch(badtype) { case PREFTYPE_SYM: - log_info(_("you may not use cipher algorithm \"%s\" " - "while in %s mode\n"), + log_info(_("you may not use cipher algorithm `%s'" + " while in %s mode\n"), badalg,compliance_option_string()); break; case PREFTYPE_HASH: - log_info(_("you may not use digest algorithm \"%s\" " - "while in %s mode\n"), + log_info(_("you may not use digest algorithm `%s'" + " while in %s mode\n"), badalg,compliance_option_string()); break; case PREFTYPE_ZIP: - log_info(_("you may not use compression algorithm \"%s\" " - "while in %s mode\n"), + log_info(_("you may not use compression algorithm `%s'" + " while in %s mode\n"), badalg,compliance_option_string()); break; default: @@ -2343,20 +3016,33 @@ main( int argc, char **argv ) } } - /* set the random seed file */ + /* Set the random seed file. */ if( use_random_seed ) { char *p = make_filename(opt.homedir, "random_seed", NULL ); + set_random_seed_file(p); gcry_control (GCRYCTL_SET_RANDOM_SEED_FILE, p); - xfree (p); + if (!access (p, F_OK)) + register_secured_file (p); + xfree(p); } if( !cmd && opt.fingerprint && !with_fpr ) { set_cmd( &cmd, aListKeys); } - /* Compression algorithm 0 means no compression at all */ - if( opt.def_compress_algo == 0) - opt.compress = 0; + if( cmd == aKMode || cmd == aKModeC ) { /* kludge to be compatible to pgp */ + if( cmd == aKModeC ) { + opt.fingerprint = 1; + cmd = aKMode; + } + opt.list_sigs = 0; + if( opt.verbose > 2 ) + opt.check_sigs++; + if( opt.verbose > 1 ) + opt.list_sigs++; + + opt.verbose = opt.verbose > 1; + } /* kludge to let -sat generate a clear text signature */ if( opt.textmode == 2 && !detached_sig && opt.armor && cmd == aSign ) @@ -2365,22 +3051,29 @@ main( int argc, char **argv ) if( opt.verbose > 1 ) set_packet_list_mode(1); - /* Add the keyrings, but not for some special commands. Also - avoid adding the secret keyring for a couple of commands to - avoid unneeded access in case the secrings are stored on a - floppy */ - if( cmd != aDeArmor && cmd != aEnArmor && cmd != aGPGConfList ) + /* Add the keyrings, but not for some special commands and not in + case of "-kvv userid keyring". Also avoid adding the secret + keyring for a couple of commands to avoid unneeded access in + case the secrings are stored on a floppy. + + We always need to add the keyrings if we are running under + SELinux, this is so that the rings are added to the list of + secured files. */ + if( ALWAYS_ADD_KEYRINGS + || (cmd != aDeArmor && cmd != aEnArmor + && !(cmd == aKMode && argc == 2 )) ) { - if (cmd != aCheckKeys && cmd != aListSigs && cmd != aListKeys - && cmd != aVerify && cmd != aSym) + if (ALWAYS_ADD_KEYRINGS + || (cmd != aCheckKeys && cmd != aListSigs && cmd != aListKeys + && cmd != aVerify && cmd != aSym)) { if (!sec_nrings || default_keyring) /* add default secret rings */ - keydb_add_resource ("secring" EXTSEP_S "gpg", 0, 1); + keydb_add_resource ("secring" EXTSEP_S "gpg", 4, 1); for (sl = sec_nrings; sl; sl = sl->next) keydb_add_resource ( sl->d, 0, 1 ); } if( !nrings || default_keyring ) /* add default ring */ - keydb_add_resource ("pubring" EXTSEP_S "gpg", 0, 0); + keydb_add_resource ("pubring" EXTSEP_S "gpg", 4, 0); for(sl = nrings; sl; sl = sl->next ) keydb_add_resource ( sl->d, sl->flags, 0 ); } @@ -2401,20 +3094,17 @@ main( int argc, char **argv ) case aDeArmor: case aEnArmor: case aFixTrustDB: - case aCardStatus: - case aCardEdit: - case aChangePIN: - case aGPGConfList: break; case aExportOwnerTrust: rc = setup_trustdb( 0, trustdb_name ); break; case aListTrustDB: rc = setup_trustdb( argc? 1:0, trustdb_name ); break; default: rc = setup_trustdb(1, trustdb_name ); break; } if( rc ) - log_error(_("failed to initialize the TrustDB: %s\n"), gpg_strerror (rc)); + log_error(_("failed to initialize the TrustDB: %s\n"), g10_errstr(rc)); - switch (cmd) { + switch (cmd) + { case aStore: case aSym: case aSign: @@ -2426,22 +3116,23 @@ main( int argc, char **argv ) break; default: break; - } + } - switch( cmd ) { + switch( cmd ) + { case aStore: /* only store the file */ if( argc > 1 ) wrong_args(_("--store [filename]")); if( (rc = encode_store(fname)) ) - log_error ("\b%s: store failed: %s\n", - print_fname_stdin(fname), gpg_strerror (rc) ); + log_error ("storing `%s' failed: %s\n", + print_fname_stdin(fname),g10_errstr(rc) ); break; case aSym: /* encrypt the given file only with the symmetric cipher */ if( argc > 1 ) wrong_args(_("--symmetric [filename]")); if( (rc = encode_symmetric(fname)) ) - log_error ("\b%s: symmetric encryption failed: %s\n", - print_fname_stdin(fname), gpg_strerror (rc) ); + log_error (_("symmetric encryption of `%s' failed: %s\n"), + print_fname_stdin(fname),g10_errstr(rc) ); break; case aEncr: /* encrypt the given file */ @@ -2449,12 +3140,33 @@ main( int argc, char **argv ) encode_crypt_files(argc, argv, remusr); else { - if( argc > 1 ) - wrong_args(_("--encrypt [filename]")); - if( (rc = encode_crypt(fname,remusr)) ) - log_error("%s: encryption failed: %s\n", - print_fname_stdin(fname), gpg_strerror (rc) ); - } + if( argc > 1 ) + wrong_args(_("--encrypt [filename]")); + if( (rc = encode_crypt(fname,remusr,0)) ) + log_error("%s: encryption failed: %s\n", + print_fname_stdin(fname), g10_errstr(rc) ); + } + break; + + case aEncrSym: + /* This works with PGP 8 in the sense that it acts just like a + symmetric message. It doesn't work at all with 2 or 6. It + might work with 7, but alas, I don't have a copy to test + with right now. */ + if( argc > 1 ) + wrong_args(_("--symmetric --encrypt [filename]")); + else if(opt.s2k_mode==0) + log_error(_("you cannot use --symmetric --encrypt" + " with --s2k-mode 0\n")); + else if(PGP2 || PGP6 || PGP7 || RFC1991) + log_error(_("you cannot use --symmetric --encrypt" + " while in %s mode\n"),compliance_option_string()); + else + { + if( (rc = encode_crypt(fname,remusr,1)) ) + log_error("%s: encryption failed: %s\n", + print_fname_stdin(fname), g10_errstr(rc) ); + } break; case aSign: /* sign the given file */ @@ -2467,12 +3179,12 @@ main( int argc, char **argv ) if( argc > 1 ) wrong_args(_("--sign [filename]")); if( argc ) { - sl = xcalloc (1, sizeof *sl + strlen(fname)); + sl = xmalloc_clear( sizeof *sl + strlen(fname)); strcpy(sl->d, fname); } } if( (rc = sign_file( sl, detached_sig, locusr, 0, NULL, NULL)) ) - log_error("signing failed: %s\n", gpg_strerror (rc) ); + log_error("signing failed: %s\n", g10_errstr(rc) ); free_strlist(sl); break; @@ -2480,23 +3192,49 @@ main( int argc, char **argv ) if( argc > 1 ) wrong_args(_("--sign --encrypt [filename]")); if( argc ) { - sl = xcalloc (1, sizeof *sl + strlen(fname)); + sl = xmalloc_clear( sizeof *sl + strlen(fname)); strcpy(sl->d, fname); } else sl = NULL; if( (rc = sign_file(sl, detached_sig, locusr, 1, remusr, NULL)) ) - log_error("%s: sign+encrypt failed: %s\n", print_fname_stdin(fname), gpg_strerror (rc) ); + log_error("%s: sign+encrypt failed: %s\n", + print_fname_stdin(fname), g10_errstr(rc) ); free_strlist(sl); break; + case aSignEncrSym: /* sign and encrypt the given file */ + if( argc > 1 ) + wrong_args(_("--symmetric --sign --encrypt [filename]")); + else if(opt.s2k_mode==0) + log_error(_("you cannot use --symmetric --sign --encrypt" + " with --s2k-mode 0\n")); + else if(PGP2 || PGP6 || PGP7 || RFC1991) + log_error(_("you cannot use --symmetric --sign --encrypt" + " while in %s mode\n"),compliance_option_string()); + else + { + if( argc ) + { + sl = xmalloc_clear( sizeof *sl + strlen(fname)); + strcpy(sl->d, fname); + } + else + sl = NULL; + if( (rc = sign_file(sl, detached_sig, locusr, 2, remusr, NULL)) ) + log_error("%s: symmetric+sign+encrypt failed: %s\n", + print_fname_stdin(fname), g10_errstr(rc) ); + free_strlist(sl); + } + break; + case aSignSym: /* sign and conventionally encrypt the given file */ if (argc > 1) wrong_args(_("--sign --symmetric [filename]")); rc = sign_symencrypt_file (fname, locusr); if (rc) log_error("%s: sign+symmetric failed: %s\n", - print_fname_stdin(fname), gpg_strerror (rc) ); + print_fname_stdin(fname), g10_errstr(rc) ); break; case aClearsign: /* make a clearsig */ @@ -2504,65 +3242,58 @@ main( int argc, char **argv ) wrong_args(_("--clearsign [filename]")); if( (rc = clearsign_file(fname, locusr, NULL)) ) log_error("%s: clearsign failed: %s\n", - print_fname_stdin(fname), gpg_strerror (rc) ); + print_fname_stdin(fname), g10_errstr(rc) ); break; case aVerify: - if(multifile) + if(multifile) { if( (rc = verify_files( argc, argv ) )) - log_error("verify files failed: %s\n", gpg_strerror (rc) ); + log_error("verify files failed: %s\n", g10_errstr(rc) ); + } + else + { + if( (rc = verify_signatures( argc, argv ) )) + log_error("verify signatures failed: %s\n", g10_errstr(rc) ); } - else - { - if( (rc = verify_signatures( argc, argv ) )) - log_error("verify signatures failed: %s\n", gpg_strerror (rc) ); - } break; case aDecrypt: if(multifile) decrypt_messages(argc, argv); else - { - if( argc > 1 ) - wrong_args(_("--decrypt [filename]")); - if( (rc = decrypt_message( fname ) )) - log_error("decrypt_message failed: %s\n", gpg_strerror (rc) ); - } + { + if( argc > 1 ) + wrong_args(_("--decrypt [filename]")); + if( (rc = decrypt_message( fname ) )) + log_error("decrypt_message failed: %s\n", g10_errstr(rc) ); + } break; - - case aSignKey: /* sign the key given as argument */ + + case aSignKey: if( argc != 1 ) - wrong_args(_("--sign-key user-id")); - username = make_username( fname ); - keyedit_menu(fname, locusr, NULL, 1 ); - xfree (username); - break; - + wrong_args(_("--sign-key user-id")); + /* fall through */ case aLSignKey: if( argc != 1 ) - wrong_args(_("--lsign-key user-id")); - username = make_username( fname ); - keyedit_menu(fname, locusr, NULL, 2 ); - xfree (username); - break; + wrong_args(_("--lsign-key user-id")); + /* fall through */ - case aNRSignKey: - if( argc != 1 ) - wrong_args(_("--nrsign-key user-id")); - username = make_username( fname ); - keyedit_menu(fname, locusr, NULL, 3 ); - xfree (username); - break; + sl=NULL; - case aNRLSignKey: - if( argc != 1 ) - wrong_args(_("--nrlsign-key user-id")); + if(cmd==aSignKey) + append_to_strlist(&sl,"sign"); + else if(cmd==aLSignKey) + append_to_strlist(&sl,"lsign"); + else + BUG(); + + append_to_strlist( &sl, "save" ); username = make_username( fname ); - keyedit_menu(fname, locusr, NULL, 4 ); - xfree (username); - break; + keyedit_menu(fname, locusr, sl, 0, 0 ); + xfree(username); + free_strlist(sl); + break; case aEditKey: /* Edit a key signature */ if( !argc ) @@ -2572,12 +3303,12 @@ main( int argc, char **argv ) sl = NULL; for( argc--, argv++ ; argc; argc--, argv++ ) append_to_strlist( &sl, *argv ); - keyedit_menu( username, locusr, sl, 0 ); + keyedit_menu( username, locusr, sl, 0, 1 ); free_strlist(sl); } else - keyedit_menu(username, locusr, NULL, 0 ); - xfree (username); + keyedit_menu(username, locusr, NULL, 0, 1 ); + xfree(username); break; case aDeleteKeys: @@ -2612,27 +3343,54 @@ main( int argc, char **argv ) free_strlist(sl); break; + case aKMode: /* list keyring -- NOTE: This will be removed soon */ + if( argc < 2 ) { /* -kv [userid] */ + sl = NULL; + if (argc && **argv) + add_to_strlist2( &sl, *argv, utf8_strings ); + public_key_list( sl ); + free_strlist(sl); + } + else if( argc == 2 ) { /* -kv userid keyring */ + if( access( argv[1], R_OK ) ) { + log_error(_("can't open `%s': %s\n"), + print_fname_stdin(argv[1]), strerror(errno)); + } + else { + /* add keyring (default keyrings are not registered in this + * special case */ + keydb_add_resource( argv[1], 0, 0 ); + sl = NULL; + if (**argv) + add_to_strlist2( &sl, *argv, utf8_strings ); + public_key_list( sl ); + free_strlist(sl); + } + } + else + wrong_args(_("-k[v][v][v][c] [user-id] [keyring]") ); + break; + case aKeygen: /* generate a key */ if( opt.batch ) { if( argc > 1 ) wrong_args("--gen-key [parameterfile]"); - generate_keypair( argc? *argv : NULL, NULL ); + generate_keypair( argc? *argv : NULL, NULL, NULL ); } else { if( argc ) wrong_args("--gen-key"); - generate_keypair(NULL, NULL); + generate_keypair(NULL, NULL, NULL); } break; case aFastImport: - opt.import_options |= IMPORT_FAST_IMPORT; + opt.import_options |= IMPORT_FAST; case aImport: import_keys( argc? argv:NULL, argc, NULL, opt.import_options ); break; case aExport: - case aExportAll: case aSendKeys: case aRecvKeys: sl = NULL; @@ -2647,11 +3405,11 @@ main( int argc, char **argv ) if(rc) { if(cmd==aSendKeys) - log_error(_("keyserver send failed: %s\n"),gpg_strerror (rc)); + log_error(_("keyserver send failed: %s\n"),g10_errstr(rc)); else if(cmd==aRecvKeys) - log_error(_("keyserver receive failed: %s\n"),gpg_strerror (rc)); + log_error(_("keyserver receive failed: %s\n"),g10_errstr(rc)); else - log_error(_("key export failed: %s\n"),gpg_strerror (rc)); + log_error(_("key export failed: %s\n"),g10_errstr(rc)); } free_strlist(sl); break; @@ -2659,20 +3417,10 @@ main( int argc, char **argv ) case aSearchKeys: sl = NULL; for( ; argc; argc--, argv++ ) - { - if (utf8_strings) - sl = append_to_strlist ( &sl, *argv ); - else - { - char *p = native_to_utf8 ( *argv ); - sl = append_to_strlist( &sl, p ); - xfree( p ); - } - } - + append_to_strlist2( &sl, *argv, utf8_strings ); rc=keyserver_search( sl ); if(rc) - log_error(_("keyserver search failed: %s\n"),gpg_strerror (rc)); + log_error(_("keyserver search failed: %s\n"),g10_errstr(rc)); free_strlist(sl); break; @@ -2682,7 +3430,17 @@ main( int argc, char **argv ) add_to_strlist2( &sl, *argv, utf8_strings ); rc=keyserver_refresh(sl); if(rc) - log_error(_("keyserver refresh failed: %s\n"),gpg_strerror (rc)); + log_error(_("keyserver refresh failed: %s\n"),g10_errstr(rc)); + free_strlist(sl); + break; + + case aFetchKeys: + sl = NULL; + for( ; argc; argc--, argv++ ) + add_to_strlist2( &sl, *argv, utf8_strings ); + rc=keyserver_fetch(sl); + if(rc) + log_error("key fetch failed: %s\n",g10_errstr(rc)); free_strlist(sl); break; @@ -2707,15 +3465,15 @@ main( int argc, char **argv ) wrong_args("--gen-revoke user-id"); username = make_username(*argv); gen_revoke( username ); - xfree ( username ); + xfree( username ); break; case aDesigRevoke: if( argc != 1 ) wrong_args("--desig-revoke user-id"); username = make_username(*argv); - gen_desig_revoke( username ); - xfree ( username ); + gen_desig_revoke( username, locusr ); + xfree( username ); break; case aDeArmor: @@ -2723,7 +3481,7 @@ main( int argc, char **argv ) wrong_args("--dearmor [file]"); rc = dearmor_file( argc? *argv: NULL ); if( rc ) - log_error(_("dearmoring failed: %s\n"), gpg_strerror (rc)); + log_error(_("dearmoring failed: %s\n"), g10_errstr(rc)); break; case aEnArmor: @@ -2731,12 +3489,12 @@ main( int argc, char **argv ) wrong_args("--enarmor [file]"); rc = enarmor_file( argc? *argv: NULL ); if( rc ) - log_error(_("enarmoring failed: %s\n"), gpg_strerror (rc)); + log_error(_("enarmoring failed: %s\n"), g10_errstr(rc)); break; case aPrimegen: -#if 0 /*FIXME-XXX*/ +#if 0 /*FIXME*/ { int mode = argc < 2 ? 0 : atoi(*argv); if( mode == 1 && argc == 2 ) { @@ -2748,7 +3506,7 @@ main( int argc, char **argv ) atoi(argv[2]), NULL,NULL ), 1); } else if( mode == 3 && argc == 3 ) { - gcry_mpi_t *factors; + MPI *factors; mpi_print( stdout, generate_elg_prime( 1, atoi(argv[1]), atoi(argv[2]), NULL,&factors ), 1); @@ -2756,7 +3514,7 @@ main( int argc, char **argv ) mpi_print( stdout, factors[0], 1 ); /* print q */ } else if( mode == 4 && argc == 3 ) { - gcry_mpi_t g = mpi_alloc(1); + MPI g = mpi_alloc(1); mpi_print( stdout, generate_elg_prime( 0, atoi(argv[1]), atoi(argv[2]), g, NULL ), 1); @@ -2769,6 +3527,7 @@ main( int argc, char **argv ) putchar('\n'); } #endif + wrong_args("--gen-prime not yet supported "); break; case aGenRandom: @@ -2803,7 +3562,7 @@ main( int argc, char **argv ) } else { fwrite( p, n, 1, stdout ); } - xfree (p); + xfree(p); if( !endless ) count -= n; } @@ -2874,7 +3633,7 @@ main( int argc, char **argv ) for( ; argc; argc--, argv++ ) { username = make_username( *argv ); list_trust_path( username ); - xfree (username); + xfree(username); } break; @@ -2890,84 +3649,48 @@ main( int argc, char **argv ) import_ownertrust( argc? *argv:NULL ); break; - case aPipeMode: - if ( argc ) - wrong_args ("--pipemode"); - run_in_pipemode (); - break; - case aRebuildKeydbCaches: if (argc) wrong_args ("--rebuild-keydb-caches"); - keydb_rebuild_caches (); + keydb_rebuild_caches (1); break; - case aCardStatus: - if (argc) - wrong_args ("--card-status"); - card_status (stdout, NULL, 0); - break; +#ifdef ENABLE_CARD_SUPPORT + case aCardStatus: + if (argc) + wrong_args ("--card-status"); + card_status (stdout, NULL, 0); + break; - case aCardEdit: - if (argc) - { - sl = NULL; - for (argc--, argv++ ; argc; argc--, argv++) - append_to_strlist (&sl, *argv); - card_edit (sl); - free_strlist (sl); + case aCardEdit: + if (argc) { + sl = NULL; + for (argc--, argv++ ; argc; argc--, argv++) + append_to_strlist (&sl, *argv); + card_edit (sl); + free_strlist (sl); } - else - card_edit (NULL); - break; + else + card_edit (NULL); + break; - case aChangePIN: - if (!argc) - change_pin (0,1); - else if (argc == 1) - change_pin ( atoi (*argv), 1); - else + case aChangePIN: + if (!argc) + change_pin (0,1); + else if (argc == 1) + change_pin (atoi (*argv),1); + else wrong_args ("--change-pin [no]"); - break; - - case aGPGConfList: - { /* List options and default values in the GPG Conf format. */ - - /* The following list is taken from gnupg/tools/gpgconf-comp.c. */ - /* Option flags. YOU MUST NOT CHANGE THE NUMBERS OF THE EXISTING - FLAGS, AS THEY ARE PART OF THE EXTERNAL INTERFACE. */ -#define GC_OPT_FLAG_NONE 0UL - /* The RUNTIME flag for an option indicates that the option can be - changed at runtime. */ -#define GC_OPT_FLAG_RUNTIME (1UL << 3) - /* The DEFAULT flag for an option indicates that the option has a - default value. */ -#define GC_OPT_FLAG_DEFAULT (1UL << 4) - /* The DEF_DESC flag for an option indicates that the option has a - default, which is described by the value of the default field. */ -#define GC_OPT_FLAG_DEF_DESC (1UL << 5) - /* The NO_ARG_DESC flag for an option indicates that the argument has - a default, which is described by the value of the ARGDEF field. */ -#define GC_OPT_FLAG_NO_ARG_DESC (1UL << 6) - - if (!config_filename) - config_filename = make_filename (opt.homedir, "gpg.conf", NULL); - - printf ("gpgconf-gpg.conf:%lu:\"%s\n", - GC_OPT_FLAG_DEFAULT, config_filename); - - printf ("verbose:%lu:\n" - "quiet:%lu:\n" - "debug-level:%lu:\"none:\n" - "log-file:%lu:\n", - GC_OPT_FLAG_NONE, - GC_OPT_FLAG_NONE, - GC_OPT_FLAG_DEFAULT, - GC_OPT_FLAG_NONE ); - printf ("keyserver:%lu:\n", GC_OPT_FLAG_NONE); + break; +#endif /* ENABLE_CARD_SUPPORT*/ - } - break; + case aListConfig: + { + char *str=collapse_args(argc,argv); + list_config(str); + xfree(str); + } + break; case aListPackets: opt.list_packets=2; @@ -2979,7 +3702,14 @@ main( int argc, char **argv ) && isatty( fileno(stdout) ) && isatty( fileno(stderr) ) ) log_info(_("Go ahead and type your message ...\n")); - if( !(a = iobuf_open(fname)) ) + a = iobuf_open(fname); + if (a && is_secured_file (iobuf_get_fd (a))) + { + iobuf_close (a); + a = NULL; + errno = EPERM; + } + if( !a ) log_error(_("can't open `%s'\n"), print_fname_stdin(fname)); else { @@ -2995,11 +3725,11 @@ main( int argc, char **argv ) } rc = proc_packets(NULL, a ); if( rc ) - log_error("processing message failed: %s\n", gpg_strerror (rc) ); + log_error("processing message failed: %s\n", g10_errstr(rc) ); iobuf_close(a); } break; - } + } /* cleanup */ FREE_STRLIST(remusr); @@ -3008,6 +3738,7 @@ main( int argc, char **argv ) return 8; /*NEVER REACHED*/ } + /* Note: This function is used by signal handlers!. */ static void emergency_cleanup (void) @@ -3019,18 +3750,23 @@ emergency_cleanup (void) void g10_exit( int rc ) { +#ifdef ENABLE_CARD_SUPPORT + card_close (); +#endif + gcry_control (GCRYCTL_UPDATE_RANDOM_SEED_FILE); - if (opt.debug & DBG_MEMSTAT_VALUE) + if ( (opt.debug & DBG_MEMSTAT_VALUE) ) { - gcry_control( GCRYCTL_DUMP_MEMORY_STATS ); - gcry_control( GCRYCTL_DUMP_RANDOM_STATS ); + gcry_control (GCRYCTL_DUMP_MEMORY_STATS); + gcry_control (GCRYCTL_DUMP_RANDOM_STATS); } if (opt.debug) gcry_control (GCRYCTL_DUMP_SECMEM_STATS ); + emergency_cleanup (); - rc = rc? rc : log_get_errorcount(0)? 2 : - g10_errors_seen? 1 : 0; - exit (rc ); + + rc = rc? rc : log_get_errorcount(0)? 2 : g10_errors_seen? 1 : 0; + exit (rc); } @@ -3038,7 +3774,7 @@ g10_exit( int rc ) display, but there are a few other similar assumptions in the display code. */ static void -print_hex( MD_HANDLE md, int algo, const char *fname ) +print_hex( gcry_md_hd_t md, int algo, const char *fname ) { int i,n,count,indent=0; const byte *p; @@ -3064,7 +3800,7 @@ print_hex( MD_HANDLE md, int algo, const char *fname ) p = gcry_md_read (md, algo); n = gcry_md_get_algo_dlen (algo); - count+=printf("%02X",*p++); + count += printf ("%02X",*p++); for(i=1;i<n;i++,p++) { @@ -3118,13 +3854,13 @@ print_hex( MD_HANDLE md, int algo, const char *fname ) } static void -print_hashline( MD_HANDLE md, int algo, const char *fname ) +print_hashline( gcry_md_hd_t md, int algo, const char *fname ) { int i, n; const byte *p; if ( fname ) { - for (p = (const unsigned char *)fname; *p; p++ ) { + for (p = fname; *p; p++ ) { if ( *p <= 32 || *p > 127 || *p == ':' || *p == '%' ) printf("%%%02X", *p ); else @@ -3133,7 +3869,7 @@ print_hashline( MD_HANDLE md, int algo, const char *fname ) } putchar(':'); printf("%d:", algo ); - p = gcry_md_read (md, algo ); + p = gcry_md_read (md, algo); n = gcry_md_get_algo_dlen (algo); for(i=0; i < n ; i++, p++ ) printf("%02X", *p ); @@ -3147,7 +3883,7 @@ print_mds( const char *fname, int algo ) FILE *fp; char buf[1024]; size_t n; - MD_HANDLE md; + gcry_md_hd_t md; if( !fname ) { fp = stdin; @@ -3157,25 +3893,31 @@ print_mds( const char *fname, int algo ) } else { fp = fopen( fname, "rb" ); + if (fp && is_secured_file (fileno (fp))) + { + fclose (fp); + fp = NULL; + errno = EPERM; + } } if( !fp ) { log_error("%s: %s\n", fname?fname:"[stdin]", strerror(errno) ); return; } - gcry_md_open (&md, 0, 0 ); + gcry_md_open (&md, 0, 0); if( algo ) - gcry_md_enable ( md, algo ); + gcry_md_enable (md, algo); else { - gcry_md_enable (md, GCRY_MD_MD5 ); - gcry_md_enable (md, GCRY_MD_SHA1 ); - gcry_md_enable (md, GCRY_MD_RMD160 ); + gcry_md_enable (md, GCRY_MD_MD5); + gcry_md_enable (md, GCRY_MD_SHA1); + gcry_md_enable (md, GCRY_MD_RMD160); #ifdef USE_SHA256 - gcry_md_enable (md, GCRY_MD_SHA256 ); + gcry_md_enable (md, GCRY_MD_SHA256); #endif #ifdef USE_SHA512 - gcry_md_enable (md, GCRY_MD_SHA384 ); - gcry_md_enable (md, GCRY_MD_SHA512 ); + gcry_md_enable (md, GCRY_MD_SHA384); + gcry_md_enable (md, GCRY_MD_SHA512); #endif } @@ -3218,7 +3960,7 @@ print_mds( const char *fname, int algo ) } } } - gcry_md_close (md); + gcry_md_close(md); if( fp != stdin ) fclose(fp); @@ -3233,71 +3975,28 @@ print_mds( const char *fname, int algo ) static void add_notation_data( const char *string, int which ) { - const char *s; - STRLIST sl,*notation_data; - int critical=0; - int highbit=0; - int saw_at=0; - - if(which) - notation_data=&opt.cert_notation_data; - else - notation_data=&opt.sig_notation_data; - - if( *string == '!' ) { - critical = 1; - string++; - } - - /* If and when the IETF assigns some official name tags, we'll - have to add them here. */ - - for( s=string ; *s != '='; s++ ) - { - if( *s=='@') - saw_at=1; - - if( !*s || (*s & 0x80) || (!isgraph(*s) && !isspace(*s)) ) - { - log_error(_("a notation name must have only printable characters " - "or spaces, and end with an '='\n") ); - return; - } - } + struct notation *notation; - if(!saw_at && !opt.expert) - { - log_error( - _("a user notation name must contain the '@' character\n")); - return; - } - - /* we only support printable text - therefore we enforce the use - * of only printable characters (an empty value is valid) */ - for( s++; *s ; s++ ) { - if( *s & 0x80 ) - highbit = 1; - else if( iscntrl(*s) ) { - log_error(_("a notation value must not use " - "any control characters\n") ); - return; + notation=string_to_notation(string,utf8_strings); + if(notation) + { + if(which) + { + notation->next=opt.cert_notations; + opt.cert_notations=notation; + } + else + { + notation->next=opt.sig_notations; + opt.sig_notations=notation; } } - - if( highbit ) /* must use UTF8 encoding */ - sl = add_to_strlist2( notation_data, string, utf8_strings ); - else - sl = add_to_strlist( notation_data, string ); - - if( critical ) - sl->flags |= 1; } - static void add_policy_url( const char *string, int which ) { - int i,critical=0; + unsigned int i,critical=0; STRLIST sl; if(*string=='!') @@ -3307,7 +4006,7 @@ add_policy_url( const char *string, int which ) } for(i=0;i<strlen(string);i++) - if(string[i]&0x80 || iscntrl(string[i])) + if( !isascii (string[i]) || iscntrl(string[i])) break; if(i==0 || i<strlen(string)) @@ -3327,11 +4026,10 @@ add_policy_url( const char *string, int which ) sl->flags |= 1; } - static void add_keyserver_url( const char *string, int which ) { - int i,critical=0; + unsigned int i,critical=0; STRLIST sl; if(*string=='!') @@ -3341,7 +4039,7 @@ add_keyserver_url( const char *string, int which ) } for(i=0;i<strlen(string);i++) - if(string[i]&0x80 || iscntrl(string[i])) + if( !isascii (string[i]) || iscntrl(string[i])) break; if(i==0 || i<strlen(string)) @@ -3349,8 +4047,7 @@ add_keyserver_url( const char *string, int which ) if(which) BUG(); else - log_error(_("the given signature preferred" - " keyserver URL is invalid\n")); + log_error(_("the given preferred keyserver URL is invalid\n")); } if(which) @@ -3361,4 +4058,3 @@ add_keyserver_url( const char *string, int which ) if(critical) sl->flags |= 1; } - @@ -1,5 +1,5 @@ /* gpg.h - top level include file for gpg etc. - * Copyright (C) 2003 Free Software Foundation, Inc. + * Copyright (C) 2003, 2006 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -32,9 +32,70 @@ #define map_assuan_err(a) \ map_assuan_err_with_source (GPG_ERR_SOURCE_DEFAULT, (a)) #include <gpg-error.h> +#include <gcrypt.h> -/* FIXME: merge this with global.h */ +/* Number of bits we accept when reading or writing MPIs. */ +#define MAX_EXTERN_MPI_BITS 16384 + +/* The maximum length of a binary fingerprints. */ +#define MAX_FINGERPRINT_LEN 20 + + +/* Forward declarations. */ +typedef struct kbnode_struct *KBNODE; +typedef struct keydb_search_desc KEYDB_SEARCH_DESC; + + + +/* Simple wrappers. */ +#define g10_errstr(a) gpg_strerror ((a)) + + +/* Mapping of the old erro codes to the gpg-error ones. Fixme: This + is just a temporary solution: We need to do all these gpg_error() + calls in the code. */ +#define G10ERR_BAD_KEY GPG_ERR_BAD_KEY +#define G10ERR_BAD_PASS GPG_ERR_BAD_PASS +#define G10ERR_BAD_PUBKEY GPG_ERR_BAD_PUBKEY +#define G10ERR_BAD_SIGN GPG_ERR_BAD_SIGNATURE +#define G10ERR_BAD_URI GPG_ERR_BAD_URI +#define G10ERR_CHECKSUM GPG_ERR_CHECKSUM +#define G10ERR_CIPHER_ALGO GPG_ERR_CIPHER_ALGO +#define G10ERR_CLOSE_FILE GPG_ERR_CLOSE_FILE +#define G10ERR_COMPR_ALGO GPG_ERR_COMPR_ALGO +#define G10ERR_CREATE_FILE GPG_ERR_CREATE_FILE +#define G10ERR_DIGEST_ALGO GPG_ERR_DIGEST_ALGO +#define G10ERR_FILE_EXISTS GPG_ERR_EEXIST +#define G10ERR_GENERAL GPG_ERR_GENERAL +#define G10ERR_INV_ARG GPG_ERR_INV_ARG +#define G10ERR_INV_KEYRING GPG_ERR_INV_KEYRING +#define G10ERR_INV_USER_ID GPG_ERR_INV_USER_ID +#define G10ERR_INVALID_ARMOR GPG_ERR_INV_ARMOR +#define G10ERR_INVALID_PACKET GPG_ERR_INV_PACKET +#define G10ERR_KEYRING_OPEN GPG_ERR_KEYRING_OPEN +#define G10ERR_KEYSERVER GPG_ERR_KEYSERVER +#define G10ERR_NO_DATA GPG_ERR_NO_DATA +#define G10ERR_NO_PUBKEY GPG_ERR_NO_PUBKEY +#define G10ERR_NO_SECKEY GPG_ERR_NO_SECKEY +#define G10ERR_NO_USER_ID GPG_ERR_NO_USER_ID +#define G10ERR_NOT_PROCESSED GPG_ERR_NOT_PROCESSED +#define G10ERR_OPEN_FILE GPG_ERR_OPEN_FILE +#define G10ERR_PASSPHRASE GPG_ERR_PASSPHRASE +#define G10ERR_PUBKEY_ALGO GPG_ERR_PUBKEY_ALGO +#define G10ERR_READ_FILE GPG_ERR_READ_FILE +#define G10ERR_RENAME_FILE GPG_ERR_RENAME_FILE +#define G10ERR_RESOURCE_LIMIT GPG_ERR_RESOURCE_LIMIT +#define G10ERR_SIG_CLASS GPG_ERR_SIG_CLASS +#define G10ERR_TIME_CONFLICT GPG_ERR_TIME_CONFLICT +#define G10ERR_TRUSTDB GPG_ERR_TRUSTDB +#define G10ERR_UNEXPECTED GPG_ERR_UNEXPECTED +#define G10ERR_UNKNOWN_PACKET GPG_ERR_UNKNOWN_PACKET +#define G10ERR_UNSUPPORTED GPG_ERR_UNSUPPORTED +#define G10ERR_UNU_PUBKEY GPG_ERR_UNUSABLE_PUBKEY +#define G10ERR_UNU_SECKEY GPG_ERR_UNUSABLE_SECKEY +#define G10ERR_WRONG_SECKEY GPG_ERR_WRONG_SECKEY + #endif /*GNUPG_G10_GPG_H*/ diff --git a/g10/gpgv.c b/g10/gpgv.c index 0a97d56b9..5fc7dcd75 100644 --- a/g10/gpgv.c +++ b/g10/gpgv.c @@ -1,6 +1,6 @@ /* gpgv.c - The GnuPG signature verify utility - * Copyright (C) 1998, 1999, 2000, 2001, 2002, - * 2003 Free Software Foundation, Inc. + * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2005, + * 2006 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -16,7 +16,8 @@ * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. */ #include <config.h> @@ -29,23 +30,27 @@ #ifdef HAVE_DOSISH_SYSTEM #include <fcntl.h> /* for setmode() */ #endif +#ifdef HAVE_LIBREADLINE +#include <stdio.h> +#include <readline/readline.h> +#endif #define INCLUDED_BY_MAIN_MODULE 1 #include "gpg.h" #include "packet.h" #include "iobuf.h" -#include "memory.h" #include "util.h" #include "main.h" #include "options.h" #include "keydb.h" #include "trustdb.h" -#include "mpi.h" #include "cipher.h" #include "filter.h" #include "ttyio.h" #include "i18n.h" #include "status.h" +#include "g10defs.h" +#include "cardglue.h" enum cmd_and_opt_values { aNull = 0, @@ -79,10 +84,6 @@ static ARGPARSE_OPTS opts[] = { int g10_errors_seen = 0; -#ifdef __riscos__ -RISCOS_GLOBAL_STATICS("GnuPG (gpgv) Heap") -#endif /* __riscos__ */ - static const char * my_strusage( int level ) { @@ -110,23 +111,22 @@ my_strusage( int level ) } - - static void i18n_init(void) { #ifdef USE_SIMPLE_GETTEXT - set_gettext_file( PACKAGE_GT ); + set_gettext_file (PACKAGE_GT, "Software\\GNU\\GnuPG"); #else #ifdef ENABLE_NLS - setlocale( LC_ALL, "" ); - bindtextdomain( PACKAGE_GT, LOCALEDIR ); - textdomain( PACKAGE_GT ); + setlocale (LC_ALL, ""); + bindtextdomain (PACKAGE_GT, LOCALEDIR); + textdomain (PACKAGE_GT); #endif #endif } + int main( int argc, char **argv ) { @@ -136,17 +136,13 @@ main( int argc, char **argv ) STRLIST nrings=NULL; unsigned configlineno; -#ifdef __riscos__ - riscos_global_defaults(); -#endif /* __riscos__ */ - set_strusage (my_strusage); log_set_prefix ("gpgv", 1); - gnupg_init_signals(0, NULL); + gnupg_init_signals (0, NULL); i18n_init(); opt.command_fd = -1; /* no command fd */ opt.pgp2_workarounds = 1; - opt.keyserver_options.auto_key_retrieve = 1; + opt.keyserver_options.options|=KEYSERVER_AUTO_KEY_RETRIEVE; opt.trust_model = TM_ALWAYS; opt.batch = 1; @@ -164,8 +160,11 @@ main( int argc, char **argv ) while( optfile_parse( NULL, NULL, &configlineno, &pargs, opts) ) { switch( pargs.r_opt ) { case oQuiet: opt.quiet = 1; break; - case oVerbose: g10_opt_verbose++; - opt.verbose++; opt.list_sigs=1; break; + case oVerbose: + opt.verbose++; + opt.list_sigs=1; + gcry_control (GCRYCTL_SET_VERBOSITY, (int)opt.verbose); + break; case oKeyring: append_to_strlist( &nrings, pargs.r.ret_str); break; case oStatusFD: set_status_fd( pargs.r.ret_int ); break; case oLoggerFD: @@ -180,9 +179,7 @@ main( int argc, char **argv ) if( log_get_errorcount(0) ) g10_exit(2); - g10_opt_homedir = opt.homedir; - - if( opt.verbose > 1 ) + if( opt.verbose > 1 ) set_packet_list_mode(1); if( !nrings ) /* no keyring given: use default one */ @@ -193,7 +190,7 @@ main( int argc, char **argv ) FREE_STRLIST(nrings); if( (rc = verify_signatures( argc, argv ) )) - log_error("verify signatures failed: %s\n", gpg_strerror (rc) ); + log_error("verify signatures failed: %s\n", g10_errstr(rc) ); /* cleanup */ g10_exit(0); @@ -210,14 +207,6 @@ g10_exit( int rc ) } - -void -read_trust_options (byte *trust_model,ulong *created,ulong *nextcheck, - byte *marginals,byte *completes,byte *cert_depth) -{ -} - - /* Stub: * We have to override the trustcheck from pkclist.c becuase * this utility assumes that all keys in the keyring are trustworthy @@ -228,6 +217,9 @@ check_signatures_trust( PKT_signature *sig ) return 0; } +void +read_trust_options(byte *trust_model,ulong *created,ulong *nextcheck, + byte *marginals,byte *completes,byte *cert_depth) {} /* Stub: * We don't have the trustdb , so we have to provide some stub functions @@ -240,6 +232,9 @@ cache_disabled_value(PKT_public_key *pk) return 0; } +void +check_trustdb_stale(void) {} + int get_validity_info (PKT_public_key *pk, PKT_user_id *uid) { @@ -258,7 +253,12 @@ trust_value_to_string (unsigned int value) return "err"; } -/* Stub: */ +const char * +uid_trust_string_fixed(PKT_public_key *key,PKT_user_id *uid) +{ + return "err"; +} + int get_ownertrust_info (PKT_public_key *pk) { @@ -272,35 +272,54 @@ get_ownertrust (PKT_public_key *pk) } -/* Stub: +/* Stubs: * Because we only work with trusted keys, it does not make sense to * get them from a keyserver */ + +struct keyserver_spec * +keyserver_match(struct keyserver_spec *spec) { return NULL; } + int keyserver_import_keyid( u32 *keyid, void *dummy ) { return -1; } +int +keyserver_import_cert(const char *name) { return -1; } + +int +keyserver_import_pka(const char *name,unsigned char *fpr) { return -1; } + +int +keyserver_import_name(const char *name,struct keyserver_spec *spec) +{ + return -1; +} + +int +keyserver_import_ldap(const char *name) { return -1; } + /* Stub: * No encryption here but mainproc links to these functions. */ int get_session_key( PKT_pubkey_enc *k, DEK *dek ) { - return GPG_ERR_GENERAL; + return G10ERR_GENERAL; } /* Stub: */ int get_override_session_key( DEK *dek, const char *string ) { - return GPG_ERR_GENERAL; + return G10ERR_GENERAL; } /* Stub: */ int decrypt_data( void *procctx, PKT_encrypted *ed, DEK *dek ) { - return GPG_ERR_GENERAL; + return G10ERR_GENERAL; } @@ -318,7 +337,7 @@ display_online_help( const char *keyword ) int check_secret_key( PKT_secret_key *sk, int n ) { - return GPG_ERR_GENERAL; + return G10ERR_GENERAL; } /* Stub: @@ -334,11 +353,25 @@ passphrase_to_dek( u32 *keyid, int pubkey_algo, return NULL; } +struct keyserver_spec *parse_preferred_keyserver(PKT_signature *sig) {return NULL;} +struct keyserver_spec *parse_keyserver_uri(const char *uri,int require_scheme, + const char *configname, + unsigned int configlineno) +{ + return NULL; +} + +void free_keyserver_spec(struct keyserver_spec *keyserver) {} + /* Stubs to avoid linking to photoid.c */ void show_photos(const struct user_attribute *attrs,int count,PKT_public_key *pk) {} int parse_image_header(const struct user_attribute *attr,byte *type,u32 *len) {return 0;} char *image_type_to_string(byte type,int string) {return NULL;} +#ifdef ENABLE_CARD_SUPPORT +int agent_scd_getattr (const char *name, struct agent_card_info_s *info) {return 0;} +#endif /* ENABLE_CARD_SUPPORT */ + /* Stubs to void linking to ../cipher/cipher.c */ int string_to_cipher_algo( const char *string ) { return 0; } const char *cipher_algo_to_string( int algo ) { return "?";} @@ -356,10 +389,31 @@ void cipher_decrypt( CIPHER_HANDLE c, byte *outbuf, byte *inbuf, unsigned nbytes ) {} void cipher_sync( CIPHER_HANDLE c ) {} +/* Stubs to avoid linking to ../cipher/random.c */ +void random_dump_stats(void) {} +int quick_random_gen( int onoff ) { return -1;} +void randomize_buffer( byte *buffer, size_t length, int level ) {} +int random_is_faked() { return -1;} +byte *get_random_bits( size_t nbits, int level, int secure ) { return NULL;} +void set_random_seed_file( const char *name ) {} +void update_random_seed_file() {} +void fast_random_poll() {} + +/* Stubs to avoid linking of ../cipher/primegen.c */ +void register_primegen_progress ( void (*cb)( void *, int), void *cb_data ) {} +MPI generate_secret_prime( unsigned nbits ) { return NULL;} +MPI generate_public_prime( unsigned nbits ) { return NULL;} +MPI generate_elg_prime( int mode, unsigned pbits, unsigned qbits, + MPI g, MPI **ret_factors ) { return NULL;} + +/* Do not link to ../cipher/rndlinux.c */ +void rndlinux_constructor(void) {} + /* Stubs to avoid linking to ../util/ttyio.c */ int tty_batchmode( int onoff ) { return 0; } void tty_printf( const char *fmt, ... ) { } +void tty_fprintf (FILE *fp, const char *fmt, ... ) { } void tty_print_string( const byte *p, size_t n ) { } void tty_print_utf8_string( const byte *p, size_t n ) {} void tty_print_utf8_string2( const byte *p, size_t n, size_t max_n ) {} @@ -368,10 +422,15 @@ char *tty_get_hidden( const char *prompt ) {return NULL; } void tty_kill_prompt(void) {} int tty_get_answer_is_yes( const char *prompt ) {return 0;} int tty_no_terminal(int onoff) {return 0;} +#ifdef HAVE_LIBREADLINE +void tty_enable_completion(rl_completion_func_t *completer) {} +void tty_disable_completion(void) {} +#endif /* We do not do any locking, so use these stubs here */ void disable_dotlock(void) {} DOTLOCK create_dotlock( const char *file_to_lock ) { return NULL; } +void destroy_dotlock (DOTLOCK h) {} int make_dotlock( DOTLOCK h, long timeout ) { return 0;} int release_dotlock( DOTLOCK h ) {return 0;} -void dotlock_remove_lockfiles(void) {} +void remove_lockfiles(void) {} diff --git a/g10/helptext.c b/g10/helptext.c index 4a65314eb..c720cc7cf 100644 --- a/g10/helptext.c +++ b/g10/helptext.c @@ -1,5 +1,6 @@ /* helptext.c - English help texts - * Copyright (C) 1998, 1999, 2000, 2001, 2002 Free Software Foundation, Inc. + * Copyright (C) 1998, 1999, 2000, 2001, 2002, + * 2004 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -15,7 +16,8 @@ * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. */ #include <config.h> @@ -58,10 +60,6 @@ static struct helptexts { const char *key; const char *help; } helptexts[] = { "ultimately trusted\n" )}, -{ "revoked_key.override", N_( -"If you want to use this revoked key anyway, answer \"yes\"." -)}, - { "untrusted_key.override", N_( "If you want to use this untrusted key anyway, answer \"yes\"." )}, @@ -73,29 +71,17 @@ static struct helptexts { const char *key; const char *help; } helptexts[] = { { "keygen.algo", N_( "Select the algorithm to use.\n" "\n" -"DSA (aka DSS) is the digital signature algorithm which can only be used\n" -"for signatures. This is the suggested algorithm because verification of\n" -"DSA signatures are much faster than those of ElGamal.\n" +"DSA (aka DSS) is the Digital Signature Algorithm and can only be used\n" +"for signatures.\n" +"\n" +"Elgamal is an encrypt-only algorithm.\n" "\n" -"ElGamal is an algorithm which can be used for signatures and encryption.\n" -"OpenPGP distinguishs between two flavors of this algorithms: an encrypt only\n" -"and a sign+encrypt; actually it is the same, but some parameters must be\n" -"selected in a special way to create a safe key for signatures: this program\n" -"does this but other OpenPGP implementations are not required to understand\n" -"the signature+encryption flavor.\n" +"RSA may be used for signatures or encryption.\n" "\n" -"The first (primary) key must always be a key which is capable of signing;\n" -"this is the reason why the encryption only ElGamal key is not available in\n" -"this menu." +"The first (primary) key must always be a key which is capable of signing." )}, -{ "keygen.algo.elg_se", N_( -"Although these keys are defined in RFC2440 they are not suggested\n" -"because they are not supported by all programs and signatures created\n" -"with them are quite large and very slow to verify." -)}, - { "keygen.algo.rsa_se", N_( "In general it is not a good idea to use the same key for signing and\n" "encryption. This algorithm should only be used in certain domains.\n" @@ -199,7 +185,7 @@ static struct helptexts { const char *key; const char *help; } helptexts[] = { )}, { "keyedit.sign_all.okay", N_( - "Answer \"yes\" is you want to sign ALL the user IDs" + "Answer \"yes\" if you want to sign ALL the user IDs" )}, { "keyedit.remove.uid.okay", N_( diff --git a/g10/import.c b/g10/import.c index 9c323243a..31af7fe02 100644 --- a/g10/import.c +++ b/g10/import.c @@ -1,6 +1,6 @@ -/* import.c - Import OpenPGP key material - * Copyright (C) 1998, 1999, 2000, 2001, 2002, - * 2003 Free Software Foundation, Inc. +/* import.c - import a key into our key storage. + * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, + * 2006 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -16,7 +16,8 @@ * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. */ #include <config.h> @@ -26,11 +27,11 @@ #include <errno.h> #include <assert.h> +#include "gpg.h" #include "options.h" #include "packet.h" #include "errors.h" #include "keydb.h" -#include "memory.h" #include "util.h" #include "trustdb.h" #include "main.h" @@ -54,15 +55,18 @@ struct stats_s { ulong secret_dups; ulong skipped_new_keys; ulong not_imported; + ulong n_sigs_cleaned; + ulong n_uids_cleaned; }; -static int import( iobuf_t inp, const char* fname, - struct stats_s *stats, unsigned int options ); -static int read_block( iobuf_t a, PACKET **pending_pkt, KBNODE *ret_root ); +static int import( IOBUF inp, const char* fname,struct stats_s *stats, + unsigned char **fpr,size_t *fpr_len,unsigned int options ); +static int read_block( IOBUF a, PACKET **pending_pkt, KBNODE *ret_root ); static void revocation_present(KBNODE keyblock); -static int import_one( const char *fname, KBNODE keyblock, - struct stats_s *stats, unsigned int options); +static int import_one(const char *fname, KBNODE keyblock,struct stats_s *stats, + unsigned char **fpr,size_t *fpr_len, + unsigned int options); static int import_secret_one( const char *fname, KBNODE keyblock, struct stats_s *stats, unsigned int options); static int import_revoke_cert( const char *fname, KBNODE node, @@ -84,25 +88,41 @@ static int merge_keysigs( KBNODE dst, KBNODE src, int *n_sigs, const char *fname, u32 *keyid ); int -parse_import_options(char *str,unsigned int *options) +parse_import_options(char *str,unsigned int *options,int noisy) { struct parse_options import_opts[]= { - {"allow-local-sigs",IMPORT_ALLOW_LOCAL_SIGS}, - {"repair-hkp-subkey-bug",IMPORT_REPAIR_PKS_SUBKEY_BUG}, - {"repair-pks-subkey-bug",IMPORT_REPAIR_PKS_SUBKEY_BUG}, - {"fast-import",IMPORT_FAST_IMPORT}, - {"convert-sk-to-pk",IMPORT_SK2PK}, - {NULL,0} + {"import-local-sigs",IMPORT_LOCAL_SIGS,NULL, + N_("import signatures that are marked as local-only")}, + {"repair-pks-subkey-bug",IMPORT_REPAIR_PKS_SUBKEY_BUG,NULL, + N_("repair damage from the pks keyserver during import")}, + {"fast-import",IMPORT_FAST,NULL, + N_("do not update the trustdb after import")}, + {"convert-sk-to-pk",IMPORT_SK2PK,NULL, + N_("create a public key when importing a secret key")}, + {"merge-only",IMPORT_MERGE_ONLY,NULL, + N_("only accept updates to existing keys")}, + {"import-clean",IMPORT_CLEAN,NULL, + N_("remove unusable parts from key after import")}, + {"import-minimal",IMPORT_MINIMAL|IMPORT_CLEAN,NULL, + N_("remove as much as possible from key after import")}, + /* Aliases for backward compatibility */ + {"allow-local-sigs",IMPORT_LOCAL_SIGS,NULL,NULL}, + {"repair-hkp-subkey-bug",IMPORT_REPAIR_PKS_SUBKEY_BUG,NULL,NULL}, + /* dummy */ + {"import-unusable-sigs",0,NULL,NULL}, + {"import-clean-sigs",0,NULL,NULL}, + {"import-clean-uids",0,NULL,NULL}, + {NULL,0,NULL,NULL} }; - return parse_options(str,options,import_opts); + return parse_options(str,options,import_opts,noisy); } void * import_new_stats_handle (void) { - return xcalloc (1, sizeof (struct stats_s) ); + return xmalloc_clear ( sizeof (struct stats_s) ); } void @@ -143,8 +163,9 @@ import_release_stats_handle (void *p) * */ static int -import_keys_internal( iobuf_t inp, char **fnames, int nnames, - void *stats_handle, unsigned int options ) +import_keys_internal( IOBUF inp, char **fnames, int nnames, + void *stats_handle, unsigned char **fpr, size_t *fpr_len, + unsigned int options ) { int i, rc = 0; struct stats_s *stats = stats_handle; @@ -153,7 +174,7 @@ import_keys_internal( iobuf_t inp, char **fnames, int nnames, stats = import_new_stats_handle (); if (inp) { - rc = import( inp, "[stream]", stats, options); + rc = import( inp, "[stream]", stats, fpr, fpr_len, options); } else { if( !fnames && !nnames ) @@ -161,20 +182,27 @@ import_keys_internal( iobuf_t inp, char **fnames, int nnames, for(i=0; i < nnames; i++ ) { const char *fname = fnames? fnames[i] : NULL; - iobuf_t inp2 = iobuf_open(fname); + IOBUF inp2 = iobuf_open(fname); if( !fname ) fname = "[stdin]"; + if (inp2 && is_secured_file (iobuf_get_fd (inp2))) + { + iobuf_close (inp2); + inp2 = NULL; + errno = EPERM; + } if( !inp2 ) log_error(_("can't open `%s': %s\n"), fname, strerror(errno) ); - else { - rc = import( inp2, fname, stats, options ); + else + { + rc = import( inp2, fname, stats, fpr, fpr_len, options ); iobuf_close(inp2); /* Must invalidate that ugly cache to actually close it. */ iobuf_ioctl (NULL, 2, 0, (char*)fname); if( rc ) - log_error("import from `%s' failed: %s\n", fname, - gpg_strerror (rc) ); - } + log_error("import from `%s' failed: %s\n", fname, + g10_errstr(rc) ); + } if( !fname ) break; } @@ -183,18 +211,15 @@ import_keys_internal( iobuf_t inp, char **fnames, int nnames, import_print_stats (stats); import_release_stats_handle (stats); } + /* If no fast import and the trustdb is dirty (i.e. we added a key or userID that had something other than a selfsig, a signature that was other than a selfsig, or any revocation), then update/check the trustdb if the user specified by setting interactive or by not setting no-auto-check-trustdb */ - if (!(options&IMPORT_FAST_IMPORT) && trustdb_pending_check()) - { - if (opt.interactive) - update_trustdb(); - else if (!opt.no_auto_check_trustdb) - check_trustdb(); - } + + if(!(options&IMPORT_FAST)) + trustdb_check_or_update(); return rc; } @@ -203,18 +228,19 @@ void import_keys( char **fnames, int nnames, void *stats_handle, unsigned int options ) { - import_keys_internal( NULL, fnames, nnames, stats_handle, options); + import_keys_internal(NULL,fnames,nnames,stats_handle,NULL,NULL,options); } int -import_keys_stream( iobuf_t inp, void *stats_handle, unsigned int options ) +import_keys_stream( IOBUF inp, void *stats_handle, + unsigned char **fpr, size_t *fpr_len,unsigned int options ) { - return import_keys_internal( inp, NULL, 0, stats_handle, options); + return import_keys_internal(inp,NULL,0,stats_handle,fpr,fpr_len,options); } static int -import( iobuf_t inp, const char* fname, - struct stats_s *stats, unsigned int options ) +import( IOBUF inp, const char* fname,struct stats_s *stats, + unsigned char **fpr,size_t *fpr_len,unsigned int options ) { PACKET *pending_pkt = NULL; KBNODE keyblock; @@ -223,14 +249,14 @@ import( iobuf_t inp, const char* fname, getkey_disable_caches(); if( !opt.no_armor ) { /* armored reading is not disabled */ - armor_filter_context_t *afx = xcalloc (1, sizeof *afx ); + armor_filter_context_t *afx = xmalloc_clear( sizeof *afx ); afx->only_keyblocks = 1; iobuf_push_filter2( inp, armor_filter, afx, 1 ); } while( !(rc = read_block( inp, &pending_pkt, &keyblock) )) { if( keyblock->pkt->pkttype == PKT_PUBLIC_KEY ) - rc = import_one( fname, keyblock, stats, options ); + rc = import_one( fname, keyblock, stats, fpr, fpr_len, options ); else if( keyblock->pkt->pkttype == PKT_SECRET_KEY ) rc = import_secret_one( fname, keyblock, stats, options ); else if( keyblock->pkt->pkttype == PKT_SIGNATURE @@ -250,8 +276,8 @@ import( iobuf_t inp, const char* fname, } if( rc == -1 ) rc = 0; - else if( rc && rc != GPG_ERR_INV_KEYRING ) - log_error( _("error reading `%s': %s\n"), fname, gpg_strerror (rc)); + else if( rc && rc != G10ERR_INV_KEYRING ) + log_error( _("error reading `%s': %s\n"), fname, g10_errstr(rc)); return rc; } @@ -293,6 +319,10 @@ import_print_stats (void *hd) log_info(_(" secret keys unchanged: %lu\n"), stats->secret_dups ); if( stats->not_imported ) log_info(_(" not imported: %lu\n"), stats->not_imported ); + if( stats->n_sigs_cleaned) + log_info(_(" signatures cleaned: %lu\n"),stats->n_sigs_cleaned); + if( stats->n_uids_cleaned) + log_info(_(" user IDs cleaned: %lu\n"),stats->n_uids_cleaned); } if( is_status_enabled() ) { @@ -324,7 +354,7 @@ import_print_stats (void *hd) * Retunr: 0 = okay, -1 no more blocks or another errorcode. */ static int -read_block( iobuf_t a, PACKET **pending_pkt, KBNODE *ret_root ) +read_block( IOBUF a, PACKET **pending_pkt, KBNODE *ret_root ) { int rc; PACKET *pkt; @@ -338,13 +368,13 @@ read_block( iobuf_t a, PACKET **pending_pkt, KBNODE *ret_root ) } else in_cert = 0; - pkt = xmalloc ( sizeof *pkt ); + pkt = xmalloc( sizeof *pkt ); init_packet(pkt); while( (rc=parse_packet(a, pkt)) != -1 ) { if( rc ) { /* ignore errors */ - if( rc != GPG_ERR_UNKNOWN_PACKET ) { - log_error("read_block: read error: %s\n", gpg_strerror (rc) ); - rc = GPG_ERR_INV_KEYRING; + if( rc != G10ERR_UNKNOWN_PACKET ) { + log_error("read_block: read error: %s\n", g10_errstr(rc) ); + rc = G10ERR_INV_KEYRING; goto ready; } free_packet( pkt ); @@ -364,17 +394,17 @@ read_block( iobuf_t a, PACKET **pending_pkt, KBNODE *ret_root ) /* make a linked list of all packets */ switch( pkt->pkttype ) { case PKT_COMPRESSED: - if( pkt->pkt.compressed->algorithm < 1 - || pkt->pkt.compressed->algorithm > 2 ) { - rc = GPG_ERR_COMPR_ALGO; + if(check_compress_algo(pkt->pkt.compressed->algorithm)) + { + rc = G10ERR_COMPR_ALGO; goto ready; - } - { - compress_filter_context_t *cfx = xcalloc (1, sizeof *cfx ); - cfx->algo = pkt->pkt.compressed->algorithm; + } + else + { + compress_filter_context_t *cfx = xmalloc_clear( sizeof *cfx ); pkt->pkt.compressed->buf = NULL; - iobuf_push_filter2( a, compress_filter, cfx, 1 ); - } + push_compress_filter2(a,cfx,pkt->pkt.compressed->algorithm,1); + } free_packet( pkt ); init_packet(pkt); break; @@ -399,7 +429,7 @@ read_block( iobuf_t a, PACKET **pending_pkt, KBNODE *ret_root ) root = new_kbnode( pkt ); else add_kbnode( root, new_kbnode( pkt ) ); - pkt = xmalloc ( sizeof *pkt ); + pkt = xmalloc( sizeof *pkt ); } init_packet(pkt); break; @@ -414,7 +444,7 @@ read_block( iobuf_t a, PACKET **pending_pkt, KBNODE *ret_root ) else *ret_root = root; free_packet( pkt ); - xfree ( pkt ); + xfree( pkt ); return rc; } @@ -508,7 +538,7 @@ print_import_ok (PKT_public_key *pk, PKT_secret_key *sk, unsigned int reason) write_status_text (STATUS_IMPORT_OK, buf); } -void +static void print_import_check (PKT_public_key * pk, PKT_user_id * id) { char * buf; @@ -530,6 +560,115 @@ print_import_check (PKT_public_key * pk, PKT_user_id * id) xfree (buf); } +static void +check_prefs_warning(PKT_public_key *pk) +{ + log_info(_("WARNING: key %s contains preferences for unavailable\n" + "algorithms on these user IDs:\n"), keystr_from_pk(pk)); +} + +static void +check_prefs(KBNODE keyblock) +{ + KBNODE node; + PKT_public_key *pk; + int problem=0; + + merge_keys_and_selfsig(keyblock); + pk=keyblock->pkt->pkt.public_key; + + for(node=keyblock;node;node=node->next) + { + if(node->pkt->pkttype==PKT_USER_ID + && node->pkt->pkt.user_id->created + && node->pkt->pkt.user_id->prefs) + { + PKT_user_id *uid=node->pkt->pkt.user_id; + prefitem_t *prefs=uid->prefs; + char *user=utf8_to_native(uid->name,strlen(uid->name),0); + + for(;prefs->type;prefs++) + { + char num[10]; /* prefs->value is a byte, so we're over + safe here */ + + sprintf(num,"%u",prefs->value); + + if(prefs->type==PREFTYPE_SYM) + { + if(check_cipher_algo(prefs->value)) + { + const char *algo=cipher_algo_to_string(prefs->value); + if(!problem) + check_prefs_warning(pk); + log_info(_(" \"%s\": preference for cipher" + " algorithm %s\n"),user,algo?algo:num); + problem=1; + } + } + else if(prefs->type==PREFTYPE_HASH) + { + if(check_digest_algo(prefs->value)) + { + const char *algo=digest_algo_to_string(prefs->value); + if(!problem) + check_prefs_warning(pk); + log_info(_(" \"%s\": preference for digest" + " algorithm %s\n"),user,algo?algo:num); + problem=1; + } + } + else if(prefs->type==PREFTYPE_ZIP) + { + if(check_compress_algo(prefs->value)) + { + const char *algo=compress_algo_to_string(prefs->value); + if(!problem) + check_prefs_warning(pk); + log_info(_(" \"%s\": preference for compression" + " algorithm %s\n"),user,algo?algo:num); + problem=1; + } + } + } + + xfree(user); + } + } + + if(problem) + { + log_info(_("it is strongly suggested that you update" + " your preferences and\n")); + log_info(_("re-distribute this key to avoid potential algorithm" + " mismatch problems\n")); + + if(!opt.batch) + { + STRLIST sl=NULL,locusr=NULL; + size_t fprlen=0; + byte fpr[MAX_FINGERPRINT_LEN],*p; + char username[(MAX_FINGERPRINT_LEN*2)+1]; + unsigned int i; + + p=fingerprint_from_pk(pk,fpr,&fprlen); + for(i=0;i<fprlen;i++,p++) + sprintf(username+2*i,"%02X",*p); + add_to_strlist(&locusr,username); + + append_to_strlist(&sl,"updpref"); + append_to_strlist(&sl,"save"); + + keyedit_menu( username, locusr, sl, 1, 1 ); + free_strlist(sl); + free_strlist(locusr); + } + else if(!opt.quiet) + log_info(_("you can update your preferences with:" + " gpg --edit-key %s updpref save\n"),keystr_from_pk(pk)); + } +} + /**************** * Try to import one keyblock. Return an error only in serious cases, but * never for an invalid keyblock. It uses log_error to increase the @@ -537,8 +676,8 @@ print_import_check (PKT_public_key * pk, PKT_user_id * id) * which called g10. */ static int -import_one( const char *fname, KBNODE keyblock, - struct stats_s *stats, unsigned int options ) +import_one( const char *fname, KBNODE keyblock, struct stats_s *stats, + unsigned char **fpr,size_t *fpr_len,unsigned int options ) { PKT_public_key *pk; PKT_public_key *pk_orig; @@ -556,27 +695,30 @@ import_one( const char *fname, KBNODE keyblock, BUG(); pk = node->pkt->pkt.public_key; + + if(fpr) + *fpr=fingerprint_from_pk(pk,NULL,fpr_len); + keyid_from_pk( pk, keyid ); uidnode = find_next_kbnode( keyblock, PKT_USER_ID ); - if(pk->pubkey_algo==PUBKEY_ALGO_ELGAMAL) - log_info(_("NOTE: Elgamal primary key detected - " - "this may take some time to import\n")); - - if( opt.verbose && !opt.interactive ) { - log_info( "pub %4u%c/%08lX %s ", + if( opt.verbose && !opt.interactive ) + { + log_info( "pub %4u%c/%s %s ", nbits_from_pk( pk ), pubkey_letter( pk->pubkey_algo ), - (ulong)keyid[1], datestr_from_pk(pk) ); + keystr_from_pk(pk), datestr_from_pk(pk) ); if( uidnode ) - print_utf8_string( stderr, uidnode->pkt->pkt.user_id->name, - uidnode->pkt->pkt.user_id->len ); + print_utf8_string( stderr, uidnode->pkt->pkt.user_id->name, + uidnode->pkt->pkt.user_id->len ); putc('\n', stderr); - } - if( !uidnode ) { - log_error( _("key %08lX: no user ID\n"), (ulong)keyid[1]); + } + + if( !uidnode ) + { + log_error( _("key %s: no user ID\n"), keystr_from_pk(pk)); return 0; - } + } if (opt.interactive) { if(is_status_enabled()) @@ -590,12 +732,21 @@ import_one( const char *fname, KBNODE keyblock, return 0; } + collapse_uids(&keyblock); + + /* Clean the key that we're about to import, to cut down on things + that we have to clean later. This has no practical impact on + the end result, but does result in less logging which might + confuse the user. */ + if(options&IMPORT_CLEAN) + clean_key(keyblock,opt.verbose,options&IMPORT_MINIMAL,NULL,NULL); + clear_kbnode_flags( keyblock ); if((options&IMPORT_REPAIR_PKS_SUBKEY_BUG) && fix_pks_corruption(keyblock) && opt.verbose) - log_info(_("key %08lX: PKS subkey corruption repaired\n"), - (ulong)keyid[1]); + log_info(_("key %s: PKS subkey corruption repaired\n"), + keystr_from_pk(pk)); rc = chk_self_sigs( fname, keyblock , pk, keyid, &non_self ); if( rc ) @@ -609,48 +760,50 @@ import_one( const char *fname, KBNODE keyblock, char *user=utf8_to_native(node->pkt->pkt.user_id->name, node->pkt->pkt.user_id->len,0); node->flag |= 1; - log_info( _("key %08lX: accepted non self-signed user ID '%s'\n"), - (ulong)keyid[1],user); - xfree (user); + log_info( _("key %s: accepted non self-signed user ID \"%s\"\n"), + keystr_from_pk(pk),user); + xfree(user); } if( !delete_inv_parts( fname, keyblock, keyid, options ) ) { - log_error ( _("key %08lX: no valid user IDs\n"), (ulong)keyid[1]); + log_error( _("key %s: no valid user IDs\n"), keystr_from_pk(pk)); if( !opt.quiet ) - log_info(_("this may be caused by a missing self-signature\n")); + log_info(_("this may be caused by a missing self-signature\n")); stats->no_user_id++; return 0; } /* do we have this key already in one of our pubrings ? */ - pk_orig = xcalloc (1, sizeof *pk_orig ); + pk_orig = xmalloc_clear( sizeof *pk_orig ); rc = get_pubkey_fast ( pk_orig, keyid ); - if( rc && gpg_err_code (rc) != GPG_ERR_NO_PUBKEY - && gpg_err_code (rc) != GPG_ERR_UNUSABLE_PUBKEY ) { - log_error( _("key %08lX: public key not found: %s\n"), - (ulong)keyid[1], gpg_strerror (rc)); - } - else if ( rc && opt.merge_only ) { + if( rc && rc != G10ERR_NO_PUBKEY && rc != G10ERR_UNU_PUBKEY ) + { + log_error( _("key %s: public key not found: %s\n"), + keystr(keyid), g10_errstr(rc)); + } + else if ( rc && (opt.import_options&IMPORT_MERGE_ONLY) ) + { if( opt.verbose ) - log_info( _("key %08lX: new key - skipped\n"), (ulong)keyid[1] ); + log_info( _("key %s: new key - skipped\n"), keystr(keyid)); rc = 0; stats->skipped_new_keys++; - } + } else if( rc ) { /* insert this key */ KEYDB_HANDLE hd = keydb_new (0); rc = keydb_locate_writable (hd, NULL); if (rc) { - log_error (_("no writable keyring found: %s\n"), gpg_strerror (rc)); + log_error (_("no writable keyring found: %s\n"), g10_errstr (rc)); keydb_release (hd); - return GPG_ERR_GENERAL; + return G10ERR_GENERAL; } if( opt.verbose > 1 ) log_info (_("writing to `%s'\n"), keydb_get_resource_name (hd) ); + rc = keydb_insert_keyblock (hd, keyblock ); if (rc) log_error (_("error writing keyring `%s': %s\n"), - keydb_get_resource_name (hd), gpg_strerror (rc)); + keydb_get_resource_name (hd), g10_errstr(rc)); else { /* This should not be possible since we delete the @@ -665,18 +818,20 @@ import_one( const char *fname, KBNODE keyblock, keydb_release (hd); /* we are ready */ - if( !opt.quiet ) { - char *p=get_user_id_printable (keyid); - log_info( _("key %08lX: public key \"%s\" imported\n"), - (ulong)keyid[1],p); - xfree (p); - } - if( is_status_enabled() ) { + if( !opt.quiet ) + { + char *p=get_user_id_native (keyid); + log_info( _("key %s: public key \"%s\" imported\n"), + keystr(keyid),p); + xfree(p); + } + if( is_status_enabled() ) + { char *us = get_long_user_id_string( keyid ); write_status_text( STATUS_IMPORTED, us ); - xfree (us); + xfree(us); print_import_ok (pk,NULL, 1); - } + } stats->imported++; if( is_RSA( pk->pubkey_algo ) ) stats->imported_rsa++; @@ -684,15 +839,15 @@ import_one( const char *fname, KBNODE keyblock, } else { /* merge */ KEYDB_HANDLE hd; - int n_uids, n_sigs, n_subk; + int n_uids, n_sigs, n_subk, n_sigs_cleaned, n_uids_cleaned; /* Compare the original against the new key; just to be sure nothing * weird is going on */ - if( cmp_public_keys( pk_orig, pk ) ) { - log_error( _("key %08lX: doesn't match our copy\n"), - (ulong)keyid[1]); + if( cmp_public_keys( pk_orig, pk ) ) + { + log_error( _("key %s: doesn't match our copy\n"),keystr(keyid)); goto leave; - } + } /* now read the original keyblock */ hd = keydb_new (0); @@ -705,94 +860,135 @@ import_one( const char *fname, KBNODE keyblock, afp[an++] = 0; rc = keydb_search_fpr (hd, afp); } - if( rc ) { - log_error (_("key %08lX: can't locate original keyblock: %s\n"), - (ulong)keyid[1], gpg_strerror (rc)); + if( rc ) + { + log_error (_("key %s: can't locate original keyblock: %s\n"), + keystr(keyid), g10_errstr(rc)); keydb_release (hd); goto leave; - } + } rc = keydb_get_keyblock (hd, &keyblock_orig ); - if (rc) { - log_error (_("key %08lX: can't read original keyblock: %s\n"), - (ulong)keyid[1], gpg_strerror (rc)); + if (rc) + { + log_error (_("key %s: can't read original keyblock: %s\n"), + keystr(keyid), g10_errstr(rc)); keydb_release (hd); goto leave; - } + } - collapse_uids( &keyblock ); /* and try to merge the block */ clear_kbnode_flags( keyblock_orig ); clear_kbnode_flags( keyblock ); - n_uids = n_sigs = n_subk = 0; + n_uids = n_sigs = n_subk = n_sigs_cleaned = n_uids_cleaned = 0; rc = merge_blocks( fname, keyblock_orig, keyblock, - keyid, &n_uids, &n_sigs, &n_subk ); - if( rc ) { + keyid, &n_uids, &n_sigs, &n_subk ); + if( rc ) + { keydb_release (hd); goto leave; - } - if( n_uids || n_sigs || n_subk ) { + } + + if(options&IMPORT_CLEAN) + clean_key(keyblock_orig,opt.verbose,options&IMPORT_MINIMAL, + &n_uids_cleaned,&n_sigs_cleaned); + + if( n_uids || n_sigs || n_subk || n_sigs_cleaned || n_uids_cleaned) { mod_key = 1; /* keyblock_orig has been updated; write */ rc = keydb_update_keyblock (hd, keyblock_orig); if (rc) log_error (_("error writing keyring `%s': %s\n"), - keydb_get_resource_name (hd), gpg_strerror (rc) ); + keydb_get_resource_name (hd), g10_errstr(rc) ); else if(non_self) revalidation_mark (); /* we are ready */ - if( !opt.quiet ) { - char *p=get_user_id_printable(keyid); + if( !opt.quiet ) + { + char *p=get_user_id_native(keyid); if( n_uids == 1 ) - log_info( _("key %08lX: \"%s\" 1 new user ID\n"), - (ulong)keyid[1], p); + log_info( _("key %s: \"%s\" 1 new user ID\n"), + keystr(keyid),p); else if( n_uids ) - log_info( _("key %08lX: \"%s\" %d new user IDs\n"), - (ulong)keyid[1], p, n_uids ); + log_info( _("key %s: \"%s\" %d new user IDs\n"), + keystr(keyid),p,n_uids); if( n_sigs == 1 ) - log_info( _("key %08lX: \"%s\" 1 new signature\n"), - (ulong)keyid[1], p); + log_info( _("key %s: \"%s\" 1 new signature\n"), + keystr(keyid), p); else if( n_sigs ) - log_info( _("key %08lX: \"%s\" %d new signatures\n"), - (ulong)keyid[1], p, n_sigs ); + log_info( _("key %s: \"%s\" %d new signatures\n"), + keystr(keyid), p, n_sigs ); if( n_subk == 1 ) - log_info( _("key %08lX: \"%s\" 1 new subkey\n"), - (ulong)keyid[1], p); + log_info( _("key %s: \"%s\" 1 new subkey\n"), + keystr(keyid), p); else if( n_subk ) - log_info( _("key %08lX: \"%s\" %d new subkeys\n"), - (ulong)keyid[1], p, n_subk ); - xfree (p); - } + log_info( _("key %s: \"%s\" %d new subkeys\n"), + keystr(keyid), p, n_subk ); + if(n_sigs_cleaned==1) + log_info(_("key %s: \"%s\" %d signature cleaned\n"), + keystr(keyid),p,n_sigs_cleaned); + else if(n_sigs_cleaned) + log_info(_("key %s: \"%s\" %d signatures cleaned\n"), + keystr(keyid),p,n_sigs_cleaned); + if(n_uids_cleaned==1) + log_info(_("key %s: \"%s\" %d user ID cleaned\n"), + keystr(keyid),p,n_uids_cleaned); + else if(n_uids_cleaned) + log_info(_("key %s: \"%s\" %d user IDs cleaned\n"), + keystr(keyid),p,n_uids_cleaned); + xfree(p); + } stats->n_uids +=n_uids; stats->n_sigs +=n_sigs; stats->n_subk +=n_subk; + stats->n_sigs_cleaned +=n_sigs_cleaned; + stats->n_uids_cleaned +=n_uids_cleaned; if (is_status_enabled ()) print_import_ok (pk, NULL, ((n_uids?2:0)|(n_sigs?4:0)|(n_subk?8:0))); } - else { - if (is_status_enabled ()) - print_import_ok (pk, NULL, 0); - - if( !opt.quiet ) { - char *p=get_user_id_printable(keyid); - log_info( _("key %08lX: \"%s\" not changed\n"), - (ulong)keyid[1],p); - xfree (p); - } + else + { + if (is_status_enabled ()) + print_import_ok (pk, NULL, 0); + + if( !opt.quiet ) + { + char *p=get_user_id_native(keyid); + log_info( _("key %s: \"%s\" not changed\n"),keystr(keyid),p); + xfree(p); + } + stats->unchanged++; - } + } + keydb_release (hd); hd = NULL; } leave: + + /* Now that the key is definitely incorporated into the keydb, we + need to check if a designated revocation is present or if the + prefs are not rational so we can warn the user. */ + + if(mod_key) + { + revocation_present(keyblock_orig); + if(seckey_available(keyid)==0) + check_prefs(keyblock_orig); + } + else if(new_key) + { + revocation_present(keyblock); + if(seckey_available(keyid)==0) + check_prefs(keyblock); + } + release_kbnode( keyblock_orig ); free_public_key( pk_orig ); - revocation_present(keyblock); - return rc; } @@ -813,8 +1009,8 @@ sec_to_pub_keyblock(KBNODE sec_keyblock) write the keyblock out. */ PKT_secret_key *sk=secnode->pkt->pkt.secret_key; - PACKET *pkt=xcalloc (1,sizeof(PACKET)); - PKT_public_key *pk=xcalloc (1,sizeof(PKT_public_key)); + PACKET *pkt=xmalloc_clear(sizeof(PACKET)); + PKT_public_key *pk=xmalloc_clear(sizeof(PKT_public_key)); int n; if(secnode->pkt->pkttype==PKT_SECRET_KEY) @@ -831,7 +1027,12 @@ sec_to_pub_keyblock(KBNODE sec_keyblock) n=pubkey_get_npkey(pk->pubkey_algo); if(n==0) - pk->pkey[0]=mpi_copy(sk->skey[0]); + { + /* we can't properly extract the pubkey without knowing + the number of MPIs */ + release_kbnode(pub_keyblock); + return NULL; + } else { int i; @@ -880,80 +1081,108 @@ import_secret_one( const char *fname, KBNODE keyblock, keyid_from_sk( sk, keyid ); uidnode = find_next_kbnode( keyblock, PKT_USER_ID ); - if( opt.verbose ) { - log_info( "sec %4u%c/%08lX %s ", + if( opt.verbose ) + { + log_info( "sec %4u%c/%s %s ", nbits_from_sk( sk ), pubkey_letter( sk->pubkey_algo ), - (ulong)keyid[1], datestr_from_sk(sk) ); + keystr_from_sk(sk), datestr_from_sk(sk) ); if( uidnode ) - print_utf8_string( stderr, uidnode->pkt->pkt.user_id->name, - uidnode->pkt->pkt.user_id->len ); + print_utf8_string( stderr, uidnode->pkt->pkt.user_id->name, + uidnode->pkt->pkt.user_id->len ); putc('\n', stderr); - } + } stats->secret_read++; - if( !uidnode ) { - log_error( _("key %08lX: no user ID\n"), (ulong)keyid[1]); + if( !uidnode ) + { + log_error( _("key %s: no user ID\n"), keystr_from_sk(sk)); return 0; - } + } if(sk->protect.algo>110) { - log_error(_("key %08lX: secret key with invalid cipher %d " - "- skipped\n"),(ulong)keyid[1],sk->protect.algo); + log_error(_("key %s: secret key with invalid cipher %d" + " - skipped\n"),keystr_from_sk(sk),sk->protect.algo); return 0; } +#ifdef ENABLE_SELINUX_HACKS + if (1) + { + /* We don't allow to import secret keys because that may be used + to put a secret key into the keyring and the user might later + be tricked into signing stuff with that key. */ + log_error (_("importing secret keys not allowed\n")); + return 0; + } +#endif + clear_kbnode_flags( keyblock ); /* do we have this key already in one of our secrings ? */ rc = seckey_available( keyid ); - if( gpg_err_code (rc) == GPG_ERR_NO_SECKEY && !opt.merge_only ) { - /* simply insert this key */ + if( rc == G10ERR_NO_SECKEY && !(opt.import_options&IMPORT_MERGE_ONLY) ) + { + /* simply insert this key */ KEYDB_HANDLE hd = keydb_new (1); /* get default resource */ rc = keydb_locate_writable (hd, NULL); if (rc) { - log_error (_("no default secret keyring: %s\n"), gpg_strerror (rc)); - keydb_release (hd); - return GPG_ERR_GENERAL; + log_error (_("no default secret keyring: %s\n"), g10_errstr (rc)); + keydb_release (hd); + return G10ERR_GENERAL; } rc = keydb_insert_keyblock (hd, keyblock ); if (rc) - log_error (_("error writing keyring `%s': %s\n"), - keydb_get_resource_name (hd), gpg_strerror (rc) ); + log_error (_("error writing keyring `%s': %s\n"), + keydb_get_resource_name (hd), g10_errstr(rc) ); keydb_release (hd); /* we are ready */ if( !opt.quiet ) - log_info( _("key %08lX: secret key imported\n"), (ulong)keyid[1]); + log_info( _("key %s: secret key imported\n"), keystr_from_sk(sk)); stats->secret_imported++; if (is_status_enabled ()) - print_import_ok (NULL, sk, 1|16); + print_import_ok (NULL, sk, 1|16); if(options&IMPORT_SK2PK) { /* Try and make a public key out of this. */ KBNODE pub_keyblock=sec_to_pub_keyblock(keyblock); - import_one(fname,pub_keyblock,stats,opt.import_options); - release_kbnode(pub_keyblock); + if(pub_keyblock) + { + import_one(fname,pub_keyblock,stats, + NULL,NULL,opt.import_options); + release_kbnode(pub_keyblock); + } } - } - else if( !rc ) { /* we can't merge secret keys */ - log_error( _("key %08lX: already in secret keyring\n"), - (ulong)keyid[1]); + /* Now that the key is definitely incorporated into the keydb, + if we have the public part of this key, we need to check if + the prefs are rational. */ + node=get_pubkeyblock(keyid); + if(node) + { + check_prefs(node); + release_kbnode(node); + } + } + else if( !rc ) + { /* we can't merge secret keys */ + log_error( _("key %s: already in secret keyring\n"), + keystr_from_sk(sk)); stats->secret_dups++; if (is_status_enabled ()) - print_import_ok (NULL, sk, 16); + print_import_ok (NULL, sk, 16); /* TODO: if we ever do merge secret keys, make sure to handle the sec_to_pub_keyblock feature as well. */ - } + } else - log_error( _("key %08lX: secret key not found: %s\n"), - (ulong)keyid[1], gpg_strerror (rc)); + log_error( _("key %s: secret key not found: %s\n"), + keystr_from_sk(sk), g10_errstr(rc)); return rc; } @@ -978,19 +1207,21 @@ import_revoke_cert( const char *fname, KBNODE node, struct stats_s *stats ) keyid[0] = node->pkt->pkt.signature->keyid[0]; keyid[1] = node->pkt->pkt.signature->keyid[1]; - pk = xcalloc (1, sizeof *pk ); + pk = xmalloc_clear( sizeof *pk ); rc = get_pubkey( pk, keyid ); - if( gpg_err_code (rc) == GPG_ERR_NO_PUBKEY ) { - log_error ( _("key %08lX: no public key - " - "can't apply revocation certificate\n"), (ulong)keyid[1]); + if( rc == G10ERR_NO_PUBKEY ) + { + log_error(_("key %s: no public key -" + " can't apply revocation certificate\n"), keystr(keyid)); rc = 0; goto leave; - } - else if( rc ) { - log_error( _("key %08lX: public key not found: %s\n"), - (ulong)keyid[1], gpg_strerror (rc)); + } + else if( rc ) + { + log_error(_("key %s: public key not found: %s\n"), + keystr(keyid), g10_errstr(rc)); goto leave; - } + } /* read the original keyblock */ hd = keydb_new (0); @@ -1003,41 +1234,42 @@ import_revoke_cert( const char *fname, KBNODE node, struct stats_s *stats ) afp[an++] = 0; rc = keydb_search_fpr (hd, afp); } - if (rc) { - log_error (_("key %08lX: can't locate original keyblock: %s\n"), - (ulong)keyid[1], gpg_strerror (rc)); + if (rc) + { + log_error (_("key %s: can't locate original keyblock: %s\n"), + keystr(keyid), g10_errstr(rc)); goto leave; - } + } rc = keydb_get_keyblock (hd, &keyblock ); - if (rc) { - log_error (_("key %08lX: can't read original keyblock: %s\n"), - (ulong)keyid[1], gpg_strerror (rc)); + if (rc) + { + log_error (_("key %s: can't read original keyblock: %s\n"), + keystr(keyid), g10_errstr(rc)); goto leave; - } - + } /* it is okay, that node is not in keyblock because * check_key_signature works fine for sig_class 0x20 in this * special case. */ rc = check_key_signature( keyblock, node, NULL); - if( rc ) { - log_error( _("key %08lX: invalid revocation certificate" - ": %s - rejected\n"), (ulong)keyid[1], gpg_strerror (rc)); + if( rc ) + { + log_error( _("key %s: invalid revocation certificate" + ": %s - rejected\n"), keystr(keyid), g10_errstr(rc)); goto leave; - } - + } /* check whether we already have this */ for(onode=keyblock->next; onode; onode=onode->next ) { if( onode->pkt->pkttype == PKT_USER_ID ) break; else if( onode->pkt->pkttype == PKT_SIGNATURE - && !cmp_signatures(node->pkt->pkt.signature, - onode->pkt->pkt.signature)) - { - rc = 0; - goto leave; /* yes, we already know about it */ - } + && !cmp_signatures(node->pkt->pkt.signature, + onode->pkt->pkt.signature)) + { + rc = 0; + goto leave; /* yes, we already know about it */ + } } @@ -1048,15 +1280,16 @@ import_revoke_cert( const char *fname, KBNODE node, struct stats_s *stats ) rc = keydb_update_keyblock (hd, keyblock ); if (rc) log_error (_("error writing keyring `%s': %s\n"), - keydb_get_resource_name (hd), gpg_strerror (rc) ); + keydb_get_resource_name (hd), g10_errstr(rc) ); keydb_release (hd); hd = NULL; /* we are ready */ - if( !opt.quiet ) { - char *p=get_user_id_printable (keyid); - log_info( _("key %08lX: \"%s\" revocation certificate imported\n"), - (ulong)keyid[1],p); - xfree (p); - } + if( !opt.quiet ) + { + char *p=get_user_id_native (keyid); + log_info( _("key %s: \"%s\" revocation certificate imported\n"), + keystr(keyid),p); + xfree(p); + } stats->n_revoc++; /* If the key we just revoked was ultimately trusted, remove its @@ -1109,89 +1342,90 @@ chk_self_sigs( const char *fname, KBNODE keyblock, sig = n->pkt->pkt.signature; if( keyid[0] == sig->keyid[0] && keyid[1] == sig->keyid[1] ) { - /* This just caches the sigs for later use. That way we - import a fully-cached key which speeds things up. */ - if(!opt.no_sig_cache) - check_key_signature(keyblock,n,NULL); + /* This just caches the sigs for later use. That way we + import a fully-cached key which speeds things up. */ + if(!opt.no_sig_cache) + check_key_signature(keyblock,n,NULL); - if( (sig->sig_class&~3) == 0x10 ) { + if( IS_UID_SIG(sig) || IS_UID_REV(sig) ) + { KBNODE unode = find_prev_kbnode( keyblock, n, PKT_USER_ID ); - if( !unode ) { - log_error( _("key %08lX: no user ID for signature\n"), - (ulong)keyid[1]); + if( !unode ) + { + log_error( _("key %s: no user ID for signature\n"), + keystr(keyid)); return -1; /* the complete keyblock is invalid */ - } + } /* If it hasn't been marked valid yet, keep trying */ if(!(unode->flag&1)) { rc = check_key_signature( keyblock, n, NULL); if( rc ) { - if (opt.verbose) - { - char *p=utf8_to_native(unode->pkt->pkt.user_id->name, - strlen(unode->pkt->pkt.user_id->name),0); - log_info( gpg_err_code (rc) == GPG_ERR_PUBKEY_ALGO ? - _("key %08lX: unsupported public key " - "algorithm on user id \"%s\"\n"): - _("key %08lX: invalid self-signature " - "on user id \"%s\"\n"), - (ulong)keyid[1],p); - xfree (p); - } - } - else + if( opt.verbose ) + { + char *p=utf8_to_native(unode->pkt->pkt.user_id->name, + strlen(unode->pkt->pkt.user_id->name),0); + log_info( rc == G10ERR_PUBKEY_ALGO ? + _("key %s: unsupported public key " + "algorithm on user ID \"%s\"\n"): + _("key %s: invalid self-signature " + "on user ID \"%s\"\n"), + keystr(keyid),p); + xfree(p); + } + } + else unode->flag |= 1; /* mark that signature checked */ } - } + } else if( sig->sig_class == 0x18 ) { /* Note that this works based solely on the timestamps like the rest of gpg. If the standard gets revocation targets, this may need to be revised. */ if( !knode ) - { - if (opt.verbose) - log_info( _("key %08lX: no subkey for subkey " - "binding signature\n"),(ulong)keyid[1]); - n->flag |= 4; /* delete this */ - } + { + if(opt.verbose) + log_info( _("key %s: no subkey for key binding\n"), + keystr(keyid)); + n->flag |= 4; /* delete this */ + } else - { - rc = check_key_signature( keyblock, n, NULL); - if( rc ) - { - if (opt.verbose) - log_info( gpg_err_code (rc) == GPG_ERR_PUBKEY_ALGO ? - _("key %08lX: unsupported public key algorithm\n"): - _("key %08lX: invalid subkey binding\n"), - (ulong)keyid[1]); - n->flag|=4; - } - else - { - /* It's valid, so is it newer? */ - if(sig->timestamp>=bsdate) - { - knode->flag |= 1; /* the subkey is valid */ - if(bsnode) - { - bsnode->flag|=4; /* Delete the last binding - sig since this one is - newer */ - if (opt.verbose) - log_info(_("key %08lX: removed multiple " - "subkey binding\n"), - (ulong)keyid[1]); - } - - bsnode=n; - bsdate=sig->timestamp; - } - else - n->flag|=4; /* older */ - } - } + { + rc = check_key_signature( keyblock, n, NULL); + if( rc ) + { + if(opt.verbose) + log_info(rc == G10ERR_PUBKEY_ALGO ? + _("key %s: unsupported public key" + " algorithm\n"): + _("key %s: invalid subkey binding\n"), + keystr(keyid)); + n->flag|=4; + } + else + { + /* It's valid, so is it newer? */ + if(sig->timestamp>=bsdate) { + knode->flag |= 1; /* the subkey is valid */ + if(bsnode) + { + bsnode->flag|=4; /* Delete the last binding + sig since this one is + newer */ + if(opt.verbose) + log_info(_("key %s: removed multiple subkey" + " binding\n"),keystr(keyid)); + } + + bsnode=n; + bsdate=sig->timestamp; + } + else + n->flag|=4; /* older */ + } + } } else if( sig->sig_class == 0x28 ) { /* We don't actually mark the subkey as revoked right @@ -1200,42 +1434,48 @@ chk_self_sigs( const char *fname, KBNODE keyblock, the binding sig is newer than the revocation sig. See the comment in getkey.c:merge_selfsigs_subkey for more */ - if( !knode ) { - if (opt.verbose) - log_info( _("key %08lX: no subkey for subkey " - "revocation signature\n"),(ulong)keyid[1]); - n->flag |= 4; /* delete this */ - } - else { - rc = check_key_signature( keyblock, n, NULL); - if( rc ) { - if (opt.verbose) - log_info( gpg_err_code (rc) == GPG_ERR_PUBKEY_ALGO ? - _("key %08lX: unsupported public key algorithm\n"): - _("key %08lX: invalid subkey revocation\n"), - (ulong)keyid[1]); - n->flag|=4; + if( !knode ) + { + if(opt.verbose) + log_info( _("key %s: no subkey for key revocation\n"), + keystr(keyid)); + n->flag |= 4; /* delete this */ } - else { - /* It's valid, so is it newer? */ - if(sig->timestamp>=rsdate) { - if(rsnode) { - rsnode->flag|=4; /* Delete the last revocation - sig since this one is - newer */ - if (opt.verbose) - log_info(_("key %08lX: removed multiple subkey " - "revocation signatures\n"), - (ulong)keyid[1]); + else + { + rc = check_key_signature( keyblock, n, NULL); + if( rc ) + { + if(opt.verbose) + log_info(rc == G10ERR_PUBKEY_ALGO ? + _("key %s: unsupported public" + " key algorithm\n"): + _("key %s: invalid subkey revocation\n"), + keystr(keyid)); + n->flag|=4; } - - rsnode=n; - rsdate=sig->timestamp; - } else - n->flag|=4; /* older */ + { + /* It's valid, so is it newer? */ + if(sig->timestamp>=rsdate) + { + if(rsnode) + { + rsnode->flag|=4; /* Delete the last revocation + sig since this one is + newer */ + if(opt.verbose) + log_info(_("key %s: removed multiple subkey" + " revocation\n"),keystr(keyid)); + } + + rsnode=n; + rsdate=sig->timestamp; + } + else + n->flag|=4; /* older */ + } } - } } } else @@ -1263,13 +1503,14 @@ delete_inv_parts( const char *fname, KBNODE keyblock, if( node->pkt->pkttype == PKT_USER_ID ) { uid_seen = 1; if( (node->flag & 2) || !(node->flag & 1) ) { - if( opt.verbose ) { - log_info( _("key %08lX: skipped user ID '"), - (ulong)keyid[1]); - print_utf8_string( stderr, node->pkt->pkt.user_id->name, - node->pkt->pkt.user_id->len ); - fputs("'\n", stderr ); - } + if( opt.verbose ) + { + char *p=utf8_to_native(node->pkt->pkt.user_id->name, + node->pkt->pkt.user_id->len,0); + log_info( _("key %s: skipped user ID \"%s\"\n"), + keystr(keyid),p); + xfree(p); + } delete_kbnode( node ); /* the user-id */ /* and all following packets up to the next user-id */ while( node->next @@ -1286,10 +1527,9 @@ delete_inv_parts( const char *fname, KBNODE keyblock, else if( node->pkt->pkttype == PKT_PUBLIC_SUBKEY || node->pkt->pkttype == PKT_SECRET_SUBKEY ) { if( (node->flag & 2) || !(node->flag & 1) ) { - if( opt.verbose ) { - log_info( _("key %08lX: skipped subkey\n"), - (ulong)keyid[1]); - } + if( opt.verbose ) + log_info( _("key %s: skipped subkey\n"),keystr(keyid)); + delete_kbnode( node ); /* the subkey */ /* and all following signature packets */ while( node->next @@ -1302,34 +1542,33 @@ delete_inv_parts( const char *fname, KBNODE keyblock, subkey_seen = 1; } else if( node->pkt->pkttype == PKT_SIGNATURE - && openpgp_pk_test_algo( node->pkt->pkt.signature - ->pubkey_algo, 0) + && check_pubkey_algo( node->pkt->pkt.signature->pubkey_algo) && node->pkt->pkt.signature->pubkey_algo != PUBKEY_ALGO_RSA ) delete_kbnode( node ); /* build_packet() can't handle this */ else if( node->pkt->pkttype == PKT_SIGNATURE && !node->pkt->pkt.signature->flags.exportable && - !(options&IMPORT_ALLOW_LOCAL_SIGS) && - seckey_available( node->pkt->pkt.signature->keyid ) ) { - /* Here we violate the rfc a bit by still allowing + !(options&IMPORT_LOCAL_SIGS) && + seckey_available( node->pkt->pkt.signature->keyid ) ) + { + /* here we violate the rfc a bit by still allowing * to import non-exportable signature when we have the * the secret key used to create this signature - it - * seems that this makes sense. */ - if (opt.verbose) - log_info( _("key %08lX: non exportable signature " - "(class %02x) - skipped\n"), - (ulong)keyid[1], - node->pkt->pkt.signature->sig_class ); - delete_kbnode( node ); - } + * seems that this makes sense */ + if(opt.verbose) + log_info( _("key %s: non exportable signature" + " (class 0x%02X) - skipped\n"), + keystr(keyid), node->pkt->pkt.signature->sig_class ); + delete_kbnode( node ); + } else if( node->pkt->pkttype == PKT_SIGNATURE && node->pkt->pkt.signature->sig_class == 0x20 ) { - if( uid_seen ) { - if (opt.verbose) - log_error( _("key %08lX: revocation certificate " - "at wrong place - skipped\n"), - (ulong)keyid[1]); - delete_kbnode( node ); - } + if( uid_seen ) + { + if(opt.verbose) + log_info( _("key %s: revocation certificate" + " at wrong place - skipped\n"),keystr(keyid)); + delete_kbnode( node ); + } else { /* If the revocation cert is from a different key than the one we're working on don't check it - it's @@ -1342,10 +1581,10 @@ delete_inv_parts( const char *fname, KBNODE keyblock, int rc = check_key_signature( keyblock, node, NULL); if( rc ) { - if (opt.verbose) - log_info ( _("key %08lX: invalid revocation " - "certificate: %s - skipped\n"), - (ulong)keyid[1], gpg_strerror (rc)); + if(opt.verbose) + log_info( _("key %s: invalid revocation" + " certificate: %s - skipped\n"), + keystr(keyid), g10_errstr(rc)); delete_kbnode( node ); } } @@ -1354,24 +1593,24 @@ delete_inv_parts( const char *fname, KBNODE keyblock, else if( node->pkt->pkttype == PKT_SIGNATURE && (node->pkt->pkt.signature->sig_class == 0x18 || node->pkt->pkt.signature->sig_class == 0x28) && - !subkey_seen ) { - if (opt.verbose) - log_info ( _("key %08lX: subkey signature " - "in wrong place - skipped\n"), - (ulong)keyid[1]); - delete_kbnode( node ); - } + !subkey_seen ) + { + if(opt.verbose) + log_info( _("key %s: subkey signature" + " in wrong place - skipped\n"), keystr(keyid)); + delete_kbnode( node ); + } else if( node->pkt->pkttype == PKT_SIGNATURE && !IS_CERT(node->pkt->pkt.signature)) { - if (opt.verbose) - log_info (_("key %08lX: unexpected signature class (0x%02X) -" - " skipped\n"),(ulong)keyid[1], - node->pkt->pkt.signature->sig_class); + if(opt.verbose) + log_info(_("key %s: unexpected signature class (0x%02X) -" + " skipped\n"),keystr(keyid), + node->pkt->pkt.signature->sig_class); delete_kbnode(node); } else if( (node->flag & 4) ) /* marked for deletion */ - delete_kbnode( node ); + delete_kbnode( node ); } /* note: because keyblock is the public key, it is never marked @@ -1393,7 +1632,6 @@ collapse_uids( KBNODE *keyblock ) KBNODE n, n2; int in_uid; int any=0; - u32 kid1; restart: for( n = *keyblock; n; n = n->next ) { @@ -1457,22 +1695,24 @@ collapse_uids( KBNODE *keyblock ) } } - if( (n = find_kbnode( *keyblock, PKT_PUBLIC_KEY )) ) - kid1 = keyid_from_pk( n->pkt->pkt.public_key, NULL ); - else if( (n = find_kbnode( *keyblock, PKT_SECRET_KEY )) ) - kid1 = keyid_from_sk( n->pkt->pkt.secret_key, NULL ); - else - kid1 = 0; - if (!opt.quiet) - log_info (_("key %08lX: duplicated user ID detected - merged\n"), - (ulong)kid1); + if(!opt.quiet) + { + const char *key="???"; + + if( (n = find_kbnode( *keyblock, PKT_PUBLIC_KEY )) ) + key=keystr_from_pk(n->pkt->pkt.public_key); + else if( (n = find_kbnode( *keyblock, PKT_SECRET_KEY )) ) + key=keystr_from_sk(n->pkt->pkt.secret_key); + + log_info(_("key %s: duplicated user ID detected - merged\n"),key); + } return 1; } /* Check for a 0x20 revocation from a revocation key that is not - present. This gets called without the benefit of merge_xxxx so you - can't rely on pk->revkey and friends. */ + present. This may be called without the benefit of merge_xxxx so + you can't rely on pk->revkey and friends. */ static void revocation_present(KBNODE keyblock) { @@ -1517,19 +1757,21 @@ revocation_present(KBNODE keyblock) rc=get_pubkey_byfprint_fast (NULL,sig->revkey[idx]->fpr, MAX_FINGERPRINT_LEN); - if ( gpg_err_code (rc) == GPG_ERR_NO_PUBKEY - || gpg_err_code (rc) == GPG_ERR_UNUSABLE_PUBKEY) + if(rc==G10ERR_NO_PUBKEY || rc==G10ERR_UNU_PUBKEY) { + char *tempkeystr=xstrdup(keystr_from_pk(pk)); + /* No, so try and get it */ - if(opt.keyserver_scheme && - opt.keyserver_options.auto_key_retrieve) + if(opt.keyserver + && (opt.keyserver_options.options + & KEYSERVER_AUTO_KEY_RETRIEVE)) { - log_info(_("WARNING: key %08lX may be revoked: " - "fetching revocation key %08lX\n"), - (ulong)keyid_from_pk(pk,NULL), - (ulong)keyid[1]); + log_info(_("WARNING: key %s may be revoked:" + " fetching revocation key %s\n"), + tempkeystr,keystr(keyid)); keyserver_import_fprint(sig->revkey[idx]->fpr, - MAX_FINGERPRINT_LEN); + MAX_FINGERPRINT_LEN, + opt.keyserver); /* Do we have it now? */ rc=get_pubkey_byfprint_fast (NULL, @@ -1537,12 +1779,12 @@ revocation_present(KBNODE keyblock) MAX_FINGERPRINT_LEN); } - if (gpg_err_code (rc) == GPG_ERR_NO_PUBKEY - || gpg_err_code (rc) == GPG_ERR_UNUSABLE_PUBKEY) - log_info(_("WARNING: key %08lX may be revoked: " - "revocation key %08lX not present.\n"), - (ulong)keyid_from_pk(pk,NULL), - (ulong)keyid[1]); + if(rc==G10ERR_NO_PUBKEY || rc==G10ERR_UNU_PUBKEY) + log_info(_("WARNING: key %s may be revoked:" + " revocation key %s not present.\n"), + tempkeystr,keystr(keyid)); + + xfree(tempkeystr); } } } @@ -1584,25 +1826,23 @@ merge_blocks( const char *fname, KBNODE keyblock_orig, KBNODE keyblock, && onode->pkt->pkt.signature->sig_class == 0x20 && !cmp_signatures(onode->pkt->pkt.signature, node->pkt->pkt.signature)) - { - found = 1; - break; - } + { + found = 1; + break; + } } if( !found ) { KBNODE n2 = clone_kbnode(node); insert_kbnode( keyblock_orig, n2, 0 ); n2->flag |= 1; ++*n_sigs; - - if (!opt.quiet) - { - char *p=get_user_id_printable (keyid); - log_info(_("key %08lX: \"%s\" " - "revocation certificate added\n"), - (ulong)keyid[1],p); - xfree (p); - } + if(!opt.quiet) + { + char *p=get_user_id_native (keyid); + log_info(_("key %s: \"%s\" revocation" + " certificate added\n"), keystr(keyid),p); + xfree(p); + } } } } @@ -1626,15 +1866,16 @@ merge_blocks( const char *fname, KBNODE keyblock_orig, KBNODE keyblock, break; } } - if( !found ) { + if( !found ) + { KBNODE n2 = clone_kbnode(node); insert_kbnode( keyblock_orig, n2, 0 ); n2->flag |= 1; ++*n_sigs; - if (!opt.quiet) - log_info( _("key %08lX: direct key signature added\n"), - (ulong)keyid[1]); - } + if(!opt.quiet) + log_info( _("key %s: direct key signature added\n"), + keystr(keyid)); + } } } @@ -1806,7 +2047,7 @@ merge_sigs( KBNODE dst, KBNODE src, int *n_sigs, { found++; break; - } + } if( !found ) { /* This signature is new or newer, append N to DST. * We add a clone to the original keyblock, because this @@ -1904,3 +2145,232 @@ append_key( KBNODE keyblock, KBNODE node, int *n_sigs, return 0; } + + + +/* Walk a public keyblock and produce a secret keyblock out of it. + Instead of inserting the secret key parameters (which we don't + have), we insert a stub. */ +static KBNODE +pub_to_sec_keyblock (KBNODE pub_keyblock) +{ + KBNODE pubnode, secnode; + KBNODE sec_keyblock = NULL; + KBNODE walkctx = NULL; + + while((pubnode = walk_kbnode (pub_keyblock,&walkctx,0))) + { + if (pubnode->pkt->pkttype == PKT_PUBLIC_KEY + || pubnode->pkt->pkttype == PKT_PUBLIC_SUBKEY) + { + /* Make a secret key. We only need to convert enough to + write the keyblock out. */ + PKT_public_key *pk = pubnode->pkt->pkt.public_key; + PACKET *pkt = xmalloc_clear (sizeof *pkt); + PKT_secret_key *sk = xmalloc_clear (sizeof *sk); + int i, n; + + if (pubnode->pkt->pkttype == PKT_PUBLIC_KEY) + pkt->pkttype = PKT_SECRET_KEY; + else + pkt->pkttype = PKT_SECRET_SUBKEY; + + pkt->pkt.secret_key = sk; + + copy_public_parts_to_secret_key ( pk, sk ); + sk->version = pk->version; + sk->timestamp = pk->timestamp; + + n = pubkey_get_npkey (pk->pubkey_algo); + if (!n) + n = 1; /* Unknown number of parameters, however the data + is stored in the first mpi. */ + for (i=0; i < n; i++ ) + sk->skey[i] = mpi_copy (pk->pkey[i]); + + sk->is_protected = 1; + sk->protect.s2k.mode = 1001; + + secnode = new_kbnode (pkt); + } + else + { + secnode = clone_kbnode (pubnode); + } + + if(!sec_keyblock) + sec_keyblock = secnode; + else + add_kbnode (sec_keyblock, secnode); + } + + return sec_keyblock; +} + + +/* Walk over the secret keyring SEC_KEYBLOCK and update any simple + stub keys with the serial number SNNUM of the card if one of the + fingerprints FPR1, FPR2 or FPR3 match. Print a note if the key is + a duplicate (may happen in case of backed uped keys). + + Returns: True if anything changed. +*/ +static int +update_sec_keyblock_with_cardinfo (KBNODE sec_keyblock, + const unsigned char *fpr1, + const unsigned char *fpr2, + const unsigned char *fpr3, + const char *serialnostr) +{ + KBNODE node; + KBNODE walkctx = NULL; + PKT_secret_key *sk; + byte array[MAX_FINGERPRINT_LEN]; + size_t n; + int result = 0; + const char *s; + + while((node = walk_kbnode (sec_keyblock, &walkctx, 0))) + { + if (node->pkt->pkttype != PKT_SECRET_KEY + && node->pkt->pkttype != PKT_SECRET_SUBKEY) + continue; + sk = node->pkt->pkt.secret_key; + + fingerprint_from_sk (sk, array, &n); + if (n != 20) + continue; /* Can't be a card key. */ + if ( !((fpr1 && !memcmp (array, fpr1, 20)) + || (fpr2 && !memcmp (array, fpr2, 20)) + || (fpr3 && !memcmp (array, fpr3, 20))) ) + continue; /* No match. */ + + if (sk->is_protected == 1 && sk->protect.s2k.mode == 1001) + { + /* Standard case: migrate that stub to a key stub. */ + sk->protect.s2k.mode = 1002; + s = serialnostr; + for (sk->protect.ivlen=0; sk->protect.ivlen < 16 && *s && s[1]; + sk->protect.ivlen++, s += 2) + sk->protect.iv[sk->protect.ivlen] = xtoi_2 (s); + result = 1; + } + else if (sk->is_protected == 1 && sk->protect.s2k.mode == 1002) + { + s = serialnostr; + for (sk->protect.ivlen=0; sk->protect.ivlen < 16 && *s && s[1]; + sk->protect.ivlen++, s += 2) + if (sk->protect.iv[sk->protect.ivlen] != xtoi_2 (s)) + { + log_info (_("NOTE: a key's S/N does not " + "match the card's one\n")); + break; + } + } + else + { + if (node->pkt->pkttype != PKT_SECRET_KEY) + log_info (_("NOTE: primary key is online and stored on card\n")); + else + log_info (_("NOTE: secondary key is online and stored on card\n")); + } + } + + return result; +} + + + +/* Check whether a secret key stub exists for the public key PK. If + not create such a stub key and store it into the secring. If it + exists, add appropriate subkey stubs and update the secring. + Return 0 if the key could be created. */ +int +auto_create_card_key_stub ( const char *serialnostr, + const unsigned char *fpr1, + const unsigned char *fpr2, + const unsigned char *fpr3) +{ + KBNODE pub_keyblock; + KBNODE sec_keyblock; + KEYDB_HANDLE hd; + int rc; + + /* We only want to do this for an OpenPGP card. */ + if (!serialnostr || strncmp (serialnostr, "D27600012401", 12) + || strlen (serialnostr) != 32 ) + return G10ERR_GENERAL; + + /* First get the public keyring from any of the provided fingerprints. */ + if ( (fpr1 && !get_keyblock_byfprint (&pub_keyblock, fpr1, 20)) + || (fpr2 && !get_keyblock_byfprint (&pub_keyblock, fpr2, 20)) + || (fpr3 && !get_keyblock_byfprint (&pub_keyblock, fpr3, 20))) + ; + else + return G10ERR_GENERAL; + + hd = keydb_new (1); + + /* Now check whether there is a secret keyring. */ + { + PKT_public_key *pk = pub_keyblock->pkt->pkt.public_key; + byte afp[MAX_FINGERPRINT_LEN]; + size_t an; + + fingerprint_from_pk (pk, afp, &an); + memset (afp, 0, MAX_FINGERPRINT_LEN); + rc = keydb_search_fpr (hd, afp); + } + + if (!rc) + { + rc = keydb_get_keyblock (hd, &sec_keyblock); + if (rc) + { + log_error (_("error reading keyblock: %s\n"), g10_errstr(rc) ); + rc = G10ERR_GENERAL; + } + else + { + merge_keys_and_selfsig (sec_keyblock); + + /* FIXME: We need to add new subkeys first. */ + if (update_sec_keyblock_with_cardinfo (sec_keyblock, + fpr1, fpr2, fpr3, + serialnostr)) + { + rc = keydb_update_keyblock (hd, sec_keyblock ); + if (rc) + log_error (_("error writing keyring `%s': %s\n"), + keydb_get_resource_name (hd), g10_errstr(rc) ); + } + } + } + else /* A secret key does not exists - create it. */ + { + sec_keyblock = pub_to_sec_keyblock (pub_keyblock); + update_sec_keyblock_with_cardinfo (sec_keyblock, + fpr1, fpr2, fpr3, + serialnostr); + + rc = keydb_locate_writable (hd, NULL); + if (rc) + { + log_error (_("no default secret keyring: %s\n"), g10_errstr (rc)); + rc = G10ERR_GENERAL; + } + else + { + rc = keydb_insert_keyblock (hd, sec_keyblock ); + if (rc) + log_error (_("error writing keyring `%s': %s\n"), + keydb_get_resource_name (hd), g10_errstr(rc) ); + } + } + + release_kbnode (sec_keyblock); + release_kbnode (pub_keyblock); + keydb_release (hd); + return rc; +} + diff --git a/g10/kbnode.c b/g10/kbnode.c index 58daad871..b09546451 100644 --- a/g10/kbnode.c +++ b/g10/kbnode.c @@ -1,5 +1,6 @@ /* kbnode.c - keyblock node utility functions - * Copyright (C) 1998, 1999, 2000, 2001 Free Software Foundation, Inc. + * Copyright (C) 1998, 1999, 2000, 2001, 2002, + * 2005 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -15,7 +16,8 @@ * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. */ #include <config.h> @@ -26,7 +28,6 @@ #include "gpg.h" #include "util.h" -#include "memory.h" #include "packet.h" #include "keydb.h" @@ -43,7 +44,7 @@ alloc_node(void) if( n ) unused_nodes = n->next; else - n = xmalloc ( sizeof *n ); + n = xmalloc( sizeof *n ); n->next = NULL; n->pkt = NULL; n->flag = 0; @@ -60,7 +61,7 @@ free_node( KBNODE n ) n->next = unused_nodes; unused_nodes = n; #else - xfree ( n ); + xfree( n ); #endif } } @@ -96,7 +97,7 @@ release_kbnode( KBNODE n ) n2 = n->next; if( !is_cloned_kbnode(n) ) { free_packet( n->pkt ); - xfree ( n->pkt ); + xfree( n->pkt ); } free_node( n ); n = n2; @@ -114,8 +115,6 @@ delete_kbnode( KBNODE node ) node->private_flag |= 1; } - - /**************** * Append NODE to ROOT. ROOT must exist! */ @@ -269,7 +268,7 @@ commit_kbnode( KBNODE *root ) nl->next = n->next; if( !is_cloned_kbnode(n) ) { free_packet( n->pkt ); - xfree ( n->pkt ); + xfree( n->pkt ); } free_node( n ); changed = 1; @@ -293,7 +292,7 @@ remove_kbnode( KBNODE *root, KBNODE node ) nl->next = n->next; if( !is_cloned_kbnode(n) ) { free_packet( n->pkt ); - xfree ( n->pkt ); + xfree( n->pkt ); } free_node( n ); } diff --git a/g10/keydb.c b/g10/keydb.c index b64f38cbc..dbad8435a 100644 --- a/g10/keydb.c +++ b/g10/keydb.c @@ -1,5 +1,5 @@ /* keydb.c - key database dispatcher - * Copyright (C) 2001, 2002, 2003 Free Software Foundation, Inc. + * Copyright (C) 2001, 2002, 2003, 2004, 2005 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -15,7 +15,8 @@ * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. */ #include <config.h> @@ -71,6 +72,124 @@ static int lock_all (KEYDB_HANDLE hd); static void unlock_all (KEYDB_HANDLE hd); +/* Handle the creation of a keyring if it does not yet exist. Take + into acount that other processes might have the keyring already + locked. This lock check does not work if the directory itself is + not yet available. */ +static int +maybe_create_keyring (char *filename, int force) +{ + DOTLOCK lockhd = NULL; + IOBUF iobuf; + int rc; + mode_t oldmask; + char *last_slash_in_filename; + + /* A quick test whether the filename already exists. */ + if (!access (filename, F_OK)) + return 0; + + /* If we don't want to create a new file at all, there is no need to + go any further - bail out right here. */ + if (!force) + return gpg_error (GPG_ERR_ENOENT); + + /* First of all we try to create the home directory. Note, that we + don't do any locking here because any sane application of gpg + would create the home directory by itself and not rely on gpg's + tricky auto-creation which is anyway only done for some home + directory name patterns. */ + last_slash_in_filename = strrchr (filename, DIRSEP_C); + *last_slash_in_filename = 0; + if (access(filename, F_OK)) + { + static int tried; + + if (!tried) + { + tried = 1; + try_make_homedir (filename); + } + if (access (filename, F_OK)) + { + rc = gpg_error_from_errno (errno); + *last_slash_in_filename = DIRSEP_C; + goto leave; + } + } + *last_slash_in_filename = DIRSEP_C; + + + /* To avoid races with other instances of gpg trying to create or + update the keyring (it is removed during an update for a short + time), we do the next stuff in a locked state. */ + lockhd = create_dotlock (filename); + if (!lockhd) + { + /* A reason for this to fail is that the directory is not + writable. However, this whole locking stuff does not make + sense if this is the case. An empty non-writable directory + with no keyring is not really useful at all. */ + if (opt.verbose) + log_info ("can't allocate lock for `%s'\n", filename ); + + if (!force) + return G10ERR_OPEN_FILE; + else + return G10ERR_GENERAL; + } + + if ( make_dotlock (lockhd, -1) ) + { + /* This is something bad. Probably a stale lockfile. */ + log_info ("can't lock `%s'\n", filename ); + rc = G10ERR_GENERAL; + goto leave; + } + + /* Now the real test while we are locked. */ + if (!access(filename, F_OK)) + { + rc = 0; /* Okay, we may access the file now. */ + goto leave; + } + + /* The file does not yet exist, create it now. */ + oldmask = umask (077); + if (is_secured_filename (filename)) + { + iobuf = NULL; + errno = EPERM; + } + else + iobuf = iobuf_create (filename); + umask (oldmask); + if (!iobuf) + { + log_error ( _("error creating keyring `%s': %s\n"), + filename, strerror(errno)); + rc = G10ERR_OPEN_FILE; + goto leave; + } + + if (!opt.quiet) + log_info (_("keyring `%s' created\n"), filename); + + iobuf_close (iobuf); + /* Must invalidate that ugly cache */ + iobuf_ioctl (NULL, 2, 0, filename); + rc = 0; + + leave: + if (lockhd) + { + release_dotlock (lockhd); + destroy_dotlock (lockhd); + } + return rc; +} + + /* * Register a resource (which currently may only be a keyring file). * The first keyring which is added by this function is @@ -78,14 +197,14 @@ static void unlock_all (KEYDB_HANDLE hd); * Note: this function may be called before secure memory is * available. * Flag 1 == force - * Flag 2 == default + * Flag 2 == mark resource as primary + * Flag 4 == This is a default resources */ int keydb_add_resource (const char *url, int flags, int secret) { static int any_secret, any_public; const char *resname = url; - iobuf_t iobuf = NULL; char *filename = NULL; int force=(flags&1); int rc = 0; @@ -104,7 +223,7 @@ keydb_add_resource (const char *url, int flags, int secret) #if !defined(HAVE_DRIVE_LETTERS) && !defined(__riscos__) else if (strchr (resname, ':')) { log_error ("invalid key resource URL `%s'\n", url ); - rc = GPG_ERR_GENERAL; + rc = G10ERR_GENERAL; goto leave; } #endif /* !HAVE_DRIVE_LETTERS && !__riscos__ */ @@ -146,65 +265,18 @@ keydb_add_resource (const char *url, int flags, int secret) switch (rt) { case KEYDB_RESOURCE_TYPE_NONE: log_error ("unknown type of key resource `%s'\n", url ); - rc = GPG_ERR_GENERAL; + rc = G10ERR_GENERAL; goto leave; case KEYDB_RESOURCE_TYPE_KEYRING: - if (access(filename, F_OK)) - { /* file does not exist */ - mode_t oldmask; - char *last_slash_in_filename; - - if (!force) - { - rc = gpg_error_from_errno (errno); - goto leave; - } - - last_slash_in_filename = strrchr (filename, DIRSEP_C); - *last_slash_in_filename = 0; - if (access(filename, F_OK)) - { /* On the first time we try to create the default - homedir and check again. */ - static int tried; - - if (!tried) - { - tried = 1; - try_make_homedir (filename); - } - if (access (filename, F_OK)) - { - rc = gpg_error_from_errno (errno); - *last_slash_in_filename = DIRSEP_C; - goto leave; - } - } - *last_slash_in_filename = DIRSEP_C; - - oldmask=umask(077); - iobuf = iobuf_create (filename); - umask(oldmask); - if (!iobuf) - { - log_error ( _("error creating keyring `%s': %s\n"), - filename, strerror(errno)); - rc = gpg_error_from_errno (errno); - goto leave; - } - - if (!opt.quiet) - log_info (_("keyring `%s' created\n"), filename); - iobuf_close (iobuf); - iobuf = NULL; - /* must invalidate that ugly cache */ - iobuf_ioctl (NULL, 2, 0, (char*)filename); - } /* end file creation */ + rc = maybe_create_keyring (filename, force); + if (rc) + goto leave; if(keyring_register_filename (filename, secret, &token)) { if (used_resources >= MAX_KEYDB_RESOURCES) - rc = GPG_ERR_RESOURCE_LIMIT; + rc = G10ERR_RESOURCE_LIMIT; else { if(flags&2) @@ -228,7 +300,7 @@ keydb_add_resource (const char *url, int flags, int secret) default: log_error ("resource type of `%s' not supported\n", url); - rc = GPG_ERR_GENERAL; + rc = G10ERR_GENERAL; goto leave; } @@ -236,7 +308,18 @@ keydb_add_resource (const char *url, int flags, int secret) leave: if (rc) - log_error ("keyblock resource `%s': %s\n", filename, gpg_strerror (rc)); + { + /* Secret keyrings are not required in all cases. To avoid + having gpg return failure we use log_info here if the + rewsource is a secret one and marked as default + resource. */ + if ((flags&4) && secret) + log_info (_("keyblock resource `%s': %s\n"), + filename, g10_errstr(rc)); + else + log_error (_("keyblock resource `%s': %s\n"), + filename, g10_errstr(rc)); + } else if (secret) any_secret = 1; else @@ -254,7 +337,7 @@ keydb_new (int secret) KEYDB_HANDLE hd; int i, j; - hd = xcalloc (1,sizeof *hd); + hd = xmalloc_clear (sizeof *hd); hd->found = -1; assert (used_resources <= MAX_KEYDB_RESOURCES); @@ -414,14 +497,14 @@ keydb_get_keyblock (KEYDB_HANDLE hd, KBNODE *ret_kb) int rc = 0; if (!hd) - return GPG_ERR_INV_ARG; + return G10ERR_INV_ARG; if ( hd->found < 0 || hd->found >= hd->used) return -1; /* nothing found */ switch (hd->active[hd->found].type) { case KEYDB_RESOURCE_TYPE_NONE: - rc = GPG_ERR_GENERAL; /* oops */ + rc = G10ERR_GENERAL; /* oops */ break; case KEYDB_RESOURCE_TYPE_KEYRING: rc = keyring_get_keyblock (hd->active[hd->found].u.kr, ret_kb); @@ -440,7 +523,7 @@ keydb_update_keyblock (KEYDB_HANDLE hd, KBNODE kb) int rc = 0; if (!hd) - return GPG_ERR_INV_ARG; + return G10ERR_INV_ARG; if ( hd->found < 0 || hd->found >= hd->used) return -1; /* nothing found */ @@ -454,7 +537,7 @@ keydb_update_keyblock (KEYDB_HANDLE hd, KBNODE kb) switch (hd->active[hd->found].type) { case KEYDB_RESOURCE_TYPE_NONE: - rc = GPG_ERR_GENERAL; /* oops */ + rc = G10ERR_GENERAL; /* oops */ break; case KEYDB_RESOURCE_TYPE_KEYRING: rc = keyring_update_keyblock (hd->active[hd->found].u.kr, kb); @@ -476,7 +559,7 @@ keydb_insert_keyblock (KEYDB_HANDLE hd, KBNODE kb) int idx; if (!hd) - return GPG_ERR_INV_ARG; + return G10ERR_INV_ARG; if( opt.dry_run ) return 0; @@ -486,7 +569,7 @@ keydb_insert_keyblock (KEYDB_HANDLE hd, KBNODE kb) else if ( hd->current >= 0 && hd->current < hd->used) idx = hd->current; else - return GPG_ERR_GENERAL; + return G10ERR_GENERAL; rc = lock_all (hd); if (rc) @@ -494,7 +577,7 @@ keydb_insert_keyblock (KEYDB_HANDLE hd, KBNODE kb) switch (hd->active[idx].type) { case KEYDB_RESOURCE_TYPE_NONE: - rc = GPG_ERR_GENERAL; /* oops */ + rc = G10ERR_GENERAL; /* oops */ break; case KEYDB_RESOURCE_TYPE_KEYRING: rc = keyring_insert_keyblock (hd->active[idx].u.kr, kb); @@ -515,7 +598,7 @@ keydb_delete_keyblock (KEYDB_HANDLE hd) int rc = -1; if (!hd) - return GPG_ERR_INV_ARG; + return G10ERR_INV_ARG; if ( hd->found < 0 || hd->found >= hd->used) return -1; /* nothing found */ @@ -529,7 +612,7 @@ keydb_delete_keyblock (KEYDB_HANDLE hd) switch (hd->active[hd->found].type) { case KEYDB_RESOURCE_TYPE_NONE: - rc = GPG_ERR_GENERAL; /* oops */ + rc = G10ERR_GENERAL; /* oops */ break; case KEYDB_RESOURCE_TYPE_KEYRING: rc = keyring_delete_keyblock (hd->active[hd->found].u.kr); @@ -552,7 +635,7 @@ keydb_locate_writable (KEYDB_HANDLE hd, const char *reserved) int rc; if (!hd) - return GPG_ERR_INV_ARG; + return G10ERR_INV_ARG; rc = keydb_search_reset (hd); /* this does reset hd->current */ if (rc) @@ -598,7 +681,7 @@ keydb_locate_writable (KEYDB_HANDLE hd, const char *reserved) * Rebuild the caches of all key resources. */ void -keydb_rebuild_caches (void) +keydb_rebuild_caches (int noisy) { int i, rc; @@ -611,10 +694,10 @@ keydb_rebuild_caches (void) case KEYDB_RESOURCE_TYPE_NONE: /* ignore */ break; case KEYDB_RESOURCE_TYPE_KEYRING: - rc = keyring_rebuild_cache (all_resources[i].token); + rc = keyring_rebuild_cache (all_resources[i].token,noisy); if (rc) log_error (_("failed to rebuild keyring cache: %s\n"), - gpg_strerror (rc)); + g10_errstr (rc)); break; } } @@ -631,7 +714,7 @@ keydb_search_reset (KEYDB_HANDLE hd) int i, rc = 0; if (!hd) - return GPG_ERR_INV_ARG; + return G10ERR_INV_ARG; hd->current = 0; hd->found = -1; @@ -660,7 +743,7 @@ keydb_search2 (KEYDB_HANDLE hd, KEYDB_SEARCH_DESC *desc, int rc = -1; if (!hd) - return GPG_ERR_INV_ARG; + return G10ERR_INV_ARG; while (rc == -1 && hd->current >= 0 && hd->current < hd->used) { switch (hd->active[hd->current].type) { diff --git a/g10/keydb.h b/g10/keydb.h index 4920e88a1..4923a842c 100644 --- a/g10/keydb.h +++ b/g10/keydb.h @@ -1,5 +1,6 @@ /* keydb.h - Key database - * Copyright (C) 1998, 1999, 2000, 2001, 2003 Free Software Foundation, Inc. + * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, + * 2006 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -15,14 +16,16 @@ * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. */ #ifndef G10_KEYDB_H #define G10_KEYDB_H +#include <assuan.h> + #include "types.h" -#include "global.h" #include "packet.h" #include "cipher.h" @@ -75,7 +78,7 @@ struct keyblock_pos_struct { enum resource_type rt; off_t offset; /* position information */ unsigned count; /* length of the keyblock in packets */ - iobuf_t fp; /* used by enum_keyblocks */ + iobuf_t fp; /* Used by enum_keyblocks. */ int secret; /* working on a secret keyring */ PACKET *pkt; /* ditto */ int valid; @@ -131,11 +134,11 @@ typedef enum { struct keydb_search_desc { KeydbSearchMode mode; - int (*skipfnc)(void *,u32*); + int (*skipfnc)(void *,u32*,PKT_user_id*); void *skipfncvalue; union { const char *name; - char fpr[MAX_FINGERPRINT_LEN]; + byte fpr[MAX_FINGERPRINT_LEN]; u32 kid[2]; } u; int exact; @@ -156,7 +159,7 @@ int keydb_update_keyblock (KEYDB_HANDLE hd, KBNODE kb); int keydb_insert_keyblock (KEYDB_HANDLE hd, KBNODE kb); int keydb_delete_keyblock (KEYDB_HANDLE hd); int keydb_locate_writable (KEYDB_HANDLE hd, const char *reserved); -void keydb_rebuild_caches (void); +void keydb_rebuild_caches (int noisy); int keydb_search_reset (KEYDB_HANDLE hd); #define keydb_search(a,b,c) keydb_search2((a),(b),(c),NULL) int keydb_search2 (KEYDB_HANDLE hd, KEYDB_SEARCH_DESC *desc, @@ -183,14 +186,23 @@ int build_sk_list( STRLIST locusr, SK_LIST *ret_sk_list, int unlock, unsigned use ); /*-- passphrase.h --*/ +assuan_context_t agent_open (int try, const char *orig_codeset); +void agent_close (assuan_context_t ctx); int have_static_passphrase(void); +void set_passphrase_from_string(const char *pass); void read_passphrase_from_fd( int fd ); -void passphrase_clear_cache ( u32 *keyid, int algo ); +void passphrase_clear_cache ( u32 *keyid, const char *cacheid, int algo ); +char *ask_passphrase (const char *description, + const char *tryagain_text, + const char *promptid, + const char *prompt, + const char *cacheid, int *canceled); DEK *passphrase_to_dek( u32 *keyid, int pubkey_algo, int cipher_algo, STRING2KEY *s2k, int mode, const char *tryagain_text, int *canceled); void set_next_passphrase( const char *s ); char *get_last_passphrase(void); +void next_to_last_passphrase(void); /*-- getkey.c --*/ int classify_user_id( const char *name, KEYDB_SEARCH_DESC *desc); @@ -201,7 +213,7 @@ int get_pubkey_fast ( PKT_public_key *pk, u32 *keyid ); KBNODE get_pubkeyblock( u32 *keyid ); int get_pubkey_byname( PKT_public_key *pk, const char *name, KBNODE *ret_keyblock, KEYDB_HANDLE *ret_kdbhd, - int include_disabled ); + int include_unusable ); int get_pubkey_bynames( GETKEY_CTX *rx, PKT_public_key *pk, STRLIST names, KBNODE *ret_keyblock ); int get_pubkey_next( GETKEY_CTX ctx, PKT_public_key *pk, KBNODE *ret_keyblock ); @@ -219,27 +231,39 @@ 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, STRLIST names, KBNODE *ret_keyblock ); +int get_seckey_next (GETKEY_CTX ctx, PKT_secret_key *sk, KBNODE *ret_keyblock); +void get_seckey_end( GETKEY_CTX ctx ); + int get_seckey_byfprint( PKT_secret_key *sk, const byte *fprint, size_t fprint_len); -int get_seckey_next( GETKEY_CTX ctx, PKT_secret_key *sk, KBNODE *ret_keyblock ); -void get_seckey_end( GETKEY_CTX ctx ); +int get_seckeyblock_byfprint (KBNODE *ret_keyblock, const byte *fprint, + size_t fprint_len ); + + int enum_secret_keys( void **context, PKT_secret_key *sk, int with_subkeys, int with_spm ); void merge_keys_and_selfsig( KBNODE keyblock ); char*get_user_id_string( u32 *keyid ); -char*get_user_id_string_printable( u32 *keyid ); +char*get_user_id_string_native( u32 *keyid ); char*get_long_user_id_string( u32 *keyid ); char*get_user_id( u32 *keyid, size_t *rn ); -char*get_user_id_printable( u32 *keyid ); +char*get_user_id_native( u32 *keyid ); KEYDB_HANDLE get_ctx_handle(GETKEY_CTX ctx); +void release_akl(void); +int parse_auto_key_locate(char *options); /*-- keyid.c --*/ int pubkey_letter( int algo ); -u32 v3_keyid (gcry_mpi_t a, u32 *ki); +void hash_public_key( gcry_md_hd_t md, PKT_public_key *pk ); +size_t keystrlen(void); +const char *keystr(u32 *keyid); +const char *keystr_from_pk(PKT_public_key *pk); +const char *keystr_from_sk(PKT_secret_key *sk); +const char *keystr_from_desc(KEYDB_SEARCH_DESC *desc); u32 keyid_from_sk( PKT_secret_key *sk, u32 *keyid ); u32 keyid_from_pk( PKT_public_key *pk, u32 *keyid ); u32 keyid_from_sig( PKT_signature *sig, u32 *keyid ); -u32 keyid_from_fingerprint( const byte *fprint, size_t fprint_len, u32 *keyid ); +u32 keyid_from_fingerprint(const byte *fprint, size_t fprint_len, u32 *keyid); byte *namehash_from_uid(PKT_user_id *uid); unsigned nbits_from_pk( PKT_public_key *pk ); unsigned nbits_from_sk( PKT_secret_key *sk ); @@ -249,20 +273,16 @@ const char *datestr_from_sig( PKT_signature *sig ); const char *expirestr_from_pk( PKT_public_key *pk ); const char *expirestr_from_sk( PKT_secret_key *sk ); const char *expirestr_from_sig( PKT_signature *sig ); - +const char *revokestr_from_pk( PKT_public_key *pk ); +const char *usagestr_from_pk( PKT_public_key *pk ); const char *colon_strtime (u32 t); const char *colon_datestr_from_pk (PKT_public_key *pk); const char *colon_datestr_from_sk (PKT_secret_key *sk); const char *colon_datestr_from_sig (PKT_signature *sig); const char *colon_expirestr_from_sig (PKT_signature *sig); - byte *fingerprint_from_sk( PKT_secret_key *sk, byte *buf, size_t *ret_len ); byte *fingerprint_from_pk( PKT_public_key *pk, byte *buf, size_t *ret_len ); -char *serialno_and_fpr_from_sk (const unsigned char *sn, size_t snlen, - PKT_secret_key *sk); - - /*-- kbnode.c --*/ KBNODE new_kbnode( PACKET *pkt ); KBNODE clone_kbnode( KBNODE node ); diff --git a/g10/keyedit.c b/g10/keyedit.c index 2f9fccbf5..2a31d5973 100644 --- a/g10/keyedit.c +++ b/g10/keyedit.c @@ -1,6 +1,6 @@ /* keyedit.c - keyedit stuff - * Copyright (C) 1998, 1999, 2000, 2001, 2002, - * 2003 Free Software Foundation, Inc. + * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, + * 2006 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -16,7 +16,8 @@ * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. */ #include <config.h> @@ -26,6 +27,10 @@ #include <errno.h> #include <assert.h> #include <ctype.h> +#ifdef HAVE_LIBREADLINE +#include <stdio.h> +#include <readline/readline.h> +#endif #include "gpg.h" #include "options.h" @@ -33,7 +38,6 @@ #include "errors.h" #include "iobuf.h" #include "keydb.h" -#include "memory.h" #include "photoid.h" #include "util.h" #include "main.h" @@ -42,22 +46,32 @@ #include "ttyio.h" #include "status.h" #include "i18n.h" +#include "keyserver-internal.h" -static void show_prefs( PKT_user_id *uid, int verbose ); +static void show_prefs( PKT_user_id *uid, PKT_signature *selfsig, int verbose); +static void show_names(KBNODE keyblock,PKT_public_key *pk, + unsigned int flag,int with_prefs); static void show_key_with_all_names( KBNODE keyblock, int only_marked, int with_revoker, int with_fpr, int with_subkeys, int with_prefs ); static void show_key_and_fingerprint( KBNODE keyblock ); -static int menu_adduid( KBNODE keyblock, KBNODE sec_keyblock, int photo ); +static int menu_adduid( KBNODE keyblock, KBNODE sec_keyblock, + int photo, const char *photo_name ); static void menu_deluid( KBNODE pub_keyblock, KBNODE sec_keyblock ); -static int menu_delsig( KBNODE pub_keyblock ); +static int menu_delsig( KBNODE pub_keyblock ); +static int menu_clean(KBNODE keyblock,int self_only); static void menu_delkey( KBNODE pub_keyblock, KBNODE sec_keyblock ); static int menu_addrevoker( KBNODE pub_keyblock, KBNODE sec_keyblock, int sensitive ); static int menu_expire( KBNODE pub_keyblock, KBNODE sec_keyblock ); +static int menu_backsign(KBNODE pub_keyblock,KBNODE sec_keyblock); static int menu_set_primary_uid( KBNODE pub_keyblock, KBNODE sec_keyblock ); static int menu_set_preferences( KBNODE pub_keyblock, KBNODE sec_keyblock ); -static int menu_set_keyserver_url (KBNODE pub_keyblock, KBNODE sec_keyblock ); +static int menu_set_keyserver_url (const char *url, + KBNODE pub_keyblock, KBNODE sec_keyblock ); +static int menu_set_notation(const char *string, + KBNODE pub_keyblock,KBNODE sec_keyblock); static int menu_select_uid( KBNODE keyblock, int idx ); +static int menu_select_uid_namehash( KBNODE keyblock, const char *namehash ); static int menu_select_key( KBNODE keyblock, int idx ); static int count_uids( KBNODE keyblock ); static int count_uids_with_flag( KBNODE keyblock, unsigned flag ); @@ -68,6 +82,7 @@ static int count_selected_keys( KBNODE keyblock ); static int menu_revsig( KBNODE keyblock ); static int menu_revuid( KBNODE keyblock, KBNODE sec_keyblock ); static int menu_revkey( KBNODE pub_keyblock, KBNODE sec_keyblock ); +static int menu_revsubkey( KBNODE pub_keyblock, KBNODE sec_keyblock ); static int enable_disable_key( KBNODE keyblock, int disable ); static void menu_showphoto( KBNODE keyblock ); @@ -93,6 +108,100 @@ struct sign_attrib { char *trust_regexp; }; + +#ifdef ENABLE_CARD_SUPPORT +/* Given a node SEC_NODE with a secret key or subkey, locate the + corresponding public key from pub_keyblock. */ +static PKT_public_key * +find_pk_from_sknode (KBNODE pub_keyblock, KBNODE sec_node) +{ + KBNODE node = pub_keyblock; + PKT_secret_key *sk; + PKT_public_key *pk; + + if (sec_node->pkt->pkttype == PKT_SECRET_KEY + && node->pkt->pkttype == PKT_PUBLIC_KEY) + return node->pkt->pkt.public_key; + if (sec_node->pkt->pkttype != PKT_SECRET_SUBKEY) + return NULL; + sk = sec_node->pkt->pkt.secret_key; + for (; node; node = node->next) + if (node->pkt->pkttype == PKT_PUBLIC_SUBKEY) + { + pk = node->pkt->pkt.public_key; + if (pk->keyid[0] == sk->keyid[0] && pk->keyid[1] == sk->keyid[1]) + return pk; + } + + return NULL; +} +#endif /* ENABLE_CARD_SUPPORT */ + + +/* TODO: Fix duplicated code between here and the check-sigs/list-sigs + code in keylist.c. */ +static int +print_and_check_one_sig_colon( KBNODE keyblock, KBNODE node, + int *inv_sigs, int *no_key, int *oth_err, + int *is_selfsig, int print_without_key ) +{ + PKT_signature *sig = node->pkt->pkt.signature; + int rc, sigrc; + + /* TODO: Make sure a cached sig record here still has the pk that + issued it. See also keylist.c:list_keyblock_print */ + + switch((rc=check_key_signature(keyblock,node,is_selfsig))) + { + case 0: + node->flag &= ~(NODFLG_BADSIG|NODFLG_NOKEY|NODFLG_SIGERR); + sigrc = '!'; + break; + case G10ERR_BAD_SIGN: + node->flag = NODFLG_BADSIG; + sigrc = '-'; + if( inv_sigs ) + ++*inv_sigs; + break; + case G10ERR_NO_PUBKEY: + case G10ERR_UNU_PUBKEY: + node->flag = NODFLG_NOKEY; + sigrc = '?'; + if( no_key ) + ++*no_key; + break; + default: + node->flag = NODFLG_SIGERR; + sigrc = '%'; + if( oth_err ) + ++*oth_err; + break; + } + + if( sigrc != '?' || print_without_key ) + { + printf("sig:%c::%d:%08lX%08lX:%lu:%lu:", + sigrc,sig->pubkey_algo,(ulong)sig->keyid[0],(ulong)sig->keyid[1], + (ulong)sig->timestamp,(ulong)sig->expiredate); + + if(sig->trust_depth || sig->trust_value) + printf("%d %d",sig->trust_depth,sig->trust_value); + + printf(":"); + + if(sig->trust_regexp) + print_string(stdout,sig->trust_regexp,strlen(sig->trust_regexp),':'); + + printf("::%02x%c\n",sig->sig_class,sig->flags.exportable?'x':'l'); + + if(opt.show_subpackets) + print_subpackets_colon(sig); + } + + return (sigrc == '!'); +} + + /**************** * Print information about a signature, check it and return true * if the signature is okay. NODE must be a signature packet. @@ -109,20 +218,19 @@ print_and_check_one_sig( KBNODE keyblock, KBNODE node, /* TODO: Make sure a cached sig record here still has the pk that issued it. See also keylist.c:list_keyblock_print */ - rc = check_key_signature (keyblock, node, is_selfsig); - switch ( gpg_err_code (rc) ) { + switch( (rc = check_key_signature( keyblock, node, is_selfsig)) ) { case 0: node->flag &= ~(NODFLG_BADSIG|NODFLG_NOKEY|NODFLG_SIGERR); sigrc = '!'; break; - case GPG_ERR_BAD_SIGNATURE: + case G10ERR_BAD_SIGN: node->flag = NODFLG_BADSIG; sigrc = '-'; if( inv_sigs ) ++*inv_sigs; break; - case GPG_ERR_NO_PUBKEY: - case GPG_ERR_UNUSABLE_PUBKEY: + case G10ERR_NO_PUBKEY: + case G10ERR_UNU_PUBKEY: node->flag = NODFLG_NOKEY; sigrc = '?'; if( no_key ) @@ -136,7 +244,7 @@ print_and_check_one_sig( KBNODE keyblock, KBNODE node, break; } if( sigrc != '?' || print_without_key ) { - tty_printf("%s%c%c %c%c%c%c%c%c ", + tty_printf("%s%c%c %c%c%c%c%c%c %s %s", is_rev? "rev":"sig",sigrc, (sig->sig_class-0x10>0 && sig->sig_class-0x10<4)?'0'+sig->sig_class-0x10:' ', @@ -146,38 +254,38 @@ print_and_check_one_sig( KBNODE keyblock, KBNODE node, sig->flags.notation?'N':' ', sig->flags.expired?'X':' ', (sig->trust_depth>9)?'T': - (sig->trust_depth>0)?'0'+sig->trust_depth:' '); - if(opt.list_options&LIST_SHOW_LONG_KEYID) - tty_printf("%08lX%08lX",(ulong)sig->keyid[0],(ulong)sig->keyid[1]); - else - tty_printf("%08lX",(ulong)sig->keyid[1]); - tty_printf(" %s", datestr_from_sig(sig)); + (sig->trust_depth>0)?'0'+sig->trust_depth:' ', + keystr(sig->keyid),datestr_from_sig(sig)); if(opt.list_options&LIST_SHOW_SIG_EXPIRE) tty_printf(" %s",expirestr_from_sig(sig)); tty_printf(" "); if( sigrc == '%' ) - tty_printf("[%s] ", gpg_strerror (rc) ); + tty_printf("[%s] ", g10_errstr(rc) ); else if( sigrc == '?' ) ; else if( *is_selfsig ) { tty_printf( is_rev? _("[revocation]") : _("[self-signature]") ); } - else { + else + { size_t n; char *p = get_user_id( sig->keyid, &n ); - tty_print_utf8_string2( p, n, 40 ); - xfree (p); - } + tty_print_utf8_string2(p, n, opt.screen_columns-keystrlen()-26- + ((opt.list_options&LIST_SHOW_SIG_EXPIRE)?11:0)); + xfree(p); + } tty_printf("\n"); - if(sig->flags.policy_url && (opt.list_options&LIST_SHOW_POLICY)) + if(sig->flags.policy_url && (opt.list_options&LIST_SHOW_POLICY_URLS)) show_policy_url(sig,3,0); - if(sig->flags.notation && (opt.list_options&LIST_SHOW_NOTATION)) - show_notation(sig,3,0); + if(sig->flags.notation && (opt.list_options&LIST_SHOW_NOTATIONS)) + show_notation(sig,3,0, + ((opt.list_options&LIST_SHOW_STD_NOTATIONS)?1:0)+ + ((opt.list_options&LIST_SHOW_USER_NOTATIONS)?2:0)); - if(sig->flags.pref_ks && (opt.list_options&LIST_SHOW_KEYSERVER)) + if(sig->flags.pref_ks && (opt.list_options&LIST_SHOW_KEYSERVER_URLS)) show_keyserver_url(sig,3,0); } @@ -256,8 +364,6 @@ check_all_keysigs( KBNODE keyblock, int only_selected ) } - - static int sign_mk_attrib( PKT_signature *sig, void *opaque ) { @@ -298,7 +404,7 @@ sign_mk_attrib( PKT_signature *sig, void *opaque ) } static void -trustsig_prompt(byte *trust_value, byte *trust_depth, char **regexp) +trustsig_prompt(byte *trust_value,byte *trust_depth,char **regexp) { char *p; @@ -306,14 +412,13 @@ trustsig_prompt(byte *trust_value, byte *trust_depth, char **regexp) *trust_depth=0; *regexp=NULL; - tty_printf("\n"); /* Same string as pkclist.c:do_edit_ownertrust */ - tty_printf(_( - "Please decide how far you trust this user to correctly\n" - "verify other users' keys (by looking at passports,\n" - "checking fingerprints from different sources...)?\n\n")); - tty_printf (_(" (%d) I trust marginally\n"), 1); - tty_printf (_(" (%d) I trust fully\n"), 2); + tty_printf(_("Please decide how far you trust this user to correctly verify" + " other users' keys\n(by looking at passports, checking" + " fingerprints from different sources, etc.)\n")); + tty_printf("\n"); + tty_printf (_(" %d = I trust marginally\n"), 1); + tty_printf (_(" %d = I trust fully\n"), 2); tty_printf("\n"); while(*trust_value==0) @@ -326,7 +431,7 @@ trustsig_prompt(byte *trust_value, byte *trust_depth, char **regexp) *trust_value=60; else if(p[0]=='2' && !p[1]) *trust_value=120; - xfree (p); + xfree(p); } tty_printf("\n"); @@ -343,9 +448,7 @@ trustsig_prompt(byte *trust_value, byte *trust_depth, char **regexp) trim_spaces(p); cpr_kill_prompt(); *trust_depth=atoi(p); - xfree (p); - if(*trust_depth < 1 ) - *trust_depth=0; + xfree(p); } tty_printf("\n"); @@ -364,7 +467,7 @@ trustsig_prompt(byte *trust_value, byte *trust_depth, char **regexp) char *q=p; int regexplen=100,ind; - *regexp=xmalloc (regexplen); + *regexp=xmalloc(regexplen); /* Now mangle the domain the user entered into a regexp. To do this, \-escape everything that isn't alphanumeric, and attach @@ -394,7 +497,7 @@ trustsig_prompt(byte *trust_value, byte *trust_depth, char **regexp) strcat(*regexp,">$"); } - xfree (p); + xfree(p); tty_printf("\n"); } @@ -405,7 +508,7 @@ trustsig_prompt(byte *trust_value, byte *trust_depth, char **regexp) */ static int sign_uids( KBNODE keyblock, STRLIST locusr, int *ret_modified, - int local, int nonrevocable, int trust ) + int local, int nonrevocable, int trust, int interactive ) { int rc = 0; SK_LIST sk_list = NULL; @@ -413,7 +516,7 @@ sign_uids( KBNODE keyblock, STRLIST locusr, int *ret_modified, PKT_secret_key *sk = NULL; KBNODE node, uidnode; PKT_public_key *primary_pk=NULL; - int select_all = !count_selected_uids(keyblock); + int select_all = !count_selected_uids(keyblock) || interactive; int all_v3=1; /* Are there any non-v3 sigs on this key already? */ @@ -432,22 +535,21 @@ sign_uids( KBNODE keyblock, STRLIST locusr, int *ret_modified, * be one which is capable of signing keys. I can't see a reason * why to sign keys using a subkey. Implementation of USAGE_CERT * is just a hack in getkey.c and does not mean that a subkey - * marked as certification capable will be used */ - rc=build_sk_list( locusr, &sk_list, 0, PUBKEY_USAGE_SIG|PUBKEY_USAGE_CERT); + * marked as certification capable will be used. */ + rc=build_sk_list( locusr, &sk_list, 0, PUBKEY_USAGE_CERT); if( rc ) goto leave; /* loop over all signators */ for( sk_rover = sk_list; sk_rover; sk_rover = sk_rover->next ) { u32 sk_keyid[2],pk_keyid[2]; - size_t n; char *p,*trust_regexp=NULL; int force_v4=0,class=0,selfsig=0; u32 duration=0,timestamp=0; byte trust_depth=0,trust_value=0; if(local || nonrevocable || trust || - opt.cert_policy_url || opt.cert_notation_data) + opt.cert_policy_url || opt.cert_notations) force_v4=1; /* we have to use a copy of the sk, because make_keysig_packet @@ -483,10 +585,12 @@ sign_uids( KBNODE keyblock, STRLIST locusr, int *ret_modified, force_v4=0; } } - else if( node->pkt->pkttype == PKT_USER_ID ) { + else if( node->pkt->pkttype == PKT_USER_ID ) + { uidnode = (node->flag & NODFLG_MARK_A)? node : NULL; if(uidnode) { + int yesreally=0; char *user=utf8_to_native(uidnode->pkt->pkt.user_id->name, uidnode->pkt->pkt.user_id->len, 0); @@ -495,7 +599,9 @@ sign_uids( KBNODE keyblock, STRLIST locusr, int *ret_modified, { tty_printf(_("User ID \"%s\" is revoked."),user); - if(opt.expert) + if(selfsig) + tty_printf("\n"); + else if(opt.expert) { tty_printf("\n"); /* No, so remove the mark and continue */ @@ -503,11 +609,17 @@ sign_uids( KBNODE keyblock, STRLIST locusr, int *ret_modified, _("Are you sure you " "still want to sign " "it? (y/N) "))) - uidnode->flag &= ~NODFLG_MARK_A; + { + uidnode->flag &= ~NODFLG_MARK_A; + uidnode=NULL; + } + else if(interactive) + yesreally=1; } else { uidnode->flag &= ~NODFLG_MARK_A; + uidnode=NULL; tty_printf(_(" Unable to sign.\n")); } } @@ -515,7 +627,9 @@ sign_uids( KBNODE keyblock, STRLIST locusr, int *ret_modified, { tty_printf(_("User ID \"%s\" is expired."),user); - if(opt.expert) + if(selfsig) + tty_printf("\n"); + else if(opt.expert) { tty_printf("\n"); /* No, so remove the mark and continue */ @@ -523,11 +637,17 @@ sign_uids( KBNODE keyblock, STRLIST locusr, int *ret_modified, _("Are you sure you " "still want to sign " "it? (y/N) "))) - uidnode->flag &= ~NODFLG_MARK_A; + { + uidnode->flag &= ~NODFLG_MARK_A; + uidnode=NULL; + } + else if(interactive) + yesreally=1; } else { uidnode->flag &= ~NODFLG_MARK_A; + uidnode=NULL; tty_printf(_(" Unable to sign.\n")); } } @@ -544,17 +664,35 @@ sign_uids( KBNODE keyblock, STRLIST locusr, int *ret_modified, _("Are you sure you " "still want to sign " "it? (y/N) "))) - uidnode->flag &= ~NODFLG_MARK_A; + { + uidnode->flag &= ~NODFLG_MARK_A; + uidnode=NULL; + } + else if(interactive) + yesreally=1; } else { uidnode->flag &= ~NODFLG_MARK_A; + uidnode=NULL; tty_printf(_(" Unable to sign.\n")); } - } - xfree (user); + } + + if(uidnode && interactive && !yesreally) + { + tty_printf(_("User ID \"%s\" is signable. "),user); + if(!cpr_get_answer_is_yes("sign_uid.sign_okay", + _("Sign it? (y/N) "))) + { + uidnode->flag &= ~NODFLG_MARK_A; + uidnode=NULL; + } + } + + xfree(user); } - } + } else if( uidnode && node->pkt->pkttype == PKT_SIGNATURE && (node->pkt->pkt.signature->sig_class&~3) == 0x10 ) { if( sk_keyid[0] == node->pkt->pkt.signature->keyid[0] @@ -582,7 +720,7 @@ sign_uids( KBNODE keyblock, STRLIST locusr, int *ret_modified, { force_v4=1; node->flag|=NODFLG_DELSIG; - xfree (user); + xfree(user); continue; } } @@ -606,7 +744,7 @@ sign_uids( KBNODE keyblock, STRLIST locusr, int *ret_modified, in place. */ node->flag|=NODFLG_DELSIG; - xfree (user); + xfree(user); continue; } } @@ -631,7 +769,7 @@ sign_uids( KBNODE keyblock, STRLIST locusr, int *ret_modified, in place. */ node->flag|=NODFLG_DELSIG; - xfree (user); + xfree(user); continue; } } @@ -640,12 +778,11 @@ sign_uids( KBNODE keyblock, STRLIST locusr, int *ret_modified, * case we should allow to sign it again. */ if (!node->pkt->pkt.signature->flags.exportable && local) tty_printf(_( - "\"%s\" was already locally signed by key %08lX\n"), - user,(ulong)sk_keyid[1] ); + "\"%s\" was already locally signed by key %s\n"), + user,keystr_from_sk(sk)); else - tty_printf(_( - "\"%s\" was already signed by key %08lX\n"), - user,(ulong)sk_keyid[1] ); + tty_printf(_("\"%s\" was already signed by key %s\n"), + user,keystr_from_sk(sk)); if(opt.expert && cpr_get_answer_is_yes("sign_uid.dupe_okay", @@ -654,7 +791,7 @@ sign_uids( KBNODE keyblock, STRLIST locusr, int *ret_modified, { /* Don't delete the old sig here since this is an --expert thing. */ - xfree (user); + xfree(user); continue; } @@ -663,16 +800,18 @@ sign_uids( KBNODE keyblock, STRLIST locusr, int *ret_modified, write_status_text (STATUS_ALREADY_SIGNED, buf); uidnode->flag &= ~NODFLG_MARK_A; /* remove mark */ - xfree (user); + xfree(user); } } } + /* check whether any uids are left for signing */ - if( !count_uids_with_flag(keyblock, NODFLG_MARK_A) ) { - tty_printf(_("Nothing to sign with key %08lX\n"), - (ulong)sk_keyid[1] ); + if( !count_uids_with_flag(keyblock, NODFLG_MARK_A) ) + { + tty_printf(_("Nothing to sign with key %s\n"),keystr_from_sk(sk)); continue; - } + } + /* Ask whether we really should sign these user id(s) */ tty_printf("\n"); show_key_with_all_names( keyblock, 1, 0, 1, 0, 0 ); @@ -702,35 +841,42 @@ sign_uids( KBNODE keyblock, STRLIST locusr, int *ret_modified, } else { - char *answer; - tty_printf(_("This key is due to expire on %s.\n"), expirestr_from_pk(primary_pk)); - answer=cpr_get("sign_uid.expire", - _("Do you want your signature to " - "expire at the same time? (Y/n) ")); - if(answer_is_yes_no_default(answer,1)) + if(opt.ask_cert_expire) { - /* This fixes the signature timestamp we're going - to make as now. This is so the expiration date - is exactly correct, and not a few seconds off - (due to the time it takes to answer the - questions, enter the passphrase, etc). */ - timestamp=now; - duration=primary_pk->expiredate-now; - force_v4=1; - } + char *answer=cpr_get("sign_uid.expire", + _("Do you want your signature to " + "expire at the same time? (Y/n) ")); + if(answer_is_yes_no_default(answer,1)) + { + /* This fixes the signature timestamp we're + going to make as now. This is so the + expiration date is exactly correct, and not + a few seconds off (due to the time it takes + to answer the questions, enter the + passphrase, etc). */ + timestamp=now; + duration=primary_pk->expiredate-now; + force_v4=1; + } - cpr_kill_prompt(); - xfree (answer); + cpr_kill_prompt(); + xfree(answer); + } } } /* Only ask for duration if we haven't already set it to match the expiration of the pk */ - if(opt.ask_cert_expire && !duration && !selfsig) - duration=ask_expire_interval(1); + if(!duration && !selfsig) + { + if(opt.ask_cert_expire) + duration=ask_expire_interval(1,opt.def_cert_expire); + else + duration=parse_expire_string(opt.def_cert_expire); + } if(duration) force_v4=1; @@ -762,8 +908,8 @@ sign_uids( KBNODE keyblock, STRLIST locusr, int *ret_modified, ; else { - if(opt.batch) - class=0x10+opt.def_cert_check_level; + if(opt.batch || !opt.ask_cert_level) + class=0x10+opt.def_cert_level; else { char *answer; @@ -774,22 +920,21 @@ sign_uids( KBNODE keyblock, STRLIST locusr, int *ret_modified, "answer, enter \"0\".\n")); tty_printf("\n"); tty_printf(_(" (0) I will not answer.%s\n"), - opt.def_cert_check_level==0?" (default)":""); + opt.def_cert_level==0?" (default)":""); tty_printf(_(" (1) I have not checked at all.%s\n"), - opt.def_cert_check_level==1?" (default)":""); + opt.def_cert_level==1?" (default)":""); tty_printf(_(" (2) I have done casual checking.%s\n"), - opt.def_cert_check_level==2?" (default)":""); + opt.def_cert_level==2?" (default)":""); tty_printf(_(" (3) I have done very careful checking.%s\n"), - opt.def_cert_check_level==3?" (default)":""); + opt.def_cert_level==3?" (default)":""); tty_printf("\n"); while(class==0) { answer = cpr_get("sign_uid.class",_("Your selection? " - "(enter '?' for more information): ")); - + "(enter `?' for more information): ")); if(answer[0]=='\0') - class=0x10+opt.def_cert_check_level; /* Default */ + class=0x10+opt.def_cert_level; /* Default */ else if(ascii_strcasecmp(answer,"0")==0) class=0x10; /* Generic */ else if(ascii_strcasecmp(answer,"1")==0) @@ -801,7 +946,7 @@ sign_uids( KBNODE keyblock, STRLIST locusr, int *ret_modified, else tty_printf(_("Invalid selection.\n")); - xfree (answer); + xfree(answer); } } @@ -809,49 +954,63 @@ sign_uids( KBNODE keyblock, STRLIST locusr, int *ret_modified, trustsig_prompt(&trust_value,&trust_depth,&trust_regexp); } - tty_printf(_("Are you really sure that you want to sign this key\n" - "with your key: \"")); - p = get_user_id( sk_keyid, &n ); - tty_print_utf8_string( p, n ); - xfree (p); p = NULL; - tty_printf("\" (%08lX)\n",(ulong)sk_keyid[1]); + p=get_user_id_native(sk_keyid); + tty_printf(_("Are you sure that you want to sign this key with your\n" + "key \"%s\" (%s)\n"),p,keystr_from_sk(sk)); + xfree(p); if(selfsig) { - tty_printf(_("\nThis will be a self-signature.\n")); + tty_printf("\n"); + tty_printf(_("This will be a self-signature.\n")); if( local ) - tty_printf( - _("\nWARNING: the signature will not be marked " + { + tty_printf("\n"); + tty_printf( + _("WARNING: the signature will not be marked " "as non-exportable.\n")); + } if( nonrevocable ) - tty_printf( - _("\nWARNING: the signature will not be marked " + { + tty_printf("\n"); + tty_printf( + _("WARNING: the signature will not be marked " "as non-revocable.\n")); + } } else { if( local ) - tty_printf( - _("\nThe signature will be marked as non-exportable.\n")); + { + tty_printf("\n"); + tty_printf( + _("The signature will be marked as non-exportable.\n")); + } if( nonrevocable ) - tty_printf( - _("\nThe signature will be marked as non-revocable.\n")); + { + tty_printf("\n"); + tty_printf( + _("The signature will be marked as non-revocable.\n")); + } switch(class) { case 0x11: - tty_printf(_("\nI have not checked this key at all.\n")); + tty_printf("\n"); + tty_printf(_("I have not checked this key at all.\n")); break; case 0x12: - tty_printf(_("\nI have checked this key casually.\n")); + tty_printf("\n"); + tty_printf(_("I have checked this key casually.\n")); break; case 0x13: - tty_printf(_("\nI have checked this key very carefully.\n")); + tty_printf("\n"); + tty_printf(_("I have checked this key very carefully.\n")); break; } } @@ -860,7 +1019,8 @@ sign_uids( KBNODE keyblock, STRLIST locusr, int *ret_modified, if( opt.batch && opt.answer_yes ) ; - else if( !cpr_get_answer_is_yes("sign_uid.okay", _("Really sign? ")) ) + else if( !cpr_get_answer_is_yes("sign_uid.okay", + _("Really sign? (y/N) ")) ) continue; /* now we can sign the user ids */ @@ -905,14 +1065,14 @@ sign_uids( KBNODE keyblock, STRLIST locusr, int *ret_modified, timestamp, duration, sign_mk_attrib, &attrib ); if( rc ) { - log_error(_("signing failed: %s\n"), gpg_strerror (rc)); + log_error(_("signing failed: %s\n"), g10_errstr(rc)); goto leave; } *ret_modified = 1; /* we changed the keyblock */ update_trust = 1; - pkt = xcalloc (1, sizeof *pkt ); + pkt = xmalloc_clear( sizeof *pkt ); pkt->pkttype = PKT_SIGNATURE; pkt->pkt.signature = sig; insert_kbnode( node, new_kbnode(pkt), PKT_SIGNATURE ); @@ -948,6 +1108,7 @@ change_passphrase( KBNODE keyblock ) PKT_secret_key *sk; char *passphrase = NULL; int no_primary_secrets = 0; + int any; node = find_kbnode( keyblock, PKT_SECRET_KEY ); if( !node ) { @@ -956,9 +1117,28 @@ change_passphrase( KBNODE keyblock ) } sk = node->pkt->pkt.secret_key; + for (any = 0, node=keyblock; node; node = node->next) { + if (node->pkt->pkttype == PKT_SECRET_KEY + || node->pkt->pkttype == PKT_SECRET_SUBKEY) { + PKT_secret_key *tmpsk = node->pkt->pkt.secret_key; + if (!(tmpsk->is_protected + && (tmpsk->protect.s2k.mode == 1001 + || tmpsk->protect.s2k.mode == 1002))) { + any = 1; + break; + } + } + } + if (!any) { + tty_printf (_("Key has only stub or on-card key items - " + "no passphrase to change.\n")); + goto leave; + } + + /* See how to handle this key. */ switch( is_secret_key_protected( sk ) ) { case -1: - rc = GPG_ERR_PUBKEY_ALGO; + rc = G10ERR_PUBKEY_ALGO; break; case 0: tty_printf(_("This key is not protected.\n")); @@ -969,8 +1149,8 @@ change_passphrase( KBNODE keyblock ) no_primary_secrets = 1; } else if( sk->protect.s2k.mode == 1002 ) { - tty_printf(_("Secret key is actually stored on a card.\n")); - goto leave; + tty_printf(_("Secret parts of primary key are stored on-card.\n")); + no_primary_secrets = 1; } else { tty_printf(_("Key is protected.\n")); @@ -981,22 +1161,26 @@ change_passphrase( KBNODE keyblock ) break; } - /* unprotect all subkeys (use the supplied passphrase or ask)*/ + /* Unprotect all subkeys (use the supplied passphrase or ask)*/ for(node=keyblock; !rc && node; node = node->next ) { if( node->pkt->pkttype == PKT_SECRET_SUBKEY ) { PKT_secret_key *subsk = node->pkt->pkt.secret_key; - set_next_passphrase( passphrase ); - rc = check_secret_key( subsk, 0 ); - if( !rc && !passphrase ) - passphrase = get_last_passphrase(); + if ( !(subsk->is_protected + && (subsk->protect.s2k.mode == 1001 + || subsk->protect.s2k.mode == 1002))) { + set_next_passphrase( passphrase ); + rc = check_secret_key( subsk, 0 ); + if( !rc && !passphrase ) + passphrase = get_last_passphrase(); + } } } if( rc ) - tty_printf(_("Can't edit this key: %s\n"), gpg_strerror (rc)); + tty_printf(_("Can't edit this key: %s\n"), g10_errstr(rc)); else { DEK *dek = NULL; - STRING2KEY *s2k = xmalloc_secure ( sizeof *s2k ); + STRING2KEY *s2k = xmalloc_secure( sizeof *s2k ); const char *errtext = NULL; tty_printf(_("Enter the new passphrase for this secret key.\n\n") ); @@ -1004,7 +1188,7 @@ change_passphrase( KBNODE keyblock ) set_next_passphrase( NULL ); for(;;) { s2k->mode = opt.s2k_mode; - s2k->hash_algo = opt.s2k_digest_algo; + s2k->hash_algo = S2K_DIGEST_ALGO; dek = passphrase_to_dek( NULL, 0, opt.s2k_cipher_algo, s2k, 2, errtext, NULL); if( !dek ) { @@ -1016,11 +1200,11 @@ change_passphrase( KBNODE keyblock ) tty_printf(_( "You don't want a passphrase -" " this is probably a *bad* idea!\n\n")); if( cpr_get_answer_is_yes("change_passwd.empty.okay", - _("Do you really want to do this? "))) - { + _("Do you really want to do this? (y/N) "))) + { changed++; - break; - } + break; + } } else { /* okay */ rc = 0; @@ -1032,24 +1216,29 @@ change_passphrase( KBNODE keyblock ) for(node=keyblock; !rc && node; node = node->next ) { if( node->pkt->pkttype == PKT_SECRET_SUBKEY ) { PKT_secret_key *subsk = node->pkt->pkt.secret_key; - subsk->protect.algo = dek->algo; - subsk->protect.s2k = *s2k; - rc = protect_secret_key( subsk, dek ); + if ( !(subsk->is_protected + && (subsk->protect.s2k.mode == 1001 + || subsk->protect.s2k.mode == 1002))) { + subsk->protect.algo = dek->algo; + subsk->protect.s2k = *s2k; + rc = protect_secret_key( subsk, dek ); + } } } if( rc ) - log_error("protect_secret_key failed: %s\n", gpg_strerror (rc) ); + log_error("protect_secret_key failed: %s\n", + g10_errstr(rc) ); else changed++; break; } } - xfree (s2k); - xfree (dek); + xfree(s2k); + xfree(dek); } leave: - xfree ( passphrase ); + xfree( passphrase ); set_next_passphrase( NULL ); return changed && !rc; } @@ -1098,84 +1287,223 @@ fix_keyblock( KBNODE keyblock ) return fixed; } +static int +parse_sign_type(const char *str,int *localsig,int *nonrevokesig,int *trustsig) +{ + const char *p=str; + + while(*p) + { + if(ascii_strncasecmp(p,"l",1)==0) + { + *localsig=1; + p++; + } + else if(ascii_strncasecmp(p,"nr",2)==0) + { + *nonrevokesig=1; + p+=2; + } + else if(ascii_strncasecmp(p,"t",1)==0) + { + *trustsig=1; + p++; + } + else + return 0; + } + + return 1; +} + + /**************** - * Menu driven key editor. If sign_mode is true semi-automatical signing - * will be performed. commands are ignore in this case + * Menu driven key editor. If seckey_check is true, then a secret key + * that matches username will be looked for. If it is false, not all + * commands will be available. * * Note: to keep track of some selection we use node->mark MARKBIT_xxxx. */ +/* Need an SK for this command */ +#define KEYEDIT_NEED_SK 1 +/* Cannot be viewing the SK for this command */ +#define KEYEDIT_NOT_SK 2 +/* Must be viewing the SK for this command */ +#define KEYEDIT_ONLY_SK 4 +/* Match the tail of the string */ +#define KEYEDIT_TAIL_MATCH 8 + +enum cmdids + { + cmdNONE = 0, + cmdQUIT, cmdHELP, cmdFPR, cmdLIST, cmdSELUID, cmdCHECK, cmdSIGN, + cmdREVSIG, cmdREVKEY, cmdREVUID, cmdDELSIG, cmdPRIMARY, cmdDEBUG, + cmdSAVE, cmdADDUID, cmdADDPHOTO, cmdDELUID, cmdADDKEY, cmdDELKEY, + cmdADDREVOKER, cmdTOGGLE, cmdSELKEY, cmdPASSWD, cmdTRUST, cmdPREF, + cmdEXPIRE, cmdBACKSIGN, cmdENABLEKEY, cmdDISABLEKEY, cmdSHOWPREF, + cmdSETPREF, cmdPREFKS, cmdNOTATION, cmdINVCMD, cmdSHOWPHOTO, cmdUPDTRUST, + cmdCHKTRUST, cmdADDCARDKEY, cmdKEYTOCARD, cmdBKUPTOCARD, cmdCLEAN, + cmdMINIMIZE, cmdNOP + }; + +static struct +{ + const char *name; + enum cmdids id; + int flags; + const char *desc; +} cmds[] = + { + { "quit" , cmdQUIT , 0, N_("quit this menu") }, + { "q" , cmdQUIT , 0, NULL }, + { "save" , cmdSAVE , 0, N_("save and quit") }, + { "help" , cmdHELP , 0, N_("show this help") }, + { "?" , cmdHELP , 0, NULL }, + { "fpr" , cmdFPR , 0, N_("show key fingerprint") }, + { "list" , cmdLIST , 0, N_("list key and user IDs") }, + { "l" , cmdLIST , 0, NULL }, + { "uid" , cmdSELUID , 0, N_("select user ID N") }, + { "key" , cmdSELKEY , 0, N_("select subkey N") }, + { "check" , cmdCHECK , 0, N_("check signatures") }, + { "c" , cmdCHECK , 0, NULL }, + { "cross-certify", cmdBACKSIGN , KEYEDIT_NOT_SK|KEYEDIT_NEED_SK, NULL }, + { "backsign", cmdBACKSIGN , KEYEDIT_NOT_SK|KEYEDIT_NEED_SK, NULL }, + { "sign" , cmdSIGN , KEYEDIT_NOT_SK|KEYEDIT_TAIL_MATCH, + N_("sign selected user IDs [* see below for related commands]") }, + { "s" , cmdSIGN , KEYEDIT_NOT_SK, NULL }, + /* "lsign" and friends will never match since "sign" comes first + and it is a tail match. They are just here so they show up in + the help menu. */ + { "lsign" , cmdNOP , 0, N_("sign selected user IDs locally") }, + { "tsign" , cmdNOP , 0, + N_("sign selected user IDs with a trust signature") }, + { "nrsign" , cmdNOP , 0, + N_("sign selected user IDs with a non-revocable signature") }, + + { "debug" , cmdDEBUG , 0, NULL }, + { "adduid" , cmdADDUID , KEYEDIT_NOT_SK|KEYEDIT_NEED_SK, + N_("add a user ID") }, + { "addphoto", cmdADDPHOTO , KEYEDIT_NOT_SK|KEYEDIT_NEED_SK, + N_("add a photo ID") }, + { "deluid" , cmdDELUID , KEYEDIT_NOT_SK, + N_("delete selected user IDs") }, + /* delphoto is really deluid in disguise */ + { "delphoto", cmdDELUID , KEYEDIT_NOT_SK, NULL }, + + { "addkey" , cmdADDKEY , KEYEDIT_NOT_SK|KEYEDIT_NEED_SK, + N_("add a subkey") }, + +#ifdef ENABLE_CARD_SUPPORT + { "addcardkey", cmdADDCARDKEY , KEYEDIT_NOT_SK|KEYEDIT_NEED_SK, + N_("add a key to a smartcard") }, + { "keytocard", cmdKEYTOCARD , KEYEDIT_NEED_SK|KEYEDIT_ONLY_SK, + N_("move a key to a smartcard")}, + { "bkuptocard", cmdBKUPTOCARD , KEYEDIT_NEED_SK|KEYEDIT_ONLY_SK, + N_("move a backup key to a smartcard")}, +#endif /*ENABLE_CARD_SUPPORT*/ + + { "delkey" , cmdDELKEY , KEYEDIT_NOT_SK, + N_("delete selected subkeys") }, + { "addrevoker",cmdADDREVOKER,KEYEDIT_NOT_SK|KEYEDIT_NEED_SK, + N_("add a revocation key") }, + { "delsig" , cmdDELSIG , KEYEDIT_NOT_SK, + N_("delete signatures from the selected user IDs") }, + { "expire" , cmdEXPIRE , KEYEDIT_NOT_SK|KEYEDIT_NEED_SK, + N_("change the expiration date for the key or selected subkeys") }, + { "primary" , cmdPRIMARY , KEYEDIT_NOT_SK|KEYEDIT_NEED_SK, + N_("flag the selected user ID as primary")}, + { "toggle" , cmdTOGGLE , KEYEDIT_NEED_SK, + N_("toggle between the secret and public key listings") }, + { "t" , cmdTOGGLE , KEYEDIT_NEED_SK, NULL }, + { "pref" , cmdPREF , KEYEDIT_NOT_SK, + N_("list preferences (expert)")}, + { "showpref", cmdSHOWPREF , KEYEDIT_NOT_SK, + N_("list preferences (verbose)") }, + { "setpref" , cmdSETPREF , KEYEDIT_NOT_SK|KEYEDIT_NEED_SK, + N_("set preference list for the selected user IDs") }, + /* Alias */ + { "updpref" , cmdSETPREF , KEYEDIT_NOT_SK|KEYEDIT_NEED_SK, NULL }, + + { "keyserver",cmdPREFKS , KEYEDIT_NOT_SK|KEYEDIT_NEED_SK, + N_("set the preferred keyserver URL for the selected user IDs")}, + { "notation", cmdNOTATION , KEYEDIT_NOT_SK|KEYEDIT_NEED_SK, + N_("set a notation for the selected user IDs")}, + { "passwd" , cmdPASSWD , KEYEDIT_NOT_SK|KEYEDIT_NEED_SK, + N_("change the passphrase") }, + /* Alias */ + { "password", cmdPASSWD , KEYEDIT_NOT_SK|KEYEDIT_NEED_SK, NULL }, + + { "trust" , cmdTRUST , KEYEDIT_NOT_SK, N_("change the ownertrust") }, + { "revsig" , cmdREVSIG , KEYEDIT_NOT_SK, + N_("revoke signatures on the selected user IDs") }, + { "revuid" , cmdREVUID , KEYEDIT_NOT_SK|KEYEDIT_NEED_SK, + N_("revoke selected user IDs") }, + /* Alias */ + { "revphoto", cmdREVUID , KEYEDIT_NOT_SK|KEYEDIT_NEED_SK, NULL }, + + { "revkey" , cmdREVKEY , KEYEDIT_NOT_SK|KEYEDIT_NEED_SK, + N_("revoke key or selected subkeys") }, + { "enable" , cmdENABLEKEY , KEYEDIT_NOT_SK, N_("enable key") }, + { "disable" , cmdDISABLEKEY, KEYEDIT_NOT_SK, N_("disable key") }, + { "showphoto",cmdSHOWPHOTO , 0, N_("show selected photo IDs") }, + { "clean", cmdCLEAN , KEYEDIT_NOT_SK, + N_("compact unusable user IDs and remove unusable signatures from key")}, + { "minimize", cmdMINIMIZE , KEYEDIT_NOT_SK, + N_("compact unusable user IDs and remove all signatures from key") }, + { NULL, cmdNONE, 0, NULL } + }; + +#ifdef HAVE_LIBREADLINE + +/* These two functions are used by readline for command completion. */ + +static char * +command_generator(const char *text,int state) +{ + static int list_index,len; + const char *name; + + /* If this is a new word to complete, initialize now. This includes + saving the length of TEXT for efficiency, and initializing the + index variable to 0. */ + if(!state) + { + list_index=0; + len=strlen(text); + } + + /* Return the next partial match */ + while((name=cmds[list_index].name)) + { + /* Only complete commands that have help text */ + if(cmds[list_index++].desc && strncmp(name,text,len)==0) + return strdup(name); + } + + return NULL; +} + +static char ** +keyedit_completion(const char *text, int start, int end) +{ + /* If we are at the start of a line, we try and command-complete. + If not, just do nothing for now. */ + + if(start==0) + return rl_completion_matches(text,command_generator); + + rl_attempted_completion_over=1; + + return NULL; +} +#endif /* HAVE_LIBREADLINE */ + + void -keyedit_menu( const char *username, STRLIST locusr, STRLIST commands, - int sign_mode ) +keyedit_menu( const char *username, STRLIST locusr, + STRLIST commands, int quiet, int seckey_check ) { - enum cmdids { cmdNONE = 0, - cmdQUIT, cmdHELP, cmdFPR, cmdLIST, cmdSELUID, cmdCHECK, cmdSIGN, - cmdTSIGN, cmdLSIGN, cmdNRSIGN, cmdNRLSIGN, cmdREVSIG, cmdREVKEY, - cmdREVUID, cmdDELSIG, cmdPRIMARY, cmdDEBUG, cmdSAVE, cmdADDUID, - cmdADDPHOTO, cmdDELUID, cmdADDKEY, cmdDELKEY, cmdADDREVOKER, - cmdTOGGLE, cmdSELKEY, cmdPASSWD, cmdTRUST, cmdPREF, cmdEXPIRE, - cmdENABLEKEY, cmdDISABLEKEY, cmdSHOWPREF, cmdSETPREF, cmdUPDPREF, - cmdPREFKS, cmdINVCMD, cmdSHOWPHOTO, cmdUPDTRUST, cmdCHKTRUST, - cmdNOP }; - static struct { const char *name; - enum cmdids id; - int need_sk; - int not_with_sk; - int signmode; - const char *desc; - } cmds[] = { - { N_("quit") , cmdQUIT , 0,0,1, N_("quit this menu") }, - { N_("q") , cmdQUIT , 0,0,1, NULL }, - { N_("save") , cmdSAVE , 0,0,1, N_("save and quit") }, - { N_("help") , cmdHELP , 0,0,1, N_("show this help") }, - { "?" , cmdHELP , 0,0,1, NULL }, - { N_("fpr") , cmdFPR , 0,0,1, N_("show fingerprint") }, - { N_("list") , cmdLIST , 0,0,1, N_("list key and user IDs") }, - { N_("l") , cmdLIST , 0,0,1, NULL }, - { N_("uid") , cmdSELUID , 0,0,1, N_("select user ID N") }, - { N_("key") , cmdSELKEY , 0,0,0, N_("select secondary key N") }, - { N_("check") , cmdCHECK , 0,0,1, N_("list signatures") }, - { N_("c") , cmdCHECK , 0,0,1, NULL }, - { N_("sign") , cmdSIGN , 0,1,1, N_("sign the key") }, - { N_("s") , cmdSIGN , 0,1,1, NULL }, - { N_("tsign") , cmdTSIGN , 0,1,1, N_("make a trust signature")}, - { N_("lsign") , cmdLSIGN , 0,1,1, N_("sign the key locally") }, - { N_("nrsign") , cmdNRSIGN , 0,1,1, N_("sign the key non-revocably") }, - { N_("nrlsign") , cmdNRLSIGN , 0,1,1, N_("sign the key locally and non-revocably") }, - { N_("debug") , cmdDEBUG , 0,0,0, NULL }, - { N_("adduid") , cmdADDUID , 1,1,0, N_("add a user ID") }, - { N_("addphoto"), cmdADDPHOTO , 1,1,0, N_("add a photo ID") }, - { N_("deluid") , cmdDELUID , 0,1,0, N_("delete user ID") }, - /* delphoto is really deluid in disguise */ - { N_("delphoto"), cmdDELUID , 0,1,0, NULL }, - { N_("addkey") , cmdADDKEY , 1,1,0, N_("add a secondary key") }, - { N_("delkey") , cmdDELKEY , 0,1,0, N_("delete a secondary key") }, - { N_("addrevoker"),cmdADDREVOKER,1,1,0, N_("add a revocation key") }, - { N_("delsig") , cmdDELSIG , 0,1,0, N_("delete signatures") }, - { N_("expire") , cmdEXPIRE , 1,1,0, N_("change the expire date") }, - { N_("primary") , cmdPRIMARY , 1,1,0, N_("flag user ID as primary")}, - { N_("toggle") , cmdTOGGLE , 1,0,0, N_("toggle between secret " - "and public key listing") }, - { N_("t" ) , cmdTOGGLE , 1,0,0, NULL }, - { N_("pref") , cmdPREF , 0,1,0, - N_("list preferences (expert)")}, - { N_("showpref"), cmdSHOWPREF , 0,1,0, - N_("list preferences (verbose)")}, - { N_("setpref") , cmdSETPREF , 1,1,0, N_("set preference list") }, - { N_("updpref") , cmdUPDPREF , 1,1,0, N_("updated preferences") }, - { N_("keyserver"),cmdPREFKS , 1,1,0, - N_("set preferred keyserver URL")}, - { N_("passwd") , cmdPASSWD , 1,1,0, N_("change the passphrase") }, - { N_("trust") , cmdTRUST , 0,1,0, N_("change the ownertrust") }, - { N_("revsig") , cmdREVSIG , 0,1,0, N_("revoke signatures") }, - { N_("revuid") , cmdREVUID , 1,1,0, N_("revoke a user ID") }, - { N_("revkey") , cmdREVKEY , 1,1,0, N_("revoke a secondary key") }, - { N_("disable") , cmdDISABLEKEY, 0,1,0, N_("disable a key") }, - { N_("enable") , cmdENABLEKEY , 0,1,0, N_("enable a key") }, - { N_("showphoto"),cmdSHOWPHOTO , 0,0,0, N_("show photo ID") }, - - { NULL, cmdNONE } }; enum cmdids cmd = 0; int rc = 0; KBNODE keyblock = NULL; @@ -1192,20 +1520,26 @@ keyedit_menu( const char *username, STRLIST locusr, STRLIST commands, if ( opt.command_fd != -1 ) ; - else if( opt.batch && !have_commands ) { - log_error(_("can't do that in batchmode\n")); + else if( opt.batch && !have_commands ) + { + log_error(_("can't do this in batch mode\n")); goto leave; - } - - if( sign_mode ) { - commands = NULL; - append_to_strlist( &commands, sign_mode == 1? "sign": - sign_mode == 2?"lsign": - sign_mode == 3?"nrsign":"nrlsign"); - have_commands = 1; - } + } - /* get the public key */ +#ifdef HAVE_W32_SYSTEM + /* Due to Windows peculiarities we need to make sure that the + trustdb stale check is done before we open another file + (i.e. by searching for a key). In theory we could make sure + that the files are closed after use but the open/close caches + inhibits that and flushing the cache right before the stale + check is not easy to implement. Thus we take the easy way out + and run the stale check as early as possible. Note, that for + non- W32 platforms it is run indirectly trough a call to + get_validity (). */ + check_trustdb_stale (); +#endif + + /* Get the public key */ rc = get_pubkey_byname (NULL, username, &keyblock, &kdbhd, 1); if( rc ) goto leave; @@ -1215,7 +1549,8 @@ keyedit_menu( const char *username, STRLIST locusr, STRLIST commands, modified++; reorder_keyblock(keyblock); - if( !sign_mode ) {/* see whether we have a matching secret key */ + if(seckey_check) + {/* see whether we have a matching secret key */ PKT_public_key *pk = keyblock->pkt->pkt.public_key; sec_kdbhd = keydb_new (1); @@ -1228,28 +1563,30 @@ keyedit_menu( const char *username, STRLIST locusr, STRLIST commands, afp[an++] = 0; rc = keydb_search_fpr (sec_kdbhd, afp); } - if (!rc) { + if (!rc) + { rc = keydb_get_keyblock (sec_kdbhd, &sec_keyblock); - if (rc) { - log_error (_("error reading secret keyblock `%s': %s\n"), - username, gpg_strerror (rc)); - } - else { + if (rc) + { + log_error (_("error reading secret keyblock \"%s\": %s\n"), + username, g10_errstr(rc)); + } + else + { merge_keys_and_selfsig( sec_keyblock ); if( fix_keyblock( sec_keyblock ) ) - sec_modified++; - } - } + sec_modified++; + } + } if (rc) { sec_keyblock = NULL; keydb_release (sec_kdbhd); sec_kdbhd = NULL; rc = 0; } - } - if( sec_keyblock ) { - tty_printf(_("Secret key is available.\n")); + if( sec_keyblock && !quiet ) + tty_printf(_("Secret key is available.\n")); } toggle = 0; @@ -1261,28 +1598,33 @@ keyedit_menu( const char *username, STRLIST locusr, STRLIST commands, PKT_public_key *pk=keyblock->pkt->pkt.public_key; tty_printf("\n"); - if( redisplay ) { + + if( redisplay && !quiet ) + { show_key_with_all_names( cur_keyblock, 0, 1, 0, 1, 0 ); tty_printf("\n"); redisplay = 0; - } + } do { - xfree (answer); + xfree(answer); if( have_commands ) { if( commands ) { - answer = xstrdup ( commands->d ); + answer = xstrdup( commands->d ); commands = commands->next; } else if( opt.batch ) { - answer = xstrdup ("quit"); + answer = xstrdup("quit"); } else have_commands = 0; } - if( !have_commands ) { + if( !have_commands ) + { + tty_enable_completion(keyedit_completion); answer = cpr_get_no_help("keyedit.prompt", _("Command> ")); cpr_kill_prompt(); - } + tty_disable_completion(); + } trim_spaces(answer); } while( *answer == '#' ); @@ -1292,7 +1634,7 @@ keyedit_menu( const char *username, STRLIST locusr, STRLIST commands, cmd = cmdLIST; else if( *answer == CONTROL_D ) cmd = cmdQUIT; - else if( digitp( answer ) ) { + else if( digitp(answer ) ) { cmd = cmdSELUID; arg_number = atoi(answer); } @@ -1305,33 +1647,58 @@ keyedit_menu( const char *username, STRLIST locusr, STRLIST commands, arg_string = p; } - for(i=0; cmds[i].name; i++ ) { - if( !ascii_strcasecmp( answer, cmds[i].name ) ) - break; - } - if( sign_mode && !cmds[i].signmode ) - cmd = cmdINVCMD; - else if( cmds[i].need_sk && !sec_keyblock ) { + for(i=0; cmds[i].name; i++ ) + { + if(cmds[i].flags & KEYEDIT_TAIL_MATCH) + { + size_t l=strlen(cmds[i].name); + size_t a=strlen(answer); + if(a>=l) + { + if(ascii_strcasecmp(&answer[a-l],cmds[i].name)==0) + { + answer[a-l]='\0'; + break; + } + } + } + else if( !ascii_strcasecmp( answer, cmds[i].name ) ) + break; + } + if((cmds[i].flags & KEYEDIT_NEED_SK) && !sec_keyblock ) + { tty_printf(_("Need the secret key to do this.\n")); cmd = cmdNOP; - } - else if( cmds[i].not_with_sk && sec_keyblock && toggle ) { + } + else if(((cmds[i].flags & KEYEDIT_NOT_SK) && sec_keyblock + && toggle) + ||((cmds[i].flags & KEYEDIT_ONLY_SK) && sec_keyblock + && !toggle)) + { tty_printf(_("Please use the command \"toggle\" first.\n")); cmd = cmdNOP; - } + } else - cmd = cmds[i].id; + cmd = cmds[i].id; } - switch( cmd ) { + switch( cmd ) + { case cmdHELP: - for(i=0; cmds[i].name; i++ ) { - if( sign_mode && !cmds[i].signmode ) - ; - else if( cmds[i].need_sk && !sec_keyblock ) - ; /* skip if we do not have the secret key */ + for(i=0; cmds[i].name; i++ ) + { + if((cmds[i].flags & KEYEDIT_NEED_SK) && !sec_keyblock ) + ; /* skip if we do not have the secret key */ else if( cmds[i].desc ) - tty_printf("%-10s %s\n", cmds[i].name, _(cmds[i].desc) ); - } + tty_printf("%-11s %s\n", cmds[i].name, _(cmds[i].desc) ); + } + + tty_printf("\n"); + tty_printf(_( +"* The `sign' command may be prefixed with an `l' for local " +"signatures (lsign),\n" +" a `t' for trust signatures (tsign), an `nr' for non-revocable signatures\n" +" (nrsign), or any combination thereof (ltsign, tnrsign, etc.).\n")); + break; case cmdLIST: @@ -1343,8 +1710,10 @@ keyedit_menu( const char *username, STRLIST locusr, STRLIST commands, break; case cmdSELUID: - if( menu_select_uid( cur_keyblock, arg_number ) ) - redisplay = 1; + if(strlen(arg_string)==NAMEHASH_LEN*2) + redisplay=menu_select_uid_namehash(cur_keyblock,arg_string); + else + redisplay=menu_select_uid(cur_keyblock,arg_number); break; case cmdSELKEY: @@ -1360,42 +1729,53 @@ keyedit_menu( const char *username, STRLIST locusr, STRLIST commands, break; case cmdSIGN: /* sign (only the public key) */ - case cmdLSIGN: /* sign (only the public key) */ - case cmdNRSIGN: /* sign (only the public key) */ - case cmdNRLSIGN: /* sign (only the public key) */ - case cmdTSIGN: - if( pk->is_revoked ) - { - tty_printf(_("Key is revoked.")); + { + int localsig=0,nonrevokesig=0,trustsig=0,interactive=0; - if(opt.expert) - { - tty_printf(" "); - if(!cpr_get_answer_is_yes("keyedit.sign_revoked.okay", - _("Are you sure you still want " - "to sign it? (y/N) "))) + if( pk->is_revoked ) + { + tty_printf(_("Key is revoked.")); + + if(opt.expert) + { + tty_printf(" "); + if(!cpr_get_answer_is_yes("keyedit.sign_revoked.okay", + _("Are you sure you still want" + " to sign it? (y/N) "))) + break; + } + else + { + tty_printf(_(" Unable to sign.\n")); break; - } - else - { - tty_printf(_(" Unable to sign.\n")); - break; - } - } + } + } - if( count_uids(keyblock) > 1 && !count_selected_uids(keyblock) ) { - if( !cpr_get_answer_is_yes("keyedit.sign_all.okay", - _("Really sign all user IDs? ")) ) { - tty_printf(_("Hint: Select the user IDs to sign\n")); - break; + if(count_uids(keyblock) > 1 && !count_selected_uids(keyblock) + && !cpr_get_answer_is_yes("keyedit.sign_all.okay", + _("Really sign all user IDs?" + " (y/N) "))) + { + if(opt.interactive) + interactive=1; + else + { + tty_printf(_("Hint: Select the user IDs to sign\n")); + have_commands = 0; + break; + } + + } + /* What sort of signing are we doing? */ + if(!parse_sign_type(answer,&localsig,&nonrevokesig,&trustsig)) + { + tty_printf(_("Unknown signature type `%s'\n"),answer); + break; } + + sign_uids(keyblock, locusr, &modified, + localsig, nonrevokesig, trustsig, interactive); } - if( !sign_uids( keyblock, locusr, &modified, - (cmd == cmdLSIGN) || (cmd == cmdNRLSIGN), - (cmd == cmdNRSIGN) || (cmd==cmdNRLSIGN), - (cmd == cmdTSIGN)) - && sign_mode ) - goto do_cmd_save; break; case cmdDEBUG: @@ -1420,12 +1800,14 @@ keyedit_menu( const char *username, STRLIST locusr, STRLIST commands, /* fall through */ case cmdADDUID: - if( menu_adduid( keyblock, sec_keyblock, photo ) ) { + if( menu_adduid( keyblock, sec_keyblock, photo, arg_string ) ) + { + update_trust = 1; redisplay = 1; sec_modified = modified = 1; merge_keys_and_selfsig( sec_keyblock ); merge_keys_and_selfsig( keyblock ); - } + } break; case cmdDELUID: { @@ -1435,10 +1817,9 @@ keyedit_menu( const char *username, STRLIST locusr, STRLIST commands, tty_printf(_("You must select at least one user ID.\n")); else if( real_uids_left(keyblock) < 1 ) tty_printf(_("You can't delete the last user ID!\n")); - else if( cpr_get_answer_is_yes( - "keyedit.remove.uid.okay", - n1 > 1? _("Really remove all selected user IDs? ") - : _("Really remove this user ID? ") + else if( cpr_get_answer_is_yes("keyedit.remove.uid.okay", + n1 > 1? _("Really remove all selected user IDs? (y/N) ") + : _("Really remove this user ID? (y/N) ") ) ) { menu_deluid( keyblock, sec_keyblock ); redisplay = 1; @@ -1471,17 +1852,123 @@ keyedit_menu( const char *username, STRLIST locusr, STRLIST commands, } break; +#ifdef ENABLE_CARD_SUPPORT + case cmdADDCARDKEY: + if (card_generate_subkey (keyblock, sec_keyblock)) { + redisplay = 1; + sec_modified = modified = 1; + merge_keys_and_selfsig( sec_keyblock ); + merge_keys_and_selfsig( keyblock ); + } + break; + + case cmdKEYTOCARD: + { + KBNODE node=NULL; + switch ( count_selected_keys (sec_keyblock) ) + { + case 0: + if (cpr_get_answer_is_yes("keyedit.keytocard.use_primary", + _("Really move the primary key? (y/N) "))) + node = sec_keyblock; + break; + case 1: + for (node = sec_keyblock; node; node = node->next ) + { + if (node->pkt->pkttype == PKT_SECRET_SUBKEY + && node->flag & NODFLG_SELKEY) + break; + } + break; + default: + tty_printf(_("You must select exactly one key.\n")); + break; + } + if (node) + { + PKT_public_key *xxpk = find_pk_from_sknode (keyblock, node); + if (card_store_subkey (node, xxpk?xxpk->pubkey_usage:0)) + { + redisplay = 1; + sec_modified = 1; + } + } + } + break; + + case cmdBKUPTOCARD: + { + /* Ask for a filename, check whether this is really a + backup key as generated by the card generation, parse + that key and store it on card. */ + KBNODE node; + const char *fname; + PACKET *pkt; + IOBUF a; + + fname = arg_string; + if (!*fname) + { + tty_printf (_("Command expects a filename argument\n")); + break; + } + + /* Open that file. */ + a = iobuf_open (fname); + if (a && is_secured_file (iobuf_get_fd (a))) + { + iobuf_close (a); + a = NULL; + errno = EPERM; + } + if (!a) + { + tty_printf (_("Can't open `%s': %s\n"), + fname, strerror(errno)); + break; + } + + /* Parse and check that file. */ + pkt = xmalloc (sizeof *pkt); + init_packet (pkt); + rc = parse_packet (a, pkt); + iobuf_close (a); + iobuf_ioctl (NULL, 2, 0, (char*)fname); /* (invalidate cache). */ + if (!rc + && pkt->pkttype != PKT_SECRET_KEY + && pkt->pkttype != PKT_SECRET_SUBKEY) + rc = G10ERR_NO_SECKEY; + if (rc) + { + tty_printf(_("Error reading backup key from `%s': %s\n"), + fname, g10_errstr (rc)); + free_packet (pkt); + xfree (pkt); + break; + } + node = new_kbnode (pkt); + + /* Store it. */ + if (card_store_subkey (node, 0)) + { + redisplay = 1; + sec_modified = 1; + } + release_kbnode (node); + } + break; + +#endif /* ENABLE_CARD_SUPPORT */ case cmdDELKEY: { int n1; if( !(n1=count_selected_keys( keyblock )) ) tty_printf(_("You must select at least one key.\n")); - else if( sec_keyblock && !cpr_get_answer_is_yes( - "keyedit.remove.subkey.okay", + else if( !cpr_get_answer_is_yes( "keyedit.remove.subkey.okay", n1 > 1? - _("Do you really want to delete the selected keys? "): - _("Do you really want to delete this key? ") + _("Do you really want to delete the selected keys? (y/N) "): + _("Do you really want to delete this key? (y/N) ") )) ; else { @@ -1498,7 +1985,7 @@ keyedit_menu( const char *username, STRLIST locusr, STRLIST commands, { int sensitive=0; - if(arg_string && ascii_strcasecmp(arg_string,"sensitive")==0) + if(ascii_strcasecmp(arg_string,"sensitive")==0) sensitive=1; if( menu_addrevoker( keyblock, sec_keyblock, sensitive ) ) { redisplay = 1; @@ -1516,8 +2003,8 @@ keyedit_menu( const char *username, STRLIST locusr, STRLIST commands, tty_printf(_("You must select at least one user ID.\n")); else if( cpr_get_answer_is_yes( "keyedit.revoke.uid.okay", - n1 > 1? _("Really revoke all selected user IDs? ") - : _("Really revoke this user ID? ") + n1 > 1? _("Really revoke all selected user IDs? (y/N) ") + : _("Really revoke this user ID? (y/N) ") ) ) { if(menu_revuid(keyblock,sec_keyblock)) { @@ -1528,36 +2015,58 @@ keyedit_menu( const char *username, STRLIST locusr, STRLIST commands, } break; - case cmdREVKEY: { - int n1; + case cmdREVKEY: + { + int n1; - if( !(n1=count_selected_keys( keyblock )) ) - tty_printf(_("You must select at least one key.\n")); - else if( sec_keyblock && !cpr_get_answer_is_yes( - "keyedit.revoke.subkey.okay", - n1 > 1? - _("Do you really want to revoke the selected keys? "): - _("Do you really want to revoke this key? ") - )) - ; - else { - if( menu_revkey( keyblock, sec_keyblock ) ) { - modified = 1; - /*sec_modified = 1;*/ + if( !(n1=count_selected_keys( keyblock )) ) + { + if(cpr_get_answer_is_yes("keyedit.revoke.subkey.okay", + _("Do you really want to revoke" + " the entire key? (y/N) "))) + { + if(menu_revkey(keyblock,sec_keyblock)) + modified=1; + + redisplay=1; } - redisplay = 1; } + else if(cpr_get_answer_is_yes("keyedit.revoke.subkey.okay", + n1 > 1? + _("Do you really want to revoke" + " the selected subkeys? (y/N) "): + _("Do you really want to revoke" + " this subkey? (y/N) "))) + { + if( menu_revsubkey( keyblock, sec_keyblock ) ) + modified = 1; + + redisplay = 1; + } + + if(modified) + merge_keys_and_selfsig( keyblock ); } break; case cmdEXPIRE: - if( menu_expire( keyblock, sec_keyblock ) ) { + if( menu_expire( keyblock, sec_keyblock ) ) + { merge_keys_and_selfsig( sec_keyblock ); merge_keys_and_selfsig( keyblock ); sec_modified = 1; modified = 1; redisplay = 1; - } + } + break; + + case cmdBACKSIGN: + if(menu_backsign(keyblock,sec_keyblock)) + { + sec_modified = 1; + modified = 1; + redisplay = 1; + } break; case cmdPRIMARY: @@ -1574,6 +2083,13 @@ keyedit_menu( const char *username, STRLIST locusr, STRLIST commands, break; case cmdTRUST: + if(opt.trust_model==TM_EXTERNAL) + { + tty_printf(_("Owner trust may not be set while " + "using an user provided trust database\n")); + break; + } + show_key_with_all_names( keyblock, 0, 0, 0, 1, 0 ); tty_printf("\n"); if( edit_ownertrust( find_kbnode( keyblock, @@ -1587,44 +2103,68 @@ keyedit_menu( const char *username, STRLIST locusr, STRLIST commands, break; case cmdPREF: - show_key_with_all_names( keyblock, 0, 0, 0, 0, 1 ); + { + int count=count_selected_uids(keyblock); + assert(keyblock->pkt->pkttype==PKT_PUBLIC_KEY); + show_names(keyblock,keyblock->pkt->pkt.public_key, + count?NODFLG_SELUID:0,1); + } break; case cmdSHOWPREF: - show_key_with_all_names( keyblock, 0, 0, 0, 0, 2 ); + { + int count=count_selected_uids(keyblock); + assert(keyblock->pkt->pkttype==PKT_PUBLIC_KEY); + show_names(keyblock,keyblock->pkt->pkt.public_key, + count?NODFLG_SELUID:0,2); + } break; case cmdSETPREF: - keygen_set_std_prefs ( !*arg_string? "default" : arg_string, 0); - break; + { + PKT_user_id *tempuid; - case cmdUPDPREF: - { - PKT_user_id *temp=keygen_get_std_prefs(); - tty_printf(_("Current preference list:\n")); - show_prefs(temp,1); - xfree (temp); - } - if (cpr_get_answer_is_yes ("keyedit.updpref.okay", - count_selected_uids (keyblock)? - _("Really update the preferences" - " for the selected user IDs? "): - _("Really update the preferences? "))){ - - if ( menu_set_preferences (keyblock, sec_keyblock) ) { - merge_keys_and_selfsig (keyblock); - modified = 1; - redisplay = 1; - } - } + keygen_set_std_prefs(!*arg_string?"default" : arg_string, 0); + + tempuid=keygen_get_std_prefs(); + tty_printf(_("Set preference list to:\n")); + show_prefs(tempuid,NULL,1); + free_user_id(tempuid); + + if(cpr_get_answer_is_yes("keyedit.setpref.okay", + count_selected_uids (keyblock)? + _("Really update the preferences" + " for the selected user IDs? (y/N) "): + _("Really update the preferences? (y/N) "))) + { + if ( menu_set_preferences (keyblock, sec_keyblock) ) + { + merge_keys_and_selfsig (keyblock); + modified = 1; + redisplay = 1; + } + } + } break; case cmdPREFKS: - if( menu_set_keyserver_url ( keyblock, sec_keyblock ) ) { + if( menu_set_keyserver_url ( *arg_string?arg_string:NULL, + keyblock, sec_keyblock ) ) + { merge_keys_and_selfsig( keyblock ); modified = 1; redisplay = 1; - } + } + break; + + case cmdNOTATION: + if( menu_set_notation ( *arg_string?arg_string:NULL, + keyblock, sec_keyblock ) ) + { + merge_keys_and_selfsig( keyblock ); + modified = 1; + redisplay = 1; + } break; case cmdNOP: @@ -1645,9 +2185,17 @@ keyedit_menu( const char *username, STRLIST locusr, STRLIST commands, } break; - case cmdSHOWPHOTO: - menu_showphoto(keyblock); - break; + case cmdSHOWPHOTO: + menu_showphoto(keyblock); + break; + + case cmdCLEAN: + redisplay=modified=menu_clean(keyblock,0); + break; + + case cmdMINIMIZE: + redisplay=modified=menu_clean(keyblock,1); + break; case cmdQUIT: if( have_commands ) @@ -1655,21 +2203,20 @@ keyedit_menu( const char *username, STRLIST locusr, STRLIST commands, if( !modified && !sec_modified ) goto leave; if( !cpr_get_answer_is_yes("keyedit.save.okay", - _("Save changes? ")) ) { + _("Save changes? (y/N) ")) ) { if( cpr_enabled() || cpr_get_answer_is_yes("keyedit.cancel.okay", - _("Quit without saving? ")) ) + _("Quit without saving? (y/N) "))) goto leave; break; } /* fall thru */ case cmdSAVE: - do_cmd_save: if( modified || sec_modified ) { if( modified ) { rc = keydb_update_keyblock (kdbhd, keyblock); if( rc ) { - log_error(_("update failed: %s\n"), gpg_strerror (rc) ); + log_error(_("update failed: %s\n"), g10_errstr(rc) ); break; } } @@ -1677,7 +2224,7 @@ keyedit_menu( const char *username, STRLIST locusr, STRLIST commands, rc = keydb_update_keyblock (sec_kdbhd, sec_keyblock ); if( rc ) { log_error( _("update secret failed: %s\n"), - gpg_strerror (rc) ); + g10_errstr(rc) ); break; } } @@ -1704,15 +2251,44 @@ keyedit_menu( const char *username, STRLIST locusr, STRLIST commands, release_kbnode( keyblock ); release_kbnode( sec_keyblock ); keydb_release (kdbhd); - xfree (answer); + xfree(answer); } +static void +tty_print_notations(int indent,PKT_signature *sig) +{ + int first=1; + struct notation *notation,*nd; + + if(indent<0) + { + first=0; + indent=-indent; + } + + notation=sig_to_notation(sig); + + for(nd=notation;nd;nd=nd->next) + { + if(!first) + tty_printf("%*s",indent,""); + else + first=0; + + tty_print_utf8_string(nd->name,strlen(nd->name)); + tty_printf("="); + tty_print_utf8_string(nd->value,strlen(nd->value)); + tty_printf("\n"); + } + + free_notation(notation); +} /**************** * show preferences of a public keyblock. */ static void -show_prefs (PKT_user_id *uid, int verbose) +show_prefs (PKT_user_id *uid, PKT_signature *selfsig, int verbose) { const prefitem_t fake={0,0}; const prefitem_t *prefs; @@ -1730,11 +2306,12 @@ show_prefs (PKT_user_id *uid, int verbose) if (verbose) { int any, des_seen=0, sha1_seen=0, uncomp_seen=0; + tty_printf (" "); tty_printf (_("Cipher: ")); for(i=any=0; prefs[i].type; i++ ) { if( prefs[i].type == PREFTYPE_SYM ) { - const char *s = gcry_cipher_algo_name (prefs[i].value); + const char *s = cipher_algo_to_string (prefs[i].value); if (any) tty_printf (", "); @@ -1751,13 +2328,13 @@ show_prefs (PKT_user_id *uid, int verbose) if (!des_seen) { if (any) tty_printf (", "); - tty_printf ("%s", gcry_cipher_algo_name (CIPHER_ALGO_3DES)); + tty_printf ("%s",cipher_algo_to_string(CIPHER_ALGO_3DES)); } tty_printf ("\n "); tty_printf (_("Digest: ")); for(i=any=0; prefs[i].type; i++ ) { if( prefs[i].type == PREFTYPE_HASH ) { - const char *s = gcry_md_algo_name (prefs[i].value); + const char *s = digest_algo_to_string (prefs[i].value); if (any) tty_printf (", "); @@ -1774,7 +2351,7 @@ show_prefs (PKT_user_id *uid, int verbose) if (!sha1_seen) { if (any) tty_printf (", "); - tty_printf ("%s", gcry_md_algo_name (DIGEST_ALGO_SHA1)); + tty_printf ("%s",digest_algo_to_string(DIGEST_ALGO_SHA1)); } tty_printf ("\n "); tty_printf (_("Compression: ")); @@ -1790,7 +2367,7 @@ show_prefs (PKT_user_id *uid, int verbose) tty_printf ("%s", s ); else tty_printf ("[%d]", prefs[i].value); - if (prefs[i].value == 0 ) + if (prefs[i].value == COMPRESS_ALGO_NONE ) uncomp_seen = 1; } } @@ -1798,22 +2375,22 @@ show_prefs (PKT_user_id *uid, int verbose) if (any) tty_printf (", "); else { - tty_printf ("%s",compress_algo_to_string(1)); + tty_printf ("%s",compress_algo_to_string(COMPRESS_ALGO_ZIP)); tty_printf (", "); } - tty_printf ("%s",compress_algo_to_string(0)); + tty_printf ("%s",compress_algo_to_string(COMPRESS_ALGO_NONE)); } - if(uid->mdc_feature || !uid->ks_modify) + if(uid->flags.mdc || !uid->flags.ks_modify) { tty_printf ("\n "); tty_printf (_("Features: ")); any=0; - if(uid->mdc_feature) + if(uid->flags.mdc) { tty_printf ("MDC"); any=1; } - if(!uid->ks_modify) + if(!uid->flags.ks_modify) { if(any) tty_printf (", "); @@ -1821,6 +2398,29 @@ show_prefs (PKT_user_id *uid, int verbose) } } tty_printf("\n"); + + if(selfsig) + { + const byte *pref_ks; + size_t pref_ks_len; + + pref_ks=parse_sig_subpkt(selfsig->hashed, + SIGSUBPKT_PREF_KS,&pref_ks_len); + if(pref_ks && pref_ks_len) + { + tty_printf (" "); + tty_printf(_("Preferred keyserver: ")); + tty_print_utf8_string(pref_ks,pref_ks_len); + tty_printf("\n"); + } + + if(selfsig->flags.notation) + { + tty_printf (" "); + tty_printf(_("Notations: ")); + tty_print_notations(5+strlen(_("Notations: ")),selfsig); + } + } } else { tty_printf(" "); @@ -1830,15 +2430,14 @@ show_prefs (PKT_user_id *uid, int verbose) prefs[i].type == PREFTYPE_ZIP ? 'Z':'?', prefs[i].value); } - if (uid->mdc_feature) + if (uid->flags.mdc) tty_printf (" [mdc]"); - if (!uid->ks_modify) + if (!uid->flags.ks_modify) tty_printf (" [no-ks-modify]"); tty_printf("\n"); } } - /* This is the version of show_key_with_all_names used when opt.with_colons is used. It prints all available data in a easy to parse format and does not translate utf8 */ @@ -1882,15 +2481,12 @@ show_key_with_all_names_colon (KBNODE keyblock) putchar (trust); } - printf (":%u:%d:%08lX%08lX:%lu:%lu:", + printf (":%u:%d:%08lX%08lX:%lu:%lu::", nbits_from_pk (pk), pk->pubkey_algo, (ulong)keyid[0], (ulong)keyid[1], (ulong)pk->timestamp, (ulong)pk->expiredate ); - if (pk->local_id) - printf ("%lu", pk->local_id); - putchar (':'); if (node->pkt->pkttype==PKT_PUBLIC_KEY && !(opt.fast_list_mode || opt.no_expensive_trust_checks )) putchar(get_ownertrust_info (pk)); @@ -1898,24 +2494,7 @@ show_key_with_all_names_colon (KBNODE keyblock) putchar('\n'); print_fingerprint (pk, NULL, 0); - - /* print the revoker record */ - if( !pk->revkey && pk->numrevkeys ) - BUG(); - else - { - for (i=0; i < pk->numrevkeys; i++) - { - byte *p; - - printf ("rvk:::%d::::::", pk->revkey[i].algid); - p = pk->revkey[i].fpr; - for (j=0; j < 20; j++, p++ ) - printf ("%02X", *p); - printf (":%02x%s:\n", pk->revkey[i].class, - (pk->revkey[i].class&0x40)?"s":""); - } - } + print_revokers(pk); } } @@ -1975,9 +2554,9 @@ show_key_with_all_names_colon (KBNODE keyblock) prefs[j].type == PREFTYPE_ZIP ? 'Z':'?', prefs[j].value); } - if (uid->mdc_feature) + if (uid->flags.mdc) printf (",mdc"); - if (!uid->ks_modify) + if (!uid->flags.ks_modify) printf (",no-ks-modify"); } putchar (':'); @@ -1999,6 +2578,63 @@ show_key_with_all_names_colon (KBNODE keyblock) } } +static void +show_names(KBNODE keyblock,PKT_public_key *pk,unsigned int flag,int with_prefs) +{ + KBNODE node; + int i=0; + + for( node = keyblock; node; node = node->next ) + { + if( node->pkt->pkttype == PKT_USER_ID + && !is_deleted_kbnode(node)) + { + PKT_user_id *uid = node->pkt->pkt.user_id; + ++i; + if(!flag || (flag && (node->flag & flag))) + { + if(!(flag&NODFLG_MARK_A) && pk) + tty_printf("%s ",uid_trust_string_fixed(pk,uid)); + + if( flag & NODFLG_MARK_A ) + tty_printf(" "); + else if( node->flag & NODFLG_SELUID ) + tty_printf("(%d)* ", i); + else if( uid->is_primary ) + tty_printf("(%d). ", i); + else + tty_printf("(%d) ", i); + tty_print_utf8_string( uid->name, uid->len ); + tty_printf("\n"); + if(with_prefs && pk) + { + if(pk->version>3 || uid->selfsigversion>3) + { + PKT_signature *selfsig=NULL; + KBNODE signode; + + for(signode=node->next; + signode && signode->pkt->pkttype==PKT_SIGNATURE; + signode=signode->next) + { + if(signode->pkt->pkt.signature-> + flags.chosen_selfsig) + { + selfsig=signode->pkt->pkt.signature; + break; + } + } + + show_prefs (uid, selfsig, with_prefs == 2); + } + else + tty_printf(_("There are no preferences on a" + " PGP 2.x-style user ID.\n")); + } + } + } + } +} /**************** * Display the key a the user ids, if only_marked is true, do only @@ -2009,7 +2645,7 @@ show_key_with_all_names( KBNODE keyblock, int only_marked, int with_revoker, int with_fpr, int with_subkeys, int with_prefs ) { KBNODE node; - int i, rc; + int i; int do_warn = 0; byte pk_version=0; PKT_public_key *primary=NULL; @@ -2023,7 +2659,8 @@ show_key_with_all_names( KBNODE keyblock, int only_marked, int with_revoker, /* the keys */ for( node = keyblock; node; node = node->next ) { if( node->pkt->pkttype == PKT_PUBLIC_KEY - || (with_subkeys && node->pkt->pkttype == PKT_PUBLIC_SUBKEY) ) { + || (with_subkeys && node->pkt->pkttype == PKT_PUBLIC_SUBKEY + && !is_deleted_kbnode(node)) ) { PKT_public_key *pk = node->pkt->pkt.public_key; const char *otrust="err",*trust="err"; @@ -2042,58 +2679,88 @@ show_key_with_all_names( KBNODE keyblock, int only_marked, int with_revoker, do_warn = 1; } - pk_version = pk->version; - primary = pk; + pk_version=pk->version; + primary=pk; } - if(with_revoker) { + if(pk->is_revoked) + { + char *user=get_user_id_string_native(pk->revoked.keyid); + const char *algo=pubkey_algo_to_string(pk->revoked.algo); + tty_printf(_("This key was revoked on %s by %s key %s\n"), + revokestr_from_pk(pk),algo?algo:"?",user); + xfree(user); + } + + if(with_revoker) + { if( !pk->revkey && pk->numrevkeys ) - BUG(); + BUG(); else - for(i=0;i<pk->numrevkeys;i++) { - u32 r_keyid[2]; - char *user; - const char *algo= - gcry_pk_algo_name (pk->revkey[i].algid); - - keyid_from_fingerprint(pk->revkey[i].fpr, - MAX_FINGERPRINT_LEN,r_keyid); - - user=get_user_id_string (r_keyid); - tty_printf (_("This key may be revoked by %s key "), - algo?algo:"?"); - tty_print_utf8_string (user, strlen (user)); - if ((pk->revkey[i].class&0x40)) - tty_printf (_(" (sensitive)")); - tty_printf ("\n"); - xfree (user); - } - } + for(i=0;i<pk->numrevkeys;i++) + { + u32 r_keyid[2]; + char *user; + const char *algo= + pubkey_algo_to_string(pk->revkey[i].algid); + + keyid_from_fingerprint(pk->revkey[i].fpr, + MAX_FINGERPRINT_LEN,r_keyid); + + user=get_user_id_string_native(r_keyid); + tty_printf(_("This key may be revoked by %s key %s"), + algo?algo:"?",user); + + if(pk->revkey[i].class&0x40) + { + tty_printf(" "); + tty_printf(_("(sensitive)")); + } + + tty_printf ("\n"); + xfree(user); + } + } keyid_from_pk(pk,NULL); - tty_printf("%s%c %4u%c/", + tty_printf("%s%c %4u%c/%s ", node->pkt->pkttype == PKT_PUBLIC_KEY? "pub":"sub", (node->flag & NODFLG_SELKEY)? '*':' ', nbits_from_pk( pk ), - pubkey_letter( pk->pubkey_algo )); - - if(opt.list_options&LIST_SHOW_LONG_KEYID) - tty_printf("%08lX",(ulong)pk->keyid[0]); - - tty_printf("%08lX ",(ulong)pk->keyid[1]); - tty_printf(_("created: %s expires: %s"), - datestr_from_pk(pk), - expirestr_from_pk(pk) ); + pubkey_letter( pk->pubkey_algo ), + keystr(pk->keyid)); + + tty_printf(_("created: %s"),datestr_from_pk(pk)); + tty_printf(" "); + if(pk->is_revoked) + tty_printf(_("revoked: %s"),revokestr_from_pk(pk)); + else if(pk->has_expired) + tty_printf(_("expired: %s"),expirestr_from_pk(pk)); + else + tty_printf(_("expires: %s"),expirestr_from_pk(pk)); + tty_printf(" "); + tty_printf(_("usage: %s"),usagestr_from_pk(pk)); tty_printf("\n"); if( node->pkt->pkttype == PKT_PUBLIC_KEY ) { - tty_printf(" "); - if(opt.list_options&LIST_SHOW_LONG_KEYID) - tty_printf(" "); - tty_printf(_("trust: %-13s"), otrust); - tty_printf(_("validity: %s"), trust ); - tty_printf("\n"); + if(opt.trust_model!=TM_ALWAYS) + { + tty_printf("%*s", (int)keystrlen()+13,""); + /* Ownertrust is only meaningful for the PGP or + classic trust models */ + if(opt.trust_model==TM_PGP || opt.trust_model==TM_CLASSIC) + { + int width=14-strlen(otrust); + if(width<=0) + width=1; + tty_printf(_("trust: %s"), otrust); + tty_printf("%*s",width,""); + } + + tty_printf(_("validity: %s"), trust ); + tty_printf("\n"); + } if( node->pkt->pkttype == PKT_PUBLIC_KEY && (get_ownertrust (pk)&TRUST_FLAG_DISABLED)) { @@ -2110,16 +2777,18 @@ show_key_with_all_names( KBNODE keyblock, int only_marked, int with_revoker, } } else if( node->pkt->pkttype == PKT_SECRET_KEY - || (with_subkeys && node->pkt->pkttype == PKT_SECRET_SUBKEY) ) { + || (with_subkeys && node->pkt->pkttype == PKT_SECRET_SUBKEY) ) + { PKT_secret_key *sk = node->pkt->pkt.secret_key; - tty_printf(_("%s%c %4u%c/%08lX created: %s expires: %s"), - node->pkt->pkttype == PKT_SECRET_KEY? "sec":"ssb", - (node->flag & NODFLG_SELKEY)? '*':' ', - nbits_from_sk( sk ), - pubkey_letter( sk->pubkey_algo ), - (ulong)keyid_from_sk(sk,NULL), - datestr_from_sk(sk), - expirestr_from_sk(sk) ); + tty_printf("%s%c %4u%c/%s ", + node->pkt->pkttype == PKT_SECRET_KEY? "sec":"ssb", + (node->flag & NODFLG_SELKEY)? '*':' ', + nbits_from_sk( sk ), + pubkey_letter( sk->pubkey_algo ), + keystr_from_sk(sk)); + tty_printf(_("created: %s"),datestr_from_sk(sk)); + tty_printf(" "); + tty_printf(_("expires: %s"),expirestr_from_sk(sk)); tty_printf("\n"); if (sk->is_protected && sk->protect.s2k.mode == 1002) { @@ -2142,67 +2811,19 @@ show_key_with_all_names( KBNODE keyblock, int only_marked, int with_revoker, } tty_printf ("\n"); } - } - else if( with_subkeys && node->pkt->pkttype == PKT_SIGNATURE - && node->pkt->pkt.signature->sig_class == 0x28 ) { - PKT_signature *sig = node->pkt->pkt.signature; - - rc = check_key_signature( keyblock, node, NULL ); - if( !rc ) - tty_printf( _("rev! subkey has been revoked: %s\n"), - datestr_from_sig( sig ) ); - else if( gpg_err_code (rc) == GPG_ERR_BAD_SIGNATURE ) - tty_printf( _("rev- faked revocation found\n") ); - else if( rc ) - tty_printf( _("rev? problem checking revocation: %s\n"), - gpg_strerror (rc) ); - } - } - /* the user ids */ - i = 0; - for( node = keyblock; node; node = node->next ) { - if( node->pkt->pkttype == PKT_USER_ID ) { - PKT_user_id *uid = node->pkt->pkt.user_id; - ++i; - if( !only_marked || (only_marked && (node->flag & NODFLG_MARK_A))){ - if(opt.list_options&LIST_SHOW_VALIDITY && primary) - tty_printf("[%8.8s] ", - trust_value_to_string(get_validity(primary,uid))); - if( only_marked ) - tty_printf(" "); - else if( node->flag & NODFLG_SELUID ) - tty_printf("(%d)* ", i); - else if( uid->is_primary ) - tty_printf("(%d). ", i); - else - tty_printf("(%d) ", i); - if ( uid->is_revoked ) - tty_printf (_("[revoked] ")); - if ( uid->is_expired ) - tty_printf (_("[expired] ")); - tty_print_utf8_string( uid->name, uid->len ); - tty_printf("\n"); - if( with_prefs ) - { - if(pk_version>3 || uid->selfsigversion>3) - show_prefs (uid, with_prefs == 2); - else - tty_printf(_("There are no preferences on a " - "PGP 2.x-style user ID.\n")); - } - } - } + } } + show_names(keyblock,primary,only_marked?NODFLG_MARK_A:0,with_prefs); + if (do_warn) - tty_printf (_("Please note that the shown key validity " - "is not necessarily correct\n" + tty_printf (_("Please note that the shown key validity" + " is not necessarily correct\n" "unless you restart the program.\n")); - } -/* Display basic key information. This fucntion is suitable to show +/* Display basic key information. This function is suitable to show information on the key without any dependencies on the trustdb or any other internal GnuPG stuff. KEYBLOCK may either be a public or a secret key.*/ @@ -2221,14 +2842,14 @@ show_basic_key_info ( KBNODE keyblock ) /* Note, we use the same format string as in other show functions to make the translation job easier. */ - tty_printf (_("%s%c %4u%c/%08lX created: %s expires: %s"), + tty_printf ("%s %4u%c/%s ", node->pkt->pkttype == PKT_PUBLIC_KEY? "pub":"sub", - ' ', nbits_from_pk( pk ), pubkey_letter( pk->pubkey_algo ), - (ulong)keyid_from_pk(pk,NULL), - datestr_from_pk(pk), - expirestr_from_pk(pk) ); + keystr_from_pk(pk)); + tty_printf(_("created: %s"),datestr_from_pk(pk)); + tty_printf(" "); + tty_printf(_("expires: %s"),expirestr_from_pk(pk)); tty_printf("\n"); print_fingerprint ( pk, NULL, 3 ); tty_printf("\n"); @@ -2236,14 +2857,14 @@ show_basic_key_info ( KBNODE keyblock ) else if (node->pkt->pkttype == PKT_SECRET_KEY) { PKT_secret_key *sk = node->pkt->pkt.secret_key; - tty_printf(_("%s%c %4u%c/%08lX created: %s expires: %s"), + tty_printf("%s %4u%c/%s", node->pkt->pkttype == PKT_SECRET_KEY? "sec":"ssb", - ' ', nbits_from_sk( sk ), pubkey_letter( sk->pubkey_algo ), - (ulong)keyid_from_sk(sk,NULL), - datestr_from_sk(sk), - expirestr_from_sk(sk) ); + keystr_from_sk(sk)); + tty_printf(_("created: %s"),datestr_from_sk(sk)); + tty_printf(" "); + tty_printf(_("expires: %s"),expirestr_from_sk(sk)); tty_printf("\n"); print_fingerprint (NULL, sk, 3 ); tty_printf("\n"); @@ -2260,9 +2881,9 @@ show_basic_key_info ( KBNODE keyblock ) tty_printf (" "); if (uid->is_revoked) - tty_printf ("[revoked] "); - if ( uid->is_expired ) - tty_printf ("[expired] "); + tty_printf("[%s] ",_("revoked")); + else if ( uid->is_expired ) + tty_printf("[%s] ",_("expired")); tty_print_utf8_string (uid->name, uid->len); tty_printf ("\n"); } @@ -2272,40 +2893,40 @@ show_basic_key_info ( KBNODE keyblock ) static void show_key_and_fingerprint( KBNODE keyblock ) { - KBNODE node; - PKT_public_key *pk = NULL; + KBNODE node; + PKT_public_key *pk = NULL; - for( node = keyblock; node; node = node->next ) { - if( node->pkt->pkttype == PKT_PUBLIC_KEY ) { - pk = node->pkt->pkt.public_key; - tty_printf("pub %4u%c/%08lX %s ", - nbits_from_pk( pk ), - pubkey_letter( pk->pubkey_algo ), - (ulong)keyid_from_pk(pk,NULL), - datestr_from_pk(pk) ); + for( node = keyblock; node; node = node->next ) + { + if( node->pkt->pkttype == PKT_PUBLIC_KEY ) + { + pk = node->pkt->pkt.public_key; + tty_printf("pub %4u%c/%s %s ", + nbits_from_pk( pk ), + pubkey_letter( pk->pubkey_algo ), + keystr_from_pk(pk), + datestr_from_pk(pk) ); } - else if( node->pkt->pkttype == PKT_USER_ID ) { - PKT_user_id *uid = node->pkt->pkt.user_id; - tty_print_utf8_string( uid->name, uid->len ); - break; + else if( node->pkt->pkttype == PKT_USER_ID ) + { + PKT_user_id *uid = node->pkt->pkt.user_id; + tty_print_utf8_string( uid->name, uid->len ); + break; } } - tty_printf("\n"); - if( pk ) - print_fingerprint( pk, NULL, 2 ); + tty_printf("\n"); + if( pk ) + print_fingerprint( pk, NULL, 2 ); } /* Show a warning if no uids on the key have the primary uid flag set. */ static void -no_primary_warning(KBNODE keyblock, int uids) +no_primary_warning(KBNODE keyblock) { KBNODE node; - int select_all=1,have_uid=0,uid_count=0; - - if(uids) - select_all=!count_selected_uids(keyblock); + int have_primary=0,uid_count=0; /* TODO: if we ever start behaving differently with a primary or non-primary attribute ID, we will need to check for attributes @@ -2318,17 +2939,18 @@ no_primary_warning(KBNODE keyblock, int uids) { uid_count++; - if((select_all || (node->flag & NODFLG_SELUID)) - && node->pkt->pkt.user_id->is_primary==2) - have_uid|=2; - else - have_uid|=1; + if(node->pkt->pkt.user_id->is_primary==2) + { + have_primary=1; + break; + } } } - if(uid_count>1 && have_uid&1 && !(have_uid&2)) - log_info(_("WARNING: no user ID has been marked as primary. This command " - "may\n cause a different user ID to become the assumed primary.\n")); + if(uid_count>1 && !have_primary) + log_info(_("WARNING: no user ID has been marked as primary. This command" + " may\n cause a different user ID to become" + " the assumed primary.\n")); } /**************** @@ -2337,7 +2959,8 @@ no_primary_warning(KBNODE keyblock, int uids) * Return true if there is a new user id */ static int -menu_adduid( KBNODE pub_keyblock, KBNODE sec_keyblock, int photo) +menu_adduid( KBNODE pub_keyblock, KBNODE sec_keyblock, + int photo, const char *photo_name) { PKT_user_id *uid; PKT_public_key *pk=NULL; @@ -2403,7 +3026,7 @@ menu_adduid( KBNODE pub_keyblock, KBNODE sec_keyblock, int photo) } } - uid = generate_photo_id(pk); + uid = generate_photo_id(pk,photo_name); } else uid = generate_user_id(); if( !uid ) @@ -2413,13 +3036,13 @@ menu_adduid( KBNODE pub_keyblock, KBNODE sec_keyblock, int photo) keygen_add_std_prefs, pk ); free_secret_key( sk ); if( rc ) { - log_error("signing failed: %s\n", gpg_strerror (rc) ); + log_error("signing failed: %s\n", g10_errstr(rc) ); free_user_id(uid); return 0; } /* insert/append to secret keyblock */ - pkt = xcalloc (1, sizeof *pkt ); + pkt = xmalloc_clear( sizeof *pkt ); pkt->pkttype = PKT_USER_ID; pkt->pkt.user_id = scopy_user_id(uid); node = new_kbnode(pkt); @@ -2427,7 +3050,7 @@ menu_adduid( KBNODE pub_keyblock, KBNODE sec_keyblock, int photo) insert_kbnode( sec_where, node, 0 ); else add_kbnode( sec_keyblock, node ); - pkt = xcalloc (1, sizeof *pkt ); + pkt = xmalloc_clear( sizeof *pkt ); pkt->pkttype = PKT_SIGNATURE; pkt->pkt.signature = copy_signature(NULL, sig); if( sec_where ) @@ -2435,7 +3058,7 @@ menu_adduid( KBNODE pub_keyblock, KBNODE sec_keyblock, int photo) else add_kbnode( sec_keyblock, new_kbnode(pkt) ); /* insert/append to public keyblock */ - pkt = xcalloc (1, sizeof *pkt ); + pkt = xmalloc_clear( sizeof *pkt ); pkt->pkttype = PKT_USER_ID; pkt->pkt.user_id = uid; node = new_kbnode(pkt); @@ -2443,7 +3066,7 @@ menu_adduid( KBNODE pub_keyblock, KBNODE sec_keyblock, int photo) insert_kbnode( pub_where, node, 0 ); else add_kbnode( pub_keyblock, node ); - pkt = xcalloc (1, sizeof *pkt ); + pkt = xmalloc_clear( sizeof *pkt ); pkt->pkttype = PKT_SIGNATURE; pkt->pkt.signature = copy_signature(NULL, sig); if( pub_where ) @@ -2455,7 +3078,7 @@ menu_adduid( KBNODE pub_keyblock, KBNODE sec_keyblock, int photo) /**************** - * Remove all selceted userids from the keyrings + * Remove all selected userids from the keyrings */ static void menu_deluid( KBNODE pub_keyblock, KBNODE sec_keyblock ) @@ -2524,10 +3147,15 @@ menu_delsig( KBNODE pub_keyblock ) tty_print_utf8_string( uid->name, uid->len ); tty_printf("\n"); - okay = inv_sig = no_key = other_err = 0; - valid = print_and_check_one_sig( pub_keyblock, node, - &inv_sig, &no_key, &other_err, - &selfsig, 1 ); + okay = inv_sig = no_key = other_err = 0; + if(opt.with_colons) + valid = print_and_check_one_sig_colon( pub_keyblock, node, + &inv_sig, &no_key, &other_err, + &selfsig, 1 ); + else + valid = print_and_check_one_sig( pub_keyblock, node, + &inv_sig, &no_key, &other_err, + &selfsig, 1 ); if( valid ) { okay = cpr_get_answer_yes_no_quit( @@ -2575,6 +3203,58 @@ menu_delsig( KBNODE pub_keyblock ) return changed; } +static int +menu_clean(KBNODE keyblock,int self_only) +{ + KBNODE uidnode; + int modified=0,select_all=!count_selected_uids(keyblock); + + for(uidnode=keyblock->next; + uidnode && uidnode->pkt->pkttype!=PKT_PUBLIC_SUBKEY; + uidnode=uidnode->next) + { + if(uidnode->pkt->pkttype==PKT_USER_ID + && (uidnode->flag&NODFLG_SELUID || select_all)) + { + int uids=0,sigs=0; + char *user=utf8_to_native(uidnode->pkt->pkt.user_id->name, + uidnode->pkt->pkt.user_id->len, + 0); + + clean_one_uid(keyblock,uidnode,opt.verbose,self_only,&uids,&sigs); + if(uids) + { + const char *reason; + + if(uidnode->pkt->pkt.user_id->is_revoked) + reason=_("revoked"); + else if(uidnode->pkt->pkt.user_id->is_expired) + reason=_("expired"); + else + reason=_("invalid"); + + tty_printf("User ID \"%s\" compacted: %s\n",user,reason); + + modified=1; + } + else if(sigs) + { + tty_printf(sigs==1? + "User ID \"%s\": %d signature removed\n": + "User ID \"%s\": %d signatures removed\n", + user,sigs); + + modified=1; + } + else + tty_printf(_("User ID \"%s\": already clean\n"),user); + + xfree(user); + } + } + + return modified; +} /**************** * Remove some of the secondary keys @@ -2681,14 +3361,11 @@ menu_addrevoker( KBNODE pub_keyblock, KBNODE sec_keyblock, int sensitive ) for(;;) { char *answer; - u32 keyid[2]; - char *p; - size_t n; if(revoker_pk) free_public_key(revoker_pk); - revoker_pk=xcalloc (1,sizeof(*revoker_pk)); + revoker_pk=xmalloc_clear(sizeof(*revoker_pk)); tty_printf("\n"); @@ -2696,21 +3373,24 @@ menu_addrevoker( KBNODE pub_keyblock, KBNODE sec_keyblock, int sensitive ) _("Enter the user ID of the designated revoker: ")); if(answer[0]=='\0' || answer[0]=='\004') { - xfree(answer); answer = NULL; + xfree(answer); goto fail; } - - rc=get_pubkey_byname(revoker_pk,answer,NULL,NULL,1); + /* Note that I'm requesting CERT here, which usually implies + primary keys only, but some casual testing shows that PGP and + GnuPG both can handle a designated revokation from a + subkey. */ + revoker_pk->req_usage=PUBKEY_USAGE_CERT; + rc=get_pubkey_byname(revoker_pk,answer,NULL,NULL,1); if(rc) { - log_error (_("key `%s' not found: %s\n"),answer,gpg_strerror (rc)); - xfree (answer); answer = NULL; + log_error (_("key \"%s\" not found: %s\n"),answer,g10_errstr(rc)); + xfree(answer); continue; } - xfree (answer); answer = NULL; - + xfree(answer); fingerprint_from_pk(revoker_pk,revkey.fpr,&fprlen); if(fprlen!=20) @@ -2767,17 +3447,7 @@ menu_addrevoker( KBNODE pub_keyblock, KBNODE sec_keyblock, int sensitive ) continue; } - keyid_from_pk(revoker_pk,keyid); - - tty_printf("\npub %4u%c/%08lX %s ", - nbits_from_pk( revoker_pk ), - pubkey_letter( revoker_pk->pubkey_algo ), - (ulong)keyid[1], datestr_from_pk(pk) ); - - p = get_user_id( keyid, &n ); - tty_print_utf8_string( p, n ); - xfree (p); - tty_printf("\n"); + print_pubkey_info(NULL,revoker_pk); print_fingerprint(revoker_pk,NULL,2); tty_printf("\n"); @@ -2788,7 +3458,7 @@ menu_addrevoker( KBNODE pub_keyblock, KBNODE sec_keyblock, int sensitive ) if(!cpr_get_answer_is_yes("keyedit.add_revoker.okay", _("Are you sure you want to appoint this " - "key as a designated revoker? (y/N): "))) + "key as a designated revoker? (y/N) "))) continue; free_public_key(revoker_pk); @@ -2802,7 +3472,7 @@ menu_addrevoker( KBNODE pub_keyblock, KBNODE sec_keyblock, int sensitive ) keygen_add_revkey,&revkey ); if( rc ) { - log_error("signing failed: %s\n", gpg_strerror (rc) ); + log_error("signing failed: %s\n", g10_errstr(rc) ); goto fail; } @@ -2810,13 +3480,13 @@ menu_addrevoker( KBNODE pub_keyblock, KBNODE sec_keyblock, int sensitive ) sk=NULL; /* insert into secret keyblock */ - pkt = xcalloc (1, sizeof *pkt ); + pkt = xmalloc_clear( sizeof *pkt ); pkt->pkttype = PKT_SIGNATURE; pkt->pkt.signature = copy_signature(NULL, sig); insert_kbnode( sec_keyblock, new_kbnode(pkt), PKT_SIGNATURE ); /* insert into public keyblock */ - pkt = xcalloc (1, sizeof *pkt ); + pkt = xmalloc_clear( sizeof *pkt ); pkt->pkttype = PKT_SIGNATURE; pkt->pkt.signature = sig; insert_kbnode( pub_keyblock, new_kbnode(pkt), PKT_SIGNATURE ); @@ -2854,17 +3524,17 @@ menu_expire( KBNODE pub_keyblock, KBNODE sec_keyblock ) n1 = count_selected_keys( pub_keyblock ); if( n1 > 1 ) { - tty_printf(_("Please select at most one secondary key.\n")); + tty_printf(_("Please select at most one subkey.\n")); return 0; } else if( n1 ) - tty_printf(_("Changing expiration time for a secondary key.\n")); - else { + tty_printf(_("Changing expiration time for a subkey.\n")); + else + { tty_printf(_("Changing expiration time for the primary key.\n")); mainkey=1; - } - - no_primary_warning(pub_keyblock,0); + no_primary_warning(pub_keyblock); + } expiredate = ask_expiredate(); node = find_kbnode( sec_keyblock, PKT_SECRET_KEY ); @@ -2891,9 +3561,11 @@ menu_expire( KBNODE pub_keyblock, KBNODE sec_keyblock ) && ( mainkey || sub_pk ) ) { PKT_signature *sig = node->pkt->pkt.signature; if( keyid[0] == sig->keyid[0] && keyid[1] == sig->keyid[1] - && ( (mainkey && uid - && uid->created && (sig->sig_class&~3) == 0x10) - || (!mainkey && sig->sig_class == 0x18) ) ) { + && ( (mainkey && uid + && uid->created && (sig->sig_class&~3) == 0x10) + || (!mainkey && sig->sig_class == 0x18) ) + && sig->flags.chosen_selfsig ) + { /* this is a selfsignature which is to be replaced */ PKT_signature *newsig; PACKET *newpkt; @@ -2931,23 +3603,23 @@ menu_expire( KBNODE pub_keyblock, KBNODE sec_keyblock ) sk, keygen_add_key_expire, sub_pk ); if( rc ) { log_error("make_keysig_packet failed: %s\n", - gpg_strerror (rc)); + g10_errstr(rc)); free_secret_key( sk ); return 0; } /* replace the packet */ - newpkt = xcalloc (1, sizeof *newpkt ); + newpkt = xmalloc_clear( sizeof *newpkt ); newpkt->pkttype = PKT_SIGNATURE; newpkt->pkt.signature = newsig; free_packet( node->pkt ); - xfree ( node->pkt ); + xfree( node->pkt ); node->pkt = newpkt; if( sn ) { - newpkt = xcalloc (1, sizeof *newpkt ); + newpkt = xmalloc_clear( sizeof *newpkt ); newpkt->pkttype = PKT_SIGNATURE; newpkt->pkt.signature = copy_signature( NULL, newsig ); free_packet( sn->pkt ); - xfree ( sn->pkt ); + xfree( sn->pkt ); sn->pkt = newpkt; } sub_pk = NULL; @@ -2961,6 +3633,152 @@ menu_expire( KBNODE pub_keyblock, KBNODE sec_keyblock ) } static int +menu_backsign(KBNODE pub_keyblock,KBNODE sec_keyblock) +{ + int rc,modified=0; + PKT_public_key *main_pk; + PKT_secret_key *main_sk,*sub_sk=NULL; + KBNODE node; + + assert(pub_keyblock->pkt->pkttype==PKT_PUBLIC_KEY); + assert(sec_keyblock->pkt->pkttype==PKT_SECRET_KEY); + + merge_keys_and_selfsig(pub_keyblock); + main_pk=pub_keyblock->pkt->pkt.public_key; + main_sk=copy_secret_key(NULL,sec_keyblock->pkt->pkt.secret_key); + keyid_from_pk(main_pk,NULL); + + for(node=pub_keyblock;node;node=node->next) + { + PKT_public_key *sub_pk=NULL; + KBNODE node2,sig_pk=NULL,sig_sk=NULL; + char *passphrase; + + if(sub_sk) + { + free_secret_key(sub_sk); + sub_sk=NULL; + } + + /* Find a signing subkey with no backsig */ + if(node->pkt->pkttype==PKT_PUBLIC_SUBKEY + && (node->pkt->pkt.public_key->pubkey_usage&PUBKEY_USAGE_SIG) + && !node->pkt->pkt.public_key->backsig) + sub_pk=node->pkt->pkt.public_key; + + if(!sub_pk) + continue; + + /* Find the selected selfsig on this subkey */ + for(node2=node->next; + node2 && node2->pkt->pkttype==PKT_SIGNATURE; + node2=node2->next) + if(node2->pkt->pkt.signature->version>=4 + && node2->pkt->pkt.signature->flags.chosen_selfsig) + { + sig_pk=node2; + break; + } + + if(!sig_pk) + continue; + + /* Find the secret subkey that matches the public subkey */ + for(node2=sec_keyblock;node2;node2=node2->next) + if(node2->pkt->pkttype==PKT_SECRET_SUBKEY + && !cmp_public_secret_key(sub_pk,node2->pkt->pkt.secret_key)) + { + sub_sk=copy_secret_key(NULL,node2->pkt->pkt.secret_key); + break; + } + + if(!sub_sk) + continue; + + /* Now finally find the matching selfsig on the secret subkey. + We can't use chosen_selfsig here (it's not set for secret + keys), so we just pick the selfsig with the right class. + This is what menu_expire does as well. */ + for(node2=node2->next; + node2 && node2->pkt->pkttype!=PKT_SECRET_SUBKEY; + node2=node2->next) + if(node2->pkt->pkttype==PKT_SIGNATURE + && node2->pkt->pkt.signature->version>=4 + && node2->pkt->pkt.signature->keyid[0]==sig_pk->pkt->pkt.signature->keyid[0] + && node2->pkt->pkt.signature->keyid[1]==sig_pk->pkt->pkt.signature->keyid[1] + && node2->pkt->pkt.signature->sig_class==sig_pk->pkt->pkt.signature->sig_class) + { + sig_sk=node2; + break; + } + + if(!sig_sk) + continue; + + /* Now we can get to work. We have a main key and secret part, + a signing subkey with signature and secret part with + signature. */ + + passphrase=get_last_passphrase(); + set_next_passphrase(passphrase); + xfree(passphrase); + + rc=make_backsig(sig_pk->pkt->pkt.signature,main_pk,sub_pk,sub_sk); + if(rc==0) + { + PKT_signature *newsig; + PACKET *newpkt; + + passphrase=get_last_passphrase(); + set_next_passphrase(passphrase); + xfree(passphrase); + + rc=update_keysig_packet(&newsig,sig_pk->pkt->pkt.signature,main_pk, + NULL,sub_pk,main_sk,NULL,NULL); + if(rc==0) + { + /* Put the new sig into place on the pubkey */ + newpkt=xmalloc_clear(sizeof(*newpkt)); + newpkt->pkttype=PKT_SIGNATURE; + newpkt->pkt.signature=newsig; + free_packet(sig_pk->pkt); + xfree(sig_pk->pkt); + sig_pk->pkt=newpkt; + + /* Put the new sig into place on the seckey */ + newpkt=xmalloc_clear(sizeof(*newpkt)); + newpkt->pkttype=PKT_SIGNATURE; + newpkt->pkt.signature=copy_signature(NULL,newsig); + free_packet(sig_sk->pkt); + xfree(sig_sk->pkt); + sig_sk->pkt=newpkt; + + modified=1; + } + else + { + log_error("update_keysig_packet failed: %s\n",g10_errstr(rc)); + break; + } + } + else + { + log_error("make_backsig failed: %s\n",g10_errstr(rc)); + break; + } + } + + set_next_passphrase(NULL); + + free_secret_key(main_sk); + if(sub_sk) + free_secret_key(sub_sk); + + return modified; +} + + +static int change_primary_uid_cb ( PKT_signature *sig, void *opaque ) { byte buf[1]; @@ -3033,14 +3851,16 @@ menu_set_primary_uid ( KBNODE pub_keyblock, KBNODE sec_keyblock ) else if ( main_pk && uid && node->pkt->pkttype == PKT_SIGNATURE ) { PKT_signature *sig = node->pkt->pkt.signature; if ( keyid[0] == sig->keyid[0] && keyid[1] == sig->keyid[1] - && (uid && (sig->sig_class&~3) == 0x10) - && attribute == (uid->attrib_data!=NULL)) { + && (uid && (sig->sig_class&~3) == 0x10) + && attribute == (uid->attrib_data!=NULL) + && sig->flags.chosen_selfsig ) + { if(sig->version < 4) { char *user=utf8_to_native(uid->name,strlen(uid->name),0); - log_info(_("skipping v3 self-signature on user id \"%s\"\n"), + log_info(_("skipping v3 self-signature on user ID \"%s\"\n"), user); - xfree (user); + xfree(user); } else { /* This is a selfsignature which is to be replaced. @@ -3079,16 +3899,16 @@ menu_set_primary_uid ( KBNODE pub_keyblock, KBNODE sec_keyblock ) action > 0? "x":NULL ); if( rc ) { log_error ("update_keysig_packet failed: %s\n", - gpg_strerror (rc)); + g10_errstr(rc)); free_secret_key( sk ); return 0; } /* replace the packet */ - newpkt = xcalloc (1, sizeof *newpkt ); + newpkt = xmalloc_clear( sizeof *newpkt ); newpkt->pkttype = PKT_SIGNATURE; newpkt->pkt.signature = newsig; free_packet( node->pkt ); - xfree ( node->pkt ); + xfree( node->pkt ); node->pkt = newpkt; modified = 1; } @@ -3116,7 +3936,7 @@ menu_set_preferences (KBNODE pub_keyblock, KBNODE sec_keyblock ) int selected, select_all; int modified = 0; - no_primary_warning(pub_keyblock,1); + no_primary_warning(pub_keyblock); select_all = !count_selected_uids (pub_keyblock); @@ -3143,13 +3963,14 @@ menu_set_preferences (KBNODE pub_keyblock, KBNODE sec_keyblock ) && node->pkt->pkttype == PKT_SIGNATURE ) { PKT_signature *sig = node->pkt->pkt.signature; if ( keyid[0] == sig->keyid[0] && keyid[1] == sig->keyid[1] - && (uid && (sig->sig_class&~3) == 0x10) ) { + && (uid && (sig->sig_class&~3) == 0x10) + && sig->flags.chosen_selfsig ) { if( sig->version < 4 ) { char *user=utf8_to_native(uid->name,strlen(uid->name),0); - log_info(_("skipping v3 self-signature on user id \"%s\"\n"), + log_info(_("skipping v3 self-signature on user ID \"%s\"\n"), user); - xfree (user); + xfree(user); } else { /* This is a selfsignature which is to be replaced @@ -3166,16 +3987,16 @@ menu_set_preferences (KBNODE pub_keyblock, KBNODE sec_keyblock ) NULL ); if( rc ) { log_error ("update_keysig_packet failed: %s\n", - gpg_strerror (rc)); + g10_errstr(rc)); free_secret_key( sk ); return 0; } /* replace the packet */ - newpkt = xcalloc (1, sizeof *newpkt ); + newpkt = xmalloc_clear( sizeof *newpkt ); newpkt->pkttype = PKT_SIGNATURE; newpkt->pkt.signature = newsig; free_packet( node->pkt ); - xfree ( node->pkt ); + xfree( node->pkt ); node->pkt = newpkt; modified = 1; } @@ -3188,98 +4009,360 @@ menu_set_preferences (KBNODE pub_keyblock, KBNODE sec_keyblock ) } - static int -menu_set_keyserver_url (KBNODE pub_keyblock, KBNODE sec_keyblock ) +menu_set_keyserver_url (const char *url, + KBNODE pub_keyblock, KBNODE sec_keyblock ) { - PKT_secret_key *sk; /* copy of the main sk */ - PKT_public_key *main_pk; - PKT_user_id *uid; - KBNODE node; - u32 keyid[2]; - int selected, select_all; - int modified = 0; - char *answer; + PKT_secret_key *sk; /* copy of the main sk */ + PKT_public_key *main_pk; + PKT_user_id *uid; + KBNODE node; + u32 keyid[2]; + int selected, select_all; + int modified = 0; + char *answer,*uri; - no_primary_warning(pub_keyblock,1); + no_primary_warning(pub_keyblock); - answer=cpr_get_utf8("keyedit.add_keyserver", - _("Enter your preferred keyserver URL: ")); - if(answer[0]=='\0' || answer[0]=='\004') - { - xfree(answer); - return 0; - } + if(url) + answer=xstrdup(url); + else + { + answer=cpr_get_utf8("keyedit.add_keyserver", + _("Enter your preferred keyserver URL: ")); + if(answer[0]=='\0' || answer[0]=='\004') + { + xfree(answer); + return 0; + } + } - select_all = !count_selected_uids (pub_keyblock); + if(ascii_strcasecmp(answer,"none")==0) + uri=NULL; + else + { + struct keyserver_spec *keyserver=NULL; + /* Sanity check the format */ + keyserver=parse_keyserver_uri(answer,1,NULL,0); + xfree(answer); + if(!keyserver) + { + log_info(_("could not parse keyserver URL\n")); + return 0; + } + uri=xstrdup(keyserver->uri); + free_keyserver_spec(keyserver); + } - node = find_kbnode( sec_keyblock, PKT_SECRET_KEY ); - sk = copy_secret_key( NULL, node->pkt->pkt.secret_key); + select_all = !count_selected_uids (pub_keyblock); - /* Now we can actually change the self signature(s) */ - main_pk = NULL; - uid = NULL; - selected = 0; - for ( node=pub_keyblock; node; node = node->next ) { - if ( node->pkt->pkttype == PKT_PUBLIC_SUBKEY ) - break; /* ready */ + node = find_kbnode( sec_keyblock, PKT_SECRET_KEY ); + sk = copy_secret_key( NULL, node->pkt->pkt.secret_key); - if ( node->pkt->pkttype == PKT_PUBLIC_KEY ) { - main_pk = node->pkt->pkt.public_key; - keyid_from_pk( main_pk, keyid ); + /* Now we can actually change the self signature(s) */ + main_pk = NULL; + uid = NULL; + selected = 0; + for ( node=pub_keyblock; node; node = node->next ) + { + if ( node->pkt->pkttype == PKT_PUBLIC_SUBKEY ) + break; /* ready */ + + if ( node->pkt->pkttype == PKT_PUBLIC_KEY ) + { + main_pk = node->pkt->pkt.public_key; + keyid_from_pk( main_pk, keyid ); } - else if ( node->pkt->pkttype == PKT_USER_ID ) { - uid = node->pkt->pkt.user_id; - selected = select_all || (node->flag & NODFLG_SELUID); - } - else if ( main_pk && uid && selected - && node->pkt->pkttype == PKT_SIGNATURE ) { - PKT_signature *sig = node->pkt->pkt.signature; - if ( keyid[0] == sig->keyid[0] && keyid[1] == sig->keyid[1] - && (uid && (sig->sig_class&~3) == 0x10) ) { - if( sig->version < 4 ) { - char *user=utf8_to_native(uid->name,strlen(uid->name),0); + else if ( node->pkt->pkttype == PKT_USER_ID ) + { + uid = node->pkt->pkt.user_id; + selected = select_all || (node->flag & NODFLG_SELUID); + } + else if ( main_pk && uid && selected + && node->pkt->pkttype == PKT_SIGNATURE ) + { + PKT_signature *sig = node->pkt->pkt.signature; + if ( keyid[0] == sig->keyid[0] && keyid[1] == sig->keyid[1] + && (uid && (sig->sig_class&~3) == 0x10) + && sig->flags.chosen_selfsig) + { + char *user=utf8_to_native(uid->name,strlen(uid->name),0); + if( sig->version < 4 ) + log_info(_("skipping v3 self-signature on user ID \"%s\"\n"), + user); + else + { + /* This is a selfsignature which is to be replaced + * We have to ignore v3 signatures because they are + * not able to carry the subpacket. */ + PKT_signature *newsig; + PACKET *newpkt; + int rc; + const byte *p; + size_t plen; + + p=parse_sig_subpkt(sig->hashed,SIGSUBPKT_PREF_KS,&plen); + if(p && plen) + { + tty_printf("Current preferred keyserver for user" + " ID \"%s\": ",user); + tty_print_utf8_string(p,plen); + tty_printf("\n"); + if(!cpr_get_answer_is_yes("keyedit.confirm_keyserver", + uri?_("Are you sure you want to replace it? (y/N) "): + _("Are you sure you want to delete it? (y/N) "))) + continue; + } + else if(uri==NULL) + { + /* There is no current keyserver URL, so there + is no point in trying to un-set it. */ + continue; + } + + rc = update_keysig_packet (&newsig, sig, + main_pk, uid, NULL, + sk, + keygen_add_keyserver_url, uri ); + if( rc ) + { + log_error ("update_keysig_packet failed: %s\n", + g10_errstr(rc)); + free_secret_key( sk ); + xfree(uri); + return 0; + } + /* replace the packet */ + newpkt = xmalloc_clear( sizeof *newpkt ); + newpkt->pkttype = PKT_SIGNATURE; + newpkt->pkt.signature = newsig; + free_packet( node->pkt ); + xfree( node->pkt ); + node->pkt = newpkt; + modified = 1; + } + + xfree(user); + } + } + } + + xfree(uri); + free_secret_key( sk ); + return modified; +} + +static int +menu_set_notation(const char *string,KBNODE pub_keyblock,KBNODE sec_keyblock) +{ + PKT_secret_key *sk; /* copy of the main sk */ + PKT_public_key *main_pk; + PKT_user_id *uid; + KBNODE node; + u32 keyid[2]; + int selected, select_all; + int modified = 0; + char *answer; + struct notation *notation; + + no_primary_warning(pub_keyblock); + + if(string) + answer=xstrdup(string); + else + { + answer=cpr_get_utf8("keyedit.add_notation", + _("Enter the notation: ")); + if(answer[0]=='\0' || answer[0]=='\004') + { + xfree(answer); + return 0; + } + } + + if(ascii_strcasecmp(answer,"none")==0 + || ascii_strcasecmp(answer,"-")==0) + notation=NULL; /* delete them all */ + else + { + notation=string_to_notation(answer,0); + if(!notation) + { + xfree(answer); + return 0; + } + } + + xfree(answer); + + select_all = !count_selected_uids (pub_keyblock); + + node = find_kbnode( sec_keyblock, PKT_SECRET_KEY ); + sk = copy_secret_key( NULL, node->pkt->pkt.secret_key); - log_info(_("skipping v3 self-signature on user id \"%s\"\n"), + /* Now we can actually change the self signature(s) */ + main_pk = NULL; + uid = NULL; + selected = 0; + for ( node=pub_keyblock; node; node = node->next ) + { + if ( node->pkt->pkttype == PKT_PUBLIC_SUBKEY ) + break; /* ready */ + + if ( node->pkt->pkttype == PKT_PUBLIC_KEY ) + { + main_pk = node->pkt->pkt.public_key; + keyid_from_pk( main_pk, keyid ); + } + else if ( node->pkt->pkttype == PKT_USER_ID ) + { + uid = node->pkt->pkt.user_id; + selected = select_all || (node->flag & NODFLG_SELUID); + } + else if ( main_pk && uid && selected + && node->pkt->pkttype == PKT_SIGNATURE ) + { + PKT_signature *sig = node->pkt->pkt.signature; + if ( keyid[0] == sig->keyid[0] && keyid[1] == sig->keyid[1] + && (uid && (sig->sig_class&~3) == 0x10) + && sig->flags.chosen_selfsig) + { + char *user=utf8_to_native(uid->name,strlen(uid->name),0); + if( sig->version < 4 ) + log_info(_("skipping v3 self-signature on user ID \"%s\"\n"), user); - xfree(user); - } - else { - /* This is a selfsignature which is to be replaced - * We have to ignore v3 signatures because they are - * not able to carry the preferences */ - PKT_signature *newsig; - PACKET *newpkt; - int rc; + else + { + PKT_signature *newsig; + PACKET *newpkt; + int rc,skip=0,addonly=1; - rc = update_keysig_packet (&newsig, sig, - main_pk, uid, NULL, - sk, - keygen_add_keyserver_url, - answer ); - if( rc ) { - log_error ("update_keysig_packet failed: %s\n", - gpg_strerror (rc)); - xfree(answer); - free_secret_key( sk ); - return 0; - } - /* replace the packet */ - newpkt = xcalloc (1, sizeof *newpkt ); - newpkt->pkttype = PKT_SIGNATURE; - newpkt->pkt.signature = newsig; - free_packet( node->pkt ); - xfree (node->pkt); - node->pkt = newpkt; - modified = 1; - } - } + if(sig->flags.notation) + { + tty_printf("Current notations for user ID \"%s\":\n", + user); + tty_print_notations(-9,sig); + } + else + { + tty_printf("No notations on user ID \"%s\"\n",user); + if(notation==NULL) + { + /* There are no current notations, so there + is no point in trying to un-set them. */ + continue; + } + } + + if(notation) + { + struct notation *n; + int deleting=0; + + notation->next=sig_to_notation(sig); + + for(n=notation->next;n;n=n->next) + if(strcmp(n->name,notation->name)==0) + { + if(notation->value) + { + if(strcmp(n->value,notation->value)==0) + { + if(notation->flags.ignore) + { + /* Value match with a delete + flag. */ + n->flags.ignore=1; + deleting=1; + } + else + { + /* Adding the same notation + twice, so don't add it at + all. */ + skip=1; + tty_printf("Skipping notation:" + " %s=%s\n", + notation->name, + notation->value); + break; + } + } + } + else + { + /* No value, so it means delete. */ + n->flags.ignore=1; + deleting=1; + } + + if(n->flags.ignore) + { + tty_printf("Removing notation: %s=%s\n", + n->name,n->value); + addonly=0; + } + } + + if(!notation->flags.ignore && !skip) + tty_printf("Adding notation: %s=%s\n", + notation->name,notation->value); + + /* We tried to delete, but had no matches */ + if(notation->flags.ignore && !deleting) + continue; + } + else + { + tty_printf("Removing all notations\n"); + addonly=0; + } + + if(skip + || (!addonly + && !cpr_get_answer_is_yes("keyedit.confirm_notation", + _("Proceed? (y/N) ")))) + continue; + + rc = update_keysig_packet (&newsig, sig, + main_pk, uid, NULL, + sk, + keygen_add_notations, notation ); + if( rc ) + { + log_error ("update_keysig_packet failed: %s\n", + g10_errstr(rc)); + free_secret_key( sk ); + free_notation(notation); + xfree(user); + return 0; + } + + /* replace the packet */ + newpkt = xmalloc_clear( sizeof *newpkt ); + newpkt->pkttype = PKT_SIGNATURE; + newpkt->pkt.signature = newsig; + free_packet( node->pkt ); + xfree( node->pkt ); + node->pkt = newpkt; + modified = 1; + + if(notation) + { + /* Snip off the notation list from the sig */ + free_notation(notation->next); + notation->next=NULL; + } + + xfree(user); + } + } } } - xfree(answer); - free_secret_key( sk ); - return modified; + free_notation(notation); + free_secret_key( sk ); + return modified; } @@ -3328,6 +4411,45 @@ menu_select_uid( KBNODE keyblock, int idx ) return 1; } +/* Search in the keyblock for a uid that matches namehash */ +static int +menu_select_uid_namehash( KBNODE keyblock, const char *namehash ) +{ + byte hash[NAMEHASH_LEN]; + KBNODE node; + int i; + + assert(strlen(namehash)==NAMEHASH_LEN*2); + + for(i=0;i<NAMEHASH_LEN;i++) + hash[i]=hextobyte(&namehash[i*2]); + + for(node=keyblock->next;node;node=node->next) + { + if(node->pkt->pkttype==PKT_USER_ID) + { + namehash_from_uid(node->pkt->pkt.user_id); + if(memcmp(node->pkt->pkt.user_id->namehash,hash,NAMEHASH_LEN)==0) + { + if(node->flag&NODFLG_SELUID) + node->flag &= ~NODFLG_SELUID; + else + node->flag |= NODFLG_SELUID; + + break; + } + } + } + + if(!node) + { + tty_printf(_("No user ID with hash %s\n"),namehash); + return 0; + } + + return 1; +} + /**************** * Select secondary keys * Returns: True if the selection changed; @@ -3348,7 +4470,7 @@ menu_select_key( KBNODE keyblock, int idx ) } } if( !node ) { - tty_printf(_("No secondary key with index %d\n"), idx ); + tty_printf(_("No subkey with index %d\n"), idx ); return 0; } } @@ -3454,6 +4576,7 @@ static void ask_revoke_sig( KBNODE keyblock, KBNODE node ) { int doit=0; + PKT_user_id *uid; PKT_signature *sig = node->pkt->pkt.signature; KBNODE unode = find_prev_kbnode( keyblock, node, PKT_USER_ID ); @@ -3462,17 +4585,33 @@ ask_revoke_sig( KBNODE keyblock, KBNODE node ) return; } - tty_printf(_("user ID: \"")); - tty_print_utf8_string( unode->pkt->pkt.user_id->name, - unode->pkt->pkt.user_id->len ); + uid=unode->pkt->pkt.user_id; - if(sig->flags.exportable) - tty_printf(_("\"\nsigned with your key %08lX at %s\n"), - (ulong)sig->keyid[1], datestr_from_sig(sig) ); - else - tty_printf(_("\"\nlocally signed with your key %08lX at %s\n"), - (ulong)sig->keyid[1], datestr_from_sig(sig) ); + if(opt.with_colons) + { + if(uid->attrib_data) + printf("uat:::::::::%u %lu",uid->numattribs,uid->attrib_len); + else + { + printf("uid:::::::::"); + print_string (stdout, uid->name, uid->len, ':'); + } + printf("\n"); + + print_and_check_one_sig_colon(keyblock,node,NULL,NULL,NULL,NULL,1); + } + else + { + char *p=utf8_to_native(unode->pkt->pkt.user_id->name, + unode->pkt->pkt.user_id->len,0); + tty_printf(_("user ID: \"%s\"\n"),p); + xfree(p); + + tty_printf(_("signed by your key %s on %s%s%s\n"), + keystr(sig->keyid),datestr_from_sig(sig), + sig->flags.exportable?"":_(" (non-exportable)"),""); + } if(sig->flags.expired) { tty_printf(_("This signature expired on %s.\n"), @@ -3507,8 +4646,11 @@ menu_revsig( KBNODE keyblock ) int rc, any, skip=1, all=!count_selected_uids(keyblock); struct revocation_reason_info *reason = NULL; + assert(keyblock->pkt->pkttype==PKT_PUBLIC_KEY); + /* FIXME: detect duplicates here */ - tty_printf(_("You have signed these user IDs:\n")); + tty_printf(_("You have signed these user IDs on key %s:\n"), + keystr_from_pk(keyblock->pkt->pkt.public_key)); for( node = keyblock; node; node = node->next ) { node->flag &= ~(NODFLG_SELSIG | NODFLG_MARK_A); if( node->pkt->pkttype == PKT_USER_ID ) { @@ -3525,22 +4667,29 @@ menu_revsig( KBNODE keyblock ) } else if( !skip && node->pkt->pkttype == PKT_SIGNATURE && ((sig = node->pkt->pkt.signature), - !seckey_available(sig->keyid) ) ) { - if( (sig->sig_class&~3) == 0x10 ) { - tty_printf(_(" signed by %08lX at %s%s%s\n"), - (ulong)sig->keyid[1], datestr_from_sig(sig), - sig->flags.exportable?"":" (non-exportable)", - sig->flags.revocable?"":" (non-revocable)"); + !seckey_available(sig->keyid) ) ) + { + if( (sig->sig_class&~3) == 0x10 ) + { + tty_printf(" "); + tty_printf(_("signed by your key %s on %s%s%s\n"), + keystr(sig->keyid), datestr_from_sig(sig), + sig->flags.exportable?"":_(" (non-exportable)"), + sig->flags.revocable?"":_(" (non-revocable)")); if(sig->flags.revocable) node->flag |= NODFLG_SELSIG; - } - else if( sig->sig_class == 0x30 ) { - tty_printf(_(" revoked by %08lX at %s\n"), - (ulong)sig->keyid[1], datestr_from_sig(sig) ); - } - } + } + else if( sig->sig_class == 0x30 ) + { + tty_printf(" "); + tty_printf(_("revoked by your key %s on %s\n"), + keystr(sig->keyid),datestr_from_sig(sig)); + } + } } + tty_printf("\n"); + /* ask */ for( node = keyblock; node; node = node->next ) { if( !(node->flag & NODFLG_SELSIG) ) @@ -3565,8 +4714,9 @@ menu_revsig( KBNODE keyblock ) } else if( node->pkt->pkttype == PKT_SIGNATURE ) { sig = node->pkt->pkt.signature; - tty_printf(_(" signed by %08lX at %s%s\n"), - (ulong)sig->keyid[1], datestr_from_sig(sig), + tty_printf(" "); + tty_printf(_("signed by your key %s on %s%s%s\n"), + keystr(sig->keyid), datestr_from_sig(sig),"", sig->flags.exportable?"":_(" (non-exportable)") ); } } @@ -3602,7 +4752,7 @@ menu_revsig( KBNODE keyblock ) attrib.non_exportable=!node->pkt->pkt.signature->flags.exportable; node->flag &= ~NODFLG_MARK_A; - sk = xcalloc_secure (1, sizeof *sk ); + sk = xmalloc_secure_clear( sizeof *sk ); if( get_seckey( sk, node->pkt->pkt.signature->keyid ) ) { log_info(_("no secret key\n")); continue; @@ -3616,7 +4766,7 @@ menu_revsig( KBNODE keyblock ) &attrib ); free_secret_key(sk); if( rc ) { - log_error(_("signing failed: %s\n"), gpg_strerror (rc)); + log_error(_("signing failed: %s\n"), g10_errstr(rc)); release_revocation_reason_info( reason ); return changed; } @@ -3626,7 +4776,7 @@ menu_revsig( KBNODE keyblock ) if(primary_pk->keyid[0]==sig->keyid[0] && primary_pk->keyid[1]==sig->keyid[1]) unode->pkt->pkt.user_id->is_revoked=1; - pkt = xcalloc (1, sizeof *pkt ); + pkt = xmalloc_clear( sizeof *pkt ); pkt->pkttype = PKT_SIGNATURE; pkt->pkt.signature = sig; insert_kbnode( unode, new_kbnode(pkt), 0 ); @@ -3675,7 +4825,7 @@ menu_revuid( KBNODE pub_keyblock, KBNODE sec_keyblock ) { char *user=utf8_to_native(uid->name,uid->len,0); log_info(_("user ID \"%s\" is already revoked\n"),user); - xfree (user); + xfree(user); } else { @@ -3707,12 +4857,12 @@ menu_revuid( KBNODE pub_keyblock, KBNODE sec_keyblock ) sign_mk_attrib, &attrib ); if( rc ) { - log_error(_("signing failed: %s\n"), gpg_strerror (rc)); + log_error(_("signing failed: %s\n"), g10_errstr(rc)); goto leave; } else { - pkt = xcalloc (1, sizeof *pkt ); + pkt = xmalloc_clear( sizeof *pkt ); pkt->pkttype = PKT_SIGNATURE; pkt->pkt.signature = sig; insert_kbnode( node, new_kbnode(pkt), 0 ); @@ -3741,13 +4891,58 @@ menu_revuid( KBNODE pub_keyblock, KBNODE sec_keyblock ) } /**************** - * Revoke some of the secondary keys. - * Hmmm: Should we add a revocation to the secret keyring too? - * Does its all make sense to duplicate most of the information? + * Revoke the whole key. */ static int menu_revkey( KBNODE pub_keyblock, KBNODE sec_keyblock ) { + PKT_public_key *pk=pub_keyblock->pkt->pkt.public_key; + PKT_secret_key *sk; + int rc,changed = 0; + struct revocation_reason_info *reason; + PACKET *pkt; + PKT_signature *sig; + + if(pk->is_revoked) + { + tty_printf(_("Key %s is already revoked.\n"),keystr_from_pk(pk)); + return 0; + } + + reason = ask_revocation_reason( 1, 0, 0 ); + /* user decided to cancel */ + if( !reason ) + return 0; + + sk = copy_secret_key( NULL, sec_keyblock->pkt->pkt.secret_key ); + rc = make_keysig_packet( &sig, pk, NULL, NULL, sk, + 0x20, 0, opt.force_v4_certs?4:0, 0, 0, + revocation_reason_build_cb, reason ); + free_secret_key(sk); + if( rc ) + { + log_error(_("signing failed: %s\n"), g10_errstr(rc)); + goto scram; + } + + changed = 1; /* we changed the keyblock */ + + pkt = xmalloc_clear( sizeof *pkt ); + pkt->pkttype = PKT_SIGNATURE; + pkt->pkt.signature = sig; + insert_kbnode( pub_keyblock, new_kbnode(pkt), 0 ); + commit_kbnode( &pub_keyblock ); + + update_trust=1; + + scram: + release_revocation_reason_info( reason ); + return changed; +} + +static int +menu_revsubkey( KBNODE pub_keyblock, KBNODE sec_keyblock ) +{ PKT_public_key *mainpk; KBNODE node; int changed = 0; @@ -3770,6 +4965,13 @@ menu_revkey( KBNODE pub_keyblock, KBNODE sec_keyblock ) PKT_public_key *subpk = node->pkt->pkt.public_key; struct sign_attrib attrib; + if(subpk->is_revoked) + { + tty_printf(_("Subkey %s is already revoked.\n"), + keystr_from_pk(subpk)); + continue; + } + memset( &attrib, 0, sizeof attrib ); attrib.reason = reason; @@ -3780,13 +4982,13 @@ menu_revkey( KBNODE pub_keyblock, KBNODE sec_keyblock ) sign_mk_attrib, &attrib ); free_secret_key(sk); if( rc ) { - log_error(_("signing failed: %s\n"), gpg_strerror (rc)); + log_error(_("signing failed: %s\n"), g10_errstr(rc)); release_revocation_reason_info( reason ); return changed; } changed = 1; /* we changed the keyblock */ - pkt = xcalloc (1, sizeof *pkt ); + pkt = xmalloc_clear( sizeof *pkt ); pkt->pkttype = PKT_SIGNATURE; pkt->pkt.signature = sig; insert_kbnode( node, new_kbnode(pkt), 0 ); @@ -3833,7 +5035,6 @@ menu_showphoto( KBNODE keyblock ) int select_all = !count_selected_uids(keyblock); int count=0; PKT_public_key *pk=NULL; - u32 keyid[2]; /* Look for the public key first. We have to be really, really, explicit as to which photo this is, and what key it is a UID on @@ -3842,10 +5043,7 @@ menu_showphoto( KBNODE keyblock ) for( node = keyblock; node; node = node->next ) { if( node->pkt->pkttype == PKT_PUBLIC_KEY ) - { - pk = node->pkt->pkt.public_key; - keyid_from_pk(pk, keyid); - } + pk = node->pkt->pkt.public_key; else if( node->pkt->pkttype == PKT_USER_ID ) { PKT_user_id *uid = node->pkt->pkt.user_id; @@ -3865,9 +5063,9 @@ menu_showphoto( KBNODE keyblock ) parse_image_header(&uid->attribs[i],&type,&size)) { tty_printf(_("Displaying %s photo ID of size %ld for " - "key 0x%08lX (uid %d)\n"), + "key %s (uid %d)\n"), image_type_to_string(type,1), - (ulong)size,(ulong)keyid[1],count); + (ulong)size,keystr_from_pk(pk),count); show_photos(&uid->attribs[i],1,pk,NULL); } } diff --git a/g10/keygen.c b/g10/keygen.c index 72c5e1e8a..6a64ff317 100644 --- a/g10/keygen.c +++ b/g10/keygen.c @@ -1,6 +1,6 @@ /* keygen.c - generate a key pair - * Copyright (C) 1998, 1999, 2000, 2001, 2002, - * 2003, 2004 Free Software Foundation, Inc. + * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, + * 2006 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -16,7 +16,8 @@ * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. */ #include <config.h> @@ -26,6 +27,9 @@ #include <ctype.h> #include <errno.h> #include <assert.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> #include "gpg.h" #include "util.h" @@ -63,7 +67,9 @@ enum para_name { pPASSPHRASE, pPASSPHRASE_DEK, pPASSPHRASE_S2K, - pSERIALNO + pSERIALNO, + pBACKUPENCDIR, + pHANDLE }; struct para_data_s { @@ -87,13 +93,13 @@ struct output_control_s { struct { char *fname; char *newfname; - iobuf_t stream; + IOBUF stream; armor_filter_context_t afx; } pub; struct { char *fname; char *newfname; - iobuf_t stream; + IOBUF stream; armor_filter_context_t afx; } sec; }; @@ -115,21 +121,67 @@ static int nzip_prefs; static int mdc_available,ks_modify; static void do_generate_keypair( struct para_data_s *para, - struct output_control_s *outctrl, int card); -static int write_keyblock( iobuf_t out, KBNODE node ); -static int gen_card_key (int algo, int keyno, KBNODE pub_root, KBNODE sec_root, + struct output_control_s *outctrl, int card ); +static int write_keyblock( IOBUF out, KBNODE node ); +static int gen_card_key (int algo, int keyno, int is_primary, + KBNODE pub_root, KBNODE sec_root, u32 expireval, struct para_data_s *para); +static int gen_card_key_with_backup (int algo, int keyno, int is_primary, + KBNODE pub_root, KBNODE sec_root, + u32 expireval, struct para_data_s *para, + const char *backup_dir); + + +static void +print_status_key_created (int letter, PKT_public_key *pk, const char *handle) +{ + byte array[MAX_FINGERPRINT_LEN], *s; + char *buf, *p; + size_t i, n; + + if (!handle) + handle = ""; + + buf = xmalloc (MAX_FINGERPRINT_LEN*2+31 + strlen (handle) + 1); + + p = buf; + if (letter || pk) + { + *p++ = letter; + *p++ = ' '; + fingerprint_from_pk (pk, array, &n); + s = array; + for (i=0; i < n ; i++, s++, p += 2) + sprintf (p, "%02X", *s); + } + if (*handle) + { + *p++ = ' '; + for (i=0; handle[i] && i < 100; i++) + *p++ = isspace ((unsigned int)handle[i])? '_':handle[i]; + } + *p = 0; + write_status_text ((letter || pk)?STATUS_KEY_CREATED:STATUS_KEY_NOT_CREATED, + buf); + xfree (buf); +} + +static void +print_status_key_not_created (const char *handle) +{ + print_status_key_created (0, NULL, handle); +} static void write_uid( KBNODE root, const char *s ) { - PACKET *pkt = xcalloc (1,sizeof *pkt ); + PACKET *pkt = xmalloc_clear(sizeof *pkt ); size_t n = strlen(s); pkt->pkttype = PKT_USER_ID; - pkt->pkt.user_id = xcalloc (1, sizeof *pkt->pkt.user_id + n - 1 ); + pkt->pkt.user_id = xmalloc_clear( sizeof *pkt->pkt.user_id + n - 1 ); pkt->pkt.user_id->len = n; pkt->pkt.user_id->ref = 1; strcpy(pkt->pkt.user_id->name, s); @@ -141,21 +193,22 @@ do_add_key_flags (PKT_signature *sig, unsigned int use) { byte buf[1]; - if (!use) - return; - buf[0] = 0; + + /* The spec says that all primary keys MUST be able to certify. */ + if(sig->sig_class!=0x18) + buf[0] |= 0x01; + if (use & PUBKEY_USAGE_SIG) - { - if(sig->sig_class==0x18) - buf[0] |= 0x02; /* Don't set the certify flag for subkeys */ - else - buf[0] |= 0x01 | 0x02; - } + buf[0] |= 0x02; if (use & PUBKEY_USAGE_ENC) buf[0] |= 0x04 | 0x08; if (use & PUBKEY_USAGE_AUTH) buf[0] |= 0x20; + + if (!buf[0]) + return; + build_sig_subpkt (sig, SIGSUBPKT_KEY_FLAGS, buf, 1); } @@ -228,18 +281,6 @@ set_one_pref (int val, int type, const char *item, byte *buf, int *nbuf) return 0; } -#ifdef USE_AES -#define AES "S9 S8 S7 " -#else -#define AES "" -#endif - -#ifdef USE_CAST5 -#define CAST5 "S3 " -#else -#define CAST5 "" -#endif - /* * Parse the supplied string and use it to set the standard * preferences. The string may be in a form like the one printed by @@ -253,23 +294,71 @@ keygen_set_std_prefs (const char *string,int personal) byte sym[MAX_PREFS], hash[MAX_PREFS], zip[MAX_PREFS]; int nsym=0, nhash=0, nzip=0, val, rc=0; int mdc=1, modify=0; /* mdc defaults on, modify defaults off. */ + char dummy_string[45+1]; /* Enough for 15 items. */ - if (!string || !ascii_strcasecmp (string, "default")) { - if (opt.def_preference_list) - string=opt.def_preference_list; - else if ( !openpgp_cipher_test_algo(CIPHER_ALGO_IDEA) ) - string = AES CAST5 "S2 S1 H2 H3 Z2 Z1"; - else - string = AES CAST5 "S2 H2 H3 Z2 Z1"; - - /* If we have it, IDEA goes *after* 3DES so it won't be used - unless we're encrypting along with a V3 key. Ideally, we - would only put the S1 preference in if the key was RSA and - <=2048 bits, as that is what won't break PGP2, but that is - difficult with the current code, and not really worth - checking as a non-RSA <=2048 bit key wouldn't be usable by - PGP2 anyway. -dms */ - } + if (!string || !ascii_strcasecmp (string, "default")) + { + if (opt.def_preference_list) + string=opt.def_preference_list; + else + { + dummy_string[0]='\0'; + + /* The rationale why we use the order AES256,192,128 is + for compatibility reasons with PGP. If gpg would + define AES128 first, we would get the somewhat + confusing situation: + + gpg -r pgpkey -r gpgkey ---gives--> AES256 + gpg -r gpgkey -r pgpkey ---gives--> AES + + Note that by using --personal-cipher-preferences it is + possible to prefer AES128. + */ + + /* Make sure we do not add more than 15 items here, as we + could overflow the size of dummy_string. We currently + have at most 12. */ + if(!check_cipher_algo(CIPHER_ALGO_AES256)) + strcat(dummy_string,"S9 "); + if(!check_cipher_algo(CIPHER_ALGO_AES192)) + strcat(dummy_string,"S8 "); + if(!check_cipher_algo(CIPHER_ALGO_AES)) + strcat(dummy_string,"S7 "); + if(!check_cipher_algo(CIPHER_ALGO_CAST5)) + strcat(dummy_string,"S3 "); + strcat(dummy_string,"S2 "); /* 3DES */ + /* If we have it, IDEA goes *after* 3DES so it won't be + used unless we're encrypting along with a V3 key. + Ideally, we would only put the S1 preference in if the + key was RSA and <=2048 bits, as that is what won't + break PGP2, but that is difficult with the current + code, and not really worth checking as a non-RSA <=2048 + bit key wouldn't be usable by PGP2 anyway. -dms */ + if(!check_cipher_algo(CIPHER_ALGO_IDEA)) + strcat(dummy_string,"S1 "); + + /* SHA-1 */ + strcat(dummy_string,"H2 "); + + if(!check_digest_algo(DIGEST_ALGO_SHA256)) + strcat(dummy_string,"H8 "); + + /* RIPEMD160 */ + strcat(dummy_string,"H3 "); + + /* ZLIB */ + strcat(dummy_string,"Z2 "); + + if(!check_compress_algo(COMPRESS_ALGO_BZIP2)) + strcat(dummy_string,"Z3 "); + + /* ZIP */ + strcat(dummy_string,"Z1"); + + string=dummy_string; + } + } else if (!ascii_strcasecmp (string, "none")) string = ""; @@ -277,16 +366,16 @@ keygen_set_std_prefs (const char *string,int personal) { char *tok,*prefstring; - prefstring=xstrdup (string); /* need a writable string! */ + prefstring=xstrdup(string); /* need a writable string! */ while((tok=strsep(&prefstring," ,"))) { - if((val=openpgp_cipher_map_name(tok))) + if((val=string_to_cipher_algo(tok))) { if(set_one_pref(val,1,tok,sym,&nsym)) rc=-1; } - else if((val=openpgp_md_map_name(tok))) + else if((val=string_to_digest_algo(tok))) { if(set_one_pref(val,2,tok,hash,&nhash)) rc=-1; @@ -317,7 +406,7 @@ keygen_set_std_prefs (const char *string,int personal) } } - xfree (prefstring); + xfree(prefstring); } if(!rc) @@ -326,7 +415,7 @@ keygen_set_std_prefs (const char *string,int personal) { if(personal==PREFTYPE_SYM) { - xfree (opt.personal_cipher_prefs); + xfree(opt.personal_cipher_prefs); if(nsym==0) opt.personal_cipher_prefs=NULL; @@ -335,7 +424,7 @@ keygen_set_std_prefs (const char *string,int personal) int i; opt.personal_cipher_prefs= - xmalloc (sizeof(prefitem_t *)*(nsym+1)); + xmalloc(sizeof(prefitem_t *)*(nsym+1)); for (i=0; i<nsym; i++) { @@ -349,7 +438,7 @@ keygen_set_std_prefs (const char *string,int personal) } else if(personal==PREFTYPE_HASH) { - xfree (opt.personal_digest_prefs); + xfree(opt.personal_digest_prefs); if(nhash==0) opt.personal_digest_prefs=NULL; @@ -358,7 +447,7 @@ keygen_set_std_prefs (const char *string,int personal) int i; opt.personal_digest_prefs= - xmalloc (sizeof(prefitem_t *)*(nhash+1)); + xmalloc(sizeof(prefitem_t *)*(nhash+1)); for (i=0; i<nhash; i++) { @@ -372,7 +461,7 @@ keygen_set_std_prefs (const char *string,int personal) } else if(personal==PREFTYPE_ZIP) { - xfree (opt.personal_compress_prefs); + xfree(opt.personal_compress_prefs); if(nzip==0) opt.personal_compress_prefs=NULL; @@ -381,7 +470,7 @@ keygen_set_std_prefs (const char *string,int personal) int i; opt.personal_compress_prefs= - xmalloc (sizeof(prefitem_t *)*(nzip+1)); + xmalloc(sizeof(prefitem_t *)*(nzip+1)); for (i=0; i<nzip; i++) { @@ -408,20 +497,19 @@ keygen_set_std_prefs (const char *string,int personal) return rc; } -#undef CAST5 -#undef AES - /* Return a fake user ID containing the preferences. Caller must free. */ PKT_user_id *keygen_get_std_prefs(void) { int i,j=0; - PKT_user_id *uid=xcalloc (1,sizeof(PKT_user_id)); + PKT_user_id *uid=xmalloc_clear(sizeof(PKT_user_id)); if(!prefs_initialized) keygen_set_std_prefs(NULL,0); - uid->prefs=xmalloc ((sizeof(prefitem_t *)* + uid->ref=1; + + uid->prefs=xmalloc((sizeof(prefitem_t *)* (nsym_prefs+nhash_prefs+nzip_prefs+1))); for(i=0;i<nsym_prefs;i++,j++) @@ -445,8 +533,8 @@ PKT_user_id *keygen_get_std_prefs(void) uid->prefs[j].type=PREFTYPE_NONE; uid->prefs[j].value=0; - uid->mdc_feature=mdc_available; - uid->ks_modify=ks_modify; + uid->flags.mdc=mdc_available; + uid->flags.ks_modify=ks_modify; return uid; } @@ -467,7 +555,7 @@ add_feature_mdc (PKT_signature *sig,int enabled) if (!s || !n) { /* create a new one */ n = 1; - buf = xcalloc (1,n); + buf = xmalloc_clear (n); } else { buf = xmalloc (n); @@ -511,7 +599,7 @@ add_keyserver_modify (PKT_signature *sig,int enabled) if (!s || !n) { /* create a new one */ n = 1; - buf = xcalloc (1,n); + buf = xmalloc_clear (n); } else { buf = xmalloc (n); @@ -591,17 +679,67 @@ keygen_add_std_prefs( PKT_signature *sig, void *opaque ) return 0; } - int keygen_add_keyserver_url(PKT_signature *sig, void *opaque) { const char *url=opaque; - build_sig_subpkt(sig,SIGSUBPKT_PREF_KS,url,strlen(url)); + if(url) + build_sig_subpkt(sig,SIGSUBPKT_PREF_KS,url,strlen(url)); + else + delete_sig_subpkt (sig->hashed,SIGSUBPKT_PREF_KS); return 0; } +int +keygen_add_notations(PKT_signature *sig,void *opaque) +{ + struct notation *notation; + + /* We always start clean */ + delete_sig_subpkt(sig->hashed,SIGSUBPKT_NOTATION); + delete_sig_subpkt(sig->unhashed,SIGSUBPKT_NOTATION); + sig->flags.notation=0; + + for(notation=opaque;notation;notation=notation->next) + if(!notation->flags.ignore) + { + unsigned char *buf; + unsigned int n1,n2; + + n1=strlen(notation->name); + if(notation->altvalue) + n2=strlen(notation->altvalue); + else if(notation->bdat) + n2=notation->blen; + else + n2=strlen(notation->value); + + buf = xmalloc( 8 + n1 + n2 ); + + /* human readable or not */ + buf[0] = notation->bdat?0:0x80; + buf[1] = buf[2] = buf[3] = 0; + buf[4] = n1 >> 8; + buf[5] = n1; + buf[6] = n2 >> 8; + buf[7] = n2; + memcpy(buf+8, notation->name, n1 ); + if(notation->altvalue) + memcpy(buf+8+n1, notation->altvalue, n2 ); + else if(notation->bdat) + memcpy(buf+8+n1, notation->bdat, n2 ); + else + memcpy(buf+8+n1, notation->value, n2 ); + build_sig_subpkt( sig, SIGSUBPKT_NOTATION | + (notation->flags.critical?SIGSUBPKT_FLAG_CRITICAL:0), + buf, 8+n1+n2 ); + xfree(buf); + } + + return 0; +} int keygen_add_revkey(PKT_signature *sig, void *opaque) @@ -625,6 +763,96 @@ keygen_add_revkey(PKT_signature *sig, void *opaque) return 0; } +int +make_backsig(PKT_signature *sig,PKT_public_key *pk, + PKT_public_key *sub_pk,PKT_secret_key *sub_sk) +{ + PKT_signature *backsig; + int rc; + + cache_public_key(sub_pk); + + rc=make_keysig_packet(&backsig,pk,NULL,sub_pk,sub_sk,0x19,0,0,0,0,NULL,NULL); + if(rc) + log_error("make_keysig_packet failed for backsig: %s\n",g10_errstr(rc)); + else + { + /* get it into a binary packed form. */ + IOBUF backsig_out=iobuf_temp(); + PACKET backsig_pkt; + + init_packet(&backsig_pkt); + backsig_pkt.pkttype=PKT_SIGNATURE; + backsig_pkt.pkt.signature=backsig; + rc=build_packet(backsig_out,&backsig_pkt); + free_packet(&backsig_pkt); + if(rc) + log_error("build_packet failed for backsig: %s\n",g10_errstr(rc)); + else + { + size_t pktlen=0; + byte *buf=iobuf_get_temp_buffer(backsig_out); + + /* Remove the packet header */ + if(buf[0]&0x40) + { + if(buf[1]<192) + { + pktlen=buf[1]; + buf+=2; + } + else if(buf[1]<224) + { + pktlen=(buf[1]-192)*256; + pktlen+=buf[2]+192; + buf+=3; + } + else if(buf[1]==255) + { + pktlen =buf[2] << 24; + pktlen|=buf[3] << 16; + pktlen|=buf[4] << 8; + pktlen|=buf[5]; + buf+=6; + } + else + BUG(); + } + else + { + int mark=1; + + switch(buf[0]&3) + { + case 3: + BUG(); + break; + + case 2: + pktlen =buf[mark++] << 24; + pktlen|=buf[mark++] << 16; + + case 1: + pktlen|=buf[mark++] << 8; + + case 0: + pktlen|=buf[mark++]; + } + + buf+=mark; + } + + /* now make the binary blob into a subpacket */ + build_sig_subpkt(sig,SIGSUBPKT_SIGNATURE,buf,pktlen); + + iobuf_close(backsig_out); + } + } + + return rc; +} + + static int write_direct_sig( KBNODE root, KBNODE pub_root, PKT_secret_key *sk, struct revocation_key *revkey ) @@ -652,11 +880,11 @@ write_direct_sig( KBNODE root, KBNODE pub_root, PKT_secret_key *sk, rc = make_keysig_packet(&sig,pk,NULL,NULL,sk,0x1F,0,0,0,0, keygen_add_revkey,revkey); if( rc ) { - log_error("make_keysig_packet failed: %s\n", gpg_strerror (rc) ); + log_error("make_keysig_packet failed: %s\n", g10_errstr(rc) ); return rc; } - pkt = xcalloc (1, sizeof *pkt ); + pkt = xmalloc_clear( sizeof *pkt ); pkt->pkttype = PKT_SIGNATURE; pkt->pkt.signature = sig; add_kbnode( root, new_kbnode( pkt ) ); @@ -664,8 +892,8 @@ write_direct_sig( KBNODE root, KBNODE pub_root, PKT_secret_key *sk, } static int -write_selfsig( KBNODE root, KBNODE pub_root, PKT_secret_key *sk, - unsigned int use ) +write_selfsigs( KBNODE sec_root, KBNODE pub_root, PKT_secret_key *sk, + unsigned int use ) { PACKET *pkt; PKT_signature *sig; @@ -678,7 +906,7 @@ write_selfsig( KBNODE root, KBNODE pub_root, PKT_secret_key *sk, log_info(_("writing self signature\n")); /* get the uid packet from the list */ - node = find_kbnode( root, PKT_USER_ID ); + node = find_kbnode( pub_root, PKT_USER_ID ); if( !node ) BUG(); /* no user id packet in tree */ uid = node->pkt->pkt.user_id; @@ -696,26 +924,33 @@ write_selfsig( KBNODE root, KBNODE pub_root, PKT_secret_key *sk, rc = make_keysig_packet( &sig, pk, uid, NULL, sk, 0x13, 0, 0, 0, 0, keygen_add_std_prefs, pk ); if( rc ) { - log_error("make_keysig_packet failed: %s\n", gpg_strerror (rc) ); + log_error("make_keysig_packet failed: %s\n", g10_errstr(rc) ); return rc; } - pkt = xcalloc (1, sizeof *pkt ); + pkt = xmalloc_clear( sizeof *pkt ); pkt->pkttype = PKT_SIGNATURE; pkt->pkt.signature = sig; - add_kbnode( root, new_kbnode( pkt ) ); + add_kbnode( sec_root, new_kbnode( pkt ) ); + + pkt = xmalloc_clear( sizeof *pkt ); + pkt->pkttype = PKT_SIGNATURE; + pkt->pkt.signature = copy_signature(NULL,sig); + add_kbnode( pub_root, new_kbnode( pkt ) ); return rc; } +/* sub_sk is currently unused (reserved for backsigs) */ static int -write_keybinding( KBNODE root, KBNODE pub_root, PKT_secret_key *sk, +write_keybinding( KBNODE root, KBNODE pub_root, + PKT_secret_key *pri_sk, PKT_secret_key *sub_sk, unsigned int use ) { PACKET *pkt; PKT_signature *sig; int rc=0; KBNODE node; - PKT_public_key *pk, *subpk; + PKT_public_key *pri_pk, *sub_pk; struct opaque_data_usage_and_pk oduap; if( opt.verbose ) @@ -725,31 +960,39 @@ write_keybinding( KBNODE root, KBNODE pub_root, PKT_secret_key *sk, node = find_kbnode( pub_root, PKT_PUBLIC_KEY ); if( !node ) BUG(); - pk = node->pkt->pkt.public_key; + pri_pk = node->pkt->pkt.public_key; /* we have to cache the key, so that the verification of the signature * creation is able to retrieve the public key */ - cache_public_key (pk); + cache_public_key (pri_pk); /* find the last subkey */ - subpk = NULL; + sub_pk = NULL; for(node=pub_root; node; node = node->next ) { if( node->pkt->pkttype == PKT_PUBLIC_SUBKEY ) - subpk = node->pkt->pkt.public_key; + sub_pk = node->pkt->pkt.public_key; } - if( !subpk ) + if( !sub_pk ) BUG(); /* and make the signature */ oduap.usage = use; - oduap.pk = subpk; - rc = make_keysig_packet( &sig, pk, NULL, subpk, sk, 0x18, 0, 0, 0, 0, - keygen_add_key_flags_and_expire, &oduap ); + oduap.pk = sub_pk; + rc=make_keysig_packet(&sig, pri_pk, NULL, sub_pk, pri_sk, 0x18, 0, 0, 0, 0, + keygen_add_key_flags_and_expire, &oduap ); if( rc ) { - log_error("make_keysig_packet failed: %s\n", gpg_strerror (rc) ); + log_error("make_keysig_packet failed: %s\n", g10_errstr(rc) ); return rc; } - pkt = xcalloc (1, sizeof *pkt ); + /* make a backsig */ + if(use&PUBKEY_USAGE_SIG) + { + rc=make_backsig(sig,pri_pk,sub_pk,sub_sk); + if(rc) + return rc; + } + + pkt = xmalloc_clear( sizeof *pkt ); pkt->pkttype = PKT_SIGNATURE; pkt->pkt.signature = sig; add_kbnode( root, new_kbnode( pkt ) ); @@ -757,6 +1000,7 @@ write_keybinding( KBNODE root, KBNODE pub_root, PKT_secret_key *sk, } + static int key_from_sexp (gcry_mpi_t *array, gcry_sexp_t sexp, const char *topname, const char *elems) @@ -855,101 +1099,113 @@ genhelp_factors (gcry_sexp_t misc_key_info, KBNODE sec_root) static int gen_elg(int algo, unsigned int nbits, KBNODE pub_root, KBNODE sec_root, DEK *dek, - STRING2KEY *s2k, PKT_secret_key **ret_sk, u32 expireval ) + STRING2KEY *s2k, PKT_secret_key **ret_sk, u32 expireval, + int is_subkey) { - int rc; - PACKET *pkt; - PKT_secret_key *sk; - PKT_public_key *pk; - gcry_sexp_t s_parms, s_key; - gcry_sexp_t misc_key_info; - - assert (is_ELGAMAL(algo)); + int rc; + PACKET *pkt; + PKT_secret_key *sk; + PKT_public_key *pk; + gcry_sexp_t s_parms, s_key; + gcry_sexp_t misc_key_info; - if (nbits < 512) - { - nbits = 1024; - log_info (_("keysize invalid; using %u bits\n"), nbits); + assert( is_ELGAMAL(algo) ); + + if( nbits < 512 ) { + nbits = 1024; + log_info(_("keysize invalid; using %u bits\n"), nbits ); } - if ((nbits % 32)) - { - nbits = ((nbits + 31) / 32) * 32; - log_info (_("keysize rounded up to %u bits\n"), nbits); + if( (nbits % 32) ) { + nbits = ((nbits + 31) / 32) * 32; + log_info(_("keysize rounded up to %u bits\n"), nbits ); } - rc = gcry_sexp_build ( &s_parms, NULL, - "(genkey(%s(nbits %d)))", - algo == GCRY_PK_ELG_E ? "openpgp-elg" : - algo == GCRY_PK_ELG ? "elg" : "x-oops" , - (int)nbits); - if (rc) - log_bug ("gcry_sexp_build failed: %s\n", gpg_strerror (rc)); + + rc = gcry_sexp_build ( &s_parms, NULL, + "(genkey(%s(nbits %d)))", + algo == GCRY_PK_ELG_E ? "openpgp-elg" : + algo == GCRY_PK_ELG ? "elg" : "x-oops" , + (int)nbits); + if (rc) + log_bug ("gcry_sexp_build failed: %s\n", gpg_strerror (rc)); - rc = gcry_pk_genkey (&s_key, s_parms); - gcry_sexp_release (s_parms); - if (rc) - { - log_error ("gcry_pk_genkey failed: %s\n", gpg_strerror (rc) ); - return rc; - } + rc = gcry_pk_genkey (&s_key, s_parms); + gcry_sexp_release (s_parms); + if (rc) + { + log_error ("gcry_pk_genkey failed: %s\n", gpg_strerror (rc) ); + return rc; + } - sk = xcalloc (1, sizeof *sk); - pk = xcalloc (1, sizeof *pk); - sk->timestamp = pk->timestamp = make_timestamp(); - sk->version = pk->version = 4; - if (expireval) - sk->expiredate = pk->expiredate = sk->timestamp + expireval; - sk->pubkey_algo = pk->pubkey_algo = algo; + sk = xmalloc_clear( sizeof *sk ); + pk = xmalloc_clear( sizeof *pk ); + sk->timestamp = pk->timestamp = make_timestamp(); + sk->version = pk->version = 4; + if( expireval ) { + sk->expiredate = pk->expiredate = sk->timestamp + expireval; + } + sk->pubkey_algo = pk->pubkey_algo = algo; +/* pk->pkey[0] = mpi_copy( skey[0] ); */ +/* pk->pkey[1] = mpi_copy( skey[1] ); */ +/* pk->pkey[2] = mpi_copy( skey[2] ); */ +/* sk->skey[0] = skey[0]; */ +/* sk->skey[1] = skey[1]; */ +/* sk->skey[2] = skey[2]; */ +/* sk->skey[3] = skey[3]; */ + + rc = key_from_sexp (pk->pkey, s_key, "public-key", "pgy"); + if (rc) + { + log_error ("key_from_sexp failed: %s\n", gpg_strerror (rc) ); + gcry_sexp_release (s_key); + free_secret_key (sk); + free_public_key (pk); + return rc; + } + rc = key_from_sexp (sk->skey, s_key, "private-key", "pgyx"); + if (rc) + { + log_error("key_from_sexp failed: %s\n", gpg_strerror (rc) ); + gcry_sexp_release (s_key); + free_secret_key (sk); + free_public_key (pk); + return rc; + } + misc_key_info = gcry_sexp_find_token (s_key, "misc-key-info", 0); + gcry_sexp_release (s_key); + + sk->is_protected = 0; + sk->protect.algo = 0; - rc = key_from_sexp (pk->pkey, s_key, "public-key", "pgy"); - if (rc) - { - log_error ("key_from_sexp failed: %s\n", gpg_strerror (rc) ); - gcry_sexp_release (s_key); - return rc; - } - rc = key_from_sexp (sk->skey, s_key, "private-key", "pgyx"); - if (rc) - { - log_error("key_from_sexp failed: %s\n", gpg_strerror (rc) ); - gcry_sexp_release (s_key); - return rc; - } - misc_key_info = gcry_sexp_find_token (s_key, "misc-key-info", 0); - gcry_sexp_release (s_key); + sk->csum = checksum_mpi( sk->skey[3] ); + if( ret_sk ) /* return an unprotected version of the sk */ + *ret_sk = copy_secret_key( NULL, sk ); - sk->is_protected = 0; - sk->protect.algo = 0; + rc = genhelp_protect (dek, s2k, sk); + if (rc) + { + free_public_key (pk); + free_secret_key (sk); + gcry_sexp_release (misc_key_info); + return rc; + } - sk->csum = checksum_mpi (sk->skey[3]); - if (ret_sk) /* not a subkey: return an unprotected version of the sk */ - *ret_sk = copy_secret_key (NULL, sk); + pkt = xmalloc_clear (sizeof *pkt); + pkt->pkttype = is_subkey ? PKT_PUBLIC_SUBKEY : PKT_PUBLIC_KEY; + pkt->pkt.public_key = pk; + add_kbnode(pub_root, new_kbnode( pkt )); - rc = genhelp_protect (dek, s2k, sk); - if (rc) - { - free_public_key (pk); - free_secret_key (sk); - gcry_sexp_release (misc_key_info); - return rc; - } + /* Don't know whether it makes sense to have the factors, so for now + * we store them in the secret keyring (but they are not secret). */ + pkt = xmalloc_clear(sizeof *pkt); + pkt->pkttype = is_subkey ? PKT_SECRET_SUBKEY : PKT_SECRET_KEY; + pkt->pkt.secret_key = sk; + add_kbnode(sec_root, new_kbnode( pkt )); - pkt = xcalloc (1,sizeof *pkt); - pkt->pkttype = ret_sk ? PKT_PUBLIC_KEY : PKT_PUBLIC_SUBKEY; - pkt->pkt.public_key = pk; - add_kbnode(pub_root, new_kbnode( pkt )); - - /* don't know whether it makes sense to have the factors, so for now - * we store them in the secret keyring (but they are not secret) */ - pkt = xcalloc (1,sizeof *pkt); - pkt->pkttype = ret_sk ? PKT_SECRET_KEY : PKT_SECRET_SUBKEY; - pkt->pkt.secret_key = sk; - add_kbnode(sec_root, new_kbnode( pkt )); - - genhelp_factors (misc_key_info, sec_root); - - return 0; + genhelp_factors (misc_key_info, sec_root); + + return 0; } @@ -958,95 +1214,104 @@ gen_elg(int algo, unsigned int nbits, */ static int gen_dsa (unsigned int nbits, KBNODE pub_root, KBNODE sec_root, DEK *dek, - STRING2KEY *s2k, PKT_secret_key **ret_sk, u32 expireval ) + STRING2KEY *s2k, PKT_secret_key **ret_sk, u32 expireval, int is_subkey) { - int rc; - PACKET *pkt; - PKT_secret_key *sk; - PKT_public_key *pk; - gcry_sexp_t s_parms, s_key; - gcry_sexp_t misc_key_info; + int rc; + PACKET *pkt; + PKT_secret_key *sk; + PKT_public_key *pk; + gcry_sexp_t s_parms, s_key; + gcry_sexp_t misc_key_info; - if (nbits > 1024 || nbits < 512) - { - nbits = 1024; - log_info(_("keysize invalid; using %u bits\n"), nbits); + if( nbits > 1024 || nbits < 512 ) { + nbits = 1024; + log_info(_("keysize invalid; using %u bits\n"), nbits ); } - if ((nbits % 64)) - { - nbits = ((nbits + 63) / 64) * 64; - log_info (_("keysize rounded up to %u bits\n"), nbits); + if( (nbits % 64) ) { + nbits = ((nbits + 63) / 64) * 64; + log_info(_("keysize rounded up to %u bits\n"), nbits ); } - rc = gcry_sexp_build (&s_parms, NULL, - "(genkey(dsa(nbits %d)))", - (int)nbits); - if (rc) - log_bug ("gcry_sexp_build failed: %s\n", gpg_strerror (rc)); + rc = gcry_sexp_build (&s_parms, NULL, + "(genkey(dsa(nbits %d)))", + (int)nbits); + if (rc) + log_bug ("gcry_sexp_build failed: %s\n", gpg_strerror (rc)); - rc = gcry_pk_genkey (&s_key, s_parms); - gcry_sexp_release (s_parms); - if (rc) - { - log_error ("gcry_pk_genkey failed: %s\n", gpg_strerror (rc) ); - return rc; - } - - sk = xcalloc (1, sizeof *sk ); - pk = xcalloc (1, sizeof *pk ); - sk->timestamp = pk->timestamp = make_timestamp(); - sk->version = pk->version = 4; - if (expireval) - sk->expiredate = pk->expiredate = sk->timestamp + expireval; - sk->pubkey_algo = pk->pubkey_algo = PUBKEY_ALGO_DSA; + rc = gcry_pk_genkey (&s_key, s_parms); + gcry_sexp_release (s_parms); + if (rc) + { + log_error ("gcry_pk_genkey failed: %s\n", gpg_strerror (rc) ); + return rc; + } - rc = key_from_sexp (pk->pkey, s_key, "public-key", "pqgy"); - if (rc) - { - log_error ("key_from_sexp failed: %s\n", gpg_strerror (rc)); - gcry_sexp_release (s_key); - return rc; + sk = xmalloc_clear( sizeof *sk ); + pk = xmalloc_clear( sizeof *pk ); + sk->timestamp = pk->timestamp = make_timestamp(); + sk->version = pk->version = 4; + if( expireval ) { + sk->expiredate = pk->expiredate = sk->timestamp + expireval; } - rc = key_from_sexp (sk->skey, s_key, "private-key", "pqgyx"); - if (rc) - { - log_error ("key_from_sexp failed: %s\n", gpg_strerror (rc) ); - gcry_sexp_release (s_key); - return rc; - } - misc_key_info = gcry_sexp_find_token (s_key, "misc-key-info", 0); - gcry_sexp_release (s_key); + sk->pubkey_algo = pk->pubkey_algo = PUBKEY_ALGO_DSA; - sk->is_protected = 0; - sk->protect.algo = 0; + rc = key_from_sexp (pk->pkey, s_key, "public-key", "pqgy"); + if (rc) + { + log_error ("key_from_sexp failed: %s\n", gpg_strerror (rc)); + gcry_sexp_release (s_key); + free_public_key(pk); + free_secret_key(sk); + return rc; + } + rc = key_from_sexp (sk->skey, s_key, "private-key", "pqgyx"); + if (rc) + { + log_error ("key_from_sexp failed: %s\n", gpg_strerror (rc) ); + gcry_sexp_release (s_key); + free_public_key(pk); + free_secret_key(sk); + return rc; + } + misc_key_info = gcry_sexp_find_token (s_key, "misc-key-info", 0); + gcry_sexp_release (s_key); + + sk->is_protected = 0; + sk->protect.algo = 0; - sk->csum = checksum_mpi ( sk->skey[4] ); - if (ret_sk) /* not a subkey: return an unprotected version of the sk */ - *ret_sk = copy_secret_key( NULL, sk ); + sk->csum = checksum_mpi ( sk->skey[4] ); + if( ret_sk ) /* return an unprotected version of the sk */ + *ret_sk = copy_secret_key( NULL, sk ); - rc = genhelp_protect (dek, s2k, sk); - if (rc) - { - free_public_key (pk); - free_secret_key (sk); - gcry_sexp_release (misc_key_info); - return rc; - } + rc = genhelp_protect (dek, s2k, sk); + if (rc) + { + free_public_key (pk); + free_secret_key (sk); + gcry_sexp_release (misc_key_info); + return rc; + } - pkt = xcalloc (1,sizeof *pkt); - pkt->pkttype = ret_sk ? PKT_PUBLIC_KEY : PKT_PUBLIC_SUBKEY; - pkt->pkt.public_key = pk; - add_kbnode(pub_root, new_kbnode( pkt )); + pkt = xmalloc_clear(sizeof *pkt); + pkt->pkttype = is_subkey ? PKT_PUBLIC_SUBKEY : PKT_PUBLIC_KEY; + pkt->pkt.public_key = pk; + add_kbnode(pub_root, new_kbnode( pkt )); - pkt = xcalloc (1,sizeof *pkt); - pkt->pkttype = ret_sk ? PKT_SECRET_KEY : PKT_SECRET_SUBKEY; - pkt->pkt.secret_key = sk; - add_kbnode(sec_root, new_kbnode( pkt )); + /* Don't know whether it makes sense to have the factors, so for now + * we store them in the secret keyring (but they are not secret) + * p = 2 * q * f1 * f2 * ... * fn + * We store only f1 to f_n-1; fn can be calculated because p and q + * are known. + */ + pkt = xmalloc_clear(sizeof *pkt); + pkt->pkttype = is_subkey ? PKT_SECRET_SUBKEY : PKT_SECRET_KEY; + pkt->pkt.secret_key = sk; + add_kbnode(sec_root, new_kbnode( pkt )); - genhelp_factors (misc_key_info, sec_root); + genhelp_factors (misc_key_info, sec_root); - return 0; + return 0; } @@ -1055,95 +1320,98 @@ gen_dsa (unsigned int nbits, KBNODE pub_root, KBNODE sec_root, DEK *dek, */ static int gen_rsa(int algo, unsigned nbits, KBNODE pub_root, KBNODE sec_root, DEK *dek, - STRING2KEY *s2k, PKT_secret_key **ret_sk, u32 expireval ) + STRING2KEY *s2k, PKT_secret_key **ret_sk, u32 expireval, int is_subkey) { - int rc; - PACKET *pkt; - PKT_secret_key *sk; - PKT_public_key *pk; - gcry_sexp_t s_parms, s_key; + int rc; + PACKET *pkt; + PKT_secret_key *sk; + PKT_public_key *pk; + gcry_sexp_t s_parms, s_key; - assert (is_RSA(algo)); + assert( is_RSA(algo) ); - if (nbits < 1024) - { - nbits = 1024; - log_info(_("keysize invalid; using %u bits\n"), nbits); + if( nbits < 1024 ) { + nbits = 1024; + log_info(_("keysize invalid; using %u bits\n"), nbits ); } - if ((nbits % 32)) - { - nbits = ((nbits + 31) / 32) * 32; - log_info (_("keysize rounded up to %u bits\n"), nbits); + if( (nbits % 32) ) { + nbits = ((nbits + 31) / 32) * 32; + log_info(_("keysize rounded up to %u bits\n"), nbits ); } - rc = gcry_sexp_build (&s_parms, NULL, - "(genkey(rsa(nbits %d)))", - (int)nbits); - if (rc) - log_bug ("gcry_sexp_build failed: %s\n", gpg_strerror (rc)); + rc = gcry_sexp_build (&s_parms, NULL, + "(genkey(rsa(nbits %d)))", + (int)nbits); + if (rc) + log_bug ("gcry_sexp_build failed: %s\n", gpg_strerror (rc)); - rc = gcry_pk_genkey (&s_key, s_parms); - gcry_sexp_release (s_parms); - if (rc) - { - log_error ("gcry_pk_genkey failed: %s\n", gpg_strerror (rc) ); - return rc; - } - - sk = xcalloc (1, sizeof *sk ); - pk = xcalloc (1, sizeof *pk ); - sk->timestamp = pk->timestamp = make_timestamp(); - sk->version = pk->version = 4; - if (expireval) - sk->expiredate = pk->expiredate = sk->timestamp + expireval; - sk->pubkey_algo = pk->pubkey_algo = algo; + rc = gcry_pk_genkey (&s_key, s_parms); + gcry_sexp_release (s_parms); + if (rc) + { + log_error ("gcry_pk_genkey failed: %s\n", gpg_strerror (rc) ); + return rc; + } - rc = key_from_sexp (pk->pkey, s_key, "public-key", "ne"); - if (rc) - { - log_error ("key_from_sexp failed: %s\n", gpg_strerror (rc)); - gcry_sexp_release (s_key); - return rc; + sk = xmalloc_clear( sizeof *sk ); + pk = xmalloc_clear( sizeof *pk ); + sk->timestamp = pk->timestamp = make_timestamp(); + sk->version = pk->version = 4; + if( expireval ) { + sk->expiredate = pk->expiredate = sk->timestamp + expireval; } - rc = key_from_sexp (sk->skey, s_key, "private-key", "nedpqu"); - if (rc) - { - log_error ("key_from_sexp failed: %s\n", gpg_strerror (rc) ); - gcry_sexp_release (s_key); - return rc; - } - gcry_sexp_release (s_key); + sk->pubkey_algo = pk->pubkey_algo = algo; - sk->is_protected = 0; - sk->protect.algo = 0; + rc = key_from_sexp (pk->pkey, s_key, "public-key", "ne"); + if (rc) + { + log_error ("key_from_sexp failed: %s\n", gpg_strerror (rc)); + gcry_sexp_release (s_key); + free_public_key(pk); + free_secret_key(sk); + return rc; + } + rc = key_from_sexp (sk->skey, s_key, "private-key", "nedpqu"); + if (rc) + { + log_error ("key_from_sexp failed: %s\n", gpg_strerror (rc) ); + gcry_sexp_release (s_key); + free_public_key(pk); + free_secret_key(sk); + return rc; + } + gcry_sexp_release (s_key); - sk->csum = checksum_mpi (sk->skey[2] ); - sk->csum += checksum_mpi (sk->skey[3] ); - sk->csum += checksum_mpi (sk->skey[4] ); - sk->csum += checksum_mpi (sk->skey[5] ); - if (ret_sk) /* not a subkey: return an unprotected version of the sk */ - *ret_sk = copy_secret_key (NULL, sk); + sk->is_protected = 0; + sk->protect.algo = 0; - rc = genhelp_protect (dek, s2k, sk); - if (rc) - { - free_public_key (pk); - free_secret_key (sk); - return rc; - } + sk->csum = checksum_mpi (sk->skey[2] ); + sk->csum += checksum_mpi (sk->skey[3] ); + sk->csum += checksum_mpi (sk->skey[4] ); + sk->csum += checksum_mpi (sk->skey[5] ); + if( ret_sk ) /* return an unprotected version of the sk */ + *ret_sk = copy_secret_key( NULL, sk ); - pkt = xcalloc (1,sizeof *pkt); - pkt->pkttype = ret_sk ? PKT_PUBLIC_KEY : PKT_PUBLIC_SUBKEY; - pkt->pkt.public_key = pk; - add_kbnode (pub_root, new_kbnode( pkt )); + rc = genhelp_protect (dek, s2k, sk); + if (rc) + { + free_public_key (pk); + free_secret_key (sk); + return rc; + } - pkt = xcalloc (1,sizeof *pkt); - pkt->pkttype = ret_sk ? PKT_SECRET_KEY : PKT_SECRET_SUBKEY; - pkt->pkt.secret_key = sk; - add_kbnode(sec_root, new_kbnode( pkt )); + pkt = xmalloc_clear(sizeof *pkt); + pkt->pkttype = is_subkey ? PKT_PUBLIC_SUBKEY : PKT_PUBLIC_KEY; + pkt->pkt.public_key = pk; + add_kbnode(pub_root, new_kbnode( pkt )); - return 0; + pkt = xmalloc_clear(sizeof *pkt); + pkt->pkttype = is_subkey ? PKT_SECRET_SUBKEY : PKT_SECRET_KEY; + pkt->pkt.secret_key = sk; + add_kbnode(sec_root, new_kbnode( pkt )); + + return 0; } @@ -1175,8 +1443,111 @@ check_valid_days( const char *s ) } +static void +print_key_flags(int flags) +{ + if(flags&PUBKEY_USAGE_SIG) + tty_printf("%s ",_("Sign")); + + if(flags&PUBKEY_USAGE_CERT) + tty_printf("%s ",_("Certify")); + + if(flags&PUBKEY_USAGE_ENC) + tty_printf("%s ",_("Encrypt")); + + if(flags&PUBKEY_USAGE_AUTH) + tty_printf("%s ",_("Authenticate")); +} + + +/* Returns the key flags */ +static unsigned int +ask_key_flags(int algo,int subkey) +{ + const char *togglers=_("SsEeAaQq"); + char *answer=NULL; + unsigned int current=0; + unsigned int possible=openpgp_pk_algo_usage(algo); + + if(strlen(togglers)!=8) + BUG(); + + /* Only primary keys may certify. */ + if(subkey) + possible&=~PUBKEY_USAGE_CERT; + + /* Preload the current set with the possible set, minus + authentication, since nobody really uses auth yet. */ + current=possible&~PUBKEY_USAGE_AUTH; + + for(;;) + { + tty_printf("\n"); + tty_printf(_("Possible actions for a %s key: "), + pubkey_algo_to_string(algo)); + print_key_flags(possible); + tty_printf("\n"); + tty_printf(_("Current allowed actions: ")); + print_key_flags(current); + tty_printf("\n\n"); + + if(possible&PUBKEY_USAGE_SIG) + tty_printf(_(" (%c) Toggle the sign capability\n"), + togglers[0]); + if(possible&PUBKEY_USAGE_ENC) + tty_printf(_(" (%c) Toggle the encrypt capability\n"), + togglers[2]); + if(possible&PUBKEY_USAGE_AUTH) + tty_printf(_(" (%c) Toggle the authenticate capability\n"), + togglers[4]); + + tty_printf(_(" (%c) Finished\n"),togglers[6]); + tty_printf("\n"); + + xfree(answer); + answer = cpr_get("keygen.flags",_("Your selection? ")); + cpr_kill_prompt(); + + if(strlen(answer)>1) + tty_printf(_("Invalid selection.\n")); + else if(*answer=='\0' || *answer==togglers[6] || *answer==togglers[7]) + break; + else if((*answer==togglers[0] || *answer==togglers[1]) + && possible&PUBKEY_USAGE_SIG) + { + if(current&PUBKEY_USAGE_SIG) + current&=~PUBKEY_USAGE_SIG; + else + current|=PUBKEY_USAGE_SIG; + } + else if((*answer==togglers[2] || *answer==togglers[3]) + && possible&PUBKEY_USAGE_ENC) + { + if(current&PUBKEY_USAGE_ENC) + current&=~PUBKEY_USAGE_ENC; + else + current|=PUBKEY_USAGE_ENC; + } + else if((*answer==togglers[4] || *answer==togglers[5]) + && possible&PUBKEY_USAGE_AUTH) + { + if(current&PUBKEY_USAGE_AUTH) + current&=~PUBKEY_USAGE_AUTH; + else + current|=PUBKEY_USAGE_AUTH; + } + else + tty_printf(_("Invalid selection.\n")); + } + + xfree(answer); + + return current; +} + + /**************** - * Returns: 0 to create both a DSA and a ElGamal key. + * Returns: 0 to create both a DSA and a Elgamal key. * and only if key flags are to be written the desired usage. */ static int @@ -1188,30 +1559,30 @@ ask_algo (int addmode, unsigned int *r_usage) *r_usage = 0; tty_printf(_("Please select what kind of key you want:\n")); if( !addmode ) - tty_printf(_(" (%d) DSA and ElGamal (default)\n"), 1 ); + tty_printf(_(" (%d) DSA and Elgamal (default)\n"), 1 ); tty_printf( _(" (%d) DSA (sign only)\n"), 2 ); - if( addmode ) - tty_printf( _(" (%d) ElGamal (encrypt only)\n"), 3 ); if (opt.expert) - tty_printf( _(" (%d) ElGamal (sign and encrypt)\n"), 4 ); + tty_printf( _(" (%d) DSA (set your own capabilities)\n"), 3 ); + if( addmode ) + tty_printf(_(" (%d) Elgamal (encrypt only)\n"), 4 ); tty_printf( _(" (%d) RSA (sign only)\n"), 5 ); if (addmode) - tty_printf( _(" (%d) RSA (encrypt only)\n"), 6 ); + tty_printf(_(" (%d) RSA (encrypt only)\n"), 6 ); if (opt.expert) - tty_printf( _(" (%d) RSA (sign and encrypt)\n"), 7 ); + tty_printf( _(" (%d) RSA (set your own capabilities)\n"), 7 ); for(;;) { answer = cpr_get("keygen.algo",_("Your selection? ")); cpr_kill_prompt(); algo = *answer? atoi(answer): 1; - xfree (answer); + xfree(answer); if( algo == 1 && !addmode ) { algo = 0; /* create both keys */ break; } else if( algo == 7 && opt.expert ) { algo = PUBKEY_ALGO_RSA; - *r_usage = PUBKEY_USAGE_ENC | PUBKEY_USAGE_SIG; + *r_usage=ask_key_flags(algo,addmode); break; } else if( algo == 6 && addmode ) { @@ -1224,26 +1595,16 @@ ask_algo (int addmode, unsigned int *r_usage) *r_usage = PUBKEY_USAGE_SIG; break; } - else if( algo == 4 && opt.expert) - { - tty_printf(_( -"The use of this algorithm is only supported by GnuPG. You will not be\n" -"able to use this key to communicate with PGP users. This algorithm is also\n" -"very slow, and may not be as secure as the other choices.\n")); - - if( cpr_get_answer_is_yes("keygen.algo.elg_se", - _("Create anyway? "))) - { - algo = PUBKEY_ALGO_ELGAMAL; - *r_usage = PUBKEY_USAGE_ENC | PUBKEY_USAGE_SIG; - break; - } - } - else if( algo == 3 && addmode ) { + else if( algo == 4 && addmode ) { algo = PUBKEY_ALGO_ELGAMAL_E; *r_usage = PUBKEY_USAGE_ENC; break; } + else if( algo == 3 && opt.expert ) { + algo = PUBKEY_ALGO_DSA; + *r_usage=ask_key_flags(algo,addmode); + break; + } else if( algo == 2 ) { algo = PUBKEY_ALGO_DSA; *r_usage = PUBKEY_USAGE_SIG; @@ -1252,6 +1613,7 @@ ask_algo (int addmode, unsigned int *r_usage) else tty_printf(_("Invalid selection.\n")); } + return algo; } @@ -1259,116 +1621,119 @@ ask_algo (int addmode, unsigned int *r_usage) static unsigned ask_keysize( int algo ) { - char *answer; - unsigned nbits; + unsigned int nbits, min, def=2048, max=4096; - if (algo != PUBKEY_ALGO_DSA && algo != PUBKEY_ALGO_RSA) { - tty_printf (_("About to generate a new %s keypair.\n" - " minimum keysize is 768 bits\n" - " default keysize is 1024 bits\n" - " highest suggested keysize is 2048 bits\n"), - gcry_pk_algo_name (algo) ); - } + if(opt.expert) + min=512; + else + min=1024; - for(;;) { - answer = cpr_get("keygen.size", - _("What keysize do you want? (1024) ")); - cpr_kill_prompt(); - nbits = *answer? atoi(answer): 1024; - xfree (answer); - if( algo == PUBKEY_ALGO_DSA && (nbits < 512 || nbits > 1024) ) - tty_printf(_("DSA only allows keysizes from 512 to 1024\n")); - else if( algo == PUBKEY_ALGO_RSA && nbits < 1024 ) - tty_printf(_("keysize too small;" - " 1024 is smallest value allowed for RSA.\n")); - else if( nbits < 768 ) - tty_printf(_("keysize too small;" - " 768 is smallest value allowed.\n")); - else if( nbits > 4096 ) { - /* It is ridiculous and an annoyance to use larger key sizes! - * GnuPG can handle much larger sizes; but it takes an eternity - * to create such a key (but less than the time the Sirius - * Computer Corporation needs to process one of the usual - * complaints) and {de,en}cryption although needs some time. - * So, before you complain about this limitation, I suggest that - * you start a discussion with Marvin about this theme and then - * do whatever you want. */ - tty_printf(_("keysize too large; %d is largest value allowed.\n"), - 4096); + switch(algo) + { + case PUBKEY_ALGO_DSA: + if(opt.expert) + { + def=1024; + max=1024; } - else if( nbits > 2048 && !cpr_enabled() ) { - tty_printf( - _("Keysizes larger than 2048 are not suggested because\n" - "computations take REALLY long!\n")); - if( cpr_get_answer_is_yes("keygen.size.huge.okay",_( - "Are you sure that you want this keysize? ")) ) { - tty_printf(_("Okay, but keep in mind that your monitor " - "and keyboard radiation is also very vulnerable " - "to attacks!\n")); - break; - } + else + { + tty_printf(_("DSA keypair will have %u bits.\n"),1024); + return 1024; } - else - break; + break; + + case PUBKEY_ALGO_RSA: + min=1024; + break; } - tty_printf(_("Requested keysize is %u bits\n"), nbits ); - if( algo == PUBKEY_ALGO_DSA && (nbits % 64) ) { - nbits = ((nbits + 63) / 64) * 64; - tty_printf(_("rounded up to %u bits\n"), nbits ); + + tty_printf(_("%s keys may be between %u and %u bits long.\n"), + pubkey_algo_to_string(algo),min,max); + + for(;;) + { + char *prompt,*answer; + +#define PROMPTSTRING _("What keysize do you want? (%u) ") + + prompt=xmalloc(strlen(PROMPTSTRING)+20); + sprintf(prompt,PROMPTSTRING,def); + +#undef PROMPTSTRING + + answer = cpr_get("keygen.size",prompt); + cpr_kill_prompt(); + nbits = *answer? atoi(answer): def; + xfree(prompt); + xfree(answer); + + if(nbits<min || nbits>max) + tty_printf(_("%s keysizes must be in the range %u-%u\n"), + pubkey_algo_to_string(algo),min,max); + else + break; } - else if( (nbits % 32) ) { - nbits = ((nbits + 31) / 32) * 32; - tty_printf(_("rounded up to %u bits\n"), nbits ); + + tty_printf(_("Requested keysize is %u bits\n"), nbits ); + + if( algo == PUBKEY_ALGO_DSA && (nbits % 64) ) + { + nbits = ((nbits + 63) / 64) * 64; + tty_printf(_("rounded up to %u bits\n"), nbits ); } - return nbits; + else if( (nbits % 32) ) + { + nbits = ((nbits + 31) / 32) * 32; + tty_printf(_("rounded up to %u bits\n"), nbits ); + } + + return nbits; } /**************** - * Parse an expire string and return it's value in days. - * Returns -1 on error. + * Parse an expire string and return its value in seconds. + * Returns (u32)-1 on error. + * This isn't perfect since scan_isodatestr returns unix time, and + * OpenPGP actually allows a 32-bit time *plus* a 32-bit offset. + * Because of this, we only permit setting expirations up to 2106, but + * OpenPGP could theoretically allow up to 2242. I think we'll all + * just cope for the next few years until we get a 64-bit time_t or + * similar. */ -static int +u32 parse_expire_string( const char *string ) { int mult; - u32 abs_date=0; - u32 curtime = make_timestamp(); - int valid_days; + u32 seconds,abs_date=0,curtime = make_timestamp(); if( !*string ) - valid_days = 0; - else if( (abs_date = scan_isodatestr(string)) && abs_date > curtime ) { - /* This calculation is not perfectly okay because we - * are later going to simply multiply by 86400 and don't - * correct for leapseconds. A solution would be to change - * the whole implemenation to work with dates and not intervals - * which are required for v3 keys. - */ - valid_days = abs_date/86400-curtime/86400+1; - } - else if( (mult=check_valid_days(string)) ) { - valid_days = atoi(string) * mult; - if( valid_days < 0 || valid_days > 39447 ) - valid_days = 0; - } - else { - valid_days = -1; - } - return valid_days; + seconds = 0; + else if ( !strncmp (string, "seconds=", 8) ) + seconds = atoi (string+8); + else if( (abs_date = scan_isodatestr(string)) && abs_date > curtime ) + seconds = abs_date - curtime; + else if( (mult=check_valid_days(string)) ) + seconds = atoi(string) * 86400L * mult; + else + seconds=(u32)-1; + + return seconds; } /* object == 0 for a key, and 1 for a sig */ u32 -ask_expire_interval(int object) +ask_expire_interval(int object,const char *def_expire) { + u32 interval; char *answer; - int valid_days=0; - u32 interval = 0; switch(object) { case 0: + if(def_expire) + BUG(); tty_printf(_("Please specify how long the key should be valid.\n" " 0 = key does not expire\n" " <n> = key expires in n days\n" @@ -1378,6 +1743,8 @@ ask_expire_interval(int object) break; case 1: + if(!def_expire) + BUG(); tty_printf(_("Please specify how long the signature should be valid.\n" " 0 = signature does not expire\n" " <n> = signature expires in n days\n" @@ -1395,91 +1762,76 @@ ask_expire_interval(int object) * date */ answer = NULL; - for(;;) { + for(;;) + { u32 curtime=make_timestamp(); - xfree (answer); + xfree(answer); if(object==0) answer = cpr_get("keygen.valid",_("Key is valid for? (0) ")); else - answer = cpr_get("siggen.valid",_("Signature is valid for? (0) ")); + { + char *prompt; + +#define PROMPTSTRING _("Signature is valid for? (%s) ") + /* This will actually end up larger than necessary because + of the 2 bytes for '%s' */ + prompt=xmalloc(strlen(PROMPTSTRING)+strlen(def_expire)+1); + sprintf(prompt,PROMPTSTRING,def_expire); +#undef PROMPTSTRING + + answer = cpr_get("siggen.valid",prompt); + xfree(prompt); + + if(*answer=='\0') + answer=xstrdup(def_expire); + } cpr_kill_prompt(); trim_spaces(answer); - valid_days = parse_expire_string( answer ); - if( valid_days < 0 ) { + interval = parse_expire_string( answer ); + if( interval == (u32)-1 ) + { tty_printf(_("invalid value\n")); continue; - } + } - if( !valid_days ) { - tty_printf(_("%s does not expire at all\n"), - object==0?"Key":"Signature"); - interval = 0; - } - else { - interval = valid_days * 86400L; - /* print the date when the key expires */ - tty_printf(_("%s expires at %s\n"), - object==0?"Key":"Signature", - asctimestamp((ulong)(curtime + interval) ) ); - /* FIXME: This check yields warning some machines: write a - configure check and do this check here only for 32 bit - machines */ + if( !interval ) + { + tty_printf((object==0) + ? _("Key does not expire at all\n") + : _("Signature does not expire at all\n")); + } + else + { + tty_printf(object==0 + ? _("Key expires at %s\n") + : _("Signature expires at %s\n"), + asctimestamp((ulong)(curtime + interval) ) ); + /* FIXME: This check yields warning on alhas: Write a + configure check and to this check here only for 32 bit + machines */ if( (time_t)((ulong)(curtime+interval)) < 0 ) - tty_printf(_("Your system can't display dates beyond 2038.\n" - "However, it will be correctly handled up to 2106.\n")); - } + tty_printf(_("Your system can't display dates beyond 2038.\n" + "However, it will be correctly handled up to 2106.\n")); + } if( cpr_enabled() || cpr_get_answer_is_yes("keygen.valid.okay", - _("Is this correct (y/n)? ")) ) - break; - } - xfree (answer); + _("Is this correct? (y/N) ")) ) + break; + } + + xfree(answer); return interval; } u32 ask_expiredate() { - u32 x = ask_expire_interval(0); + u32 x = ask_expire_interval(0,NULL); return x? make_timestamp() + x : 0; } -static int -count_chr( const char *string, int c ) -{ - int count; - - for (count=0; *string; string++ ) - if ( *string == c ) - count++; - return count; -} - - -static int -has_invalid_email_chars( const char *s ) -{ - int at_seen=0; - static char valid_chars[] = "01234567890_-." - "abcdefghijklmnopqrstuvwxyz" - "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; - - for( ; *s; s++ ) { - if( *s & 0x80 ) - return 1; - if( *s == '@' ) - at_seen=1; - else if( !at_seen && !( !!strchr( valid_chars, *s ) || *s == '+' ) ) - return 1; - else if( at_seen && !strchr( valid_chars, *s ) ) - return 1; - } - return 0; -} - - static char * ask_user_id( int mode ) { @@ -1488,8 +1840,9 @@ ask_user_id( int mode ) if( !mode ) tty_printf( _("\n" -"You need a User-ID to identify your key; the software constructs the user id\n" -"from Real Name, Comment and Email Address in this form:\n" +"You need a user ID to identify your key; " + "the software constructs the user ID\n" +"from the Real Name, Comment and Email Address in this form:\n" " \"Heinrich Heine (Der Dichter) <heinrichh@duesseldorf.de>\"\n\n") ); uid = aname = acomment = amail = NULL; for(;;) { @@ -1498,7 +1851,7 @@ ask_user_id( int mode ) if( !aname ) { for(;;) { - xfree (aname); + xfree(aname); aname = cpr_get("keygen.name",_("Real name: ")); trim_spaces(aname); cpr_kill_prompt(); @@ -1518,26 +1871,21 @@ ask_user_id( int mode ) } if( !amail ) { for(;;) { - xfree (amail); + xfree(amail); amail = cpr_get("keygen.email",_("Email address: ")); trim_spaces(amail); cpr_kill_prompt(); if( !*amail || opt.allow_freeform_uid ) break; /* no email address is okay */ - else if( has_invalid_email_chars(amail) - || count_chr(amail,'@') != 1 - || *amail == '@' - || amail[strlen(amail)-1] == '@' - || amail[strlen(amail)-1] == '.' - || strstr(amail, "..") ) - tty_printf(_("Not a valid email address\n")); + else if ( !is_valid_mailbox (amail) ) + tty_printf(_("Not a valid email address\n")); else break; } } if( !acomment ) { for(;;) { - xfree (acomment); + xfree(acomment); acomment = cpr_get("keygen.comment",_("Comment: ")); trim_spaces(acomment); cpr_kill_prompt(); @@ -1551,14 +1899,19 @@ ask_user_id( int mode ) } - xfree (uid); - uid = p = xmalloc (strlen(aname)+strlen(amail)+strlen(acomment)+12+10); + xfree(uid); + uid = p = xmalloc(strlen(aname)+strlen(amail)+strlen(acomment)+12+10); p = stpcpy(p, aname ); if( *acomment ) p = stpcpy(stpcpy(stpcpy(p," ("), acomment),")"); if( *amail ) p = stpcpy(stpcpy(stpcpy(p," <"), amail),">"); + /* append a warning if we do not have dev/random + * or it is switched into quick testmode */ + if( quick_random_gen(-1) ) + strcpy(p, " (INSECURE!)" ); + /* print a note in case that UTF8 mapping has to be done */ for(p=uid; *p; p++ ) { if( *p & 0x80 ) { @@ -1571,19 +1924,30 @@ ask_user_id( int mode ) tty_printf(_("You selected this USER-ID:\n \"%s\"\n\n"), uid); /* fixme: add a warning if this user-id already exists */ if( !*amail && !opt.allow_freeform_uid - && (strchr( aname, '@' ) || strchr( acomment, '@'))) { + && (strchr( aname, '@' ) || strchr( acomment, '@'))) { fail = 1; tty_printf(_("Please don't put the email address " "into the real name or the comment\n") ); } for(;;) { + /* TRANSLATORS: These are the allowed answers in + lower and uppercase. Below you will find the matching + string which should be translated accordingly and the + letter changed to match the one in the answer string. + + n = Change name + c = Change comment + e = Change email + o = Okay (ready, continue) + q = Quit + */ const char *ansstr = _("NnCcEeOoQq"); if( strlen(ansstr) != 10 ) BUG(); if( cpr_enabled() ) { - answer = xstrdup (ansstr+6); + answer = xstrdup(ansstr+6); answer[1] = 0; } else { @@ -1595,15 +1959,15 @@ ask_user_id( int mode ) if( strlen(answer) > 1 ) ; else if( *answer == ansstr[0] || *answer == ansstr[1] ) { - xfree (aname); aname = NULL; + xfree(aname); aname = NULL; break; } else if( *answer == ansstr[2] || *answer == ansstr[3] ) { - xfree (acomment); acomment = NULL; + xfree(acomment); acomment = NULL; break; } else if( *answer == ansstr[4] || *answer == ansstr[5] ) { - xfree (amail); amail = NULL; + xfree(amail); amail = NULL; break; } else if( *answer == ansstr[6] || *answer == ansstr[7] ) { @@ -1611,37 +1975,38 @@ ask_user_id( int mode ) tty_printf(_("Please correct the error first\n")); } else { - xfree (aname); aname = NULL; - xfree (acomment); acomment = NULL; - xfree (amail); amail = NULL; + xfree(aname); aname = NULL; + xfree(acomment); acomment = NULL; + xfree(amail); amail = NULL; break; } } else if( *answer == ansstr[8] || *answer == ansstr[9] ) { - xfree (aname); aname = NULL; - xfree (acomment); acomment = NULL; - xfree (amail); amail = NULL; - xfree (uid); uid = NULL; + xfree(aname); aname = NULL; + xfree(acomment); acomment = NULL; + xfree(amail); amail = NULL; + xfree(uid); uid = NULL; break; } - xfree (answer); + xfree(answer); } - xfree (answer); + xfree(answer); if( !amail && !acomment && !amail ) - break; - xfree (uid); uid = NULL; + break; + xfree(uid); uid = NULL; } if( uid ) { char *p = native_to_utf8( uid ); - xfree ( uid ); + xfree( uid ); uid = p; } return uid; } +/* FIXME: We need a way to cancel this prompt. */ static DEK * -ask_passphrase( STRING2KEY **ret_s2k ) +do_ask_passphrase( STRING2KEY **ret_s2k ) { DEK *dek = NULL; STRING2KEY *s2k; @@ -1649,10 +2014,10 @@ ask_passphrase( STRING2KEY **ret_s2k ) tty_printf(_("You need a Passphrase to protect your secret key.\n\n") ); - s2k = xmalloc ( sizeof *s2k ); + s2k = xmalloc_secure( sizeof *s2k ); for(;;) { s2k->mode = opt.s2k_mode; - s2k->hash_algo = opt.s2k_digest_algo; + s2k->hash_algo = S2K_DIGEST_ALGO; dek = passphrase_to_dek( NULL, 0, opt.s2k_cipher_algo, s2k,2, errtext, NULL); if( !dek ) { @@ -1660,8 +2025,8 @@ ask_passphrase( STRING2KEY **ret_s2k ) tty_printf(_("%s.\n"), _(errtext)); } else if( !dek->keylen ) { - xfree (dek); dek = NULL; - xfree (s2k); s2k = NULL; + xfree(dek); dek = NULL; + xfree(s2k); s2k = NULL; tty_printf(_( "You don't want a passphrase - this is probably a *bad* idea!\n" "I will do it anyway. You can change your passphrase at any time,\n" @@ -1678,37 +2043,31 @@ ask_passphrase( STRING2KEY **ret_s2k ) static int do_create( int algo, unsigned int nbits, KBNODE pub_root, KBNODE sec_root, - DEK *dek, STRING2KEY *s2k, PKT_secret_key **sk, u32 expiredate ) + DEK *dek, STRING2KEY *s2k, PKT_secret_key **sk, u32 expiredate, + int is_subkey ) { - int rc=0; + int rc=0; - if( !opt.batch ) - tty_printf(_( + if( !opt.batch ) + tty_printf(_( "We need to generate a lot of random bytes. It is a good idea to perform\n" "some other action (type on the keyboard, move the mouse, utilize the\n" "disks) during the prime generation; this gives the random number\n" "generator a better chance to gain enough entropy.\n") ); - if( algo == PUBKEY_ALGO_ELGAMAL || algo == PUBKEY_ALGO_ELGAMAL_E ) - rc = gen_elg(algo, nbits, pub_root, sec_root, dek, s2k, sk, expiredate); - else if( algo == PUBKEY_ALGO_DSA ) - rc = gen_dsa(nbits, pub_root, sec_root, dek, s2k, sk, expiredate); - else if( algo == PUBKEY_ALGO_RSA ) - rc = gen_rsa(algo, nbits, pub_root, sec_root, dek, s2k, sk, expiredate); - else - BUG(); + if( algo == PUBKEY_ALGO_ELGAMAL_E ) + rc = gen_elg(algo, nbits, pub_root, sec_root, dek, s2k, sk, expiredate, + is_subkey); + else if( algo == PUBKEY_ALGO_DSA ) + rc = gen_dsa(nbits, pub_root, sec_root, dek, s2k, sk, expiredate, + is_subkey); + else if( algo == PUBKEY_ALGO_RSA ) + rc = gen_rsa(algo, nbits, pub_root, sec_root, dek, s2k, sk, expiredate, + is_subkey); + else + BUG(); -#ifdef ENABLE_COMMENT_PACKETS - if( !rc ) { - add_kbnode( pub_root, - make_comment_node("#created by GNUPG v" VERSION " (" - PRINTABLE_OS_NAME ")")); - add_kbnode( sec_root, - make_comment_node("#created by GNUPG v" VERSION " (" - PRINTABLE_OS_NAME ")")); - } -#endif - return rc; + return rc; } @@ -1726,7 +2085,7 @@ generate_user_id() if( !p ) return NULL; n = strlen(p); - uid = xcalloc (1, sizeof *uid + n - 1 ); + uid = xmalloc_clear( sizeof *uid + n - 1 ); uid->len = n; strcpy(uid->name, p); uid->ref = 1; @@ -1742,11 +2101,11 @@ release_parameter_list( struct para_data_s *r ) for( ; r ; r = r2 ) { r2 = r->next; if( r->key == pPASSPHRASE_DEK ) - xfree ( r->u.dek ); + xfree( r->u.dek ); else if( r->key == pPASSPHRASE_S2K ) - xfree ( r->u.s2k ); + xfree( r->u.s2k ); - xfree (r); + xfree(r); } } @@ -1777,7 +2136,7 @@ get_parameter_algo( struct para_data_s *para, enum para_name key ) if( digitp( r->u.value ) ) i = atoi( r->u.value ); else - i = openpgp_pk_map_name ( r->u.value ); + i = string_to_pubkey_algo( r->u.value ); if (i == PUBKEY_ALGO_RSA_E || i == PUBKEY_ALGO_RSA_S) i = 0; /* we don't want to allow generation of these algorithms */ return i; @@ -1814,7 +2173,7 @@ parse_parameter_usage (const char *fname, } } r->u.usage = use; - return 0; + return 1; } static int @@ -1917,124 +2276,172 @@ static int proc_parameter_file( struct para_data_s *para, const char *fname, struct output_control_s *outctrl, int card ) { - struct para_data_s *r; - const char *s1, *s2, *s3; - size_t n; - char *p; - int i; + struct para_data_s *r; + const char *s1, *s2, *s3; + size_t n; + char *p; + int have_user_id=0,err,algo; - /* check that we have all required parameters */ - assert( get_parameter( para, pKEYTYPE ) ); - i = get_parameter_algo( para, pKEYTYPE ); - if( i < 1 || openpgp_pk_test_algo ( i, PUBKEY_USAGE_SIG ) ) { - r = get_parameter( para, pKEYTYPE ); - log_error("%s:%d: invalid algorithm\n", fname, r->lnr ); - return -1; + /* Check that we have all required parameters. */ + r = get_parameter( para, pKEYTYPE ); + if(r) + { + algo=get_parameter_algo(para,pKEYTYPE); + if(check_pubkey_algo2(algo,PUBKEY_USAGE_SIG)) + { + log_error("%s:%d: invalid algorithm\n", fname, r->lnr ); + return -1; + } + } + else + { + log_error("%s: no Key-Type specified\n",fname); + return -1; } - if (parse_parameter_usage (fname, para, pKEYUSAGE)) - return -1; + err=parse_parameter_usage (fname, para, pKEYUSAGE); + if(err==0) + { + /* Default to algo capabilities if key-usage is not provided */ + r=xmalloc_clear(sizeof(*r)); + r->key=pKEYUSAGE; + r->u.usage=openpgp_pk_algo_usage(algo); + r->next=para; + para=r; + } + else if(err==-1) + return -1; + + r = get_parameter( para, pSUBKEYTYPE ); + if(r) + { + algo=get_parameter_algo( para, pSUBKEYTYPE); + if(check_pubkey_algo(algo)) + { + log_error("%s:%d: invalid algorithm\n", fname, r->lnr ); + return -1; + } - i = get_parameter_algo( para, pSUBKEYTYPE ); - if( i > 0 && openpgp_pk_test_algo ( i, 0 ) ) { - r = get_parameter( para, pSUBKEYTYPE ); - log_error("%s:%d: invalid algorithm\n", fname, r->lnr ); + err=parse_parameter_usage (fname, para, pSUBKEYUSAGE); + if(err==0) + { + /* Default to algo capabilities if subkey-usage is not + provided */ + r=xmalloc_clear(sizeof(*r)); + r->key=pSUBKEYUSAGE; + r->u.usage=openpgp_pk_algo_usage(algo); + r->next=para; + para=r; + } + else if(err==-1) return -1; } - if (i > 0 && parse_parameter_usage (fname, para, pSUBKEYUSAGE)) - return -1; - - if( !get_parameter_value( para, pUSERID ) ) { - /* create the formatted user ID */ - s1 = get_parameter_value( para, pNAMEREAL ); - s2 = get_parameter_value( para, pNAMECOMMENT ); - s3 = get_parameter_value( para, pNAMEEMAIL ); - if( s1 || s2 || s3 ) { - n = (s1?strlen(s1):0) + (s2?strlen(s2):0) + (s3?strlen(s3):0); - r = xcalloc (1, sizeof *r + n + 20 ); - r->key = pUSERID; - p = r->u.value; - if( s1 ) - p = stpcpy(p, s1 ); - if( s2 ) - p = stpcpy(stpcpy(stpcpy(p," ("), s2 ),")"); - if( s3 ) - p = stpcpy(stpcpy(stpcpy(p," <"), s3 ),">"); - r->next = para; - para = r; + if( get_parameter_value( para, pUSERID ) ) + have_user_id=1; + else + { + /* create the formatted user ID */ + s1 = get_parameter_value( para, pNAMEREAL ); + s2 = get_parameter_value( para, pNAMECOMMENT ); + s3 = get_parameter_value( para, pNAMEEMAIL ); + if( s1 || s2 || s3 ) + { + n = (s1?strlen(s1):0) + (s2?strlen(s2):0) + (s3?strlen(s3):0); + r = xmalloc_clear( sizeof *r + n + 20 ); + r->key = pUSERID; + p = r->u.value; + if( s1 ) + p = stpcpy(p, s1 ); + if( s2 ) + p = stpcpy(stpcpy(stpcpy(p," ("), s2 ),")"); + if( s3 ) + p = stpcpy(stpcpy(stpcpy(p," <"), s3 ),">"); + r->next = para; + para = r; + have_user_id=1; } } - /* Set preferences, if any. */ - keygen_set_std_prefs(get_parameter_value( para, pPREFERENCES ), 0); - - /* Set revoker, if any. */ - if (parse_revocation_key (fname, para, pREVOKER)) + if(!have_user_id) + { + log_error("%s: no User-ID specified\n",fname); return -1; + } - /* make DEK and S2K from the Passphrase */ - r = get_parameter( para, pPASSPHRASE ); - if( r && *r->u.value ) { - /* we have a plain text passphrase - create a DEK from it. - * It is a little bit ridiculous to keep it in secure memory - * but because we do this always, why not here. */ - STRING2KEY *s2k; - DEK *dek; + /* Set preferences, if any. */ + keygen_set_std_prefs(get_parameter_value( para, pPREFERENCES ), 0); - s2k = xmalloc_secure ( sizeof *s2k ); - s2k->mode = opt.s2k_mode; - s2k->hash_algo = opt.s2k_digest_algo; - set_next_passphrase( r->u.value ); - dek = passphrase_to_dek( NULL, 0, opt.s2k_cipher_algo, s2k, 2, - NULL, NULL); - set_next_passphrase( NULL ); - assert( dek ); - memset( r->u.value, 0, strlen(r->u.value) ); - - r = xcalloc (1, sizeof *r ); - r->key = pPASSPHRASE_S2K; - r->u.s2k = s2k; - r->next = para; - para = r; - r = xcalloc (1, sizeof *r ); - r->key = pPASSPHRASE_DEK; - r->u.dek = dek; - r->next = para; - para = r; - } + /* Set revoker, if any. */ + if (parse_revocation_key (fname, para, pREVOKER)) + return -1; - /* make KEYEXPIRE from Expire-Date */ - r = get_parameter( para, pEXPIREDATE ); - if( r && *r->u.value ) { - i = parse_expire_string( r->u.value ); - if( i < 0 ) { - log_error("%s:%d: invalid expire date\n", fname, r->lnr ); - return -1; + /* make DEK and S2K from the Passphrase */ + r = get_parameter( para, pPASSPHRASE ); + if( r && *r->u.value ) { + /* we have a plain text passphrase - create a DEK from it. + * It is a little bit ridiculous to keep it ih secure memory + * but becuase we do this alwasy, why not here */ + STRING2KEY *s2k; + DEK *dek; + + s2k = xmalloc_secure( sizeof *s2k ); + s2k->mode = opt.s2k_mode; + s2k->hash_algo = S2K_DIGEST_ALGO; + set_next_passphrase( r->u.value ); + dek = passphrase_to_dek( NULL, 0, opt.s2k_cipher_algo, s2k, 2, + NULL, NULL); + set_next_passphrase( NULL ); + assert( dek ); + memset( r->u.value, 0, strlen(r->u.value) ); + + r = xmalloc_clear( sizeof *r ); + r->key = pPASSPHRASE_S2K; + r->u.s2k = s2k; + r->next = para; + para = r; + r = xmalloc_clear( sizeof *r ); + r->key = pPASSPHRASE_DEK; + r->u.dek = dek; + r->next = para; + para = r; + } + + /* make KEYEXPIRE from Expire-Date */ + r = get_parameter( para, pEXPIREDATE ); + if( r && *r->u.value ) + { + u32 seconds; + + seconds = parse_expire_string( r->u.value ); + if( seconds == (u32)-1 ) + { + log_error("%s:%d: invalid expire date\n", fname, r->lnr ); + return -1; } - r->u.expire = i * 86400L; - r->key = pKEYEXPIRE; /* change hat entry */ - /* also set it for the subkey */ - r = xcalloc (1, sizeof *r + 20 ); - r->key = pSUBKEYEXPIRE; - r->u.expire = i * 86400L; - r->next = para; - para = r; + r->u.expire = seconds; + r->key = pKEYEXPIRE; /* change hat entry */ + /* also set it for the subkey */ + r = xmalloc_clear( sizeof *r + 20 ); + r->key = pSUBKEYEXPIRE; + r->u.expire = seconds; + r->next = para; + para = r; } - if( !!outctrl->pub.newfname ^ !!outctrl->sec.newfname ) { - log_error("%s:%d: only one ring name is set\n", fname, outctrl->lnr ); - return -1; - } + if( !!outctrl->pub.newfname ^ !!outctrl->sec.newfname ) { + log_error("%s:%d: only one ring name is set\n", fname, outctrl->lnr ); + return -1; + } - do_generate_keypair( para, outctrl, card); - return 0; + do_generate_keypair( para, outctrl, card ); + return 0; } /**************** * Kludge to allow non interactive key generation controlled - * by a parameter file (which currently is only stdin) + * by a parameter file. * Note, that string parameters are expected to be in UTF-8 */ static void @@ -2056,10 +2463,13 @@ read_parameter_file( const char *fname ) { "Passphrase", pPASSPHRASE }, { "Preferences", pPREFERENCES }, { "Revoker", pREVOKER }, + { "Handle", pHANDLE }, { NULL, 0 } }; - FILE *fp; - char line[1024], *p; + IOBUF fp; + byte *line; + unsigned int maxlen, nline; + char *p; int lnr; const char *err = NULL; struct para_data_s *para, *r; @@ -2068,26 +2478,32 @@ read_parameter_file( const char *fname ) memset( &outctrl, 0, sizeof( outctrl ) ); - if( !fname || !*fname || !strcmp(fname,"-") ) { - fp = stdin; - fname = "-"; - } - else { - fp = fopen( fname, "r" ); - if( !fp ) { - log_error(_("can't open `%s': %s\n"), fname, strerror(errno) ); - return; - } + if( !fname || !*fname) + fname = "-"; + + fp = iobuf_open (fname); + if (fp && is_secured_file (iobuf_get_fd (fp))) + { + iobuf_close (fp); + fp = NULL; + errno = EPERM; + } + if (!fp) { + log_error (_("can't open `%s': %s\n"), fname, strerror(errno) ); + return; } + iobuf_ioctl (fp, 3, 1, NULL); /* No file caching. */ lnr = 0; err = NULL; para = NULL; - while( fgets( line, DIM(line)-1, fp ) ) { + maxlen = 1024; + line = NULL; + while ( iobuf_read_line (fp, &line, &nline, &maxlen) ) { char *keyword, *value; lnr++; - if( *line && line[strlen(line)-1] != '\n' ) { + if( !maxlen ) { err = "line too long"; break; } @@ -2111,7 +2527,9 @@ read_parameter_file( const char *fname ) outctrl.dryrun = 1; else if( !ascii_strcasecmp( keyword, "%commit" ) ) { outctrl.lnr = lnr; - proc_parameter_file( para, fname, &outctrl, 0 ); + if (proc_parameter_file( para, fname, &outctrl, 0 )) + print_status_key_not_created + (get_parameter_value (para, pHANDLE)); release_parameter_list( para ); para = NULL; } @@ -2119,8 +2537,8 @@ read_parameter_file( const char *fname ) if( outctrl.pub.fname && !strcmp( outctrl.pub.fname, value ) ) ; /* still the same file - ignore it */ else { - xfree ( outctrl.pub.newfname ); - outctrl.pub.newfname = xstrdup ( value ); + xfree( outctrl.pub.newfname ); + outctrl.pub.newfname = xstrdup( value ); outctrl.use_files = 1; } } @@ -2128,8 +2546,8 @@ read_parameter_file( const char *fname ) if( outctrl.sec.fname && !strcmp( outctrl.sec.fname, value ) ) ; /* still the same file - ignore it */ else { - xfree ( outctrl.sec.newfname ); - outctrl.sec.newfname = xstrdup ( value ); + xfree( outctrl.sec.newfname ); + outctrl.sec.newfname = xstrdup( value ); outctrl.use_files = 1; } } @@ -2171,7 +2589,9 @@ read_parameter_file( const char *fname ) if( keywords[i].key == pKEYTYPE && para ) { outctrl.lnr = lnr; - proc_parameter_file( para, fname, &outctrl, 0 ); + if (proc_parameter_file( para, fname, &outctrl, 0 )) + print_status_key_not_created + (get_parameter_value (para, pHANDLE)); release_parameter_list( para ); para = NULL; } @@ -2185,7 +2605,7 @@ read_parameter_file( const char *fname ) break; } } - r = xcalloc (1, sizeof *r + strlen( value ) ); + r = xmalloc_clear( sizeof *r + strlen( value ) ); r->lnr = lnr; r->key = keywords[i].key; strcpy( r->u.value, value ); @@ -2194,36 +2614,47 @@ read_parameter_file( const char *fname ) } if( err ) log_error("%s:%d: %s\n", fname, lnr, err ); - else if( ferror(fp) ) { - log_error("%s:%d: read error: %s\n", fname, lnr, strerror(errno) ); + else if( iobuf_error (fp) ) { + log_error("%s:%d: read error\n", fname, lnr); } else if( para ) { outctrl.lnr = lnr; - proc_parameter_file( para, fname, &outctrl, 0 ); + if (proc_parameter_file( para, fname, &outctrl, 0 )) + print_status_key_not_created (get_parameter_value (para, pHANDLE)); } if( outctrl.use_files ) { /* close open streams */ iobuf_close( outctrl.pub.stream ); iobuf_close( outctrl.sec.stream ); - xfree ( outctrl.pub.fname ); - xfree ( outctrl.pub.newfname ); - xfree ( outctrl.sec.fname ); - xfree ( outctrl.sec.newfname ); + + /* Must invalidate that ugly cache to actually close it. */ + if (outctrl.pub.fname) + iobuf_ioctl (NULL, 2, 0, (char*)outctrl.pub.fname); + if (outctrl.sec.fname) + iobuf_ioctl (NULL, 2, 0, (char*)outctrl.sec.fname); + + xfree( outctrl.pub.fname ); + xfree( outctrl.pub.newfname ); + xfree( outctrl.sec.fname ); + xfree( outctrl.sec.newfname ); } release_parameter_list( para ); - if( strcmp( fname, "-" ) ) - fclose(fp); + iobuf_close (fp); } -/**************** +/* * Generate a keypair (fname is only used in batch mode) If * CARD_SERIALNO is not NULL the fucntion will create the keys on an - * OpenPGP Card. + * OpenPGP Card. If BACKUP_ENCRYPTION_DIR has been set and + * CARD_SERIALNO is NOT NULL, the encryption key for the card gets + * generate in software, imported to the card and a backup file + * written to directory given by this argument . */ void -generate_keypair( const char *fname, const char *card_serialno ) +generate_keypair (const char *fname, const char *card_serialno, + const char *backup_encryption_dir) { unsigned int nbits; char *uid = NULL; @@ -2236,16 +2667,16 @@ generate_keypair( const char *fname, const char *card_serialno ) struct para_data_s *para = NULL; struct para_data_s *r; struct output_control_s outctrl; - - memset (&outctrl, 0, sizeof (outctrl)); - + + memset( &outctrl, 0, sizeof( outctrl ) ); + if (opt.batch && card_serialno) { /* We don't yet support unattended key generation. */ - log_error (_("sorry, can't do this in batch mode\n")); + log_error (_("can't do this in batch mode\n")); return; } - + if (opt.batch) { read_parameter_file( fname ); @@ -2254,14 +2685,15 @@ generate_keypair( const char *fname, const char *card_serialno ) if (card_serialno) { +#ifdef ENABLE_CARD_SUPPORT r = xcalloc (1, sizeof *r + strlen (card_serialno) ); r->key = pSERIALNO; strcpy( r->u.value, card_serialno); r->next = para; para = r; - + algo = PUBKEY_ALGO_RSA; - + r = xcalloc (1, sizeof *r + 20 ); r->key = pKEYTYPE; sprintf( r->u.value, "%d", algo ); @@ -2272,7 +2704,7 @@ generate_keypair( const char *fname, const char *card_serialno ) strcpy (r->u.value, "sign"); r->next = para; para = r; - + r = xcalloc (1, sizeof *r + 20 ); r->key = pSUBKEYTYPE; sprintf( r->u.value, "%d", algo ); @@ -2283,435 +2715,540 @@ generate_keypair( const char *fname, const char *card_serialno ) strcpy (r->u.value, "encrypt"); r->next = para; para = r; - + r = xcalloc (1, sizeof *r + 20 ); r->key = pAUTHKEYTYPE; sprintf( r->u.value, "%d", algo ); r->next = para; para = r; + + if (backup_encryption_dir) + { + r = xcalloc (1, sizeof *r + strlen (backup_encryption_dir) ); + r->key = pBACKUPENCDIR; + strcpy (r->u.value, backup_encryption_dir); + r->next = para; + para = r; + } +#endif /*ENABLE_CARD_SUPPORT*/ } else { - algo = ask_algo (0, &use); - - if (!algo) + algo = ask_algo( 0, &use ); + if( !algo ) { /* default: DSA with ElG subkey of the specified size */ both = 1; - r = xcalloc (1, sizeof *r + 20 ); + r = xmalloc_clear( sizeof *r + 20 ); r->key = pKEYTYPE; sprintf( r->u.value, "%d", PUBKEY_ALGO_DSA ); r->next = para; para = r; - tty_printf(_("DSA keypair will have 1024 bits.\n")); - r = xcalloc (1, sizeof *r + 20 ); + tty_printf(_("DSA keypair will have %u bits.\n"),1024); + r = xmalloc_clear( sizeof *r + 20 ); r->key = pKEYLENGTH; strcpy( r->u.value, "1024" ); r->next = para; para = r; - r = xcalloc (1, sizeof *r + 20 ); + r = xmalloc_clear( sizeof *r + 20 ); r->key = pKEYUSAGE; strcpy( r->u.value, "sign" ); r->next = para; para = r; - + algo = PUBKEY_ALGO_ELGAMAL_E; - r = xcalloc (1, sizeof *r + 20 ); + r = xmalloc_clear( sizeof *r + 20 ); r->key = pSUBKEYTYPE; sprintf( r->u.value, "%d", algo ); r->next = para; para = r; - r = xcalloc (1, sizeof *r + 20 ); + r = xmalloc_clear( sizeof *r + 20 ); r->key = pSUBKEYUSAGE; strcpy( r->u.value, "encrypt" ); r->next = para; - r->next = para; para = r; } else { - r = xcalloc (1, sizeof *r + 20 ); + r = xmalloc_clear( sizeof *r + 20 ); r->key = pKEYTYPE; sprintf( r->u.value, "%d", algo ); r->next = para; para = r; - + if (use) { - r = xcalloc (1, sizeof *r + 20 ); + r = xmalloc_clear( sizeof *r + 25 ); r->key = pKEYUSAGE; - sprintf( r->u.value, "%s%s", + sprintf( r->u.value, "%s%s%s", (use & PUBKEY_USAGE_SIG)? "sign ":"", - (use & PUBKEY_USAGE_ENC)? "encrypt ":"" ); + (use & PUBKEY_USAGE_ENC)? "encrypt ":"", + (use & PUBKEY_USAGE_AUTH)? "auth":"" ); r->next = para; para = r; } + } - + nbits = ask_keysize( algo ); - r = xcalloc (1, sizeof *r + 20 ); + r = xmalloc_clear( sizeof *r + 20 ); r->key = both? pSUBKEYLENGTH : pKEYLENGTH; sprintf( r->u.value, "%u", nbits); r->next = para; para = r; } - - expire = ask_expire_interval(0); - r = xcalloc (1, sizeof *r + 20 ); + + expire = ask_expire_interval(0,NULL); + r = xmalloc_clear( sizeof *r + 20 ); r->key = pKEYEXPIRE; r->u.expire = expire; r->next = para; para = r; - r = xcalloc (1, sizeof *r + 20 ); + r = xmalloc_clear( sizeof *r + 20 ); r->key = pSUBKEYEXPIRE; r->u.expire = expire; r->next = para; para = r; - + uid = ask_user_id(0); - if (!uid) + if( !uid ) { log_error(_("Key generation canceled.\n")); release_parameter_list( para ); return; } - r = xcalloc (1, sizeof *r + strlen(uid) ); + r = xmalloc_clear( sizeof *r + strlen(uid) ); r->key = pUSERID; strcpy( r->u.value, uid ); r->next = para; para = r; - - dek = card_serialno? NULL : ask_passphrase( &s2k ); - if (dek) + + dek = card_serialno? NULL : do_ask_passphrase( &s2k ); + if( dek ) { - r = xcalloc (1, sizeof *r ); + r = xmalloc_clear( sizeof *r ); r->key = pPASSPHRASE_DEK; r->u.dek = dek; r->next = para; para = r; - r = xcalloc (1, sizeof *r ); + r = xmalloc_clear( sizeof *r ); r->key = pPASSPHRASE_S2K; r->u.s2k = s2k; r->next = para; para = r; } - - proc_parameter_file (para, "[internal]", &outctrl, !!card_serialno); - release_parameter_list (para); + + proc_parameter_file( para, "[internal]", &outctrl, !!card_serialno); + release_parameter_list( para ); } -static void -print_status_key_created (int letter, PKT_public_key *pk) +#ifdef ENABLE_CARD_SUPPORT +/* Generate a raw key and return it as a secret key packet. The + function will ask for the passphrase and return a protected as well + as an unprotected copy of a new secret key packet. 0 is returned + on success and the caller must then free the returned values. */ +static int +generate_raw_key (int algo, unsigned int nbits, u32 created_at, + PKT_secret_key **r_sk_unprotected, + PKT_secret_key **r_sk_protected) { - byte array[MAX_FINGERPRINT_LEN], *s; - char buf[MAX_FINGERPRINT_LEN*2+30], *p; - size_t i, n; - - p = buf; - *p++ = letter; - *p++ = ' '; - fingerprint_from_pk (pk, array, &n); - s = array; - for (i=0; i < n ; i++, s++, p += 2) - sprintf (p, "%02X", *s); - *p = 0; - write_status_text (STATUS_KEY_CREATED, buf); -} + int rc; + DEK *dek = NULL; + STRING2KEY *s2k = NULL; + PKT_secret_key *sk = NULL; + int i; + size_t nskey, npkey; + npkey = pubkey_get_npkey (algo); + nskey = pubkey_get_nskey (algo); + assert (nskey <= PUBKEY_MAX_NSKEY && npkey < nskey); + if (nbits < 512) + { + nbits = 512; + log_info (_("keysize invalid; using %u bits\n"), nbits ); + } -static void -do_generate_keypair (struct para_data_s *para, - struct output_control_s *outctrl, int card) -{ - KBNODE pub_root = NULL; - KBNODE sec_root = NULL; - PKT_secret_key *sk = NULL; - const char *s; - struct revocation_key *revkey; - int rc; - int did_sub = 0; + if ((nbits % 32)) + { + nbits = ((nbits + 31) / 32) * 32; + log_info(_("keysize rounded up to %u bits\n"), nbits ); + } + + dek = do_ask_passphrase (&s2k); - if (outctrl->dryrun) + sk = xmalloc_clear (sizeof *sk); + sk->timestamp = created_at; + sk->version = 4; + sk->pubkey_algo = algo; + + if ( !is_RSA (algo) ) { - log_info ("dry-run mode - key generation skipped\n"); - return; + log_error ("only RSA is supported for offline generated keys\n"); + rc = gpg_error (GPG_ERR_NOT_IMPLEMENTED); + goto leave; } + rc = gcry_sexp_build (&s_parms, NULL, + "(genkey(rsa(nbits %d)))", + (int)nbits); + if (rc) + log_bug ("gcry_sexp_build failed: %s\n", gpg_strerror (rc)); + rc = gcry_pk_genkey (&s_key, s_parms); + gcry_sexp_release (s_parms); + if (rc) + { + log_error ("gcry_pk_genkey failed: %s\n", gpg_strerror (rc) ); + goto leave; + } + rc = key_from_sexp (sk->skey, s_key, "private-key", "nedpqu"); + gcry_sexp_release (s_key); + if (rc) + { + log_error ("key_from_sexp failed: %s\n", gpg_strerror (rc) ); + goto leave; + } + + for (i=npkey; i < nskey; i++) + sk->csum += checksum_mpi (sk->skey[i]); + if (r_sk_unprotected) + *r_sk_unprotected = copy_secret_key (NULL, sk); - if (outctrl->use_files) + rc = genhelp_protect (dek, s2k, sk); + if (rc) + goto leave; + + if (r_sk_protected) { - if (outctrl->pub.newfname) - { - iobuf_close (outctrl->pub.stream); - outctrl->pub.stream = NULL; - xfree (outctrl->pub.fname); - outctrl->pub.fname = outctrl->pub.newfname; - outctrl->pub.newfname = NULL; - - outctrl->pub.stream = iobuf_create (outctrl->pub.fname); - if (!outctrl->pub.stream) - { - log_error ("can't create `%s': %s\n", outctrl->pub.fname, - strerror (errno)); - return; + *r_sk_protected = sk; + sk = NULL; + } + + leave: + if (sk) + free_secret_key (sk); + xfree (dek); + xfree (s2k); + return rc; +} +#endif /* ENABLE_CARD_SUPPORT */ + +/* Create and delete a dummy packet to start off a list of kbnodes. */ +static void +start_tree(KBNODE *tree) +{ + PACKET *pkt; + + pkt=xmalloc_clear(sizeof(*pkt)); + pkt->pkttype=PKT_NONE; + *tree=new_kbnode(pkt); + delete_kbnode(*tree); +} + +static void +do_generate_keypair( struct para_data_s *para, + struct output_control_s *outctrl, int card ) +{ + KBNODE pub_root = NULL; + KBNODE sec_root = NULL; + PKT_secret_key *pri_sk = NULL, *sub_sk = NULL; + const char *s; + struct revocation_key *revkey; + int rc; + int did_sub = 0; + + if( outctrl->dryrun ) + { + log_info("dry-run mode - key generation skipped\n"); + return; + } + + if( outctrl->use_files ) { + if( outctrl->pub.newfname ) { + iobuf_close(outctrl->pub.stream); + outctrl->pub.stream = NULL; + if (outctrl->pub.fname) + iobuf_ioctl (NULL, 2, 0, (char*)outctrl->pub.fname); + xfree( outctrl->pub.fname ); + outctrl->pub.fname = outctrl->pub.newfname; + outctrl->pub.newfname = NULL; + + if (is_secured_filename (outctrl->pub.fname) ) { + outctrl->pub.stream = NULL; + errno = EPERM; + } + else + outctrl->pub.stream = iobuf_create( outctrl->pub.fname ); + if( !outctrl->pub.stream ) { + log_error(_("can't create `%s': %s\n"), outctrl->pub.newfname, + strerror(errno) ); + return; } - if (opt.armor) - { - outctrl->pub.afx.what = 1; - iobuf_push_filter (outctrl->pub.stream, armor_filter, - &outctrl->pub.afx); + if( opt.armor ) { + outctrl->pub.afx.what = 1; + iobuf_push_filter( outctrl->pub.stream, armor_filter, + &outctrl->pub.afx ); } } - if (outctrl->sec.newfname) - { - iobuf_close (outctrl->sec.stream); - outctrl->sec.stream = NULL; - xfree (outctrl->sec.fname); - outctrl->sec.fname = outctrl->sec.newfname; - outctrl->sec.newfname = NULL; - - outctrl->sec.stream = iobuf_create (outctrl->sec.fname); - if (!outctrl->sec.stream) - { - log_error ("can't create `%s': %s\n", outctrl->sec.fname, - strerror (errno)); - return; + if( outctrl->sec.newfname ) { + mode_t oldmask; + + iobuf_close(outctrl->sec.stream); + outctrl->sec.stream = NULL; + if (outctrl->sec.fname) + iobuf_ioctl (NULL, 2, 0, (char*)outctrl->sec.fname); + xfree( outctrl->sec.fname ); + outctrl->sec.fname = outctrl->sec.newfname; + outctrl->sec.newfname = NULL; + + oldmask = umask (077); + if (is_secured_filename (outctrl->sec.fname) ) { + outctrl->sec.stream = NULL; + errno = EPERM; + } + else + outctrl->sec.stream = iobuf_create( outctrl->sec.fname ); + umask (oldmask); + if( !outctrl->sec.stream ) { + log_error(_("can't create `%s': %s\n"), outctrl->sec.newfname, + strerror(errno) ); + return; } - if (opt.armor) - { - outctrl->sec.afx.what = 5; - iobuf_push_filter (outctrl->sec.stream, armor_filter, - &outctrl->sec.afx); + if( opt.armor ) { + outctrl->sec.afx.what = 5; + iobuf_push_filter( outctrl->sec.stream, armor_filter, + &outctrl->sec.afx ); } } - assert (outctrl->pub.stream); - assert (outctrl->sec.stream); - if (opt.verbose) - { - log_info (_("writing public key to `%s'\n"), outctrl->pub.fname); - if (card) - log_info (_("writing secret key stub to `%s'\n"), - outctrl->sec.fname); - else - log_info (_("writing secret key to `%s'\n"), outctrl->sec.fname); - } + assert( outctrl->pub.stream ); + assert( outctrl->sec.stream ); + if( opt.verbose ) { + log_info(_("writing public key to `%s'\n"), outctrl->pub.fname ); + if (card) + log_info (_("writing secret key stub to `%s'\n"), + outctrl->sec.fname); + else + log_info(_("writing secret key to `%s'\n"), outctrl->sec.fname ); + } } - /* We create the packets as a tree of kbnodes. Because the structure - * we create is known in advance we simply generate a linked list. - * The first packet is a dummy comment packet which we flag - * as deleted. The very first packet must always be a KEY packet. - */ - pub_root = make_comment_node ("#"); - delete_kbnode (pub_root); - sec_root = make_comment_node ("#"); - delete_kbnode (sec_root); - if (!card) - { - rc = do_create (get_parameter_algo (para, pKEYTYPE), - get_parameter_uint (para, pKEYLENGTH), - pub_root, sec_root, - get_parameter_dek (para, pPASSPHRASE_DEK), - get_parameter_s2k (para, pPASSPHRASE_S2K), - &sk, get_parameter_u32 (para, pKEYEXPIRE)); - } - else - { - rc = gen_card_key (PUBKEY_ALGO_RSA, 1, pub_root, sec_root, - get_parameter_u32 (para, pKEYEXPIRE), para); - if (!rc) - { - sk = sec_root->next->pkt->pkt.secret_key; - assert (sk); - } - - } + /* we create the packets as a tree of kbnodes. Because the + * structure we create is known in advance we simply generate a + * linked list. The first packet is a dummy packet which we flag + * as deleted. The very first packet must always be a KEY packet. + */ + + start_tree(&pub_root); + start_tree(&sec_root); - if (!rc && (revkey = get_parameter_revkey (para, pREVOKER))) - { - rc = write_direct_sig (pub_root, pub_root, sk, revkey); - if (!rc) - write_direct_sig (sec_root, pub_root, sk, revkey); - } + if (!card) + { + rc = do_create( get_parameter_algo( para, pKEYTYPE ), + get_parameter_uint( para, pKEYLENGTH ), + pub_root, sec_root, + get_parameter_dek( para, pPASSPHRASE_DEK ), + get_parameter_s2k( para, pPASSPHRASE_S2K ), + &pri_sk, + get_parameter_u32( para, pKEYEXPIRE ), 0 ); + } + else + { + rc = gen_card_key (PUBKEY_ALGO_RSA, 1, 1, pub_root, sec_root, + get_parameter_u32 (para, pKEYEXPIRE), para); + if (!rc) + { + pri_sk = sec_root->next->pkt->pkt.secret_key; + assert (pri_sk); + } + } - if (!rc && (s = get_parameter_value (para, pUSERID))) - { - write_uid (pub_root, s); - if (!rc) - write_uid (sec_root, s); - if (!rc) - rc = write_selfsig (pub_root, pub_root, sk, - get_parameter_uint (para, pKEYUSAGE)); - if (!rc) - rc = write_selfsig (sec_root, pub_root, sk, - get_parameter_uint (para, pKEYUSAGE)); - } + if(!rc && (revkey=get_parameter_revkey(para,pREVOKER))) + { + rc=write_direct_sig(pub_root,pub_root,pri_sk,revkey); + if(!rc) + write_direct_sig(sec_root,pub_root,pri_sk,revkey); + } - if ((! rc) && get_parameter (para, pSUBKEYTYPE)) - { - if (!card) - { - rc = do_create (get_parameter_algo (para, pSUBKEYTYPE), - get_parameter_uint (para, pSUBKEYLENGTH), - pub_root, sec_root, - get_parameter_dek (para, pPASSPHRASE_DEK), - get_parameter_s2k (para, pPASSPHRASE_S2K), - NULL, get_parameter_u32 (para, pSUBKEYEXPIRE)); - } - else - { - rc = gen_card_key (PUBKEY_ALGO_RSA, 2, pub_root, sec_root, - get_parameter_u32 (para, pKEYEXPIRE), para); - } + if( !rc && (s=get_parameter_value(para, pUSERID)) ) + { + write_uid(pub_root, s ); + if( !rc ) + write_uid(sec_root, s ); - if (!rc) - rc = write_keybinding (pub_root, pub_root, sk, - get_parameter_uint (para, pSUBKEYUSAGE)); - if (!rc) - rc = write_keybinding (sec_root, pub_root, sk, - get_parameter_uint (para, pSUBKEYUSAGE)); - did_sub = 1; - } + if( !rc ) + rc = write_selfsigs(sec_root, pub_root, pri_sk, + get_parameter_uint (para, pKEYUSAGE)); + } - if ((! rc) && card && get_parameter (para, pAUTHKEYTYPE)) - { - rc = gen_card_key (PUBKEY_ALGO_RSA, 3, pub_root, sec_root, - get_parameter_u32 (para, pKEYEXPIRE), para); + /* Write the auth key to the card before the encryption key. This + is a partial workaround for a PGP bug (as of this writing, all + versions including 8.1), that causes it to try and encrypt to + the most recent subkey regardless of whether that subkey is + actually an encryption type. In this case, the auth key is an + RSA key so it succeeds. */ - if (!rc) - rc = write_keybinding (pub_root, pub_root, sk, PUBKEY_USAGE_AUTH); - if (!rc) - rc = write_keybinding (sec_root, pub_root, sk, PUBKEY_USAGE_AUTH); - } + if (!rc && card && get_parameter (para, pAUTHKEYTYPE)) + { + rc = gen_card_key (PUBKEY_ALGO_RSA, 3, 0, pub_root, sec_root, + get_parameter_u32 (para, pKEYEXPIRE), para); + + if (!rc) + rc = write_keybinding (pub_root, pub_root, pri_sk, sub_sk, PUBKEY_USAGE_AUTH); + if (!rc) + rc = write_keybinding (sec_root, pub_root, pri_sk, sub_sk, PUBKEY_USAGE_AUTH); + } + if( !rc && get_parameter( para, pSUBKEYTYPE ) ) + { + if (!card) + { + rc = do_create( get_parameter_algo( para, pSUBKEYTYPE ), + get_parameter_uint( para, pSUBKEYLENGTH ), + pub_root, sec_root, + get_parameter_dek( para, pPASSPHRASE_DEK ), + get_parameter_s2k( para, pPASSPHRASE_S2K ), + &sub_sk, + get_parameter_u32( para, pSUBKEYEXPIRE ), 1 ); + } + else + { + if ((s = get_parameter_value (para, pBACKUPENCDIR))) + { + /* A backup of the encryption key has been requested. + Generate the key i software and import it then to + the card. Write a backup file. */ + rc = gen_card_key_with_backup (PUBKEY_ALGO_RSA, 2, 0, + pub_root, sec_root, + get_parameter_u32 (para, + pKEYEXPIRE), + para, s); + } + else + rc = gen_card_key (PUBKEY_ALGO_RSA, 2, 0, pub_root, sec_root, + get_parameter_u32 (para, pKEYEXPIRE), para); + } + + if( !rc ) + rc = write_keybinding(pub_root, pub_root, pri_sk, sub_sk, + get_parameter_uint (para, pSUBKEYUSAGE)); + if( !rc ) + rc = write_keybinding(sec_root, pub_root, pri_sk, sub_sk, + get_parameter_uint (para, pSUBKEYUSAGE)); + did_sub = 1; + } - if (!rc && outctrl->use_files) - { /* direct write to specified files */ - rc = write_keyblock (outctrl->pub.stream, pub_root); - if (rc) - log_error ("can't write public key: %s\n", gpg_strerror (rc)); - if (!rc) - { - rc = write_keyblock (outctrl->sec.stream, sec_root); - if (rc) - log_error ("can't write secret key: %s\n", gpg_strerror (rc)); + if( !rc && outctrl->use_files ) { /* direct write to specified files */ + rc = write_keyblock( outctrl->pub.stream, pub_root ); + if( rc ) + log_error("can't write public key: %s\n", g10_errstr(rc) ); + if( !rc ) { + rc = write_keyblock( outctrl->sec.stream, sec_root ); + if( rc ) + log_error("can't write secret key: %s\n", g10_errstr(rc) ); } } - else if (!rc) - { /* write to the standard keyrings */ - KEYDB_HANDLE pub_hd = keydb_new (0); - KEYDB_HANDLE sec_hd = keydb_new (1); + else if( !rc ) { /* write to the standard keyrings */ + KEYDB_HANDLE pub_hd = keydb_new (0); + KEYDB_HANDLE sec_hd = keydb_new (1); - /* FIXME: we may have to create the keyring first */ - rc = keydb_locate_writable (pub_hd, NULL); - if (rc) - log_error (_("no writable public keyring found: %s\n"), - gpg_strerror (rc)); + /* FIXME: we may have to create the keyring first */ + rc = keydb_locate_writable (pub_hd, NULL); + if (rc) + log_error (_("no writable public keyring found: %s\n"), + g10_errstr (rc)); - if (!rc) - { - rc = keydb_locate_writable (sec_hd, NULL); - if (rc) - log_error (_("no writable secret keyring found: %s\n"), - gpg_strerror (rc)); - } + if (!rc) { + rc = keydb_locate_writable (sec_hd, NULL); + if (rc) + log_error (_("no writable secret keyring found: %s\n"), + g10_errstr (rc)); + } - if (!rc && opt.verbose) - { - log_info (_("writing public key to `%s'\n"), - keydb_get_resource_name (pub_hd)); - if (card) - log_info (_("writing secret key stub to `%s'\n"), - keydb_get_resource_name (sec_hd)); - else - log_info (_("writing secret key to `%s'\n"), - keydb_get_resource_name (sec_hd)); - } + if (!rc && opt.verbose) { + log_info(_("writing public key to `%s'\n"), + keydb_get_resource_name (pub_hd)); + if (card) + log_info (_("writing secret key stub to `%s'\n"), + keydb_get_resource_name (sec_hd)); + else + log_info(_("writing secret key to `%s'\n"), + keydb_get_resource_name (sec_hd)); + } - if (!rc) - { - rc = keydb_insert_keyblock (pub_hd, pub_root); - if (rc) - log_error (_("error writing public keyring `%s': %s\n"), - keydb_get_resource_name (pub_hd), gpg_strerror (rc)); - } + if (!rc) { + rc = keydb_insert_keyblock (pub_hd, pub_root); + if (rc) + log_error (_("error writing public keyring `%s': %s\n"), + keydb_get_resource_name (pub_hd), g10_errstr(rc)); + } - if (!rc) - { - rc = keydb_insert_keyblock (sec_hd, sec_root); - if (rc) - log_error (_("error writing secret keyring `%s': %s\n"), - keydb_get_resource_name (pub_hd), gpg_strerror (rc)); - } + if (!rc) { + rc = keydb_insert_keyblock (sec_hd, sec_root); + if (rc) + log_error (_("error writing secret keyring `%s': %s\n"), + keydb_get_resource_name (pub_hd), g10_errstr(rc)); + } - keydb_release (pub_hd); - keydb_release (sec_hd); + keydb_release (pub_hd); + keydb_release (sec_hd); - if (!rc) - { - int no_enc_rsa = - get_parameter_algo (para, pKEYTYPE) == PUBKEY_ALGO_RSA - && get_parameter_uint (para, pKEYUSAGE) - && !(get_parameter_uint (para, pKEYUSAGE) & PUBKEY_USAGE_ENC); - PKT_public_key *pk = find_kbnode (pub_root, - PKT_PUBLIC_KEY)->pkt->pkt. - public_key; - - update_ownertrust (pk, - ((get_ownertrust (pk) & ~TRUST_MASK) - | TRUST_ULTIMATE)); - - if (!opt.batch) - { - tty_printf (_("public and secret key created and signed.\n")); - tty_printf (_("key marked as ultimately trusted.\n")); - tty_printf ("\n"); - list_keyblock (pub_root, 0, 1, NULL); - } + if (!rc) { + int no_enc_rsa = + get_parameter_algo(para, pKEYTYPE) == PUBKEY_ALGO_RSA + && get_parameter_uint( para, pKEYUSAGE ) + && !(get_parameter_uint( para,pKEYUSAGE) & PUBKEY_USAGE_ENC); + PKT_public_key *pk = find_kbnode (pub_root, + PKT_PUBLIC_KEY)->pkt->pkt.public_key; + keyid_from_pk(pk,pk->main_keyid); + register_trusted_keyid(pk->main_keyid); - if (!opt.batch - && (get_parameter_algo (para, pKEYTYPE) == PUBKEY_ALGO_DSA - || no_enc_rsa) && !get_parameter (para, pSUBKEYTYPE)) - { - tty_printf (_("Note that this key cannot be used for " - "encryption. You may want to use\n" - "the command \"--edit-key\" to generate a " - "secondary key for this purpose.\n")); - } + update_ownertrust (pk, + ((get_ownertrust (pk) & ~TRUST_MASK) + | TRUST_ULTIMATE )); - if (!opt.batch && card) - { - tty_printf(_( -"Please create a revocation certificate now, so that you are able\n" -"to revoke the key if it ever happens that you lose your card or\n" -"the card gets damaged. Use the command \"--gen-revoke\".\n" - )); + if (!opt.batch) { + tty_printf(_("public and secret key created and signed.\n") ); + tty_printf("\n"); + list_keyblock(pub_root,0,1,NULL); } + + + if( !opt.batch + && ( get_parameter_algo( para, pKEYTYPE ) == PUBKEY_ALGO_DSA + || no_enc_rsa ) + && !get_parameter( para, pSUBKEYTYPE ) ) + { + tty_printf(_("Note that this key cannot be used for " + "encryption. You may want to use\n" + "the command \"--edit-key\" to generate a " + "subkey for this purpose.\n") ); + } } } - if (rc) - { - if (opt.batch) - log_error ("key generation failed: %s\n", gpg_strerror (rc)); - else - tty_printf (_("Key generation failed: %s\n"), gpg_strerror (rc)); - } - else - { - PKT_public_key *pk = find_kbnode (pub_root, - PKT_PUBLIC_KEY)->pkt->pkt.public_key; - print_status_key_created (did_sub ? 'B' : 'P', pk); + if( rc ) { + if( opt.batch ) + log_error("key generation failed: %s\n", g10_errstr(rc) ); + else + tty_printf(_("Key generation failed: %s\n"), g10_errstr(rc) ); + print_status_key_not_created ( get_parameter_value (para, pHANDLE) ); } - - release_kbnode (pub_root); - release_kbnode (sec_root); - if (sk && !card) /* The unprotected secret key unless we have */ - free_secret_key (sk); /* a shallow copy in card mode. */ + else { + PKT_public_key *pk = find_kbnode (pub_root, + PKT_PUBLIC_KEY)->pkt->pkt.public_key; + print_status_key_created (did_sub? 'B':'P', pk, + get_parameter_value (para, pHANDLE)); + } + release_kbnode( pub_root ); + release_kbnode( sec_root ); + + if( pri_sk && !card) /* the unprotected secret key unless we have a */ + free_secret_key(pri_sk); /* shallow copy in card mode. */ + if( sub_sk ) + free_secret_key(sub_sk); } @@ -2724,7 +3261,7 @@ generate_subkeypair( KBNODE pub_keyblock, KBNODE sec_keyblock ) { int okay=0, rc=0; KBNODE node; - PKT_secret_key *sk = NULL; /* this is the primary sk */ + PKT_secret_key *pri_sk = NULL, *sub_sk = NULL; int algo; unsigned int use; u32 expire; @@ -2733,6 +3270,7 @@ generate_subkeypair( KBNODE pub_keyblock, KBNODE sec_keyblock ) DEK *dek = NULL; STRING2KEY *s2k = NULL; u32 cur_time; + int ask_pass = 0; /* break out the primary secret key */ node = find_kbnode( sec_keyblock, PKT_SECRET_KEY ); @@ -2742,69 +3280,81 @@ generate_subkeypair( KBNODE pub_keyblock, KBNODE sec_keyblock ) } /* make a copy of the sk to keep the protected one in the keyblock */ - sk = copy_secret_key( NULL, node->pkt->pkt.secret_key ); + pri_sk = copy_secret_key( NULL, node->pkt->pkt.secret_key ); cur_time = make_timestamp(); - if( sk->timestamp > cur_time ) { - ulong d = sk->timestamp - cur_time; + if( pri_sk->timestamp > cur_time ) { + ulong d = pri_sk->timestamp - cur_time; log_info( d==1 ? _("key has been created %lu second " "in future (time warp or clock problem)\n") : _("key has been created %lu seconds " "in future (time warp or clock problem)\n"), d ); if( !opt.ignore_time_conflict ) { - rc = GPG_ERR_TIME_CONFLICT; + rc = G10ERR_TIME_CONFLICT; goto leave; } } - if (sk->version < 4) { + if (pri_sk->version < 4) { log_info (_("NOTE: creating subkeys for v3 keys " "is not OpenPGP compliant\n")); goto leave; } - /* unprotect to get the passphrase */ - switch( is_secret_key_protected( sk ) ) { + if (pri_sk->is_protected && pri_sk->protect.s2k.mode == 1001) { + tty_printf(_("Secret parts of primary key are not available.\n")); + rc = G10ERR_NO_SECKEY; + goto leave; + } + + + /* Unprotect to get the passphrase. */ + switch( is_secret_key_protected( pri_sk ) ) { case -1: - rc = GPG_ERR_PUBKEY_ALGO; + rc = G10ERR_PUBKEY_ALGO; break; case 0: - tty_printf("This key is not protected.\n"); + tty_printf(_("This key is not protected.\n")); break; + case -2: + tty_printf(_("Secret parts of primary key are stored on-card.\n")); + ask_pass = 1; + break; default: - tty_printf("Key is protected.\n"); - rc = check_secret_key( sk, 0 ); - if( !rc ) - passphrase = get_last_passphrase(); - break; + tty_printf(_("Key is protected.\n")); + rc = check_secret_key( pri_sk, 0 ); + if( !rc ) + passphrase = get_last_passphrase(); + break; } if( rc ) goto leave; - algo = ask_algo( 1, &use ); assert(algo); nbits = ask_keysize( algo ); - expire = ask_expire_interval(0); + expire = ask_expire_interval(0,NULL); if( !cpr_enabled() && !cpr_get_answer_is_yes("keygen.sub.okay", - _("Really create? ") ) ) + _("Really create? (y/N) "))) goto leave; - if( passphrase ) { - s2k = xmalloc_secure ( sizeof *s2k ); + if (ask_pass) + dek = do_ask_passphrase (&s2k); + else if (passphrase) { + s2k = xmalloc_secure( sizeof *s2k ); s2k->mode = opt.s2k_mode; - s2k->hash_algo = opt.s2k_digest_algo; + s2k->hash_algo = S2K_DIGEST_ALGO; set_next_passphrase( passphrase ); dek = passphrase_to_dek( NULL, 0, opt.s2k_cipher_algo, s2k, 2, NULL, NULL ); } rc = do_create( algo, nbits, pub_keyblock, sec_keyblock, - dek, s2k, NULL, expire ); + dek, s2k, &sub_sk, expire, 1 ); if( !rc ) - rc = write_keybinding(pub_keyblock, pub_keyblock, sk, use); + rc = write_keybinding(pub_keyblock, pub_keyblock, pri_sk, sub_sk, use); if( !rc ) - rc = write_keybinding(sec_keyblock, pub_keyblock, sk, use); + rc = write_keybinding(sec_keyblock, pub_keyblock, pri_sk, sub_sk, use); if( !rc ) { okay = 1; write_status_text (STATUS_KEY_CREATED, "S"); @@ -2812,39 +3362,163 @@ generate_subkeypair( KBNODE pub_keyblock, KBNODE sec_keyblock ) leave: if( rc ) - log_error(_("Key generation failed: %s\n"), gpg_strerror (rc) ); - xfree ( passphrase ); - xfree ( dek ); - xfree ( s2k ); - if( sk ) /* release the copy of the (now unprotected) secret key */ - free_secret_key(sk); + log_error(_("Key generation failed: %s\n"), g10_errstr(rc) ); + xfree( passphrase ); + xfree( dek ); + xfree( s2k ); + /* release the copy of the (now unprotected) secret keys */ + if( pri_sk ) + free_secret_key(pri_sk); + if( sub_sk ) + free_secret_key(sub_sk); set_next_passphrase( NULL ); return okay; } + +#ifdef ENABLE_CARD_SUPPORT +/* Generate a subkey on a card. */ +int +generate_card_subkeypair (KBNODE pub_keyblock, KBNODE sec_keyblock, + int keyno, const char *serialno) +{ + int okay=0, rc=0; + KBNODE node; + PKT_secret_key *pri_sk = NULL; + int algo; + unsigned int use; + u32 expire; + char *passphrase = NULL; + u32 cur_time; + struct para_data_s *para = NULL; + + assert (keyno >= 1 && keyno <= 3); + + para = xcalloc (1, sizeof *para + strlen (serialno) ); + para->key = pSERIALNO; + strcpy (para->u.value, serialno); + + /* Break out the primary secret key */ + node = find_kbnode( sec_keyblock, PKT_SECRET_KEY ); + if(!node) + { + log_error("Oops; secret key not found anymore!\n"); + goto leave; + } + + /* Make a copy of the sk to keep the protected one in the keyblock */ + pri_sk = copy_secret_key (NULL, node->pkt->pkt.secret_key); + + cur_time = make_timestamp(); + if (pri_sk->timestamp > cur_time) + { + ulong d = pri_sk->timestamp - cur_time; + log_info (d==1 ? _("key has been created %lu second " + "in future (time warp or clock problem)\n") + : _("key has been created %lu seconds " + "in future (time warp or clock problem)\n"), d ); + if (!opt.ignore_time_conflict) + { + rc = G10ERR_TIME_CONFLICT; + goto leave; + } + } + + if (pri_sk->version < 4) + { + log_info (_("NOTE: creating subkeys for v3 keys " + "is not OpenPGP compliant\n")); + goto leave; + } + + /* Unprotect to get the passphrase. */ + switch( is_secret_key_protected (pri_sk) ) + { + case -1: + rc = G10ERR_PUBKEY_ALGO; + break; + case 0: + tty_printf("This key is not protected.\n"); + break; + default: + tty_printf("Key is protected.\n"); + rc = check_secret_key( pri_sk, 0 ); + if (!rc) + passphrase = get_last_passphrase(); + break; + } + if (rc) + goto leave; + + algo = PUBKEY_ALGO_RSA; + expire = ask_expire_interval (0,NULL); + if (keyno == 1) + use = PUBKEY_USAGE_SIG; + else if (keyno == 2) + use = PUBKEY_USAGE_ENC; + else + use = PUBKEY_USAGE_AUTH; + if (!cpr_enabled() && !cpr_get_answer_is_yes("keygen.cardsub.okay", + _("Really create? (y/N) "))) + goto leave; + + if (passphrase) + set_next_passphrase (passphrase); + rc = gen_card_key (algo, keyno, 0, pub_keyblock, sec_keyblock, expire, para); + if (!rc) + rc = write_keybinding (pub_keyblock, pub_keyblock, pri_sk, NULL, use); + if (!rc) + rc = write_keybinding (sec_keyblock, pub_keyblock, pri_sk, NULL, use); + if (!rc) + { + okay = 1; + write_status_text (STATUS_KEY_CREATED, "S"); + } + + leave: + if (rc) + log_error (_("Key generation failed: %s\n"), g10_errstr(rc) ); + xfree (passphrase); + /* Release the copy of the (now unprotected) secret keys. */ + if (pri_sk) + free_secret_key (pri_sk); + set_next_passphrase( NULL ); + release_parameter_list (para); + return okay; +} +#endif /* !ENABLE_CARD_SUPPORT */ + + /**************** * Write a keyblock to an output stream */ static int -write_keyblock( iobuf_t out, KBNODE node ) +write_keyblock( IOBUF out, KBNODE node ) { - for( ; node ; node = node->next ) { - int rc = build_packet( out, node->pkt ); - if( rc ) { - log_error("build_packet(%d) failed: %s\n", - node->pkt->pkttype, gpg_strerror (rc) ); - return rc; + for( ; node ; node = node->next ) + { + if(!is_deleted_kbnode(node)) + { + int rc = build_packet( out, node->pkt ); + if( rc ) + { + log_error("build_packet(%d) failed: %s\n", + node->pkt->pkttype, g10_errstr(rc) ); + return G10ERR_WRITE_FILE; + } } } - return 0; + + return 0; } - static int -gen_card_key (int algo, int keyno, KBNODE pub_root, KBNODE sec_root, +gen_card_key (int algo, int keyno, int is_primary, + KBNODE pub_root, KBNODE sec_root, u32 expireval, struct para_data_s *para) { +#ifdef ENABLE_CARD_SUPPORT int rc; const char *s; struct agent_card_genkey_s info; @@ -2853,8 +3527,9 @@ gen_card_key (int algo, int keyno, KBNODE pub_root, KBNODE sec_root, PKT_public_key *pk; assert (algo == PUBKEY_ALGO_RSA); - - rc = agent_scd_genkey (&info, keyno, 1); + + /* Fixme: We don't have the serialnumber available, thus passing NULL. */ + rc = agent_scd_genkey (&info, keyno, 1, NULL); /* if (gpg_err_code (rc) == GPG_ERR_EEXIST) */ /* { */ /* tty_printf ("\n"); */ @@ -2866,13 +3541,15 @@ gen_card_key (int algo, int keyno, KBNODE pub_root, KBNODE sec_root, /* } */ if (rc) - return rc; - + { + log_error ("key generation failed: %s\n", gpg_strerror (rc)); + return rc; + } if ( !info.n || !info.e ) { log_error ("communication error with SCD\n"); - gcry_mpi_release (info.n); - gcry_mpi_release (info.e); + mpi_free (info.n); + mpi_free (info.e); return gpg_error (GPG_ERR_GENERAL); } @@ -2900,17 +3577,243 @@ gen_card_key (int algo, int keyno, KBNODE pub_root, KBNODE sec_root, } pkt = xcalloc (1,sizeof *pkt); - pkt->pkttype = keyno == 1 ? PKT_PUBLIC_KEY : PKT_PUBLIC_SUBKEY; + pkt->pkttype = is_primary ? PKT_PUBLIC_KEY : PKT_PUBLIC_SUBKEY; pkt->pkt.public_key = pk; add_kbnode(pub_root, new_kbnode( pkt )); pkt = xcalloc (1,sizeof *pkt); - pkt->pkttype = keyno == 1 ? PKT_SECRET_KEY : PKT_SECRET_SUBKEY; + pkt->pkttype = is_primary ? PKT_SECRET_KEY : PKT_SECRET_SUBKEY; pkt->pkt.secret_key = sk; add_kbnode(sec_root, new_kbnode( pkt )); return 0; +#else + return -1; +#endif /*!ENABLE_CARD_SUPPORT*/ } +static int +gen_card_key_with_backup (int algo, int keyno, int is_primary, + KBNODE pub_root, KBNODE sec_root, + u32 expireval, struct para_data_s *para, + const char *backup_dir) +{ +#ifdef ENABLE_CARD_SUPPORT + int rc; + const char *s; + PACKET *pkt; + PKT_secret_key *sk, *sk_unprotected, *sk_protected; + PKT_public_key *pk; + size_t n; + int i; + + rc = generate_raw_key (algo, 1024, make_timestamp (), + &sk_unprotected, &sk_protected); + if (rc) + return rc; + + /* First, store the key to the card. */ + rc = save_unprotected_key_to_card (sk_unprotected, keyno); + if (rc) + { + log_error (_("storing key onto card failed: %s\n"), g10_errstr (rc)); + free_secret_key (sk_unprotected); + free_secret_key (sk_protected); + return rc; + } + + /* Get rid of the secret key parameters and store the serial numer. */ + sk = sk_unprotected; + n = pubkey_get_nskey (sk->pubkey_algo); + for (i=pubkey_get_npkey (sk->pubkey_algo); i < n; i++) + { + mpi_free (sk->skey[i]); + sk->skey[i] = NULL; + } + i = pubkey_get_npkey (sk->pubkey_algo); + sk->skey[i] = mpi_set_opaque (NULL, xstrdup ("dummydata"), 10); + sk->is_protected = 1; + sk->protect.s2k.mode = 1002; + s = get_parameter_value (para, pSERIALNO); + assert (s); + for (sk->protect.ivlen=0; sk->protect.ivlen < 16 && *s && s[1]; + sk->protect.ivlen++, s += 2) + sk->protect.iv[sk->protect.ivlen] = xtoi_2 (s); + + /* Now write the *protected* secret key to the file. */ + { + char name_buffer[50]; + char *fname; + IOBUF fp; + mode_t oldmask; + + keyid_from_sk (sk, NULL); + sprintf (name_buffer,"sk_%08lX%08lX.gpg", + (ulong)sk->keyid[0], (ulong)sk->keyid[1]); + + fname = make_filename (backup_dir, name_buffer, NULL); + oldmask = umask (077); + if (is_secured_filename (fname)) + { + fp = NULL; + errno = EPERM; + } + else + fp = iobuf_create (fname); + umask (oldmask); + if (!fp) + { + log_error (_("can't create backup file `%s': %s\n"), + fname, strerror(errno) ); + xfree (fname); + free_secret_key (sk_unprotected); + free_secret_key (sk_protected); + return G10ERR_OPEN_FILE; + } + + pkt = xcalloc (1, sizeof *pkt); + pkt->pkttype = PKT_SECRET_KEY; + pkt->pkt.secret_key = sk_protected; + sk_protected = NULL; + + rc = build_packet (fp, pkt); + if (rc) + { + log_error("build packet failed: %s\n", g10_errstr(rc) ); + iobuf_cancel (fp); + } + else + { + byte array[MAX_FINGERPRINT_LEN]; + char *fprbuf, *p; + + iobuf_close (fp); + iobuf_ioctl (NULL, 2, 0, (char*)fname); + log_info (_("NOTE: backup of card key saved to `%s'\n"), fname); + + fingerprint_from_sk (sk, array, &n); + p = fprbuf = xmalloc (MAX_FINGERPRINT_LEN*2 + 1 + 1); + for (i=0; i < n ; i++, p += 2) + sprintf (p, "%02X", array[i]); + *p++ = ' '; + *p = 0; + + write_status_text_and_buffer (STATUS_BACKUP_KEY_CREATED, + fprbuf, + fname, strlen (fname), + 0); + xfree (fprbuf); + } + free_packet (pkt); + xfree (pkt); + xfree (fname); + if (rc) + { + free_secret_key (sk_unprotected); + return rc; + } + } + + /* Create the public key from the secret key. */ + pk = xcalloc (1, sizeof *pk ); + pk->timestamp = sk->timestamp; + pk->version = sk->version; + if (expireval) + pk->expiredate = sk->expiredate = sk->timestamp + expireval; + pk->pubkey_algo = sk->pubkey_algo; + n = pubkey_get_npkey (sk->pubkey_algo); + for (i=0; i < n; i++) + pk->pkey[i] = mpi_copy (sk->skey[i]); + + /* Build packets and add them to the node lists. */ + pkt = xcalloc (1,sizeof *pkt); + pkt->pkttype = is_primary ? PKT_PUBLIC_KEY : PKT_PUBLIC_SUBKEY; + pkt->pkt.public_key = pk; + add_kbnode(pub_root, new_kbnode( pkt )); + + pkt = xcalloc (1,sizeof *pkt); + pkt->pkttype = is_primary ? PKT_SECRET_KEY : PKT_SECRET_SUBKEY; + pkt->pkt.secret_key = sk; + add_kbnode(sec_root, new_kbnode( pkt )); + + return 0; +#else + return -1; +#endif /*!ENABLE_CARD_SUPPORT*/ +} + + +#ifdef ENABLE_CARD_SUPPORT +int +save_unprotected_key_to_card (PKT_secret_key *sk, int keyno) +{ + int rc; + unsigned char *rsa_n = NULL; + unsigned char *rsa_e = NULL; + unsigned char *rsa_p = NULL; + unsigned char *rsa_q = NULL; + unsigned int rsa_n_len, rsa_e_len, rsa_p_len, rsa_q_len; + unsigned char *sexp = NULL; + unsigned char *p; + char numbuf[55], numbuf2[50]; + + assert (is_RSA (sk->pubkey_algo)); + assert (!sk->is_protected); + + /* Copy the parameters into straight buffers. */ + rsa_n = mpi_get_secure_buffer (sk->skey[0], &rsa_n_len, NULL); + rsa_e = mpi_get_secure_buffer (sk->skey[1], &rsa_e_len, NULL); + rsa_p = mpi_get_secure_buffer (sk->skey[3], &rsa_p_len, NULL); + rsa_q = mpi_get_secure_buffer (sk->skey[4], &rsa_q_len, NULL); + if (!rsa_n || !rsa_e || !rsa_p || !rsa_q) + { + rc = G10ERR_INV_ARG; + goto leave; + } + + /* Put the key into an S-expression. */ + sexp = p = xmalloc_secure (30 + + rsa_n_len + rsa_e_len + rsa_p_len + rsa_q_len + + 4*sizeof (numbuf) + 25 + sizeof(numbuf) + 20); + + p = stpcpy (p,"(11:private-key(3:rsa(1:n"); + sprintf (numbuf, "%u:", rsa_n_len); + p = stpcpy (p, numbuf); + memcpy (p, rsa_n, rsa_n_len); + p += rsa_n_len; + + sprintf (numbuf, ")(1:e%u:", rsa_e_len); + p = stpcpy (p, numbuf); + memcpy (p, rsa_e, rsa_e_len); + p += rsa_e_len; + + sprintf (numbuf, ")(1:p%u:", rsa_p_len); + p = stpcpy (p, numbuf); + memcpy (p, rsa_p, rsa_p_len); + p += rsa_p_len; + + sprintf (numbuf, ")(1:q%u:", rsa_q_len); + p = stpcpy (p, numbuf); + memcpy (p, rsa_q, rsa_q_len); + p += rsa_q_len; + + p = stpcpy (p,"))(10:created-at"); + sprintf (numbuf2, "%lu", (unsigned long)sk->timestamp); + sprintf (numbuf, "%lu:", (unsigned long)strlen (numbuf2)); + p = stpcpy (stpcpy (stpcpy (p, numbuf), numbuf2), "))"); + + /* Fixme: Unfortunately we don't have the serialnumber available - + thus we can't pass it down to the agent. */ + rc = agent_scd_writekey (keyno, NULL, sexp, p - sexp); + + leave: + xfree (sexp); + xfree (rsa_n); + xfree (rsa_e); + xfree (rsa_p); + xfree (rsa_q); + return rc; +} +#endif /*ENABLE_CARD_SUPPORT*/ diff --git a/g10/keyid.c b/g10/keyid.c index aaa70cccb..5eb51c1f4 100644 --- a/g10/keyid.c +++ b/g10/keyid.c @@ -1,5 +1,6 @@ /* keyid.c - key ID and fingerprint handling - * Copyright (C) 1998, 1999, 2000, 2001, 2003 Free Software Foundation, Inc. + * Copyright (C) 1998, 1999, 2000, 2001, 2003, + * 2004, 2006 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -15,7 +16,8 @@ * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. */ #include <config.h> @@ -31,11 +33,9 @@ #include "main.h" #include "packet.h" #include "options.h" -#include "mpi.h" #include "keydb.h" #include "i18n.h" - int pubkey_letter( int algo ) { @@ -50,66 +50,93 @@ pubkey_letter( int algo ) } } -static gcry_md_hd_t -do_fingerprint_md( PKT_public_key *pk ) +/* This function is useful for v4 fingerprints and v3 or v4 key + signing. */ +void +hash_public_key( gcry_md_hd_t md, PKT_public_key *pk ) { - gcry_md_hd_t md; - unsigned int n; - unsigned int nn[PUBKEY_MAX_NPKEY]; - byte *pp[PUBKEY_MAX_NPKEY]; - int i; - int npkey = pubkey_get_npkey( pk->pubkey_algo ); - - gcry_md_open (&md, pk->version < 4 ? DIGEST_ALGO_RMD160 - : DIGEST_ALGO_SHA1, 0); + unsigned int n = 6; + unsigned int nb[PUBKEY_MAX_NPKEY]; + unsigned int nn[PUBKEY_MAX_NPKEY]; + byte *pp[PUBKEY_MAX_NPKEY]; + int i; + size_t nbits, nbytes; + int npkey = pubkey_get_npkey (pk->pubkey_algo); - n = pk->version < 4 ? 8 : 6; - for(i=0; i < npkey; i++ ) { - size_t nbytes; + /* Two extra bytes for the expiration date in v3 */ + if(pk->version<4) + n+=2; - if (gcry_mpi_print( GCRYMPI_FMT_PGP, NULL, 0, &nbytes, pk->pkey[i] )) + if (npkey==0 && pk->pkey[0] + && gcry_mpi_get_flag (pk->pkey[0], GCRYMPI_FLAG_OPAQUE)) + { + pp[0] = gcry_mpi_get_opaque (pk->pkey[0], &nbits); + nn[0] = (nbits+7)/8; + n+=nn[0]; + } + else + for(i=0; i < npkey; i++ ) + { + nb[i] = gcry_mpi_get_nbits (pk->pkey[i]); + if (gcry_mpi_print (GCRYMPI_FMT_PGP, NULL, 0, &nbytes, pk->pkey[i])) BUG (); - /* fixme: we should try to allocate a buffer on the stack */ - pp[i] = xmalloc(nbytes); - if (gcry_mpi_print ( GCRYMPI_FMT_PGP, pp[i], nbytes, &nbytes, - pk->pkey[i] )) + pp[i] = xmalloc (nbytes); + if (gcry_mpi_print (GCRYMPI_FMT_PGP, pp[i], nbytes, + &nbytes, pk->pkey[i])) BUG (); - nn[i] = nbytes; - n += nn[i]; + nn[i] = nbytes; + n += 2 + nn[i]; + } + + gcry_md_putc ( md, 0x99 ); /* ctb */ + /* What does it mean if n is greater than than 0xFFFF ? */ + gcry_md_putc ( md, n >> 8 ); /* 2 byte length header */ + gcry_md_putc ( md, n ); + gcry_md_putc ( md, pk->version ); + + gcry_md_putc ( md, pk->timestamp >> 24 ); + gcry_md_putc ( md, pk->timestamp >> 16 ); + gcry_md_putc ( md, pk->timestamp >> 8 ); + gcry_md_putc ( md, pk->timestamp ); + + if(pk->version<4) + { + u16 days=0; + if(pk->expiredate) + days=(u16)((pk->expiredate - pk->timestamp) / 86400L); + + gcry_md_putc ( md, days >> 8 ); + gcry_md_putc ( md, days ); } - gcry_md_putc ( md, 0x99 ); /* ctb */ - gcry_md_putc ( md, n >> 8 ); /* 2 byte length header */ - gcry_md_putc ( md, n ); - if( pk->version < 4 ) - gcry_md_putc ( md, 3 ); - else - gcry_md_putc ( md, 4 ); - - { u32 a = pk->timestamp; - gcry_md_putc ( md, a >> 24 ); - gcry_md_putc ( md, a >> 16 ); - gcry_md_putc ( md, a >> 8 ); - gcry_md_putc ( md, a ); - } - if( pk->version < 4 ) { - u16 a; + gcry_md_putc ( md, pk->pubkey_algo ); - if( pk->expiredate ) - a = (u16)((pk->expiredate - pk->timestamp) / 86400L); - else - a = 0; - gcry_md_putc ( md, a >> 8 ); - gcry_md_putc ( md, a ); - } - gcry_md_putc ( md, pk->pubkey_algo ); - for(i=0; i < npkey; i++ ) { - gcry_md_write( md, pp[i], nn[i] ); - xfree (pp[i]); + if(npkey==0 && pk->pkey[0] + && gcry_mpi_get_flag (pk->pkey[0], GCRYMPI_FLAG_OPAQUE)) + { + gcry_md_write (md, pp[0], nn[0]); } - gcry_md_final ( md ); + else + for(i=0; i < npkey; i++ ) + { + gcry_md_putc ( md, nb[i]>>8); + gcry_md_putc ( md, nb[i] ); + gcry_md_write ( md, pp[i], nn[i] ); + xfree(pp[i]); + } +} + +static gcry_md_hd_t +do_fingerprint_md( PKT_public_key *pk ) +{ + gcry_md_hd_t md; - return md; + if (gcry_md_open (&md, DIGEST_ALGO_SHA1, 0)) + BUG (); + hash_public_key(md,pk); + gcry_md_final( md ); + + return md; } static gcry_md_hd_t @@ -119,13 +146,16 @@ do_fingerprint_md_sk( PKT_secret_key *sk ) int npkey = pubkey_get_npkey( sk->pubkey_algo ); /* npkey is correct! */ int i; + if(npkey==0) + return NULL; + pk.pubkey_algo = sk->pubkey_algo; pk.version = sk->version; pk.timestamp = sk->timestamp; pk.expiredate = sk->expiredate; pk.pubkey_algo = sk->pubkey_algo; for( i=0; i < npkey; i++ ) - pk.pkey[i] = sk->skey[i]; + pk.pkey[i] = sk->skey[i]; return do_fingerprint_md( &pk ); } @@ -154,6 +184,112 @@ v3_keyid (gcry_mpi_t a, u32 *ki) } +size_t +keystrlen(void) +{ + switch(opt.keyid_format) + { + case KF_SHORT: + return 8; + + case KF_LONG: + return 16; + + case KF_0xSHORT: + return 10; + + case KF_0xLONG: + return 18; + + default: + BUG(); + } +} + +const char * +keystr(u32 *keyid) +{ + static char keyid_str[19]; + + switch(opt.keyid_format) + { + case KF_SHORT: + sprintf(keyid_str,"%08lX",(ulong)keyid[1]); + break; + + case KF_LONG: + if(keyid[0]) + sprintf(keyid_str,"%08lX%08lX",(ulong)keyid[0],(ulong)keyid[1]); + else + sprintf(keyid_str,"%08lX",(ulong)keyid[1]); + break; + + case KF_0xSHORT: + sprintf(keyid_str,"0x%08lX",(ulong)keyid[1]); + break; + + case KF_0xLONG: + if(keyid[0]) + sprintf(keyid_str,"0x%08lX%08lX",(ulong)keyid[0],(ulong)keyid[1]); + else + sprintf(keyid_str,"0x%08lX",(ulong)keyid[1]); + break; + + default: + BUG(); + } + + return keyid_str; +} + +const char * +keystr_from_pk(PKT_public_key *pk) +{ + keyid_from_pk(pk,NULL); + + return keystr(pk->keyid); +} + +const char * +keystr_from_sk(PKT_secret_key *sk) +{ + keyid_from_sk(sk,NULL); + + return keystr(sk->keyid); +} + +const char * +keystr_from_desc(KEYDB_SEARCH_DESC *desc) +{ + switch(desc->mode) + { + case KEYDB_SEARCH_MODE_LONG_KID: + case KEYDB_SEARCH_MODE_SHORT_KID: + return keystr(desc->u.kid); + + case KEYDB_SEARCH_MODE_FPR20: + { + u32 keyid[2]; + + keyid[0] = ((unsigned char)desc->u.fpr[12] << 24 + | (unsigned char)desc->u.fpr[13] << 16 + | (unsigned char)desc->u.fpr[14] << 8 + | (unsigned char)desc->u.fpr[15]); + keyid[1] = ((unsigned char)desc->u.fpr[16] << 24 + | (unsigned char)desc->u.fpr[17] << 16 + | (unsigned char)desc->u.fpr[18] << 8 + | (unsigned char)desc->u.fpr[19]); + + return keystr(keyid); + } + + case KEYDB_SEARCH_MODE_FPR16: + return "?v3 fpr?"; + + default: + BUG(); + } +} /**************** * Get the keyid from the secret key and put it into keyid @@ -162,29 +298,51 @@ v3_keyid (gcry_mpi_t a, u32 *ki) u32 keyid_from_sk( PKT_secret_key *sk, u32 *keyid ) { - u32 lowbits; - u32 dummy_keyid[2]; + u32 lowbits; + u32 dummy_keyid[2]; - if( !keyid ) - keyid = dummy_keyid; + if( !keyid ) + keyid = dummy_keyid; - if( sk->version < 4 && is_RSA(sk->pubkey_algo) ) { - keyid[0] = keyid[1] = 0; - lowbits = pubkey_get_npkey(sk->pubkey_algo) ? - v3_keyid (sk->skey[0], keyid) : 0; + if( sk->keyid[0] || sk->keyid[1] ) + { + keyid[0] = sk->keyid[0]; + keyid[1] = sk->keyid[1]; + lowbits = keyid[1]; } - else { - const byte *dp; - gcry_md_hd_t md; - md = do_fingerprint_md_sk(sk); - dp = gcry_md_read ( md, 0 ); - 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] ; - lowbits = keyid[1]; - gcry_md_close (md); + else if( sk->version < 4 ) + { + if( is_RSA(sk->pubkey_algo) ) + { + lowbits = (pubkey_get_npkey (sk->pubkey_algo) ? + v3_keyid( sk->skey[0], keyid ) : 0); /* Take n. */ + sk->keyid[0]=keyid[0]; + sk->keyid[1]=keyid[1]; + } + else + sk->keyid[0]=sk->keyid[1]=keyid[0]=keyid[1]=lowbits=0xFFFFFFFF; + } + else + { + const byte *dp; + gcry_md_hd_t md; + + md = do_fingerprint_md_sk(sk); + if(md) + { + dp = gcry_md_read (md, 0); + 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] ; + lowbits = keyid[1]; + gcry_md_close (md); + sk->keyid[0] = keyid[0]; + sk->keyid[1] = keyid[1]; + } + else + sk->keyid[0]=sk->keyid[1]=keyid[0]=keyid[1]=lowbits=0xFFFFFFFF; } - return lowbits; + return lowbits; } @@ -195,38 +353,51 @@ keyid_from_sk( PKT_secret_key *sk, u32 *keyid ) u32 keyid_from_pk( PKT_public_key *pk, u32 *keyid ) { - u32 lowbits; - u32 dummy_keyid[2]; + u32 lowbits; + u32 dummy_keyid[2]; - if( !keyid ) - keyid = dummy_keyid; + if( !keyid ) + keyid = dummy_keyid; - if( pk->keyid[0] || pk->keyid[1] ) { - keyid[0] = pk->keyid[0]; - keyid[1] = pk->keyid[1]; - lowbits = keyid[1]; + if( pk->keyid[0] || pk->keyid[1] ) + { + keyid[0] = pk->keyid[0]; + keyid[1] = pk->keyid[1]; + lowbits = keyid[1]; } - else if( pk->version < 4 && is_RSA(pk->pubkey_algo) ) { - keyid[0] = keyid[1] = 0; - lowbits = pubkey_get_npkey(pk->pubkey_algo) ? - v3_keyid (pk->pkey[0], keyid) : 0 ; - pk->keyid[0] = keyid[0]; - pk->keyid[1] = keyid[1]; + else if( pk->version < 4 ) + { + if( is_RSA(pk->pubkey_algo) ) + { + lowbits = (pubkey_get_npkey (pk->pubkey_algo) ? + v3_keyid ( pk->pkey[0], keyid ) : 0); /* From n. */ + pk->keyid[0] = keyid[0]; + pk->keyid[1] = keyid[1]; + } + else + pk->keyid[0]=pk->keyid[1]=keyid[0]=keyid[1]=lowbits=0xFFFFFFFF; } - else { - const byte *dp; - gcry_md_hd_t md; - md = do_fingerprint_md(pk); - dp = gcry_md_read ( md, 0 ); - 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] ; - lowbits = keyid[1]; - gcry_md_close (md); - pk->keyid[0] = keyid[0]; - pk->keyid[1] = keyid[1]; + else + { + const byte *dp; + gcry_md_hd_t md; + + md = do_fingerprint_md(pk); + if(md) + { + dp = gcry_md_read ( md, 0 ); + 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] ; + lowbits = keyid[1]; + gcry_md_close (md); + pk->keyid[0] = keyid[0]; + pk->keyid[1] = keyid[1]; + } + else + pk->keyid[0]=pk->keyid[1]=keyid[0]=keyid[1]=lowbits=0xFFFFFFFF; } - return lowbits; + return lowbits; } @@ -282,16 +453,16 @@ namehash_from_uid(PKT_user_id *uid) { if(uid->namehash==NULL) { - uid->namehash=xmalloc (20); + uid->namehash = xmalloc(20); if(uid->attrib_data) - gcry_md_hash_buffer (GCRY_MD_RMD160, uid->namehash, - uid->attrib_data,uid->attrib_len); + gcry_md_hash_buffer (GCRY_MD_RMD160, uid->namehash, + uid->attrib_data, uid->attrib_len); else gcry_md_hash_buffer (GCRY_MD_RMD160, uid->namehash, - uid->name,uid->len); + uid->name, uid->len); } - + return uid->namehash; } @@ -397,6 +568,46 @@ expirestr_from_sig( PKT_signature *sig ) } const char * +revokestr_from_pk( PKT_public_key *pk ) +{ + static char buffer[11+5]; + time_t atime; + + if(!pk->revoked.date) + return _("never "); + atime=pk->revoked.date; + return mk_datestr (buffer, atime); +} + + +const char * +usagestr_from_pk( PKT_public_key *pk ) +{ + static char buffer[10]; + int i = 0; + unsigned int use = pk->pubkey_usage; + + if ( use & PUBKEY_USAGE_SIG ) + buffer[i++] = 'S'; + + if ( use & PUBKEY_USAGE_CERT ) + buffer[i++] = 'C'; + + if ( use & PUBKEY_USAGE_ENC ) + buffer[i++] = 'E'; + + if ( (use & PUBKEY_USAGE_AUTH) ) + buffer[i++] = 'A'; + + while (i < 4) + buffer[i++] = ' '; + + buffer[i] = 0; + return buffer; +} + + +const char * colon_strtime (u32 t) { if (!t) @@ -465,145 +676,143 @@ colon_expirestr_from_sig (PKT_signature *sig) byte * fingerprint_from_pk( PKT_public_key *pk, byte *array, size_t *ret_len ) { - byte *buf; - const byte *dp; - size_t len; - - if( pk->version < 4 && is_RSA(pk->pubkey_algo) ) { - /* RSA in version 3 packets is special */ - gcry_md_hd_t md; - - gcry_md_open (&md, DIGEST_ALGO_MD5, 0); - if( pubkey_get_npkey( pk->pubkey_algo ) > 1 ) { - size_t nbytes; - - if (gcry_mpi_print( GCRYMPI_FMT_USG, NULL, 0, &nbytes, - pk->pkey[0])) - BUG (); - /* fixme: allocate it on the stack */ - buf = xmalloc(nbytes); - if (gcry_mpi_print (GCRYMPI_FMT_USG, buf, nbytes, NULL,pk->pkey[0])) - BUG (); - gcry_md_write (md, buf, nbytes); - xfree (buf); - if (gcry_mpi_print( GCRYMPI_FMT_USG, NULL, 0, &nbytes, pk->pkey[1])) - BUG (); - /* fixme: allocate it on the stack */ - buf = xmalloc(nbytes); - if (gcry_mpi_print( GCRYMPI_FMT_USG, buf, nbytes, NULL,pk->pkey[1])) - BUG (); - gcry_md_write( md, buf, nbytes ); - xfree(buf); - } - gcry_md_final (md); - if( !array ) - array = xmalloc ( 16 ); - len = 16; - memcpy(array, gcry_md_read (md, DIGEST_ALGO_MD5), 16 ); - gcry_md_close (md); + byte *buf; + const byte *dp; + size_t len, nbytes; + int i; + + if ( pk->version < 4 ) + { + if ( is_RSA(pk->pubkey_algo) ) + { + /* RSA in version 3 packets is special. */ + gcry_md_hd_t md; + + if (gcry_md_open (&md, DIGEST_ALGO_MD5, 0)) + BUG (); + if ( pubkey_get_npkey (pk->pubkey_algo) > 1 ) + { + for (i=0; i < 2; i++) + { + if (gcry_mpi_print (GCRYMPI_FMT_USG, NULL, 0, + &nbytes, pk->pkey[i])) + BUG (); + /* fixme: Better allocate BUF on the stack */ + buf = xmalloc (nbytes); + if (gcry_mpi_print (GCRYMPI_FMT_USG, buf, nbytes, + NULL, pk->pkey[i])) + BUG (); + gcry_md_write (md, buf, nbytes); + xfree (buf); + } + } + gcry_md_final (md); + if (!array) + array = xmalloc (16); + len = 16; + memcpy (array, gcry_md_read (md, DIGEST_ALGO_MD5), 16); + gcry_md_close(md); + } + else + { + if (!array) + array = xmalloc(16); + len = 16; + memset (array,0,16); + } } - else { - gcry_md_hd_t md; - md = do_fingerprint_md(pk); - dp = gcry_md_read ( md, 0 ); - len = gcry_md_get_algo_dlen (gcry_md_get_algo (md)); - assert( len <= MAX_FINGERPRINT_LEN ); - if( !array ) - array = xmalloc ( len ); - memcpy(array, dp, len ); - pk->keyid[0] = dp[12] << 24 | dp[13] << 16 | dp[14] << 8 | dp[15] ; - pk->keyid[1] = dp[16] << 24 | dp[17] << 16 | dp[18] << 8 | dp[19] ; - gcry_md_close (md); + else + { + gcry_md_hd_t md; + + md = do_fingerprint_md(pk); + dp = gcry_md_read( md, 0 ); + len = gcry_md_get_algo_dlen (gcry_md_get_algo (md)); + assert( len <= MAX_FINGERPRINT_LEN ); + if (!array) + array = xmalloc ( len ); + memcpy (array, dp, len ); + pk->keyid[0] = dp[12] << 24 | dp[13] << 16 | dp[14] << 8 | dp[15] ; + pk->keyid[1] = dp[16] << 24 | dp[17] << 16 | dp[18] << 8 | dp[19] ; + gcry_md_close( md); } - - *ret_len = len; - return array; + + *ret_len = len; + return array; } byte * fingerprint_from_sk( PKT_secret_key *sk, byte *array, size_t *ret_len ) { - byte *buf; - const char *dp; - size_t len; - - if( sk->version < 4 && is_RSA(sk->pubkey_algo) ) { - /* RSA in version 3 packets is special */ - gcry_md_hd_t md; - - gcry_md_open (&md, DIGEST_ALGO_MD5, 0); - if( pubkey_get_npkey( sk->pubkey_algo ) > 1 ) { - size_t nbytes; - - if (gcry_mpi_print( GCRYMPI_FMT_USG, NULL, 0, &nbytes, sk->skey[0])) - BUG (); - /* fixme: allocate it on the stack */ - buf = xmalloc(nbytes); - if (gcry_mpi_print (GCRYMPI_FMT_USG, buf, nbytes, NULL,sk->skey[0])) - BUG (); - gcry_md_write (md, buf, nbytes); - xfree (buf); - if (gcry_mpi_print( GCRYMPI_FMT_USG, NULL, 0, &nbytes, sk->skey[1])) - BUG (); - /* fixme: allocate it on the stack */ - buf = xmalloc(nbytes); - if (gcry_mpi_print( GCRYMPI_FMT_USG, buf,nbytes, NULL, sk->skey[1])) - BUG (); - gcry_md_write( md, buf, nbytes ); - xfree(buf); - } - gcry_md_final (md); - if( !array ) - array = xmalloc ( 16 ); - len = 16; - memcpy(array, gcry_md_read (md, DIGEST_ALGO_MD5), 16 ); - gcry_md_close (md); + byte *buf; + const char *dp; + size_t len, nbytes; + int i; + + if (sk->version < 4) + { + if ( is_RSA(sk->pubkey_algo) ) + { + /* RSA in version 3 packets is special. */ + gcry_md_hd_t md; + + if (gcry_md_open (&md, DIGEST_ALGO_MD5, 0)) + BUG (); + if (pubkey_get_npkey( sk->pubkey_algo ) > 1) + { + for (i=0; i < 2; i++) + { + if (gcry_mpi_print (GCRYMPI_FMT_USG, NULL, 0, + &nbytes, sk->skey[i])) + BUG (); + /* fixme: Better allocate BUF on the stack */ + buf = xmalloc (nbytes); + if (gcry_mpi_print (GCRYMPI_FMT_USG, buf, nbytes, + NULL, sk->skey[i])) + BUG (); + gcry_md_write (md, buf, nbytes); + xfree (buf); + } + } + gcry_md_final(md); + if (!array) + array = xmalloc (16); + len = 16; + memcpy (array, gcry_md_read (md, DIGEST_ALGO_MD5), 16); + gcry_md_close (md); + } + else + { + if (!array) + array = xmalloc (16); + len=16; + memset (array,0,16); + } } - else { - gcry_md_hd_t md; - - md = do_fingerprint_md_sk(sk); - dp = gcry_md_read ( md, 0 ); - len = gcry_md_get_algo_dlen (gcry_md_get_algo (md)); - assert( len <= MAX_FINGERPRINT_LEN ); - if( !array ) - array = xmalloc ( len ); - memcpy(array, dp, len ); - gcry_md_close (md); + else + { + gcry_md_hd_t md; + + md = do_fingerprint_md_sk(sk); + if (md) + { + dp = gcry_md_read ( md, 0 ); + len = gcry_md_get_algo_dlen ( gcry_md_get_algo (md) ); + assert ( len <= MAX_FINGERPRINT_LEN ); + if (!array) + array = xmalloc( len ); + memcpy (array, dp, len); + gcry_md_close (md); + } + else + { + len = MAX_FINGERPRINT_LEN; + if (!array) + array = xmalloc (len); + memset (array, 0, len); + } } - - *ret_len = len; - return array; -} - - -/* Create a serialno/fpr string from the serial number and the secret - * key. caller must free the returned string. There is no error - * return. */ -char * -serialno_and_fpr_from_sk (const unsigned char *sn, size_t snlen, - PKT_secret_key *sk) -{ - unsigned char fpr[MAX_FINGERPRINT_LEN]; - size_t fprlen; - char *buffer, *p; - int i; - fingerprint_from_sk (sk, fpr, &fprlen); - buffer = p= xmalloc (snlen*2 + 1 + fprlen*2 + 1); - for (i=0; i < snlen; i++, p+=2) - sprintf (p, "%02X", sn[i]); - *p++ = '/'; - for (i=0; i < fprlen; i++, p+=2) - sprintf (p, "%02X", fpr[i]); - *p = 0; - return buffer; + *ret_len = len; + return array; } - - - - - - - - diff --git a/g10/keylist.c b/g10/keylist.c index 50850de71..0ff788e18 100644 --- a/g10/keylist.c +++ b/g10/keylist.c @@ -1,6 +1,6 @@ -/* keylist.c - List all or selected keys - * Copyright (C) 1998, 1999, 2000, 2001, 2002, - * 2003 Free Software Foundation, Inc. +/* keylist.c - print keys + * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, + * 2004, 2005 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -16,7 +16,8 @@ * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. */ #include <config.h> @@ -26,11 +27,11 @@ #include <errno.h> #include <assert.h> +#include "gpg.h" #include "options.h" #include "packet.h" #include "errors.h" #include "keydb.h" -#include "memory.h" #include "photoid.h" #include "util.h" #include "ttyio.h" @@ -95,6 +96,13 @@ public_key_list( STRLIST list ) printf("\n"); } + /* We need to do the stale check right here because it might need to + update the keyring while we already have the keyring open. This + is very bad for W32 because of a sharing violation. For real OSes + it might lead to false results if we are later listing a keyring + which is associated with the inode of a deleted file. */ + check_trustdb_stale (); + if( !list ) list_all(0); else @@ -104,6 +112,8 @@ public_key_list( STRLIST list ) void secret_key_list( STRLIST list ) { + check_trustdb_stale (); + if( !list ) list_all(1); else /* List by user id */ @@ -113,21 +123,18 @@ secret_key_list( STRLIST list ) void print_seckey_info (PKT_secret_key *sk) { - u32 sk_keyid[2]; - size_t n; - char *p; - - keyid_from_sk (sk, sk_keyid); - tty_printf ("\nsec %4u%c/%08lX %s ", - nbits_from_sk (sk), - pubkey_letter (sk->pubkey_algo), - (ulong)sk_keyid[1], datestr_from_sk (sk)); - - p = get_user_id (sk_keyid, &n); - tty_print_utf8_string (p, n); - xfree (p); + u32 keyid[2]; + char *p; - tty_printf ("\n"); + keyid_from_sk (sk, keyid); + p=get_user_id_native(keyid); + + tty_printf ("\nsec %4u%c/%s %s %s\n", + nbits_from_sk (sk), + pubkey_letter (sk->pubkey_algo), + keystr(keyid), datestr_from_sk (sk), p); + + xfree (p); } /* Print information about the public key. With FP passed as NULL, @@ -136,33 +143,98 @@ print_seckey_info (PKT_secret_key *sk) void print_pubkey_info (FILE *fp, PKT_public_key *pk) { - u32 pk_keyid[2]; - size_t n; + u32 keyid[2]; char *p; - keyid_from_pk (pk, pk_keyid); + keyid_from_pk (pk, keyid); + + /* If the pk was chosen by a particular user ID, that is the one to + print. */ + if(pk->user_id) + p=utf8_to_native(pk->user_id->name,pk->user_id->len,0); + else + p=get_user_id_native(keyid); + if (fp) - fprintf (fp, "pub %4u%c/%08lX %s ", + fprintf (fp, "pub %4u%c/%s %s %s\n", nbits_from_pk (pk), pubkey_letter (pk->pubkey_algo), - (ulong)pk_keyid[1], datestr_from_pk (pk)); + keystr(keyid), datestr_from_pk (pk), p); else - tty_printf ("\npub %4u%c/%08lX %s ", - nbits_from_pk (pk), - pubkey_letter (pk->pubkey_algo), - (ulong)pk_keyid[1], datestr_from_pk (pk)); + tty_printf ("\npub %4u%c/%s %s %s\n", + nbits_from_pk (pk), pubkey_letter (pk->pubkey_algo), + keystr(keyid), datestr_from_pk (pk), p); - p = get_user_id (pk_keyid, &n); - if (fp) - print_utf8_string2 (fp, p, n, '\n'); - else - tty_print_utf8_string (p, n); xfree (p); - - if (fp) - putc ('\n', fp); - else - tty_printf ("\n\n"); +} + + +/* Print basic information of a secret key including the card serial + number information. */ +void +print_card_key_info (FILE *fp, KBNODE keyblock) +{ + KBNODE node; + int i; + + for (node = keyblock; node; node = node->next ) + { + if (node->pkt->pkttype == PKT_SECRET_KEY + || (node->pkt->pkttype == PKT_SECRET_SUBKEY) ) + { + PKT_secret_key *sk = node->pkt->pkt.secret_key; + + tty_fprintf (fp, "%s%c %4u%c/%s ", + node->pkt->pkttype == PKT_SECRET_KEY? "sec":"ssb", + (sk->protect.s2k.mode==1001)?'#': + (sk->protect.s2k.mode==1002)?'>':' ', + nbits_from_sk (sk), + pubkey_letter (sk->pubkey_algo), + keystr_from_sk(sk)); + tty_fprintf (fp, _("created: %s"), datestr_from_sk (sk)); + tty_fprintf (fp, " "); + tty_fprintf (fp, _("expires: %s"), expirestr_from_sk (sk)); + if (sk->is_protected && sk->protect.s2k.mode == 1002) + { + tty_fprintf (fp, "\n "); + tty_fprintf (fp, _("card-no: ")); + if (sk->protect.ivlen == 16 + && !memcmp (sk->protect.iv, "\xD2\x76\x00\x01\x24\x01", 6)) + { + /* This is an OpenPGP card. */ + for (i=8; i < 14; i++) + { + if (i == 10) + tty_fprintf (fp, " "); + tty_fprintf (fp, "%02X", sk->protect.iv[i]); + } + } + else + { /* Something is wrong: Print all. */ + for (i=0; i < sk->protect.ivlen; i++) + tty_fprintf (fp, "%02X", sk->protect.iv[i]); + } + } + tty_fprintf (fp, "\n"); + } + } +} + + + +/* Flags = 0x01 hashed 0x02 critical */ +static void +status_one_subpacket(sigsubpkttype_t type,size_t len,int flags,const byte *buf) +{ + char status[40]; + + /* Don't print these. */ + if(len>256) + return; + + sprintf(status,"%d %u %u ",type,flags,(unsigned int)len); + + write_status_text_and_buffer(STATUS_SIG_SUBPACKET,status,buf,len,0); } /* @@ -184,7 +256,7 @@ show_policy_url(PKT_signature *sig,int indent,int mode) if(mode!=2) { int i; - char *str; + const char *str; for(i=0;i<indent;i++) putchar(' '); @@ -206,7 +278,6 @@ show_policy_url(PKT_signature *sig,int indent,int mode) } } - /* mode=0 for stdout. mode=1 for log_info + status messages @@ -226,7 +297,7 @@ show_keyserver_url(PKT_signature *sig,int indent,int mode) if(mode!=2) { int i; - char *str; + const char *str; for(i=0;i<indent;i++) putchar(' '); @@ -243,77 +314,73 @@ show_keyserver_url(PKT_signature *sig,int indent,int mode) fprintf(fp,"\n"); } - /* TODO: put in a status-fd tag for preferred keyservers */ + if(mode) + status_one_subpacket(SIGSUBPKT_PREF_KS,len,(crit?0x02:0)|0x01,p); } } - /* mode=0 for stdout. mode=1 for log_info + status messages mode=2 for status messages only + + which bits: + 1 == standard notations + 2 == user notations */ void -show_notation(PKT_signature *sig,int indent,int mode) +show_notation(PKT_signature *sig,int indent,int mode,int which) { - const byte *p; - size_t len; - int seq=0,crit; FILE *fp=mode?log_get_stream():stdout; + struct notation *nd,*notations; - /* There may be multiple notations in the same sig. */ - - while((p=enum_sig_subpkt(sig->hashed,SIGSUBPKT_NOTATION,&len,&seq,&crit))) - if(len>=8) - { - int n1,n2; - - n1=(p[4]<<8)|p[5]; - n2=(p[6]<<8)|p[7]; - - if(8+n1+n2!=len) - { - log_info(_("WARNING: invalid notation data found\n")); - return; - } - - if(mode!=2) - { - int i; - char *str; - - for(i=0;i<indent;i++) - putchar(' '); + if(which==0) + which=3; - /* This is UTF8 */ - if(crit) - str=_("Critical signature notation: "); - else - str=_("Signature notation: "); - if(mode) - log_info("%s",str); - else - printf("%s",str); - print_utf8_string(fp,p+8,n1); - fprintf(fp,"="); + notations=sig_to_notation(sig); - if(*p&0x80) - print_utf8_string(fp,p+8+n1,n2); - else - fprintf(fp,"[ %s ]",_("not human readable")); + /* There may be multiple notations in the same sig. */ + for(nd=notations;nd;nd=nd->next) + { + if(mode!=2) + { + int has_at=!!strchr(nd->name,'@'); + + if((which&1 && !has_at) || (which&2 && has_at)) + { + int i; + const char *str; + + for(i=0;i<indent;i++) + putchar(' '); + + if(nd->flags.critical) + str=_("Critical signature notation: "); + else + str=_("Signature notation: "); + if(mode) + log_info("%s",str); + else + printf("%s",str); + /* This is all UTF8 */ + print_utf8_string(fp,nd->name,strlen(nd->name)); + fprintf(fp,"="); + print_utf8_string(fp,nd->value,strlen(nd->value)); + fprintf(fp,"\n"); + } + } - fprintf(fp,"\n"); - } + if(mode) + { + write_status_buffer(STATUS_NOTATION_NAME, + nd->name,strlen(nd->name),0); + write_status_buffer(STATUS_NOTATION_DATA, + nd->value,strlen(nd->value),50); + } + } - if(mode) - { - write_status_buffer ( STATUS_NOTATION_NAME, p+8 , n1, 0 ); - write_status_buffer ( STATUS_NOTATION_DATA, p+8+n1, n2, 50 ); - } - } - else - log_info(_("WARNING: invalid notation data found\n")); + free_notation(notations); } static void @@ -346,12 +413,12 @@ list_all( int secret ) hd = keydb_new (secret); if (!hd) - rc = GPG_ERR_GENERAL; + rc = G10ERR_GENERAL; else rc = keydb_search_first (hd); if( rc ) { if( rc != -1 ) - log_error("keydb_search_first failed: %s\n", gpg_strerror (rc) ); + log_error("keydb_search_first failed: %s\n", g10_errstr(rc) ); goto leave; } @@ -359,7 +426,7 @@ list_all( int secret ) do { rc = keydb_get_keyblock (hd, &keyblock); if (rc) { - log_error ("keydb_get_keyblock failed: %s\n", gpg_strerror (rc)); + log_error ("keydb_get_keyblock failed: %s\n", g10_errstr(rc)); goto leave; } if(!opt.with_colons) @@ -383,7 +450,7 @@ list_all( int secret ) keyblock = NULL; } while (!(rc = keydb_search_next (hd))); if( rc && rc != -1 ) - log_error ("keydb_search_next failed: %s\n", gpg_strerror (rc)); + log_error ("keydb_search_next failed: %s\n", g10_errstr(rc)); if(opt.check_sigs && !opt.with_colons) print_signature_stats(&stats); @@ -401,7 +468,7 @@ list_one( STRLIST names, int secret ) KBNODE keyblock = NULL; GETKEY_CTX ctx; const char *resname; - char *keyring_str = _("Keyring"); + const char *keyring_str = _("Keyring"); int i; struct sig_stats stats; @@ -419,7 +486,7 @@ list_one( STRLIST names, int secret ) if( secret ) { rc = get_seckey_bynames( &ctx, NULL, names, &keyblock ); if( rc ) { - log_error("error reading key: %s\n", gpg_strerror (rc) ); + log_error("error reading key: %s\n", g10_errstr(rc) ); get_seckey_end( ctx ); return; } @@ -439,7 +506,7 @@ list_one( STRLIST names, int secret ) else { rc = get_pubkey_bynames( &ctx, NULL, names, &keyblock ); if( rc ) { - log_error("error reading key: %s\n", gpg_strerror (rc) ); + log_error("error reading key: %s\n", g10_errstr(rc) ); get_pubkey_end( ctx ); return; } @@ -463,7 +530,7 @@ list_one( STRLIST names, int secret ) } static void -print_key_data( PKT_public_key *pk, u32 *keyid ) +print_key_data( PKT_public_key *pk ) { int n = pk ? pubkey_get_npkey( pk->pubkey_algo ) : 0; int i; @@ -483,10 +550,10 @@ print_capabilities (PKT_public_key *pk, PKT_secret_key *sk, KBNODE keyblock) { unsigned int use = pk? pk->pubkey_usage : sk->pubkey_usage; - if ( (use & PUBKEY_USAGE_ENC) ) + if ( use & PUBKEY_USAGE_ENC ) putchar ('e'); - if ( (use & PUBKEY_USAGE_SIG) ) + if ( use & PUBKEY_USAGE_SIG ) { putchar ('s'); if( pk? pk->is_primary : sk->is_primary ) @@ -510,9 +577,9 @@ print_capabilities (PKT_public_key *pk, PKT_secret_key *sk, KBNODE keyblock) disabled=pk_is_disabled(pk); if ( pk->is_valid && !pk->is_revoked && !pk->has_expired ) { - if ( (pk->pubkey_usage & PUBKEY_USAGE_ENC) ) + if ( pk->pubkey_usage & PUBKEY_USAGE_ENC ) enc = 1; - if ( (pk->pubkey_usage & PUBKEY_USAGE_SIG) ) + if ( pk->pubkey_usage & PUBKEY_USAGE_SIG ) { sign = 1; if(pk->is_primary) @@ -527,9 +594,9 @@ print_capabilities (PKT_public_key *pk, PKT_secret_key *sk, KBNODE keyblock) sk = k->pkt->pkt.secret_key; if ( sk->is_valid && !sk->is_revoked && !sk->has_expired && sk->protect.s2k.mode!=1001 ) { - if ( (sk->pubkey_usage & PUBKEY_USAGE_ENC) ) + if ( sk->pubkey_usage & PUBKEY_USAGE_ENC ) enc = 1; - if ( (sk->pubkey_usage & PUBKEY_USAGE_SIG) ) + if ( sk->pubkey_usage & PUBKEY_USAGE_SIG ) { sign = 1; if(sk->is_primary) @@ -555,6 +622,51 @@ print_capabilities (PKT_public_key *pk, PKT_secret_key *sk, KBNODE keyblock) putchar(':'); } +/* Flags = 0x01 hashed 0x02 critical */ +static void +print_one_subpacket(sigsubpkttype_t type,size_t len,int flags,const byte *buf) +{ + size_t i; + + printf("spk:%d:%u:%u:",type,flags,(unsigned int)len); + + for(i=0;i<len;i++) + { + /* printable ascii other than : and % */ + if(buf[i]>=32 && buf[i]<=126 && buf[i]!=':' && buf[i]!='%') + printf("%c",buf[i]); + else + printf("%%%02X",buf[i]); + } + + printf("\n"); +} + +void +print_subpackets_colon(PKT_signature *sig) +{ + byte *i; + + assert(opt.show_subpackets); + + for(i=opt.show_subpackets;*i;i++) + { + const byte *p; + size_t len; + int seq,crit; + + seq=0; + + while((p=enum_sig_subpkt(sig->hashed,*i,&len,&seq,&crit))) + print_one_subpacket(*i,len,0x01|(crit?0x02:0),p); + + seq=0; + + while((p=enum_sig_subpkt(sig->unhashed,*i,&len,&seq,&crit))) + print_one_subpacket(*i,len,0x00|(crit?0x02:0),p); + } +} + void dump_attribs(const PKT_user_id *uid,PKT_public_key *pk,PKT_secret_key *sk) { @@ -603,11 +715,8 @@ list_keyblock_print ( KBNODE keyblock, int secret, int fpr, void *opaque ) KBNODE node; PKT_public_key *pk; PKT_secret_key *sk; - u32 keyid[2]; - int any=0; struct sig_stats *stats=opaque; - int newformat=((opt.list_options&LIST_SHOW_VALIDITY) && !secret) - || (opt.list_options&LIST_SHOW_LONG_KEYID); + int skip_sigs=0; /* get the keyid from the keyblock */ node = find_kbnode( keyblock, secret? PKT_SECRET_KEY : PKT_PUBLIC_KEY ); @@ -621,164 +730,184 @@ list_keyblock_print ( KBNODE keyblock, int secret, int fpr, void *opaque ) { pk = NULL; sk = node->pkt->pkt.secret_key; - keyid_from_sk( sk, keyid ); - - printf("sec%c %4u%c/",(sk->protect.s2k.mode==1001)?'#':' ', - nbits_from_sk( sk ),pubkey_letter( sk->pubkey_algo )); - if(opt.list_options&LIST_SHOW_LONG_KEYID) - printf("%08lX%08lX",(ulong)keyid[0],(ulong)keyid[1]); - else - printf("%08lX",(ulong)keyid[1]); + printf("sec%c %4u%c/%s %s",(sk->protect.s2k.mode==1001)?'#': + (sk->protect.s2k.mode==1002)?'>':' ', + nbits_from_sk( sk ),pubkey_letter( sk->pubkey_algo ), + keystr_from_sk(sk),datestr_from_sk( sk )); - printf(" %s%s",datestr_from_sk( sk ),newformat?"":" " ); + if(sk->has_expired) + { + printf(" ["); + printf(_("expired: %s"),expirestr_from_sk(sk)); + printf("]"); + } + else if(sk->expiredate ) + { + printf(" ["); + printf(_("expires: %s"),expirestr_from_sk(sk)); + printf("]"); + } - if(newformat && sk->expiredate ) - printf(_(" [expires: %s]"), expirestr_from_sk( sk ) ); + printf("\n"); } else { - int validity; pk = node->pkt->pkt.public_key; sk = NULL; - keyid_from_pk( pk, keyid ); - - validity=get_validity(pk,NULL); - printf("pub %4u%c/", - nbits_from_pk(pk),pubkey_letter(pk->pubkey_algo)); - - if(opt.list_options&LIST_SHOW_LONG_KEYID) - printf("%08lX%08lX",(ulong)keyid[0],(ulong)keyid[1]); - else - printf("%08lX",(ulong)keyid[1]); + check_trustdb_stale(); - printf(" %s%s",datestr_from_pk( pk ),newformat?"":" " ); + printf("pub %4u%c/%s %s", + nbits_from_pk(pk),pubkey_letter(pk->pubkey_algo), + keystr_from_pk(pk),datestr_from_pk( pk )); /* We didn't include this before in the key listing, but there is room in the new format, so why not? */ - if(newformat && pk->expiredate) - printf(_(" [expires: %s]"), expirestr_from_pk( pk ) ); + if(pk->is_revoked) + { + printf(" ["); + printf(_("revoked: %s"),revokestr_from_pk(pk)); + printf("]"); + } + else if(pk->has_expired) + { + printf(" ["); + printf(_("expired: %s"),expirestr_from_pk(pk)); + printf("]"); + } + else if(pk->expiredate) + { + printf(" ["); + printf(_("expires: %s"),expirestr_from_pk(pk)); + printf("]"); + } + +#if 0 + /* I need to think about this some more. It's easy enough to + include, but it looks sort of confusing in the + listing... */ if(opt.list_options&LIST_SHOW_VALIDITY) - printf(" [%s]",trust_value_to_string(validity)); + { + int validity=get_validity(pk,NULL); + printf(" [%s]",trust_value_to_string(validity)); + } +#endif + + printf("\n"); } + if( fpr ) + print_fingerprint( pk, sk, 0 ); + print_card_serialno (sk); + if( opt.with_key_data ) + print_key_data( pk ); + for( kbctx=NULL; (node=walk_kbnode( keyblock, &kbctx, 0)) ; ) { if( node->pkt->pkttype == PKT_USER_ID && !opt.fast_list_mode ) { - int indent; - /* don't list revoked or expired UIDS unless we are in - * verbose mode and signature listing has not been - * requested */ - if ( !opt.verbose && !opt.list_sigs && - (node->pkt->pkt.user_id->is_revoked || - node->pkt->pkt.user_id->is_expired )) - continue; + PKT_user_id *uid=node->pkt->pkt.user_id; - if(attrib_fp && node->pkt->pkt.user_id->attrib_data!=NULL) - dump_attribs(node->pkt->pkt.user_id,pk,sk); + if(pk && (uid->is_expired || uid->is_revoked) + && !(opt.list_options&LIST_SHOW_UNUSABLE_UIDS)) + { + skip_sigs=1; + continue; + } + else + skip_sigs=0; - if(!any && newformat) - printf("\n"); + if(attrib_fp && uid->attrib_data!=NULL) + dump_attribs(uid,pk,sk); - if((opt.list_options&LIST_SHOW_VALIDITY) && pk) + if((uid->is_revoked || uid->is_expired) + || ((opt.list_options&LIST_SHOW_UID_VALIDITY) && pk)) { - const char *validity= - trust_value_to_string(get_validity(pk,node->pkt->pkt.user_id)); + const char *validity; + int indent; - /* Includes the 3 spaces for [, ], and " ". */ - indent=((opt.list_options&LIST_SHOW_LONG_KEYID)?23:15) - -strlen(validity); + validity=uid_trust_string_fixed(pk,uid); + indent=(keystrlen()+9)-atoi(uid_trust_string_fixed(NULL,NULL)); - if(indent<0) + if(indent<0 || indent>40) indent=0; - printf("uid%*s[%s] ",indent,"",validity); + printf("uid%*s%s ",indent,"",validity); } - else if(newformat) - printf("uid%*s",26,""); - else if(any) - printf("uid%*s",29,""); - - if ( node->pkt->pkt.user_id->is_revoked ) - fputs ("[revoked] ", stdout); - if ( node->pkt->pkt.user_id->is_expired ) - fputs ("[expired] ", stdout); - - print_utf8_string( stdout, node->pkt->pkt.user_id->name, - node->pkt->pkt.user_id->len ); + else + printf("uid%*s", (int)keystrlen()+10,""); + + print_utf8_string( stdout, uid->name, uid->len ); putchar('\n'); - if( !any ) { - if( fpr ) - print_fingerprint( pk, sk, 0 ); - print_card_serialno (sk); - if( opt.with_key_data ) - print_key_data( pk, keyid ); - any = 1; - } - if((opt.list_options&LIST_SHOW_PHOTOS) - && node->pkt->pkt.user_id->attribs!=NULL) - show_photos(node->pkt->pkt.user_id->attribs, - node->pkt->pkt.user_id->numattribs,pk,sk); + if((opt.list_options&LIST_SHOW_PHOTOS) && uid->attribs!=NULL) + show_photos(uid->attribs,uid->numattribs,pk,sk); } - else if( node->pkt->pkttype == PKT_PUBLIC_SUBKEY ) { - u32 keyid2[2]; + else if( node->pkt->pkttype == PKT_PUBLIC_SUBKEY ) + { PKT_public_key *pk2 = node->pkt->pkt.public_key; - if( !any ) { - putchar('\n'); - if( fpr ) - print_fingerprint( pk, sk, 0 ); /* of the main key */ - any = 1; - } - - keyid_from_pk( pk2, keyid2 ); - printf("sub %4u%c/", - nbits_from_pk( pk2 ),pubkey_letter( pk2->pubkey_algo )); - if(opt.list_options&LIST_SHOW_LONG_KEYID) - printf("%08lX%08lX",(ulong)keyid2[0],(ulong)keyid2[1]); + if((pk2->is_revoked || pk2->has_expired) + && !(opt.list_options&LIST_SHOW_UNUSABLE_SUBKEYS)) + { + skip_sigs=1; + continue; + } else - printf("%08lX",(ulong)keyid2[1]); - printf(" %s",datestr_from_pk(pk2)); - if( pk2->expiredate ) - printf(_(" [expires: %s]"), expirestr_from_pk( pk2 ) ); + skip_sigs=0; + + printf("sub %4u%c/%s %s", + nbits_from_pk( pk2 ),pubkey_letter( pk2->pubkey_algo ), + keystr_from_pk(pk2),datestr_from_pk(pk2)); + if( pk2->is_revoked ) + { + printf(" ["); + printf(_("revoked: %s"),revokestr_from_pk(pk2)); + printf("]"); + } + else if( pk2->has_expired ) + { + printf(" ["); + printf(_("expired: %s"),expirestr_from_pk(pk2)); + printf("]"); + } + else if( pk2->expiredate ) + { + printf(" ["); + printf(_("expires: %s"),expirestr_from_pk(pk2)); + printf("]"); + } putchar('\n'); if( fpr > 1 ) - print_fingerprint( pk2, NULL, 0 ); + print_fingerprint( pk2, NULL, 0 ); if( opt.with_key_data ) - print_key_data( pk2, keyid2 ); - } - else if( node->pkt->pkttype == PKT_SECRET_SUBKEY ) { - u32 keyid2[2]; + print_key_data( pk2 ); + } + else if( node->pkt->pkttype == PKT_SECRET_SUBKEY ) + { PKT_secret_key *sk2 = node->pkt->pkt.secret_key; - if( !any ) { - putchar('\n'); - if( fpr ) - print_fingerprint( pk, sk, 0 ); /* of the main key */ - print_card_serialno (sk); - any = 1; - } - - keyid_from_sk( sk2, keyid2 ); - printf("ssb %4u%c/", - nbits_from_sk( sk2 ),pubkey_letter( sk2->pubkey_algo )); - if(opt.list_options&LIST_SHOW_LONG_KEYID) - printf("%08lX%08lX",(ulong)keyid2[0],(ulong)keyid2[1]); - else - printf("%08lX",(ulong)keyid2[1]); - printf(" %s",datestr_from_sk( sk2 ) ); + printf("ssb%c %4u%c/%s %s", + (sk2->protect.s2k.mode==1001)?'#': + (sk2->protect.s2k.mode==1002)?'>':' ', + nbits_from_sk( sk2 ),pubkey_letter( sk2->pubkey_algo ), + keystr_from_sk(sk2),datestr_from_sk( sk2 ) ); if( sk2->expiredate ) - printf(_(" [expires: %s]"), expirestr_from_sk( sk2 ) ); + { + printf(" ["); + printf(_("expires: %s"),expirestr_from_sk(sk2)); + printf("]"); + } putchar('\n'); if( fpr > 1 ) { - print_fingerprint( NULL, sk2, 0 ); - print_card_serialno (sk); + print_fingerprint( NULL, sk2, 0 ); + print_card_serialno (sk2); } - } - else if( opt.list_sigs && node->pkt->pkttype == PKT_SIGNATURE ) { + } + else if( opt.list_sigs + && node->pkt->pkttype == PKT_SIGNATURE + && !skip_sigs ) { PKT_signature *sig = node->pkt->pkt.signature; int sigrc; char *sigstr; @@ -787,42 +916,22 @@ list_keyblock_print ( KBNODE keyblock, int secret, int fpr, void *opaque ) /*fflush(stdout);*/ rc = check_key_signature( keyblock, node, NULL ); switch( gpg_err_code (rc) ) { - case 0: sigrc = '!'; break; - case GPG_ERR_BAD_SIGNATURE: stats->inv_sigs++; sigrc = '-'; break; + case 0: sigrc = '!'; break; + case GPG_ERR_BAD_SIGN: stats->inv_sigs++; sigrc = '-'; break; case GPG_ERR_NO_PUBKEY: case GPG_ERR_UNUSABLE_PUBKEY: stats->no_key++; continue; - default: stats->oth_err++; sigrc = '%'; break; + default: stats->oth_err++; sigrc = '%'; break; } /* TODO: Make sure a cached sig record here still has the pk that issued it. See also keyedit.c:print_and_check_one_sig */ - } else { rc = 0; sigrc = ' '; } - if( !any ) { /* no user id, (maybe a revocation follows)*/ - /* Check if the pk is really revoked - there could be a - 0x20 sig packet there even if we are not revoked - (say, if a revocation key issued the packet, but the - revocation key isn't present to verify it.) */ - if( sig->sig_class == 0x20 && pk->is_revoked ) - puts("[revoked]"); - else if( sig->sig_class == 0x18 ) - puts("[key binding]"); - else if( sig->sig_class == 0x28 ) - puts("[subkey revoked]"); - else - putchar('\n'); - if( fpr ) - print_fingerprint( pk, sk, 0 ); - print_card_serialno (sk); - any=1; - } - if( sig->sig_class == 0x20 || sig->sig_class == 0x28 || sig->sig_class == 0x30 ) sigstr = "rev"; @@ -839,7 +948,7 @@ list_keyblock_print ( KBNODE keyblock, int secret, int fpr, void *opaque ) } fputs( sigstr, stdout ); - printf("%c%c %c%c%c%c%c%c ", + printf("%c%c %c%c%c%c%c%c %s %s", sigrc,(sig->sig_class-0x10>0 && sig->sig_class-0x10<4)?'0'+sig->sig_class-0x10:' ', sig->flags.exportable?' ':'L', @@ -848,31 +957,34 @@ list_keyblock_print ( KBNODE keyblock, int secret, int fpr, void *opaque ) sig->flags.notation?'N':' ', sig->flags.expired?'X':' ', (sig->trust_depth>9)?'T': - (sig->trust_depth>0)?'0'+sig->trust_depth:' '); - if(opt.list_options&LIST_SHOW_LONG_KEYID) - printf("%08lX%08lX",(ulong)sig->keyid[0],(ulong)sig->keyid[1]); - else - printf("%08lX",(ulong)sig->keyid[1]); - printf(" %s ", datestr_from_sig(sig)); + (sig->trust_depth>0)?'0'+sig->trust_depth:' ', + keystr(sig->keyid),datestr_from_sig(sig)); + if(opt.list_options&LIST_SHOW_SIG_EXPIRE) + printf(" %s", expirestr_from_sig(sig)); + printf(" "); if( sigrc == '%' ) - printf("[%s] ", gpg_strerror (rc) ); + printf("[%s] ", g10_errstr(rc) ); else if( sigrc == '?' ) ; else if ( !opt.fast_list_mode ) { size_t n; char *p = get_user_id( sig->keyid, &n ); print_utf8_string( stdout, p, n ); - xfree (p); + xfree(p); } putchar('\n'); - if(sig->flags.policy_url && (opt.list_options&LIST_SHOW_POLICY)) + if(sig->flags.policy_url + && (opt.list_options&LIST_SHOW_POLICY_URLS)) show_policy_url(sig,3,0); - if(sig->flags.notation && (opt.list_options&LIST_SHOW_NOTATION)) - show_notation(sig,3,0); + if(sig->flags.notation && (opt.list_options&LIST_SHOW_NOTATIONS)) + show_notation(sig,3,0, + ((opt.list_options&LIST_SHOW_STD_NOTATIONS)?1:0)+ + ((opt.list_options&LIST_SHOW_USER_NOTATIONS)?2:0)); - if(sig->flags.pref_ks && (opt.list_options&LIST_SHOW_KEYSERVER)) + if(sig->flags.pref_ks + && (opt.list_options&LIST_SHOW_KEYSERVER_URLS)) show_keyserver_url(sig,3,0); /* fixme: check or list other sigs here */ @@ -881,6 +993,29 @@ list_keyblock_print ( KBNODE keyblock, int secret, int fpr, void *opaque ) putchar('\n'); } +void +print_revokers(PKT_public_key *pk) +{ + /* print the revoker record */ + if( !pk->revkey && pk->numrevkeys ) + BUG(); + else + { + int i,j; + + for (i=0; i < pk->numrevkeys; i++) + { + byte *p; + + printf ("rvk:::%d::::::", pk->revkey[i].algid); + p = pk->revkey[i].fpr; + for (j=0; j < 20; j++, p++ ) + printf ("%02X", *p); + printf (":%02x%s:\n", pk->revkey[i].class, + (pk->revkey[i].class&0x40)?"s":""); + } + } +} static void list_keyblock_colon( KBNODE keyblock, int secret, int fpr ) @@ -894,6 +1029,7 @@ list_keyblock_colon( KBNODE keyblock, int secret, int fpr ) int any=0; int trustletter = 0; int ulti_hack = 0; + int i; /* get the keyid from the keyblock */ node = find_kbnode( keyblock, secret? PKT_SECRET_KEY : PKT_PUBLIC_KEY ); @@ -934,34 +1070,46 @@ list_keyblock_colon( KBNODE keyblock, int secret, int fpr ) ulti_hack = 1; putchar(trustletter); } - printf(":%u:%d:%08lX%08lX:%s:%s:", + printf(":%u:%d:%08lX%08lX:%s:%s::", nbits_from_pk( pk ), pk->pubkey_algo, (ulong)keyid[0],(ulong)keyid[1], colon_datestr_from_pk( pk ), colon_strtime (pk->expiredate) ); - if( pk->local_id ) - printf("%lu", pk->local_id ); - putchar(':'); if( !opt.fast_list_mode && !opt.no_expensive_trust_checks ) putchar( get_ownertrust_info(pk) ); putchar(':'); } - + if (opt.fixed_list_mode) { /* do not merge the first uid with the primary key */ putchar(':'); putchar(':'); print_capabilities (pk, sk, keyblock); + if (secret) { + putchar(':'); /* End of field 13. */ + putchar(':'); /* End of field 14. */ + if (sk->protect.s2k.mode == 1001) + putchar('#'); /* Key is just a stub. */ + else if (sk->protect.s2k.mode == 1002) { + /* Key is stored on an external token (card) or handled by + the gpg-agent. Print the serial number of that token + here. */ + for (i=0; i < sk->protect.ivlen; i++) + printf ("%02X", sk->protect.iv[i]); + } + putchar(':'); /* End of field 15. */ + } putchar('\n'); + if(pk) + print_revokers(pk); if( fpr ) print_fingerprint( pk, sk, 0 ); if( opt.with_key_data ) - print_key_data( pk, keyid ); + print_key_data( pk ); any = 1; } - for( kbctx=NULL; (node=walk_kbnode( keyblock, &kbctx, 0)) ; ) { if( node->pkt->pkttype == PKT_USER_ID && !opt.fast_list_mode ) { PKT_user_id *uid=node->pkt->pkt.user_id; @@ -971,11 +1119,10 @@ list_keyblock_colon( KBNODE keyblock, int secret, int fpr ) * Fixme: We need a is_valid flag here too */ if( any ) { - int i; char *str=uid->attrib_data?"uat":"uid"; /* If we're listing a secret key, leave out the - validity values for now. FIXME: This should be - handled better in 1.9. */ + validity values for now. This is handled better in + 1.9. */ if ( sk ) printf("%s:::::",str); else if ( uid->is_revoked ) @@ -1018,7 +1165,7 @@ list_keyblock_colon( KBNODE keyblock, int secret, int fpr ) if( fpr ) print_fingerprint( pk, sk, 0 ); if( opt.with_key_data ) - print_key_data( pk, keyid ); + print_key_data( pk ); any = 1; } } @@ -1051,7 +1198,7 @@ list_keyblock_colon( KBNODE keyblock, int secret, int fpr ) if(trustletter) printf("%c", trustletter ); } - printf(":%u:%d:%08lX%08lX:%s:%s:", + printf(":%u:%d:%08lX%08lX:%s:%s:::::", nbits_from_pk( pk2 ), pk2->pubkey_algo, (ulong)keyid2[0],(ulong)keyid2[1], @@ -1059,18 +1206,12 @@ list_keyblock_colon( KBNODE keyblock, int secret, int fpr ) colon_strtime (pk2->expiredate) /* fixme: add LID and ownertrust here */ ); - if( pk->local_id ) /* use the local_id of the main key??? */ - printf("%lu", pk->local_id ); - putchar(':'); - putchar(':'); - putchar(':'); - putchar(':'); print_capabilities (pk2, NULL, NULL); putchar('\n'); if( fpr > 1 ) print_fingerprint( pk2, NULL, 0 ); if( opt.with_key_data ) - print_key_data( pk2, keyid2 ); + print_key_data( pk2 ); } else if( node->pkt->pkttype == PKT_SECRET_SUBKEY ) { u32 keyid2[2]; @@ -1095,13 +1236,31 @@ list_keyblock_colon( KBNODE keyblock, int secret, int fpr ) colon_strtime (sk2->expiredate) /* fixme: add LID */ ); print_capabilities (NULL, sk2, NULL); + if (opt.fixed_list_mode) { + /* We print the serial number only in fixed list mode + for the primary key so, so avoid questions we print + it for subkeys also only in this mode. There is no + technical reason, though. */ + putchar(':'); /* End of field 13. */ + putchar(':'); /* End of field 14. */ + if (sk2->protect.s2k.mode == 1001) + putchar('#'); /* Key is just a stub. */ + else if (sk2->protect.s2k.mode == 1002) { + /* Key is stored on an external token (card) or handled by + the gpg-agent. Print the serial number of that token + here. */ + for (i=0; i < sk2->protect.ivlen; i++) + printf ("%02X", sk2->protect.iv[i]); + } + putchar(':'); /* End of field 15. */ + } putchar ('\n'); if( fpr > 1 ) - print_fingerprint( NULL, sk2, 0 ); + print_fingerprint( NULL, sk2, 0 ); } else if( opt.list_sigs && node->pkt->pkttype == PKT_SIGNATURE ) { PKT_signature *sig = node->pkt->pkt.signature; - int sigrc, fprokay=0; + int sigrc,fprokay=0; char *sigstr; size_t fplen; byte fparray[MAX_FINGERPRINT_LEN]; @@ -1142,21 +1301,21 @@ list_keyblock_colon( KBNODE keyblock, int secret, int fpr ) fflush(stdout); if(opt.no_sig_cache) - signer_pk = xcalloc (1, sizeof(PKT_public_key)); + signer_pk=xmalloc_clear(sizeof(PKT_public_key)); rc = check_key_signature2( keyblock, node, NULL, signer_pk, NULL, NULL, NULL ); - switch( gpg_err_code (rc) ) { + switch ( gpg_err_code (rc) ) { case 0: sigrc = '!'; break; - case GPG_ERR_BAD_SIGNATURE: sigrc = '-'; break; + case GPG_ERR_BAD_SIGN: sigrc = '-'; break; case GPG_ERR_NO_PUBKEY: - case GPG_ERR_UNUSABLE_PUBKEY: sigrc = '?'; break; + case GPG_ERR_UNU_PUBKEY: sigrc = '?'; break; default: sigrc = '%'; break; } if(opt.no_sig_cache) { - if(!rc) + if(rc==0) { fingerprint_from_pk (signer_pk, fparray, &fplen); fprokay=1; @@ -1187,20 +1346,19 @@ list_keyblock_colon( KBNODE keyblock, int secret, int fpr ) printf(":"); if( sigrc == '%' ) - printf("[%s] ", gpg_strerror (rc) ); + printf("[%s] ", g10_errstr(rc) ); else if( sigrc == '?' ) ; else if ( !opt.fast_list_mode ) { size_t n; char *p = get_user_id( sig->keyid, &n ); print_string( stdout, p, n, ':' ); - xfree (p); + xfree(p); } printf(":%02x%c:", sig->sig_class,sig->flags.exportable?'x':'l'); + if(opt.no_sig_cache && opt.check_sigs && fprokay) { - size_t i; - printf(":"); for (i=0; i < fplen ; i++ ) @@ -1210,6 +1368,10 @@ list_keyblock_colon( KBNODE keyblock, int secret, int fpr ) } printf("\n"); + + if(opt.show_subpackets) + print_subpackets_colon(sig); + /* fixme: check or list other sigs here */ } } @@ -1225,15 +1387,16 @@ list_keyblock_colon( KBNODE keyblock, int secret, int fpr ) * Reorder the keyblock so that the primary user ID (and not attribute * packet) comes first. Fixme: Replace this by a generic sort * function. */ -void -reorder_keyblock (KBNODE keyblock) +static void +do_reorder_keyblock (KBNODE keyblock,int attr) { KBNODE primary = NULL, primary0 = NULL, primary2 = NULL; KBNODE last, node; for (node=keyblock; node; primary0=node, node = node->next) { if( node->pkt->pkttype == PKT_USER_ID && - !node->pkt->pkt.user_id->attrib_data && + ((attr && node->pkt->pkt.user_id->attrib_data) || + (!attr && !node->pkt->pkt.user_id->attrib_data)) && node->pkt->pkt.user_id->is_primary ) { primary = primary2 = node; for (node=node->next; node; primary2=node, node = node->next ) { @@ -1265,6 +1428,13 @@ reorder_keyblock (KBNODE keyblock) } void +reorder_keyblock (KBNODE keyblock) +{ + do_reorder_keyblock(keyblock,1); + do_reorder_keyblock(keyblock,0); +} + +void list_keyblock( KBNODE keyblock, int secret, int fpr, void *opaque ) { reorder_keyblock (keyblock); @@ -1315,14 +1485,14 @@ print_fingerprint (PKT_public_key *pk, PKT_secret_key *sk, int mode ) { if(sk) { - PKT_secret_key *primary_sk=xcalloc (1,sizeof(*primary_sk)); + PKT_secret_key *primary_sk=xmalloc_clear(sizeof(*primary_sk)); get_seckey(primary_sk,sk->main_keyid); print_fingerprint(NULL,primary_sk,mode|0x80); free_secret_key(primary_sk); } else { - PKT_public_key *primary_pk=xcalloc (1,sizeof(*primary_pk)); + PKT_public_key *primary_pk=xmalloc_clear(sizeof(*primary_pk)); get_pubkey(primary_pk,pk->main_keyid); print_fingerprint(primary_pk,NULL,mode|0x80); free_public_key(primary_pk); @@ -1338,9 +1508,9 @@ print_fingerprint (PKT_public_key *pk, PKT_secret_key *sk, int mode ) } else if (mode == 2) { fp = NULL; /* use tty */ - /* Translators: this should fit into 24 bytes to that the fingerprint - * data is properly aligned with the user ID */ if(primary) + /* TRANSLATORS: this should fit into 24 bytes to that the + * fingerprint data is properly aligned with the user ID */ text = _(" Primary key fingerprint:"); else text = _(" Subkey fingerprint:"); @@ -1405,7 +1575,6 @@ print_fingerprint (PKT_public_key *pk, PKT_secret_key *sk, int mode ) tty_printf ("\n"); } - /* Print the serial number of an OpenPGP card if available. */ static void print_card_serialno (PKT_secret_key *sk) @@ -1417,7 +1586,7 @@ print_card_serialno (PKT_secret_key *sk) if (!sk->is_protected || sk->protect.s2k.mode != 1002) return; /* Not a card. */ if (opt.with_colons) - return; /* Format not yet defined. */ + return; /* Handled elsewhere. */ fputs (_(" Card serial no. ="), stdout); putchar (' '); @@ -1439,6 +1608,8 @@ print_card_serialno (PKT_secret_key *sk) putchar ('\n'); } + + void set_attrib_fd(int fd) { static int last_fd=-1; @@ -1457,10 +1628,11 @@ void set_attrib_fd(int fd) else if( fd == 2 ) attrib_fp = stderr; else - attrib_fp = fdopen( fd, "w" ); + attrib_fp = fdopen( fd, "wb" ); if( !attrib_fp ) { log_fatal("can't open fd %d for attribute output: %s\n", fd, strerror(errno)); } + last_fd = fd; } diff --git a/g10/keyring.c b/g10/keyring.c index 03a22667c..bd577a63b 100644 --- a/g10/keyring.c +++ b/g10/keyring.c @@ -1,5 +1,5 @@ /* keyring.c - keyring file handling - * Copyright (C) 2001, 2003 Free Software Foundation, Inc. + * Copyright (C) 2001, 2004 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -15,7 +15,8 @@ * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. */ #include <config.h> @@ -73,7 +74,7 @@ struct keyring_handle { int secret; /* this is for a secret keyring */ struct { CONST_KR_NAME kr; - iobuf_t iobuf; + IOBUF iobuf; int eof; int error; } current; @@ -102,7 +103,7 @@ new_offset_item (void) { struct off_item *k; - k = xcalloc (1,sizeof *k); + k = xmalloc_clear (sizeof *k); return k; } @@ -125,7 +126,7 @@ new_offset_hash_table (void) { struct off_item **tbl; - tbl = xcalloc (2048, sizeof *tbl); + tbl = xmalloc_clear (2048 * sizeof *tbl); return tbl; } @@ -214,6 +215,9 @@ keyring_register_filename (const char *fname, int secret, void **ptr) } } + if (secret) + register_secured_file (fname); + kr = xmalloc (sizeof *kr + strlen (fname)); strcpy (kr->fname, fname); kr->secret = !!secret; @@ -255,7 +259,7 @@ keyring_new (void *token, int secret) assert (resource && !resource->secret == !secret); - hd = xcalloc (1,sizeof *hd); + hd = xmalloc_clear (sizeof *hd); hd->resource = resource; hd->secret = !!secret; active_handles++; @@ -304,7 +308,7 @@ keyring_lock (KEYRING_HANDLE hd, int yes) kr->lockhd = create_dotlock( kr->fname ); if (!kr->lockhd) { log_info ("can't allocate lock for `%s'\n", kr->fname ); - rc = GPG_ERR_GENERAL; + rc = G10ERR_GENERAL; } } } @@ -319,7 +323,7 @@ keyring_lock (KEYRING_HANDLE hd, int yes) ; else if (make_dotlock (kr->lockhd, -1) ) { log_info ("can't lock `%s'\n", kr->fname ); - rc = GPG_ERR_GENERAL; + rc = G10ERR_GENERAL; } else kr->is_locked = 1; @@ -356,7 +360,7 @@ keyring_get_keyblock (KEYRING_HANDLE hd, KBNODE *ret_kb) PACKET *pkt; int rc; KBNODE keyblock = NULL, node, lastnode; - iobuf_t a; + IOBUF a; int in_cert = 0; int pk_no = 0; int uid_no = 0; @@ -369,15 +373,16 @@ keyring_get_keyblock (KEYRING_HANDLE hd, KBNODE *ret_kb) return -1; /* no successful search */ a = iobuf_open (hd->found.kr->fname); - if (!a) { - log_error ("can't open `%s'\n", hd->found.kr->fname); - return GPG_ERR_KEYRING_OPEN; - } + if (!a) + { + log_error(_("can't open `%s'\n"), hd->found.kr->fname); + return G10ERR_KEYRING_OPEN; + } if (iobuf_seek (a, hd->found.offset) ) { log_error ("can't seek `%s'\n", hd->found.kr->fname); iobuf_close(a); - return GPG_ERR_KEYRING_OPEN; + return G10ERR_KEYRING_OPEN; } pkt = xmalloc (sizeof *pkt); @@ -387,15 +392,15 @@ keyring_get_keyblock (KEYRING_HANDLE hd, KBNODE *ret_kb) save_mode = set_packet_list_mode(0); while ((rc=parse_packet (a, pkt)) != -1) { hd->found.n_packets++; - if (gpg_err_code (rc) == GPG_ERR_UNKNOWN_PACKET) { + if (rc == G10ERR_UNKNOWN_PACKET) { free_packet (pkt); init_packet (pkt); continue; } if (rc) { log_error ("keyring_get_keyblock: read error: %s\n", - gpg_strerror (rc) ); - rc = GPG_ERR_INV_KEYRING; + g10_errstr(rc) ); + rc = G10ERR_INV_KEYRING; break; } if (pkt->pkttype == PKT_COMPRESSED) { @@ -478,7 +483,7 @@ keyring_get_keyblock (KEYRING_HANDLE hd, KBNODE *ret_kb) /* Make sure that future search operations fail immediately when * we know that we are working on a invalid keyring */ - if (gpg_err_code (rc) == GPG_ERR_INV_KEYRING) + if (rc == G10ERR_INV_KEYRING) hd->current.error = rc; return rc; @@ -496,7 +501,7 @@ keyring_update_keyblock (KEYRING_HANDLE hd, KBNODE kb) /* need to know the number of packets - do a dummy get_keyblock*/ rc = keyring_get_keyblock (hd, NULL); if (rc) { - log_error ("re-reading keyblock failed: %s\n", gpg_strerror (rc)); + log_error ("re-reading keyblock failed: %s\n", g10_errstr (rc)); return rc; } if (!hd->found.n_packets) @@ -540,7 +545,7 @@ keyring_insert_keyblock (KEYRING_HANDLE hd, KBNODE kb) fname = hd->resource? hd->resource->fname:NULL; if (!fname) - return GPG_ERR_GENERAL; + return G10ERR_GENERAL; /* close this one otherwise we will lose the position for * a next search. Fixme: it would be better to adjust the position @@ -572,7 +577,7 @@ keyring_delete_keyblock (KEYRING_HANDLE hd) /* need to know the number of packets - do a dummy get_keyblock*/ rc = keyring_get_keyblock (hd, NULL); if (rc) { - log_error ("re-reading keyblock failed: %s\n", gpg_strerror (rc)); + log_error ("re-reading keyblock failed: %s\n", g10_errstr (rc)); return rc; } if (!hd->found.n_packets) @@ -629,7 +634,7 @@ prepare_search (KEYRING_HANDLE hd) if (hd->current.kr && !hd->current.eof) { if ( !hd->current.iobuf ) - return GPG_ERR_GENERAL; /* position invalid after a modify */ + return G10ERR_GENERAL; /* position invalid after a modify */ return 0; /* okay */ } @@ -654,11 +659,12 @@ prepare_search (KEYRING_HANDLE hd) hd->current.eof = 0; hd->current.iobuf = iobuf_open (hd->current.kr->fname); - if (!hd->current.iobuf) { + if (!hd->current.iobuf) + { hd->current.error = gpg_error_from_errno (errno); - log_error ("can't open `%s'\n", hd->current.kr->fname ); + log_error(_("can't open `%s'\n"), hd->current.kr->fname ); return hd->current.error; - } + } return 0; } @@ -776,7 +782,7 @@ prepare_word_match (const byte *name) int c; /* the original length is always enough for the pattern */ - p = pattern = xmalloc (strlen(name)+1); + p = pattern = xmalloc(strlen(name)+1); do { /* skip leading delimiters */ while( *name && !word_match_chars[*name] ) @@ -1071,7 +1077,7 @@ keyring_search (KEYRING_HANDLE hd, KEYDB_SEARCH_DESC *desc, goto found; break; default: - rc = GPG_ERR_INV_ARG; + rc = G10ERR_INV_ARG; goto found; } } @@ -1085,7 +1091,7 @@ keyring_search (KEYRING_HANDLE hd, KEYDB_SEARCH_DESC *desc, for (n=any_skip?0:ndesc; n < ndesc; n++) { if (desc[n].skipfnc - && desc[n].skipfnc (desc[n].skipfncvalue, aki)) + && desc[n].skipfnc (desc[n].skipfncvalue, aki, uid)) break; } if (n == ndesc) @@ -1141,7 +1147,7 @@ keyring_search (KEYRING_HANDLE hd, KEYDB_SEARCH_DESC *desc, static int create_tmp_file (const char *template, - char **r_bakfname, char **r_tmpfname, iobuf_t *r_fp) + char **r_bakfname, char **r_tmpfname, IOBUF *r_fp) { char *bakfname, *tmpfname; mode_t oldmask; @@ -1184,15 +1190,22 @@ create_tmp_file (const char *template, /* Create the temp file with limited access */ oldmask=umask(077); - *r_fp = iobuf_create (tmpfname); + if (is_secured_filename (tmpfname)) + { + *r_fp = NULL; + errno = EPERM; + } + else + *r_fp = iobuf_create (tmpfname); umask(oldmask); - if (!*r_fp) { - int tmperr = gpg_error_from_errno (errno); - log_error ("can't create `%s': %s\n", tmpfname, strerror(errno) ); + if (!*r_fp) + { + int rc = gpg_error_from_errno (errno); + log_error(_("can't create `%s': %s\n"), tmpfname, strerror(errno) ); xfree (tmpfname); xfree (bakfname); - return tmperr; - } + return rc; + } *r_bakfname = bakfname; *r_tmpfname = tmpfname; @@ -1219,10 +1232,10 @@ rename_tmp_file (const char *bakfname, const char *tmpfname, #endif if (rename (fname, bakfname) ) { - int tmperr = gpg_error_from_errno (errno); + rc = gpg_error_from_errno (errno); log_error ("renaming `%s' to `%s' failed: %s\n", fname, bakfname, strerror(errno) ); - return tmperr; + return rc; } } @@ -1230,11 +1243,14 @@ rename_tmp_file (const char *bakfname, const char *tmpfname, #if defined(HAVE_DOSISH_SYSTEM) || defined(__riscos__) remove( fname ); #endif + if (secret) + unregister_secured_file (fname); if (rename (tmpfname, fname) ) { rc = gpg_error_from_errno (errno); - log_error ("renaming `%s' to `%s' failed: %s\n", + log_error (_("renaming `%s' to `%s' failed: %s\n"), tmpfname, fname, strerror(errno) ); + register_secured_file (fname); if (secret) { log_info(_("WARNING: 2 files with confidential" @@ -1269,7 +1285,7 @@ rename_tmp_file (const char *bakfname, const char *tmpfname, static int -write_keyblock (iobuf_t fp, KBNODE keyblock) +write_keyblock (IOBUF fp, KBNODE keyblock) { KBNODE kbctx = NULL, node; int rc; @@ -1282,7 +1298,7 @@ write_keyblock (iobuf_t fp, KBNODE keyblock) if ( (rc = build_packet (fp, node->pkt) )) { log_error ("build_packet(%d) failed: %s\n", - node->pkt->pkttype, gpg_strerror (rc) ); + node->pkt->pkttype, g10_errstr(rc) ); return rc; } if (node->pkt->pkttype == PKT_SIGNATURE) @@ -1299,11 +1315,12 @@ write_keyblock (iobuf_t fp, KBNODE keyblock) iobuf_put (fp, 0xb0); /* old style packet 12, 1 byte len*/ iobuf_put (fp, 2); /* 2 bytes */ iobuf_put (fp, 0); /* unused */ - if (iobuf_put (fp, cacheval)) { - int tmperr = gpg_error_from_errno (errno); - log_error ("writing sigcache packet failed\n"); - return tmperr; - } + if (iobuf_put (fp, cacheval)) + { + rc = gpg_error_from_errno (errno); + log_error ("writing sigcache packet failed\n"); + return rc; + } } } return 0; @@ -1315,13 +1332,13 @@ write_keyblock (iobuf_t fp, KBNODE keyblock) * This is only done for the public keyrings. */ int -keyring_rebuild_cache (void *token) +keyring_rebuild_cache (void *token,int noisy) { KEYRING_HANDLE hd; KEYDB_SEARCH_DESC desc; KBNODE keyblock = NULL, node; const char *lastresname = NULL, *resname; - iobuf_t tmpfp = NULL; + IOBUF tmpfp = NULL; char *tmpfilename = NULL; char *bakfilename = NULL; int rc; @@ -1361,8 +1378,8 @@ keyring_rebuild_cache (void *token) if (rc) goto leave; lastresname = resname; - if (!opt.quiet) - log_info (_("checking keyring `%s'\n"), resname); + if (noisy && !opt.quiet) + log_info (_("caching keyring `%s'\n"), resname); rc = create_tmp_file (resname, &bakfilename, &tmpfilename, &tmpfp); if (rc) goto leave; @@ -1372,7 +1389,7 @@ keyring_rebuild_cache (void *token) rc = keyring_get_keyblock (hd, &keyblock); if (rc) { - log_error ("keyring_get_keyblock failed: %s\n", gpg_strerror (rc)); + log_error ("keyring_get_keyblock failed: %s\n", g10_errstr(rc)); goto leave; } assert (keyblock->pkt->pkttype == PKT_PUBLIC_KEY); @@ -1380,16 +1397,24 @@ keyring_rebuild_cache (void *token) /* check all signature to set the signature's cache flags */ for (node=keyblock; node; node=node->next) { + /* Note that this doesn't cache the result of a revocation + issued by a designated revoker. This is because the pk + in question does not carry the revkeys as we haven't + merged the key and selfsigs. It is questionable whether + this matters very much since there are very very few + designated revoker revocation packets out there. */ + if (node->pkt->pkttype == PKT_SIGNATURE) { - /* Note that this doesn't cache the result of a - revocation issued by a designated revoker. This is - because the pk in question does not carry the revkeys - as we haven't merged the key and selfsigs. It is - questionable whether this matters very much since - there are very very few designated revoker revocation - packets out there. */ - check_key_signature (keyblock, node, NULL); + PKT_signature *sig=node->pkt->pkt.signature; + + if(!opt.no_sig_cache && sig->flags.checked && sig->flags.valid + && (openpgp_md_test_algo(sig->digest_algo) + || openpgp_pk_test_algo(sig->pubkey_algo))) + sig->flags.checked=sig->flags.valid=0; + else + check_key_signature (keyblock, node, NULL); + sigcount++; } } @@ -1399,8 +1424,8 @@ keyring_rebuild_cache (void *token) if (rc) goto leave; - if ( !(++count % 50) && !opt.quiet) - log_info(_("%lu keys checked so far (%lu signatures)\n"), + if ( !(++count % 50) && noisy && !opt.quiet) + log_info(_("%lu keys cached so far (%lu signatures)\n"), count, sigcount ); } /* end main loop */ @@ -1408,10 +1433,11 @@ keyring_rebuild_cache (void *token) rc = 0; if (rc) { - log_error ("keyring_search failed: %s\n", gpg_strerror (rc)); + log_error ("keyring_search failed: %s\n", g10_errstr(rc)); goto leave; } - log_info(_("%lu keys checked (%lu signatures)\n"), count, sigcount ); + if(noisy || opt.verbose) + log_info(_("%lu keys cached (%lu signatures)\n"), count, sigcount ); if (tmpfp) { if (iobuf_close (tmpfp)) @@ -1452,17 +1478,16 @@ static int do_copy (int mode, const char *fname, KBNODE root, int secret, off_t start_offset, unsigned int n_packets ) { - iobuf_t fp, newfp; + IOBUF fp, newfp; int rc=0; char *bakfname = NULL; char *tmpfname = NULL; - /* Open the source file. Because we do a rname, we have to check the + /* Open the source file. Because we do a rename, we have to check the permissions of the file */ if (access (fname, W_OK)) return gpg_error_from_errno (errno); - fp = iobuf_open (fname); if (mode == 1 && !fp && errno == ENOENT) { /* insert mode but file does not exist: create a new file */ @@ -1470,14 +1495,19 @@ do_copy (int mode, const char *fname, KBNODE root, int secret, mode_t oldmask; oldmask=umask(077); - newfp = iobuf_create (fname); + if (!secret && is_secured_filename (fname)) { + newfp = NULL; + errno = EPERM; + } + else + newfp = iobuf_create (fname); umask(oldmask); - if( !newfp ) { - int tmperr = gpg_error_from_errno (errno); - log_error (_("%s: can't create: %s\n"), - fname, strerror(errno)); - return tmperr; - } + if( !newfp ) + { + rc = gpg_error_from_errno (errno); + log_error (_("can't create `%s': %s\n"), fname, strerror(errno)); + return rc; + } if( !opt.quiet ) log_info(_("%s: keyring created\n"), fname ); @@ -1485,38 +1515,44 @@ do_copy (int mode, const char *fname, KBNODE root, int secret, while ( (node = walk_kbnode( root, &kbctx, 0 )) ) { if( (rc = build_packet( newfp, node->pkt )) ) { log_error("build_packet(%d) failed: %s\n", - node->pkt->pkttype, gpg_strerror (rc) ); + node->pkt->pkttype, g10_errstr(rc) ); iobuf_cancel(newfp); return rc; } } - if (iobuf_close(newfp)) { - int tmperr = gpg_error_from_errno (errno); + if( iobuf_close(newfp) ) { + rc = gpg_error_from_errno (errno); log_error ("%s: close failed: %s\n", fname, strerror(errno)); - return tmperr; + return rc; } return 0; /* ready */ } - if( !fp ) { + if( !fp ) + { rc = gpg_error_from_errno (errno); - log_error ("%s: can't open: %s\n", fname, strerror(errno) ); + log_error(_("can't open `%s': %s\n"), fname, strerror(errno) ); goto leave; - } + } - /* create the new file */ + /* Create the new file. */ rc = create_tmp_file (fname, &bakfname, &tmpfname, &newfp); if (rc) { iobuf_close(fp); goto leave; } + if (secret) + register_secured_file (tmpfname); + if( mode == 1 ) { /* insert */ /* copy everything to the new file */ rc = copy_all_packets (fp, newfp); if( rc != -1 ) { log_error("%s: copy to `%s' failed: %s\n", - fname, tmpfname, gpg_strerror (rc) ); + fname, tmpfname, g10_errstr(rc) ); iobuf_close(fp); + if (secret) + unregister_secured_file (tmpfname); iobuf_cancel(newfp); goto leave; } @@ -1528,8 +1564,10 @@ do_copy (int mode, const char *fname, KBNODE root, int secret, rc = copy_some_packets( fp, newfp, start_offset ); if( rc ) { /* should never get EOF here */ log_error ("%s: copy to `%s' failed: %s\n", - fname, tmpfname, gpg_strerror (rc) ); + fname, tmpfname, g10_errstr(rc) ); iobuf_close(fp); + if (secret) + unregister_secured_file (tmpfname); iobuf_cancel(newfp); goto leave; } @@ -1538,8 +1576,10 @@ do_copy (int mode, const char *fname, KBNODE root, int secret, rc = skip_some_packets( fp, n_packets ); if( rc ) { log_error("%s: skipping %u packets failed: %s\n", - fname, n_packets, gpg_strerror (rc)); + fname, n_packets, g10_errstr(rc)); iobuf_close(fp); + if (secret) + unregister_secured_file (tmpfname); iobuf_cancel(newfp); goto leave; } @@ -1549,6 +1589,8 @@ do_copy (int mode, const char *fname, KBNODE root, int secret, rc = write_keyblock (newfp, root); if (rc) { iobuf_close(fp); + if (secret) + unregister_secured_file (tmpfname); iobuf_cancel(newfp); goto leave; } @@ -1559,8 +1601,10 @@ do_copy (int mode, const char *fname, KBNODE root, int secret, rc = copy_all_packets( fp, newfp ); if( rc != -1 ) { log_error("%s: copy to `%s' failed: %s\n", - fname, tmpfname, gpg_strerror (rc) ); + fname, tmpfname, g10_errstr(rc) ); iobuf_close(fp); + if (secret) + unregister_secured_file (tmpfname); iobuf_cancel(newfp); goto leave; } @@ -1582,7 +1626,7 @@ do_copy (int mode, const char *fname, KBNODE root, int secret, rc = rename_tmp_file (bakfname, tmpfname, fname, secret); leave: - xfree (bakfname); - xfree (tmpfname); + xfree(bakfname); + xfree(tmpfname); return rc; } diff --git a/g10/keyring.h b/g10/keyring.h index 528557a70..773d7b492 100644 --- a/g10/keyring.h +++ b/g10/keyring.h @@ -15,14 +15,13 @@ * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. */ #ifndef GPG_KEYRING_H #define GPG_KEYRING_H 1 -#include "global.h" - typedef struct keyring_handle *KEYRING_HANDLE; @@ -41,6 +40,6 @@ int keyring_delete_keyblock (KEYRING_HANDLE hd); int keyring_search_reset (KEYRING_HANDLE hd); int keyring_search (KEYRING_HANDLE hd, KEYDB_SEARCH_DESC *desc, size_t ndesc, size_t *descindex); -int keyring_rebuild_cache (void *); +int keyring_rebuild_cache (void *token,int noisy); #endif /*GPG_KEYRING_H*/ diff --git a/g10/keyserver-internal.h b/g10/keyserver-internal.h index 314d7898e..a5e6e8c37 100644 --- a/g10/keyserver-internal.h +++ b/g10/keyserver-internal.h @@ -1,4 +1,23 @@ -/* Keyserver internals */ +/* keyserver-internal.h - Keyserver internals + * Copyright (C) 2001, 2002, 2004, 2005, 2006 Free Software Foundation, Inc. + * + * This file is part of GnuPG. + * + * GnuPG is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * GnuPG is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. + */ #ifndef _KEYSERVER_INTERNAL_H_ #define _KEYSERVER_INTERNAL_H_ @@ -8,14 +27,28 @@ #include "../common/iobuf.h" #include "types.h" -void parse_keyserver_options(char *options); -int parse_keyserver_uri(char *uri, - const char *configname,unsigned int configlineno); +int parse_keyserver_options(char *options); +void free_keyserver_spec(struct keyserver_spec *keyserver); +struct keyserver_spec *keyserver_match(struct keyserver_spec *spec); +struct keyserver_spec *parse_keyserver_uri(const char *string, + int require_scheme, + const char *configname, + unsigned int configlineno); +struct keyserver_spec *parse_preferred_keyserver(PKT_signature *sig); int keyserver_export(STRLIST users); int keyserver_import(STRLIST users); -int keyserver_import_fprint(const byte *fprint,size_t fprint_len); -int keyserver_import_keyid(u32 *keyid); +int keyserver_import_fprint(const byte *fprint,size_t fprint_len, + struct keyserver_spec *keyserver); +int keyserver_import_keyid(u32 *keyid,struct keyserver_spec *keyserver); int keyserver_refresh(STRLIST users); int keyserver_search(STRLIST tokens); +int keyserver_fetch(STRLIST urilist); +int keyserver_import_cert(const char *name, + unsigned char **fpr,size_t *fpr_len); +int keyserver_import_pka(const char *name,unsigned char **fpr,size_t *fpr_len); +int keyserver_import_name(const char *name,unsigned char **fpr,size_t *fpr_len, + struct keyserver_spec *keyserver); +int keyserver_import_ldap(const char *name, + unsigned char **fpr,size_t *fpr_len); #endif /* !_KEYSERVER_INTERNAL_H_ */ diff --git a/g10/keyserver.c b/g10/keyserver.c index 445c07620..af1e5f773 100644 --- a/g10/keyserver.c +++ b/g10/keyserver.c @@ -1,5 +1,6 @@ /* keyserver.c - generic keyserver code - * Copyright (C) 2001, 2002, 2003 Free Software Foundation, Inc. + * Copyright (C) 2001, 2002, 2003, 2004, 2005, + * 2006 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -15,7 +16,8 @@ * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. */ #include <config.h> @@ -24,6 +26,9 @@ #include <string.h> #include <stdlib.h> #include <assert.h> +#ifdef HAVE_LIBCURL +#include <curl/curl.h> +#endif #include "gpg.h" #include "filter.h" @@ -33,149 +38,302 @@ #include "main.h" #include "i18n.h" #include "iobuf.h" -#include "memory.h" #include "ttyio.h" #include "options.h" #include "packet.h" +#include "trustdb.h" #include "keyserver-internal.h" #include "util.h" -#define GET 0 -#define SEND 1 -#define SEARCH 2 +#define GPGKEYS_PREFIX "gpgkeys_" + +#if defined(HAVE_LIBCURL) || defined(FAKE_CURL) +#define GPGKEYS_CURL "gpgkeys_curl" +#endif + +#ifdef GPGKEYS_CURL +#define GPGKEYS_PREFIX_LEN (strlen(GPGKEYS_PREFIX)+strlen(GPGKEYS_CURL)) +#else +#define GPGKEYS_PREFIX_LEN (strlen(GPGKEYS_PREFIX)) +#endif struct keyrec { KEYDB_SEARCH_DESC desc; - time_t createtime,expiretime; + u32 createtime,expiretime; int size,flags; byte type; - iobuf_t uidbuf; - int lines; + IOBUF uidbuf; + unsigned int lines; }; -struct kopts -{ - char *name; - int tell; /* tell remote process about this one */ - int *flag; -} keyserver_opts[]= +enum ks_action {KS_UNKNOWN=0,KS_GET,KS_GETNAME,KS_SEND,KS_SEARCH}; + +static struct parse_options keyserver_opts[]= + { + /* some of these options are not real - just for the help + message */ + {"max-cert-size",0,NULL,NULL}, + {"include-revoked",0,NULL,N_("include revoked keys in search results")}, + {"include-subkeys",0,NULL,N_("include subkeys when searching by key ID")}, + {"use-temp-files",0,NULL, + N_("use temporary files to pass data to keyserver helpers")}, + {"keep-temp-files",KEYSERVER_KEEP_TEMP_FILES,NULL, + N_("do not delete temporary files after using them")}, + {"refresh-add-fake-v3-keyids",KEYSERVER_ADD_FAKE_V3,NULL, + NULL}, + {"auto-key-retrieve",KEYSERVER_AUTO_KEY_RETRIEVE,NULL, + N_("automatically retrieve keys when verifying signatures")}, + {"honor-keyserver-url",KEYSERVER_HONOR_KEYSERVER_URL,NULL, + N_("honor the preferred keyserver URL set on the key")}, + {"honor-pka-record",KEYSERVER_HONOR_PKA_RECORD,NULL, + N_("honor the PKA record set on a key when retrieving keys")}, + {NULL,0,NULL,NULL} + }; + +static int keyserver_work(enum ks_action action,STRLIST list, + KEYDB_SEARCH_DESC *desc,int count, + unsigned char **fpr,size_t *fpr_len, + struct keyserver_spec *keyserver); + +/* Reasonable guess */ +#define DEFAULT_MAX_CERT_SIZE 16384 + +static size_t max_cert_size=DEFAULT_MAX_CERT_SIZE; + +static void +add_canonical_option(char *option,STRLIST *list) { - {"include-revoked",1,&opt.keyserver_options.include_revoked}, - {"include-disabled",1,&opt.keyserver_options.include_disabled}, - {"include-subkeys",1,&opt.keyserver_options.include_subkeys}, - {"keep-temp-files",0,&opt.keyserver_options.keep_temp_files}, - {"honor-http-proxy",1,&opt.keyserver_options.honor_http_proxy}, - {"broken-http-proxy",1,&opt.keyserver_options.broken_http_proxy}, - {"refresh-add-fake-v3-keyids",0,&opt.keyserver_options.fake_v3_keyids}, - {"auto-key-retrieve",0,&opt.keyserver_options.auto_key_retrieve}, - {"try-dns-srv",1,&opt.keyserver_options.try_dns_srv}, - {NULL} -}; + char *arg=argsplit(option); -static int keyserver_work(int action,STRLIST list, - KEYDB_SEARCH_DESC *desc,int count); + if(arg) + { + char *joined; + + joined=xmalloc(strlen(option)+1+strlen(arg)+1); + /* Make a canonical name=value form with no spaces */ + strcpy(joined,option); + strcat(joined,"="); + strcat(joined,arg); + append_to_strlist(list,joined); + xfree(joined); + } + else + append_to_strlist(list,option); +} -void +int parse_keyserver_options(char *options) { + int ret=1; char *tok; + char *max_cert=NULL; - while((tok=strsep(&options," ,"))) - { - int i,hit=0; + keyserver_opts[0].value=&max_cert; + while((tok=optsep(&options))) + { if(tok[0]=='\0') continue; - for(i=0;keyserver_opts[i].name;i++) - { - if(ascii_strcasecmp(tok,keyserver_opts[i].name)==0) - { - *(keyserver_opts[i].flag)=1; - hit=1; - break; - } - else if(ascii_strncasecmp("no-",tok,3)==0 && - ascii_strcasecmp(&tok[3],keyserver_opts[i].name)==0) - { - *(keyserver_opts[i].flag)=0; - hit=1; - break; - } - } + /* For backwards compatibility. 1.2.x used honor-http-proxy and + there are a good number of documents published that recommend + it. */ + if(ascii_strcasecmp(tok,"honor-http-proxy")==0) + tok="http-proxy"; + else if(ascii_strcasecmp(tok,"no-honor-http-proxy")==0) + tok="no-http-proxy"; + + /* We accept quite a few possible options here - some options to + handle specially, the keyserver_options list, and import and + export options that pertain to keyserver operations. Note + that you must use strncasecmp here as there might be an + =argument attached which will foil the use of strcasecmp. */ - /* These options need more than just a flag */ - if(!hit) - { - if(ascii_strcasecmp(tok,"verbose")==0) - opt.keyserver_options.verbose++; - else if(ascii_strcasecmp(tok,"no-verbose")==0) - opt.keyserver_options.verbose--; #ifdef EXEC_TEMPFILE_ONLY - else if(ascii_strcasecmp(tok,"use-temp-files")==0 || - ascii_strcasecmp(tok,"no-use-temp-files")==0) - log_info(_("WARNING: keyserver option \"%s\" is not used " - "on this platform\n"),tok); + if(ascii_strncasecmp(tok,"use-temp-files",14)==0 || + ascii_strncasecmp(tok,"no-use-temp-files",17)==0) + log_info(_("WARNING: keyserver option `%s' is not used" + " on this platform\n"),tok); #else - else if(ascii_strcasecmp(tok,"use-temp-files")==0) - opt.keyserver_options.use_temp_files=1; - else if(ascii_strcasecmp(tok,"no-use-temp-files")==0) - opt.keyserver_options.use_temp_files=0; + if(ascii_strncasecmp(tok,"use-temp-files",14)==0) + opt.keyserver_options.options|=KEYSERVER_USE_TEMP_FILES; + else if(ascii_strncasecmp(tok,"no-use-temp-files",17)==0) + opt.keyserver_options.options&=~KEYSERVER_USE_TEMP_FILES; #endif - else - if(!parse_import_options(tok, - &opt.keyserver_options.import_options) && - !parse_export_options(tok, - &opt.keyserver_options.export_options)) - add_to_strlist(&opt.keyserver_options.other,tok); + else if(!parse_options(tok,&opt.keyserver_options.options, + keyserver_opts,0) + && !parse_import_options(tok, + &opt.keyserver_options.import_options,0) + && !parse_export_options(tok, + &opt.keyserver_options.export_options,0)) + { + /* All of the standard options have failed, so the option is + destined for a keyserver plugin. */ + add_canonical_option(tok,&opt.keyserver_options.other); } } + + if(max_cert) + { + max_cert_size=strtoul(max_cert,(char **)NULL,10); + + if(max_cert_size==0) + max_cert_size=DEFAULT_MAX_CERT_SIZE; + } + + return ret; } -int -parse_keyserver_uri(char *uri,const char *configname,unsigned int configlineno) +void +free_keyserver_spec(struct keyserver_spec *keyserver) +{ + xfree(keyserver->uri); + xfree(keyserver->scheme); + xfree(keyserver->auth); + xfree(keyserver->host); + xfree(keyserver->port); + xfree(keyserver->path); + xfree(keyserver->opaque); + free_strlist(keyserver->options); + xfree(keyserver); +} + +/* Return 0 for match */ +static int +cmp_keyserver_spec(struct keyserver_spec *one,struct keyserver_spec *two) +{ + if(ascii_strcasecmp(one->scheme,two->scheme)==0) + { + if(one->host && two->host && ascii_strcasecmp(one->host,two->host)==0) + { + if((one->port && two->port + && ascii_strcasecmp(one->port,two->port)==0) + || (!one->port && !two->port)) + return 0; + } + else if(one->opaque && two->opaque + && ascii_strcasecmp(one->opaque,two->opaque)==0) + return 0; + } + + return 1; +} + +/* Try and match one of our keyservers. If we can, return that. If + we can't, return our input. */ +struct keyserver_spec * +keyserver_match(struct keyserver_spec *spec) +{ + struct keyserver_spec *ks; + + for(ks=opt.keyserver;ks;ks=ks->next) + if(cmp_keyserver_spec(spec,ks)==0) + return ks; + + return spec; +} + +/* TODO: once we cut over to an all-curl world, we don't need this + parser any longer so it can be removed, or at least moved to + keyserver/ksutil.c for limited use in gpgkeys_ldap or the like. */ + +struct keyserver_spec * +parse_keyserver_uri(const char *string,int require_scheme, + const char *configname,unsigned int configlineno) { int assume_hkp=0; + struct keyserver_spec *keyserver; + const char *idx; + int count; + char *uri,*options; + + assert(string!=NULL); + + keyserver=xmalloc_clear(sizeof(struct keyserver_spec)); + + uri=xstrdup(string); + + options=strchr(uri,' '); + if(options) + { + char *tok; - assert(uri!=NULL); + *options='\0'; + options++; - opt.keyserver_host=NULL; - opt.keyserver_port=NULL; - opt.keyserver_opaque=NULL; + while((tok=optsep(&options))) + add_canonical_option(tok,&keyserver->options); + } /* Get the scheme */ - opt.keyserver_scheme=strsep(&uri,":"); - if(uri==NULL) + for(idx=uri,count=0;*idx && *idx!=':';idx++) + { + count++; + + /* Do we see the start of an RFC-2732 ipv6 address here? If so, + there clearly isn't a scheme so get out early. */ + if(*idx=='[') + { + /* Was the '[' the first thing in the string? If not, we + have a mangled scheme with a [ in it so fail. */ + if(count==1) + break; + else + goto fail; + } + } + + if(count==0) + goto fail; + + if(*idx=='\0' || *idx=='[') { + if(require_scheme) + return NULL; + /* Assume HKP if there is no scheme */ assume_hkp=1; - uri=opt.keyserver_scheme; - opt.keyserver_scheme="hkp"; + keyserver->scheme=xstrdup("hkp"); + + keyserver->uri=xmalloc(strlen(keyserver->scheme)+3+strlen(uri)+1); + strcpy(keyserver->uri,keyserver->scheme); + strcat(keyserver->uri,"://"); + strcat(keyserver->uri,uri); } else { + int i; + + keyserver->uri=xstrdup(uri); + + keyserver->scheme=xmalloc(count+1); + /* Force to lowercase */ - char *i; + for(i=0;i<count;i++) + keyserver->scheme[i]=ascii_tolower(uri[i]); + + keyserver->scheme[i]='\0'; - for(i=opt.keyserver_scheme;*i!='\0';i++) - *i=ascii_tolower(*i); + /* Skip past the scheme and colon */ + uri+=count+1; } - if(ascii_strcasecmp(opt.keyserver_scheme,"x-broken-hkp")==0) + if(ascii_strcasecmp(keyserver->scheme,"x-broken-hkp")==0) { deprecated_warning(configname,configlineno,"x-broken-hkp", "--keyserver-options ","broken-http-proxy"); - opt.keyserver_scheme="hkp"; - opt.keyserver_options.broken_http_proxy=1; + xfree(keyserver->scheme); + keyserver->scheme=xstrdup("hkp"); + append_to_strlist(&opt.keyserver_options.other,"broken-http-proxy"); } - else if(ascii_strcasecmp(opt.keyserver_scheme,"x-hkp")==0 - || ascii_strcasecmp(opt.keyserver_scheme,"http")==0) + else if(ascii_strcasecmp(keyserver->scheme,"x-hkp")==0) { /* Canonicalize this to "hkp" so it works with both the internal and external keyserver interface. */ - opt.keyserver_scheme="hkp"; + xfree(keyserver->scheme); + keyserver->scheme=xstrdup("hkp"); } if(assume_hkp || (uri[0]=='/' && uri[1]=='/')) @@ -186,57 +344,124 @@ parse_keyserver_uri(char *uri,const char *configname,unsigned int configlineno) if(!assume_hkp) uri+=2; - /* Get the host */ - opt.keyserver_host=strsep(&uri,":/"); - if(opt.keyserver_host[0]=='\0') - return GPG_ERR_BAD_URI; + /* Do we have userinfo auth data present? */ + for(idx=uri,count=0;*idx && *idx!='@' && *idx!='/';idx++) + count++; - if(uri==NULL || uri[0]=='\0') - opt.keyserver_port=NULL; - else + /* We found a @ before the slash, so that means everything + before the @ is auth data. */ + if(*idx=='@') { - char *ch; + if(count==0) + goto fail; - /* Get the port */ - opt.keyserver_port=strsep(&uri,"/"); + keyserver->auth=xmalloc(count+1); + strncpy(keyserver->auth,uri,count); + keyserver->auth[count]='\0'; + uri+=count+1; + } - /* Ports are digits only */ - ch=opt.keyserver_port; - while(*ch!='\0') - { - if(!digitp(ch)) - return GPG_ERR_BAD_URI; + /* Is it an RFC-2732 ipv6 [literal address] ? */ + if(*uri=='[') + { + for(idx=uri+1,count=1;*idx + && ((isascii (*idx) && isxdigit(*idx)) + || *idx==':' || *idx=='.');idx++) + count++; + + /* Is the ipv6 literal address terminated? */ + if(*idx==']') + count++; + else + goto fail; + } + else + for(idx=uri,count=0;*idx && *idx!=':' && *idx!='/';idx++) + count++; - ch++; - } + if(count==0) + goto fail; + + keyserver->host=xmalloc(count+1); + strncpy(keyserver->host,uri,count); + keyserver->host[count]='\0'; + /* Skip past the host */ + uri+=count; + + if(*uri==':') + { /* It would seem to be reasonable to limit the range of the ports to values between 1-65535, but RFC 1738 and 1808 imply there is no limit. Of course, the real world has limits. */ + + for(idx=uri+1,count=0;*idx && *idx!='/';idx++) + { + count++; + + /* Ports are digits only */ + if(!digitp(idx)) + goto fail; + } + + keyserver->port=xmalloc(count+1); + strncpy(keyserver->port,uri+1,count); + keyserver->port[count]='\0'; + + /* Skip past the colon and port number */ + uri+=1+count; } - /* (any path part of the URI is discarded for now as no keyserver - uses it yet) */ + /* Everything else is the path */ + if(*uri) + keyserver->path=xstrdup(uri); + else + keyserver->path=xstrdup("/"); + + if(keyserver->path[1]!='\0') + keyserver->flags.direct_uri=1; } else if(uri[0]!='/') { /* No slash means opaque. Just record the opaque blob and get out. */ - opt.keyserver_opaque=uri; - return 0; + keyserver->opaque=xstrdup(uri); } else { /* One slash means absolute path. We don't need to support that yet. */ - return GPG_ERR_BAD_URI; + goto fail; } - if(opt.keyserver_scheme[0]=='\0') - return GPG_ERR_BAD_URI; + return keyserver; - return 0; + fail: + free_keyserver_spec(keyserver); + + return NULL; +} + +struct keyserver_spec * +parse_preferred_keyserver(PKT_signature *sig) +{ + struct keyserver_spec *spec=NULL; + const byte *p; + size_t plen; + + p=parse_sig_subpkt(sig->hashed,SIGSUBPKT_PREF_KS,&plen); + if(p && plen) + { + byte *dupe=xmalloc(plen+1); + + memcpy(dupe,p,plen); + dupe[plen]='\0'; + spec=parse_keyserver_uri(dupe,1,NULL,0); + xfree(dupe); + } + + return spec; } static void @@ -253,7 +478,7 @@ print_keyrec(int number,struct keyrec *keyrec) if(keyrec->type) { - const char *str = gcry_pk_algo_name (keyrec->type); + const char *str=pubkey_algo_to_string(keyrec->type); if(str) printf("%s ",str); @@ -263,25 +488,32 @@ print_keyrec(int number,struct keyrec *keyrec) switch(keyrec->desc.mode) { + /* If the keyserver helper gave us a short keyid, we have no + choice but to use it. Do check --keyid-format to add a 0x if + needed. */ case KEYDB_SEARCH_MODE_SHORT_KID: - printf("key %08lX",(ulong)keyrec->desc.u.kid[1]); + printf("key %s%08lX", + (opt.keyid_format==KF_0xSHORT + || opt.keyid_format==KF_0xLONG)?"0x":"", + (ulong)keyrec->desc.u.kid[1]); break; + /* However, if it gave us a long keyid, we can honor + --keyid-format */ case KEYDB_SEARCH_MODE_LONG_KID: - printf("key %08lX%08lX",(ulong)keyrec->desc.u.kid[0], - (ulong)keyrec->desc.u.kid[1]); + printf("key %s",keystr(keyrec->desc.u.kid)); break; case KEYDB_SEARCH_MODE_FPR16: printf("key "); for(i=0;i<16;i++) - printf("%02X",(unsigned char)keyrec->desc.u.fpr[i]); + printf("%02X",keyrec->desc.u.fpr[i]); break; case KEYDB_SEARCH_MODE_FPR20: printf("key "); for(i=0;i<20;i++) - printf("%02X",(unsigned char)keyrec->desc.u.fpr[i]); + printf("%02X",keyrec->desc.u.fpr[i]); break; default: @@ -290,17 +522,23 @@ print_keyrec(int number,struct keyrec *keyrec) } if(keyrec->createtime>0) - printf(", created %s",strtimestamp(keyrec->createtime)); + { + printf(", "); + printf(_("created: %s"),strtimestamp(keyrec->createtime)); + } if(keyrec->expiretime>0) - printf(", expires %s",strtimestamp(keyrec->expiretime)); + { + printf(", "); + printf(_("expires: %s"),strtimestamp(keyrec->expiretime)); + } if(keyrec->flags&1) - printf(" (%s)",("revoked")); + printf(" (%s)",_("revoked")); if(keyrec->flags&2) - printf(" (%s)",("disabled")); + printf(" (%s)",_("disabled")); if(keyrec->flags&4) - printf(" (%s)",("expired")); + printf(" (%s)",_("expired")); printf("\n"); } @@ -322,7 +560,7 @@ parse_keyrec(char *keystring) return NULL; else if(work->desc.mode==KEYDB_SEARCH_MODE_NONE) { - xfree (work); + xfree(work); return NULL; } else @@ -335,7 +573,7 @@ parse_keyrec(char *keystring) if(work==NULL) { - work=xcalloc (1,sizeof(struct keyrec)); + work=xmalloc_clear(sizeof(struct keyrec)); work->uidbuf=iobuf_temp(); } @@ -356,7 +594,7 @@ parse_keyrec(char *keystring) if(work->desc.mode) { ret=work; - work=xcalloc (1,sizeof(struct keyrec)); + work=xmalloc_clear(sizeof(struct keyrec)); work->uidbuf=iobuf_temp(); } @@ -391,12 +629,23 @@ parse_keyrec(char *keystring) if((tok=strsep(&keystring,":"))==NULL) return ret; - work->createtime=atoi(tok); + if(atoi(tok)<=0) + work->createtime=0; + else + work->createtime=atoi(tok); if((tok=strsep(&keystring,":"))==NULL) return ret; - work->expiretime=atoi(tok); + if(atoi(tok)<=0) + work->expiretime=0; + else + { + work->expiretime=atoi(tok); + /* Force the 'e' flag on if this key is expired. */ + if(work->expiretime<=make_timestamp()) + work->flags|=4; + } if((tok=strsep(&keystring,":"))==NULL) return ret; @@ -419,9 +668,6 @@ parse_keyrec(char *keystring) work->flags|=4; break; } - - if(work->expiretime && work->expiretime<=make_timestamp()) - work->flags|=4; } else if(ascii_strcasecmp("uid",record)==0 && work->desc.mode) { @@ -460,12 +706,14 @@ parse_keyrec(char *keystring) does this for us. */ decoded=utf8_to_native(userid,i,0); + if(strlen(decoded)>opt.screen_columns-10) + decoded[opt.screen_columns-10]='\0'; iobuf_writestr(work->uidbuf,decoded); - xfree (decoded); + xfree(decoded); iobuf_writestr(work->uidbuf,"\n\t"); work->lines++; } - + /* Ignore any records other than "pri" and "uid" for easy future growth. */ @@ -499,7 +747,7 @@ show_prompt(KEYDB_SEARCH_DESC *desc,int numdesc,int count,const char *search) if(answer[0]=='q' || answer[0]=='Q') { - xfree (answer); + xfree(answer); return 1; } else if(atoi(answer)>=1 && atoi(answer)<=numdesc) @@ -508,9 +756,10 @@ show_prompt(KEYDB_SEARCH_DESC *desc,int numdesc,int count,const char *search) while((num=strsep(&split," ,"))!=NULL) if(atoi(num)>=1 && atoi(num)<=numdesc) - keyserver_work(GET,NULL,&desc[atoi(num)-1],1); + keyserver_work(KS_GET,NULL,&desc[atoi(num)-1],1, + NULL,NULL,opt.keyserver); - xfree (answer); + xfree(answer); return 1; } @@ -519,18 +768,20 @@ show_prompt(KEYDB_SEARCH_DESC *desc,int numdesc,int count,const char *search) /* Count and searchstr are just for cosmetics. If the count is too small, it will grow safely. If negative it disables the "Key x-y - of z" messages. */ + of z" messages. searchstr should be UTF-8 (rather than native). */ static void -keyserver_search_prompt(iobuf_t buffer,const char *searchstr) +keyserver_search_prompt(IOBUF buffer,const char *searchstr) { int i=0,validcount=0,started=0,header=0,count=1; - unsigned int maxlen,buflen; + unsigned int maxlen,buflen,numlines=0; KEYDB_SEARCH_DESC *desc; byte *line=NULL; - /* TODO: Something other than 23? That's 24-1 (the prompt). */ - int maxlines=23,numlines=0; + char *localstr=NULL; + + if(searchstr) + localstr=utf8_to_native(searchstr,strlen(searchstr),0); - desc=xmalloc (count*sizeof(KEYDB_SEARCH_DESC)); + desc=xmalloc(count*sizeof(KEYDB_SEARCH_DESC)); for(;;) { @@ -609,7 +860,7 @@ keyserver_search_prompt(iobuf_t buffer,const char *searchstr) for(;;) { - if(show_prompt(desc,i,validcount?count:0,searchstr)) + if(show_prompt(desc,i,validcount?count:0,localstr)) break; validcount=0; } @@ -635,9 +886,10 @@ keyserver_search_prompt(iobuf_t buffer,const char *searchstr) if(!opt.with_colons) { - if(numlines+keyrec->lines>maxlines) + /* screen_lines - 1 for the prompt. */ + if(numlines+keyrec->lines>opt.screen_lines-1) { - if(show_prompt(desc,i,validcount?count:0,searchstr)) + if(show_prompt(desc,i,validcount?count:0,localstr)) break; else numlines=0; @@ -648,62 +900,144 @@ keyserver_search_prompt(iobuf_t buffer,const char *searchstr) numlines+=keyrec->lines; iobuf_close(keyrec->uidbuf); - xfree (keyrec); + xfree(keyrec); started=1; i++; } } - xfree (desc); - xfree (line); - notfound: + /* Leave this commented out or now, and perhaps for a very long + time. All HKPish servers return HTML error messages for + no-key-found. */ + /* + if(!started) + log_info(_("keyserver does not support searching\n")); + else + */ if(count==0) { - if(searchstr) - log_info(_("key \"%s\" not found on keyserver\n"),searchstr); + if(localstr) + log_info(_("key \"%s\" not found on keyserver\n"),localstr); else log_info(_("key not found on keyserver\n")); - return; } + + xfree(localstr); + xfree(desc); + xfree(line); } +/* We sometimes want to use a different gpgkeys_xxx for a given + protocol (for example, ldaps is handled by gpgkeys_ldap). Map + these here. */ +static const char * +keyserver_typemap(const char *type) +{ + if(strcmp(type,"ldaps")==0) + return "ldap"; + else + return type; +} + +#ifdef GPGKEYS_CURL +/* The PGP LDAP and the curl fetch-a-LDAP-object methodologies are + sufficiently different that we can't use curl to do LDAP. */ +static int +curl_cant_handle(const char *scheme,unsigned int direct_uri) +{ + if(!direct_uri && (strcmp(scheme,"ldap")==0 || strcmp(scheme,"ldaps")==0)) + return 1; + + return 0; +} +#endif + #define KEYSERVER_ARGS_KEEP " -o \"%O\" \"%I\"" #define KEYSERVER_ARGS_NOKEEP " -o \"%o\" \"%i\"" static int -keyserver_spawn(int action,STRLIST list, - KEYDB_SEARCH_DESC *desc,int count,int *prog) +keyserver_spawn(enum ks_action action,STRLIST list,KEYDB_SEARCH_DESC *desc, + int count,int *prog,unsigned char **fpr,size_t *fpr_len, + struct keyserver_spec *keyserver) { int ret=0,i,gotversion=0,outofband=0; STRLIST temp; unsigned int maxlen,buflen; - char *command=NULL,*searchstr=NULL; + char *command,*end,*searchstr=NULL; byte *line=NULL; - struct kopts *kopts; struct exec_info *spawn; + const char *scheme; + const char *libexecdir = get_libexecdir (); + + assert(keyserver); #ifdef EXEC_TEMPFILE_ONLY - opt.keyserver_options.use_temp_files=1; + opt.keyserver_options.options|=KEYSERVER_USE_TEMP_FILES; #endif - /* Push the libexecdir into path. If DISABLE_KEYSERVER_PATH is set, - use the 0 arg to replace the path. */ + /* Build the filename for the helper to execute */ + scheme=keyserver_typemap(keyserver->scheme); + #ifdef DISABLE_KEYSERVER_PATH - set_exec_path(GNUPG_LIBEXECDIR,0); + /* Destroy any path we might have. This is a little tricky, + portability-wise. It's not correct to delete the PATH + environment variable, as that may fall back to a system built-in + PATH. Similarly, it is not correct to set PATH to the null + string (PATH="") since this actually deletes the PATH environment + variable under MinGW. The safest thing to do here is to force + PATH to be GNUPG_LIBEXECDIR. All this is not that meaningful on + Unix-like systems (since we're going to give a full path to + gpgkeys_foo), but on W32 it prevents loading any DLLs from + directories in %PATH%. + + After some more thinking about this we came to the conclusion + that it is better to load the helpers from the directory where + the program of this process lives. Fortunately Windows provides + a way to retrieve this and our get_libexecdir function has been + modified to return just this. Setting the exec-path is not + anymore required. + set_exec_path(libexecdir); + */ #else - set_exec_path(GNUPG_LIBEXECDIR,opt.exec_path_set); + if(opt.exec_path_set) + { + /* If exec-path was set, and DISABLE_KEYSERVER_PATH is + undefined, then don't specify a full path to gpgkeys_foo, so + that the PATH can work. */ + command=xmalloc(GPGKEYS_PREFIX_LEN+strlen(scheme)+3+strlen(EXEEXT)+1); + command[0]='\0'; + } + else #endif + { + /* Specify a full path to gpgkeys_foo. */ + command=xmalloc(strlen(libexecdir)+strlen(DIRSEP_S)+ + GPGKEYS_PREFIX_LEN+strlen(scheme)+3+strlen(EXEEXT)+1); + strcpy(command,libexecdir); + strcat(command,DIRSEP_S); + } - /* Build the filename for the helper to execute */ - command=xmalloc (strlen("gpgkeys_")+strlen(opt.keyserver_scheme)+1); - strcpy(command,"gpgkeys_"); - strcat(command,opt.keyserver_scheme); + end=command+strlen(command); + + strcat(command,GPGKEYS_PREFIX); + strcat(command,scheme); + + if(keyserver->flags.direct_uri) + strcat(command,"uri"); - if(opt.keyserver_options.use_temp_files) + strcat(command,EXEEXT); + +#ifdef GPGKEYS_CURL + if(!curl_cant_handle(scheme,keyserver->flags.direct_uri) + && path_access(command,X_OK)!=0) + strcpy(end,GPGKEYS_CURL); +#endif + + if(opt.keyserver_options.options&KEYSERVER_USE_TEMP_FILES) { - if(opt.keyserver_options.keep_temp_files) + if(opt.keyserver_options.options&KEYSERVER_KEEP_TEMP_FILES) { command=xrealloc(command,strlen(command)+ strlen(KEYSERVER_ARGS_KEEP)+1); @@ -721,41 +1055,47 @@ keyserver_spawn(int action,STRLIST list, else ret=exec_write(&spawn,command,NULL,NULL,0,0); + xfree(command); + if(ret) return ret; - fprintf(spawn->tochild,"# This is a gpg keyserver communications file\n"); + fprintf(spawn->tochild, + "# This is a GnuPG %s keyserver communications file\n",VERSION); fprintf(spawn->tochild,"VERSION %d\n",KEYSERVER_PROTO_VERSION); fprintf(spawn->tochild,"PROGRAM %s\n",VERSION); + fprintf(spawn->tochild,"SCHEME %s\n",keyserver->scheme); - if(opt.keyserver_opaque) - fprintf(spawn->tochild,"OPAQUE %s\n",opt.keyserver_opaque); + if(keyserver->opaque) + fprintf(spawn->tochild,"OPAQUE %s\n",keyserver->opaque); else { - if(opt.keyserver_host) - fprintf(spawn->tochild,"HOST %s\n",opt.keyserver_host); + if(keyserver->auth) + fprintf(spawn->tochild,"AUTH %s\n",keyserver->auth); - if(opt.keyserver_port) - fprintf(spawn->tochild,"PORT %s\n",opt.keyserver_port); - } + if(keyserver->host) + fprintf(spawn->tochild,"HOST %s\n",keyserver->host); - /* Write options */ + if(keyserver->port) + fprintf(spawn->tochild,"PORT %s\n",keyserver->port); - for(i=0,kopts=keyserver_opts;kopts[i].name;i++) - if(*(kopts[i].flag) && kopts[i].tell) - fprintf(spawn->tochild,"OPTION %s\n",kopts[i].name); + if(keyserver->path) + fprintf(spawn->tochild,"PATH %s\n",keyserver->path); + } + + /* Write global options */ - for(i=0;i<opt.keyserver_options.verbose;i++) - fprintf(spawn->tochild,"OPTION verbose\n"); + for(temp=opt.keyserver_options.other;temp;temp=temp->next) + fprintf(spawn->tochild,"OPTION %s\n",temp->d); - temp=opt.keyserver_options.other; + /* Write per-keyserver options */ - for(;temp;temp=temp->next) + for(temp=keyserver->options;temp;temp=temp->next) fprintf(spawn->tochild,"OPTION %s\n",temp->d); switch(action) { - case GET: + case KS_GET: { fprintf(spawn->tochild,"COMMAND GET\n\n"); @@ -763,6 +1103,8 @@ keyserver_spawn(int action,STRLIST list, for(i=0;i<count;i++) { + int quiet=0; + if(desc[i].mode==KEYDB_SEARCH_MODE_FPR20) { int f; @@ -770,7 +1112,7 @@ keyserver_spawn(int action,STRLIST list, fprintf(spawn->tochild,"0x"); for(f=0;f<MAX_FINGERPRINT_LEN;f++) - fprintf(spawn->tochild,"%02X",(byte)desc[i].u.fpr[f]); + fprintf(spawn->tochild,"%02X",desc[i].u.fpr[f]); fprintf(spawn->tochild,"\n"); } @@ -781,7 +1123,7 @@ keyserver_spawn(int action,STRLIST list, fprintf(spawn->tochild,"0x"); for(f=0;f<16;f++) - fprintf(spawn->tochild,"%02X",(byte)desc[i].u.fpr[f]); + fprintf(spawn->tochild,"%02X",desc[i].u.fpr[f]); fprintf(spawn->tochild,"\n"); } @@ -789,9 +1131,29 @@ keyserver_spawn(int action,STRLIST list, fprintf(spawn->tochild,"0x%08lX%08lX\n", (ulong)desc[i].u.kid[0], (ulong)desc[i].u.kid[1]); - else + else if(desc[i].mode==KEYDB_SEARCH_MODE_SHORT_KID) fprintf(spawn->tochild,"0x%08lX\n", (ulong)desc[i].u.kid[1]); + else if(desc[i].mode==KEYDB_SEARCH_MODE_EXACT) + { + fprintf(spawn->tochild,"0x0000000000000000\n"); + quiet=1; + } + else if(desc[i].mode==KEYDB_SEARCH_MODE_NONE) + continue; + else + BUG(); + + if(!quiet) + { + if(keyserver->host) + log_info(_("requesting key %s from %s server %s\n"), + keystr_from_desc(&desc[i]), + keyserver->scheme,keyserver->host); + else + log_info(_("requesting key %s from %s\n"), + keystr_from_desc(&desc[i]),keyserver->uri); + } } fprintf(spawn->tochild,"\n"); @@ -799,7 +1161,29 @@ keyserver_spawn(int action,STRLIST list, break; } - case SEND: + case KS_GETNAME: + { + STRLIST key; + + fprintf(spawn->tochild,"COMMAND GETNAME\n\n"); + + /* Which names do we want? */ + + for(key=list;key!=NULL;key=key->next) + fprintf(spawn->tochild,"%s\n",key->d); + + fprintf(spawn->tochild,"\n"); + + if(keyserver->host) + log_info(_("searching for names from %s server %s\n"), + keyserver->scheme,keyserver->host); + else + log_info(_("searching for names from %s\n"),keyserver->uri); + + break; + } + + case KS_SEND: { STRLIST key; @@ -809,7 +1193,7 @@ keyserver_spawn(int action,STRLIST list, for(key=list;key!=NULL;key=key->next) { armor_filter_context_t afx; - iobuf_t buffer=iobuf_temp(); + IOBUF buffer=iobuf_temp(); KBNODE block; temp=NULL; @@ -817,11 +1201,17 @@ keyserver_spawn(int action,STRLIST list, memset(&afx,0,sizeof(afx)); afx.what=1; + /* Tell the armor filter to use Unix-style \n line + endings, since we're going to fprintf this to a file + that (on Win32) is open in text mode. The win32 stdio + will transform the \n to \r\n and we'll end up with the + proper line endings on win32. This is a no-op on + Unix. */ + afx.eol[0]='\n'; iobuf_push_filter(buffer,armor_filter,&afx); - /* TODO: Don't use the keyblock hack here - instead, - output each key as a different ascii armored blob with - its own INFO section. */ + /* TODO: Remove Comment: lines from keys exported this + way? */ if(export_pubkeys_stream(buffer,temp,&block, opt.keyserver_options.export_options)==-1) @@ -834,7 +1224,9 @@ keyserver_spawn(int action,STRLIST list, merge_keys_and_selfsig(block); - fprintf(spawn->tochild,"INFO %s BEGIN\n",key->d); + fprintf(spawn->tochild,"INFO %08lX%08lX BEGIN\n", + (ulong)block->pkt->pkt.public_key->keyid[0], + (ulong)block->pkt->pkt.public_key->keyid[1]); for(node=block;node;node=node->next) { @@ -864,9 +1256,8 @@ keyserver_spawn(int action,STRLIST list, fprintf(spawn->tochild,"e"); fprintf(spawn->tochild,"\n"); - - break; } + break; case PKT_USER_ID: { @@ -884,7 +1275,8 @@ keyserver_spawn(int action,STRLIST list, { if(uid->name[r]==':' || uid->name[r]=='%' || uid->name[r]&0x80) - fprintf(spawn->tochild,"%%%02X",uid->name[r]); + fprintf(spawn->tochild,"%%%02X", + (byte)uid->name[r]); else fprintf(spawn->tochild,"%c",uid->name[r]); } @@ -899,10 +1291,32 @@ keyserver_spawn(int action,STRLIST list, fprintf(spawn->tochild,"\n"); } + break; + + /* This bit is really for the benefit of + people who store their keys in LDAP + servers. It makes it easy to do queries + for things like "all keys signed by + Isabella". */ + case PKT_SIGNATURE: + { + PKT_signature *sig=node->pkt->pkt.signature; + + if(!IS_UID_SIG(sig)) + continue; + + fprintf(spawn->tochild,"sig:%08lX%08lX:%X:%u:%u\n", + (ulong)sig->keyid[0],(ulong)sig->keyid[1], + sig->sig_class,sig->timestamp, + sig->expiredate); + } + break; } } - fprintf(spawn->tochild,"INFO %s END\n",key->d); + fprintf(spawn->tochild,"INFO %08lX%08lX END\n", + (ulong)block->pkt->pkt.public_key->keyid[0], + (ulong)block->pkt->pkt.public_key->keyid[1]); fprintf(spawn->tochild,"KEY %s BEGIN\n",key->d); fwrite(iobuf_get_temp_buffer(buffer), @@ -910,6 +1324,16 @@ keyserver_spawn(int action,STRLIST list, fprintf(spawn->tochild,"KEY %s END\n",key->d); iobuf_close(buffer); + + if(keyserver->host) + log_info(_("sending key %s to %s server %s\n"), + keystr(block->pkt->pkt.public_key->keyid), + keyserver->scheme,keyserver->host); + else + log_info(_("sending key %s to %s\n"), + keystr(block->pkt->pkt.public_key->keyid), + keyserver->uri); + release_kbnode(block); } @@ -919,7 +1343,7 @@ keyserver_spawn(int action,STRLIST list, break; } - case SEARCH: + case KS_SEARCH: { STRLIST key; @@ -939,7 +1363,7 @@ keyserver_spawn(int action,STRLIST list, } else { - searchstr=xmalloc (strlen(key->d)+1); + searchstr=xmalloc(strlen(key->d)+1); searchstr[0]='\0'; } @@ -948,6 +1372,13 @@ keyserver_spawn(int action,STRLIST list, fprintf(spawn->tochild,"\n"); + if(keyserver->host) + log_info(_("searching for \"%s\" from %s server %s\n"), + searchstr,keyserver->scheme,keyserver->host); + else + log_info(_("searching for \"%s\" from %s\n"), + searchstr,keyserver->uri); + break; } @@ -971,7 +1402,7 @@ keyserver_spawn(int action,STRLIST list, maxlen=1024; if(iobuf_read_line(spawn->fromchild,&line,&buflen,&maxlen)==0) { - ret = iobuf_error (spawn->fromchild); + ret=G10ERR_READ_FILE; goto fail; /* i.e. EOF */ } @@ -1000,8 +1431,8 @@ keyserver_spawn(int action,STRLIST list, else if(ascii_strncasecmp(ptr,"PROGRAM ",8)==0) { if(ascii_strncasecmp(&ptr[8],VERSION,strlen(VERSION))!=0) - log_info(_("WARNING: keyserver handler from a different " - "version of GnuPG (%s)\n"),&ptr[8]); + log_info(_("WARNING: keyserver handler from a different" + " version of GnuPG (%s)\n"),&ptr[8]); } else if(ascii_strncasecmp(ptr,"OPTION OUTOFBAND",16)==0) outofband=1; /* Currently the only OPTION */ @@ -1016,7 +1447,8 @@ keyserver_spawn(int action,STRLIST list, if(!outofband) switch(action) { - case GET: + case KS_GET: + case KS_GETNAME: { void *stats_handle; @@ -1029,7 +1461,7 @@ keyserver_spawn(int action,STRLIST list, way to do this could be to continue parsing this line-by-line and make a temp iobuf for each key. */ - import_keys_stream(spawn->fromchild,stats_handle, + import_keys_stream(spawn->fromchild,stats_handle,fpr,fpr_len, opt.keyserver_options.import_options); import_print_stats(stats_handle); @@ -1039,15 +1471,12 @@ keyserver_spawn(int action,STRLIST list, } /* Nothing to do here */ - case SEND: + case KS_SEND: break; - case SEARCH: - { - keyserver_search_prompt(spawn->fromchild,searchstr); - - break; - } + case KS_SEARCH: + keyserver_search_prompt(spawn->fromchild,searchstr); + break; default: log_fatal(_("no keyserver action!\n")); @@ -1055,7 +1484,9 @@ keyserver_spawn(int action,STRLIST list, } fail: - xfree (line); + xfree(line); + xfree(searchstr); + *prog=exec_finish(spawn); @@ -1063,45 +1494,53 @@ keyserver_spawn(int action,STRLIST list, } static int -keyserver_work(int action,STRLIST list,KEYDB_SEARCH_DESC *desc,int count) +keyserver_work(enum ks_action action,STRLIST list,KEYDB_SEARCH_DESC *desc, + int count,unsigned char **fpr,size_t *fpr_len, + struct keyserver_spec *keyserver) { int rc=0,ret=0; - if(opt.keyserver_scheme==NULL) + if(!keyserver) { log_error(_("no keyserver known (use option --keyserver)\n")); - return GPG_ERR_BAD_URI; + return G10ERR_BAD_URI; } #ifdef DISABLE_KEYSERVER_HELPERS log_error(_("external keyserver calls are not supported in this build\n")); - return GPG_ERR_KEYSERVER; + return G10ERR_KEYSERVER; #else /* Spawn a handler */ - rc=keyserver_spawn(action,list,desc,count,&ret); + rc=keyserver_spawn(action,list,desc,count,&ret,fpr,fpr_len,keyserver); if(ret) { switch(ret) { case KEYSERVER_SCHEME_NOT_FOUND: - log_error(_("no handler for keyserver scheme \"%s\"\n"), - opt.keyserver_scheme); + log_error(_("no handler for keyserver scheme `%s'\n"), + keyserver->scheme); break; case KEYSERVER_NOT_SUPPORTED: - log_error(_("action \"%s\" not supported with keyserver " - "scheme \"%s\"\n"), - action==GET?"get":action==SEND?"send": - action==SEARCH?"search":"unknown", - opt.keyserver_scheme); + log_error(_("action `%s' not supported with keyserver " + "scheme `%s'\n"), + action==KS_GET?"get":action==KS_SEND?"send": + action==KS_SEARCH?"search":"unknown", + keyserver->scheme); break; case KEYSERVER_VERSION_ERROR: - log_error(_("gpgkeys_%s does not support handler version %d\n"), - opt.keyserver_scheme,KEYSERVER_PROTO_VERSION); + log_error(_(GPGKEYS_PREFIX "%s does not support" + " handler version %d\n"), + keyserver_typemap(keyserver->scheme), + KEYSERVER_PROTO_VERSION); + break; + + case KEYSERVER_TIMEOUT: + log_error(_("keyserver timed out\n")); break; case KEYSERVER_INTERNAL_ERROR: @@ -1110,12 +1549,12 @@ keyserver_work(int action,STRLIST list,KEYDB_SEARCH_DESC *desc,int count) break; } - return GPG_ERR_KEYSERVER; + return G10ERR_KEYSERVER; } if(rc) { - log_error(_("keyserver communications error: %s\n"),gpg_strerror (rc)); + log_error(_("keyserver communications error: %s\n"),g10_errstr(rc)); return rc; } @@ -1127,18 +1566,33 @@ keyserver_work(int action,STRLIST list,KEYDB_SEARCH_DESC *desc,int count) int keyserver_export(STRLIST users) { - /* We better ask for confirmation when the user entered --send-keys - without arguments. Sending all keys might not be the thing he - intended to do */ - if (users || opt.batch || opt.answer_yes) - ; - else if ( !cpr_get_answer_is_yes - ("keyserver_export.send_all", - _("Do you really want to send all your " - "public keys to the keyserver? (y/N) "))) - return -1; + STRLIST sl=NULL; + KEYDB_SEARCH_DESC desc; + int rc=0; + + /* Weed out descriptors that we don't support sending */ + for(;users;users=users->next) + { + classify_user_id (users->d, &desc); + if(desc.mode!=KEYDB_SEARCH_MODE_SHORT_KID && + desc.mode!=KEYDB_SEARCH_MODE_LONG_KID && + desc.mode!=KEYDB_SEARCH_MODE_FPR16 && + desc.mode!=KEYDB_SEARCH_MODE_FPR20) + { + log_error(_("\"%s\" not a key ID: skipping\n"),users->d); + continue; + } + else + append_to_strlist(&sl,users->d); + } + + if(sl) + { + rc=keyserver_work(KS_SEND,sl,NULL,0,NULL,NULL,opt.keyserver); + free_strlist(sl); + } - return keyserver_work(SEND,users,NULL,0); + return rc; } int @@ -1149,7 +1603,7 @@ keyserver_import(STRLIST users) int rc=0; /* Build a list of key ids */ - desc=xmalloc (sizeof(KEYDB_SEARCH_DESC)*num); + desc=xmalloc(sizeof(KEYDB_SEARCH_DESC)*num); for(;users;users=users->next) { @@ -1159,7 +1613,7 @@ keyserver_import(STRLIST users) desc[count].mode!=KEYDB_SEARCH_MODE_FPR16 && desc[count].mode!=KEYDB_SEARCH_MODE_FPR20) { - log_error(_("skipping invalid key ID \"%s\"\n"),users->d); + log_error(_("\"%s\" not a key ID: skipping\n"),users->d); continue; } @@ -1172,15 +1626,16 @@ keyserver_import(STRLIST users) } if(count>0) - rc=keyserver_work(GET,NULL,desc,count); + rc=keyserver_work(KS_GET,NULL,desc,count,NULL,NULL,opt.keyserver); - xfree (desc); + xfree(desc); return rc; } int -keyserver_import_fprint(const byte *fprint,size_t fprint_len) +keyserver_import_fprint(const byte *fprint,size_t fprint_len, + struct keyserver_spec *keyserver) { KEYDB_SEARCH_DESC desc; @@ -1195,11 +1650,13 @@ keyserver_import_fprint(const byte *fprint,size_t fprint_len) memcpy(desc.u.fpr,fprint,fprint_len); - return keyserver_work(GET,NULL,&desc,1); + /* TODO: Warn here if the fingerprint we got doesn't match the one + we asked for? */ + return keyserver_work(KS_GET,NULL,&desc,1,NULL,NULL,keyserver); } int -keyserver_import_keyid(u32 *keyid) +keyserver_import_keyid(u32 *keyid,struct keyserver_spec *keyserver) { KEYDB_SEARCH_DESC desc; @@ -1209,7 +1666,7 @@ keyserver_import_keyid(u32 *keyid) desc.u.kid[0]=keyid[0]; desc.u.kid[1]=keyid[1]; - return keyserver_work(GET,NULL,&desc,1); + return keyserver_work(KS_GET,NULL,&desc,1,NULL,NULL,keyserver); } /* code mostly stolen from do_export_stream */ @@ -1224,14 +1681,14 @@ keyidlist(STRLIST users,KEYDB_SEARCH_DESC **klist,int *count,int fakev3) *count=0; - *klist=xmalloc (sizeof(KEYDB_SEARCH_DESC)*num); + *klist=xmalloc(sizeof(KEYDB_SEARCH_DESC)*num); kdbhd=keydb_new(0); if(!users) { ndesc = 1; - desc = xcalloc (1, ndesc * sizeof *desc); + desc = xmalloc_clear ( ndesc * sizeof *desc); desc[0].mode = KEYDB_SEARCH_MODE_FIRST; } else @@ -1245,8 +1702,8 @@ keyidlist(STRLIST users,KEYDB_SEARCH_DESC **klist,int *count,int fakev3) if(classify_user_id (sl->d, desc+ndesc)) ndesc++; else - log_error (_("key `%s' not found: %s\n"), - sl->d, gpg_strerror (GPG_ERR_INV_USER_ID)); + log_error (_("key \"%s\" not found: %s\n"), + sl->d, g10_errstr (G10ERR_INV_USER_ID)); } } @@ -1259,7 +1716,7 @@ keyidlist(STRLIST users,KEYDB_SEARCH_DESC **klist,int *count,int fakev3) rc = keydb_get_keyblock (kdbhd, &keyblock ); if( rc ) { - log_error (_("error reading keyblock: %s\n"), gpg_strerror (rc) ); + log_error (_("error reading keyblock: %s\n"), g10_errstr(rc) ); goto leave; } @@ -1276,8 +1733,8 @@ keyidlist(STRLIST users,KEYDB_SEARCH_DESC **klist,int *count,int fakev3) node->pkt->pkt.public_key->version>=4) { (*klist)[*count].mode=KEYDB_SEARCH_MODE_LONG_KID; - v3_keyid (node->pkt->pkt.public_key->pkey[0], - (*klist)[*count].u.kid); + mpi_get_keyid(node->pkt->pkt.public_key->pkey[0], + (*klist)[*count].u.kid); (*count)++; if(*count==num) @@ -1288,7 +1745,7 @@ keyidlist(STRLIST users,KEYDB_SEARCH_DESC **klist,int *count,int fakev3) } /* v4 keys get full fingerprints. v3 keys get long keyids. - This is because it's easy to calculate any sort of key id + This is because it's easy to calculate any sort of keyid from a v4 fingerprint, but not a v3 fingerprint. */ if(node->pkt->pkt.public_key->version<4) @@ -1306,6 +1763,43 @@ keyidlist(STRLIST users,KEYDB_SEARCH_DESC **klist,int *count,int fakev3) (*klist)[*count].u.fpr,&dummy); } + /* This is a little hackish, using the skipfncvalue as a + void* pointer to the keyserver spec, but we don't need + the skipfnc here, and it saves having an additional field + for this (which would be wasted space most of the + time). */ + + (*klist)[*count].skipfncvalue=NULL; + + /* Are we honoring preferred keyservers? */ + if(opt.keyserver_options.options&KEYSERVER_HONOR_KEYSERVER_URL) + { + PKT_user_id *uid=NULL; + PKT_signature *sig=NULL; + + merge_keys_and_selfsig(keyblock); + + for(node=node->next;node;node=node->next) + { + if(node->pkt->pkttype==PKT_USER_ID + && node->pkt->pkt.user_id->is_primary) + uid=node->pkt->pkt.user_id; + else if(node->pkt->pkttype==PKT_SIGNATURE + && node->pkt->pkt.signature-> + flags.chosen_selfsig && uid) + { + sig=node->pkt->pkt.signature; + break; + } + } + + /* Try and parse the keyserver URL. If it doesn't work, + then we end up writing NULL which indicates we are + the same as any other key. */ + if(sig) + (*klist)[*count].skipfncvalue=parse_preferred_keyserver(sig); + } + (*count)++; if(*count==num) @@ -1320,7 +1814,9 @@ keyidlist(STRLIST users,KEYDB_SEARCH_DESC **klist,int *count,int fakev3) rc=0; leave: - xfree (desc); + if(rc) + xfree(*klist); + xfree(desc); keydb_release(kdbhd); release_kbnode(keyblock); @@ -1330,43 +1826,91 @@ keyidlist(STRLIST users,KEYDB_SEARCH_DESC **klist,int *count,int fakev3) /* Note this is different than the original HKP refresh. It allows usernames to refresh only part of the keyring. */ -int +int keyserver_refresh(STRLIST users) { - int rc,count,fakev3=0; + int rc,count,numdesc,fakev3=0; KEYDB_SEARCH_DESC *desc; + unsigned int options=opt.keyserver_options.import_options; - /* We switch merge_only on during a refresh, as 'refresh' should - never import new keys, even if their keyids match. Is it worth - preserving the old merge_only value here? */ - opt.merge_only=1; + /* We switch merge-only on during a refresh, as 'refresh' should + never import new keys, even if their keyids match. */ + opt.keyserver_options.import_options|=IMPORT_MERGE_ONLY; + + /* Similarly, we switch on fast-import, since refresh may make + multiple import sets (due to preferred keyserver URLs). We don't + want each set to rebuild the trustdb. Instead we do it once at + the end here. */ + opt.keyserver_options.import_options|=IMPORT_FAST; /* If refresh_add_fake_v3_keyids is on and it's a HKP or MAILTO scheme, then enable fake v3 keyid generation. */ - if(opt.keyserver_options.fake_v3_keyids && opt.keyserver_scheme && - (ascii_strcasecmp(opt.keyserver_scheme,"hkp")==0 || - ascii_strcasecmp(opt.keyserver_scheme,"mailto")==0)) + if((opt.keyserver_options.options&KEYSERVER_ADD_FAKE_V3) && opt.keyserver + && (ascii_strcasecmp(opt.keyserver->scheme,"hkp")==0 || + ascii_strcasecmp(opt.keyserver->scheme,"mailto")==0)) fakev3=1; - rc=keyidlist(users,&desc,&count,fakev3); + rc=keyidlist(users,&desc,&numdesc,fakev3); if(rc) return rc; + count=numdesc; if(count>0) { - if(opt.keyserver_uri) + int i; + + /* Try to handle preferred keyserver keys first */ + for(i=0;i<numdesc;i++) + { + if(desc[i].skipfncvalue) + { + struct keyserver_spec *keyserver=desc[i].skipfncvalue; + + /* We use the keyserver structure we parsed out before. + Note that a preferred keyserver without a scheme:// + will be interpreted as hkp:// */ + + rc=keyserver_work(KS_GET,NULL,&desc[i],1,NULL,NULL,keyserver); + if(rc) + log_info(_("WARNING: unable to refresh key %s" + " via %s: %s\n"),keystr_from_desc(&desc[i]), + keyserver->uri,g10_errstr(rc)); + else + { + /* We got it, so mark it as NONE so we don't try and + get it again from the regular keyserver. */ + + desc[i].mode=KEYDB_SEARCH_MODE_NONE; + count--; + } + + free_keyserver_spec(keyserver); + } + } + } + + if(count>0) + { + if(opt.keyserver) { if(count==1) - log_info(_("refreshing 1 key from %s\n"),opt.keyserver_uri); + log_info(_("refreshing 1 key from %s\n"),opt.keyserver->uri); else log_info(_("refreshing %d keys from %s\n"), - count,opt.keyserver_uri); + count,opt.keyserver->uri); } - rc=keyserver_work(GET,NULL,desc,count); + rc=keyserver_work(KS_GET,NULL,desc,numdesc,NULL,NULL,opt.keyserver); } - xfree (desc); + xfree(desc); + + opt.keyserver_options.import_options=options; + + /* If the original options didn't have fast import, and the trustdb + is dirty, rebuild. */ + if(!(opt.keyserver_options.import_options&IMPORT_FAST)) + trustdb_check_or_update(); return rc; } @@ -1375,7 +1919,217 @@ int keyserver_search(STRLIST tokens) { if(tokens) - return keyserver_work(SEARCH,tokens,NULL,0); + return keyserver_work(KS_SEARCH,tokens,NULL,0,NULL,NULL,opt.keyserver); else return 0; } + +int +keyserver_fetch(STRLIST urilist) +{ + KEYDB_SEARCH_DESC desc; + STRLIST sl; + unsigned int options=opt.keyserver_options.import_options; + + /* Switch on fast-import, since fetch can handle more than one + import and we don't want each set to rebuild the trustdb. + Instead we do it once at the end. */ + opt.keyserver_options.import_options|=IMPORT_FAST; + + /* A dummy desc since we're not actually fetching a particular key + ID */ + memset(&desc,0,sizeof(desc)); + desc.mode=KEYDB_SEARCH_MODE_EXACT; + + for(sl=urilist;sl;sl=sl->next) + { + struct keyserver_spec *spec; + + spec=parse_keyserver_uri(sl->d,1,NULL,0); + if(spec) + { + int rc; + + /* + Set the direct_uri flag so we know later to call a direct + handler instead of the keyserver style. This lets us use + gpgkeys_curl or gpgkeys_ldapuri instead of gpgkeys_ldap to + fetch things like + ldap://keyserver.pgp.com/o=PGP%20keys?pgpkey?sub?pgpkeyid=99242560 + */ + spec->flags.direct_uri=1; + + rc=keyserver_work(KS_GET,NULL,&desc,1,NULL,NULL,spec); + if(rc) + log_info (_("WARNING: unable to fetch URI %s: %s\n"), + sl->d,g10_errstr(rc)); + + free_keyserver_spec(spec); + } + else + log_info (_("WARNING: unable to parse URI %s\n"),sl->d); + } + + opt.keyserver_options.import_options=options; + + /* If the original options didn't have fast import, and the trustdb + is dirty, rebuild. */ + if(!(opt.keyserver_options.import_options&IMPORT_FAST)) + trustdb_check_or_update(); + + return 0; +} + +/* Import key in a CERT or pointed to by a CERT */ +int +keyserver_import_cert(const char *name,unsigned char **fpr,size_t *fpr_len) +{ + char *domain,*look,*url; + IOBUF key; + int type,rc=G10ERR_GENERAL; + + look=xstrdup(name); + + domain=strrchr(look,'@'); + if(domain) + *domain='.'; + + type=get_cert(look,max_cert_size,&key,fpr,fpr_len,&url); + if(type==1) + { + int armor_status=opt.no_armor; + + /* CERTs are always in binary format */ + opt.no_armor=1; + + rc=import_keys_stream(key,NULL,fpr,fpr_len, + opt.keyserver_options.import_options); + + opt.no_armor=armor_status; + + iobuf_close(key); + } + else if(type==2 && *fpr) + { + /* We only consider the IPGP type if a fingerprint was provided. + This lets us select the right key regardless of what a URL + points to, or get the key from a keyserver. */ + if(url) + { + struct keyserver_spec *spec; + + spec=parse_keyserver_uri(url,1,NULL,0); + if(spec) + { + STRLIST list=NULL; + + add_to_strlist(&list,url); + + rc=keyserver_fetch(list); + + free_strlist(list); + free_keyserver_spec(spec); + } + } + else if(opt.keyserver) + { + /* If only a fingerprint is provided, try and fetch it from + our --keyserver */ + + rc=keyserver_import_fprint(*fpr,*fpr_len,opt.keyserver); + } + + xfree(url); + } + + xfree(look); + + return rc; +} + +/* Import key pointed to by a PKA record. Return the requested + fingerprint in fpr. */ +int +keyserver_import_pka(const char *name,unsigned char **fpr,size_t *fpr_len) +{ + char *uri; + int rc=-1; + + *fpr=xmalloc(20); + *fpr_len=20; + + uri = get_pka_info (name, *fpr); + if (uri) + { + struct keyserver_spec *spec; + spec = parse_keyserver_uri (uri, 1, NULL, 0); + if (spec) + { + rc=keyserver_import_fprint (*fpr, 20, spec); + free_keyserver_spec (spec); + } + xfree (uri); + } + + if(rc!=0) + xfree(*fpr); + + return rc; +} + +/* Import all keys that match name */ +int +keyserver_import_name(const char *name,unsigned char **fpr,size_t *fpr_len, + struct keyserver_spec *keyserver) +{ + STRLIST list=NULL; + int rc; + + append_to_strlist(&list,name); + + rc=keyserver_work(KS_GETNAME,list,NULL,0,fpr,fpr_len,keyserver); + + free_strlist(list); + + return rc; +} + +/* Use the PGP Universal trick of asking ldap://keys.(maildomain) for + the key. */ +int +keyserver_import_ldap(const char *name,unsigned char **fpr,size_t *fpr_len) +{ + char *domain; + struct keyserver_spec *keyserver; + STRLIST list=NULL; + int rc; + + append_to_strlist(&list,name); + + /* Parse out the domain */ + domain=strrchr(name,'@'); + if(!domain) + return G10ERR_GENERAL; + + domain++; + + keyserver=xmalloc_clear(sizeof(struct keyserver_spec)); + + keyserver->scheme=xstrdup("ldap"); + keyserver->host=xmalloc(5+strlen(domain)+1); + strcpy(keyserver->host,"keys."); + strcat(keyserver->host,domain); + keyserver->uri=xmalloc(strlen(keyserver->scheme)+ + 3+strlen(keyserver->host)+1); + strcpy(keyserver->uri,keyserver->scheme); + strcat(keyserver->uri,"://"); + strcat(keyserver->uri,keyserver->host); + + rc=keyserver_work(KS_GETNAME,list,NULL,0,fpr,fpr_len,keyserver); + + free_strlist(list); + + free_keyserver_spec(keyserver); + + return rc; +} diff --git a/g10/main.h b/g10/main.h index 939d12ded..fd306a467 100644 --- a/g10/main.h +++ b/g10/main.h @@ -1,5 +1,6 @@ /* main.h - * Copyright (C) 1998, 1999, 2000, 2001, 2002 Free Software Foundation, Inc. + * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, + * 2006 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -15,28 +16,36 @@ * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. */ #ifndef G10_MAIN_H #define G10_MAIN_H + #include "types.h" -#include "gpg.h" #include "../common/iobuf.h" -#include "mpi.h" #include "cipher.h" #include "keydb.h" /* It could be argued that the default cipher should be 3DES rather than CAST5, and the default compression should be 0 - (i.e. uncompressed) rather than 1 (zip). */ -#define DEFAULT_CIPHER_ALGO CIPHER_ALGO_CAST5 -#define DEFAULT_DIGEST_ALGO DIGEST_ALGO_SHA1 -#define DEFAULT_COMPRESS_ALGO 1 - -typedef struct { - int header_okay; - PK_LIST pk_list; - cipher_filter_context_t cfx; + (i.e. uncompressed) rather than 1 (zip). However, the real world + issues of speed and size come into play here. */ + +#define DEFAULT_CIPHER_ALGO CIPHER_ALGO_CAST5 +#define DEFAULT_DIGEST_ALGO DIGEST_ALGO_SHA1 +#define DEFAULT_COMPRESS_ALGO COMPRESS_ALGO_ZIP +#define DEFAULT_S2K_DIGEST_ALGO DIGEST_ALGO_SHA1 + +#define S2K_DIGEST_ALGO (opt.s2k_digest_algo?opt.s2k_digest_algo:DEFAULT_S2K_DIGEST_ALGO) + +typedef struct +{ + int header_okay; + PK_LIST pk_list; + DEK *symkey_dek; + STRING2KEY *symkey_s2k; + cipher_filter_context_t cfx; } encrypt_filter_context_t; struct groupitem @@ -46,7 +55,7 @@ struct groupitem struct groupitem *next; }; -/*-- g10.c --*/ +/*-- gpg.c --*/ extern int g10_errors_seen; #if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 5 ) @@ -64,18 +73,19 @@ char *make_radix64_string( const byte *data, size_t len ); /*-- misc.c --*/ void trap_unaligned(void); int disable_core_dumps(void); +void register_secured_file (const char *fname); +void unregister_secured_file (const char *fname); +int is_secured_file (int fd); +int is_secured_filename (const char *fname); u16 checksum_u16( unsigned n ); u16 checksum( byte *p, unsigned n ); u16 checksum_mpi( gcry_mpi_t a ); u32 buffer_to_u32( const byte *buffer ); const byte *get_session_marker( size_t *rlen ); int openpgp_cipher_test_algo( int algo ); -int openpgp_pk_test_algo( int algo, unsigned int usage_flags ); +int openpgp_pk_test_algo( int algo ); int openpgp_pk_algo_usage ( int algo ); int openpgp_md_test_algo( int algo ); -int openpgp_md_map_name (const char *string); -int openpgp_cipher_map_name (const char *string); -int openpgp_pk_map_name (const char *string); #ifdef USE_IDEA void idea_cipher_warn( int show ); @@ -91,9 +101,10 @@ struct expando_args }; char *pct_expando(const char *string,struct expando_args *args); -int hextobyte( const char *s ); void deprecated_warning(const char *configname,unsigned int configlineno, const char *option,const char *repl1,const char *repl2); +void deprecated_command (const char *name); + const char *compress_algo_to_string(int algo); int string_to_compress_algo(const char *string); int check_compress_algo(int algo); @@ -106,10 +117,19 @@ struct parse_options { char *name; unsigned int bit; + char **value; + char *help; }; -int parse_options(char *str,unsigned int *options,struct parse_options *opts); - +char *optsep(char **stringp); +char *argsplit(char *string); +int parse_options(char *str,unsigned int *options, + struct parse_options *opts,int noisy); +char *unescape_percent_string (const unsigned char *s); +int has_invalid_email_chars (const char *s); +int is_valid_mailbox (const char *name); +const char *get_libexecdir (void); +int path_access(const char *file,int mode); /* Temporary helpers. */ int pubkey_get_npkey( int algo ); @@ -117,31 +137,24 @@ int pubkey_get_nskey( int algo ); int pubkey_get_nsig( int algo ); int pubkey_get_nenc( int algo ); unsigned int pubkey_nbits( int algo, gcry_mpi_t *pkey ); - -/* MPI helpers. */ -int mpi_write( iobuf_t out, gcry_mpi_t a ); -int mpi_write_opaque( iobuf_t out, gcry_mpi_t a ); -gcry_mpi_t mpi_read(iobuf_t inp, unsigned int *ret_nread, int secure ); -gcry_mpi_t mpi_read_opaque(iobuf_t inp, unsigned int *ret_nread ); int mpi_print( FILE *fp, gcry_mpi_t a, int mode ); - - /*-- helptext.c --*/ void display_online_help( const char *keyword ); /*-- encode.c --*/ +int setup_symkey(STRING2KEY **symkey_s2k,DEK **symkey_dek); int encode_symmetric( const char *filename ); int encode_store( const char *filename ); -int encode_crypt( const char *filename, STRLIST remusr ); +int encode_crypt( const char *filename, STRLIST remusr, int use_symkey ); void encode_crypt_files(int nfiles, char **files, STRLIST remusr); int encrypt_filter( void *opaque, int control, iobuf_t a, byte *buf, size_t *ret_len); /*-- sign.c --*/ -int complete_sig( PKT_signature *sig, PKT_secret_key *sk, MD_HANDLE md ); +int complete_sig( PKT_signature *sig, PKT_secret_key *sk, gcry_md_hd_t md ); int sign_file( STRLIST filenames, int detached, STRLIST locusr, int do_encrypt, STRLIST remusr, const char *outfile ); int clearsign_file( const char *fname, STRLIST locusr, const char *outfile ); @@ -149,31 +162,43 @@ int sign_symencrypt_file (const char *fname, STRLIST locusr); /*-- sig-check.c --*/ int check_revocation_keys (PKT_public_key *pk, PKT_signature *sig); +int check_backsig(PKT_public_key *main_pk,PKT_public_key *sub_pk, + PKT_signature *backsig); int check_key_signature( KBNODE root, KBNODE node, int *is_selfsig ); int check_key_signature2( KBNODE root, KBNODE node, PKT_public_key *check_pk, - PKT_public_key *ret_pk, int *is_selfsig, - u32 *r_expiredate, int *r_expired ); + PKT_public_key *ret_pk, int *is_selfsig, + u32 *r_expiredate, int *r_expired ); /*-- delkey.c --*/ int delete_keys( STRLIST names, int secret, int allow_both ); /*-- keyedit.c --*/ -void keyedit_menu( const char *username, STRLIST locusr, STRLIST cmds, - int sign_mode ); +void keyedit_menu( const char *username, STRLIST locusr, + STRLIST commands, int quiet, int seckey_check ); void show_basic_key_info (KBNODE keyblock); /*-- keygen.c --*/ -u32 ask_expire_interval(int object); +u32 parse_expire_string(const char *string); +u32 ask_expire_interval(int object,const char *def_expire); u32 ask_expiredate(void); -void generate_keypair( const char *fname, const char *card_serialno ); +void generate_keypair( const char *fname, const char *card_serialno, + const char *backup_encryption_dir ); int keygen_set_std_prefs (const char *string,int personal); PKT_user_id *keygen_get_std_prefs (void); int keygen_add_key_expire( PKT_signature *sig, void *opaque ); int keygen_add_std_prefs( PKT_signature *sig, void *opaque ); int keygen_upd_std_prefs( PKT_signature *sig, void *opaque ); int keygen_add_keyserver_url(PKT_signature *sig, void *opaque); +int keygen_add_notations(PKT_signature *sig,void *opaque); int keygen_add_revkey(PKT_signature *sig, void *opaque); +int make_backsig(PKT_signature *sig,PKT_public_key *pk, + PKT_public_key *sub_pk,PKT_secret_key *sub_sk); int generate_subkeypair( KBNODE pub_keyblock, KBNODE sec_keyblock ); +#ifdef ENABLE_CARD_SUPPORT +int generate_card_subkeypair (KBNODE pub_keyblock, KBNODE sec_keyblock, + int keyno, const char *serialno); +int save_unprotected_key_to_card (PKT_secret_key *sk, int keyno); +#endif /*-- openfile.c --*/ int overwrite_filep( const char *fname ); @@ -185,29 +210,29 @@ void try_make_homedir( const char *fname ); /*-- seskey.c --*/ void make_session_key( DEK *dek ); -gcry_mpi_t encode_session_key( DEK *dek, unsigned int nbits); -gcry_mpi_t encode_md_value( int pubkey_algo, MD_HANDLE md, - int hash_algo, unsigned nbits, int v3compathack ); - -/*-- comment.c --*/ -KBNODE make_comment_node_from_buffer (const char *s, size_t n); -KBNODE make_comment_node( const char *s ); -KBNODE make_mpi_comment_node( const char *s, gcry_mpi_t a ); +gcry_mpi_t encode_session_key( DEK *dek, unsigned nbits ); +gcry_mpi_t encode_md_value( PKT_public_key *pk, PKT_secret_key *sk, + gcry_md_hd_t md, int hash_algo ); /*-- import.c --*/ -int parse_import_options(char *str,unsigned int *options); +int parse_import_options(char *str,unsigned int *options,int noisy); void import_keys( char **fnames, int nnames, void *stats_hd, unsigned int options ); -int import_keys_stream( iobuf_t inp, - void *stats_hd, unsigned int options ); +int import_keys_stream( iobuf_t inp,void *stats_hd,unsigned char **fpr, + size_t *fpr_len,unsigned int options ); void *import_new_stats_handle (void); void import_release_stats_handle (void *p); void import_print_stats (void *hd); int collapse_uids( KBNODE *keyblock ); +int auto_create_card_key_stub ( const char *serialnostr, + const unsigned char *fpr1, + const unsigned char *fpr2, + const unsigned char *fpr3); + /*-- export.c --*/ -int parse_export_options(char *str,unsigned int *options); +int parse_export_options(char *str,unsigned int *options,int noisy); int export_pubkeys( STRLIST users, unsigned int options ); int export_pubkeys_stream( iobuf_t out, STRLIST users, KBNODE *keyblock_out, unsigned int options ); @@ -221,7 +246,7 @@ int enarmor_file( const char *fname ); /*-- revoke.c --*/ struct revocation_reason_info; int gen_revoke( const char *uname ); -int gen_desig_revoke( const char *uname ); +int gen_desig_revoke( const char *uname, STRLIST locusr); int revocation_reason_build_cb( PKT_signature *sig, void *opaque ); struct revocation_reason_info * ask_revocation_reason( int key_rev, int cert_rev, int hint ); @@ -230,17 +255,20 @@ void release_revocation_reason_info( struct revocation_reason_info *reason ); /*-- keylist.c --*/ void public_key_list( STRLIST list ); void secret_key_list( STRLIST list ); +void print_subpackets_colon(PKT_signature *sig); void reorder_keyblock (KBNODE keyblock); void list_keyblock( KBNODE keyblock, int secret, int fpr, void *opaque ); void print_fingerprint (PKT_public_key *pk, PKT_secret_key *sk, int mode); +void print_revokers(PKT_public_key *pk); void show_policy_url(PKT_signature *sig,int indent,int mode); void show_keyserver_url(PKT_signature *sig,int indent,int mode); -void show_notation(PKT_signature *sig,int indent,int mode); +void show_notation(PKT_signature *sig,int indent,int mode,int which); void dump_attribs(const PKT_user_id *uid, PKT_public_key *pk,PKT_secret_key *sk); void set_attrib_fd(int fd); void print_seckey_info (PKT_secret_key *sk); void print_pubkey_info (FILE *fp, PKT_public_key *pk); +void print_card_key_info (FILE *fp, KBNODE keyblock); /*-- verify.c --*/ void print_file_status( int status, const char *name, int what ); @@ -249,24 +277,26 @@ int verify_files( int nfiles, char **files ); /*-- decrypt.c --*/ int decrypt_message( const char *filename ); -void decrypt_messages(int nfiles, char **files); +void decrypt_messages(int nfiles, char *files[]); /*-- plaintext.c --*/ -int hash_datafiles( MD_HANDLE md, MD_HANDLE md2, +int hash_datafiles( gcry_md_hd_t md, gcry_md_hd_t md2, STRLIST files, const char *sigfilename, int textmode ); -/*-- pipemode.c --*/ -void run_in_pipemode (void); - -/*-- card-util.c --*/ -void change_pin (int no, int allow_admin); -void card_status (FILE *fp, char *serialnobuf, size_t serialnobuflen); -void card_edit (STRLIST commands); - /*-- signal.c --*/ void init_signals(void); void pause_on_sigusr( int which ); void block_all_signals(void); void unblock_all_signals(void); + +#ifdef ENABLE_CARD_SUPPORT +/*-- card-util.c --*/ +void change_pin (int no, int allow_admin); +void card_status (FILE *fp, char *serialno, size_t serialnobuflen); +void card_edit (STRLIST commands); +int card_generate_subkey (KBNODE pub_keyblock, KBNODE sec_keyblock); +int card_store_subkey (KBNODE node, int use); +#endif + #endif /*G10_MAIN_H*/ diff --git a/g10/mainproc.c b/g10/mainproc.c index 40b9bd20a..22711cf59 100644 --- a/g10/mainproc.c +++ b/g10/mainproc.c @@ -1,5 +1,6 @@ /* mainproc.c - handle packets - * Copyright (C) 1998,1999,2000,2001,2002,2003 Free Software Foundation, Inc. + * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, + * 2005, 2006 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -15,7 +16,8 @@ * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. */ #include <config.h> @@ -25,9 +27,9 @@ #include <assert.h> #include <time.h> +#include "gpg.h" #include "packet.h" #include "iobuf.h" -#include "memory.h" #include "options.h" #include "util.h" #include "cipher.h" @@ -49,37 +51,34 @@ struct kidlist_item { }; - /**************** * Structure to hold the context */ typedef struct mainproc_context *CTX; -struct mainproc_context { - struct mainproc_context *anchor; /* may be useful in the future */ - PKT_public_key *last_pubkey; - PKT_secret_key *last_seckey; - PKT_user_id *last_user_id; - md_filter_context_t mfx; - int sigs_only; /* process only signatures and reject all other stuff */ - int encrypt_only; /* process only encrytion messages */ - STRLIST signed_data; - const char *sigfilename; - DEK *dek; - int last_was_session_key; - KBNODE list; /* the current list of packets */ - int have_data; - iobuf_t iobuf; /* used to get the filename etc. */ - int trustletter; /* temp usage in list_node */ - ulong local_id; /* ditto */ - struct kidlist_item *pkenc_list; /* list of encryption packets */ - struct { - int op; - int stop_now; - } pipemode; +struct mainproc_context +{ + struct mainproc_context *anchor; /* May be useful in the future. */ + PKT_public_key *last_pubkey; + PKT_secret_key *last_seckey; + PKT_user_id *last_user_id; + md_filter_context_t mfx; + int sigs_only; /* Process only signatures and reject all other stuff. */ + int encrypt_only; /* Process only encryption messages. */ + STRLIST signed_data; + const char *sigfilename; + DEK *dek; + int last_was_session_key; + KBNODE list; /* The current list of packets. */ + int have_data; + IOBUF iobuf; /* Used to get the filename etc. */ + int trustletter; /* Temporary usage in list_node. */ + ulong symkeys; + struct kidlist_item *pkenc_list; /* List of encryption packets. */ + int any_sig_seen; /* Set to true if a signature packet has been seen. */ }; -static int do_proc_packets( CTX c, iobuf_t a ); +static int do_proc_packets( CTX c, IOBUF a ); static void list_node( CTX c, KBNODE node ); static void proc_tree( CTX c, KBNODE node ); @@ -94,43 +93,28 @@ release_list( CTX c ) release_kbnode( c->list ); while( c->pkenc_list ) { struct kidlist_item *tmp = c->pkenc_list->next; - xfree ( c->pkenc_list ); + xfree( c->pkenc_list ); c->pkenc_list = tmp; } c->pkenc_list = NULL; c->list = NULL; c->have_data = 0; c->last_was_session_key = 0; - c->pipemode.op = 0; - c->pipemode.stop_now = 0; - xfree (c->dek); c->dek = NULL; + xfree(c->dek); c->dek = NULL; } static int add_onepass_sig( CTX c, PACKET *pkt ) { - KBNODE node; + KBNODE node; - if( c->list ) { /* add another packet */ - /* We can only append another onepass packet if the list - * does contain only onepass packets */ - for( node=c->list; node && node->pkt->pkttype == PKT_ONEPASS_SIG; - node = node->next ) - ; - if( node ) { - /* this is not the case, so we flush the current thing and - * allow this packet to start a new verification thing */ - release_list( c ); - c->list = new_kbnode( pkt ); - } - else - add_kbnode( c->list, new_kbnode( pkt )); - } - else /* insert the first one */ - c->list = node = new_kbnode( pkt ); + if ( c->list ) /* add another packet */ + add_kbnode( c->list, new_kbnode( pkt )); + else /* insert the first one */ + c->list = node = new_kbnode( pkt ); - return 1; + return 1; } @@ -142,30 +126,6 @@ add_gpg_control( CTX c, PACKET *pkt ) * Process the last one and reset everything */ release_list(c); } - else if ( pkt->pkt.gpg_control->control == CTRLPKT_PIPEMODE ) { - /* Pipemode control packet */ - if ( pkt->pkt.gpg_control->datalen < 2 ) - log_fatal ("invalid pipemode control packet length\n"); - if (pkt->pkt.gpg_control->data[0] == 1) { - /* start the whole thing */ - assert ( !c->list ); /* we should be in a pretty virgin state */ - assert ( !c->pipemode.op ); - c->pipemode.op = pkt->pkt.gpg_control->data[1]; - } - else if (pkt->pkt.gpg_control->data[0] == 2) { - /* the signed material follows in a plaintext packet */ - assert ( c->pipemode.op == 'B' ); - } - else if (pkt->pkt.gpg_control->data[0] == 3) { - assert ( c->pipemode.op == 'B' ); - release_list (c); - /* and tell the outer loop to terminate */ - c->pipemode.stop_now = 1; - } - else - log_fatal ("invalid pipemode control packet code\n"); - return 0; /* no need to store the packet */ - } if( c->list ) /* add another packet */ add_kbnode( c->list, new_kbnode( pkt )); @@ -216,6 +176,7 @@ add_signature( CTX c, PACKET *pkt ) { KBNODE node; + c->any_sig_seen = 1; if( pkt->pkttype == PKT_SIGNATURE && !c->list ) { /* This is the first signature for the following datafile. * GPG does not write such packets; instead it always uses @@ -238,43 +199,47 @@ add_signature( CTX c, PACKET *pkt ) return 1; } -static void -symkey_decrypt_sesskey (DEK * dek, byte *sesskey, size_t slen) +static int +symkey_decrypt_seskey( DEK *dek, byte *seskey, size_t slen ) { - CIPHER_HANDLE hd; - int n; + gcry_cipher_hd_t hd; - if (slen < 17 || slen > 33) + if(slen < 17 || slen > 33) { log_error ( _("weird size for an encrypted session key (%d)\n"), - (int)slen); - return; + (int)slen); + return G10ERR_BAD_KEY; } - /* we checked the DEK values before, so consider all errors as fatal */ + if (gcry_cipher_open (&hd, dek->algo, GCRY_CIPHER_MODE_CFB, 1)) - BUG(); - if (gcry_cipher_setkey (hd, dek->key, dek->keylen)) - BUG(); - gcry_cipher_setiv (hd, NULL, 0); - gcry_cipher_decrypt (hd, sesskey, slen, NULL, 0); - gcry_cipher_close (hd); - /* check first byte (the cipher algo) */ - if (openpgp_cipher_test_algo (sesskey[0])) - { - log_error (_("invalid symkey algorithm detected (%d)\n"), - sesskey[0]); - return; - } - n = gcry_cipher_get_algo_keylen (sesskey[0]); - if (n > DIM(dek->key)) + BUG (); + if (gcry_cipher_setkey ( hd, dek->key, dek->keylen )) BUG (); - /* now we replace the dek components with the real session key - to decrypt the contents of the sequencing packet. */ - dek->keylen = n; - dek->algo = sesskey[0]; - memcpy (dek->key, sesskey + 1, dek->keylen); - /*log_hexdump ("thekey", dek->key, dek->keylen);*/ -} + gcry_cipher_setiv ( hd, NULL, 0 ); + gcry_cipher_decrypt ( hd, seskey, slen, NULL, 0 ); + gcry_cipher_close ( hd ); + + /* Now we replace the dek components with the real session key to + decrypt the contents of the sequencing packet. */ + + dek->keylen=slen-1; + dek->algo=seskey[0]; + + if(dek->keylen > DIM(dek->key)) + BUG (); + + /* This is not completely accurate, since a bad passphrase may have + resulted in a garbage algorithm byte, but it's close enough since + a bogus byte here will fail later. */ + if(dek->algo==CIPHER_ALGO_IDEA) + idea_cipher_warn(0); + + memcpy(dek->key, seskey + 1, dek->keylen); + + /*log_hexdump( "thekey", dek->key, dek->keylen );*/ + + return 0; +} static void proc_symkey_enc( CTX c, PACKET *pkt ) @@ -284,26 +249,91 @@ proc_symkey_enc( CTX c, PACKET *pkt ) enc = pkt->pkt.symkey_enc; if (!enc) log_error ("invalid symkey encrypted packet\n"); - else { + else if(!c->dek) + { int algo = enc->cipher_algo; - const char *s; + const char *s = gcry_cipher_algo_name (algo); - s = gcry_cipher_algo_name (algo); - if (s && *s) - log_info(_("%s encrypted data\n"), s ); + if(s) + { + if(!opt.quiet) + { + if(enc->seskeylen) + log_info(_("%s encrypted session key\n"), s ); + else + log_info(_("%s encrypted data\n"), s ); + } + } else - log_info(_("encrypted with unknown algorithm %d\n"), algo ); + log_error(_("encrypted with unknown algorithm %d\n"), algo ); + + if(openpgp_md_test_algo (enc->s2k.hash_algo)) + { + log_error(_("passphrase generated with unknown digest" + " algorithm %d\n"),enc->s2k.hash_algo); + s=NULL; + } c->last_was_session_key = 2; - if ( opt.list_only ) - goto leave; - c->dek = passphrase_to_dek( NULL, 0, algo, &enc->s2k, 0, NULL, NULL ); - if (c->dek) - c->dek->algo_info_printed = 1; - if ( c->dek && enc->seskeylen ) - symkey_decrypt_sesskey( c->dek, enc->seskey, enc->seskeylen ); - } -leave: + if(!s || opt.list_only) + goto leave; + + if(opt.override_session_key) + { + c->dek = xmalloc_clear( sizeof *c->dek ); + if(get_override_session_key(c->dek, opt.override_session_key)) + { + xfree(c->dek); + c->dek = NULL; + } + } + else + { + int canceled; + + c->dek = passphrase_to_dek (NULL, 0, algo, &enc->s2k, 0, + NULL, &canceled); + if (canceled) + { + /* For unknown reasons passphrase_to_dek does only + return NULL if a new passphrase has been requested + and has not been repeated correctly. Thus even + with a cancel requested (by means of the gpg-agent) + it won't return NULL but an empty passphrase. We + take the most conservative approach for now and + work around it right here. */ + xfree (c->dek); + c->dek = NULL; + } + + if(c->dek) + { + c->dek->symmetric=1; + + /* FIXME: This doesn't work perfectly if a symmetric + key comes before a public key in the message - if + the user doesn't know the passphrase, then there is + a chance that the "decrypted" algorithm will happen + to be a valid one, which will make the returned dek + appear valid, so we won't try any public keys that + come later. */ + if(enc->seskeylen) + { + if(symkey_decrypt_seskey(c->dek, enc->seskey, + enc->seskeylen)) + { + xfree(c->dek); + c->dek=NULL; + } + } + else + c->dek->algo_info_printed = 1; + } + } + } + + leave: + c->symkeys++; free_packet(pkt); } @@ -320,7 +350,7 @@ proc_pubkey_enc( CTX c, PACKET *pkt ) /* Hmmm: why do I have this algo check here - anyway there is * function to check it. */ if( opt.verbose ) - log_info(_("public key is %08lX\n"), (ulong)enc->keyid[1] ); + log_info(_("public key is %s\n"), keystr(enc->keyid) ); if( is_status_enabled() ) { char buf[50]; @@ -332,62 +362,54 @@ proc_pubkey_enc( CTX c, PACKET *pkt ) if( !opt.list_only && opt.override_session_key ) { /* It does not make much sense to store the session key in * secure memory because it has already been passed on the - * command line and the GCHQ knows about it */ - c->dek = xcalloc (1, sizeof *c->dek ); + * command line and the GCHQ knows about it. */ + c->dek = xmalloc_clear( sizeof *c->dek ); result = get_override_session_key ( c->dek, opt.override_session_key ); if ( result ) { - xfree (c->dek); c->dek = NULL; + xfree(c->dek); c->dek = NULL; } } else if( is_ELGAMAL(enc->pubkey_algo) || enc->pubkey_algo == PUBKEY_ALGO_DSA || is_RSA(enc->pubkey_algo) ) { + /* FIXME: strore this all in a list and process it later */ + if ( !c->dek && ((!enc->keyid[0] && !enc->keyid[1]) || opt.try_all_secrets || !seckey_available( enc->keyid )) ) { if( opt.list_only ) result = -1; else { - c->dek = xcalloc_secure (1, sizeof *c->dek); + c->dek = xmalloc_secure_clear( sizeof *c->dek ); if( (result = get_session_key( enc, c->dek )) ) { /* error: delete the DEK */ - xfree (c->dek); c->dek = NULL; + xfree(c->dek); c->dek = NULL; } } } else - result = GPG_ERR_NO_SECKEY; + result = G10ERR_NO_SECKEY; } else - result = GPG_ERR_PUBKEY_ALGO; + result = G10ERR_PUBKEY_ALGO; if( result == -1 ) ; - else { - if( !result ) { - if( opt.verbose > 1 ) - log_info( _("public key encrypted data: good DEK\n") ); - if ( opt.show_session_key ) { - int i; - char *buf = xmalloc ( c->dek->keylen*2 + 20 ); - sprintf ( buf, "%d:", c->dek->algo ); - for(i=0; i < c->dek->keylen; i++ ) - sprintf(buf+strlen(buf), "%02X", c->dek->key[i] ); - log_info( "session key: \"%s\"\n", buf ); - write_status_text ( STATUS_SESSION_KEY, buf ); - } - } + else + { /* store it for later display */ - { - struct kidlist_item *x = xmalloc ( sizeof *x ); - x->kid[0] = enc->keyid[0]; - x->kid[1] = enc->keyid[1]; - x->pubkey_algo = enc->pubkey_algo; - x->reason = result; - x->next = c->pkenc_list; - c->pkenc_list = x; - } - } + struct kidlist_item *x = xmalloc( sizeof *x ); + x->kid[0] = enc->keyid[0]; + x->kid[1] = enc->keyid[1]; + x->pubkey_algo = enc->pubkey_algo; + x->reason = result; + x->next = c->pkenc_list; + c->pkenc_list = x; + + if( !result && opt.verbose > 1 ) + log_info( _("public key encrypted data: good DEK\n") ); + } + free_packet(pkt); } @@ -409,31 +431,29 @@ print_pkenc_list( struct kidlist_item *list, int failed ) if ( !failed && list->reason ) continue; - algstr = gcry_pk_algo_name (list->pubkey_algo); - pk = xcalloc (1, sizeof *pk ); + algstr = gcry_pk_algo_name ( list->pubkey_algo ); + pk = xmalloc_clear( sizeof *pk ); - if (!algstr || !*algstr) - algstr = "[?]"; + if( !algstr ) + algstr = "[?]"; pk->pubkey_algo = list->pubkey_algo; - if( !get_pubkey( pk, list->kid ) ) { - size_t n; + if( !get_pubkey( pk, list->kid ) ) + { char *p; - log_info( _("encrypted with %u-bit %s key, ID %08lX, created %s\n"), - nbits_from_pk( pk ), algstr, (ulong)list->kid[1], - strtimestamp(pk->timestamp) ); - fputs(" \"", log_get_stream() ); - p = get_user_id( list->kid, &n ); - print_utf8_string2 ( log_get_stream(), p, n, '"' ); - xfree (p); - fputs("\"\n", log_get_stream() ); - } - else { - log_info(_("encrypted with %s key, ID %08lX\n"), - algstr, (ulong) list->kid[1] ); - } + log_info( _("encrypted with %u-bit %s key, ID %s, created %s\n"), + nbits_from_pk( pk ), algstr, keystr_from_pk(pk), + strtimestamp(pk->timestamp) ); + p=get_user_id_native(list->kid); + fprintf(log_get_stream(),_(" \"%s\"\n"),p); + xfree(p); + } + else + log_info(_("encrypted with %s key, ID %s\n"), + algstr,keystr(list->kid)); + free_public_key( pk ); - if( gpg_err_code (list->reason) == GPG_ERR_NO_SECKEY ) { + if( list->reason == G10ERR_NO_SECKEY ) { if( is_status_enabled() ) { char buf[20]; sprintf(buf,"%08lX%08lX", (ulong)list->kid[0], @@ -443,7 +463,7 @@ print_pkenc_list( struct kidlist_item *list, int failed ) } else if (list->reason) log_info(_("public key decryption failed: %s\n"), - gpg_strerror (list->reason)); + g10_errstr(list->reason)); } } @@ -453,10 +473,18 @@ proc_encrypted( CTX c, PACKET *pkt ) { int result = 0; - if (!opt.quiet) { + if (!opt.quiet) + { + if(c->symkeys>1) + log_info(_("encrypted with %lu passphrases\n"),c->symkeys); + else if(c->symkeys==1) + log_info(_("encrypted with 1 passphrase\n")); print_pkenc_list ( c->pkenc_list, 1 ); print_pkenc_list ( c->pkenc_list, 0 ); - } + } + + /* FIXME: Figure out the session key by looking at all pkenc packets. */ + write_status( STATUS_BEGIN_DECRYPTION ); @@ -467,43 +495,58 @@ proc_encrypted( CTX c, PACKET *pkt ) int algo; STRING2KEY s2kbuf, *s2k = NULL; - /* assume this is old style conventional encrypted data */ - if ( (algo = opt.def_cipher_algo)) - log_info (_("assuming %s encrypted data\n"), + if(opt.override_session_key) + { + c->dek = xmalloc_clear( sizeof *c->dek ); + result=get_override_session_key(c->dek, opt.override_session_key); + if(result) + { + xfree(c->dek); + c->dek = NULL; + } + } + else + { + /* assume this is old style conventional encrypted data */ + if ( (algo = opt.def_cipher_algo)) + log_info (_("assuming %s encrypted data\n"), gcry_cipher_algo_name (algo)); - else if ( gcry_cipher_test_algo(CIPHER_ALGO_IDEA) ) { - algo = opt.def_cipher_algo; - if (!algo) - algo = opt.s2k_cipher_algo; - idea_cipher_warn(1); - log_info (_("IDEA cipher unavailable, " - "optimistically attempting to use %s instead\n"), - gcry_cipher_algo_name (algo)); - } - else { - algo = CIPHER_ALGO_IDEA; - if (!opt.s2k_digest_algo) { - /* If no digest is given we assume MD5 */ - s2kbuf.mode = 0; - s2kbuf.hash_algo = GCRY_MD_MD5; - s2k = &s2kbuf; - } - log_info (_("assuming %s encrypted data\n"), "IDEA"); - } + else if ( gcry_cipher_test_algo (CIPHER_ALGO_IDEA) ) + { + algo = opt.def_cipher_algo; + if (!algo) + algo = opt.s2k_cipher_algo; + idea_cipher_warn(1); + log_info (_("IDEA cipher unavailable, " + "optimistically attempting to use %s instead\n"), + gcry_cipher_algo_name (algo)); + } + else + { + algo = CIPHER_ALGO_IDEA; + if (!opt.s2k_digest_algo) + { + /* If no digest is given we assume MD5 */ + s2kbuf.mode = 0; + s2kbuf.hash_algo = DIGEST_ALGO_MD5; + s2k = &s2kbuf; + } + log_info (_("assuming %s encrypted data\n"), "IDEA"); + } - c->dek = passphrase_to_dek ( NULL, 0, algo, s2k, 0, NULL, NULL ); - if (c->dek) - c->dek->algo_info_printed = 1; + c->dek = passphrase_to_dek ( NULL, 0, algo, s2k, 0, NULL, NULL ); + if (c->dek) + c->dek->algo_info_printed = 1; + } } else if( !c->dek ) - result = GPG_ERR_NO_SECKEY; + result = G10ERR_NO_SECKEY; if( !result ) result = decrypt_data( c, pkt->pkt.encrypted, c->dek ); - xfree (c->dek); c->dek = NULL; if( result == -1 ) ; - else if( !result || (gpg_err_code (result)==GPG_ERR_BAD_SIGNATURE + else if( !result || (gpg_err_code (result) == GPG_ERR_BAD_SIGNATURE && opt.ignore_mdc_error)) { write_status( STATUS_DECRYPTION_OKAY ); if( opt.verbose > 1 ) @@ -512,25 +555,35 @@ proc_encrypted( CTX c, PACKET *pkt ) write_status( STATUS_GOODMDC ); else if(!opt.no_mdc_warn) log_info (_("WARNING: message was not integrity protected\n")); + if(opt.show_session_key) + { + int i; + char *buf = xmalloc ( c->dek->keylen*2 + 20 ); + sprintf ( buf, "%d:", c->dek->algo ); + for(i=0; i < c->dek->keylen; i++ ) + sprintf(buf+strlen(buf), "%02X", c->dek->key[i] ); + log_info( "session key: `%s'\n", buf ); + write_status_text ( STATUS_SESSION_KEY, buf ); + } } - else if( gpg_err_code (result) == GPG_ERR_BAD_SIGNATURE ) { + else if( result == G10ERR_BAD_SIGN ) { log_error(_("WARNING: encrypted message has been manipulated!\n")); write_status( STATUS_BADMDC ); write_status( STATUS_DECRYPTION_FAILED ); } else { write_status( STATUS_DECRYPTION_FAILED ); - log_error(_("decryption failed: %s\n"), gpg_strerror (result)); + log_error(_("decryption failed: %s\n"), g10_errstr(result)); /* Hmmm: does this work when we have encrypted using multiple * ways to specify the session key (symmmetric and PK)*/ } + xfree(c->dek); c->dek = NULL; free_packet(pkt); c->last_was_session_key = 0; write_status( STATUS_END_DECRYPTION ); } - static void proc_plaintext( CTX c, PACKET *pkt ) { @@ -543,7 +596,8 @@ proc_plaintext( CTX c, PACKET *pkt ) else if( opt.verbose ) log_info(_("original file name='%.*s'\n"), pt->namelen, pt->name); free_md_filter_context( &c->mfx ); - gcry_md_open (&c->mfx.md, 0, 0); + if (gcry_md_open (&c->mfx.md, 0, 0)) + BUG (); /* fixme: we may need to push the textfilter if we have sigclass 1 * and no armoring - Not yet tested * Hmmm, why don't we need it at all if we have sigclass 1 @@ -551,72 +605,90 @@ proc_plaintext( CTX c, PACKET *pkt ) * See: Russ Allbery's mail 1999-02-09 */ any = clearsig = only_md5 = 0; - for(n=c->list; n; n = n->next ) { - if( n->pkt->pkttype == PKT_ONEPASS_SIG ) { - if( n->pkt->pkt.onepass_sig->digest_algo ) { - gcry_md_enable ( c->mfx.md, n->pkt->pkt.onepass_sig->digest_algo ); + for(n=c->list; n; n = n->next ) + { + if( n->pkt->pkttype == PKT_ONEPASS_SIG ) + { + /* For the onepass signature case */ + if( n->pkt->pkt.onepass_sig->digest_algo ) + { + gcry_md_enable (c->mfx.md, + n->pkt->pkt.onepass_sig->digest_algo); if( !any && n->pkt->pkt.onepass_sig->digest_algo - == DIGEST_ALGO_MD5 ) - only_md5 = 1; + == DIGEST_ALGO_MD5 ) + only_md5 = 1; else - only_md5 = 0; + only_md5 = 0; any = 1; - } + } if( n->pkt->pkt.onepass_sig->sig_class != 0x01 ) - only_md5 = 0; - } + only_md5 = 0; + } else if( n->pkt->pkttype == PKT_GPG_CONTROL && n->pkt->pkt.gpg_control->control - == CTRLPKT_CLEARSIGN_START ) { + == CTRLPKT_CLEARSIGN_START ) + { + /* For the clearsigned message case */ size_t datalen = n->pkt->pkt.gpg_control->datalen; const byte *data = n->pkt->pkt.gpg_control->data; /* check that we have at least the sigclass and one hash */ if ( datalen < 2 ) - log_fatal("invalid control packet CTRLPKT_CLEARSIGN_START\n"); + log_fatal("invalid control packet CTRLPKT_CLEARSIGN_START\n"); /* Note that we don't set the clearsig flag for not-dash-escaped * documents */ clearsig = (*data == 0x01); for( data++, datalen--; datalen; datalen--, data++ ) - gcry_md_enable ( c->mfx.md, *data ); + md_enable( c->mfx.md, *data ); any = 1; - break; /* no pass signature pakets are expected */ - } - } + break; /* Stop here as one-pass signature packets are not + expected. */ + } + else if(n->pkt->pkttype==PKT_SIGNATURE) + { + /* For the SIG+LITERAL case that PGP used to use. */ + md_enable( c->mfx.md, n->pkt->pkt.signature->digest_algo ); + any=1; + } + } - if( !any && !opt.skip_verify ) { - /* no onepass sig packet: enable all standard algos */ - gcry_md_enable ( c->mfx.md, DIGEST_ALGO_RMD160 ); - gcry_md_enable ( c->mfx.md, DIGEST_ALGO_SHA1 ); - gcry_md_enable ( c->mfx.md, DIGEST_ALGO_MD5 ); - } + if( !any && !opt.skip_verify ) + { + /* This is for the old GPG LITERAL+SIG case. It's not legal + according to 2440, so hopefully it won't come up that + often. There is no good way to specify what algorithms to + use in that case, so these three are the historical + answer. */ + md_enable( c->mfx.md, DIGEST_ALGO_RMD160 ); + md_enable( c->mfx.md, DIGEST_ALGO_SHA1 ); + md_enable( c->mfx.md, DIGEST_ALGO_MD5 ); + } if( opt.pgp2_workarounds && only_md5 && !opt.skip_verify ) { /* This is a kludge to work around a bug in pgp2. It does only * catch those mails which are armored. To catch the non-armored * pgp mails we could see whether there is the signature packet * in front of the plaintext. If someone needs this, send me a patch. */ - gcry_md_open (&c->mfx.md2, DIGEST_ALGO_MD5, 0); + if ( gcry_md_open (&c->mfx.md2, DIGEST_ALGO_MD5, 0) ) + BUG (); } if ( DBG_HASHING ) { gcry_md_start_debug ( c->mfx.md, "verify" ); if ( c->mfx.md2 ) gcry_md_start_debug ( c->mfx.md2, "verify2" ); } - if ( c->pipemode.op == 'B' ) - rc = handle_plaintext( pt, &c->mfx, 1, 0, NULL ); - else { - int failed; - rc = handle_plaintext( pt, &c->mfx, c->sigs_only, clearsig, &failed); - if( rc && failed && !c->sigs_only) { - /* can't write output but we hash it anyway to - * check the signature */ - rc = handle_plaintext( pt, &c->mfx, 1, clearsig, NULL ); - } - } + rc = handle_plaintext( pt, &c->mfx, c->sigs_only, clearsig ); + if( gpg_err_code (rc) == G10ERR_CREATE_FILE && !c->sigs_only) + { +#warning We need to change the test for the error code + /* Can't write output but we hash it anyway to + * Check the signature. */ + rc = handle_plaintext( pt, &c->mfx, 1, clearsig ); + } + if( rc ) - log_error( "handle plaintext failed: %s\n", gpg_strerror (rc)); + log_error( "handle plaintext failed: %s\n", g10_errstr(rc)); free_packet(pkt); c->last_was_session_key = 0; @@ -632,14 +704,14 @@ proc_plaintext( CTX c, PACKET *pkt ) static int -proc_compressed_cb( iobuf_t a, void *info ) +proc_compressed_cb( IOBUF a, void *info ) { return proc_signature_packets( info, a, ((CTX)info)->signed_data, ((CTX)info)->sigfilename ); } static int -proc_encrypt_cb( iobuf_t a, void *info ) +proc_encrypt_cb( IOBUF a, void *info ) { return proc_encryption_packets( info, a ); } @@ -651,14 +723,16 @@ proc_compressed( CTX c, PACKET *pkt ) int rc; /*printf("zip: compressed data packet\n");*/ - if( c->sigs_only ) + if( !zd->algorithm ) + rc=G10ERR_COMPR_ALGO; + else if( c->sigs_only ) rc = handle_compressed( c, zd, proc_compressed_cb, c ); else if( c->encrypt_only ) rc = handle_compressed( c, zd, proc_encrypt_cb, c ); else rc = handle_compressed( c, zd, NULL, NULL ); if( rc ) - log_error("uncompressing failed: %s\n", gpg_strerror (rc)); + log_error("uncompressing failed: %s\n", g10_errstr(rc)); free_packet(pkt); c->last_was_session_key = 0; } @@ -669,10 +743,10 @@ proc_compressed( CTX c, PACKET *pkt ) */ static int do_check_sig( CTX c, KBNODE node, int *is_selfsig, - int *is_expkey, int *is_revkey ) + int *is_expkey, int *is_revkey ) { PKT_signature *sig; - MD_HANDLE md = NULL, md2 = NULL; + gcry_md_hd_t md = NULL, md2 = NULL; int algo, rc; assert( node->pkt->pkttype == PKT_SIGNATURE ); @@ -681,29 +755,39 @@ do_check_sig( CTX c, KBNODE node, int *is_selfsig, sig = node->pkt->pkt.signature; algo = sig->digest_algo; - if( (rc = gcry_md_test_algo(algo)) ) - return rc; + rc = openpgp_md_test_algo(algo); + if (rc) + return rc; if( sig->sig_class == 0x00 ) { if( c->mfx.md ) - gcry_md_copy (&md,c->mfx.md); + { + if (gcry_md_copy (&md, c->mfx.md )) + BUG (); + } else /* detached signature */ - gcry_md_open (&md, 0, 0 ); /* signature_check() will - enable the md*/ + { + /* signature_check() will enable the md*/ + if (gcry_md_open (&md, 0, 0 )) + BUG (); + } } else if( sig->sig_class == 0x01 ) { /* how do we know that we have to hash the (already hashed) text * in canonical mode ??? (calculating both modes???) */ if( c->mfx.md ) { - gcry_md_copy (&md, c->mfx.md); - if (c->mfx.md2) - gcry_md_copy (&md2, c->mfx.md2); + if (gcry_md_copy (&md, c->mfx.md )) + BUG (); + if( c->mfx.md2 && gcry_md_copy (&md2, c->mfx.md2 )) + BUG (); } else { /* detached signature */ - log_debug("Do we really need this here?"); - gcry_md_open (&md, 0, 0 ); /* signature_check() will - enable the md*/ - gcry_md_open (&md2, 0, 0 ); + log_debug("Do we really need this here?"); + /* signature_check() will enable the md*/ + if (gcry_md_open (&md, 0, 0 )) + BUG (); + if (gcry_md_open (&md2, 0, 0 )) + BUG (); } } else if( (sig->sig_class&~3) == 0x10 @@ -717,23 +801,23 @@ do_check_sig( CTX c, KBNODE node, int *is_selfsig, return check_key_signature( c->list, node, is_selfsig ); } else if( sig->sig_class == 0x20 ) { - log_info(_("standalone revocation - " - "use \"gpg --import\" to apply\n")); - return GPG_ERR_NOT_PROCESSED; + log_error (_("standalone revocation - " + "use \"gpg --import\" to apply\n")); + return G10ERR_NOT_PROCESSED; } else { log_error("invalid root packet for sigclass %02x\n", sig->sig_class); - return GPG_ERR_SIG_CLASS; + return G10ERR_SIG_CLASS; } } else - return GPG_ERR_SIG_CLASS; + return G10ERR_SIG_CLASS; rc = signature_check2( sig, md, NULL, is_expkey, is_revkey, NULL ); if( gpg_err_code (rc) == GPG_ERR_BAD_SIGNATURE && md2 ) rc = signature_check2( sig, md2, NULL, is_expkey, is_revkey, NULL ); - gcry_md_close (md); - gcry_md_close (md2); + gcry_md_close(md); + gcry_md_close(md2); return rc; } @@ -780,44 +864,38 @@ list_node( CTX c, KBNODE node ) || node->pkt->pkttype == PKT_PUBLIC_SUBKEY ) { PKT_public_key *pk = node->pkt->pkt.public_key; - if( opt.with_colons ) { + if( opt.with_colons ) + { u32 keyid[2]; keyid_from_pk( pk, keyid ); - if( mainkey ) { - c->local_id = pk->local_id; - c->trustletter = opt.fast_list_mode? - 0 : get_validity_info( pk, NULL ); - } + if( mainkey ) + c->trustletter = opt.fast_list_mode? + 0 : get_validity_info( pk, NULL ); printf("%s:", mainkey? "pub":"sub" ); if( c->trustletter ) - putchar( c->trustletter ); - printf(":%u:%d:%08lX%08lX:%s:%s:", - nbits_from_pk( pk ), - pk->pubkey_algo, - (ulong)keyid[0],(ulong)keyid[1], - colon_datestr_from_pk( pk ), - colon_strtime (pk->expiredate) ); - if( c->local_id ) - printf("%lu", c->local_id ); - putchar(':'); + putchar( c->trustletter ); + printf(":%u:%d:%08lX%08lX:%s:%s::", + nbits_from_pk( pk ), + pk->pubkey_algo, + (ulong)keyid[0],(ulong)keyid[1], + colon_datestr_from_pk( pk ), + colon_strtime (pk->expiredate) ); if( mainkey && !opt.fast_list_mode ) - putchar( get_ownertrust_info (pk) ); + putchar( get_ownertrust_info (pk) ); putchar(':'); if( node->next && node->next->pkt->pkttype == PKT_RING_TRUST) { - putchar('\n'); any=1; - if( opt.fingerprint ) - print_fingerprint( pk, NULL, 0 ); - printf("rtv:1:%u:\n", - node->next->pkt->pkt.ring_trust->trustval ); + putchar('\n'); any=1; + if( opt.fingerprint ) + print_fingerprint( pk, NULL, 0 ); + printf("rtv:1:%u:\n", + node->next->pkt->pkt.ring_trust->trustval ); } - } + } else - printf("%s %4u%c/%08lX %s ", - mainkey? "pub":"sub", - nbits_from_pk( pk ), - pubkey_letter( pk->pubkey_algo ), - (ulong)keyid_from_pk( pk, NULL ), - datestr_from_pk( pk ) ); + printf("%s %4u%c/%s %s%s", + mainkey? "pub":"sub", nbits_from_pk( pk ), + pubkey_letter( pk->pubkey_algo ), keystr_from_pk( pk ), + datestr_from_pk( pk ), mainkey?" ":""); if( mainkey ) { /* and now list all userids with their signatures */ @@ -846,10 +924,12 @@ list_node( CTX c, KBNODE node ) putchar('\n'); if( opt.fingerprint && !any ) print_fingerprint( pk, NULL, 0 ); - if( node->next + if( opt.with_colons + && node->next && node->next->pkt->pkttype == PKT_RING_TRUST ) { printf("rtv:2:%u:\n", - node->next->pkt->pkt.ring_trust->trustval ); + node->next->pkt->pkt.ring_trust? + node->next->pkt->pkt.ring_trust->trustval : 0); } any=1; } @@ -862,9 +942,22 @@ list_node( CTX c, KBNODE node ) } } } - else if( pk->expiredate ) { /* of subkey */ - printf(_(" [expires: %s]"), expirestr_from_pk( pk ) ); - } + else + { + /* of subkey */ + if( pk->is_revoked ) + { + printf(" ["); + printf(_("revoked: %s"),revokestr_from_pk(pk)); + printf("]"); + } + else if( pk->expiredate ) + { + printf(" ["); + printf(_("expires: %s"),expirestr_from_pk(pk)); + printf("]"); + } + } if( !any ) putchar('\n'); @@ -875,25 +968,23 @@ list_node( CTX c, KBNODE node ) || node->pkt->pkttype == PKT_SECRET_SUBKEY ) { PKT_secret_key *sk = node->pkt->pkt.secret_key; - if( opt.with_colons ) { + if( opt.with_colons ) + { u32 keyid[2]; keyid_from_sk( sk, keyid ); printf("%s::%u:%d:%08lX%08lX:%s:%s:::", - mainkey? "sec":"ssb", - nbits_from_sk( sk ), - sk->pubkey_algo, - (ulong)keyid[0],(ulong)keyid[1], - colon_datestr_from_sk( sk ), - colon_strtime (sk->expiredate) - /* fixme: add LID */ ); - } + mainkey? "sec":"ssb", + nbits_from_sk( sk ), + sk->pubkey_algo, + (ulong)keyid[0],(ulong)keyid[1], + colon_datestr_from_sk( sk ), + colon_strtime (sk->expiredate) + /* fixme: add LID */ ); + } else - printf("%s %4u%c/%08lX %s ", - mainkey? "sec":"ssb", - nbits_from_sk( sk ), - pubkey_letter( sk->pubkey_algo ), - (ulong)keyid_from_sk( sk, NULL ), - datestr_from_sk( sk ) ); + printf("%s %4u%c/%s %s ", mainkey? "sec":"ssb", + nbits_from_sk( sk ), pubkey_letter( sk->pubkey_algo ), + keystr_from_sk( sk ), datestr_from_sk( sk )); if( mainkey ) { /* and now list all userids with their signatures */ for( node = node->next; node; node = node->next ) { @@ -945,7 +1036,7 @@ list_node( CTX c, KBNODE node ) char *p; int sigrc = ' '; - if( !opt.list_sigs ) + if( !opt.verbose ) return; if( sig->sig_class == 0x20 || sig->sig_class == 0x30 ) @@ -954,14 +1045,13 @@ list_node( CTX c, KBNODE node ) fputs("sig", stdout); if( opt.check_sigs ) { fflush(stdout); - switch( gpg_err_code (rc2=do_check_sig( c, node, - &is_selfsig, - NULL, NULL )) ) { - case 0: sigrc = '!'; break; - case GPG_ERR_BAD_SIGNATURE: sigrc = '-'; break; + rc2=do_check_sig( c, node, &is_selfsig, NULL, NULL ); + switch (gpg_err_code (rc2)) { + case 0: sigrc = '!'; break; + case GPG_ERR_BAD_SIGNATURE: sigrc = '-'; break; case GPG_ERR_NO_PUBKEY: - case GPG_ERR_UNUSABLE_PUBKEY: sigrc = '?'; break; - default: sigrc = '%'; break; + case GPG_ERR_UNUSABLE_PUBKEY: sigrc = '?'; break; + default: sigrc = '%'; break; } } else { /* check whether this is a self signature */ @@ -997,10 +1087,10 @@ list_node( CTX c, KBNODE node ) printf(":"); } else - printf("%c %08lX %s ", - sigrc, (ulong)sig->keyid[1], datestr_from_sig(sig)); + printf("%c %s %s ", + sigrc, keystr(sig->keyid), datestr_from_sig(sig)); if( sigrc == '%' ) - printf("[%s] ", gpg_strerror (rc2) ); + printf("[%s] ", g10_errstr(rc2) ); else if( sigrc == '?' ) ; else if( is_selfsig ) { @@ -1013,7 +1103,7 @@ list_node( CTX c, KBNODE node ) else if( !opt.fast_list_mode ) { p = get_user_id( sig->keyid, &n ); print_string( stdout, p, n, opt.with_colons ); - xfree (p); + xfree(p); } if( opt.with_colons ) printf(":%02x%c:", sig->sig_class, sig->flags.exportable?'x':'l'); @@ -1026,24 +1116,24 @@ list_node( CTX c, KBNODE node ) int -proc_packets( void *anchor, iobuf_t a ) +proc_packets( void *anchor, IOBUF a ) { int rc; - CTX c = xcalloc (1, sizeof *c ); + CTX c = xmalloc_clear( sizeof *c ); c->anchor = anchor; rc = do_proc_packets( c, a ); - xfree ( c ); + xfree( c ); return rc; } int -proc_signature_packets( void *anchor, iobuf_t a, +proc_signature_packets( void *anchor, IOBUF a, STRLIST signedfiles, const char *sigfilename ) { - CTX c = xcalloc (1, sizeof *c ); + CTX c = xmalloc_clear( sizeof *c ); int rc; c->anchor = anchor; @@ -1051,28 +1141,47 @@ proc_signature_packets( void *anchor, iobuf_t a, c->signed_data = signedfiles; c->sigfilename = sigfilename; rc = do_proc_packets( c, a ); - xfree ( c ); + + /* If we have not encountered any signature we print an error + messages, send a NODATA status back and return an error code. + Using log_error is required because verify_files does not check + error codes for each file but we want to terminate the process + with an error. */ + if (!rc && !c->any_sig_seen) + { + write_status_text (STATUS_NODATA, "4"); + log_error (_("no signature found\n")); + rc = G10ERR_NO_DATA; + } + + /* Propagate the signature seen flag upward. Do this only on + success so that we won't issue the nodata status several + times. */ + if (!rc && c->anchor && c->any_sig_seen) + c->anchor->any_sig_seen = 1; + + xfree( c ); return rc; } int -proc_encryption_packets( void *anchor, iobuf_t a ) +proc_encryption_packets( void *anchor, IOBUF a ) { - CTX c = xcalloc (1, sizeof *c ); + CTX c = xmalloc_clear( sizeof *c ); int rc; c->anchor = anchor; c->encrypt_only = 1; rc = do_proc_packets( c, a ); - xfree ( c ); + xfree( c ); return rc; } int -do_proc_packets( CTX c, iobuf_t a ) +do_proc_packets( CTX c, IOBUF a ) { - PACKET *pkt = xmalloc ( sizeof *pkt ); + PACKET *pkt = xmalloc( sizeof *pkt ); int rc=0; int any_data=0; int newpkt; @@ -1084,8 +1193,9 @@ do_proc_packets( CTX c, iobuf_t a ) if( rc ) { free_packet(pkt); /* stop processing when an invalid packet has been encountered - * but don't do so when we are doing a --list-packet. */ - if( gpg_err_code (rc) == GPG_ERR_INV_PACKET && opt.list_packets != 2 ) + * but don't do so when we are doing a --list-packets. */ + if (gpg_err_code (rc) == GPG_ERR_INVALID_PACKET + && opt.list_packets != 2 ) break; continue; } @@ -1110,7 +1220,7 @@ do_proc_packets( CTX c, iobuf_t a ) case PKT_ENCRYPTED: case PKT_ENCRYPTED_MDC: write_status_text( STATUS_UNEXPECTED, "0" ); - rc = GPG_ERR_UNEXPECTED; + rc = G10ERR_UNEXPECTED; goto leave; case PKT_SIGNATURE: newpkt = add_signature( c, pkt ); break; case PKT_PLAINTEXT: proc_plaintext( c, pkt ); break; @@ -1126,7 +1236,7 @@ do_proc_packets( CTX c, iobuf_t a ) case PKT_SECRET_KEY: case PKT_USER_ID: write_status_text( STATUS_UNEXPECTED, "0" ); - rc = GPG_ERR_UNEXPECTED; + rc = G10ERR_UNEXPECTED; goto leave; case PKT_SIGNATURE: newpkt = add_signature( c, pkt ); break; case PKT_SYMKEY_ENC: proc_symkey_enc( c, pkt ); break; @@ -1180,19 +1290,13 @@ do_proc_packets( CTX c, iobuf_t a ) if( newpkt == -1 ) ; else if( newpkt ) { - pkt = xmalloc ( sizeof *pkt ); + pkt = xmalloc( sizeof *pkt ); init_packet(pkt); } else free_packet(pkt); - if ( c->pipemode.stop_now ) { - /* we won't get an EOF in pipemode, so we have to - * break the loop here */ - rc = -1; - break; - } } - if( gpg_err_code (rc) == GPG_ERR_INV_PACKET ) + if( rc == G10ERR_INVALID_PACKET ) write_status_text( STATUS_NODATA, "3" ); if( any_data ) rc = 0; @@ -1202,105 +1306,213 @@ do_proc_packets( CTX c, iobuf_t a ) leave: release_list( c ); - xfree (c->dek); + xfree(c->dek); free_packet( pkt ); - xfree ( pkt ); + xfree( pkt ); free_md_filter_context( &c->mfx ); return rc; } -static int -check_sig_and_print( CTX c, KBNODE node ) +/* Helper for pka_uri_from_sig to parse the to-be-verified address out + of the notation data. */ +static pka_info_t * +get_pka_address (PKT_signature *sig) { - PKT_signature *sig = node->pkt->pkt.signature; - const char *astr, *tstr; - int rc, is_expkey=0, is_revkey=0; + pka_info_t *pka = NULL; + struct notation *nd,*notation; - if( opt.skip_verify ) { - log_info(_("signature verification suppressed\n")); - return 0; - } + notation=sig_to_notation(sig); - /* It is not in all cases possible to check multiple signatures: - * PGP 2 (which is also allowed by OpenPGP), does use the packet - * sequence: sig+data, OpenPGP does use onepas+data=sig and GnuPG - * sometimes uses (because I did'nt read the specs right) data+sig. - * Because it is possible to create multiple signatures with - * different packet sequence (e.g. data+sig and sig+data) it might - * not be possible to get it right: let's say we have: - * data+sig, sig+data,sig+data and we have not yet encountered the last - * data, we could also see this a one data with 2 signatures and then - * data+sig. - * To protect against this we check that all signatures follow - * without any intermediate packets. Note, that we won't get this - * error when we use onepass packets or cleartext signatures because - * we reset the list every time - * - * FIXME: Now that we have these marker packets, we should create a - * real grammar and check against this. - */ + for(nd=notation;nd;nd=nd->next) { - KBNODE n; - int n_sig=0; + if(strcmp(nd->name,"pka-address@gnupg.org")!=0) + continue; /* Not the notation we want. */ + + /* For now we only use the first valid PKA notation. In future + we might want to keep additional PKA notations in a linked + list. */ + if (is_valid_mailbox (nd->value)) + { + pka = xmalloc (sizeof *pka + strlen(nd->value)); + pka->valid = 0; + pka->checked = 0; + pka->uri = NULL; + strcpy (pka->email, nd->value); + break; + } + } - for (n=c->list; n; n=n->next ) { - if ( n->pkt->pkttype == PKT_SIGNATURE ) - n_sig++; - } - if (n_sig > 1) { /* more than one signature - check sequence */ - int tmp, onepass; - - for (tmp=onepass=0,n=c->list; n; n=n->next ) { - if (n->pkt->pkttype == PKT_ONEPASS_SIG) - onepass++; - else if (n->pkt->pkttype == PKT_GPG_CONTROL - && n->pkt->pkt.gpg_control->control - == CTRLPKT_CLEARSIGN_START ) { - onepass++; /* handle the same way as a onepass */ - } - else if ( (tmp && n->pkt->pkttype != PKT_SIGNATURE) ) { - log_error(_("can't handle these multiple signatures\n")); - return 0; - } - else if ( n->pkt->pkttype == PKT_SIGNATURE ) - tmp = 1; - else if (!tmp && !onepass - && n->pkt->pkttype == PKT_GPG_CONTROL - && n->pkt->pkt.gpg_control->control - == CTRLPKT_PLAINTEXT_MARK ) { - /* plaintext before signatures but no one-pass packets*/ - log_error(_("can't handle these multiple signatures\n")); - return 0; - } + free_notation(notation); + + return pka; +} + + +/* Return the URI from a DNS PKA record. If this record has already + be retrieved for the signature we merely return it; if not we go + out and try to get that DNS record. */ +static const char * +pka_uri_from_sig (PKT_signature *sig) +{ + if (!sig->flags.pka_tried) + { + assert (!sig->pka_info); + sig->flags.pka_tried = 1; + sig->pka_info = get_pka_address (sig); + if (sig->pka_info) + { + char *uri; + + uri = get_pka_info (sig->pka_info->email, sig->pka_info->fpr); + if (uri) + { + sig->pka_info->valid = 1; + if (!*uri) + xfree (uri); + else + sig->pka_info->uri = uri; } } } + return sig->pka_info? sig->pka_info->uri : NULL; +} + + +static int +check_sig_and_print( CTX c, KBNODE node ) +{ + PKT_signature *sig = node->pkt->pkt.signature; + const char *astr; + int rc, is_expkey=0, is_revkey=0; + + if (opt.skip_verify) + { + log_info(_("signature verification suppressed\n")); + return 0; + } + + /* Check that the message composition is valid. + + Per RFC-2440bis (-15) allowed: + + S{1,n} -- detached signature. + S{1,n} P -- old style PGP2 signature + O{1,n} P S{1,n} -- standard OpenPGP signature. + C P S{1,n} -- cleartext signature. + + + O = One-Pass Signature packet. + S = Signature packet. + P = OpenPGP Message packet (Encrypted | Compressed | Literal) + (Note that the current rfc2440bis draft also allows + for a signed message but that does not work as it + introduces ambiguities.) + We keep track of these packages using the marker packet + CTRLPKT_PLAINTEXT_MARK. + C = Marker packet for cleartext signatures. + + We reject all other messages. + + Actually we are calling this too often, i.e. for verification of + each message but better have some duplicate work than to silently + introduce a bug here. + */ + { + KBNODE n; + int n_onepass, n_sig; + +/* log_debug ("checking signature packet composition\n"); */ +/* dump_kbnode (c->list); */ + + n = c->list; + assert (n); + if ( n->pkt->pkttype == PKT_SIGNATURE ) + { + /* This is either "S{1,n}" case (detached signature) or + "S{1,n} P" (old style PGP2 signature). */ + for (n = n->next; n; n = n->next) + if (n->pkt->pkttype != PKT_SIGNATURE) + break; + if (!n) + ; /* Okay, this is a detached signature. */ + else if (n->pkt->pkttype == PKT_GPG_CONTROL + && (n->pkt->pkt.gpg_control->control + == CTRLPKT_PLAINTEXT_MARK) ) + { + if (n->next) + goto ambiguous; /* We only allow one P packet. */ + } + else + goto ambiguous; + } + else if (n->pkt->pkttype == PKT_ONEPASS_SIG) + { + /* This is the "O{1,n} P S{1,n}" case (standard signature). */ + for (n_onepass=1, n = n->next; + n && n->pkt->pkttype == PKT_ONEPASS_SIG; n = n->next) + n_onepass++; + if (!n || !(n->pkt->pkttype == PKT_GPG_CONTROL + && (n->pkt->pkt.gpg_control->control + == CTRLPKT_PLAINTEXT_MARK))) + goto ambiguous; + for (n_sig=0, n = n->next; + n && n->pkt->pkttype == PKT_SIGNATURE; n = n->next) + n_sig++; + if (!n_sig) + goto ambiguous; + if (n && !opt.allow_multisig_verification) + goto ambiguous; + if (n_onepass != n_sig) + { + log_info ("number of one-pass packets does not match " + "number of signature packets\n"); + goto ambiguous; + } + } + else if (n->pkt->pkttype == PKT_GPG_CONTROL + && n->pkt->pkt.gpg_control->control == CTRLPKT_CLEARSIGN_START ) + { + /* This is the "C P S{1,n}" case (clear text signature). */ + n = n->next; + if (!n || !(n->pkt->pkttype == PKT_GPG_CONTROL + && (n->pkt->pkt.gpg_control->control + == CTRLPKT_PLAINTEXT_MARK))) + goto ambiguous; + for (n_sig=0, n = n->next; + n && n->pkt->pkttype == PKT_SIGNATURE; n = n->next) + n_sig++; + if (n || !n_sig) + goto ambiguous; + } + else + { + ambiguous: + log_error(_("can't handle this ambiguous signature data\n")); + return 0; + } + + } + + /* (Indendation below not yet changed to GNU style.) */ - tstr = asctimestamp(sig->timestamp); - astr = gcry_pk_algo_name (sig->pubkey_algo); - if(opt.verify_options&VERIFY_SHOW_LONG_KEYID) + astr = gcry_pk_algo_name ( sig->pubkey_algo ); + if(keystrlen()>8) { - log_info(_("Signature made %.*s\n"),(int)strlen(tstr), tstr); - log_info(_(" using %s key %08lX%08lX\n"), - astr? astr: "?",(ulong)sig->keyid[0],(ulong)sig->keyid[1] ); + log_info(_("Signature made %s\n"),asctimestamp(sig->timestamp)); + log_info(_(" using %s key %s\n"), + astr? astr: "?",keystr(sig->keyid)); } else - log_info(_("Signature made %.*s using %s key ID %08lX\n"), - (int)strlen(tstr), tstr, astr? astr: "?", - (ulong)sig->keyid[1] ); + log_info(_("Signature made %s using %s key ID %s\n"), + asctimestamp(sig->timestamp), astr? astr: "?", + keystr(sig->keyid)); rc = do_check_sig(c, node, NULL, &is_expkey, &is_revkey ); - if( gpg_err_code (rc) == GPG_ERR_NO_PUBKEY - && opt.keyserver_scheme && opt.keyserver_options.auto_key_retrieve) { - if( keyserver_import_keyid ( sig->keyid )==0 ) - rc = do_check_sig(c, node, NULL, &is_expkey, &is_revkey ); - } + /* If the key isn't found, check for a preferred keyserver */ - /* If the key still isn't found, try to inform the user where it - can be found. */ - if(gpg_err_code (rc)==GPG_ERR_NO_PUBKEY && sig->flags.pref_ks) + if(rc==G10ERR_NO_PUBKEY && sig->flags.pref_ks) { const byte *p; int seq=0; @@ -1313,17 +1525,81 @@ check_sig_and_print( CTX c, KBNODE node ) page, but "from" if it is located on a keyserver. I'm not going to even try to make two strings here :) */ log_info(_("Key available at: ") ); - print_string( log_get_stream(), p, n, 0 ); + print_utf8_string( log_get_stream(), p, n ); putc( '\n', log_get_stream() ); + + if(opt.keyserver_options.options&KEYSERVER_AUTO_KEY_RETRIEVE + && opt.keyserver_options.options&KEYSERVER_HONOR_KEYSERVER_URL) + { + struct keyserver_spec *spec; + + spec=parse_preferred_keyserver(sig); + if(spec) + { + int res; + + glo_ctrl.in_auto_key_retrieve++; + res=keyserver_import_keyid(sig->keyid,spec); + glo_ctrl.in_auto_key_retrieve--; + if(!res) + rc=do_check_sig(c, node, NULL, &is_expkey, &is_revkey ); + free_keyserver_spec(spec); + + if(!rc) + break; + } + } } } + /* If the preferred keyserver thing above didn't work, our second + try is to use the URI from a DNS PKA record. */ + if ( rc == G10ERR_NO_PUBKEY + && opt.keyserver_options.options&KEYSERVER_AUTO_KEY_RETRIEVE + && opt.keyserver_options.options&KEYSERVER_HONOR_PKA_RECORD) + { + const char *uri = pka_uri_from_sig (sig); + + if (uri) + { + /* FIXME: We might want to locate the key using the + fingerprint instead of the keyid. */ + int res; + struct keyserver_spec *spec; + + spec = parse_keyserver_uri (uri, 1, NULL, 0); + if (spec) + { + glo_ctrl.in_auto_key_retrieve++; + res = keyserver_import_keyid (sig->keyid, spec); + glo_ctrl.in_auto_key_retrieve--; + free_keyserver_spec (spec); + if (!res) + rc = do_check_sig(c, node, NULL, &is_expkey, &is_revkey ); + } + } + } + + /* If the preferred keyserver thing above didn't work and we got + no information from the DNS PKA, this is a third try. */ + + if( rc == G10ERR_NO_PUBKEY && opt.keyserver + && opt.keyserver_options.options&KEYSERVER_AUTO_KEY_RETRIEVE) + { + int res; + + glo_ctrl.in_auto_key_retrieve++; + res=keyserver_import_keyid ( sig->keyid, opt.keyserver ); + glo_ctrl.in_auto_key_retrieve--; + if(!res) + rc = do_check_sig(c, node, NULL, &is_expkey, &is_revkey ); + } if( !rc || gpg_err_code (rc) == GPG_ERR_BAD_SIGNATURE ) { KBNODE un, keyblock; int count=0, statno; char keyid_str[50]; - PKT_public_key *pk=NULL; + PKT_public_key *pk=NULL; if(rc) statno=STATUS_BADSIG; @@ -1343,8 +1619,8 @@ check_sig_and_print( CTX c, KBNODE node ) /* find and print the primary user ID */ for( un=keyblock; un; un = un->next ) { + char *p; int valid; - if(un->pkt->pkttype==PKT_PUBLIC_KEY) { pk=un->pkt->pkt.public_key; @@ -1360,10 +1636,10 @@ check_sig_and_print( CTX c, KBNODE node ) continue; if ( !un->pkt->pkt.user_id->is_primary ) continue; - /* We want the textual user ID here */ + /* We want the textual primary user ID here */ if ( un->pkt->pkt.user_id->attrib_data ) continue; - + assert(pk); /* Get it before we print anything to avoid interrupting @@ -1377,20 +1653,28 @@ check_sig_and_print( CTX c, KBNODE node ) un->pkt->pkt.user_id->len, -1 ); - log_info(rc? _("BAD signature from \"") - : sig->flags.expired ? _("Expired signature from \"") - : _("Good signature from \"")); - print_utf8_string( log_get_stream(), un->pkt->pkt.user_id->name, - un->pkt->pkt.user_id->len ); - if(opt.verify_options&VERIFY_SHOW_VALIDITY) - fprintf (log_get_stream(), - "\" [%s]\n",trust_value_to_string(valid)); + p=utf8_to_native(un->pkt->pkt.user_id->name, + un->pkt->pkt.user_id->len,0); + + if(rc) + log_info(_("BAD signature from \"%s\""),p); + else if(sig->flags.expired) + log_info(_("Expired signature from \"%s\""),p); + else + log_info(_("Good signature from \"%s\""),p); + + xfree(p); + + if(opt.verify_options&VERIFY_SHOW_UID_VALIDITY) + fprintf(log_get_stream()," [%s]\n",trust_value_to_string(valid)); else - fputs("\"\n", log_get_stream() ); + fputs("\n", log_get_stream() ); count++; } if( !count ) { /* just in case that we have no valid textual userid */ + char *p; + /* Try for an invalid textual userid */ for( un=keyblock; un; un = un->next ) { if( un->pkt->pkttype == PKT_USER_ID && @@ -1414,29 +1698,37 @@ check_sig_and_print( CTX c, KBNODE node ) un? un->pkt->pkt.user_id->len:3, -1 ); - log_info(rc? _("BAD signature from \"") - : sig->flags.expired ? _("Expired signature from \"") - : _("Good signature from \"")); - if (opt.trust_model!=TM_ALWAYS && un) { - fputs(_("[uncertain]"), log_get_stream() ); + if(un) + p=utf8_to_native(un->pkt->pkt.user_id->name, + un->pkt->pkt.user_id->len,0); + else + p=xstrdup("[?]"); + + if(rc) + log_info(_("BAD signature from \"%s\""),p); + else if(sig->flags.expired) + log_info(_("Expired signature from \"%s\""),p); + else + log_info(_("Good signature from \"%s\""),p); + if (opt.trust_model!=TM_ALWAYS && un) + { putc(' ', log_get_stream() ); - } - print_utf8_string( log_get_stream(), - un? un->pkt->pkt.user_id->name:"[?]", - un? un->pkt->pkt.user_id->len:3 ); - fputs("\"\n", log_get_stream() ); + fputs(_("[uncertain]"), log_get_stream() ); + } + fputs("\n", log_get_stream() ); } /* If we have a good signature and already printed * the primary user ID, print all the other user IDs */ if ( count && !rc ) { + char *p; for( un=keyblock; un; un = un->next ) { if( un->pkt->pkttype != PKT_USER_ID ) continue; - if ( un->pkt->pkt.user_id->is_revoked ) - continue; - if ( un->pkt->pkt.user_id->is_expired ) - continue; + if((un->pkt->pkt.user_id->is_revoked + || un->pkt->pkt.user_id->is_expired) + && !(opt.verify_options&VERIFY_SHOW_UNUSABLE_UIDS)) + continue; /* Only skip textual primaries */ if ( un->pkt->pkt.user_id->is_primary && !un->pkt->pkt.user_id->attrib_data ) @@ -1451,41 +1743,53 @@ check_sig_and_print( CTX c, KBNODE node ) un->pkt->pkt.user_id->numattribs,pk,NULL); } - log_info( _(" aka \"")); - print_utf8_string( log_get_stream(), un->pkt->pkt.user_id->name, - un->pkt->pkt.user_id->len ); - if(opt.verify_options&VERIFY_SHOW_VALIDITY) - fprintf (log_get_stream(), "\" [%s]\n", - trust_value_to_string(get_validity(pk, - un->pkt-> - pkt.user_id))); + p=utf8_to_native(un->pkt->pkt.user_id->name, + un->pkt->pkt.user_id->len,0); + log_info(_(" aka \"%s\""),p); + xfree(p); + + if(opt.verify_options&VERIFY_SHOW_UID_VALIDITY) + { + const char *valid; + if(un->pkt->pkt.user_id->is_revoked) + valid=_("revoked"); + else if(un->pkt->pkt.user_id->is_expired) + valid=_("expired"); + else + valid=trust_value_to_string(get_validity(pk, + un->pkt-> + pkt.user_id)); + fprintf(log_get_stream()," [%s]\n",valid); + } else - fputs("\"\n", log_get_stream() ); + fputs("\n", log_get_stream() ); } } release_kbnode( keyblock ); if( !rc ) { - if(opt.verify_options&VERIFY_SHOW_POLICY) + if(opt.verify_options&VERIFY_SHOW_POLICY_URLS) show_policy_url(sig,0,1); else show_policy_url(sig,0,2); - if(opt.verify_options&VERIFY_SHOW_KEYSERVER) + if(opt.verify_options&VERIFY_SHOW_KEYSERVER_URLS) show_keyserver_url(sig,0,1); else show_keyserver_url(sig,0,2); - if(opt.verify_options&VERIFY_SHOW_NOTATION) - show_notation(sig,0,1); + if(opt.verify_options&VERIFY_SHOW_NOTATIONS) + show_notation(sig,0,1, + ((opt.verify_options&VERIFY_SHOW_STD_NOTATIONS)?1:0)+ + ((opt.verify_options&VERIFY_SHOW_USER_NOTATIONS)?2:0)); else - show_notation(sig,0,2); - } + show_notation(sig,0,2,0); + } if( !rc && is_status_enabled() ) { /* print a status response with the fingerprint */ - PKT_public_key *vpk = xcalloc (1, sizeof *vpk ); + PKT_public_key *vpk = xmalloc_clear( sizeof *vpk ); if( !get_pubkey( vpk, sig->keyid ) ) { byte array[MAX_FINGERPRINT_LEN], *p; @@ -1513,7 +1817,7 @@ check_sig_and_print( CTX c, KBNODE node ) akid[0] = vpk->main_keyid[0]; akid[1] = vpk->main_keyid[1]; free_public_key (vpk); - vpk = xcalloc (1, sizeof *vpk ); + vpk = xmalloc_clear( sizeof *vpk ); if (get_pubkey (vpk, akid)) { /* impossible error, we simply return a zeroed out fpr */ n = MAX_FINGERPRINT_LEN < 20? MAX_FINGERPRINT_LEN : 20; @@ -1530,14 +1834,18 @@ check_sig_and_print( CTX c, KBNODE node ) free_public_key( vpk ); } - if( !rc ) + if (!rc) + { + if(opt.verify_options&VERIFY_PKA_LOOKUPS) + pka_uri_from_sig (sig); /* Make sure PKA info is available. */ rc = check_signatures_trust( sig ); + } if(sig->flags.expired) { log_info(_("Signature expired %s\n"), asctimestamp(sig->expiredate)); - rc=GPG_ERR_GENERAL; /* need a better error here? */ + rc=G10ERR_GENERAL; /* need a better error here? */ } else if(sig->expiredate) log_info(_("Signature expires %s\n"),asctimestamp(sig->expiredate)); @@ -1553,19 +1861,19 @@ check_sig_and_print( CTX c, KBNODE node ) if( opt.batch && rc ) g10_exit(1); } - else { + else { char buf[50]; sprintf(buf, "%08lX%08lX %d %d %02x %lu %d", (ulong)sig->keyid[0], (ulong)sig->keyid[1], sig->pubkey_algo, sig->digest_algo, sig->sig_class, (ulong)sig->timestamp, rc ); write_status_text( STATUS_ERRSIG, buf ); - if( gpg_err_code (rc) == GPG_ERR_NO_PUBKEY ) { + if( rc == G10ERR_NO_PUBKEY ) { buf[16] = 0; write_status_text( STATUS_NO_PUBKEY, buf ); } - if( rc != GPG_ERR_NOT_PROCESSED ) - log_error(_("Can't check signature: %s\n"), gpg_strerror (rc) ); + if( rc != G10ERR_NOT_PROCESSED ) + log_error(_("Can't check signature: %s\n"), g10_errstr(rc) ); } return rc; } @@ -1595,7 +1903,6 @@ proc_tree( CTX c, KBNODE node ) if (!node) return; - c->local_id = 0; c->trustletter = ' '; if( node->pkt->pkttype == PKT_PUBLIC_KEY || node->pkt->pkttype == PKT_PUBLIC_SUBKEY ) { @@ -1611,12 +1918,16 @@ proc_tree( CTX c, KBNODE node ) if( !c->have_data ) { free_md_filter_context( &c->mfx ); /* prepare to create all requested message digests */ - gcry_md_open (&c->mfx.md, 0, 0); - - /* fixme: why looking for the signature packet and not 1passpacket*/ - for( n1 = node; (n1 = find_next_kbnode(n1, PKT_SIGNATURE )); ) { - gcry_md_enable ( c->mfx.md, n1->pkt->pkt.signature->digest_algo); - } + if (gcry_md_open (&c->mfx.md, 0, 0)) + BUG (); + + /* fixme: why looking for the signature packet and not the + one-pass packet? */ + for ( n1 = node; (n1 = find_next_kbnode(n1, PKT_SIGNATURE )); ) + { + gcry_md_enable (c->mfx.md, + n1->pkt->pkt.signature->digest_algo); + } /* ask for file and hash it */ if( c->sigs_only ) { rc = hash_datafiles( c->mfx.md, NULL, @@ -1629,7 +1940,7 @@ proc_tree( CTX c, KBNODE node ) n1? (n1->pkt->pkt.onepass_sig->sig_class == 0x01):0 ); } if( rc ) { - log_error("can't hash datafile: %s\n", gpg_strerror (rc)); + log_error("can't hash datafile: %s\n", g10_errstr(rc)); return; } } @@ -1690,20 +2001,24 @@ proc_tree( CTX c, KBNODE node ) else if( !c->have_data ) { /* detached signature */ free_md_filter_context( &c->mfx ); - gcry_md_open (&c->mfx.md, sig->digest_algo, 0); + if (gcry_md_open (&c->mfx.md, sig->digest_algo, 0)) + BUG (): + if( !opt.pgp2_workarounds ) ; else if( sig->digest_algo == DIGEST_ALGO_MD5 && is_RSA( sig->pubkey_algo ) ) { /* enable a workaround for a pgp2 bug */ - gcry_md_open (&c->mfx.md2, DIGEST_ALGO_MD5, 0 ); + if (gcry_md_open (&c->mfx.md2, DIGEST_ALGO_MD5, 0)) + BUG (): } else if( sig->digest_algo == DIGEST_ALGO_SHA1 && sig->pubkey_algo == PUBKEY_ALGO_DSA && sig->sig_class == 0x01 ) { /* enable the workaround also for pgp5 when the detached * signature has been created in textmode */ - gcry_md_open (&c->mfx.md2, sig->digest_algo, 0 ); + if (gcry_md_open (&c->mfx.md2, sig->digest_algo, 0 )) + BUG (); } #if 0 /* workaround disabled */ /* Here we have another hack to work around a pgp 2 bug @@ -1716,9 +2031,9 @@ proc_tree( CTX c, KBNODE node ) /* c->mfx.md2? 0 :(sig->sig_class == 0x01) */ #endif if ( DBG_HASHING ) { - gcry_md_start_debug ( c->mfx.md, "verify" ); + gcry_md_start_debug( c->mfx.md, "verify" ); if ( c->mfx.md2 ) - gcry_md_start_debug ( c->mfx.md2, "verify2" ); + gcry_md_start_debug( c->mfx.md2, "verify2" ); } if( c->sigs_only ) { rc = hash_datafiles( c->mfx.md, c->mfx.md2, @@ -1731,7 +2046,7 @@ proc_tree( CTX c, KBNODE node ) (sig->sig_class == 0x01) ); } if( rc ) { - log_error("can't hash datafile: %s\n", gpg_strerror (rc)); + log_error("can't hash datafile: %s\n", g10_errstr(rc)); return; } } @@ -1739,8 +2054,6 @@ proc_tree( CTX c, KBNODE node ) log_error (_("not a detached signature\n") ); return; } - else if ( c->pipemode.op == 'B' ) - ; /* this is a detached signature trough the pipemode handler */ else if (!opt.quiet) log_info(_("old style (PGP 2.x) signature\n")); diff --git a/g10/mdfilter.c b/g10/mdfilter.c index b58189146..06aa8386c 100644 --- a/g10/mdfilter.c +++ b/g10/mdfilter.c @@ -15,7 +15,8 @@ * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. */ #include <config.h> @@ -25,9 +26,9 @@ #include <errno.h> #include <assert.h> +#include "gpg.h" #include "errors.h" #include "iobuf.h" -#include "memory.h" #include "util.h" #include "filter.h" @@ -38,7 +39,7 @@ */ int md_filter( void *opaque, int control, - iobuf_t a, byte *buf, size_t *ret_len) + IOBUF a, byte *buf, size_t *ret_len) { size_t size = *ret_len; md_filter_context_t *mfx = opaque; @@ -67,8 +68,8 @@ md_filter( void *opaque, int control, void free_md_filter_context( md_filter_context_t *mfx ) { - gcry_md_close (mfx->md); - gcry_md_close (mfx->md2); + gcry_md_close(mfx->md); + gcry_md_close(mfx->md2); mfx->md = NULL; mfx->md2 = NULL; mfx->maxbuf_size = 0; diff --git a/g10/misc.c b/g10/misc.c index a0599f304..a26aa740d 100644 --- a/g10/misc.c +++ b/g10/misc.c @@ -1,6 +1,6 @@ -/* misc.c - miscellaneous functions - * Copyright (C) 1998, 1999, 2000, 2001, 2002, - * 2003 Free Software Foundation, Inc. +/* misc.c - miscellaneous functions + * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, + * 2005, 2006 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -16,7 +16,8 @@ * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. */ #include <config.h> @@ -25,7 +26,6 @@ #include <string.h> #include <unistd.h> #include <errno.h> -#include <assert.h> #if defined(__linux__) && defined(__alpha__) && __GLIBC__ < 2 #include <asm/sysinfo.h> #include <asm/unistd.h> @@ -35,15 +35,51 @@ #include <sys/time.h> #include <sys/resource.h> #endif +#ifdef ENABLE_SELINUX_HACKS +#include <sys/stat.h> +#endif + +#ifdef HAVE_W32_SYSTEM +#include <time.h> +#include <process.h> +#include <windows.h> +#include <shlobj.h> +#ifndef CSIDL_APPDATA +#define CSIDL_APPDATA 0x001a +#endif +#ifndef CSIDL_LOCAL_APPDATA +#define CSIDL_LOCAL_APPDATA 0x001c +#endif +#ifndef CSIDL_FLAG_CREATE +#define CSIDL_FLAG_CREATE 0x8000 +#endif +#endif /*HAVE_W32_SYSTEM*/ #include "gpg.h" +#ifdef HAVE_W32_SYSTEM +# include "errors.h" +# include "dynload.h" +#endif /*HAVE_W32_SYSTEM*/ #include "util.h" #include "main.h" #include "photoid.h" #include "options.h" #include "i18n.h" -#define MAX_EXTERN_MPI_BITS 16384 + + +#ifdef ENABLE_SELINUX_HACKS +/* A object and a global variable to keep track of files marked as + secured. */ +struct secured_file_item +{ + struct secured_file_item *next; + ino_t ino; + dev_t dev; +}; +static struct secured_file_item *secured_files; +#endif /*ENABLE_SELINUX_HACKS*/ + #if defined(__linux__) && defined(__alpha__) && __GLIBC__ < 2 @@ -92,6 +128,135 @@ disable_core_dumps() } +/* For the sake of SELinux we want to restrict access through gpg to + certain files we keep under our own control. This function + registers such a file and is_secured_file may then be used to + check whether a file has ben registered as secured. */ +void +register_secured_file (const char *fname) +{ +#ifdef ENABLE_SELINUX_HACKS + struct stat buf; + struct secured_file_item *sf; + + /* Note that we stop immediatley if something goes wrong here. */ + if (stat (fname, &buf)) + log_fatal (_("fstat of `%s' failed in %s: %s\n"), fname, + "register_secured_file", strerror (errno)); +/* log_debug ("registering `%s' i=%lu.%lu\n", fname, */ +/* (unsigned long)buf.st_dev, (unsigned long)buf.st_ino); */ + for (sf=secured_files; sf; sf = sf->next) + { + if (sf->ino == buf.st_ino && sf->dev == buf.st_dev) + return; /* Already registered. */ + } + + sf = xmalloc (sizeof *sf); + sf->ino = buf.st_ino; + sf->dev = buf.st_dev; + sf->next = secured_files; + secured_files = sf; +#endif /*ENABLE_SELINUX_HACKS*/ +} + +/* Remove a file registered as secure. */ +void +unregister_secured_file (const char *fname) +{ +#ifdef ENABLE_SELINUX_HACKS + struct stat buf; + struct secured_file_item *sf, *sfprev; + + if (stat (fname, &buf)) + { + log_error (_("fstat of `%s' failed in %s: %s\n"), fname, + "unregister_secured_file", strerror (errno)); + return; + } +/* log_debug ("unregistering `%s' i=%lu.%lu\n", fname, */ +/* (unsigned long)buf.st_dev, (unsigned long)buf.st_ino); */ + for (sfprev=NULL,sf=secured_files; sf; sfprev=sf, sf = sf->next) + { + if (sf->ino == buf.st_ino && sf->dev == buf.st_dev) + { + if (sfprev) + sfprev->next = sf->next; + else + secured_files = sf->next; + xfree (sf); + return; + } + } +#endif /*ENABLE_SELINUX_HACKS*/ +} + +/* Return true if FD is corresponds to a secured file. Using -1 for + FS is allowed and will return false. */ +int +is_secured_file (int fd) +{ +#ifdef ENABLE_SELINUX_HACKS + struct stat buf; + struct secured_file_item *sf; + + if (fd == -1) + return 0; /* No file descriptor so it can't be secured either. */ + + /* Note that we print out a error here and claim that a file is + secure if something went wrong. */ + if (fstat (fd, &buf)) + { + log_error (_("fstat(%d) failed in %s: %s\n"), fd, + "is_secured_file", strerror (errno)); + return 1; + } +/* log_debug ("is_secured_file (%d) i=%lu.%lu\n", fd, */ +/* (unsigned long)buf.st_dev, (unsigned long)buf.st_ino); */ + for (sf=secured_files; sf; sf = sf->next) + { + if (sf->ino == buf.st_ino && sf->dev == buf.st_dev) + return 1; /* Yes. */ + } +#endif /*ENABLE_SELINUX_HACKS*/ + return 0; /* No. */ +} + +/* Return true if FNAME is corresponds to a secured file. Using NULL, + "" or "-" for FS is allowed and will return false. This function is + used before creating a file, thus it won't fail if the file does + not exist. */ +int +is_secured_filename (const char *fname) +{ +#ifdef ENABLE_SELINUX_HACKS + struct stat buf; + struct secured_file_item *sf; + + if (iobuf_is_pipe_filename (fname) || !*fname) + return 0; + + /* Note that we print out a error here and claim that a file is + secure if something went wrong. */ + if (stat (fname, &buf)) + { + if (errno == ENOENT || errno == EPERM || errno == EACCES) + return 0; + log_error (_("fstat of `%s' failed in %s: %s\n"), fname, + "is_secured_filename", strerror (errno)); + return 1; + } +/* log_debug ("is_secured_filename (%s) i=%lu.%lu\n", fname, */ +/* (unsigned long)buf.st_dev, (unsigned long)buf.st_ino); */ + for (sf=secured_files; sf; sf = sf->next) + { + if (sf->ino == buf.st_ino && sf->dev == buf.st_dev) + return 1; /* Yes. */ + } +#endif /*ENABLE_SELINUX_HACKS*/ + return 0; /* No. */ +} + + u16 checksum_u16( unsigned n ) @@ -115,25 +280,26 @@ checksum( byte *p, unsigned n ) } u16 -checksum_mpi( gcry_mpi_t a ) +checksum_mpi (gcry_mpi_t a) { - int rc; u16 csum; byte *buffer; - size_t nbytes; + unsigned int nbytes; + unsigned int nbits; - rc = gcry_mpi_print( GCRYMPI_FMT_PGP, NULL, 0, &nbytes, a ); - if (rc) + if ( gcry_mpi_print (GCRYMPI_FMT_PGP, NULL, 0, &nbytes, a) ) BUG (); - /* fixme: for numbers not in secure memory we should use a stack - * based buffer and only allocate a larger one if mpi_print return - * an error */ - buffer = gcry_is_secure(a)? gcry_xmalloc_secure(nbytes):gcry_xmalloc(nbytes); - rc = gcry_mpi_print (GCRYMPI_FMT_PGP, buffer, nbytes, NULL, a ); - if (rc) + /* Fixme: For numbers not in secure memory we should use a stack + * based buffer and only allocate a larger one if mpi_print returns + * an error. */ + buffer = (gcry_is_secure(a)? + gcry_xmalloc_secure (nbytes) : gcry_xmalloc (nbytes)); + if ( gcry_mpi_print (GCRYMPI_FMT_PGP, buffer, nbytes, NULL, a) ) BUG (); - csum = checksum (buffer, nbytes ); - xfree (buffer ); + nbits = gcry_mpi_get_nbits (a); + csum = checksum_u16 (nbits); + csum += checksum (buffer, nbytes); + xfree (buffer); return csum; } @@ -148,46 +314,32 @@ buffer_to_u32( const byte *buffer ) return a; } - -static void -no_exp_algo(void) -{ - static int did_note = 0; - - if( !did_note ) { - did_note = 1; - log_info(_("Experimental algorithms should not be used!\n")); - } -} - void print_pubkey_algo_note( int algo ) { - if( algo >= 100 && algo <= 110 ) - no_exp_algo(); + if(algo >= 100 && algo <= 110) + { + static int warn=0; + if(!warn) + { + warn=1; + log_info (_("WARNING: using experimental public key algorithm %s\n"), + gcry_pk_algo_name (algo)); + } + } } void print_cipher_algo_note( int algo ) { - if( algo >= 100 && algo <= 110 ) - no_exp_algo(); - else if( algo == CIPHER_ALGO_3DES - || algo == CIPHER_ALGO_CAST5 - || algo == CIPHER_ALGO_BLOWFISH - || algo == CIPHER_ALGO_TWOFISH - || algo == CIPHER_ALGO_RIJNDAEL - || algo == CIPHER_ALGO_RIJNDAEL192 - || algo == CIPHER_ALGO_RIJNDAEL256 - ) - ; - else { - static int did_note = 0; - - if( !did_note ) { - did_note = 1; - log_info(_("this cipher algorithm is deprecated; " - "please use a more standard one!\n")); + if(algo >= 100 && algo <= 110) + { + static int warn=0; + if(!warn) + { + warn=1; + log_info (_("WARNING: using experimental cipher algorithm %s\n"), + gcry_cipher_algo_name (algo)); } } } @@ -195,63 +347,81 @@ print_cipher_algo_note( int algo ) void print_digest_algo_note( int algo ) { - if( algo >= 100 && algo <= 110 ) - no_exp_algo(); + if(algo >= 100 && algo <= 110) + { + static int warn=0; + if(!warn) + { + warn=1; + log_info (_("WARNING: using experimental digest algorithm %s\n"), + gcry_md_algo_name (algo)); + } + } + else if(algo==DIGEST_ALGO_MD5) + log_info (_("WARNING: digest algorithm %s is deprecated\n"), + gcry_md_algo_name (algo)); } - /* Return a string which is used as a kind of process ID */ const byte * get_session_marker( size_t *rlen ) { - static byte marker[SIZEOF_UNSIGNED_LONG*2]; - static int initialized; - - if ( !initialized ) { - volatile ulong aa, bb; /* we really want the uninitialized value */ - ulong a, b; - - initialized = 1; - /* also this marker is guessable it is not easy to use this - * for a faked control packet because an attacker does not - * have enough control about the time the verification does - * take place. Of course, we can add just more random but - * than we need the random generator even for verification - * tasks - which does not make sense. */ - a = aa ^ (ulong)getpid(); - b = bb ^ (ulong)time(NULL); - memcpy( marker, &a, SIZEOF_UNSIGNED_LONG ); - memcpy( marker+SIZEOF_UNSIGNED_LONG, &b, SIZEOF_UNSIGNED_LONG ); + static byte marker[SIZEOF_UNSIGNED_LONG*2]; + static int initialized; + + if ( !initialized ) + { + volatile ulong aa, bb; /* We really want the uninitialized value. */ + ulong a, b; + + initialized = 1; + /* Although this marker is guessable it is not easy to use this + * for a faked control packet because an attacker does not have + * enough control about the time the verification takes place. + * Of course, we could add just more random but than we need the + * random generator even for verification tasks - which does not + * make sense. */ + a = aa ^ (ulong)getpid(); + b = bb ^ (ulong)time(NULL); + memcpy ( marker, &a, SIZEOF_UNSIGNED_LONG ); + memcpy ( marker+SIZEOF_UNSIGNED_LONG, &b, SIZEOF_UNSIGNED_LONG ); } - *rlen = sizeof(marker); - return marker; + *rlen = sizeof(marker); + return marker; } /**************** - * Wrapper around the libgcrypt function with addional checks on - * openPGP contraints for the algo ID. + * Wrapper around the libgcrypt function with additonal checks on + * the OpenPGP contraints for the algo ID. */ int openpgp_cipher_test_algo( int algo ) { - if( algo < 0 || algo > 110 ) - return GPG_ERR_CIPHER_ALGO; - return gcry_cipher_test_algo (algo); + if ( algo < 0 || algo > 110 ) + return gpg_error (GPG_ERR_CIPHER_ALGO); + return gcry_cipher_test_algo (algo); } int -openpgp_pk_test_algo( int algo, unsigned int usage_flags ) +openpgp_pk_test_algo( int algo ) { - size_t value = usage_flags; + if (algo == GCRY_PK_ELG_E) + algo = GCRY_PK_ELG; + if (algo < 0 || algo > 110) + return gpg_error (GPG_ERR_PUBKEY_ALGO); + return gcry_pk_test_algo (algo); +} + +int +openpgp_pk_test_algo2( int algo, unsigned int use ) +{ if (algo == GCRY_PK_ELG_E) algo = GCRY_PK_ELG; -#ifdef __GNUC__ -#warning need to handle the usage here? -#endif + if (algo < 0 || algo > 110) - return GPG_ERR_PUBKEY_ALGO; - return gcry_pk_algo_info (algo, GCRYCTL_TEST_ALGO, NULL, &value); + return gpg_error (GPG_ERR_PUBKEY_ALGO); + return gcry_pk_test_algo2 (algo, use); } int @@ -259,25 +429,23 @@ openpgp_pk_algo_usage ( int algo ) { int use = 0; - /* they are hardwired in gpg 1.0 */ + /* They are hardwired in gpg 1.0. */ switch ( algo ) { case PUBKEY_ALGO_RSA: - use = PUBKEY_USAGE_SIG | PUBKEY_USAGE_ENC | PUBKEY_USAGE_AUTH; + use = (PUBKEY_USAGE_CERT | PUBKEY_USAGE_SIG + | PUBKEY_USAGE_ENC | PUBKEY_USAGE_AUTH); break; case PUBKEY_ALGO_RSA_E: use = PUBKEY_USAGE_ENC; break; case PUBKEY_ALGO_RSA_S: - use = PUBKEY_USAGE_SIG; + use = PUBKEY_USAGE_CERT | PUBKEY_USAGE_SIG; break; case PUBKEY_ALGO_ELGAMAL_E: use = PUBKEY_USAGE_ENC; break; case PUBKEY_ALGO_DSA: - use = PUBKEY_USAGE_SIG | PUBKEY_USAGE_AUTH; - break; - case PUBKEY_ALGO_ELGAMAL: - use = PUBKEY_USAGE_SIG | PUBKEY_USAGE_ENC | PUBKEY_USAGE_AUTH; + use = PUBKEY_USAGE_CERT | PUBKEY_USAGE_SIG | PUBKEY_USAGE_AUTH; break; default: break; @@ -288,54 +456,9 @@ openpgp_pk_algo_usage ( int algo ) int openpgp_md_test_algo( int algo ) { - if( algo < 0 || algo > 110 ) - return GPG_ERR_DIGEST_ALGO; - return gcry_md_test_algo (algo); -} - -int -openpgp_md_map_name (const char *string) -{ - int i = gcry_md_map_name (string); - - if (!i && (string[0]=='H' || string[0]=='h')) - { /* Didn't find it, so try the Hx format */ - long val; - char *endptr; - - string++; - - val=strtol(string,&endptr,10); - if (*string!='\0' && *endptr=='\0' && !openpgp_md_test_algo(val)) - i = val; - } - return i < 0 || i > 110? 0 : i; -} - -int -openpgp_cipher_map_name (const char *string) -{ - int i = gcry_cipher_map_name (string); - - if (!i && (string[0]=='S' || string[0]=='s')) - { /* Didn't find it, so try the Sx format */ - long val; - char *endptr; - - string++; - - val=strtol(string,&endptr,10); - if (*string!='\0' && *endptr=='\0' && !openpgp_cipher_test_algo(val)) - i = val; - } - return i < 0 || i > 110? 0 : i; -} - -int -openpgp_pk_map_name (const char *string) -{ - int i = gcry_pk_map_name (string); - return i < 0 || i > 110? 0 : i; + if (algo < 0 || algo > 110) + return gpg_error (GPG_ERR_DIGEST_ALGO); + return gcry_md_test_algo (algo); } #ifdef USE_IDEA @@ -348,14 +471,30 @@ idea_cipher_warn(int show) if(!warned || show) { log_info(_("the IDEA cipher plugin is not present\n")); - log_info(_("please see http://www.gnupg.org/why-not-idea.html " - "for more information\n")); + log_info(_("please see %s for more information\n"), + "http://www.gnupg.org/faq/why-not-idea.html"); warned=1; } } #endif -/* Expand %-strings. Returns a string which must be m_freed. Returns +static unsigned long get_signature_count(PKT_secret_key *sk) +{ +#ifdef ENABLE_CARD_SUPPORT + if(sk && sk->is_protected && sk->protect.s2k.mode==1002) + { + struct agent_card_info_s info; + if(agent_scd_getattr("SIG-COUNTER",&info)==0) + return info.sig_counter; + } +#endif + + /* How to do this without a card? */ + + return 0; +} + +/* Expand %-strings. Returns a string which must be xfreed. Returns NULL if the string cannot be expanded (too large). */ char * pct_expando(const char *string,struct expando_args *args) @@ -387,7 +526,7 @@ pct_expando(const char *string,struct expando_args *args) goto fail; maxlen+=1024; - ret= xrealloc(ret,maxlen); + ret=xrealloc(ret,maxlen); } done=0; @@ -434,6 +573,15 @@ pct_expando(const char *string,struct expando_args *args) } break; + case 'c': /* signature count from card, if any. */ + if(idx+10<maxlen) + { + sprintf(&ret[idx],"%lu",get_signature_count(args->sk)); + idx+=strlen(&ret[idx]); + done=1; + } + break; + case 'p': /* primary pk fingerprint of a sk */ case 'f': /* pk fingerprint */ case 'g': /* sk fingerprint */ @@ -442,13 +590,14 @@ pct_expando(const char *string,struct expando_args *args) size_t len; int i; - if( ch[1]=='p' && args->sk) + if((*(ch+1))=='p' && args->sk) { if(args->sk->is_primary) fingerprint_from_sk(args->sk,array,&len); else if(args->sk->main_keyid[0] || args->sk->main_keyid[1]) { - PKT_public_key *pk= xcalloc(1, sizeof(PKT_public_key)); + PKT_public_key *pk= + xmalloc_clear(sizeof(PKT_public_key)); if(get_pubkey_fast(pk,args->sk->main_keyid)==0) fingerprint_from_pk(pk,array,&len); @@ -459,12 +608,12 @@ pct_expando(const char *string,struct expando_args *args) else memset(array,0,(len=MAX_FINGERPRINT_LEN)); } - else if( ch[1]=='f' && args->pk) + else if((*(ch+1))=='f' && args->pk) fingerprint_from_pk(args->pk,array,&len); - else if( ch[1]=='g' && args->sk) + else if((*(ch+1))=='g' && args->sk) fingerprint_from_sk(args->sk,array,&len); else - memset(array, 0, (len=MAX_FINGERPRINT_LEN)); + memset(array,0,(len=MAX_FINGERPRINT_LEN)); if(idx+(len*2)<maxlen) { @@ -539,35 +688,10 @@ pct_expando(const char *string,struct expando_args *args) return ret; fail: - xfree (ret); + xfree(ret); return NULL; } -int -hextobyte( const char *s ) -{ - int c; - - if( *s >= '0' && *s <= '9' ) - c = 16 * (*s - '0'); - else if( *s >= 'A' && *s <= 'F' ) - c = 16 * (10 + *s - 'A'); - else if( *s >= 'a' && *s <= 'f' ) - c = 16 * (10 + *s - 'a'); - else - return -1; - s++; - if( *s >= '0' && *s <= '9' ) - c += *s - '0'; - else if( *s >= 'A' && *s <= 'F' ) - c += 10 + *s - 'A'; - else if( *s >= 'a' && *s <= 'f' ) - c += 10 + *s - 'a'; - else - return -1; - return c; -} - void deprecated_warning(const char *configname,unsigned int configlineno, const char *option,const char *repl1,const char *repl2) @@ -589,24 +713,39 @@ deprecated_warning(const char *configname,unsigned int configlineno, log_info(_("please use \"%s%s\" instead\n"),repl1,repl2); } + +void +deprecated_command (const char *name) +{ + log_info(_("WARNING: \"%s\" is a deprecated command - do not use it\n"), + name); +} + + const char * compress_algo_to_string(int algo) { - const char *s="?"; + const char *s=NULL; switch(algo) { - case 0: - s="Uncompressed"; + case COMPRESS_ALGO_NONE: + s=_("Uncompressed"); break; - case 1: + case COMPRESS_ALGO_ZIP: s="ZIP"; break; - case 2: + case COMPRESS_ALGO_ZLIB: s="ZLIB"; break; + +#ifdef HAVE_BZIP2 + case COMPRESS_ALGO_BZIP2: + s="BZIP2"; + break; +#endif } return s; @@ -615,18 +754,31 @@ compress_algo_to_string(int algo) int string_to_compress_algo(const char *string) { - if(ascii_strcasecmp(string,"uncompressed")==0) + /* NOTE TO TRANSLATOR: See doc/TRANSLATE about this string. */ + if(match_multistr(_("uncompressed|none"),string)) + return 0; + else if(ascii_strcasecmp(string,"uncompressed")==0) + return 0; + else if(ascii_strcasecmp(string,"none")==0) return 0; else if(ascii_strcasecmp(string,"zip")==0) return 1; else if(ascii_strcasecmp(string,"zlib")==0) return 2; +#ifdef HAVE_BZIP2 + else if(ascii_strcasecmp(string,"bzip2")==0) + return 3; +#endif else if(ascii_strcasecmp(string,"z0")==0) return 0; else if(ascii_strcasecmp(string,"z1")==0) return 1; else if(ascii_strcasecmp(string,"z2")==0) return 2; +#ifdef HAVE_BZIP2 + else if(ascii_strcasecmp(string,"z3")==0) + return 3; +#endif else return -1; } @@ -634,10 +786,15 @@ string_to_compress_algo(const char *string) int check_compress_algo(int algo) { +#ifdef HAVE_BZIP2 + if(algo>=0 && algo<=3) + return 0; +#else if(algo>=0 && algo<=2) return 0; +#endif - return GPG_ERR_COMPR_ALGO; + return G10ERR_COMPR_ALGO; } int @@ -652,13 +809,13 @@ default_cipher_algo(void) } /* There is no default_digest_algo function, but see - sign.c:hash_for */ + sign.c:hash_for() */ int default_compress_algo(void) { - if(opt.def_compress_algo!=-1) - return opt.def_compress_algo; + if(opt.compress_algo!=-1) + return opt.compress_algo; else if(opt.personal_compress_prefs) return opt.personal_compress_prefs[0].value; else @@ -712,14 +869,151 @@ compliance_failure(void) opt.compliance=CO_GNUPG; } +/* Break a string into successive option pieces. Accepts single word + options and key=value argument options. */ +char * +optsep(char **stringp) +{ + char *tok,*end; + + tok=*stringp; + if(tok) + { + end=strpbrk(tok," ,="); + if(end) + { + int sawequals=0; + char *ptr=end; + + /* what we need to do now is scan along starting with *end, + If the next character we see (ignoring spaces) is an = + sign, then there is an argument. */ + + while(*ptr) + { + if(*ptr=='=') + sawequals=1; + else if(*ptr!=' ') + break; + ptr++; + } + + /* There is an argument, so grab that too. At this point, + ptr points to the first character of the argument. */ + if(sawequals) + { + /* Is it a quoted argument? */ + if(*ptr=='"') + { + ptr++; + end=strchr(ptr,'"'); + if(end) + end++; + } + else + end=strpbrk(ptr," ,"); + } + + if(end && *end) + { + *end='\0'; + *stringp=end+1; + } + else + *stringp=NULL; + } + else + *stringp=NULL; + } + + return tok; +} + +/* Breaks an option value into key and value. Returns NULL if there + is no value. Note that "string" is modified to remove the =value + part. */ +char * +argsplit(char *string) +{ + char *equals,*arg=NULL; + + equals=strchr(string,'='); + if(equals) + { + char *quote,*space; + + *equals='\0'; + arg=equals+1; + + /* Quoted arg? */ + quote=strchr(arg,'"'); + if(quote) + { + arg=quote+1; + + quote=strchr(arg,'"'); + if(quote) + *quote='\0'; + } + else + { + size_t spaces; + + /* Trim leading spaces off of the arg */ + spaces=strspn(arg," "); + arg+=spaces; + } + + /* Trim tailing spaces off of the tag */ + space=strchr(string,' '); + if(space) + *space='\0'; + } + + return arg; +} + +/* Return the length of the initial token, leaving off any + argument. */ +static size_t +optlen(const char *s) +{ + char *end=strpbrk(s," ="); + + if(end) + return end-s; + else + return strlen(s); +} + int -parse_options(char *str,unsigned int *options,struct parse_options *opts) +parse_options(char *str,unsigned int *options, + struct parse_options *opts,int noisy) { char *tok; - while((tok=strsep(&str," ,"))) + if (str && !strcmp (str, "help")) + { + int i,maxlen=0; + + /* Figure out the longest option name so we can line these up + neatly. */ + for(i=0;opts[i].name;i++) + if(opts[i].help && maxlen<strlen(opts[i].name)) + maxlen=strlen(opts[i].name); + + for(i=0;opts[i].name;i++) + if(opts[i].help) + printf("%s%*s%s\n",opts[i].name, + maxlen+2-(int)strlen(opts[i].name),"",_(opts[i].help)); + + g10_exit(0); + } + + while((tok=optsep(&str))) { int i,rev=0; + char *otok=tok; if(tok[0]=='\0') continue; @@ -732,25 +1026,248 @@ parse_options(char *str,unsigned int *options,struct parse_options *opts) for(i=0;opts[i].name;i++) { - if(ascii_strcasecmp(opts[i].name,tok)==0) + size_t toklen=optlen(tok); + + if(ascii_strncasecmp(opts[i].name,tok,toklen)==0) { + /* We have a match, but it might be incomplete */ + if(toklen!=strlen(opts[i].name)) + { + int j; + + for(j=i+1;opts[j].name;j++) + { + if(ascii_strncasecmp(opts[j].name,tok,toklen)==0) + { + if(noisy) + log_info(_("ambiguous option `%s'\n"),otok); + return 0; + } + } + } + if(rev) - *options&=~opts[i].bit; + { + *options&=~opts[i].bit; + if(opts[i].value) + *opts[i].value=NULL; + } else - *options|=opts[i].bit; + { + *options|=opts[i].bit; + if(opts[i].value) + *opts[i].value=argsplit(tok); + } break; } } if(!opts[i].name) - return 0; + { + if(noisy) + log_info(_("unknown option `%s'\n"),otok); + return 0; + } } return 1; } +/* Return a new malloced string by unescaping the string S. Escaping + is percent escaping and '+'/space mapping. A binary nul will + silently be replaced by a 0xFF. */ +char * +unescape_percent_string (const unsigned char *s) +{ + char *buffer, *d; + + buffer = d = xmalloc (strlen (s)+1); + while (*s) + { + if (*s == '%' && s[1] && s[2]) + { + s++; + *d = xtoi_2 (s); + if (!*d) + *d = '\xff'; + d++; + s += 2; + } + else if (*s == '+') + { + *d++ = ' '; + s++; + } + else + *d++ = *s++; + } + *d = 0; + return buffer; +} + + +int +has_invalid_email_chars (const char *s) +{ + int at_seen=0; + const char *valid_chars= + "01234567890_-.abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; + + for ( ; *s; s++ ) + { + if ( *s & 0x80 ) + return 1; + if ( *s == '@' ) + at_seen=1; + else if ( !at_seen && !( !!strchr( valid_chars, *s ) || *s == '+' ) ) + return 1; + else if ( at_seen && !strchr( valid_chars, *s ) ) + return 1; + } + return 0; +} + +/* Check whether NAME represents a valid mailbox according to + RFC822. Returns true if so. */ +int +is_valid_mailbox (const char *name) +{ + return !( !name + || !*name + || has_invalid_email_chars (name) + || string_count_chr (name,'@') != 1 + || *name == '@' + || name[strlen(name)-1] == '@' + || name[strlen(name)-1] == '.' + || strstr (name, "..") ); +} + + +/* This is a helper function to load a Windows function from either of + one DLLs. */ +#ifdef HAVE_W32_SYSTEM +static HRESULT +w32_shgetfolderpath (HWND a, int b, HANDLE c, DWORD d, LPSTR e) +{ + static int initialized; + static HRESULT (WINAPI * func)(HWND,int,HANDLE,DWORD,LPSTR); + + if (!initialized) + { + static char *dllnames[] = { "shell32.dll", "shfolder.dll", NULL }; + void *handle; + int i; + + initialized = 1; + + for (i=0, handle = NULL; !handle && dllnames[i]; i++) + { + handle = dlopen (dllnames[i], RTLD_LAZY); + if (handle) + { + func = dlsym (handle, "SHGetFolderPathA"); + if (!func) + { + dlclose (handle); + handle = NULL; + } + } + } + } + + if (func) + return func (a,b,c,d,e); + else + return -1; +} +#endif /*HAVE_W32_SYSTEM*/ + + +/* Return the name of the libexec directory. The name is allocated in + a static area on the first use. This function won't fail. */ +const char * +get_libexecdir (void) +{ +#ifdef HAVE_W32_SYSTEM + static int got_dir; + static char dir[MAX_PATH+5]; + + if (!got_dir) + { + char *p; + + if ( !GetModuleFileName ( NULL, dir, MAX_PATH) ) + { + log_debug ("GetModuleFileName failed: %s\n", w32_strerror (0)); + *dir = 0; + } + got_dir = 1; + p = strrchr (dir, DIRSEP_C); + if (p) + *p = 0; + else + { + log_debug ("bad filename `%s' returned for this process\n", dir); + *dir = 0; + } + } + + if (*dir) + return dir; + /* Fallback to the hardwired value. */ +#endif /*HAVE_W32_SYSTEM*/ + + return GNUPG_LIBEXECDIR; +} + +/* Similar to access(2), but uses PATH to find the file. */ +int +path_access(const char *file,int mode) +{ + char *envpath; + int ret=-1; + + envpath=getenv("PATH"); + + if(!envpath +#ifdef HAVE_DRIVE_LETTERS + || (((file[0]>='A' && file[0]<='Z') + || (file[0]>='a' && file[0]<='z')) + && file[1]==':') +#else + || file[0]=='/' +#endif + ) + return access(file,mode); + else + { + /* At least as large as, but most often larger than we need. */ + char *buffer=xmalloc(strlen(envpath)+1+strlen(file)+1); + char *split,*item,*path=xstrdup(envpath); + + split=path; + + while((item=strsep(&split,PATHSEP_S))) + { + strcpy(buffer,item); + strcat(buffer,"/"); + strcat(buffer,file); + ret=access(buffer,mode); + if(ret==0) + break; + } + + xfree(path); + xfree(buffer); + } + + return ret; +} + + + /* Temporary helper. */ int pubkey_get_npkey( int algo ) @@ -837,141 +1354,9 @@ pubkey_nbits( int algo, gcry_mpi_t *key ) return nbits; } - -/* MPI helper functions. */ - - -/**************** - * write an mpi to out. - */ -int -mpi_write( iobuf_t out, gcry_mpi_t a ) -{ - char buffer[(MAX_EXTERN_MPI_BITS+7)/8]; - size_t nbytes; - int rc; - - nbytes = (MAX_EXTERN_MPI_BITS+7)/8; - rc = gcry_mpi_print (GCRYMPI_FMT_PGP, buffer, nbytes, &nbytes, a ); - if( !rc ) - rc = iobuf_write( out, buffer, nbytes ); - - return rc; -} - -/**************** - * Writyeg a MPI to out, but in this case it is an opaque one, - * s used vor v3 protected keys. - */ -int -mpi_write_opaque( iobuf_t out, gcry_mpi_t a ) -{ - size_t nbytes, nbits; - int rc; - char *p; - - assert( gcry_mpi_get_flag( a, GCRYMPI_FLAG_OPAQUE ) ); - p = gcry_mpi_get_opaque( a, &nbits ); - nbytes = (nbits+7) / 8; - iobuf_put( out, nbits >> 8 ); - iobuf_put( out, nbits ); - rc = iobuf_write( out, p, nbytes ); - return rc; -} - - -/**************** - * Read an external representation of an mpi and return the MPI - * The external format is a 16 bit unsigned value stored in network byte order, - * giving the number of bits for the following integer. The integer is stored - * with MSB first (left padded with zeroes to align on a byte boundary). - */ -gcry_mpi_t -mpi_read(iobuf_t inp, unsigned int *ret_nread, int secure) -{ - int c, c1, c2, i; - unsigned int nbits, nbytes, nread=0; - gcry_mpi_t a = NULL; - byte *buf = NULL; - byte *p; - - if( (c = c1 = iobuf_get(inp)) == -1 ) - goto leave; - nbits = c << 8; - if( (c = c2 = iobuf_get(inp)) == -1 ) - goto leave; - nbits |= c; - if( nbits > MAX_EXTERN_MPI_BITS ) { - log_error("mpi too large (%u bits)\n", nbits); - goto leave; - } - nread = 2; - nbytes = (nbits+7) / 8; - buf = secure? gcry_xmalloc_secure( nbytes+2 ) : gcry_xmalloc( nbytes+2 ); - p = buf; - p[0] = c1; - p[1] = c2; - for( i=0 ; i < nbytes; i++ ) { - p[i+2] = iobuf_get(inp) & 0xff; - nread++; - } - nread += nbytes; - if( gcry_mpi_scan( &a, GCRYMPI_FMT_PGP, buf, nread, &nread ) ) - a = NULL; - - leave: - gcry_free(buf); - if( nread > *ret_nread ) - log_bug("mpi larger than packet"); - else - *ret_nread = nread; - return a; -} - -/**************** - * Same as mpi_read but the value is stored as an opaque MPI. - * This function is used to read encrypted MPI of v3 packets. - */ -gcry_mpi_t -mpi_read_opaque(iobuf_t inp, unsigned *ret_nread ) -{ - int c, c1, c2, i; - unsigned nbits, nbytes, nread=0; - gcry_mpi_t a = NULL; - byte *buf = NULL; - byte *p; - - if( (c = c1 = iobuf_get(inp)) == -1 ) - goto leave; - nbits = c << 8; - if( (c = c2 = iobuf_get(inp)) == -1 ) - goto leave; - nbits |= c; - if( nbits > MAX_EXTERN_MPI_BITS ) { - log_error("mpi too large (%u bits)\n", nbits); - goto leave; - } - nread = 2; - nbytes = (nbits+7) / 8; - buf = gcry_xmalloc( nbytes ); - p = buf; - for( i=0 ; i < nbytes; i++ ) { - p[i] = iobuf_get(inp) & 0xff; - } - nread += nbytes; - a = gcry_mpi_set_opaque(NULL, buf, nbits ); - buf = NULL; - - leave: - gcry_free(buf); - if( nread > *ret_nread ) - log_bug("mpi larger than packet"); - else - *ret_nread = nread; - return a; -} +/* FIXME: Use gcry_mpi_print directly. */ int mpi_print( FILE *fp, gcry_mpi_t a, int mode ) { @@ -985,11 +1370,10 @@ mpi_print( FILE *fp, gcry_mpi_t a, int mode ) n += fprintf(fp, "[%u bits]", n1); } else { - int rc; - char *buffer; + unsigned char *buffer; - rc = gcry_mpi_aprint( GCRYMPI_FMT_HEX, &buffer, NULL, a ); - assert( !rc ); + if (gcry_mpi_aprint (GCRYMPI_FMT_HEX, &buffer, NULL, a)) + BUG (); fputs( buffer, fp ); n += strlen(buffer); gcry_free( buffer ); @@ -997,4 +1381,3 @@ mpi_print( FILE *fp, gcry_mpi_t a, int mode ) return n; } - diff --git a/g10/openfile.c b/g10/openfile.c index faf9a2cd6..8c0601c3e 100644 --- a/g10/openfile.c +++ b/g10/openfile.c @@ -1,5 +1,6 @@ /* openfile.c - * Copyright (C) 1998, 1999, 2000, 2001, 2003 Free Software Foundation, Inc. + * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, + * 2005 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -15,7 +16,8 @@ * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. */ #include <config.h> @@ -31,7 +33,6 @@ #include "gpg.h" #include "util.h" -#include "memory.h" #include "ttyio.h" #include "options.h" #include "main.h" @@ -66,8 +67,8 @@ int overwrite_filep( const char *fname ) { - if( !fname || (*fname == '-' && !fname[1]) ) - return 1; /* writing to stdout is always okay */ + if( iobuf_is_pipe_filename (fname) ) + return 1; /* Writing to stdout is always okay */ if( access( fname, F_OK ) ) return 1; /* does not exist */ @@ -84,8 +85,10 @@ overwrite_filep( const char *fname ) return 0; /* do not overwrite */ tty_printf(_("File `%s' exists. "), fname); + if( cpr_enabled () ) + tty_printf ("\n"); if( cpr_get_answer_is_yes("openfile.overwrite.okay", - _("Overwrite (y/N)? ")) ) + _("Overwrite? (y/N) ")) ) return 1; return 0; } @@ -100,20 +103,20 @@ make_outfile_name( const char *iname ) { size_t n; - if( (!iname || (*iname=='-' && !iname[1]) )) - return xstrdup ("-"); + if ( iobuf_is_pipe_filename (iname) ) + return xstrdup("-"); n = strlen(iname); if( n > 4 && ( !CMP_FILENAME(iname+n-4, EXTSEP_S "gpg") || !CMP_FILENAME(iname+n-4, EXTSEP_S "pgp") || !CMP_FILENAME(iname+n-4, EXTSEP_S "sig") || !CMP_FILENAME(iname+n-4, EXTSEP_S "asc") ) ) { - char *buf = xstrdup ( iname ); + char *buf = xstrdup( iname ); buf[n-4] = 0; return buf; } else if( n > 5 && !CMP_FILENAME(iname+n-5, EXTSEP_S "sign") ) { - char *buf = xstrdup ( iname ); + char *buf = xstrdup( iname ); buf[n-5] = 0; return buf; } @@ -144,19 +147,21 @@ ask_outfile_name( const char *name, size_t namelen ) n = strlen(s) + namelen + 10; defname = name && namelen? make_printable_string( name, namelen, 0): NULL; - prompt = xmalloc (n); + prompt = xmalloc(n); if( defname ) sprintf(prompt, "%s [%s]: ", s, defname ); else sprintf(prompt, "%s: ", s ); + tty_enable_completion(NULL); fname = cpr_get("openfile.askoutname", prompt ); cpr_kill_prompt(); - xfree (prompt); + tty_disable_completion(); + xfree(prompt); if( !*fname ) { - xfree ( fname ); fname = NULL; + xfree( fname ); fname = NULL; fname = defname; defname = NULL; } - xfree (defname); + xfree(defname); if (fname) trim_spaces (fname); return fname; @@ -165,21 +170,22 @@ ask_outfile_name( const char *name, size_t namelen ) /**************** * Make an output filename for the inputfile INAME. - * Returns an iobuf_t and an errorcode + * Returns an IOBUF and an errorcode * Mode 0 = use ".gpg" * 1 = use ".asc" * 2 = use ".sig" */ int -open_outfile( const char *iname, int mode, iobuf_t *a ) +open_outfile( const char *iname, int mode, IOBUF *a ) { int rc = 0; *a = NULL; - if( (!iname || (*iname=='-' && !iname[1])) && !opt.outfile ) { - if( !(*a = iobuf_create(NULL)) ) { + if( iobuf_is_pipe_filename (iname) && !opt.outfile ) { + *a = iobuf_create(NULL); + if( !*a ) { rc = gpg_error_from_errno (errno); - log_error(_("%s: can't open: %s\n"), "[stdout]", strerror(errno) ); + log_error(_("can't open `%s': %s\n"), "[stdout]", strerror(errno) ); } else if( opt.verbose ) log_info(_("writing to stdout\n")); @@ -207,7 +213,7 @@ open_outfile( const char *iname, int mode, iobuf_t *a ) const char *newsfx = mode==1 ? ".asc" : mode==2 ? ".sig" : ".gpg"; - buf = xmalloc (strlen(iname)+4+1); + buf = xmalloc(strlen(iname)+4+1); strcpy(buf,iname); dot = strchr(buf, '.' ); if ( dot && dot > buf && dot[1] && strlen(dot) <= 4 @@ -223,7 +229,7 @@ open_outfile( const char *iname, int mode, iobuf_t *a ) if (!buf) #endif /* USE_ONLY_8DOT3 */ { - buf = xmalloc (strlen(iname)+4+1); + buf = xmalloc(strlen(iname)+4+1); strcpy(stpcpy(buf,iname), mode==1 ? EXTSEP_S "asc" : mode==2 ? EXTSEP_S "sig" : EXTSEP_S "gpg"); } @@ -237,7 +243,7 @@ open_outfile( const char *iname, int mode, iobuf_t *a ) if ( !tmp || !*tmp ) { xfree (tmp); - rc = GPG_ERR_EEXIST; + rc = gpg_error (GPG_ERR_EEXIST); break; } xfree (buf); @@ -246,17 +252,27 @@ open_outfile( const char *iname, int mode, iobuf_t *a ) if( !rc ) { - if( !(*a = iobuf_create( name )) ) + if (is_secured_filename (name) ) + { + *a = NULL; + errno = EPERM; + } + else + *a = iobuf_create( name ); + if( !*a ) { rc = gpg_error_from_errno (errno); - log_error(_("%s: can't create: %s\n"), name, strerror(errno) ); + log_error(_("can't create `%s': %s\n"), name, strerror(errno) ); } else if( opt.verbose ) log_info(_("writing to `%s'\n"), name ); } - xfree (buf); + xfree(buf); } + if (*a) + iobuf_ioctl (*a,3,1,NULL); /* disable fd caching */ + return rc; } @@ -265,26 +281,32 @@ open_outfile( const char *iname, int mode, iobuf_t *a ) * Try to open a file without the extension ".sig" or ".asc" * Return NULL if such a file is not available. */ -iobuf_t +IOBUF open_sigfile( const char *iname, progress_filter_context_t *pfx ) { - iobuf_t a = NULL; + IOBUF a = NULL; size_t len; - if( iname && !(*iname == '-' && !iname[1]) ) { + if( !iobuf_is_pipe_filename (iname) ) { len = strlen(iname); if( len > 4 && ( !strcmp(iname + len - 4, EXTSEP_S "sig") || ( len > 5 && !strcmp(iname + len - 5, EXTSEP_S "sign") ) || !strcmp(iname + len - 4, EXTSEP_S "asc")) ) { char *buf; - buf = xstrdup (iname); + buf = xstrdup(iname); buf[len-(buf[len-1]=='n'?5:4)] = 0 ; a = iobuf_open( buf ); + if (a && is_secured_file (iobuf_get_fd (a))) + { + iobuf_close (a); + a = NULL; + errno = EPERM; + } if( a && opt.verbose ) log_info(_("assuming signed data in `%s'\n"), buf ); if (a && pfx) handle_progress (pfx, a, buf); - xfree (buf); + xfree(buf); } } return a; @@ -308,23 +330,34 @@ copy_options_file( const char *destdir ) if( opt.dry_run ) return; - fname = xmalloc ( strlen(datadir) + strlen(destdir) - + strlen (SKELEXT) + 15 ); + fname = xmalloc( strlen(datadir) + strlen(destdir) + 15 ); strcpy(stpcpy(fname, datadir), DIRSEP_S "gpg-conf" SKELEXT ); src = fopen( fname, "r" ); + if (src && is_secured_file (fileno (src))) + { + fclose (src); + src = NULL; + errno = EPERM; + } if( !src ) { - log_error(_("%s: can't open: %s\n"), fname, strerror(errno) ); - xfree (fname); + log_error(_("can't open `%s': %s\n"), fname, strerror(errno) ); + xfree(fname); return; } strcpy(stpcpy(fname, destdir), DIRSEP_S "gpg" EXTSEP_S "conf" ); oldmask=umask(077); - dst = fopen( fname, "w" ); + if ( is_secured_filename (fname) ) + { + dst = NULL; + errno = EPERM; + } + else + dst = fopen( fname, "w" ); umask(oldmask); if( !dst ) { - log_error(_("%s: can't create: %s\n"), fname, strerror(errno) ); + log_error(_("can't create `%s': %s\n"), fname, strerror(errno) ); fclose( src ); - xfree (fname); + xfree(fname); return; } @@ -354,7 +387,7 @@ copy_options_file( const char *destdir ) log_info (_("WARNING: options in `%s'" " are not yet active during this run\n"), fname); - xfree (fname); + xfree(fname); } @@ -380,10 +413,10 @@ try_make_homedir( const char *fname ) && !compare_filenames( fname, defhome ) ) ) { if( mkdir( fname, S_IRUSR|S_IWUSR|S_IXUSR ) ) - log_fatal( _("%s: can't create directory: %s\n"), + log_fatal( _("can't create directory `%s': %s\n"), fname, strerror(errno) ); else if( !opt.quiet ) - log_info( _("%s: directory created\n"), fname ); + log_info( _("directory `%s' created\n"), fname ); copy_options_file( fname ); /* log_info(_("you have to start GnuPG again, " */ /* "so it can read the new configuration file\n") ); */ diff --git a/g10/options.h b/g10/options.h index a4cbc3834..7e9d0261c 100644 --- a/g10/options.h +++ b/g10/options.h @@ -1,6 +1,6 @@ /* options.h - * Copyright (C) 1998, 1999, 2000, 2001, 2002, - * 2003 Free Software Foundation, Inc. + * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, + * 2006 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -16,17 +16,17 @@ * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. */ #ifndef G10_OPTIONS_H #define G10_OPTIONS_H +#include <sys/types.h> #include <types.h> #include "main.h" #include "packet.h" -#undef ENABLE_COMMENT_PACKETS /* don't create comment packets */ - #ifndef EXTERN_UNLESS_MAIN_MODULE /* Norcraft can't cope with common symbols */ #if defined (__riscos__) && !defined (INCLUDED_BY_MAIN_MODULE) @@ -37,155 +37,215 @@ #endif EXTERN_UNLESS_MAIN_MODULE -struct { - int verbose; - int quiet; - unsigned debug; - int armor; - int compress; - char *outfile; - int dry_run; - int list_only; - int textmode; - int expert; - int ask_sig_expire; - int ask_cert_expire; - int batch; /* run in batch mode */ - int answer_yes; /* answer yes on most questions */ - int answer_no; /* answer no on most questions */ - int check_sigs; /* check key signatures */ - int with_colons; - int with_key_data; - int with_fingerprint; /* opt --with-fingerprint active */ - int fingerprint; /* list fingerprints */ - int list_sigs; /* list signatures */ - int no_armor; - int list_packets; /* list-packets mode: 1=normal, 2=invoked by command*/ - int def_cipher_algo; - int force_v3_sigs; - int force_v4_certs; - int force_mdc; - int disable_mdc; - int def_digest_algo; - int cert_digest_algo; - int def_compress_algo; - const char *def_secret_key; - char *def_recipient; - int def_recipient_self; - int def_cert_check_level; - int sk_comments; - int no_version; - int marginals_needed; - int completes_needed; - int max_cert_depth; - const char *homedir; - const char *agent_program; - char *display; /* 5 options to be passed to the gpg-agent */ - char *ttyname; - char *ttytype; - char *lc_ctype; - char *lc_messages; - - int skip_verify; - int compress_keys; - int compress_sigs; - /* TM_CLASSIC must be zero to accomodate trustdbs generated before - we started storing the trust model inside the trustdb. */ - enum {TM_CLASSIC=0, TM_PGP=1, TM_ALWAYS, TM_AUTO} trust_model; - unsigned int force_ownertrust; - enum - { - CO_GNUPG=0, CO_RFC2440, CO_RFC1991, CO_PGP2, CO_PGP6, CO_PGP7, CO_PGP8 - } compliance; - int pgp2_workarounds; - unsigned int emulate_bugs; /* bug emulation flags EMUBUG_xxxx */ - int shm_coprocess; - const char *set_filename; - STRLIST comments; - int throw_keyid; - const char *photo_viewer; - int s2k_mode; - int s2k_digest_algo; - int s2k_cipher_algo; - int simple_sk_checksum; /* create the deprecated rfc2440 secret - key protection*/ - int not_dash_escaped; - int escape_from; - int lock_once; - char *keyserver_uri; - char *keyserver_scheme; - char *keyserver_host; - char *keyserver_port; - char *keyserver_opaque; +struct +{ + int verbose; + int quiet; + unsigned debug; + int armor; + char *outfile; + off_t max_output; + int dry_run; + int list_only; + int textmode; + int expert; + const char *def_sig_expire; + int ask_sig_expire; + const char *def_cert_expire; + int ask_cert_expire; + int batch; /* run in batch mode */ + int answer_yes; /* answer yes on most questions */ + int answer_no; /* answer no on most questions */ + int check_sigs; /* check key signatures */ + int with_colons; + int with_key_data; + int with_fingerprint; /* opt --with-fingerprint active */ + int fingerprint; /* list fingerprints */ + int list_sigs; /* list signatures */ + int no_armor; + int list_packets; /* list-packets mode: 1=normal, 2=invoked by command*/ + int def_cipher_algo; + int force_v3_sigs; + int force_v4_certs; + int force_mdc; + int disable_mdc; + int def_digest_algo; + int cert_digest_algo; + int compress_algo; + int compress_level; + int bz2_compress_level; + int bz2_decompress_lowmem; + const char *def_secret_key; + char *def_recipient; + int def_recipient_self; + int def_cert_level; + int min_cert_level; + int ask_cert_level; + int no_version; + int marginals_needed; + int completes_needed; + int max_cert_depth; + const char *homedir; + const char *agent_program; + char *display; /* 5 options to be passed to the gpg-agent */ + char *ttyname; + char *ttytype; + char *lc_ctype; + char *lc_messages; + + int skip_verify; + int compress_keys; + int compress_sigs; + /* TM_CLASSIC must be zero to accomodate trustdbs generated before + we started storing the trust model inside the trustdb. */ + enum + { + TM_CLASSIC=0, TM_PGP=1, TM_EXTERNAL=2, TM_ALWAYS, TM_DIRECT, TM_AUTO + } trust_model; + int force_ownertrust; + enum + { + CO_GNUPG=0, CO_RFC2440, CO_RFC1991, CO_PGP2, CO_PGP6, CO_PGP7, CO_PGP8 + } compliance; + enum + { + KF_SHORT, KF_LONG, KF_0xSHORT, KF_0xLONG + } keyid_format; + int pgp2_workarounds; + int shm_coprocess; + const char *set_filename; + STRLIST comments; + int throw_keyid; + const char *photo_viewer; + int s2k_mode; + int s2k_digest_algo; + int s2k_cipher_algo; + int simple_sk_checksum; /* create the deprecated rfc2440 secret + key protection*/ + int not_dash_escaped; + int escape_from; + int lock_once; + struct keyserver_spec + { + char *uri; + char *scheme; + char *auth; + char *host; + char *port; + char *path; + char *opaque; + STRLIST options; struct { - int verbose; - int include_revoked; - int include_disabled; - int include_subkeys; - int honor_http_proxy; - int broken_http_proxy; - int use_temp_files; - int keep_temp_files; - int fake_v3_keyids; - int auto_key_retrieve; - int try_dns_srv; - unsigned int import_options; - unsigned int export_options; - STRLIST other; - } keyserver_options; - int exec_disable; - int exec_path_set; + unsigned int direct_uri:1; + } flags; + struct keyserver_spec *next; + } *keyserver; + struct + { + unsigned int options; unsigned int import_options; unsigned int export_options; - unsigned int list_options; - unsigned int verify_options; - char *def_preference_list; - prefitem_t *personal_cipher_prefs; - prefitem_t *personal_digest_prefs; - prefitem_t *personal_compress_prefs; - int no_perm_warn; - int no_mdc_warn; - char *temp_dir; - int no_encrypt_to; - int interactive; - STRLIST sig_notation_data; - STRLIST cert_notation_data; - STRLIST sig_policy_url; - STRLIST cert_policy_url; - STRLIST sig_keyserver_url; - int use_embedded_filename; - int allow_non_selfsigned_uid; - int allow_freeform_uid; - int no_literal; - ulong set_filesize; - int fast_list_mode; - int fixed_list_mode; - int ignore_time_conflict; - int ignore_valid_from; - int ignore_crc_error; - int ignore_mdc_error; - int command_fd; - const char *override_session_key; - int show_session_key; - int use_agent; - const char *gpg_agent_info; - int merge_only; - int try_all_secrets; - int no_expensive_trust_checks; - int no_sig_cache; - int no_sig_create_check; - int no_auto_check_trustdb; - int preserve_permissions; - int no_homedir_creation; - struct groupitem *grouplist; - int strict; - int mangle_dos_filenames; - int enable_progress_filter; -} opt; + STRLIST other; + } keyserver_options; + int exec_disable; + int exec_path_set; + unsigned int import_options; + unsigned int export_options; + unsigned int list_options; + unsigned int verify_options; + char *def_preference_list; + prefitem_t *personal_cipher_prefs; + prefitem_t *personal_digest_prefs; + prefitem_t *personal_compress_prefs; + int no_perm_warn; + int no_mdc_warn; + char *temp_dir; + int no_encrypt_to; + int interactive; + struct notation *sig_notations; + struct notation *cert_notations; + STRLIST sig_policy_url; + STRLIST cert_policy_url; + STRLIST sig_keyserver_url; + STRLIST cert_subpackets; + STRLIST sig_subpackets; + int use_embedded_filename; + int allow_non_selfsigned_uid; + int allow_freeform_uid; + int no_literal; + ulong set_filesize; + int fast_list_mode; + int fixed_list_mode; + int ignore_time_conflict; + int ignore_valid_from; + int ignore_crc_error; + int ignore_mdc_error; + int command_fd; + const char *override_session_key; + int show_session_key; + int use_agent; + const char *gpg_agent_info; + int try_all_secrets; + int no_expensive_trust_checks; + int no_sig_cache; + int no_sig_create_check; + int no_auto_check_trustdb; + int preserve_permissions; + int no_homedir_creation; + struct groupitem *grouplist; + int strict; + int mangle_dos_filenames; + int enable_progress_filter; + unsigned int screen_columns; + unsigned int screen_lines; + byte *show_subpackets; + int rfc2440_text; + + /* If true, let write failures on the status-fd exit the process. */ + int exit_on_status_write_error; + + /* If > 0, limit the number of card insertion prompts to this + value. */ + int limit_card_insert_tries; + +#ifdef ENABLE_CARD_SUPPORT + /* FIXME: We don't needs this here as it is done in scdaemon. */ + const char *ctapi_driver; /* Library to access the ctAPI. */ + const char *pcsc_driver; /* Library to access the PC/SC system. */ + int disable_ccid; /* Disable the use of the internal CCID driver. */ +#endif /*ENABLE_CARD_SUPPORT*/ + + struct + { + /* If set, require an 0x19 backsig to be present on signatures + made by signing subkeys. If not set, a missing backsig is not + an error (but an invalid backsig still is). */ + unsigned int require_cross_cert:1; + } flags; + + /* Linked list of ways to find a key if the key isn't on the local + keyring. */ + struct akl + { + enum {AKL_CERT, AKL_PKA, AKL_LDAP, AKL_KEYSERVER, AKL_SPEC} type; + struct keyserver_spec *spec; + struct akl *next; + } *auto_key_locate; + + /* True if multiple concatenated signatures may be verified. */ + int allow_multisig_verification; +} opt; -#define EMUBUG_MDENCODE 4 +/* CTRL is used to keep some global variables we currently can't + avoid. Future concurrent versions of gpg will put it into a per + request structure CTRL. */ +EXTERN_UNLESS_MAIN_MODULE +struct { + int in_auto_key_retrieve; /* True if we are doing an + auto_key_retrieve. */ +} glo_ctrl; #define DBG_PACKET_VALUE 1 /* debug packet reading/writing */ #define DBG_MPI_VALUE 2 /* debug mpi details */ @@ -199,15 +259,24 @@ struct { #define DBG_TRUST_VALUE 256 /* debug the trustdb */ #define DBG_HASHING_VALUE 512 /* debug hashing operations */ #define DBG_EXTPROG_VALUE 1024 /* debug external program calls */ - +#define DBG_CARD_IO_VALUE 2048 /* debug smart card I/O. */ #define DBG_PACKET (opt.debug & DBG_PACKET_VALUE) -#define DBG_CIPHER (opt.debug & DBG_CIPHER_VALUE) #define DBG_FILTER (opt.debug & DBG_FILTER_VALUE) #define DBG_CACHE (opt.debug & DBG_CACHE_VALUE) #define DBG_TRUST (opt.debug & DBG_TRUST_VALUE) #define DBG_HASHING (opt.debug & DBG_HASHING_VALUE) #define DBG_EXTPROG (opt.debug & DBG_EXTPROG_VALUE) +#define DBG_CARD_IO (opt.debug & DBG_CARD_IO_VALUE) + +/* FIXME: We need to check whey we did not put this into opt. */ +#define DBG_MEMORY memory_debug_mode +#define DBG_MEMSTAT memory_stat_debug_mode + +EXTERN_UNLESS_MAIN_MODULE int memory_debug_mode; +EXTERN_UNLESS_MAIN_MODULE int memory_stat_debug_mode; + + #define GNUPG (opt.compliance==CO_GNUPG) #define RFC1991 (opt.compliance==CO_RFC1991 || opt.compliance==CO_PGP2) @@ -217,39 +286,54 @@ struct { #define PGP7 (opt.compliance==CO_PGP7) #define PGP8 (opt.compliance==CO_PGP8) -/* Various option flags */ - -#define IMPORT_ALLOW_LOCAL_SIGS 1 -#define IMPORT_REPAIR_PKS_SUBKEY_BUG 2 -#define IMPORT_FAST_IMPORT 4 -#define IMPORT_SK2PK 8 +/* Various option flags. Note that there should be no common string + names between the IMPORT_ and EXPORT_ flags as they can be mixed in + the keyserver-options option. */ -#define EXPORT_INCLUDE_NON_RFC 1 -#define EXPORT_INCLUDE_LOCAL_SIGS 2 -#define EXPORT_INCLUDE_ATTRIBUTES 4 -#define EXPORT_INCLUDE_SENSITIVE_REVKEYS 8 -#define EXPORT_SEXP_FORMAT 16 +#define IMPORT_LOCAL_SIGS (1<<0) +#define IMPORT_REPAIR_PKS_SUBKEY_BUG (1<<1) +#define IMPORT_FAST (1<<2) +#define IMPORT_SK2PK (1<<3) +#define IMPORT_MERGE_ONLY (1<<4) +#define IMPORT_MINIMAL (1<<5) +#define IMPORT_CLEAN (1<<6) +#define EXPORT_LOCAL_SIGS (1<<0) +#define EXPORT_ATTRIBUTES (1<<1) +#define EXPORT_SENSITIVE_REVKEYS (1<<2) +#define EXPORT_RESET_SUBKEY_PASSWD (1<<3) +#define EXPORT_MINIMAL (1<<4) +#define EXPORT_CLEAN (1<<5) -#define LIST_SHOW_PHOTOS 1 -#define LIST_SHOW_POLICY 2 -#define LIST_SHOW_NOTATION 4 -#define LIST_SHOW_KEYSERVER 8 -#define LIST_SHOW_VALIDITY 16 -#define LIST_SHOW_LONG_KEYID 32 -#define LIST_SHOW_KEYRING 64 -#define LIST_SHOW_SIG_EXPIRE 128 +#define LIST_SHOW_PHOTOS (1<<0) +#define LIST_SHOW_POLICY_URLS (1<<1) +#define LIST_SHOW_STD_NOTATIONS (1<<2) +#define LIST_SHOW_USER_NOTATIONS (1<<3) +#define LIST_SHOW_NOTATIONS (LIST_SHOW_STD_NOTATIONS|LIST_SHOW_USER_NOTATIONS) +#define LIST_SHOW_KEYSERVER_URLS (1<<4) +#define LIST_SHOW_UID_VALIDITY (1<<5) +#define LIST_SHOW_UNUSABLE_UIDS (1<<6) +#define LIST_SHOW_UNUSABLE_SUBKEYS (1<<7) +#define LIST_SHOW_KEYRING (1<<8) +#define LIST_SHOW_SIG_EXPIRE (1<<9) +#define LIST_SHOW_SIG_SUBPACKETS (1<<10) +#define VERIFY_SHOW_PHOTOS (1<<0) +#define VERIFY_SHOW_POLICY_URLS (1<<1) +#define VERIFY_SHOW_STD_NOTATIONS (1<<2) +#define VERIFY_SHOW_USER_NOTATIONS (1<<3) +#define VERIFY_SHOW_NOTATIONS (VERIFY_SHOW_STD_NOTATIONS|VERIFY_SHOW_USER_NOTATIONS) +#define VERIFY_SHOW_KEYSERVER_URLS (1<<4) +#define VERIFY_SHOW_UID_VALIDITY (1<<5) +#define VERIFY_SHOW_UNUSABLE_UIDS (1<<6) +#define VERIFY_PKA_LOOKUPS (1<<7) +#define VERIFY_PKA_TRUST_INCREASE (1<<8) -#define VERIFY_SHOW_PHOTOS 1 -#define VERIFY_SHOW_POLICY 2 -#define VERIFY_SHOW_NOTATION 4 -#define VERIFY_SHOW_KEYSERVER 8 -#define VERIFY_SHOW_VALIDITY 16 -#define VERIFY_SHOW_LONG_KEYID 32 +#define KEYSERVER_USE_TEMP_FILES (1<<0) +#define KEYSERVER_KEEP_TEMP_FILES (1<<1) +#define KEYSERVER_ADD_FAKE_V3 (1<<2) +#define KEYSERVER_AUTO_KEY_RETRIEVE (1<<3) +#define KEYSERVER_HONOR_KEYSERVER_URL (1<<4) +#define KEYSERVER_HONOR_PKA_RECORD (1<<5) #endif /*G10_OPTIONS_H*/ - - - - diff --git a/g10/packet.h b/g10/packet.h index 2d87c9c7d..54eeda1a9 100644 --- a/g10/packet.h +++ b/g10/packet.h @@ -1,6 +1,6 @@ -/* packet.h - packet definitions - * Copyright (C) 1998, 1999, 2000, 2001, 2002, - * 2003 Free Software Foundation, Inc. +/* packet.h - OpenPGP packet definitions + * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, + * 2006 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -16,21 +16,18 @@ * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. */ #ifndef G10_PACKET_H #define G10_PACKET_H -#include "gpg.h" -#include <gcrypt.h> - #include "types.h" #include "../common/iobuf.h" #include "../jnlib/strlist.h" #include "cipher.h" #include "filter.h" -#include "global.h" #define DEBUG_PARSE_PACKET 1 @@ -124,36 +121,56 @@ struct revocation_key { byte fpr[MAX_FINGERPRINT_LEN]; }; -typedef struct { - ulong local_id; /* internal use, valid if > 0 */ - struct { - unsigned checked:1; /* signature has been checked */ - unsigned valid:1; /* signature is good (if checked is set) */ - unsigned unknown_critical:1; - unsigned exportable:1; - unsigned revocable:1; - unsigned policy_url:1; /* At least one policy URL is present */ - unsigned notation:1; /* At least one notation is present */ - unsigned pref_ks:1; /* At least one preferred keyserver is present */ - unsigned expired:1; - } flags; - u32 keyid[2]; /* 64 bit keyid */ - u32 timestamp; /* signature made */ - u32 expiredate; /* expires at this date or 0 if not at all */ - byte version; - byte sig_class; /* sig classification, append for MD calculation*/ - byte pubkey_algo; /* algorithm used for public key scheme */ - /* (PUBKEY_ALGO_xxx) */ - byte digest_algo; /* algorithm used for digest (DIGEST_ALGO_xxxx) */ - byte trust_depth; - byte trust_value; - const byte *trust_regexp; - struct revocation_key **revkey; - int numrevkeys; - subpktarea_t *hashed; /* all subpackets with hashed data (v4 only) */ - subpktarea_t *unhashed; /* ditto for unhashed data */ - byte digest_start[2]; /* first 2 bytes of the digest */ - gcry_mpi_t data[PUBKEY_MAX_NSIG]; + +/* Object to keep information about a PKA DNS record. */ +typedef struct +{ + int valid; /* An actual PKA record exists for EMAIL. */ + int checked; /* Set to true if the FPR has been checked against the + actual key. */ + char *uri; /* Malloced string with the URI. NULL if the URI is + not available.*/ + unsigned char fpr[20]; /* The fingerprint as stored in the PKA RR. */ + char email[1];/* The email address from the notation data. */ +} pka_info_t; + + +/* Object to keep information pertaining to a signature. */ +typedef struct +{ + struct + { + unsigned checked:1; /* Signature has been checked. */ + unsigned valid:1; /* Signature is good (if checked is set). */ + unsigned chosen_selfsig:1; /* A selfsig that is the chosen one. */ + unsigned unknown_critical:1; + unsigned exportable:1; + unsigned revocable:1; + unsigned policy_url:1; /* At least one policy URL is present */ + unsigned notation:1; /* At least one notation is present */ + unsigned pref_ks:1; /* At least one preferred keyserver is present */ + unsigned expired:1; + unsigned pka_tried:1; /* Set if we tried to retrieve the PKA record. */ + } flags; + u32 keyid[2]; /* 64 bit keyid */ + u32 timestamp; /* Signature made (seconds since Epoch). */ + u32 expiredate; /* Expires at this date or 0 if not at all. */ + byte version; + byte sig_class; /* Sig classification, append for MD calculation. */ + byte pubkey_algo; /* Algorithm used for public key scheme */ + /* (PUBKEY_ALGO_xxx) */ + byte digest_algo; /* Algorithm used for digest (DIGEST_ALGO_xxxx). */ + byte trust_depth; + byte trust_value; + const byte *trust_regexp; + struct revocation_key **revkey; + int numrevkeys; + pka_info_t *pka_info; /* Malloced PKA data or NULL if not + available. See also flags.pka_tried. */ + subpktarea_t *hashed; /* All subpackets with hashed data (v4 only). */ + subpktarea_t *unhashed; /* Ditto for unhashed data. */ + byte digest_start[2]; /* First 2 bytes of the digest. */ + gcry_mpi_t data[PUBKEY_MAX_NSIG]; } PKT_signature; #define ATTRIB_IMAGE 1 @@ -165,41 +182,57 @@ struct user_attribute { u32 len; }; -typedef struct { - int ref; /* reference counter */ - int len; /* length of the name */ - struct user_attribute *attribs; - int numattribs; - byte *attrib_data; /* if this is not NULL, the packet is an attribute */ - unsigned long attrib_len; - byte *namehash; - int help_key_usage; - u32 help_key_expire; - int help_full_count; - int help_marginal_count; - int is_primary; /* 2 if set via the primary flag, 1 if calculated */ - int is_revoked; - int is_expired; - u32 expiredate; /* expires at this date or 0 if not at all */ - prefitem_t *prefs; /* list of preferences (may be NULL)*/ - int mdc_feature; - int ks_modify; - u32 created; /* according to the self-signature */ - byte selfsigversion; - char name[1]; +typedef struct +{ + int ref; /* reference counter */ + int len; /* length of the name */ + struct user_attribute *attribs; + int numattribs; + byte *attrib_data; /* if this is not NULL, the packet is an attribute */ + unsigned long attrib_len; + byte *namehash; + int help_key_usage; + u32 help_key_expire; + int help_full_count; + int help_marginal_count; + int is_primary; /* 2 if set via the primary flag, 1 if calculated */ + int is_revoked; + int is_expired; + u32 expiredate; /* expires at this date or 0 if not at all */ + prefitem_t *prefs; /* list of preferences (may be NULL)*/ + u32 created; /* according to the self-signature */ + byte selfsigversion; + struct + { + /* TODO: Move more flags here */ + unsigned mdc:1; + unsigned ks_modify:1; + unsigned compacted:1; + } flags; + char name[1]; } PKT_user_id; +struct revoke_info +{ + /* revoked at this date */ + u32 date; + /* the keyid of the revoking key (selfsig or designated revoker) */ + u32 keyid[2]; + /* the algo of the revoking key */ + byte algo; +}; /**************** * Note about the pkey/skey elements: We assume that the secret keys * has the same elemts as the public key at the begin of the array, so * that npkey < nskey and it is possible to compare the secret and - * public keys by comparing the first npkey elements of pkey against skey. + * public keys by comparing the first npkey elements of pkey againts skey. */ typedef struct { u32 timestamp; /* key made */ u32 expiredate; /* expires at this date or 0 if not at all */ u32 max_expiredate; /* must not expire past this date */ + struct revoke_info revoked; byte hdrbytes; /* number of header bytes */ byte version; byte selfsigversion; /* highest version of all of the self-sigs */ @@ -208,10 +241,13 @@ typedef struct { 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_revoked; /* key has been revoked, 1 if by the + owner, 2 if by a designated revoker */ + int maybe_revoked; /* a designated revocation is present, but + without the key to check it */ int is_valid; /* key (especially subkey) is valid */ int dont_cache; /* do not cache this */ - ulong local_id; /* internal use, valid if > 0 */ + byte backsig; /* 0=none, 1=bad, 2=good */ u32 main_keyid[2]; /* keyid of the primary key */ u32 keyid[2]; /* calculated by keyid_from_pk() */ byte is_primary; @@ -273,15 +309,16 @@ typedef struct { u32 len; /* reserved */ byte new_ctb; byte algorithm; - iobuf_t buf; /* iobuf_t reference */ + iobuf_t buf; /* IOBUF reference */ } PKT_compressed; typedef struct { u32 len; /* length of encrypted data */ int extralen; /* this is (blocksize+2) */ byte new_ctb; /* uses a new CTB */ + byte is_partial; /* partial length encoded */ byte mdc_method; /* > 0: integrity protected encrypted data packet */ - iobuf_t buf; /* iobuf_t reference */ + iobuf_t buf; /* IOBUF reference */ } PKT_encrypted; typedef struct { @@ -295,7 +332,7 @@ typedef struct { typedef struct { u32 len; /* length of encrypted data */ - iobuf_t buf; /* iobuf_t reference */ + iobuf_t buf; /* IOBUF reference */ byte new_ctb; byte is_partial; /* partial length encoded */ int mode; @@ -364,9 +401,25 @@ typedef enum { SIGSUBPKT_REVOC_REASON =29, /* reason for revocation */ SIGSUBPKT_FEATURES =30, /* feature flags */ + SIGSUBPKT_SIGNATURE =32, /* embedded signature */ + SIGSUBPKT_FLAG_CRITICAL=128 } sigsubpkttype_t; +struct notation +{ + char *name; + char *value; + char *altvalue; + unsigned char *bdat; + size_t blen; + struct + { + unsigned int critical:1; + unsigned int ignore:1; + } flags; + struct notation *next; +}; /*-- mainproc.c --*/ int proc_packets( void *ctx, iobuf_t a ); @@ -407,6 +460,8 @@ int copy_some_packets( iobuf_t inp, iobuf_t out, off_t stopoff ); int skip_some_packets( iobuf_t inp, unsigned n ); #endif +int parse_signature( iobuf_t inp, int pkttype, unsigned long pktlen, + PKT_signature *sig ); const byte *enum_sig_subpkt ( const subpktarea_t *subpkts, sigsubpkttype_t reqtype, size_t *ret_n, int *start, int *critical ); @@ -427,7 +482,6 @@ PACKET *create_gpg_control ( ctrlpkttype_t type, /*-- build-packet.c --*/ int build_packet( iobuf_t inp, PACKET *pkt ); u32 calc_packet_length( PACKET *pkt ); -void hash_public_key( MD_HANDLE md, PKT_public_key *pk ); void build_sig_subpkt( PKT_signature *sig, sigsubpkttype_t type, const byte *buffer, size_t buflen ); void build_sig_subpkt_from_sig( PKT_signature *sig ); @@ -435,6 +489,9 @@ int delete_sig_subpkt(subpktarea_t *buffer, sigsubpkttype_t type ); void build_attribute_subpkt(PKT_user_id *uid,byte type, const void *buf,u32 buflen, const void *header,u32 headerlen); +struct notation *string_to_notation(const char *string,int is_utf8); +struct notation *sig_to_notation(PKT_signature *sig); +void free_notation(struct notation *notation); /*-- free-packet.c --*/ void free_symkey_enc( PKT_symkey_enc *enc ); @@ -463,8 +520,8 @@ int cmp_user_ids( PKT_user_id *a, PKT_user_id *b ); /*-- sig-check.c --*/ -int signature_check( PKT_signature *sig, MD_HANDLE digest ); -int signature_check2( PKT_signature *sig, MD_HANDLE digest, u32 *r_expiredate, +int signature_check( PKT_signature *sig, gcry_md_hd_t digest ); +int signature_check2( PKT_signature *sig, gcry_md_hd_t digest, u32 *r_expiredate, int *r_expired, int *r_revoked, PKT_public_key *ret_pk ); /*-- seckey-cert.c --*/ @@ -485,13 +542,10 @@ int decrypt_data( void *ctx, PKT_encrypted *ed, DEK *dek ); /*-- plaintext.c --*/ int handle_plaintext( PKT_plaintext *pt, md_filter_context_t *mfx, - int nooutput, int clearsig, int *create_failed ); -int ask_for_detached_datafile( MD_HANDLE md, MD_HANDLE md2, + int nooutput, int clearsig ); +int ask_for_detached_datafile( gcry_md_hd_t md, gcry_md_hd_t md2, const char *inname, int textmode ); -/*-- comment.c --*/ -int write_comment( iobuf_t out, const char *s ); - /*-- sign.c --*/ int make_keysig_packet( PKT_signature **ret_sig, PKT_public_key *pk, PKT_user_id *uid, PKT_public_key *subpk, diff --git a/g10/parse-packet.c b/g10/parse-packet.c index c922eb5d9..b4dfb8c84 100644 --- a/g10/parse-packet.c +++ b/g10/parse-packet.c @@ -1,6 +1,6 @@ /* parse-packet.c - read packets - * Copyright (C) 1998, 1999, 2000, 2001, 2002, - * 2003 Free Software Foundation, Inc. + * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, + * 2005 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -16,7 +16,8 @@ * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. */ #include <config.h> @@ -25,63 +26,61 @@ #include <string.h> #include <assert.h> +#include "gpg.h" #include "packet.h" #include "iobuf.h" -#include "mpi.h" #include "util.h" #include "cipher.h" -#include "memory.h" #include "filter.h" #include "photoid.h" #include "options.h" #include "main.h" #include "i18n.h" -static int mpi_print_mode = 0; -static int list_mode = 0; +static int mpi_print_mode; +static int list_mode; +static FILE *listfp; -static int parse( iobuf_t inp, PACKET *pkt, int onlykeypkts, - off_t *retpos, int *skip, iobuf_t out, int do_skip +static int parse( IOBUF inp, PACKET *pkt, int onlykeypkts, + off_t *retpos, int *skip, IOBUF out, int do_skip #ifdef DEBUG_PARSE_PACKET ,const char *dbg_w, const char *dbg_f, int dbg_l #endif ); -static int copy_packet( iobuf_t inp, iobuf_t out, int pkttype, - unsigned long pktlen ); -static void skip_packet( iobuf_t inp, int pkttype, unsigned long pktlen ); -static void skip_rest( iobuf_t inp, unsigned long pktlen ); -static void *read_rest( iobuf_t inp, size_t pktlen ); -static int parse_symkeyenc( iobuf_t inp, int pkttype, unsigned long pktlen, +static int copy_packet( IOBUF inp, IOBUF out, int pkttype, + unsigned long pktlen, int partial ); +static void skip_packet( IOBUF inp, int pkttype, + unsigned long pktlen, int partial ); +static void *read_rest( IOBUF inp, size_t pktlen, int partial ); +static int parse_symkeyenc( IOBUF inp, int pkttype, unsigned long pktlen, PACKET *packet ); -static int parse_pubkeyenc( iobuf_t inp, int pkttype, unsigned long pktlen, +static int parse_pubkeyenc( IOBUF inp, int pkttype, unsigned long pktlen, PACKET *packet ); -static int parse_signature( iobuf_t inp, int pkttype, unsigned long pktlen, - PKT_signature *sig ); -static int parse_onepass_sig( iobuf_t inp, int pkttype, unsigned long pktlen, +static int parse_onepass_sig( IOBUF inp, int pkttype, unsigned long pktlen, PKT_onepass_sig *ops ); -static int parse_key( iobuf_t inp, int pkttype, unsigned long pktlen, +static int parse_key( IOBUF inp, int pkttype, unsigned long pktlen, byte *hdr, int hdrlen, PACKET *packet ); -static int parse_user_id( iobuf_t inp, int pkttype, unsigned long pktlen, +static int parse_user_id( IOBUF inp, int pkttype, unsigned long pktlen, PACKET *packet ); -static int parse_attribute( iobuf_t inp, int pkttype, unsigned long pktlen, +static int parse_attribute( IOBUF inp, int pkttype, unsigned long pktlen, PACKET *packet ); -static int parse_comment( iobuf_t inp, int pkttype, unsigned long pktlen, +static int parse_comment( IOBUF inp, int pkttype, unsigned long pktlen, PACKET *packet ); -static void parse_trust( iobuf_t inp, int pkttype, unsigned long pktlen, +static void parse_trust( IOBUF inp, int pkttype, unsigned long pktlen, PACKET *packet ); -static int parse_plaintext( iobuf_t inp, int pkttype, unsigned long pktlen, - PACKET *packet, int new_ctb); -static int parse_compressed( iobuf_t inp, int pkttype, unsigned long pktlen, +static int parse_plaintext( IOBUF inp, int pkttype, unsigned long pktlen, + PACKET *packet, int new_ctb, int partial); +static int parse_compressed( IOBUF inp, int pkttype, unsigned long pktlen, PACKET *packet, int new_ctb ); -static int parse_encrypted( iobuf_t inp, int pkttype, unsigned long pktlen, - PACKET *packet, int new_ctb); -static int parse_mdc( iobuf_t inp, int pkttype, unsigned long pktlen, +static int parse_encrypted( IOBUF inp, int pkttype, unsigned long pktlen, + PACKET *packet, int new_ctb, int partial); +static int parse_mdc( IOBUF inp, int pkttype, unsigned long pktlen, PACKET *packet, int new_ctb); -static int parse_gpg_control( iobuf_t inp, int pkttype, unsigned long pktlen, - PACKET *packet ); +static int parse_gpg_control( IOBUF inp, int pkttype, unsigned long pktlen, + PACKET *packet, int partial ); static unsigned short -read_16(iobuf_t inp) +read_16(IOBUF inp) { unsigned short a; a = iobuf_get_noeof(inp) << 8; @@ -90,7 +89,7 @@ read_16(iobuf_t inp) } static unsigned long -read_32(iobuf_t inp) +read_32(IOBUF inp) { unsigned long a; a = iobuf_get_noeof(inp) << 24; @@ -101,12 +100,84 @@ read_32(iobuf_t inp) } +/* Read an external representation of an mpi and return the MPI. The + * external format is a 16 bit unsigned value stored in network byte + * order, giving the number of bits for the following integer. The + * integer is stored with MSB first (left padded with zeroes to align + * on a byte boundary). + */ +static gcry_mpi_t +mpi_read (iobuf_t inp, unsigned int *ret_nread, int secure) +{ + /*FIXME: Needs to be synced with gnupg14/mpi/mpicoder.c*/ + + int c, c1, c2, i; + unsigned int nbits, nbytes, nread=0; + gcry_mpi_t a = NULL; + byte *buf = NULL; + byte *p; + + if( (c = c1 = iobuf_get(inp)) == -1 ) + goto leave; + nbits = c << 8; + if( (c = c2 = iobuf_get(inp)) == -1 ) + goto leave; + nbits |= c; + if( nbits > MAX_EXTERN_MPI_BITS ) + { + log_error("mpi too large (%u bits)\n", nbits); + goto leave; + } + nread = 2; + nbytes = (nbits+7) / 8; + buf = secure? gcry_xmalloc_secure( nbytes+2 ) : gcry_xmalloc( nbytes+2 ); + p = buf; + p[0] = c1; + p[1] = c2; + for( i=0 ; i < nbytes; i++ ) + { + p[i+2] = iobuf_get(inp) & 0xff; + nread++; + } + nread += nbytes; + if( gcry_mpi_scan( &a, GCRYMPI_FMT_PGP, buf, nread, &nread ) ) + a = NULL; + + leave: + gcry_free(buf); + if( nread > *ret_nread ) + log_bug("mpi larger than packet"); + else + *ret_nread = nread; + return a; +} + + + + int set_packet_list_mode( int mode ) { int old = list_mode; list_mode = mode; - /* FIXME(gcrypt) mpi_print_mode = DBG_MPI; */ + /* FIXME(gcrypt) mpi_print_mode = DBG_MPI; */ + /* We use stdout print only if invoked by the --list-packets + command but switch to stderr in all otehr cases. This breaks + the previous behaviour but that seems to be more of a bug than + intentional. I don't believe that any application makes use of + this long standing annoying way of printing to stdout except + when doing a --list-packets. If this assumption fails, it will + be easy to add an option for the listing stream. Note that we + initialize it only once; mainly because some code may switch + the option value later back to 1 and we want to have all output + to the same stream. + + Using stderr is not actually very clean because it bypasses the + logging code but it is a special thing anyay. I am not sure + whether using log_stream() would be better. Perhaps we should + enable the list mdoe only with a special option. */ + if (!listfp) + listfp = opt.list_packets == 2 ? stdout : stderr; return old; } @@ -133,7 +204,7 @@ unknown_pubkey_warning( int algo ) */ #ifdef DEBUG_PARSE_PACKET int -dbg_parse_packet( iobuf_t inp, PACKET *pkt, const char *dbg_f, int dbg_l ) +dbg_parse_packet( IOBUF inp, PACKET *pkt, const char *dbg_f, int dbg_l ) { int skip, rc; @@ -144,7 +215,7 @@ dbg_parse_packet( iobuf_t inp, PACKET *pkt, const char *dbg_f, int dbg_l ) } #else int -parse_packet( iobuf_t inp, PACKET *pkt ) +parse_packet( IOBUF inp, PACKET *pkt ) { int skip, rc; @@ -160,7 +231,7 @@ parse_packet( iobuf_t inp, PACKET *pkt ) */ #ifdef DEBUG_PARSE_PACKET int -dbg_search_packet( iobuf_t inp, PACKET *pkt, off_t *retpos, int with_uid, +dbg_search_packet( IOBUF inp, PACKET *pkt, off_t *retpos, int with_uid, const char *dbg_f, int dbg_l ) { int skip, rc; @@ -172,7 +243,7 @@ dbg_search_packet( iobuf_t inp, PACKET *pkt, off_t *retpos, int with_uid, } #else int -search_packet( iobuf_t inp, PACKET *pkt, off_t *retpos, int with_uid ) +search_packet( IOBUF inp, PACKET *pkt, off_t *retpos, int with_uid ) { int skip, rc; @@ -188,7 +259,7 @@ search_packet( iobuf_t inp, PACKET *pkt, off_t *retpos, int with_uid ) */ #ifdef DEBUG_PARSE_PACKET int -dbg_copy_all_packets( iobuf_t inp, iobuf_t out, +dbg_copy_all_packets( IOBUF inp, IOBUF out, const char *dbg_f, int dbg_l ) { PACKET pkt; @@ -200,7 +271,7 @@ dbg_copy_all_packets( iobuf_t inp, iobuf_t out, } #else int -copy_all_packets( iobuf_t inp, iobuf_t out ) +copy_all_packets( IOBUF inp, IOBUF out ) { PACKET pkt; int skip, rc=0; @@ -217,7 +288,7 @@ copy_all_packets( iobuf_t inp, iobuf_t out ) */ #ifdef DEBUG_PARSE_PACKET int -dbg_copy_some_packets( iobuf_t inp, iobuf_t out, off_t stopoff, +dbg_copy_some_packets( IOBUF inp, IOBUF out, off_t stopoff, const char *dbg_f, int dbg_l ) { PACKET pkt; @@ -232,7 +303,7 @@ dbg_copy_some_packets( iobuf_t inp, iobuf_t out, off_t stopoff, } #else int -copy_some_packets( iobuf_t inp, iobuf_t out, off_t stopoff ) +copy_some_packets( IOBUF inp, IOBUF out, off_t stopoff ) { PACKET pkt; int skip, rc=0; @@ -250,7 +321,7 @@ copy_some_packets( iobuf_t inp, iobuf_t out, off_t stopoff ) */ #ifdef DEBUG_PARSE_PACKET int -dbg_skip_some_packets( iobuf_t inp, unsigned n, +dbg_skip_some_packets( IOBUF inp, unsigned n, const char *dbg_f, int dbg_l ) { int skip, rc=0; @@ -264,7 +335,7 @@ dbg_skip_some_packets( iobuf_t inp, unsigned n, } #else int -skip_some_packets( iobuf_t inp, unsigned n ) +skip_some_packets( IOBUF inp, unsigned n ) { int skip, rc=0; PACKET pkt; @@ -286,8 +357,8 @@ skip_some_packets( iobuf_t inp, unsigned n ) * if OUT is not NULL, a special copymode is used. */ static int -parse( iobuf_t inp, PACKET *pkt, int onlykeypkts, off_t *retpos, - int *skip, iobuf_t out, int do_skip +parse( IOBUF inp, PACKET *pkt, int onlykeypkts, off_t *retpos, + int *skip, IOBUF out, int do_skip #ifdef DEBUG_PARSE_PACKET ,const char *dbg_w, const char *dbg_f, int dbg_l #endif @@ -297,7 +368,7 @@ parse( iobuf_t inp, PACKET *pkt, int onlykeypkts, off_t *retpos, unsigned long pktlen; byte hdr[8]; int hdrlen; - int new_ctb = 0; + int new_ctb = 0, partial=0; int with_uid = (onlykeypkts == 2); *skip = 0; @@ -313,7 +384,7 @@ parse( iobuf_t inp, PACKET *pkt, int onlykeypkts, off_t *retpos, hdr[hdrlen++] = ctb; if( !(ctb & 0x80) ) { log_error("%s: invalid packet (ctb=%02x)\n", iobuf_where(inp), ctb ); - rc = GPG_ERR_INV_PACKET; + rc = gpg_error (GPG_ERR_INV_PACKET); goto leave; } pktlen = 0; @@ -322,81 +393,94 @@ parse( iobuf_t inp, PACKET *pkt, int onlykeypkts, off_t *retpos, pkttype = ctb & 0x3f; if( (c = iobuf_get(inp)) == -1 ) { log_error("%s: 1st length byte missing\n", iobuf_where(inp) ); - rc = GPG_ERR_INV_PACKET; + rc = gpg_error (GPG_ERR_INV_PACKET); goto leave; } if (pkttype == PKT_COMPRESSED) { iobuf_set_partial_block_mode(inp, c & 0xff); pktlen = 0;/* to indicate partial length */ + partial=1; } else { hdr[hdrlen++] = c; if( c < 192 ) - pktlen = c; - else if( c < 224 ) { - pktlen = (c - 192) * 256; - if( (c = iobuf_get(inp)) == -1 ) { - log_error("%s: 2nd length byte missing\n", - iobuf_where(inp) ); - rc = GPG_ERR_INV_PACKET; - goto leave; - } - hdr[hdrlen++] = c; - pktlen += c + 192; - } - else if( c == 255 ) { - pktlen = (hdr[hdrlen++] = iobuf_get_noeof(inp)) << 24; - pktlen |= (hdr[hdrlen++] = iobuf_get_noeof(inp)) << 16; - pktlen |= (hdr[hdrlen++] = iobuf_get_noeof(inp)) << 8; - if( (c = iobuf_get(inp)) == -1 ) { - log_error("%s: 4 byte length invalid\n", - iobuf_where(inp) ); - rc = GPG_ERR_INV_PACKET; - goto leave; - } - pktlen |= (hdr[hdrlen++] = c ); - } - else { /* partial body length */ - iobuf_set_partial_block_mode(inp, c & 0xff); - pktlen = 0;/* to indicate partial length */ - } + pktlen = c; + else if( c < 224 ) + { + pktlen = (c - 192) * 256; + if( (c = iobuf_get(inp)) == -1 ) + { + log_error("%s: 2nd length byte missing\n", + iobuf_where(inp) ); + rc = gpg_error (GPG_ERR_INV_PACKET); + goto leave; + } + hdr[hdrlen++] = c; + pktlen += c + 192; + } + else if( c == 255 ) + { + pktlen = (hdr[hdrlen++] = iobuf_get_noeof(inp)) << 24; + pktlen |= (hdr[hdrlen++] = iobuf_get_noeof(inp)) << 16; + pktlen |= (hdr[hdrlen++] = iobuf_get_noeof(inp)) << 8; + if( (c = iobuf_get(inp)) == -1 ) + { + log_error("%s: 4 byte length invalid\n", + iobuf_where(inp) ); + rc = gpg_error (GPG_ERR_INV_PACKET); + goto leave; + } + pktlen |= (hdr[hdrlen++] = c ); + } + else + { + /* Partial body length. Note that we handled + PKT_COMPRESSED earlier. */ + if(pkttype==PKT_PLAINTEXT || pkttype==PKT_ENCRYPTED + || pkttype==PKT_ENCRYPTED_MDC) + { + iobuf_set_partial_block_mode(inp, c & 0xff); + pktlen = 0;/* to indicate partial length */ + partial=1; + } + else + { + log_error("%s: partial length for invalid" + " packet type %d\n",iobuf_where(inp),pkttype); + rc = gpg_error (GPG_ERR_INV_PACKET); + goto leave; + } + } } } - else { + else + { pkttype = (ctb>>2)&0xf; lenbytes = ((ctb&3)==3)? 0 : (1<<(ctb & 3)); - if( !lenbytes ) { + if( !lenbytes ) + { pktlen = 0; /* don't know the value */ - switch (pkttype) { - case PKT_ENCRYPTED: - case PKT_PLAINTEXT: - /* These partial length encodings are from an very - early GnuPG release and deprecated. However we - still support them read-wise. Note, that we should - not allow them for any key related packets, because - this might render a keyring unusable if an errenous - packet indicated this mode but not complying to it - gets imported. */ - iobuf_set_block_mode(inp, 1); - break; - - case PKT_COMPRESSED: - break; /* the orginal pgp 2 way. */ - - default: - log_error ("%s: old style partial length " - "for invalid packet type\n", iobuf_where(inp) ); + /* This isn't really partial, but we can treat it the same + in a "read until the end" sort of way. */ + partial=1; + if(pkttype!=PKT_ENCRYPTED && pkttype!=PKT_PLAINTEXT + && pkttype!=PKT_COMPRESSED) + { + log_error ("%s: indeterminate length for invalid" + " packet type %d\n", iobuf_where(inp), pkttype ); rc = gpg_error (GPG_ERR_INV_PACKET); - goto leave; - } - } - else { - for( ; lenbytes; lenbytes-- ) { + goto leave; + } + } + else + { + for( ; lenbytes; lenbytes-- ) + { pktlen <<= 8; pktlen |= hdr[hdrlen++] = iobuf_get_noeof(inp); - } - } - } + } + } + } if (pktlen == 0xffffffff) { /* with a some probability this is caused by a problem in the @@ -407,9 +491,10 @@ parse( iobuf_t inp, PACKET *pkt, int onlykeypkts, off_t *retpos, } if( out && pkttype ) { - rc = iobuf_write( out, hdr, hdrlen ); - if (!rc) - rc = copy_packet(inp, out, pkttype, pktlen ); + if( iobuf_write( out, hdr, hdrlen ) == -1 ) + rc = G10ERR_WRITE_FILE; + else + rc = copy_packet(inp, out, pkttype, pktlen, partial ); goto leave; } @@ -421,7 +506,7 @@ parse( iobuf_t inp, PACKET *pkt, int onlykeypkts, off_t *retpos, && pkttype != PKT_PUBLIC_KEY && pkttype != PKT_SECRET_SUBKEY && pkttype != PKT_SECRET_KEY ) ) { - skip_rest(inp, pktlen); + iobuf_skip_rest(inp, pktlen, partial); *skip = 1; rc = 0; goto leave; @@ -438,16 +523,16 @@ parse( iobuf_t inp, PACKET *pkt, int onlykeypkts, off_t *retpos, #endif } pkt->pkttype = pkttype; - rc = GPG_ERR_UNKNOWN_PACKET; /* default error */ + rc = G10ERR_UNKNOWN_PACKET; /* default error */ switch( pkttype ) { case PKT_PUBLIC_KEY: case PKT_PUBLIC_SUBKEY: - pkt->pkt.public_key = xcalloc (1,sizeof *pkt->pkt.public_key ); + pkt->pkt.public_key = xmalloc_clear(sizeof *pkt->pkt.public_key ); rc = parse_key(inp, pkttype, pktlen, hdr, hdrlen, pkt ); break; case PKT_SECRET_KEY: case PKT_SECRET_SUBKEY: - pkt->pkt.secret_key = xcalloc (1,sizeof *pkt->pkt.secret_key ); + pkt->pkt.secret_key = xmalloc_clear(sizeof *pkt->pkt.secret_key ); rc = parse_key(inp, pkttype, pktlen, hdr, hdrlen, pkt ); break; case PKT_SYMKEY_ENC: @@ -457,11 +542,11 @@ parse( iobuf_t inp, PACKET *pkt, int onlykeypkts, off_t *retpos, rc = parse_pubkeyenc(inp, pkttype, pktlen, pkt ); break; case PKT_SIGNATURE: - pkt->pkt.signature = xcalloc (1,sizeof *pkt->pkt.signature ); + pkt->pkt.signature = xmalloc_clear(sizeof *pkt->pkt.signature ); rc = parse_signature(inp, pkttype, pktlen, pkt->pkt.signature ); break; case PKT_ONEPASS_SIG: - pkt->pkt.onepass_sig = xcalloc (1,sizeof *pkt->pkt.onepass_sig ); + pkt->pkt.onepass_sig = xmalloc_clear(sizeof *pkt->pkt.onepass_sig ); rc = parse_onepass_sig(inp, pkttype, pktlen, pkt->pkt.onepass_sig ); break; case PKT_USER_ID: @@ -480,29 +565,29 @@ parse( iobuf_t inp, PACKET *pkt, int onlykeypkts, off_t *retpos, rc = 0; break; case PKT_PLAINTEXT: - rc = parse_plaintext(inp, pkttype, pktlen, pkt, new_ctb ); + rc = parse_plaintext(inp, pkttype, pktlen, pkt, new_ctb, partial ); break; case PKT_COMPRESSED: rc = parse_compressed(inp, pkttype, pktlen, pkt, new_ctb ); break; case PKT_ENCRYPTED: case PKT_ENCRYPTED_MDC: - rc = parse_encrypted(inp, pkttype, pktlen, pkt, new_ctb ); + rc = parse_encrypted(inp, pkttype, pktlen, pkt, new_ctb, partial ); break; case PKT_MDC: rc = parse_mdc(inp, pkttype, pktlen, pkt, new_ctb ); break; case PKT_GPG_CONTROL: - rc = parse_gpg_control(inp, pkttype, pktlen, pkt ); + rc = parse_gpg_control(inp, pkttype, pktlen, pkt, partial ); break; default: - skip_packet(inp, pkttype, pktlen); + skip_packet(inp, pkttype, pktlen, partial); break; } leave: if( !rc && iobuf_error(inp) ) - rc = GPG_ERR_INV_KEYRING; + rc = G10ERR_INV_KEYRING; return rc; } @@ -511,34 +596,36 @@ dump_hex_line( int c, int *i ) { if( *i && !(*i%8) ) { if( *i && !(*i%24) ) - printf("\n%4d:", *i ); + fprintf (listfp, "\n%4d:", *i ); else - putchar(' '); + putc (' ', listfp); } if( c == -1 ) - printf(" EOF" ); + fprintf (listfp, " EOF" ); else - printf(" %02x", c ); + fprintf (listfp, " %02x", c ); ++*i; } static int -copy_packet( iobuf_t inp, iobuf_t out, int pkttype, unsigned long pktlen ) +copy_packet( IOBUF inp, IOBUF out, int pkttype, + unsigned long pktlen, int partial ) { - int rc, n; + int rc; + int n; char buf[100]; - if( iobuf_in_block_mode(inp) ) { + if( partial ) { while( (n = iobuf_read( inp, buf, 100 )) != -1 ) - if( (rc = iobuf_write(out, buf, n )) ) + if( (rc=iobuf_write(out, buf, n )) ) return rc; /* write error */ } else if( !pktlen && pkttype == PKT_COMPRESSED ) { log_debug("copy_packet: compressed!\n"); /* compressed packet, copy till EOF */ while( (n = iobuf_read( inp, buf, 100 )) != -1 ) - if( (rc = iobuf_write(out, buf, n )) ) + if( (rc=iobuf_write(out, buf, n )) ) return rc; /* write error */ } else { @@ -546,8 +633,8 @@ copy_packet( iobuf_t inp, iobuf_t out, int pkttype, unsigned long pktlen ) n = pktlen > 100 ? 100 : pktlen; n = iobuf_read( inp, buf, n ); if( n == -1 ) - return GPG_ERR_GENERAL; /* FIXME(gcrypt): read error*/; - if( (rc = iobuf_write(out, buf, n )) ) + return gpg_error (GPG_ERR_EOF); + if( (rc=iobuf_write(out, buf, n )) ) return rc; /* write error */ } } @@ -556,18 +643,19 @@ copy_packet( iobuf_t inp, iobuf_t out, int pkttype, unsigned long pktlen ) static void -skip_packet( iobuf_t inp, int pkttype, unsigned long pktlen ) +skip_packet( IOBUF inp, int pkttype, unsigned long pktlen, int partial ) { if( list_mode ) { if( pkttype == PKT_MARKER ) - fputs(":marker packet:\n", stdout ); + fputs(":marker packet:\n", listfp ); else - printf(":unknown packet: type %2d, length %lu\n", pkttype, pktlen); + fprintf (listfp, ":unknown packet: type %2d, length %lu\n", + pkttype, pktlen); if( pkttype ) { int c, i=0 ; if( pkttype != PKT_MARKER ) - fputs("dump:", stdout ); - if( iobuf_in_block_mode(inp) ) { + fputs("dump:", listfp ); + if( partial ) { while( (c=iobuf_get(inp)) != -1 ) dump_hex_line(c, &i); } @@ -575,40 +663,26 @@ skip_packet( iobuf_t inp, int pkttype, unsigned long pktlen ) for( ; pktlen; pktlen-- ) dump_hex_line(iobuf_get(inp), &i); } - putchar('\n'); + putc ('\n', listfp); return; } } - skip_rest(inp,pktlen); -} - -static void -skip_rest( iobuf_t inp, unsigned long pktlen ) -{ - if( iobuf_in_block_mode(inp) ) { - while( iobuf_get(inp) != -1 ) - ; - } - else { - for( ; pktlen; pktlen-- ) - if( iobuf_get(inp) == -1 ) - break; - } + iobuf_skip_rest(inp,pktlen,partial); } static void * -read_rest( iobuf_t inp, size_t pktlen ) +read_rest( IOBUF inp, size_t pktlen, int partial ) { byte *p; int i; - if( iobuf_in_block_mode(inp) ) { + if( partial ) { log_error("read_rest: can't store stream data\n"); p = NULL; } else { - p = xmalloc ( pktlen ); + p = xmalloc( pktlen ); for(i=0; pktlen; pktlen--, i++ ) p[i] = iobuf_get(inp); } @@ -618,7 +692,7 @@ read_rest( iobuf_t inp, size_t pktlen ) static int -parse_symkeyenc( iobuf_t inp, int pkttype, unsigned long pktlen, PACKET *packet ) +parse_symkeyenc( IOBUF inp, int pkttype, unsigned long pktlen, PACKET *packet ) { PKT_symkey_enc *k; int rc = 0; @@ -626,18 +700,18 @@ parse_symkeyenc( iobuf_t inp, int pkttype, unsigned long pktlen, PACKET *packet if( pktlen < 4 ) { log_error("packet(%d) too short\n", pkttype); - rc = GPG_ERR_INV_PACKET; + rc = gpg_error (GPG_ERR_INV_PACKET); goto leave; } version = iobuf_get_noeof(inp); pktlen--; if( version != 4 ) { log_error("packet(%d) with unknown version %d\n", pkttype, version); - rc = GPG_ERR_INV_PACKET; + rc = gpg_error (GPG_ERR_INV_PACKET); goto leave; } if( pktlen > 200 ) { /* (we encode the seskeylen in a byte) */ log_error("packet(%d) too large\n", pkttype); - rc = GPG_ERR_INV_PACKET; + rc = gpg_error (GPG_ERR_INV_PACKET); goto leave; } cipher_algo = iobuf_get_noeof(inp); pktlen--; @@ -659,11 +733,11 @@ parse_symkeyenc( iobuf_t inp, int pkttype, unsigned long pktlen, PACKET *packet } if( minlen > pktlen ) { log_error("packet with S2K %d too short\n", s2kmode ); - rc = GPG_ERR_INV_PACKET; + rc = gpg_error (GPG_ERR_INV_PACKET); goto leave; } seskeylen = pktlen - minlen; - k = packet->pkt.symkey_enc = xcalloc (1, sizeof *packet->pkt.symkey_enc + k = packet->pkt.symkey_enc = xmalloc_clear( sizeof *packet->pkt.symkey_enc + seskeylen - 1 ); k->version = version; k->cipher_algo = cipher_algo; @@ -677,46 +751,59 @@ parse_symkeyenc( iobuf_t inp, int pkttype, unsigned long pktlen, PACKET *packet k->s2k.count = iobuf_get(inp); pktlen--; } k->seskeylen = seskeylen; - for(i=0; i < seskeylen && pktlen; i++, pktlen-- ) - k->seskey[i] = iobuf_get_noeof(inp); + if(k->seskeylen) + { + for(i=0; i < seskeylen && pktlen; i++, pktlen-- ) + k->seskey[i] = iobuf_get_noeof(inp); + + /* What we're watching out for here is a session key decryptor + with no salt. The RFC says that using salt for this is a + MUST. */ + if(s2kmode!=1 && s2kmode!=3) + log_info(_("WARNING: potentially insecure symmetrically" + " encrypted session key\n")); + } assert( !pktlen ); if( list_mode ) { - printf(":symkey enc packet: version %d, cipher %d, s2k %d, hash %d\n", - version, cipher_algo, s2kmode, hash_algo); + fprintf (listfp, ":symkey enc packet: version %d, cipher %d, s2k %d, hash %d", + version, cipher_algo, s2kmode, hash_algo); + if(seskeylen) + fprintf (listfp, ", seskey %d bits",(seskeylen-1)*8); + fprintf (listfp, "\n"); if( s2kmode == 1 || s2kmode == 3 ) { - printf("\tsalt "); + fprintf (listfp, "\tsalt "); for(i=0; i < 8; i++ ) - printf("%02x", k->s2k.salt[i]); + fprintf (listfp, "%02x", k->s2k.salt[i]); if( s2kmode == 3 ) - printf(", count %lu\n", (ulong)k->s2k.count ); - printf("\n"); + fprintf (listfp, ", count %lu", (ulong)k->s2k.count ); + fprintf (listfp, "\n"); } } leave: - skip_rest(inp, pktlen); + iobuf_skip_rest(inp, pktlen, 0); return rc; } static int -parse_pubkeyenc( iobuf_t inp, int pkttype, unsigned long pktlen, PACKET *packet ) +parse_pubkeyenc( IOBUF inp, int pkttype, unsigned long pktlen, PACKET *packet ) { unsigned int n; int rc = 0; int i, ndata; PKT_pubkey_enc *k; - k = packet->pkt.pubkey_enc = xcalloc (1,sizeof *packet->pkt.pubkey_enc); + k = packet->pkt.pubkey_enc = xmalloc_clear(sizeof *packet->pkt.pubkey_enc); if( pktlen < 12 ) { log_error("packet(%d) too short\n", pkttype); - rc = GPG_ERR_INV_PACKET; + rc = gpg_error (GPG_ERR_INV_PACKET); goto leave; } k->version = iobuf_get_noeof(inp); pktlen--; if( k->version != 2 && k->version != 3 ) { log_error("packet(%d) with unknown version %d\n", pkttype, k->version); - rc = GPG_ERR_INV_PACKET; + rc = gpg_error (GPG_ERR_INV_PACKET); goto leave; } k->keyid[0] = read_32(inp); pktlen -= 4; @@ -724,13 +811,13 @@ parse_pubkeyenc( iobuf_t inp, int pkttype, unsigned long pktlen, PACKET *packet k->pubkey_algo = iobuf_get_noeof(inp); pktlen--; k->throw_keyid = 0; /* only used as flag for build_packet */ if( list_mode ) - printf(":pubkey enc packet: version %d, algo %d, keyid %08lX%08lX\n", + fprintf (listfp, ":pubkey enc packet: version %d, algo %d, keyid %08lX%08lX\n", k->version, k->pubkey_algo, (ulong)k->keyid[0], (ulong)k->keyid[1]); ndata = pubkey_get_nenc(k->pubkey_algo); if( !ndata ) { if( list_mode ) - printf("\tunsupported algorithm %d\n", k->pubkey_algo ); + fprintf (listfp, "\tunsupported algorithm %d\n", k->pubkey_algo ); unknown_pubkey_warning( k->pubkey_algo ); k->data[0] = NULL; /* no need to store the encrypted data */ } @@ -739,17 +826,17 @@ parse_pubkeyenc( iobuf_t inp, int pkttype, unsigned long pktlen, PACKET *packet n = pktlen; k->data[i] = mpi_read(inp, &n, 0); pktlen -=n; if( list_mode ) { - printf("\tdata: "); - mpi_print(stdout, k->data[i], mpi_print_mode ); - putchar('\n'); + fprintf (listfp, "\tdata: "); + mpi_print(listfp, k->data[i], mpi_print_mode ); + putc ('\n', listfp); } if (!k->data[i]) - rc = GPG_ERR_INV_PACKET; + rc = gpg_error (GPG_ERR_INV_PACKET); } } leave: - skip_rest(inp, pktlen); + iobuf_skip_rest(inp, pktlen, 0); return rc; } @@ -765,82 +852,83 @@ dump_sig_subpkt( int hashed, int type, int critical, * 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 ); + fprintf (listfp, + "\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 ); } buffer++; length--; - printf("\t%s%ssubpkt %d len %u (", /*)*/ + fprintf (listfp, "\t%s%ssubpkt %d len %u (", /*)*/ critical ? "critical ":"", hashed ? "hashed ":"", type, (unsigned)length ); if( length > buflen ) { - printf("too short: buffer is only %u)\n", (unsigned)buflen ); + fprintf (listfp, "too short: buffer is only %u)\n", (unsigned)buflen ); return; } switch( type ) { case SIGSUBPKT_SIG_CREATED: if( length >= 4 ) - printf("sig created %s", strtimestamp( buffer_to_u32(buffer) ) ); + fprintf (listfp, "sig created %s", strtimestamp( buffer_to_u32(buffer) ) ); break; case SIGSUBPKT_SIG_EXPIRE: if( length >= 4 ) - printf("sig expires after %s", + fprintf (listfp, "sig expires after %s", strtimevalue( buffer_to_u32(buffer) ) ); break; case SIGSUBPKT_EXPORTABLE: if( length ) - printf("%sexportable", *buffer? "":"not "); + fprintf (listfp, "%sexportable", *buffer? "":"not "); break; case SIGSUBPKT_TRUST: if(length!=2) p="[invalid trust subpacket]"; else - printf("trust signature of depth %d, value %d",buffer[0],buffer[1]); + fprintf (listfp, "trust signature of depth %d, value %d",buffer[0],buffer[1]); break; case SIGSUBPKT_REGEXP: if(!length) p="[invalid regexp subpacket]"; else - printf("regular expression: \"%s\"",buffer); + fprintf (listfp, "regular expression: \"%s\"",buffer); break; case SIGSUBPKT_REVOCABLE: if( length ) - printf("%srevocable", *buffer? "":"not "); + fprintf (listfp, "%srevocable", *buffer? "":"not "); break; case SIGSUBPKT_KEY_EXPIRE: if( length >= 4 ) - printf("key expires after %s", + fprintf (listfp, "key expires after %s", strtimevalue( buffer_to_u32(buffer) ) ); break; case SIGSUBPKT_PREF_SYM: - fputs("pref-sym-algos:", stdout ); + fputs("pref-sym-algos:", listfp ); for( i=0; i < length; i++ ) - printf(" %d", buffer[i] ); + fprintf (listfp, " %d", buffer[i] ); break; case SIGSUBPKT_REV_KEY: - fputs("revocation key: ", stdout ); + fputs("revocation key: ", listfp ); if( length < 22 ) p = "[too short]"; else { - printf("c=%02x a=%d f=", buffer[0], buffer[1] ); + fprintf (listfp, "c=%02x a=%d f=", buffer[0], buffer[1] ); for( i=2; i < length; i++ ) - printf("%02X", buffer[i] ); + fprintf (listfp, "%02X", buffer[i] ); } break; case SIGSUBPKT_ISSUER: if( length >= 8 ) - printf("issuer key ID %08lX%08lX", + fprintf (listfp, "issuer key ID %08lX%08lX", (ulong)buffer_to_u32(buffer), (ulong)buffer_to_u32(buffer+4) ); break; case SIGSUBPKT_NOTATION: { - fputs("notation: ", stdout ); + fputs("notation: ", listfp ); if( length < 8 ) p = "[too short]"; else { @@ -853,11 +941,11 @@ dump_sig_subpkt( int hashed, int type, int critical, if( 8+n1+n2 != length ) p = "[error]"; else { - print_string( stdout, s, n1, ')' ); - putc( '=', stdout ); + print_string( listfp, s, n1, ')' ); + putc( '=', listfp ); if( *buffer & 0x80 ) - print_string( stdout, s+n1, n2, ')' ); + print_string( listfp, s+n1, n2, ')' ); else p = "[not human readable]"; } @@ -865,60 +953,71 @@ dump_sig_subpkt( int hashed, int type, int critical, } break; case SIGSUBPKT_PREF_HASH: - fputs("pref-hash-algos:", stdout ); + fputs("pref-hash-algos:", listfp ); for( i=0; i < length; i++ ) - printf(" %d", buffer[i] ); + fprintf (listfp, " %d", buffer[i] ); break; case SIGSUBPKT_PREF_COMPR: - fputs("pref-zip-algos:", stdout ); + fputs("pref-zip-algos:", listfp ); for( i=0; i < length; i++ ) - printf(" %d", buffer[i] ); + fprintf (listfp, " %d", buffer[i] ); break; case SIGSUBPKT_KS_FLAGS: - fputs("key server preferences:",stdout); + fputs("key server preferences:",listfp); for(i=0;i<length;i++) - printf(" %02X", buffer[i]); + fprintf (listfp, " %02X", buffer[i]); break; case SIGSUBPKT_PREF_KS: - fputs("preferred key server: ", stdout ); - print_string( stdout, buffer, length, ')' ); + fputs("preferred key server: ", listfp ); + print_string( listfp, buffer, length, ')' ); break; case SIGSUBPKT_PRIMARY_UID: p = "primary user ID"; break; case SIGSUBPKT_POLICY: - fputs("policy: ", stdout ); - print_string( stdout, buffer, length, ')' ); + fputs("policy: ", listfp ); + print_string( listfp, buffer, length, ')' ); break; case SIGSUBPKT_KEY_FLAGS: - fputs ( "key flags:", stdout ); + fputs ( "key flags:", listfp ); for( i=0; i < length; i++ ) - printf(" %02X", buffer[i] ); + fprintf (listfp, " %02X", buffer[i] ); break; case SIGSUBPKT_SIGNERS_UID: p = "signer's user ID"; break; case SIGSUBPKT_REVOC_REASON: if( length ) { - printf("revocation reason 0x%02x (", *buffer ); - print_string( stdout, buffer+1, length-1, ')' ); + fprintf (listfp, "revocation reason 0x%02x (", *buffer ); + print_string( listfp, buffer+1, length-1, ')' ); p = ")"; } break; case SIGSUBPKT_ARR: - fputs("Big Brother's key (ignored): ", stdout ); + fputs("Big Brother's key (ignored): ", listfp ); if( length < 22 ) p = "[too short]"; else { - printf("c=%02x a=%d f=", buffer[0], buffer[1] ); + fprintf (listfp, "c=%02x a=%d f=", buffer[0], buffer[1] ); for( i=2; i < length; i++ ) - printf("%02X", buffer[i] ); + fprintf (listfp, "%02X", buffer[i] ); } break; case SIGSUBPKT_FEATURES: - fputs ( "features:", stdout ); + fputs ( "features:", listfp ); for( i=0; i < length; i++ ) - printf(" %02x", buffer[i] ); + fprintf (listfp, " %02x", buffer[i] ); + break; + case SIGSUBPKT_SIGNATURE: + fputs("signature: ",listfp); + if(length<17) + p="[too short]"; + else + fprintf (listfp, "v%d, class 0x%02X, algo %d, digest algo %d", + buffer[0], + buffer[0]==3?buffer[2]:buffer[1], + buffer[0]==3?buffer[15]:buffer[2], + buffer[0]==3?buffer[16]:buffer[3]); break; default: if(type>=100 && type<=110) @@ -928,101 +1027,115 @@ dump_sig_subpkt( int hashed, int type, int critical, break; } - printf("%s)\n", p? p: ""); + fprintf (listfp, "%s)\n", p? p: ""); } /**************** - * Returns: >= 0 offset into buffer - * -1 unknown type - * -2 unsupported type - * -3 subpacket too short + * Returns: >= 0 use this offset into buffer + * -1 explicitly reject returning this type + * -2 subpacket too short */ int parse_one_sig_subpkt( const byte *buffer, size_t n, int type ) { - switch( type ) { - case SIGSUBPKT_REV_KEY: - if(n < 22) - break; - return 0; - case SIGSUBPKT_SIG_CREATED: - case SIGSUBPKT_SIG_EXPIRE: - case SIGSUBPKT_KEY_EXPIRE: - if( n < 4 ) - break; - return 0; - case SIGSUBPKT_KEY_FLAGS: - case SIGSUBPKT_KS_FLAGS: - case SIGSUBPKT_PREF_SYM: - case SIGSUBPKT_PREF_HASH: - case SIGSUBPKT_PREF_COMPR: - case SIGSUBPKT_POLICY: - case SIGSUBPKT_PREF_KS: - case SIGSUBPKT_FEATURES: - case SIGSUBPKT_REGEXP: - return 0; - case SIGSUBPKT_EXPORTABLE: - case SIGSUBPKT_REVOCABLE: - if( !n ) - break; - return 0; - case SIGSUBPKT_ISSUER: /* issuer key ID */ - if( n < 8 ) - break; - return 0; - case SIGSUBPKT_NOTATION: - if( n < 8 ) /* minimum length needed */ - break; - return 0; - case SIGSUBPKT_REVOC_REASON: - if( !n ) - break; - return 0; - case SIGSUBPKT_PRIMARY_UID: - if ( n != 1 ) - break; - return 0; - case SIGSUBPKT_TRUST: - if ( n != 2 ) - break; - return 0; - default: return -1; + switch( type ) + { + case SIGSUBPKT_REV_KEY: + if(n < 22) + break; + return 0; + case SIGSUBPKT_SIG_CREATED: + case SIGSUBPKT_SIG_EXPIRE: + case SIGSUBPKT_KEY_EXPIRE: + if( n < 4 ) + break; + return 0; + case SIGSUBPKT_KEY_FLAGS: + case SIGSUBPKT_KS_FLAGS: + case SIGSUBPKT_PREF_SYM: + case SIGSUBPKT_PREF_HASH: + case SIGSUBPKT_PREF_COMPR: + case SIGSUBPKT_POLICY: + case SIGSUBPKT_PREF_KS: + case SIGSUBPKT_FEATURES: + case SIGSUBPKT_REGEXP: + return 0; + case SIGSUBPKT_SIGNATURE: + case SIGSUBPKT_EXPORTABLE: + case SIGSUBPKT_REVOCABLE: + case SIGSUBPKT_REVOC_REASON: + if( !n ) + break; + return 0; + case SIGSUBPKT_ISSUER: /* issuer key ID */ + if( n < 8 ) + break; + return 0; + case SIGSUBPKT_NOTATION: + /* minimum length needed, and the subpacket must be well-formed + where the name length and value length all fit inside the + packet. */ + if(n<8 || 8+((buffer[4]<<8)|buffer[5])+((buffer[6]<<8)|buffer[7]) != n) + break; + return 0; + case SIGSUBPKT_PRIMARY_UID: + if ( n != 1 ) + break; + return 0; + case SIGSUBPKT_TRUST: + if ( n != 2 ) + break; + return 0; + default: return 0; } - return -3; + return -2; } - +/* Not many critical notations we understand yet... */ static int -can_handle_critical( const byte *buffer, size_t n, int type ) +can_handle_critical_notation(const byte *name,size_t len) { - switch( type ) { - case SIGSUBPKT_NOTATION: - if( n >= 8 && (*buffer & 0x80) ) - return 1; /* human readable is handled */ - return 0; + if(len==32 && memcmp(name,"preferred-email-encoding@pgp.com",32)==0) + return 1; + if(len==21 && memcmp(name,"pka-address@gnupg.org",21)==0) + return 1; - case SIGSUBPKT_SIG_CREATED: - case SIGSUBPKT_SIG_EXPIRE: - case SIGSUBPKT_KEY_EXPIRE: - case SIGSUBPKT_EXPORTABLE: - case SIGSUBPKT_REVOCABLE: - case SIGSUBPKT_REV_KEY: - case SIGSUBPKT_ISSUER:/* issuer key ID */ - case SIGSUBPKT_PREF_SYM: - case SIGSUBPKT_PREF_HASH: - case SIGSUBPKT_PREF_COMPR: - case SIGSUBPKT_KEY_FLAGS: - case SIGSUBPKT_PRIMARY_UID: - case SIGSUBPKT_FEATURES: - case SIGSUBPKT_TRUST: - case SIGSUBPKT_REGEXP: - /* Is it enough to show the policy or keyserver? */ - case SIGSUBPKT_POLICY: - case SIGSUBPKT_PREF_KS: - return 1; + return 0; +} - default: +static int +can_handle_critical( const byte *buffer, size_t n, int type ) +{ + switch( type ) + { + case SIGSUBPKT_NOTATION: + if(n>=8) + return can_handle_critical_notation(buffer+8,(buffer[4]<<8)|buffer[5]); + else return 0; + case SIGSUBPKT_SIGNATURE: + case SIGSUBPKT_SIG_CREATED: + case SIGSUBPKT_SIG_EXPIRE: + case SIGSUBPKT_KEY_EXPIRE: + case SIGSUBPKT_EXPORTABLE: + case SIGSUBPKT_REVOCABLE: + case SIGSUBPKT_REV_KEY: + case SIGSUBPKT_ISSUER:/* issuer key ID */ + case SIGSUBPKT_PREF_SYM: + case SIGSUBPKT_PREF_HASH: + case SIGSUBPKT_PREF_COMPR: + case SIGSUBPKT_KEY_FLAGS: + case SIGSUBPKT_PRIMARY_UID: + case SIGSUBPKT_FEATURES: + case SIGSUBPKT_TRUST: + case SIGSUBPKT_REGEXP: + /* Is it enough to show the policy or keyserver? */ + case SIGSUBPKT_POLICY: + case SIGSUBPKT_PREF_KS: + return 1; + + default: + return 0; } } @@ -1106,13 +1219,11 @@ enum_sig_subpkt( const subpktarea_t *pktbuf, sigsubpkttype_t reqtype, *ret_n = n; offset = parse_one_sig_subpkt(buffer, n, type ); switch( offset ) { - case -3: - log_error("subpacket of type %d too short\n", type); - return NULL; case -2: + log_error("subpacket of type %d too short\n", type); return NULL; case -1: - BUG(); /* not yet needed */ + return NULL; default: break; } @@ -1130,7 +1241,8 @@ enum_sig_subpkt( const subpktarea_t *pktbuf, sigsubpkttype_t reqtype, return NULL; /* end of packets; not found */ too_short: - log_error("buffer shorter than subpacket\n"); + if(opt.verbose) + log_info("buffer shorter than subpacket\n"); if( start ) *start = -1; return NULL; @@ -1182,8 +1294,8 @@ void parse_revkeys(PKT_signature *sig) } } -static int -parse_signature( iobuf_t inp, int pkttype, unsigned long pktlen, +int +parse_signature( IOBUF inp, int pkttype, unsigned long pktlen, PKT_signature *sig ) { int md5_len=0; @@ -1200,8 +1312,9 @@ parse_signature( iobuf_t inp, int pkttype, unsigned long pktlen, if( sig->version == 4 ) is_v4=1; else if( sig->version != 2 && sig->version != 3 ) { - log_error("packet(%d) with unknown version %d\n", pkttype, sig->version); - rc = GPG_ERR_INV_PACKET; + log_error("packet(%d) with unknown version %d\n", + pkttype, sig->version); + rc = gpg_error (GPG_ERR_INV_PACKET); goto leave; } @@ -1222,7 +1335,7 @@ parse_signature( iobuf_t inp, int pkttype, unsigned long pktlen, n = read_16(inp); pktlen -= 2; /* length of hashed data */ if( n > 10000 ) { log_error("signature packet: hashed data too long\n"); - rc = GPG_ERR_INV_PACKET; + rc = G10ERR_INVALID_PACKET; goto leave; } if( n ) { @@ -1240,7 +1353,7 @@ parse_signature( iobuf_t inp, int pkttype, unsigned long pktlen, n = read_16(inp); pktlen -= 2; /* length of unhashed data */ if( n > 10000 ) { log_error("signature packet: unhashed data too long\n"); - rc = GPG_ERR_INV_PACKET; + rc = G10ERR_INVALID_PACKET; goto leave; } if( n ) { @@ -1259,46 +1372,47 @@ parse_signature( iobuf_t inp, int pkttype, unsigned long pktlen, if( pktlen < 5 ) { /* sanity check */ log_error("packet(%d) too short\n", pkttype); - rc = GPG_ERR_INV_PACKET; + rc = G10ERR_INVALID_PACKET; goto leave; } sig->digest_start[0] = iobuf_get_noeof(inp); pktlen--; sig->digest_start[1] = iobuf_get_noeof(inp); pktlen--; - if( is_v4 && sig->pubkey_algo ) { /*extract required information */ + if( is_v4 && sig->pubkey_algo ) + { /*extract required information */ const byte *p; size_t len; /* set sig->flags.unknown_critical if there is a * critical bit set for packets which we do not understand */ if( !parse_sig_subpkt (sig->hashed, SIGSUBPKT_TEST_CRITICAL, NULL) - || !parse_sig_subpkt (sig->unhashed, SIGSUBPKT_TEST_CRITICAL, - NULL) ) - { - sig->flags.unknown_critical = 1; - } + || !parse_sig_subpkt (sig->unhashed, SIGSUBPKT_TEST_CRITICAL, + NULL) ) + sig->flags.unknown_critical = 1; p = parse_sig_subpkt (sig->hashed, SIGSUBPKT_SIG_CREATED, NULL ); if(p) sig->timestamp = buffer_to_u32(p); - else if(!(sig->pubkey_algo>=100 && sig->pubkey_algo<=110)) - log_error("signature packet without timestamp\n"); + else if(!(sig->pubkey_algo>=100 && sig->pubkey_algo<=110) + && opt.verbose) + log_info ("signature packet without timestamp\n"); p = parse_sig_subpkt2( sig, SIGSUBPKT_ISSUER, NULL ); - if( p ) - { - sig->keyid[0] = buffer_to_u32(p); - sig->keyid[1] = buffer_to_u32(p+4); - } - else if(!(sig->pubkey_algo>=100 && sig->pubkey_algo<=110)) - log_error("signature packet without keyid\n"); + if(p) + { + sig->keyid[0] = buffer_to_u32(p); + sig->keyid[1] = buffer_to_u32(p+4); + } + else if(!(sig->pubkey_algo>=100 && sig->pubkey_algo<=110) + && opt.verbose) + log_info ("signature packet without keyid\n"); p=parse_sig_subpkt(sig->hashed,SIGSUBPKT_SIG_EXPIRE,NULL); if(p) sig->expiredate=sig->timestamp+buffer_to_u32(p); if(sig->expiredate && sig->expiredate<=make_timestamp()) - sig->flags.expired=1; + sig->flags.expired=1; p=parse_sig_subpkt(sig->hashed,SIGSUBPKT_POLICY,NULL); if(p) @@ -1345,10 +1459,10 @@ parse_signature( iobuf_t inp, int pkttype, unsigned long pktlen, /* Find all revocation keys. */ if(sig->sig_class==0x1F) parse_revkeys(sig); - } + } if( list_mode ) { - printf(":signature packet: algo %d, keyid %08lX%08lX\n" + fprintf (listfp, ":signature packet: algo %d, keyid %08lX%08lX\n" "\tversion %d, created %lu, md5len %d, sigclass %02x\n" "\tdigest algo %d, begin of digest %02x %02x\n", sig->pubkey_algo, @@ -1365,12 +1479,11 @@ parse_signature( iobuf_t inp, int pkttype, unsigned long pktlen, ndata = pubkey_get_nsig(sig->pubkey_algo); if( !ndata ) { if( list_mode ) - printf("\tunknown algorithm %d\n", sig->pubkey_algo ); + fprintf (listfp, "\tunknown algorithm %d\n", sig->pubkey_algo ); unknown_pubkey_warning( sig->pubkey_algo ); /* we store the plain material in data[0], so that we are able * to write it back with build_packet() */ - sig->data[0] = gcry_mpi_set_opaque(NULL, read_rest(inp, pktlen), - pktlen*8 ); + sig->data[0]= mpi_set_opaque(NULL, read_rest(inp, pktlen, 0), pktlen ); pktlen = 0; } else { @@ -1379,23 +1492,23 @@ parse_signature( iobuf_t inp, int pkttype, unsigned long pktlen, sig->data[i] = mpi_read(inp, &n, 0 ); pktlen -=n; if( list_mode ) { - printf("\tdata: "); - mpi_print(stdout, sig->data[i], mpi_print_mode ); - putchar('\n'); + fprintf (listfp, "\tdata: "); + mpi_print(listfp, sig->data[i], mpi_print_mode ); + putc ('\n', listfp); } if (!sig->data[i]) - rc = GPG_ERR_INV_PACKET; + rc = G10ERR_INVALID_PACKET; } } leave: - skip_rest(inp, pktlen); + iobuf_skip_rest(inp, pktlen, 0); return rc; } static int -parse_onepass_sig( iobuf_t inp, int pkttype, unsigned long pktlen, +parse_onepass_sig( IOBUF inp, int pkttype, unsigned long pktlen, PKT_onepass_sig *ops ) { int version; @@ -1403,13 +1516,13 @@ parse_onepass_sig( iobuf_t inp, int pkttype, unsigned long pktlen, if( pktlen < 13 ) { log_error("packet(%d) too short\n", pkttype); - rc = GPG_ERR_INV_PACKET; + rc = gpg_error (GPG_ERR_INV_PACKET); goto leave; } version = iobuf_get_noeof(inp); pktlen--; if( version != 3 ) { log_error("onepass_sig with unknown version %d\n", version); - rc = GPG_ERR_INV_PACKET; + rc = gpg_error (GPG_ERR_INV_PACKET); goto leave; } ops->sig_class = iobuf_get_noeof(inp); pktlen--; @@ -1419,7 +1532,7 @@ parse_onepass_sig( iobuf_t inp, int pkttype, unsigned long pktlen, ops->keyid[1] = read_32(inp); pktlen -= 4; ops->last = iobuf_get_noeof(inp); pktlen--; if( list_mode ) - printf(":onepass_sig packet: keyid %08lX%08lX\n" + fprintf (listfp, ":onepass_sig packet: keyid %08lX%08lX\n" "\tversion %d, sigclass %02x, digest %d, pubkey %d, last=%d\n", (ulong)ops->keyid[0], (ulong)ops->keyid[1], version, ops->sig_class, @@ -1427,13 +1540,13 @@ parse_onepass_sig( iobuf_t inp, int pkttype, unsigned long pktlen, leave: - skip_rest(inp, pktlen); + iobuf_skip_rest(inp, pktlen, 0); return rc; } static gcry_mpi_t -read_protected_v3_mpi (iobuf_t inp, unsigned long *length) +read_protected_v3_mpi (IOBUF inp, unsigned long *length) { int c; unsigned int nbits, nbytes; @@ -1473,14 +1586,14 @@ read_protected_v3_mpi (iobuf_t inp, unsigned long *length) return NULL; } - /* convert buffer into an opaque gcry_mpi_t */ + /* convert buffer into an opaque MPI */ val = gcry_mpi_set_opaque (NULL, buf, (p-buf)*8); return val; } static int -parse_key( iobuf_t inp, int pkttype, unsigned long pktlen, +parse_key( IOBUF inp, int pkttype, unsigned long pktlen, byte *hdr, int hdrlen, PACKET *pkt ) { int i, version, algorithm; @@ -1495,31 +1608,31 @@ parse_key( iobuf_t inp, int pkttype, unsigned long pktlen, /* early versions of G10 use old PGP comments packets; * luckily all those comments are started by a hash */ if( list_mode ) { - printf(":rfc1991 comment packet: \"" ); + fprintf (listfp, ":rfc1991 comment packet: \"" ); for( ; pktlen; pktlen-- ) { int c; c = iobuf_get_noeof(inp); if( c >= ' ' && c <= 'z' ) - putchar(c); + putc (c, listfp); else - printf("\\x%02x", c ); + fprintf (listfp, "\\x%02x", c ); } - printf("\"\n"); + fprintf (listfp, "\"\n"); } - skip_rest(inp, pktlen); + iobuf_skip_rest(inp, pktlen, 0); return 0; } else if( version == 4 ) is_v4=1; else if( version != 2 && version != 3 ) { log_error("packet(%d) with unknown version %d\n", pkttype, version); - rc = GPG_ERR_INV_PACKET; + rc = gpg_error (GPG_ERR_INV_PACKET); goto leave; } if( pktlen < 11 ) { log_error("packet(%d) too short\n", pkttype); - rc = GPG_ERR_INV_PACKET; + rc = gpg_error (GPG_ERR_INV_PACKET); goto leave; } @@ -1540,7 +1653,7 @@ parse_key( iobuf_t inp, int pkttype, unsigned long pktlen, } algorithm = iobuf_get_noeof(inp); pktlen--; if( list_mode ) - printf(":%s key packet:\n" + fprintf (listfp, ":%s key packet:\n" "\tversion %d, algo %d, created %lu, expires %lu\n", pkttype == PKT_PUBLIC_KEY? "public" : pkttype == PKT_SECRET_KEY? "secret" : @@ -1582,7 +1695,7 @@ parse_key( iobuf_t inp, int pkttype, unsigned long pktlen, npkey = pubkey_get_npkey( algorithm ); if( !npkey ) { if( list_mode ) - printf("\tunknown algorithm %d\n", algorithm ); + fprintf (listfp, "\tunknown algorithm %d\n", algorithm ); unknown_pubkey_warning( algorithm ); } @@ -1593,8 +1706,8 @@ parse_key( iobuf_t inp, int pkttype, unsigned long pktlen, size_t snlen = 0; if( !npkey ) { - sk->skey[0] = gcry_mpi_set_opaque( NULL, read_rest(inp, pktlen), - pktlen*8 ); + sk->skey[0] = mpi_set_opaque( NULL, + read_rest(inp, pktlen, 0), pktlen ); pktlen = 0; goto leave; } @@ -1602,12 +1715,12 @@ parse_key( iobuf_t inp, int pkttype, unsigned long pktlen, for(i=0; i < npkey; i++ ) { n = pktlen; sk->skey[i] = mpi_read(inp, &n, 0 ); pktlen -=n; if( list_mode ) { - printf( "\tskey[%d]: ", i); - mpi_print(stdout, sk->skey[i], mpi_print_mode ); - putchar('\n'); + fprintf (listfp, "\tskey[%d]: ", i); + mpi_print(listfp, sk->skey[i], mpi_print_mode ); + putc ('\n', listfp); } if (!sk->skey[i]) - rc = GPG_ERR_INV_PACKET; + rc = G10ERR_INVALID_PACKET; } if (rc) /* one of the MPIs were bad */ goto leave; @@ -1618,7 +1731,7 @@ parse_key( iobuf_t inp, int pkttype, unsigned long pktlen, sk->protect.s2k.count = 0; if( sk->protect.algo == 254 || sk->protect.algo == 255 ) { if( pktlen < 3 ) { - rc = GPG_ERR_INV_PACKET; + rc = G10ERR_INVALID_PACKET; goto leave; } sk->protect.sha1chk = (sk->protect.algo == 254); @@ -1634,9 +1747,9 @@ parse_key( iobuf_t inp, int pkttype, unsigned long pktlen, temp[i] = iobuf_get_noeof(inp); if( i < 4 || memcmp( temp, "GNU", 3 ) ) { if( list_mode ) - printf( "\tunknown S2K %d\n", + fprintf (listfp, "\tunknown S2K %d\n", sk->protect.s2k.mode ); - rc = GPG_ERR_INV_PACKET; + rc = G10ERR_INVALID_PACKET; goto leave; } /* here we know that it is a gnu extension @@ -1655,61 +1768,63 @@ parse_key( iobuf_t inp, int pkttype, unsigned long pktlen, break; } switch( sk->protect.s2k.mode ) { - case 0: if( list_mode ) printf( "\tsimple S2K" ); + case 0: if( list_mode ) fprintf (listfp, "\tsimple S2K" ); break; - case 1: if( list_mode ) printf( "\tsalted S2K" ); + case 1: if( list_mode ) fprintf (listfp, "\tsalted S2K" ); break; - case 3: if( list_mode ) printf( "\titer+salt S2K" ); + case 3: if( list_mode ) fprintf (listfp, "\titer+salt S2K" ); break; - case 1001: if( list_mode ) printf( "\tgnu-dummy S2K" ); + case 1001: if( list_mode ) fprintf (listfp, + "\tgnu-dummy S2K" ); break; - case 1002: if (list_mode) printf("\tgnu-divert-to-card S2K"); + case 1002: if (list_mode) fprintf (listfp, + "\tgnu-divert-to-card S2K"); break; default: if( list_mode ) - printf( "\tunknown %sS2K %d\n", + fprintf (listfp, "\tunknown %sS2K %d\n", sk->protect.s2k.mode < 1000? "":"GNU ", sk->protect.s2k.mode ); - rc = GPG_ERR_INV_PACKET; + rc = G10ERR_INVALID_PACKET; goto leave; } if( list_mode ) { - printf(", algo: %d,%s hash: %d", + fprintf (listfp, ", algo: %d,%s hash: %d", sk->protect.algo, sk->protect.sha1chk?" SHA1 protection," :" simple checksum,", sk->protect.s2k.hash_algo ); if( sk->protect.s2k.mode == 1 || sk->protect.s2k.mode == 3 ) { - printf(", salt: "); + fprintf (listfp, ", salt: "); for(i=0; i < 8; i++ ) - printf("%02x", sk->protect.s2k.salt[i]); + fprintf (listfp, "%02x", sk->protect.s2k.salt[i]); } - putchar('\n'); + putc ('\n', listfp); } if( sk->protect.s2k.mode == 3 ) { if( pktlen < 1 ) { - rc = GPG_ERR_INV_PACKET; + rc = G10ERR_INVALID_PACKET; goto leave; } sk->protect.s2k.count = iobuf_get(inp); pktlen--; if( list_mode ) - printf("\tprotect count: %lu\n", + fprintf (listfp, "\tprotect count: %lu\n", (ulong)sk->protect.s2k.count); } else if( sk->protect.s2k.mode == 1002 ) { /* Read the serial number. */ if (pktlen < 1) { - rc = GPG_ERR_INV_PACKET; + rc = G10ERR_INVALID_PACKET; goto leave; } snlen = iobuf_get (inp); pktlen--; if (pktlen < snlen || snlen == -1) { - rc = GPG_ERR_INV_PACKET; + rc = G10ERR_INVALID_PACKET; goto leave; } } @@ -1721,7 +1836,7 @@ parse_key( iobuf_t inp, int pkttype, unsigned long pktlen, sk->protect.s2k.mode = 0; sk->protect.s2k.hash_algo = DIGEST_ALGO_MD5; if( list_mode ) - printf( "\tprotect algo: %d (hash algo: %d)\n", + fprintf (listfp, "\tprotect algo: %d (hash algo: %d)\n", sk->protect.algo, sk->protect.s2k.hash_algo ); } /* It is really ugly that we don't know the size @@ -1742,24 +1857,22 @@ parse_key( iobuf_t inp, int pkttype, unsigned long pktlen, } if( sk->protect.s2k.mode == 1001 ) sk->protect.ivlen = 0; - else if( sk->protect.s2k.mode == 1002 ) { - if (snlen > 16) - log_info ("WARNING: serial number of card truncated\n"); + else if( sk->protect.s2k.mode == 1002 ) sk->protect.ivlen = snlen < 16? snlen : 16; - } if( pktlen < sk->protect.ivlen ) { - rc = GPG_ERR_INV_PACKET; + rc = G10ERR_INVALID_PACKET; goto leave; } for(i=0; i < sk->protect.ivlen && pktlen; i++, pktlen-- ) temp[i] = iobuf_get_noeof(inp); if( list_mode ) { - printf( sk->protect.s2k.mode == 1002? "\tserial-number: " - : "\tprotect IV: "); + fprintf (listfp, + sk->protect.s2k.mode == 1002? "\tserial-number: " + : "\tprotect IV: "); for(i=0; i < sk->protect.ivlen; i++ ) - printf(" %02x", temp[i] ); - putchar('\n'); + fprintf (listfp, " %02x", temp[i] ); + putc ('\n', listfp); } memcpy(sk->protect.iv, temp, sk->protect.ivlen ); } @@ -1769,22 +1882,21 @@ parse_key( iobuf_t inp, int pkttype, unsigned long pktlen, * If the user is so careless, not to protect his secret key, * we can assume, that he operates an open system :=(. * So we put the key into secure memory when we unprotect it. */ - if( sk->protect.s2k.mode == 1001 + if( sk->protect.s2k.mode == 1001 || sk->protect.s2k.mode == 1002 ) { /* better set some dummy stuff here */ - sk->skey[npkey] = gcry_mpi_set_opaque(NULL, xstrdup ("dummydata"), - 10*8); + sk->skey[npkey] = mpi_set_opaque(NULL, xstrdup("dummydata"), 10); pktlen = 0; } else if( is_v4 && sk->is_protected ) { /* ugly; the length is encrypted too, so we read all * stuff up to the end of the packet into the first * skey element */ - sk->skey[npkey] = gcry_mpi_set_opaque(NULL, read_rest(inp, pktlen), - pktlen*8 ); + sk->skey[npkey] = mpi_set_opaque(NULL, + read_rest(inp, pktlen, 0),pktlen); pktlen = 0; if( list_mode ) { - printf("\tencrypted stuff follows\n"); + fprintf (listfp, "\tencrypted stuff follows\n"); } } else { /* v3 method: the mpi length is not encrypted */ @@ -1792,28 +1904,28 @@ parse_key( iobuf_t inp, int pkttype, unsigned long pktlen, if ( sk->is_protected ) { sk->skey[i] = read_protected_v3_mpi (inp, &pktlen); if( list_mode ) - printf( "\tskey[%d]: [encrypted]\n", i); + fprintf (listfp, "\tskey[%d]: [encrypted]\n", i); } else { n = pktlen; sk->skey[i] = mpi_read(inp, &n, 0 ); pktlen -=n; if( list_mode ) { - printf( "\tskey[%d]: ", i); - mpi_print(stdout, sk->skey[i], mpi_print_mode ); - putchar('\n'); + fprintf (listfp, "\tskey[%d]: ", i); + mpi_print(listfp, sk->skey[i], mpi_print_mode ); + putc ('\n', listfp); } } if (!sk->skey[i]) - rc = GPG_ERR_INV_PACKET; + rc = G10ERR_INVALID_PACKET; } if (rc) goto leave; sk->csum = read_16(inp); pktlen -= 2; if( list_mode ) { - printf("\tchecksum: %04hx\n", sk->csum); + fprintf (listfp, "\tchecksum: %04hx\n", sk->csum); } } } @@ -1821,8 +1933,8 @@ parse_key( iobuf_t inp, int pkttype, unsigned long pktlen, PKT_public_key *pk = pkt->pkt.public_key; if( !npkey ) { - pk->pkey[0] = gcry_mpi_set_opaque( NULL, read_rest(inp, pktlen), - pktlen*8 ); + pk->pkey[0] = mpi_set_opaque( NULL, + read_rest(inp, pktlen, 0), pktlen ); pktlen = 0; goto leave; } @@ -1830,19 +1942,19 @@ parse_key( iobuf_t inp, int pkttype, unsigned long pktlen, for(i=0; i < npkey; i++ ) { n = pktlen; pk->pkey[i] = mpi_read(inp, &n, 0 ); pktlen -=n; if( list_mode ) { - printf( "\tpkey[%d]: ", i); - mpi_print(stdout, pk->pkey[i], mpi_print_mode ); - putchar('\n'); + fprintf (listfp, "\tpkey[%d]: ", i); + mpi_print(listfp, pk->pkey[i], mpi_print_mode ); + putc ('\n', listfp); } if (!pk->pkey[i]) - rc = GPG_ERR_INV_PACKET; + rc = G10ERR_INVALID_PACKET; } if (rc) goto leave; } leave: - skip_rest(inp, pktlen); + iobuf_skip_rest(inp, pktlen, 0); return rc; } @@ -1859,7 +1971,7 @@ parse_attribute_subpkts(PKT_user_id *uid) int buflen=uid->attrib_len; byte type; - xfree (uid->attribs); + xfree(uid->attribs); while(buflen) { @@ -1903,38 +2015,22 @@ parse_attribute_subpkts(PKT_user_id *uid) return count; too_short: - log_error("buffer shorter than attribute subpacket\n"); + if(opt.verbose) + log_info("buffer shorter than attribute subpacket\n"); uid->attribs=attribs; uid->numattribs=count; return count; } -static void setup_user_id(PACKET *packet) -{ - packet->pkt.user_id->ref = 1; - packet->pkt.user_id->attribs = NULL; - packet->pkt.user_id->attrib_data = NULL; - packet->pkt.user_id->attrib_len = 0; - packet->pkt.user_id->is_primary = 0; - packet->pkt.user_id->is_revoked = 0; - packet->pkt.user_id->is_expired = 0; - packet->pkt.user_id->expiredate = 0; - packet->pkt.user_id->created = 0; - packet->pkt.user_id->help_key_usage = 0; - packet->pkt.user_id->help_key_expire = 0; - packet->pkt.user_id->prefs = NULL; - packet->pkt.user_id->namehash = NULL; -} static int -parse_user_id( iobuf_t inp, int pkttype, unsigned long pktlen, PACKET *packet ) +parse_user_id( IOBUF inp, int pkttype, unsigned long pktlen, PACKET *packet ) { byte *p; - packet->pkt.user_id = xmalloc (sizeof *packet->pkt.user_id + pktlen); + packet->pkt.user_id = xmalloc_clear(sizeof *packet->pkt.user_id + pktlen); packet->pkt.user_id->len = pktlen; - - setup_user_id(packet); + packet->pkt.user_id->ref=1; p = packet->pkt.user_id->name; for( ; pktlen; pktlen--, p++ ) @@ -1943,15 +2039,15 @@ parse_user_id( iobuf_t inp, int pkttype, unsigned long pktlen, PACKET *packet ) if( list_mode ) { int n = packet->pkt.user_id->len; - printf(":user ID packet: \""); + fprintf (listfp, ":user ID packet: \""); /* fixme: Hey why don't we replace this with print_string?? */ for(p=packet->pkt.user_id->name; n; p++, n-- ) { if( *p >= ' ' && *p <= 'z' ) - putchar(*p); + putc (*p, listfp); else - printf("\\x%02x", *p ); + fprintf (listfp, "\\x%02x", *p ); } - printf("\"\n"); + fprintf (listfp, "\"\n"); } return 0; } @@ -1990,18 +2086,17 @@ make_attribute_uidname(PKT_user_id *uid, size_t max_namelen) } static int -parse_attribute( iobuf_t inp, int pkttype, unsigned long pktlen, PACKET *packet ) +parse_attribute( IOBUF inp, int pkttype, unsigned long pktlen, PACKET *packet ) { byte *p; #define EXTRA_UID_NAME_SPACE 71 - packet->pkt.user_id = xmalloc (sizeof *packet->pkt.user_id - + EXTRA_UID_NAME_SPACE); - - setup_user_id(packet); - - packet->pkt.user_id->attrib_data = xmalloc (pktlen); + packet->pkt.user_id = xmalloc_clear(sizeof *packet->pkt.user_id + + EXTRA_UID_NAME_SPACE); + packet->pkt.user_id->ref=1; + packet->pkt.user_id->attrib_data = xmalloc(pktlen); packet->pkt.user_id->attrib_len = pktlen; + p = packet->pkt.user_id->attrib_data; for( ; pktlen; pktlen--, p++ ) *p = iobuf_get_noeof(inp); @@ -2014,18 +2109,18 @@ parse_attribute( iobuf_t inp, int pkttype, unsigned long pktlen, PACKET *packet make_attribute_uidname(packet->pkt.user_id, EXTRA_UID_NAME_SPACE); if( list_mode ) { - printf(":attribute packet: %s\n", packet->pkt.user_id->name ); + fprintf (listfp, ":attribute packet: %s\n", packet->pkt.user_id->name ); } return 0; } static int -parse_comment( iobuf_t inp, int pkttype, unsigned long pktlen, PACKET *packet ) +parse_comment( IOBUF inp, int pkttype, unsigned long pktlen, PACKET *packet ) { byte *p; - packet->pkt.comment = xmalloc (sizeof *packet->pkt.comment + pktlen - 1); + packet->pkt.comment = xmalloc(sizeof *packet->pkt.comment + pktlen - 1); packet->pkt.comment->len = pktlen; p = packet->pkt.comment->data; for( ; pktlen; pktlen--, p++ ) @@ -2033,22 +2128,22 @@ parse_comment( iobuf_t inp, int pkttype, unsigned long pktlen, PACKET *packet ) if( list_mode ) { int n = packet->pkt.comment->len; - printf(":%scomment packet: \"", pkttype == PKT_OLD_COMMENT? + fprintf (listfp, ":%scomment packet: \"", pkttype == PKT_OLD_COMMENT? "OpenPGP draft " : "" ); for(p=packet->pkt.comment->data; n; p++, n-- ) { if( *p >= ' ' && *p <= 'z' ) - putchar(*p); + putc (*p, listfp); else - printf("\\x%02x", *p ); + fprintf (listfp, "\\x%02x", *p ); } - printf("\"\n"); + fprintf (listfp, "\"\n"); } return 0; } static void -parse_trust( iobuf_t inp, int pkttype, unsigned long pktlen, PACKET *pkt ) +parse_trust( IOBUF inp, int pkttype, unsigned long pktlen, PACKET *pkt ) { int c; @@ -2056,7 +2151,7 @@ parse_trust( iobuf_t inp, int pkttype, unsigned long pktlen, PACKET *pkt ) { c = iobuf_get_noeof(inp); pktlen--; - pkt->pkt.ring_trust = xmalloc ( sizeof *pkt->pkt.ring_trust ); + pkt->pkt.ring_trust = xmalloc( sizeof *pkt->pkt.ring_trust ); pkt->pkt.ring_trust->trustval = c; pkt->pkt.ring_trust->sigcache = 0; if (!c && pktlen==1) @@ -2068,42 +2163,37 @@ parse_trust( iobuf_t inp, int pkttype, unsigned long pktlen, PACKET *pkt ) pkt->pkt.ring_trust->sigcache = c; } if( list_mode ) - printf(":trust packet: flag=%02x sigcache=%02x\n", + fprintf (listfp, ":trust packet: flag=%02x sigcache=%02x\n", pkt->pkt.ring_trust->trustval, pkt->pkt.ring_trust->sigcache); } else { if( list_mode ) - printf(":trust packet: empty\n"); + fprintf (listfp, ":trust packet: empty\n"); } - skip_rest (inp, pktlen); + iobuf_skip_rest (inp, pktlen, 0); } static int -parse_plaintext( iobuf_t inp, int pkttype, unsigned long pktlen, - PACKET *pkt, int new_ctb ) +parse_plaintext( IOBUF inp, int pkttype, unsigned long pktlen, + PACKET *pkt, int new_ctb, int partial ) { int rc = 0; - int mode, namelen, partial=0; + int mode, namelen; PKT_plaintext *pt; byte *p; int c, i; - if( pktlen && pktlen < 6 ) { + if( !partial && pktlen < 6 ) { log_error("packet(%d) too short (%lu)\n", pkttype, (ulong)pktlen); - rc = GPG_ERR_INV_PACKET; + rc = gpg_error (GPG_ERR_INV_PACKET); goto leave; } - /* A packet length of zero indicates partial body length. A zero - data length isn't a zero length packet due to the header (mode, - name, etc), so this is accurate. */ - if(pktlen==0) - partial=1; mode = iobuf_get_noeof(inp); if( pktlen ) pktlen--; namelen = iobuf_get_noeof(inp); if( pktlen ) pktlen--; - pt = pkt->pkt.plaintext = xmalloc (sizeof *pkt->pkt.plaintext + namelen -1); + pt = pkt->pkt.plaintext = xmalloc(sizeof *pkt->pkt.plaintext + namelen -1); pt->new_ctb = new_ctb; pt->mode = mode; pt->namelen = namelen; @@ -2125,17 +2215,21 @@ parse_plaintext( iobuf_t inp, int pkttype, unsigned long pktlen, pktlen = 0; if( list_mode ) { - printf(":literal data packet:\n" - "\tmode %c, created %lu, name=\"", - mode >= ' ' && mode <'z'? mode : '?', + fprintf (listfp, ":literal data packet:\n" + "\tmode %c (%X), created %lu, name=\"", + mode >= ' ' && mode <'z'? mode : '?', mode, (ulong)pt->timestamp ); for(p=pt->name,i=0; i < namelen; p++, i++ ) { if( *p >= ' ' && *p <= 'z' ) - putchar(*p); + putc (*p, listfp); else - printf("\\x%02x", *p ); + fprintf (listfp, "\\x%02x", *p ); } - printf("\",\n\traw data: %lu bytes\n", (ulong)pt->len ); + fprintf (listfp, "\",\n\traw data: "); + if(partial) + fprintf (listfp, "unknown length\n"); + else + fprintf (listfp, "%lu bytes\n", (ulong)pt->len ); } leave: @@ -2144,7 +2238,7 @@ parse_plaintext( iobuf_t inp, int pkttype, unsigned long pktlen, static int -parse_compressed( iobuf_t inp, int pkttype, unsigned long pktlen, +parse_compressed( IOBUF inp, int pkttype, unsigned long pktlen, PACKET *pkt, int new_ctb ) { PKT_compressed *zd; @@ -2153,26 +2247,26 @@ parse_compressed( iobuf_t inp, int pkttype, unsigned long pktlen, * (this should be the last object in a file or * the compress algorithm should know the length) */ - zd = pkt->pkt.compressed = xmalloc (sizeof *pkt->pkt.compressed ); + zd = pkt->pkt.compressed = xmalloc(sizeof *pkt->pkt.compressed ); zd->algorithm = iobuf_get_noeof(inp); zd->len = 0; /* not used */ zd->new_ctb = new_ctb; zd->buf = inp; if( list_mode ) - printf(":compressed packet: algo=%d\n", zd->algorithm); + fprintf (listfp, ":compressed packet: algo=%d\n", zd->algorithm); return 0; } static int -parse_encrypted( iobuf_t inp, int pkttype, unsigned long pktlen, - PACKET *pkt, int new_ctb ) +parse_encrypted( IOBUF inp, int pkttype, unsigned long pktlen, + PACKET *pkt, int new_ctb, int partial ) { int rc = 0; PKT_encrypted *ed; unsigned long orig_pktlen = pktlen; - ed = pkt->pkt.encrypted = xmalloc (sizeof *pkt->pkt.encrypted ); + ed = pkt->pkt.encrypted = xmalloc(sizeof *pkt->pkt.encrypted ); ed->len = pktlen; /* we don't know the extralen which is (cipher_blocksize+2) because the algorithm ist not specified in this packet. @@ -2182,6 +2276,7 @@ parse_encrypted( iobuf_t inp, int pkttype, unsigned long pktlen, ed->extralen = 0; ed->buf = NULL; ed->new_ctb = new_ctb; + ed->is_partial = partial; ed->mdc_method = 0; if( pkttype == PKT_ENCRYPTED_MDC ) { /* fixme: add some pktlen sanity checks */ @@ -2194,28 +2289,28 @@ parse_encrypted( iobuf_t inp, int pkttype, unsigned long pktlen, log_error("encrypted_mdc packet with unknown version %d\n", version); /*skip_rest(inp, pktlen); should we really do this? */ - rc = GPG_ERR_INV_PACKET; + rc = gpg_error (GPG_ERR_INV_PACKET); goto leave; } ed->mdc_method = DIGEST_ALGO_SHA1; } if( orig_pktlen && pktlen < 10 ) { /* actually this is blocksize+2 */ log_error("packet(%d) too short\n", pkttype); - rc = GPG_ERR_INV_PACKET; - skip_rest(inp, pktlen); + rc = G10ERR_INVALID_PACKET; + iobuf_skip_rest(inp, pktlen, partial); goto leave; } if( list_mode ) { if( orig_pktlen ) - printf(":encrypted data packet:\n\tlength: %lu\n", orig_pktlen); + fprintf (listfp, ":encrypted data packet:\n\tlength: %lu\n", + orig_pktlen); else - printf(":encrypted data packet:\n\tlength: unknown\n"); + fprintf (listfp, ":encrypted data packet:\n\tlength: unknown\n"); if( ed->mdc_method ) - printf("\tmdc_method: %d\n", ed->mdc_method ); + fprintf (listfp, "\tmdc_method: %d\n", ed->mdc_method ); } ed->buf = inp; - pktlen = 0; leave: return rc; @@ -2223,19 +2318,19 @@ parse_encrypted( iobuf_t inp, int pkttype, unsigned long pktlen, static int -parse_mdc( iobuf_t inp, int pkttype, unsigned long pktlen, +parse_mdc( IOBUF inp, int pkttype, unsigned long pktlen, PACKET *pkt, int new_ctb ) { int rc = 0; PKT_mdc *mdc; byte *p; - mdc = pkt->pkt.mdc= xmalloc (sizeof *pkt->pkt.mdc ); + mdc = pkt->pkt.mdc= xmalloc(sizeof *pkt->pkt.mdc ); if( list_mode ) - printf(":mdc packet: length=%lu\n", pktlen); + fprintf (listfp, ":mdc packet: length=%lu\n", pktlen); if( !new_ctb || pktlen != 20 ) { log_error("mdc_packet with invalid encoding\n"); - rc = GPG_ERR_INV_PACKET; + rc = gpg_error (GPG_ERR_INV_PACKET); goto leave; } p = mdc->hash; @@ -2259,8 +2354,8 @@ parse_mdc( iobuf_t inp, int pkttype, unsigned long pktlen, */ static int -parse_gpg_control( iobuf_t inp, - int pkttype, unsigned long pktlen, PACKET *packet ) +parse_gpg_control( IOBUF inp, int pkttype, + unsigned long pktlen, PACKET *packet, int partial ) { byte *p; const byte *sesmark; @@ -2268,7 +2363,7 @@ parse_gpg_control( iobuf_t inp, int i; if ( list_mode ) - printf(":packet 63: length %lu ", pktlen); + fprintf (listfp, ":packet 63: length %lu ", pktlen); sesmark = get_session_marker ( &sesmarklen ); if ( pktlen < sesmarklen+1 ) /* 1 is for the control bytes */ @@ -2280,7 +2375,7 @@ parse_gpg_control( iobuf_t inp, if ( list_mode ) puts ("- gpg control packet"); - packet->pkt.gpg_control = xmalloc (sizeof *packet->pkt.gpg_control + packet->pkt.gpg_control = xmalloc(sizeof *packet->pkt.gpg_control + pktlen - 1); packet->pkt.gpg_control->control = iobuf_get_noeof(inp); pktlen--; packet->pkt.gpg_control->datalen = pktlen; @@ -2295,8 +2390,8 @@ parse_gpg_control( iobuf_t inp, int c; i=0; - printf("- private (rest length %lu)\n", pktlen); - if( iobuf_in_block_mode(inp) ) { + fprintf (listfp, "- private (rest length %lu)\n", pktlen); + if( partial ) { while( (c=iobuf_get(inp)) != -1 ) dump_hex_line(c, &i); } @@ -2304,10 +2399,10 @@ parse_gpg_control( iobuf_t inp, for( ; pktlen; pktlen-- ) dump_hex_line(iobuf_get(inp), &i); } - putchar('\n'); + putc ('\n', listfp); } - skip_rest(inp,pktlen); - return GPG_ERR_INV_PACKET; + iobuf_skip_rest(inp,pktlen, 0); + return gpg_error (GPG_ERR_INV_PACKET); } /* create a gpg control packet to be used internally as a placeholder */ @@ -2317,10 +2412,10 @@ create_gpg_control( ctrlpkttype_t type, const byte *data, size_t datalen ) PACKET *packet; byte *p; - packet = xmalloc ( sizeof *packet ); + packet = xmalloc( sizeof *packet ); init_packet(packet); packet->pkttype = PKT_GPG_CONTROL; - packet->pkt.gpg_control = xmalloc (sizeof *packet->pkt.gpg_control + packet->pkt.gpg_control = xmalloc(sizeof *packet->pkt.gpg_control + datalen - 1); packet->pkt.gpg_control->control = type; packet->pkt.gpg_control->datalen = datalen; diff --git a/g10/passphrase.c b/g10/passphrase.c index 30149908e..c63ee66d4 100644 --- a/g10/passphrase.c +++ b/g10/passphrase.c @@ -1,5 +1,6 @@ /* passphrase.c - Get a passphrase - * Copyright (C) 1998,1999,2000,2001,2002,2003 Free Software Foundation, Inc. + * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, + * 2005 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -15,7 +16,8 @@ * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. */ #include <config.h> @@ -29,8 +31,8 @@ #include <sys/socket.h> #include <sys/un.h> #endif -#if defined (_WIN32) || defined (__CYGWIN32__) -# include <windows.h> +#if defined (_WIN32) +#include <windows.h> #endif #include <errno.h> #ifdef HAVE_LOCALE_H @@ -42,7 +44,6 @@ #include "gpg.h" #include "util.h" -#include "memory.h" #include "options.h" #include "ttyio.h" #include "cipher.h" @@ -50,62 +51,14 @@ #include "main.h" #include "i18n.h" #include "status.h" - - -enum gpga_protocol_codes { - /* Request codes */ - GPGA_PROT_GET_VERSION = 1, - GPGA_PROT_GET_PASSPHRASE = 2, - GPGA_PROT_CLEAR_PASSPHRASE= 3, - GPGA_PROT_SHUTDOWN = 4, - GPGA_PROT_FLUSH = 5, - - /* Reply codes */ - GPGA_PROT_REPLY_BASE = 0x10000, - GPGA_PROT_OKAY = 0x10001, - GPGA_PROT_GOT_PASSPHRASE = 0x10002, - - /* Error codes */ - GPGA_PROT_ERROR_BASE = 0x20000, - GPGA_PROT_PROTOCOL_ERROR = 0x20001, - GPGA_PROT_INVALID_REQUEST= 0x20002, - GPGA_PROT_CANCELED = 0x20003, - GPGA_PROT_NO_PASSPHRASE = 0x20004, - GPGA_PROT_BAD_PASSPHRASE = 0x20005, - GPGA_PROT_INVALID_DATA = 0x20006, - GPGA_PROT_NOT_IMPLEMENTED= 0x20007, - GPGA_PROT_UI_PROBLEM = 0x20008 -}; - - -#define buftou32( p ) ((*(byte*)(p) << 24) | (*((byte*)(p)+1)<< 16) | \ - (*((byte*)(p)+2) << 8) | (*((byte*)(p)+3))) -#define u32tobuf( p, a ) do { \ - ((byte*)p)[0] = (byte)((a) >> 24); \ - ((byte*)p)[1] = (byte)((a) >> 16); \ - ((byte*)p)[2] = (byte)((a) >> 8); \ - ((byte*)p)[3] = (byte)((a) ); \ - } while(0) - -#define digitp(p) (*(p) >= '0' && *(p) <= '9') -#define hexdigitp(a) (digitp (a) \ - || (*(a) >= 'A' && *(a) <= 'F') \ - || (*(a) >= 'a' && *(a) <= 'f')) -#define xtoi_1(p) (*(p) <= '9'? (*(p)- '0'): \ - *(p) <= 'F'? (*(p)-'A'+10):(*(p)-'a'+10)) -#define xtoi_2(p) ((xtoi_1(p) * 16) + xtoi_1((p)+1)) - - +#ifdef ENABLE_AGENT_SUPPORT +#include "assuan.h" +#endif /*ENABLE_AGENT_SUPPORT*/ static char *fd_passwd = NULL; static char *next_pw = NULL; static char *last_pw = NULL; -#if defined (_WIN32) -static int read_fd = 0; -static int write_fd = 0; -#endif - static void hash_passphrase( DEK *dek, char *pw, STRING2KEY *s2k, int create ); int @@ -123,10 +76,10 @@ have_static_passphrase() void set_next_passphrase( const char *s ) { - xfree (next_pw); + xfree(next_pw); next_pw = NULL; if( s ) { - next_pw = gcry_xmalloc_secure ( strlen(s)+1 ); + next_pw = xmalloc_secure( strlen(s)+1 ); strcpy(next_pw, s ); } } @@ -144,6 +97,30 @@ get_last_passphrase() return p; } +/* As if we had used the passphrase - make it the last_pw. */ +void +next_to_last_passphrase(void) +{ + if(next_pw) + { + last_pw=next_pw; + next_pw=NULL; + } +} + +/* Here's an interesting question: since this passphrase was passed in + on the command line, is there really any point in using secure + memory for it? I'm going with 'yes', since it doesn't hurt, and + might help in some small way (swapping). */ + +void +set_passphrase_from_string(const char *pass) +{ + xfree( fd_passwd ); + fd_passwd = xmalloc_secure(strlen(pass)+1); + strcpy(fd_passwd,pass); +} + void read_passphrase_from_fd( int fd ) @@ -171,9 +148,12 @@ read_passphrase_from_fd( int fd ) { char *pw2 = pw; len += 100; - pw = gcry_xmalloc_secure ( len ); + pw = xmalloc_secure( len ); if( pw2 ) - memcpy(pw, pw2, i ); + { + memcpy(pw, pw2, i ); + xfree (pw2); + } else i=0; } @@ -184,181 +164,33 @@ read_passphrase_from_fd( int fd ) if (!opt.batch) tty_printf("\b\b\b \n" ); - xfree ( fd_passwd ); + xfree( fd_passwd ); fd_passwd = pw; } -static int -writen ( int fd, const void *buf, size_t nbytes ) -{ -#if defined (_WIN32) - DWORD nwritten, nleft = nbytes; - - while (nleft > 0) { - if ( !WriteFile( (HANDLE)write_fd, buf, nleft, &nwritten, NULL) ) { - log_error("write failed: ec=%d\n", (int)GetLastError()); - return -1; - } - /*log_info("** WriteFile fd=%d nytes=%d nwritten=%d\n", - write_fd, nbytes, (int)nwritten);*/ - Sleep(100); - - nleft -= nwritten; - buf = (const BYTE *)buf + nwritten; - } -#elif defined(HAVE_DOSISH_SYSTEM) || defined(__riscos__) - /* not implemented */ -#else - size_t nleft = nbytes; - int nwritten; - - while( nleft > 0 ) { - nwritten = write( fd, buf, nleft ); - if( nwritten < 0 ) { - if ( errno == EINTR ) - nwritten = 0; - else { - log_error ( "write() failed: %s\n", strerror (errno) ); - return -1; - } - } - nleft -= nwritten; - buf = (const char*)buf + nwritten; - } -#endif - - return 0; -} +#ifdef ENABLE_AGENT_SUPPORT +/* Send one option to the gpg-agent. */ static int -readn ( int fd, void *buf, size_t buflen, size_t *ret_nread ) +agent_send_option (assuan_context_t ctx, const char *name, const char *value) { -#if defined (_WIN32) - DWORD nread, nleft = buflen; - - while (nleft > 0) { - if ( !ReadFile( (HANDLE)read_fd, buf, nleft, &nread, NULL) ) { - log_error("read() error: ec=%d\n", (int)GetLastError()); - return -1; - } - if (!nread || GetLastError() == ERROR_BROKEN_PIPE) - break; - /*log_info("** ReadFile fd=%d buflen=%d nread=%d\n", - read_fd, buflen, (int)nread);*/ - Sleep(100); - - nleft -= nread; - buf = (BYTE *)buf + nread; - } - if (ret_nread) - *ret_nread = buflen - nleft; - -#elif defined(HAVE_DOSISH_SYSTEM) || defined(__riscos__) - /* not implemented */ -#else - size_t nleft = buflen; - int nread; - char *p; - - p = buf; - while( nleft > 0 ) { - nread = read ( fd, buf, nleft ); - if( nread < 0 ) { - if (nread == EINTR) - nread = 0; - else { - log_error ( "read() error: %s\n", strerror (errno) ); - return -1; - } - } - else if( !nread ) - break; /* EOF */ - nleft -= nread; - buf = (char*)buf + nread; - } - if( ret_nread ) - *ret_nread = buflen - nleft; -#endif - - return 0; -} - -/* read an entire line */ -static int -readline (int fd, char *buf, size_t buflen) -{ - size_t nleft = buflen; - char *p; - int nread = 0; - - while (nleft > 0) - { - int n = read (fd, buf, nleft); - if (n < 0) - { - if (errno == EINTR) - continue; - return -1; /* read error */ - } - else if (!n) - { - return -1; /* incomplete line */ - } - p = buf; - nleft -= n; - buf += n; - nread += n; - - for (; n && *p != '\n'; n--, p++) - ; - if (n) - { - break; /* at least one full line available - that's enough. - This function is just a temporary hack until we use - the assuna lib in gpg. So it is okay to forget - about pending bytes */ - } - } - - return nread; -} - - - -#if !defined (__riscos__) - -#if !defined (_WIN32) -/* For the new Assuan protocol we may have to send options */ -static int -agent_send_option (int fd, const char *name, const char *value) -{ - char buf[200]; - int nread; char *line; - int i; + int rc; - line = xmalloc (7 + strlen (name) + 1 + strlen (value) + 2); - strcpy (stpcpy (stpcpy (stpcpy ( - stpcpy (line, "OPTION "), name), "="), value), "\n"); - i = writen (fd, line, strlen (line)); - xfree (line); - if (i) - return -1; - - /* get response */ - nread = readline (fd, buf, DIM(buf)-1); - if (nread < 3) - return -1; - - if (buf[0] == 'O' && buf[1] == 'K' && (buf[2] == ' ' || buf[2] == '\n')) - return 0; /* okay */ + if (!value || !*value) + return 0; /* Avoid sending empty option values. */ - return -1; + line = xmalloc (7 + strlen (name) + 1 + strlen (value) + 1); + strcpy (stpcpy (stpcpy (stpcpy (line, "OPTION "), name), "="), value); + rc = assuan_transact (ctx, line, NULL, NULL, NULL, NULL, NULL, NULL); + xfree (line); + return rc? -1 : 0; } +/* Send all required options to the gpg-agent. */ static int -agent_send_all_options (int fd) +agent_send_all_options (assuan_context_t ctx) { char *dft_display = NULL; const char *dft_ttyname = NULL; @@ -370,20 +202,24 @@ agent_send_all_options (int fd) dft_display = getenv ("DISPLAY"); if (opt.display || dft_display) { - if (agent_send_option (fd, "display", + if (agent_send_option (ctx, "display", opt.display ? opt.display : dft_display)) return -1; } if (!opt.ttyname) { + const char *tmp; + dft_ttyname = getenv ("GPG_TTY"); - if ((!dft_ttyname || !*dft_ttyname) && tty_get_ttyname ()) - dft_ttyname = tty_get_ttyname (); + if ((!dft_ttyname || !*dft_ttyname) && (tmp=ttyname (0))) + dft_ttyname = tmp; + if ((!dft_ttyname || !*dft_ttyname) && (tmp=tty_get_ttyname ())) + dft_ttyname = tmp; } if (opt.ttyname || dft_ttyname) { - if (agent_send_option (fd, "ttyname", + if (agent_send_option (ctx, "ttyname", opt.ttyname ? opt.ttyname : dft_ttyname)) return -1; } @@ -391,7 +227,7 @@ agent_send_all_options (int fd) dft_ttytype = getenv ("TERM"); if (opt.ttytype || (dft_ttyname && dft_ttytype)) { - if (agent_send_option (fd, "ttytype", + if (agent_send_option (ctx, "ttytype", opt.ttyname ? opt.ttytype : dft_ttytype)) return -1; } @@ -404,7 +240,7 @@ agent_send_all_options (int fd) #endif if (opt.lc_ctype || (dft_ttyname && dft_lc)) { - rc = agent_send_option (fd, "lc-ctype", + rc = agent_send_option (ctx, "lc-ctype", opt.lc_ctype ? opt.lc_ctype : dft_lc); } #if defined(HAVE_SETLOCALE) && defined(LC_CTYPE) @@ -425,7 +261,7 @@ agent_send_all_options (int fd) #endif if (opt.lc_messages || (dft_ttyname && dft_lc)) { - rc = agent_send_option (fd, "lc-messages", + rc = agent_send_option (ctx, "lc-messages", opt.lc_messages ? opt.lc_messages : dft_lc); } #if defined(HAVE_SETLOCALE) && defined(LC_MESSAGES) @@ -437,170 +273,186 @@ agent_send_all_options (int fd) #endif return rc; } -#endif /*!_WIN32*/ +#endif /*ENABLE_AGENT_SUPPORT*/ /* - * Open a connection to the agent and send the magic string - * Returns: -1 on error or an filedescriptor for urther processing + * Open a connection to the agent and initializes the connection. + * Returns: -1 on error; on success an Assuan context for that + * connection is returned. With TRY set to true, no error messages + * are printed and the use of the agent won't get disabled on failure. + * If ORIG_CODESET is not NULL, the function will swithc the codeset + * back to that one before printing error messages. */ - -static int -agent_open (int *ret_prot) +#ifdef ENABLE_AGENT_SUPPORT +assuan_context_t +agent_open (int try, const char *orig_codeset) { -#if defined (_WIN32) - int fd; - char *infostr, *p; - HANDLE h; - char pidstr[128]; - - *ret_prot = 0; - if ( !(infostr = read_w32_registry_string(NULL, "Software\\GNU\\GnuPG", - "agentPID")) - || *infostr == '0') { - log_error( _("gpg-agent is not available in this session\n")); - return -1; - } - free(infostr); - - sprintf(pidstr, "%u", (unsigned int)GetCurrentProcessId()); - if (write_w32_registry_string(NULL, "Software\\GNU\\GnuPG", - "agentCID", pidstr)) { - log_error( _("can't set client pid for the agent\n") ); - return -1; - } - h = OpenEvent(EVENT_ALL_ACCESS, FALSE, "gpg_agent"); - SetEvent(h); - Sleep(50); /* some time for the server */ - if ( !(p = read_w32_registry_string(NULL, "Software\\GNU\\GnuPG", - "agentReadFD")) ) { - log_error( _("can't get server read FD for the agent\n") ); - return -1; - } - read_fd = atol(p); - free(p); - if ( !(p = read_w32_registry_string(NULL, "Software\\GNU\\GnuPG", - "agentWriteFD")) ) { - log_error ( _("can't get server write FD for the agent\n") ); - return -1; - } - write_fd = atol(p); - free(p); - fd = 0; + int rc; + assuan_context_t ctx; + char *infostr, *p; + int prot; + int pid; - if ( writen ( fd, "GPGA\0\0\0\x01", 8 ) ) { - fd = -1; + if (opt.gpg_agent_info) + infostr = xstrdup (opt.gpg_agent_info); + else + { + infostr = getenv ( "GPG_AGENT_INFO" ); + if (!infostr || !*infostr) + { + if (!try) + { +#ifdef ENABLE_NLS + if (orig_codeset) + bind_textdomain_codeset (PACKAGE, orig_codeset); +#endif /*ENABLE_NLS*/ + log_info (_("gpg-agent is not available in this session\n")); + opt.use_agent = 0; + } + return NULL; + } + infostr = xstrdup ( infostr ); } -#else /* Posix */ - - int fd; - char *infostr, *p; - struct sockaddr_un client_addr; - size_t len; - int prot; - - if (opt.gpg_agent_info) - infostr = xstrdup (opt.gpg_agent_info); - else - { - infostr = getenv ( "GPG_AGENT_INFO" ); - if ( !infostr || !*infostr ) { - log_error (_("gpg-agent is not available in this session\n")); + + if ( !(p = strchr (infostr, PATHSEP_C)) || p == infostr) + { + if (!try) + { +#ifdef ENABLE_NLS + if (orig_codeset) + bind_textdomain_codeset (PACKAGE, orig_codeset); +#endif /*ENABLE_NLS*/ + log_error ( _("malformed GPG_AGENT_INFO environment variable\n")); opt.use_agent = 0; - return -1; } - infostr = xstrdup ( infostr ); - } - - if ( !(p = strchr ( infostr, ':')) || p == infostr - || (p-infostr)+1 >= sizeof client_addr.sun_path ) { - log_error( _("malformed GPG_AGENT_INFO environment variable\n")); - xfree (infostr ); - opt.use_agent = 0; - return -1; - } - *p++ = 0; - /* See whether this is the new gpg-agent using the Assuna protocl. - This agent identifies itself by have an info string with a - version number in the 3rd field. */ - while (*p && *p != ':') - p++; - prot = *p? atoi (p+1) : 0; - if ( prot < 0 || prot > 1) { - log_error (_("gpg-agent protocol version %d is not supported\n"),prot); - xfree (infostr ); - opt.use_agent = 0; - return -1; + xfree (infostr); + return NULL; } - *ret_prot = prot; - - if( (fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1 ) { - log_error ("can't create socket: %s\n", strerror(errno) ); - xfree (infostr ); - opt.use_agent = 0; - return -1; + *p++ = 0; + pid = atoi (p); + while (*p && *p != PATHSEP_C) + p++; + prot = *p? atoi (p+1) : 0; + if (prot != 1) + { + if (!try) + { +#ifdef ENABLE_NLS + if (orig_codeset) + bind_textdomain_codeset (PACKAGE, orig_codeset); +#endif /*ENABLE_NLS*/ + log_error (_("gpg-agent protocol version %d is not supported\n"), + prot); + opt.use_agent = 0; + } + xfree (infostr); + return NULL; } - - memset( &client_addr, 0, sizeof client_addr ); - client_addr.sun_family = AF_UNIX; - strcpy( client_addr.sun_path, infostr ); - len = offsetof (struct sockaddr_un, sun_path) - + strlen(client_addr.sun_path) + 1; - - if( connect( fd, (struct sockaddr*)&client_addr, len ) == -1 ) { - log_error ( _("can't connect to `%s': %s\n"), - infostr, strerror (errno) ); - xfree (infostr ); - close (fd ); - opt.use_agent = 0; - return -1; + + rc = assuan_socket_connect (&ctx, infostr, pid); + if (rc) + { + if (!try) + { +#ifdef ENABLE_NLS + if (orig_codeset) + bind_textdomain_codeset (PACKAGE, orig_codeset); +#endif /*ENABLE_NLS*/ + log_error ( _("can't connect to `%s': %s\n"), + infostr, assuan_strerror (rc)); + opt.use_agent = 0; + } + xfree (infostr ); + return NULL; } - xfree (infostr); + xfree (infostr); - if (!prot) { - if ( writen ( fd, "GPGA\0\0\0\x01", 8 ) ) { - close (fd); - fd = -1; + if (agent_send_all_options (ctx)) + { + if (!try) + { +#ifdef ENABLE_NLS + if (orig_codeset) + bind_textdomain_codeset (PACKAGE, orig_codeset); +#endif /*ENABLE_NLS*/ + log_error (_("problem with the agent - disabling agent use\n")); + opt.use_agent = 0; } + assuan_disconnect (ctx); + return NULL; } - else { /* assuan based gpg-agent */ - char line[200]; - int nread; - - nread = readline (fd, line, DIM(line)); - if (nread < 3 || !(line[0] == 'O' && line[1] == 'K' - && (line[2] == '\n' || line[2] == ' ')) ) { - log_error ( _("communication problem with gpg-agent\n")); - close (fd ); - opt.use_agent = 0; - return -1; - } - if (agent_send_all_options (fd)) { - log_error (_("problem with the agent - disabling agent use\n")); - close (fd); - opt.use_agent = 0; - return -1; - } - - } -#endif + return ctx; +} +#endif/*ENABLE_AGENT_SUPPORT*/ - return fd; + +#ifdef ENABLE_AGENT_SUPPORT +void +agent_close (assuan_context_t ctx) +{ + assuan_disconnect (ctx); } +#endif /*ENABLE_AGENT_SUPPORT*/ -static void -agent_close ( int fd ) +/* Copy the text ATEXT into the buffer P and do plus '+' and percent + escaping. Note that the provided buffer needs to be 3 times the + size of ATEXT plus 1. Returns a pointer to the leading Nul in P. */ +#ifdef ENABLE_AGENT_SUPPORT +static char * +percent_plus_escape (char *p, const char *atext) { -#if defined (_WIN32) - HANDLE h = OpenEvent(EVENT_ALL_ACCESS, FALSE, "gpg_agent"); - ResetEvent(h); -#else - close (fd); -#endif + const unsigned char *s; + + for (s=atext; *s; s++) + { + if (*s < ' ' || *s == '+') + { + sprintf (p, "%%%02X", *s); + p += 3; + } + else if (*s == ' ') + *p++ = '+'; + else + *p++ = *s; + } + *p = 0; + return p; +} +#endif /*ENABLE_AGENT_SUPPORT*/ + + +#ifdef ENABLE_AGENT_SUPPORT + +/* Object for the agent_okay_cb function. */ +struct agent_okay_cb_s { + char *pw; +}; + +/* A callback used to get the passphrase from the okay line. See + agent-get_passphrase for details. LINE is the rest of the OK + status line without leading white spaces. */ +static assuan_error_t +agent_okay_cb (void *opaque, const char *line) +{ + struct agent_okay_cb_s *parm = opaque; + int i; + + /* Note: If the malloc below fails we won't be able to wipe the + memory at LINE given the current implementation of the Assuan + code. There is no easy ay around this w/o adding a lot of more + memory function code to allow wiping arbitrary stuff on memory + failure. */ + parm->pw = xmalloc_secure (strlen (line)/2+2); + + for (i=0; hexdigitp (line) && hexdigitp (line+1); line += 2) + parm->pw[i++] = xtoi_2 (line); + parm->pw[i] = 0; + return 0; } -#endif /* !__riscos__ */ +#endif /*ENABLE_AGENT_SUPPORT*/ @@ -612,26 +464,23 @@ agent_close ( int fd ) * * Note that TRYAGAIN_TEXT must not be translated. If canceled is not * NULL, the function does set it to 1 if the user canceled the - * operation. + * operation. If CACHEID is not NULL, it will be used as the cacheID + * for the gpg-agent; if is NULL and a key fingerprint can be + * computed, this will be used as the cacheid. */ static char * -agent_get_passphrase ( u32 *keyid, int mode, const char *tryagain_text, - int *canceled) +agent_get_passphrase ( u32 *keyid, int mode, const char *cacheid, + const char *tryagain_text, + const char *custom_description, + const char *custom_prompt, int *canceled) { -#if defined(__riscos__) - return NULL; -#else - size_t n; +#ifdef ENABLE_AGENT_SUPPORT char *atext = NULL; - char buf[50]; - int fd = -1; - int nread; - u32 reply; + assuan_context_t ctx = NULL; char *pw = NULL; - PKT_public_key *pk = xcalloc (1, sizeof *pk ); + PKT_public_key *pk = xmalloc_clear( sizeof *pk ); byte fpr[MAX_FINGERPRINT_LEN]; int have_fpr = 0; - int prot; char *orig_codeset = NULL; if (canceled) @@ -644,13 +493,14 @@ agent_get_passphrase ( u32 *keyid, int mode, const char *tryagain_text, memset (fpr, 0, MAX_FINGERPRINT_LEN ); if( keyid && get_pubkey( pk, keyid ) ) { - free_public_key( pk ); + if (pk) + free_public_key( pk ); pk = NULL; /* oops: no key for some reason */ } #ifdef ENABLE_NLS /* The Assuan agent protocol requires us to transmit utf-8 strings */ - orig_codeset = bind_textdomain_codeset (PACKAGE_GT, NULL); + orig_codeset = bind_textdomain_codeset (PACKAGE, NULL); #ifdef HAVE_LANGINFO_CODESET if (!orig_codeset) orig_codeset = nl_langinfo (CODESET); @@ -658,44 +508,58 @@ agent_get_passphrase ( u32 *keyid, int mode, const char *tryagain_text, if (orig_codeset) { /* We only switch when we are able to restore the codeset later. */ orig_codeset = xstrdup (orig_codeset); - if (!bind_textdomain_codeset (PACKAGE_GT, "utf-8")) + if (!bind_textdomain_codeset (PACKAGE, "utf-8")) orig_codeset = NULL; } #endif - if ( (fd = agent_open (&prot)) == -1 ) + if ( !(ctx = agent_open (0, orig_codeset)) ) goto failure; - if ( !mode && pk && keyid ) + if (custom_description) + atext = native_to_utf8 (custom_description); + else if ( !mode && pk && keyid ) { char *uid; size_t uidlen; - const char *algo_name = gcry_pk_algo_name ( pk->pubkey_algo ); + const char *algo_name = pubkey_algo_to_string ( pk->pubkey_algo ); const char *timestr; char *maink; if ( !algo_name ) algo_name = "?"; - + +#define KEYIDSTRING _(" (main key ID %s)") + + maink = xmalloc ( strlen (KEYIDSTRING) + keystrlen() + 20 ); if( keyid[2] && keyid[3] && keyid[0] != keyid[2] && keyid[1] != keyid[3] ) - maink = xasprintf ( _(" (main key ID %08lX)"), (ulong)keyid[3] ); + sprintf( maink, KEYIDSTRING, keystr(&keyid[2]) ); else - maink = NULL; + *maink = 0; uid = get_user_id ( keyid, &uidlen ); timestr = strtimestamp (pk->timestamp); - atext = xasprintf ( - _("You need a passphrase to unlock the" - " secret key for user:\n" - "\"%.*s\"\n" - "%u-bit %s key, ID %08lX, created %s%s\n" ), - uidlen, uid, - nbits_from_pk (pk), algo_name, (ulong)keyid[1], timestr, - maink?maink:"" ); + +#undef KEYIDSTRING + +#define PROMPTSTRING _("You need a passphrase to unlock the secret" \ + " key for user:\n" \ + "\"%.*s\"\n" \ + "%u-bit %s key, ID %s, created %s%s\n" ) + + atext = xmalloc ( 100 + strlen (PROMPTSTRING) + + uidlen + 15 + strlen(algo_name) + keystrlen() + + strlen (timestr) + strlen (maink) ); + sprintf (atext, PROMPTSTRING, + (int)uidlen, uid, + nbits_from_pk (pk), algo_name, keystr(&keyid[0]), timestr, + maink ); xfree (uid); xfree (maink); - + +#undef PROMPTSTRING + { size_t dummy; fingerprint_from_pk( pk, fpr, &dummy ); @@ -708,165 +572,80 @@ agent_get_passphrase ( u32 *keyid, int mode, const char *tryagain_text, else atext = xstrdup ( _("Enter passphrase\n") ); - if (!prot) - { /* old style protocol */ - n = 4 + 20 + strlen (atext); - u32tobuf (buf, n ); - u32tobuf (buf+4, GPGA_PROT_GET_PASSPHRASE ); - memcpy (buf+8, fpr, 20 ); - if ( writen ( fd, buf, 28 ) || writen ( fd, atext, strlen (atext) ) ) - goto failure; - xfree (atext); atext = NULL; - - /* get response */ - if ( readn ( fd, buf, 12, &nread ) ) - goto failure; - - if ( nread < 8 ) - { - log_error ( "response from agent too short\n" ); - goto failure; - } - n = buftou32 ( buf ); - reply = buftou32 ( buf + 4 ); - if ( reply == GPGA_PROT_GOT_PASSPHRASE ) - { - size_t pwlen; - size_t nn; - - if ( nread < 12 || n < 8 ) - { - log_error ( "response from agent too short\n" ); - goto failure; - } - pwlen = buftou32 ( buf + 8 ); - nread -= 12; - n -= 8; - if ( pwlen > n || n > 1000 ) - { - log_error (_("passphrase too long\n")); - /* or protocol error */ - goto failure; - } - /* we read the whole block in one chunk to give no hints - * on how long the passhrase actually is - this wastes some bytes - * but because we already have this padding we should not loosen - * this by issuing 2 read calls */ - pw = xmalloc_secure ( n+1 ); - if ( readn ( fd, pw, n, &nn ) ) - goto failure; - if ( n != nn ) - { - log_error (_("invalid response from agent\n")); - goto failure; - } - pw[pwlen] = 0; /* make a C String */ - agent_close (fd); - free_public_key( pk ); -#ifdef ENABLE_NLS - if (orig_codeset) - bind_textdomain_codeset (PACKAGE_GT, orig_codeset); -#endif - xfree (orig_codeset); - return pw; - } - else if ( reply == GPGA_PROT_CANCELED ) - { - log_info ( _("cancelled by user\n") ); - if (canceled) - *canceled = 1; - } - else - log_error ( _("problem with the agent: agent returns 0x%lx\n"), - (ulong)reply ); - } - else - { /* The new Assuan protocol */ + { char *line, *p; - const unsigned char *s; - int i; + int i, rc; + struct agent_okay_cb_s okay_cb_parm; if (!tryagain_text) tryagain_text = "X"; else tryagain_text = _(tryagain_text); - /* We allocate 2 time the needed space for atext so that there - is enough space for escaping */ + /* We allocate 23 times the needed space for thye texts so that + there is enough space for escaping. */ line = xmalloc (15 + 46 - + 3*strlen (tryagain_text) + 3*strlen (atext) + 2); + + 3*strlen (atext) + + 3*strlen (custom_prompt? custom_prompt:"") + + (cacheid? (3*strlen (cacheid)): 0) + + 3*strlen (tryagain_text) + + 1); strcpy (line, "GET_PASSPHRASE "); p = line+15; - if (!mode && have_fpr) + if (!mode && cacheid) + { + p = percent_plus_escape (p, cacheid); + } + else if (!mode && have_fpr) { for (i=0; i < 20; i++, p +=2 ) sprintf (p, "%02X", fpr[i]); } else - *p++ = 'X'; /* no caching */ - *p++ = ' '; - for (i=0, s=tryagain_text; *s; s++) - { - if (*s < ' ' || *s == '+') - { - sprintf (p, "%%%02X", *s); - p += 3; - } - else if (*s == ' ') - *p++ = '+'; - else - *p++ = *s; - } + *p++ = 'X'; /* No caching. */ *p++ = ' '; - *p++ = 'X'; /* Use the standard prompt */ + + p = percent_plus_escape (p, tryagain_text); *p++ = ' '; - /* copy description */ - for (i=0, s= atext; *s; s++) + + /* The prompt. */ + if (custom_prompt) { - if (*s < ' ' || *s == '+') - { - sprintf (p, "%%%02X", *s); - p += 3; - } - else if (*s == ' ') - *p++ = '+'; - else - *p++ = *s; + char *tmp = native_to_utf8 (custom_prompt); + p = percent_plus_escape (p, tmp); + xfree (tmp); } - *p++ = '\n'; - i = writen (fd, line, p - line); + else + *p++ = 'X'; /* Use the standard prompt. */ + *p++ = ' '; + + /* Copy description. */ + percent_plus_escape (p, atext); + + /* Call gpg-agent. */ + memset (&okay_cb_parm, 0, sizeof okay_cb_parm); + rc = assuan_transact2 (ctx, line, NULL, NULL, NULL, NULL, NULL, NULL, + agent_okay_cb, &okay_cb_parm); + xfree (line); - if (i) - goto failure; xfree (atext); atext = NULL; - - /* get response */ - pw = xmalloc_secure (500); - nread = readline (fd, pw, 499); - if (nread < 3) - goto failure; - - if (pw[0] == 'O' && pw[1] == 'K' && pw[2] == ' ') - { /* we got a passphrase - convert it back from hex */ - size_t pwlen = 0; - - for (i=3; i < nread && hexdigitp (pw+i); i+=2) - pw[pwlen++] = xtoi_2 (pw+i); - pw[pwlen] = 0; /* make a C String */ - agent_close (fd); - free_public_key( pk ); + if (!rc) + { + assert (okay_cb_parm.pw); + pw = okay_cb_parm.pw; + agent_close (ctx); + if (pk) + free_public_key( pk ); #ifdef ENABLE_NLS if (orig_codeset) - bind_textdomain_codeset (PACKAGE_GT, orig_codeset); + bind_textdomain_codeset (PACKAGE, orig_codeset); #endif xfree (orig_codeset); return pw; } - else if (nread > 4 && !memcmp (pw, "ERR ", 4) - && (0xffff & strtoul (&pw[4], NULL, 0)) == 99) + else if (rc && (rc & 0xffff) == 99) { - /* 99 is GPG_ERR_CANCELED. FIXME: Check tail and overflow, - and use gpg-error. */ + /* 99 is GPG_ERR_CANCELED. */ log_info (_("cancelled by user\n") ); if (canceled) *canceled = 1; @@ -876,41 +655,40 @@ agent_get_passphrase ( u32 *keyid, int mode, const char *tryagain_text, log_error (_("problem with the agent - disabling agent use\n")); opt.use_agent = 0; } - } + } failure: #ifdef ENABLE_NLS if (orig_codeset) - bind_textdomain_codeset (PACKAGE_GT, orig_codeset); + { + bind_textdomain_codeset (PACKAGE, orig_codeset); + xfree (orig_codeset); + } #endif xfree (atext); - if ( fd != -1 ) - agent_close (fd); + agent_close (ctx); xfree (pw ); - free_public_key( pk ); - + if (pk) + free_public_key( pk ); + +#endif /*ENABLE_AGENT_SUPPORT*/ + return NULL; -#endif /* Posix or W32 */ } + /* - * Clear the cached passphrase + * Clear the cached passphrase. If CACHEID is not NULL, it will be + * used instead of a cache ID derived from KEYID. */ void -passphrase_clear_cache ( u32 *keyid, int algo ) +passphrase_clear_cache ( u32 *keyid, const char *cacheid, int algo ) { -#if defined(__riscos__) - return ; -#else - size_t n; - char buf[200]; - int fd = -1; - size_t nread; - u32 reply; +#ifdef ENABLE_AGENT_SUPPORT + assuan_context_t ctx = NULL; PKT_public_key *pk; byte fpr[MAX_FINGERPRINT_LEN]; - int prot; #if MAX_FINGERPRINT_LEN < 20 #error agent needs a 20 byte fingerprint @@ -919,71 +697,50 @@ passphrase_clear_cache ( u32 *keyid, int algo ) if (!opt.use_agent) return; - pk = xcalloc (1, sizeof *pk ); - memset (fpr, 0, MAX_FINGERPRINT_LEN ); - if( !keyid || get_pubkey( pk, keyid ) ) + if (!cacheid) { - log_debug ("oops, no key in passphrase_clear_cache\n"); - goto failure; /* oops: no key for some reason */ - } + pk = xcalloc (1, sizeof *pk); + memset (fpr, 0, MAX_FINGERPRINT_LEN ); + if( !keyid || get_pubkey( pk, keyid ) ) + { + goto failure; /* oops: no key for some reason */ + } - { - size_t dummy; - fingerprint_from_pk( pk, fpr, &dummy ); - } + { + size_t dummy; + fingerprint_from_pk( pk, fpr, &dummy ); + } + } + else + pk = NULL; - if ( (fd = agent_open (&prot)) == -1 ) + if ( !(ctx = agent_open (0, NULL)) ) goto failure; - if (!prot) - { - n = 4 + 20; - u32tobuf (buf, n ); - u32tobuf (buf+4, GPGA_PROT_CLEAR_PASSPHRASE ); - memcpy (buf+8, fpr, 20 ); - if ( writen ( fd, buf, 28 ) ) - goto failure; - - /* get response */ - if ( readn ( fd, buf, 8, &nread ) ) - goto failure; - - if ( nread < 8 ) { - log_error ( "response from agent too short\n" ); - goto failure; - } - - reply = buftou32 ( buf + 4 ); - if ( reply != GPGA_PROT_OKAY && reply != GPGA_PROT_NO_PASSPHRASE ) + { + char *line, *p; + int i, rc; + + if (cacheid) { - log_error ( _("problem with the agent: agent returns 0x%lx\n"), - (ulong)reply ); + line = xmalloc (17 + 3*strlen (cacheid) + 2); + strcpy (line, "CLEAR_PASSPHRASE "); + p = line+17; + p = percent_plus_escape (p, cacheid); } - } - else - { /* The assuan protocol */ - char *line, *p; - int i; - - line = xmalloc (17 + 40 + 2); - strcpy (line, "CLEAR_PASSPHRASE "); - p = line+17; - for (i=0; i < 20; i++, p +=2 ) - sprintf (p, "%02X", fpr[i]); - *p++ = '\n'; - i = writen (fd, line, p - line); + else + { + line = xmalloc (17 + 40 + 2); + strcpy (line, "CLEAR_PASSPHRASE "); + p = line+17; + for (i=0; i < 20; i++, p +=2 ) + sprintf (p, "%02X", fpr[i]); + } + *p = 0; + + rc = assuan_transact (ctx, line, NULL, NULL, NULL, NULL, NULL, NULL); xfree (line); - if (i) - goto failure; - - /* get response */ - nread = readline (fd, buf, DIM(buf)-1); - if (nread < 3) - goto failure; - - if (buf[0] == 'O' && buf[1] == 'K' && (buf[2] == ' ' || buf[2] == '\n')) - ; - else + if (rc) { log_error (_("problem with the agent - disabling agent use\n")); opt.use_agent = 0; @@ -991,32 +748,85 @@ passphrase_clear_cache ( u32 *keyid, int algo ) } failure: - if (fd != -1) - agent_close (fd); - free_public_key( pk ); -#endif /* Posix or W32 */ + agent_close (ctx); + if (pk) + free_public_key( pk ); +#endif /*ENABLE_AGENT_SUPPORT*/ } - - /**************** - * Get a passphrase for the secret key with KEYID, display TEXT - * if the user needs to enter the passphrase. - * mode 0 = standard, 1 = same but don't show key info, - * 2 = create new passphrase - * Returns: a DEK with a session key; caller must free - * or NULL if the passphrase was not correctly repeated. - * (only for mode 2) - * a dek->keylen of 0 means: no passphrase entered. - * (only for mode 2) - * - * pubkey_algo is only informational. Note that TRYAGAIN_TEXT must - * not be translated as this is done within this function (required to - * switch to utf-8 when the agent is in use). If CANCELED is not - * NULL, it is set to 1 if the user choosed to cancel the operation, - * otherwise it will be set to 0. + * Ask for a passphrase and return that string. */ +char * +ask_passphrase (const char *description, + const char *tryagain_text, + const char *promptid, + const char *prompt, + const char *cacheid, int *canceled) +{ + char *pw = NULL; + + if (canceled) + *canceled = 0; + + if (!opt.batch && description) + { + if (strchr (description, '%')) + { + char *tmp = unescape_percent_string (description); + tty_printf ("\n%s\n", tmp); + xfree (tmp); + } + else + tty_printf ("\n%s\n",description); + } + + agent_died: + if ( opt.use_agent ) + { + pw = agent_get_passphrase (NULL, 0, cacheid, + tryagain_text, description, prompt, + canceled ); + if (!pw) + { + if (!opt.use_agent) + goto agent_died; + pw = NULL; + } + } + else if (fd_passwd) + { + pw = xmalloc_secure (strlen(fd_passwd)+1); + strcpy (pw, fd_passwd); + } + else if (opt.batch) + { + log_error(_("can't query passphrase in batch mode\n")); + pw = NULL; + } + else { + if (tryagain_text) + tty_printf(_("%s.\n"), tryagain_text); + pw = cpr_get_hidden(promptid? promptid : "passphrase.ask", + prompt?prompt : _("Enter passphrase: ") ); + tty_kill_prompt(); + } + + if (!pw || !*pw) + write_status( STATUS_MISSING_PASSPHRASE ); + + return pw; +} + + +/* Return a new DEK object Using the string-to-key sepcifier S2K. Use + * KEYID and PUBKEY_ALGO to prompt the user. + + MODE 0: Allow cached passphrase + 1: Ignore cached passphrase + 2: Ditto, but change the text to "repeat entry" +*/ DEK * passphrase_to_dek( u32 *keyid, int pubkey_algo, int cipher_algo, STRING2KEY *s2k, int mode, @@ -1034,12 +844,14 @@ passphrase_to_dek( u32 *keyid, int pubkey_algo, * Note: This must match the code in encode.c with opt.rfc1991 set */ s2k = &help_s2k; s2k->mode = 0; - s2k->hash_algo = opt.s2k_digest_algo; + s2k->hash_algo = S2K_DIGEST_ALGO; } + /* If we do not have a passphrase available in NEXT_PW and status + information are request, we print them now. */ if( !next_pw && is_status_enabled() ) { char buf[50]; - + if( keyid ) { u32 used_kid[2]; char *us; @@ -1055,7 +867,7 @@ passphrase_to_dek( u32 *keyid, int pubkey_algo, us = get_long_user_id_string( keyid ); write_status_text( STATUS_USERID_HINT, us ); - xfree (us); + xfree(us); sprintf( buf, "%08lX%08lX %08lX%08lX %d 0", (ulong)keyid[0], (ulong)keyid[1], @@ -1070,41 +882,55 @@ passphrase_to_dek( u32 *keyid, int pubkey_algo, } } + /* If we do have a keyID, we do not have a passphrase available in + NEXT_PW, we are not running in batch mode and we do not want to + ignore the passphrase cache (mode!=1), print a prompt with + information on that key. */ if( keyid && !opt.batch && !next_pw && mode!=1 ) { - PKT_public_key *pk = xcalloc (1, sizeof *pk ); - size_t n; + PKT_public_key *pk = xmalloc_clear( sizeof *pk ); char *p; - tty_printf(_("\nYou need a passphrase to unlock the secret key for\n" - "user: \"") ); - p = get_user_id( keyid, &n ); - tty_print_utf8_string( p, n ); - xfree (p); - tty_printf("\"\n"); + p=get_user_id_native(keyid); + tty_printf("\n"); + tty_printf(_("You need a passphrase to unlock the secret key for\n" + "user: \"%s\"\n"),p); + xfree(p); if( !get_pubkey( pk, keyid ) ) { - const char *s = gcry_pk_algo_name ( pk->pubkey_algo ); - tty_printf( _("%u-bit %s key, ID %08lX, created %s"), - nbits_from_pk( pk ), s?s:"?", (ulong)keyid[1], + const char *s = pubkey_algo_to_string( pk->pubkey_algo ); + tty_printf( _("%u-bit %s key, ID %s, created %s"), + nbits_from_pk( pk ), s?s:"?", keystr(keyid), strtimestamp(pk->timestamp) ); if( keyid[2] && keyid[3] && keyid[0] != keyid[2] && keyid[1] != keyid[3] ) - tty_printf( _(" (main key ID %08lX)"), (ulong)keyid[3] ); + { + if(keystrlen()>10) + { + tty_printf("\n"); + tty_printf(_(" (subkey on main key ID %s)"), + keystr(&keyid[2]) ); + } + else + tty_printf( _(" (main key ID %s)"), keystr(&keyid[2]) ); + } tty_printf("\n"); } tty_printf("\n"); - free_public_key( pk ); + if (pk) + free_public_key( pk ); } agent_died: if( next_pw ) { + /* Simply return the passphrase we already have in NEXT_PW. */ pw = next_pw; next_pw = NULL; } else if ( opt.use_agent ) { - pw = agent_get_passphrase ( keyid, mode == 2? 1: 0, - tryagain_text, canceled ); + /* Divert to the gpg-agent. */ + pw = agent_get_passphrase ( keyid, mode == 2? 1: 0, NULL, + tryagain_text, NULL, NULL, canceled ); if (!pw) { if (!opt.use_agent) @@ -1112,7 +938,8 @@ passphrase_to_dek( u32 *keyid, int pubkey_algo, pw = xstrdup (""); } if( *pw && mode == 2 ) { - char *pw2 = agent_get_passphrase ( keyid, 2, NULL, canceled ); + char *pw2 = agent_get_passphrase ( keyid, 2, NULL, NULL, NULL, + NULL, canceled ); if (!pw2) { if (!opt.use_agent) @@ -1124,22 +951,25 @@ passphrase_to_dek( u32 *keyid, int pubkey_algo, pw2 = xstrdup (""); } if( strcmp(pw, pw2) ) { - xfree (pw2); - xfree (pw); + xfree(pw2); + xfree(pw); return NULL; } - xfree (pw2); + xfree(pw2); } } else if( fd_passwd ) { - pw = xmalloc_secure ( strlen(fd_passwd)+1 ); + /* Return the passphrase we have store in FD_PASSWD. */ + pw = xmalloc_secure( strlen(fd_passwd)+1 ); strcpy( pw, fd_passwd ); } - else if( opt.batch ) { - log_error(_("can't query password in batchmode\n")); - pw = xstrdup ( "" ); /* return an empty passphrase */ - } + else if( opt.batch ) + { + log_error(_("can't query passphrase in batch mode\n")); + pw = xstrdup( "" ); /* return an empty passphrase */ + } else { + /* Read the passphrase from the tty or the command-fd. */ pw = cpr_get_hidden("passphrase.enter", _("Enter passphrase: ") ); tty_kill_prompt(); if( mode == 2 && !cpr_enabled() ) { @@ -1147,24 +977,27 @@ passphrase_to_dek( u32 *keyid, int pubkey_algo, _("Repeat passphrase: ") ); tty_kill_prompt(); if( strcmp(pw, pw2) ) { - xfree (pw2); - xfree (pw); + xfree(pw2); + xfree(pw); return NULL; } - xfree (pw2); + xfree(pw2); } } if( !pw || !*pw ) write_status( STATUS_MISSING_PASSPHRASE ); - dek = xcalloc_secure (1, sizeof *dek ); + /* Hash the passphrase and store it in a newly allocated DEK + object. Keep a copy of the passphrase in LAST_PW for use by + get_last_passphrase(). */ + dek = xmalloc_secure_clear ( sizeof *dek ); dek->algo = cipher_algo; if( !*pw && mode == 2 ) dek->keylen = 0; else hash_passphrase( dek, pw, s2k, mode==2 ); - xfree (last_pw); + xfree(last_pw); last_pw = pw; return dek; } @@ -1184,16 +1017,16 @@ hash_passphrase( DEK *dek, char *pw, STRING2KEY *s2k, int create ) int pwlen = strlen(pw); assert( s2k->hash_algo ); - dek->keylen = gcry_cipher_get_algo_keylen (dek->algo); + dek->keylen = cipher_get_keylen( dek->algo ) / 8; if( !(dek->keylen > 0 && dek->keylen <= DIM(dek->key)) ) BUG(); - gcry_md_open (&md, s2k->hash_algo, 1); + md = md_open( s2k->hash_algo, 1); for(pass=0; used < dek->keylen ; pass++ ) { if( pass ) { - gcry_md_reset(md); + md_reset(md); for(i=0; i < pass; i++ ) /* preset the hash context */ - gcry_md_putc (md, 0 ); + md_putc(md, 0 ); } if( s2k->mode == 1 || s2k->mode == 3 ) { @@ -1201,7 +1034,7 @@ hash_passphrase( DEK *dek, char *pw, STRING2KEY *s2k, int create ) ulong count = len2; if( create && !pass ) { - gcry_randomize(s2k->salt, 8, GCRY_STRONG_RANDOM ); + randomize_buffer(s2k->salt, 8, 1); if( s2k->mode == 3 ) s2k->count = 96; /* 65536 iterations */ } @@ -1213,27 +1046,27 @@ hash_passphrase( DEK *dek, char *pw, STRING2KEY *s2k, int create ) } /* a little bit complicated because we need a ulong for count */ while( count > len2 ) { /* maybe iterated+salted */ - gcry_md_write( md, s2k->salt, 8 ); - gcry_md_write( md, pw, pwlen ); + md_write( md, s2k->salt, 8 ); + md_write( md, pw, pwlen ); count -= len2; } if( count < 8 ) - gcry_md_write( md, s2k->salt, count ); + md_write( md, s2k->salt, count ); else { - gcry_md_write( md, s2k->salt, 8 ); + md_write( md, s2k->salt, 8 ); count -= 8; - gcry_md_write( md, pw, count ); + md_write( md, pw, count ); } } else - gcry_md_write( md, pw, pwlen ); - gcry_md_final ( md ); - i = gcry_md_get_algo_dlen (s2k->hash_algo); + md_write( md, pw, pwlen ); + md_final( md ); + i = md_digest_length( s2k->hash_algo ); if( i > dek->keylen - used ) i = dek->keylen - used; - memcpy( dek->key+used, gcry_md_read (md, s2k->hash_algo), i ); + memcpy( dek->key+used, md_read(md, s2k->hash_algo), i ); used += i; } - gcry_md_close (md); + md_close(md); } diff --git a/g10/photoid.c b/g10/photoid.c index 00cc7a273..cca32bc82 100644 --- a/g10/photoid.c +++ b/g10/photoid.c @@ -1,5 +1,5 @@ /* photoid.c - photo ID handling code - * Copyright (C) 2001, 2002 Free Software Foundation, Inc. + * Copyright (C) 2001, 2002, 2005 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -15,7 +15,8 @@ * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. */ #include <config.h> @@ -28,6 +29,8 @@ # define VER_PLATFORM_WIN32_WINDOWS 1 # endif #endif + +#include "gpg.h" #include "packet.h" #include "status.h" #include "exec.h" @@ -35,21 +38,23 @@ #include "util.h" #include "i18n.h" #include "iobuf.h" -#include "memory.h" #include "options.h" #include "main.h" #include "photoid.h" +#include "ttyio.h" /* Generate a new photo id packet, or return NULL if canceled */ -PKT_user_id *generate_photo_id(PKT_public_key *pk) +PKT_user_id * +generate_photo_id(PKT_public_key *pk,const char *photo_name) { PKT_user_id *uid; int error=1,i; unsigned int len; - char *filename=NULL; + char *filename; byte *photo=NULL; byte header[16]; - iobuf_t file; + IOBUF file; + int overflow; header[0]=0x10; /* little side of photo header length */ header[1]=0; /* big side of photo header length */ @@ -60,48 +65,78 @@ PKT_user_id *generate_photo_id(PKT_public_key *pk) header[i]=0; #define EXTRA_UID_NAME_SPACE 71 - uid=xcalloc (1,sizeof(*uid)+71); + uid=xmalloc_clear(sizeof(*uid)+71); - printf(_("\nPick an image to use for your photo ID. " - "The image must be a JPEG file.\n" - "Remember that the image is stored within your public key. " - "If you use a\n" - "very large picture, your key will become very large as well!\n" - "Keeping the image close to 240x288 is a good size to use.\n")); + if(photo_name && *photo_name) + filename=make_filename(photo_name,(void *)NULL); + else + { + tty_printf(_("\nPick an image to use for your photo ID." + " The image must be a JPEG file.\n" + "Remember that the image is stored within your public key." + " If you use a\n" + "very large picture, your key will become very large" + " as well!\n" + "Keeping the image close to 240x288 is a good size" + " to use.\n")); + filename=NULL; + } while(photo==NULL) { - printf("\n"); + if(filename==NULL) + { + char *tempname; - xfree (filename); + tty_printf("\n"); - filename=cpr_get("photoid.jpeg.add", - _("Enter JPEG filename for photo ID: ")); + tty_enable_completion(NULL); - if(strlen(filename)==0) - goto scram; + tempname=cpr_get("photoid.jpeg.add", + _("Enter JPEG filename for photo ID: ")); + + tty_disable_completion(); + + filename=make_filename(tempname,(void *)NULL); + + xfree(tempname); + + if(strlen(filename)==0) + goto scram; + } file=iobuf_open(filename); + if (file && is_secured_file (iobuf_get_fd (file))) + { + iobuf_close (file); + file = NULL; + errno = EPERM; + } if(!file) { - log_error(_("Unable to open photo \"%s\": %s\n"), + log_error(_("unable to open JPEG file `%s': %s\n"), filename,strerror(errno)); + xfree(filename); + filename=NULL; continue; } - len=iobuf_get_filelength(file); - if(len>6144) + + len=iobuf_get_filelength(file, &overflow); + if(len>6144 || overflow) { - printf("This JPEG is really large (%d bytes) !\n",len); + tty_printf( _("This JPEG is really large (%d bytes) !\n"),len); if(!cpr_get_answer_is_yes("photoid.jpeg.size", - _("Are you sure you want to use it (y/N)? "))) + _("Are you sure you want to use it? (y/N) "))) { iobuf_close(file); + xfree(filename); + filename=NULL; continue; } } - photo=xmalloc (len); + photo=xmalloc(len); iobuf_read(file,photo,len); iobuf_close(file); @@ -109,9 +144,11 @@ PKT_user_id *generate_photo_id(PKT_public_key *pk) if(photo[0]!=0xFF || photo[1]!=0xD8 || photo[6]!='J' || photo[7]!='F' || photo[8]!='I' || photo[9]!='F') { - log_error(_("\"%s\" is not a JPEG file\n"),filename); - xfree (photo); + log_error(_("`%s' is not a JPEG file\n"),filename); + xfree(photo); photo=NULL; + xfree(filename); + filename=NULL; continue; } @@ -132,8 +169,10 @@ PKT_user_id *generate_photo_id(PKT_public_key *pk) goto scram; case 0: free_attributes(uid); - xfree (photo); + xfree(photo); photo=NULL; + xfree(filename); + filename=NULL; continue; } } @@ -143,13 +182,13 @@ PKT_user_id *generate_photo_id(PKT_public_key *pk) uid->ref=1; scram: - xfree (filename); - xfree (photo); + xfree(filename); + xfree(photo); if(error) { free_attributes(uid); - xfree (uid); + xfree(uid); return NULL; } @@ -283,7 +322,7 @@ void show_photos(const struct user_attribute *attrs, if(!command) goto fail; - name=xmalloc (16+strlen(EXTSEP_S)+ + name=xmalloc(16+strlen(EXTSEP_S)+ strlen(image_type_to_string(args.imagetype,0))+1); /* Make the filename. Notice we are not using the image @@ -302,7 +341,7 @@ void show_photos(const struct user_attribute *attrs, if(exec_write(&spawn,NULL,command,name,1,1)!=0) { - xfree (name); + xfree(name); goto fail; } @@ -311,7 +350,7 @@ void show_photos(const struct user_attribute *attrs, image_type_to_string(args.imagetype,2)); #endif - xfree (name); + xfree(name); fwrite(&attrs[i].data[offset],attrs[i].len-offset,1,spawn->tochild); diff --git a/g10/photoid.h b/g10/photoid.h index 187ca5ba2..d13669c52 100644 --- a/g10/photoid.h +++ b/g10/photoid.h @@ -15,7 +15,8 @@ * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. */ /* Photo ID functions */ @@ -25,7 +26,7 @@ #include "packet.h" -PKT_user_id *generate_photo_id(PKT_public_key *pk); +PKT_user_id *generate_photo_id(PKT_public_key *pk,const char *filename); int parse_image_header(const struct user_attribute *attr,byte *type,u32 *len); char *image_type_to_string(byte type,int style); void show_photos(const struct user_attribute *attrs, diff --git a/g10/pipemode.c b/g10/pipemode.c deleted file mode 100644 index 9f2ddfdb5..000000000 --- a/g10/pipemode.c +++ /dev/null @@ -1,317 +0,0 @@ -/* pipemode.c - pipemode handler - * Copyright (C) 1998, 1990, 2000, 2001 Free Software Foundation, Inc. - * - * This file is part of GnuPG. - * - * GnuPG is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * GnuPG is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA - */ - -#include <config.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <errno.h> -#include <assert.h> - -#include "options.h" -#include "packet.h" -#include "errors.h" -#include "iobuf.h" -#include "keydb.h" -#include "memory.h" -#include "util.h" -#include "main.h" -#include "status.h" -#include "filter.h" - - -#define CONTROL_PACKET_SPACE 30 -#define FAKED_LITERAL_PACKET_SPACE (9+2+2) - - -enum pipemode_state_e { - STX_init = 0, - STX_wait_operation, - STX_begin, - STX_text, - STX_detached_signature, - STX_detached_signature_wait_text, - STX_signed_data, - STX_wait_init -}; - -struct pipemode_context_s { - enum pipemode_state_e state; - int operation; - int stop; - int block_mode; - UnarmorPump unarmor_ctx; -}; - - -static size_t -make_control ( byte *buf, int code, int operation ) -{ - const byte *sesmark; - size_t sesmarklen, n=0;; - - sesmark = get_session_marker( &sesmarklen ); - if ( sesmarklen > 20 ) - BUG(); - - buf[n++] = 0xff; /* new format, type 63, 1 length byte */ - n++; /* length will fixed below */ - memcpy(buf+n, sesmark, sesmarklen ); n+= sesmarklen; - buf[n++] = CTRLPKT_PIPEMODE; - buf[n++] = code; - buf[n++] = operation; - buf[1] = n-2; - return n; -} - - - -static int -pipemode_filter( void *opaque, int control, - iobuf_t a, byte *buf, size_t *ret_len) -{ - size_t size = *ret_len; - struct pipemode_context_s *stx = opaque; - int rc=0; - size_t n = 0; - int esc = 0; - - if( control == IOBUFCTRL_UNDERFLOW ) { - *ret_len = 0; - /* reserve some space for one control packet */ - if ( size <= CONTROL_PACKET_SPACE+FAKED_LITERAL_PACKET_SPACE ) - BUG(); - size -= CONTROL_PACKET_SPACE+FAKED_LITERAL_PACKET_SPACE; - - if ( stx->block_mode ) { - /* reserve 2 bytes for the block length */ - buf[n++] = 0; - buf[n++] = 0; - } - - - while ( n < size ) { - /* FIXME: we have to make sure that we have a large enough - * buffer for a control packet even after we already read - * something. The easest way to do this is probably by ungetting - * the control sequence and returning the buffer we have - * already assembled */ - int c = iobuf_get (a); - if (c == -1) { - if ( stx->state != STX_init ) { - log_error ("EOF encountered at wrong state\n"); - stx->stop = 1; - return -1; - } - break; - } - if ( esc ) { - switch (c) { - case '@': - if ( stx->state == STX_text ) { - buf[n++] = c; - break; - } - else if ( stx->state == STX_detached_signature ) { - esc = 0; - goto do_unarmor; /* not a very elegant solution */ - } - else if ( stx->state == STX_detached_signature_wait_text) { - esc = 0; - break; /* just ignore it in this state */ - } - log_error ("@@ not allowed in current state\n"); - return -1; - case '<': /* begin of stream part */ - if ( stx->state != STX_init ) { - log_error ("nested begin of stream\n"); - stx->stop = 1; - return -1; - } - stx->state = STX_wait_operation; - stx->block_mode = 0; - unarmor_pump_release (stx->unarmor_ctx); - stx->unarmor_ctx = NULL; - break; - case '>': /* end of stream part */ - if ( stx->state != STX_wait_init ) { - log_error ("invalid state for @>\n"); - stx->stop = 1; - return -1; - } - stx->state = STX_init; - break; - case 'V': /* operation = verify */ - case 'E': /* operation = encrypt */ - case 'S': /* operation = sign */ - case 'B': /* operation = detach sign */ - case 'C': /* operation = clearsign */ - case 'D': /* operation = decrypt */ - if ( stx->state != STX_wait_operation ) { - log_error ("invalid state for operation code\n"); - stx->stop = 1; - return -1; - } - stx->operation = c; - if ( stx->operation == 'B') { - stx->state = STX_detached_signature; - if ( !opt.no_armor ) - stx->unarmor_ctx = unarmor_pump_new (); - } - else - stx->state = STX_begin; - n += make_control ( buf+n, 1, stx->operation ); - /* must leave after a control packet */ - goto leave; - - case 't': /* plaintext text follows */ - if ( stx->state == STX_detached_signature_wait_text ) - stx->state = STX_detached_signature; - if ( stx->state == STX_detached_signature ) { - if ( stx->operation != 'B' ) { - log_error ("invalid operation for this state\n"); - stx->stop = 1; - return -1; - } - stx->state = STX_signed_data; - n += make_control ( buf+n, 2, 'B' ); - /* and now we fake a literal data packet much the same - * as in armor.c */ - buf[n++] = 0xaf; /* old packet format, type 11, - var length */ - buf[n++] = 0; /* set the length header */ - buf[n++] = 6; - buf[n++] = 'b'; /* we ignore it anyway */ - buf[n++] = 0; /* namelength */ - memset(buf+n, 0, 4); /* timestamp */ - n += 4; - /* and return now so that we are sure to have - * more space in the bufer for the next control - * packet */ - stx->block_mode = 1; - goto leave2; - } - else { - log_error ("invalid state for @t\n"); - stx->stop = 1; - return -1; - } - break; - - case '.': /* ready */ - if ( stx->state == STX_signed_data ) { - if (stx->block_mode) { - buf[0] = (n-2) >> 8; - buf[1] = (n-2); - if ( buf[0] || buf[1] ) { - /* end of blocks marker */ - buf[n++] = 0; - buf[n++] = 0; - } - stx->block_mode = 0; - } - n += make_control ( buf+n, 3, 'B' ); - } - else { - log_error ("invalid state for @.\n"); - stx->stop = 1; - return -1; - } - stx->state = STX_wait_init; - goto leave; - - default: - log_error ("invalid escape sequence 0x%02x in stream\n", - c); - stx->stop = 1; - return -1; - } - esc = 0; - } - else if (c == '@') - esc = 1; - else if (stx->unarmor_ctx) { - do_unarmor: /* used to handle a @@ */ - c = unarmor_pump (stx->unarmor_ctx, c); - if ( !(c & ~255) ) - buf[n++] = c; - else if ( c < 0 ) { - /* end of armor or error - we don't care becuase - the armor can be modified anyway. The unarmored - stuff should stand for itself. */ - unarmor_pump_release (stx->unarmor_ctx); - stx->unarmor_ctx = NULL; - stx->state = STX_detached_signature_wait_text; - } - } - else if (stx->state == STX_detached_signature_wait_text) - ; /* just wait */ - else - buf[n++] = c; - } - - leave: - if ( !n ) { - stx->stop = 1; - rc = -1; /* eof */ - } - if ( stx->block_mode ) { - /* fixup the block length */ - buf[0] = (n-2) >> 8; - buf[1] = (n-2); - } - leave2: - /*log_hexdump ("pipemode:", buf, n );*/ - *ret_len = n; - } - else if( control == IOBUFCTRL_DESC ) - *(char**)buf = "pipemode_filter"; - return rc; -} - - - -void -run_in_pipemode(void) -{ - iobuf_t fp; - armor_filter_context_t afx; - struct pipemode_context_s stx; - int rc; - - memset( &afx, 0, sizeof afx); - memset( &stx, 0, sizeof stx); - - fp = iobuf_open("-"); - iobuf_push_filter (fp, pipemode_filter, &stx ); - - do { - write_status (STATUS_BEGIN_STREAM); - rc = proc_packets( NULL, fp ); - write_status (STATUS_END_STREAM); - } while ( !stx.stop ); - -} - - - - - - diff --git a/g10/pkclist.c b/g10/pkclist.c index 71e6492e8..793ac6902 100644 --- a/g10/pkclist.c +++ b/g10/pkclist.c @@ -1,6 +1,6 @@ -/* pkclist.c - * Copyright (C) 1998, 1999, 2000, 2001, 2002 - * 2003 Free Software Foundation, Inc. +/* pkclist.c - create a list of public keys + * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, + * 2004, 2005 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -16,7 +16,8 @@ * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. */ #include <config.h> @@ -26,11 +27,11 @@ #include <errno.h> #include <assert.h> +#include "gpg.h" #include "options.h" #include "packet.h" #include "errors.h" #include "keydb.h" -#include "memory.h" #include "util.h" #include "main.h" #include "trustdb.h" @@ -39,10 +40,8 @@ #include "photoid.h" #include "i18n.h" - #define CONTROL_D ('D' - 'A' + 1) - /**************** * Show the revocation reason as it is stored with the given signature */ @@ -74,10 +73,10 @@ do_show_revocation_reason( PKT_signature *sig ) log_info( _("reason for revocation: ") ); if( text ) - fputs( text, log_get_stream () ); + fputs( text, log_get_stream() ); else - fprintf( log_get_stream (), "code=%02x", *p ); - putc( '\n', log_get_stream () ); + fprintf( log_get_stream(), "code=%02x", *p ); + putc( '\n', log_get_stream() ); n--; p++; pp = NULL; do { @@ -158,74 +157,6 @@ show_revocation_reason( PKT_public_key *pk, int mode ) } -static void -show_paths (const PKT_public_key *pk, int only_first ) -{ - log_debug("not yet implemented\n"); -#if 0 - void *context = NULL; - unsigned otrust, validity; - int last_level, level; - - last_level = 0; - while( (level=enum_cert_paths( &context, &lid, &otrust, &validity)) != -1){ - char *p; - int c, rc; - size_t n; - u32 keyid[2]; - PKT_public_key *pk ; - - if( level < last_level && only_first ) - break; - last_level = level; - - rc = keyid_from_lid( lid, keyid ); - - if( rc ) { - log_error("ooops: can't get keyid for lid %lu\n", lid); - return; - } - - pk = xcalloc (1, sizeof *pk ); - rc = get_pubkey( pk, keyid ); - if( rc ) { - log_error("key %08lX: public key not found: %s\n", - (ulong)keyid[1], gpg_strerror (rc) ); - return; - } - - tty_printf("%*s%4u%c/%08lX.%lu %s \"", - level*2, "", - nbits_from_pk( pk ), pubkey_letter( pk->pubkey_algo ), - (ulong)keyid[1], lid, datestr_from_pk( pk ) ); - - c = trust_letter(otrust); - if( c ) - putchar( c ); - else - printf( "%02x", otrust ); - putchar('/'); - c = trust_letter(validity); - if( c ) - putchar( c ); - else - printf( "%02x", validity ); - putchar(' '); - - p = get_user_id( keyid, &n ); - tty_print_utf8_string( p, n ), - xfree (p); - tty_printf("\"\n"); - free_public_key( pk ); - } - enum_cert_paths( &context, NULL, NULL, NULL ); /* release context */ -#endif - tty_printf("\n"); -} - - - - /**************** * mode: 0 = standard * 1 = Without key info and additional menu option 'm' @@ -241,7 +172,6 @@ do_edit_ownertrust (PKT_public_key *pk, int mode, unsigned *new_trust, int defer_help ) { char *p; - size_t n; u32 keyid[2]; int changed=0; int quit=0; @@ -252,7 +182,7 @@ do_edit_ownertrust (PKT_public_key *pk, int mode, switch(minimum) { - default: min_num=0; break; + default: case TRUST_UNDEFINED: min_num=1; break; case TRUST_NEVER: min_num=2; break; case TRUST_MARGINAL: min_num=3; break; @@ -261,7 +191,18 @@ do_edit_ownertrust (PKT_public_key *pk, int mode, keyid_from_pk (pk, keyid); for(;;) { - /* a string with valid answers */ + /* A string with valid answers. + + Note to translators: These are the allowed answers in lower and + uppercase. Below you will find the matching strings which + should be translated accordingly and the letter changed to + match the one in the answer string. + + i = please show me more information + m = back to the main menu + s = skip this key + q = quit + */ const char *ans = _("iImMqQsS"); if( !did_help ) @@ -270,69 +211,82 @@ do_edit_ownertrust (PKT_public_key *pk, int mode, { KBNODE keyblock, un; - tty_printf(_("No trust value assigned to:\n" - "%4u%c/%08lX %s \""), - nbits_from_pk( pk ), pubkey_letter( pk->pubkey_algo ), - (ulong)keyid[1], datestr_from_pk( pk ) ); - p = get_user_id( keyid, &n ); - tty_print_utf8_string( p, n ), - xfree (p); - tty_printf("\"\n"); + tty_printf(_("No trust value assigned to:\n")); + tty_printf("%4u%c/%s %s\n",nbits_from_pk( pk ), + pubkey_letter( pk->pubkey_algo ), + keystr(keyid), datestr_from_pk( pk ) ); + p=get_user_id_native(keyid); + tty_printf(_(" \"%s\"\n"),p); + xfree(p); keyblock = get_pubkeyblock (keyid); if (!keyblock) BUG (); - for (un=keyblock; un; un = un->next) { + for (un=keyblock; un; un = un->next) + { if (un->pkt->pkttype != PKT_USER_ID ) - continue; + continue; if (un->pkt->pkt.user_id->is_revoked ) - continue; + continue; if (un->pkt->pkt.user_id->is_expired ) - continue; + continue; /* Only skip textual primaries */ - if (un->pkt->pkt.user_id->is_primary && - !un->pkt->pkt.user_id->attrib_data ) - continue; + if (un->pkt->pkt.user_id->is_primary + && !un->pkt->pkt.user_id->attrib_data ) + continue; if((opt.verify_options&VERIFY_SHOW_PHOTOS) && un->pkt->pkt.user_id->attrib_data) - show_photos(un->pkt->pkt.user_id->attribs, - un->pkt->pkt.user_id->numattribs,pk,NULL); - - tty_printf (" %s", _(" aka \"")); - tty_print_utf8_string (un->pkt->pkt.user_id->name, - un->pkt->pkt.user_id->len ); - tty_printf("\"\n"); - } + show_photos(un->pkt->pkt.user_id->attribs, + un->pkt->pkt.user_id->numattribs,pk,NULL); + + p=utf8_to_native(un->pkt->pkt.user_id->name, + un->pkt->pkt.user_id->len,0); + + tty_printf(_(" aka \"%s\"\n"),p); + } print_fingerprint (pk, NULL, 2); tty_printf("\n"); + release_kbnode (keyblock); } - /* This string also used in keyedit.c:sign_uids */ - tty_printf (_( - "Please decide how far you trust this user to correctly\n" - "verify other users' keys (by looking at passports,\n" - "checking fingerprints from different sources...)?\n\n")); + + if(opt.trust_model==TM_DIRECT) + { + tty_printf(_("How much do you trust that this key actually " + "belongs to the named user?\n")); + tty_printf("\n"); + } + else + { + /* This string also used in keyedit.c:trustsig_prompt */ + tty_printf(_("Please decide how far you trust this user to" + " correctly verify other users' keys\n" + "(by looking at passports, checking fingerprints from" + " different sources, etc.)\n")); + tty_printf("\n"); + } + if(min_num<=1) - tty_printf (_(" %d = I don't know\n"), 1); + tty_printf (_(" %d = I don't know or won't say\n"), 1); if(min_num<=2) - tty_printf (_(" %d = I do NOT trust\n"), 2); + tty_printf (_(" %d = I do NOT trust\n"), 2); if(min_num<=3) - tty_printf (_(" %d = I trust marginally\n"), 3); + tty_printf (_(" %d = I trust marginally\n"), 3); if(min_num<=4) - tty_printf (_(" %d = I trust fully\n"), 4); + tty_printf (_(" %d = I trust fully\n"), 4); if (mode) - tty_printf (_(" %d = I trust ultimately\n"), 5); + tty_printf (_(" %d = I trust ultimately\n"), 5); #if 0 /* not yet implemented */ - tty_printf (_(" i = please show me more information\n") ); + tty_printf (" i = please show me more information\n"); #endif if( mode ) - tty_printf(_(" m = back to the main menu\n")); + tty_printf(_(" m = back to the main menu\n")); else { - tty_printf(_(" s = skip this key\n")); - tty_printf(_(" q = quit\n")); + tty_printf(_(" s = skip this key\n")); + tty_printf(_(" q = quit\n")); } tty_printf("\n"); if(minimum) @@ -364,7 +318,7 @@ do_edit_ownertrust (PKT_public_key *pk, int mode, if (trust == TRUST_ULTIMATE && !cpr_get_answer_is_yes ("edit_ownertrust.set_ultimate.okay", _("Do you really want to set this key" - " to ultimate trust? "))) + " to ultimate trust? (y/N) "))) ; /* no */ else { @@ -395,9 +349,9 @@ do_edit_ownertrust (PKT_public_key *pk, int mode, quit = 1; break ; /* back to the menu */ } - xfree (p); p = NULL; + xfree(p); p = NULL; } - xfree (p); + xfree(p); return show? -2: quit? -1 : changed; } @@ -419,7 +373,6 @@ edit_ownertrust (PKT_public_key *pk, int mode ) case -1: /* quit */ return -1; case -2: /* show info */ - show_paths(pk, 1); no_help = 1; break; case 1: /* trust value set */ @@ -439,93 +392,54 @@ edit_ownertrust (PKT_public_key *pk, int mode ) * Returns: true if we trust. */ static int -do_we_trust( PKT_public_key *pk, unsigned int *trustlevel ) +do_we_trust( PKT_public_key *pk, unsigned int trustlevel ) { - unsigned int trustmask = 0; - - /* FIXME: get_pubkey_byname already checks the validity and won't - * return keys which are either expired or revoked - so these - * question here won't get triggered. We have to find a solution - * for this. It might make sense to have a function in getkey.c - * which does only the basic checks and returns even revoked and - * expired keys. This fnction could then also returhn a list of - * keys if the speicified name is ambiguous - */ - if( (*trustlevel & TRUST_FLAG_REVOKED) ) { - log_info(_("key %08lX: key has been revoked!\n"), - (ulong)keyid_from_pk( pk, NULL) ); - show_revocation_reason( pk, 0 ); - if( opt.batch ) - return 0; /* no */ - - if( !cpr_get_answer_is_yes("revoked_key.override", - _("Use this key anyway? ")) ) - return 0; /* no */ - trustmask |= TRUST_FLAG_REVOKED; + /* We should not be able to get here with a revoked or expired + key */ + if(trustlevel & TRUST_FLAG_REVOKED + || trustlevel & TRUST_FLAG_SUB_REVOKED + || (trustlevel & TRUST_MASK) == TRUST_EXPIRED) + BUG(); + + if( opt.trust_model==TM_ALWAYS ) + { + if( opt.verbose ) + log_info("No trust check due to `--trust-model always' option\n"); + return 1; } - if( (*trustlevel & TRUST_FLAG_SUB_REVOKED) ) { - log_info(_("key %08lX: subkey has been revoked!\n"), - (ulong)keyid_from_pk( pk, NULL) ); - show_revocation_reason( pk, 0 ); - if( opt.batch ) - return 0; - if( !cpr_get_answer_is_yes("revoked_key.override", - _("Use this key anyway? ")) ) - return 0; - trustmask |= TRUST_FLAG_SUB_REVOKED; - } - *trustlevel &= ~trustmask; + switch(trustlevel & TRUST_MASK) + { + default: + log_error ("invalid trustlevel %u returned from validation layer\n", + trustlevel); + /* fall thru */ + case TRUST_UNKNOWN: + case TRUST_UNDEFINED: + log_info(_("%s: There is no assurance this key belongs" + " to the named user\n"),keystr_from_pk(pk)); + return 0; /* no */ - if( opt.trust_model==TM_ALWAYS ) { - if( opt.verbose ) - log_info("No trust check due to --trust-model always option\n"); - return 1; - } + case TRUST_MARGINAL: + log_info(_("%s: There is limited assurance this key belongs" + " to the named user\n"),keystr_from_pk(pk)); + return 1; /* yes */ - switch( (*trustlevel & TRUST_MASK) ) { - case TRUST_EXPIRED: - log_info(_("%08lX: key has expired\n"), - (ulong)keyid_from_pk( pk, NULL) ); - return 0; /* no */ - - default: - log_error ("invalid trustlevel %u returned from validation layer\n", - *trustlevel); - /* fall thru */ - case TRUST_UNKNOWN: - case TRUST_UNDEFINED: - log_info(_("%08lX: There is no assurance this key belongs " - "to the named user\n"),(ulong)keyid_from_pk( pk, NULL) ); - return 0; /* no */ - - /* No way to get here? */ - case TRUST_NEVER: - log_info(_("%08lX: We do NOT trust this key\n"), - (ulong)keyid_from_pk( pk, NULL) ); - return 0; /* no */ - - case TRUST_MARGINAL: - log_info(_("%08lX: There is limited assurance this key belongs " - "to the named user\n"),(ulong)keyid_from_pk(pk,NULL)); - return 1; /* yes */ - - case TRUST_FULLY: - if( opt.verbose ) - log_info(_("This key probably belongs to the named user\n")); - return 1; /* yes */ - - case TRUST_ULTIMATE: - if( opt.verbose ) - log_info(_("This key belongs to us\n")); - return 1; /* yes */ + case TRUST_FULLY: + if( opt.verbose ) + log_info(_("This key probably belongs to the named user\n")); + return 1; /* yes */ + + case TRUST_ULTIMATE: + if( opt.verbose ) + log_info(_("This key belongs to us\n")); + return 1; /* yes */ } - return 1; /* yes */ + return 1; /*NOTREACHED*/ } - /**************** * wrapper around do_we_trust, so we can ask whether to use the * key anyway. @@ -533,58 +447,34 @@ do_we_trust( PKT_public_key *pk, unsigned int *trustlevel ) static int do_we_trust_pre( PKT_public_key *pk, unsigned int trustlevel ) { - int rc; + int rc; - rc = do_we_trust( pk, &trustlevel ); + rc = do_we_trust( pk, trustlevel ); - if( (trustlevel & TRUST_FLAG_REVOKED) && !rc ) - return 0; - if( (trustlevel & TRUST_FLAG_SUB_REVOKED) && !rc ) - return 0; + if( !opt.batch && !rc ) + { + print_pubkey_info(NULL,pk); + print_fingerprint (pk, NULL, 2); + tty_printf("\n"); - if( !opt.batch && !rc ) { - u32 keyid[2]; - - keyid_from_pk( pk, keyid); - tty_printf( "%4u%c/%08lX %s \"", - nbits_from_pk( pk ), pubkey_letter( pk->pubkey_algo ), - (ulong)keyid[1], datestr_from_pk( pk ) ); - /* If the pk was chosen by a particular user ID, this is the - one to ask about. */ - if(pk->user_id) - tty_print_utf8_string(pk->user_id->name,pk->user_id->len); - else - { - size_t n; - char *p = get_user_id( keyid, &n ); - tty_print_utf8_string( p, n ); - xfree (p); - } - tty_printf("\"\n"); - print_fingerprint (pk, NULL, 2); - tty_printf("\n"); - - tty_printf(_( -"It is NOT certain that the key belongs to the person named\n" -"in the user ID. If you *really* know what you are doing,\n" -"you may answer the next question with yes\n\n")); - - if( cpr_get_answer_is_yes("untrusted_key.override", - _("Use this key anyway? ")) ) - rc = 1; - - /* Hmmm: Should we set a flag to tell the user about - * his decision the next time he encrypts for this recipient? - */ - } - else if( opt.trust_model==TM_ALWAYS && !rc ) { - if( !opt.quiet ) - log_info(_("WARNING: Using untrusted key!\n")); + tty_printf( + _("It is NOT certain that the key belongs to the person named\n" + "in the user ID. If you *really* know what you are doing,\n" + "you may answer the next question with yes.\n")); + + tty_printf("\n"); + + if( cpr_get_answer_is_yes("untrusted_key.override", + _("Use this key anyway? (y/N) ")) ) rc = 1; + + /* Hmmm: Should we set a flag to tell the user about + * his decision the next time he encrypts for this recipient? + */ } - return rc; -} + return rc; +} /**************** @@ -594,7 +484,7 @@ do_we_trust_pre( PKT_public_key *pk, unsigned int trustlevel ) int check_signatures_trust( PKT_signature *sig ) { - PKT_public_key *pk = xcalloc (1, sizeof *pk ); + PKT_public_key *pk = xmalloc_clear( sizeof *pk ); unsigned int trustlevel; int rc=0; @@ -602,7 +492,7 @@ check_signatures_trust( PKT_signature *sig ) if (rc) { /* this should not happen */ log_error("Ooops; the key vanished - can't check the trust\n"); - rc = GPG_ERR_NO_PUBKEY; + rc = G10ERR_NO_PUBKEY; goto leave; } @@ -615,13 +505,21 @@ check_signatures_trust( PKT_signature *sig ) goto leave; } + if(pk->maybe_revoked && !pk->is_revoked) + log_info(_("WARNING: this key might be revoked (revocation key" + " not present)\n")); + trustlevel = get_validity (pk, NULL); if ( (trustlevel & TRUST_FLAG_REVOKED) ) { write_status( STATUS_KEYREVOKED ); - log_info(_("WARNING: This key has been revoked by its owner!\n")); - log_info(_(" This could mean that the signature is forgery.\n")); + if(pk->is_revoked==2) + log_info(_("WARNING: This key has been revoked by its" + " designated revoker!\n")); + else + log_info(_("WARNING: This key has been revoked by its owner!\n")); + log_info(_(" This could mean that the signature is forged.\n")); show_revocation_reason( pk, 0 ); } else if ((trustlevel & TRUST_FLAG_SUB_REVOKED) ) @@ -634,6 +532,59 @@ check_signatures_trust( PKT_signature *sig ) if ((trustlevel & TRUST_FLAG_DISABLED)) log_info (_("Note: This key has been disabled.\n")); + /* If we have PKA information adjust the trustlevel. */ + if (sig->pka_info && sig->pka_info->valid) + { + unsigned char fpr[MAX_FINGERPRINT_LEN]; + PKT_public_key *primary_pk; + size_t fprlen; + int okay; + + + primary_pk = xmalloc_clear (sizeof *primary_pk); + get_pubkey (primary_pk, pk->main_keyid); + fingerprint_from_pk (primary_pk, fpr, &fprlen); + free_public_key (primary_pk); + + if ( fprlen == 20 && !memcmp (sig->pka_info->fpr, fpr, 20) ) + { + okay = 1; + write_status_text (STATUS_PKA_TRUST_GOOD, sig->pka_info->email); + log_info (_("Note: Verified signer's address is `%s'\n"), + sig->pka_info->email); + } + else + { + okay = 0; + write_status_text (STATUS_PKA_TRUST_BAD, sig->pka_info->email); + log_info (_("Note: Signer's address `%s' " + "does not match DNS entry\n"), sig->pka_info->email); + } + + switch ( (trustlevel & TRUST_MASK) ) + { + case TRUST_UNKNOWN: + case TRUST_UNDEFINED: + case TRUST_MARGINAL: + if (okay && opt.verify_options&VERIFY_PKA_TRUST_INCREASE) + { + trustlevel = ((trustlevel & ~TRUST_MASK) | TRUST_FULLY); + log_info (_("trustlevel adjusted to FULL" + " due to valid PKA info\n")); + } + /* (fall through) */ + case TRUST_FULLY: + if (!okay) + { + trustlevel = ((trustlevel & ~TRUST_MASK) | TRUST_NEVER); + log_info (_("trustlevel adjusted to NEVER" + " due to bad PKA info\n")); + } + break; + } + } + + /* Now let the user know what up with the trustlevel. */ switch ( (trustlevel & TRUST_MASK) ) { case TRUST_EXPIRED: @@ -701,7 +652,7 @@ release_pk_list( PK_LIST pk_list ) for( ; pk_list; pk_list = pk_rover ) { pk_rover = pk_list->next; free_public_key( pk_list->pk ); - xfree ( pk_list ); + xfree( pk_list ); } } @@ -730,10 +681,10 @@ default_recipient(void) int i; if( opt.def_recipient ) - return xstrdup ( opt.def_recipient ); + return xstrdup( opt.def_recipient ); if( !opt.def_recipient_self ) return NULL; - sk = xcalloc (1, sizeof *sk ); + sk = xmalloc_clear( sizeof *sk ); i = get_seckey_byname( sk, NULL, 0 ); if( i ) { free_secret_key( sk ); @@ -742,7 +693,7 @@ default_recipient(void) n = MAX_FINGERPRINT_LEN; fingerprint_from_sk( sk, fpr, &n ); free_secret_key( sk ); - p = xmalloc ( 2*n+3 ); + p = xmalloc( 2*n+3 ); *p++ = '0'; *p++ = 'x'; for(i=0; i < n; i++ ) @@ -797,315 +748,423 @@ expand_group(STRLIST input) return output; } + +/* This is the central function to collect the keys for recipients. + It is thus used to prepare a public key encryption. encrypt-to + keys, default keys and the keys for the actual recipients are all + collected here. When not in batch mode and no recipient has been + passed on the commandline, the function will also ask for + recipients. + + RCPTS is a string list with the recipients; NULL is an allowed + value but not very useful. Group expansion is done on these names; + they may be in any of the user Id formats we can handle. The flags + bits for each string in the string list are used for: + Bit 0: This is an encrypt-to recipient. + Bit 1: This is a hidden recipient. + + USE is the desired use for the key - usually PUBKEY_USAGE_ENC. + RET_PK_LIST. + + On success a list of keys is stored at the address RET_PK_LIST; the + caller must free this list. On error the value at this address is + not changed. + */ int -build_pk_list( STRLIST rcpts, PK_LIST *ret_pk_list, unsigned use ) +build_pk_list( STRLIST rcpts, PK_LIST *ret_pk_list, unsigned int use ) { - PK_LIST pk_list = NULL; - PKT_public_key *pk=NULL; - int rc=0; - int any_recipients=0; - STRLIST rov,remusr; - char *def_rec = NULL; - - if(opt.grouplist) - remusr=expand_group(rcpts); - else - remusr=rcpts; - - /* check whether there are any recipients in the list and build the - * list of the encrypt-to ones (we always trust them) */ - for( rov = remusr; rov; rov = rov->next ) { - if( !(rov->flags & 1) ) - { - any_recipients = 1; + PK_LIST pk_list = NULL; + PKT_public_key *pk=NULL; + int rc=0; + int any_recipients=0; + STRLIST rov,remusr; + char *def_rec = NULL; - if((rov->flags&2) && (PGP2 || PGP6 || PGP7 || PGP8)) - { - log_info(_("you may not use %s while in %s mode\n"), - "--hidden-recipient", - compliance_option_string()); + /* Try to expand groups if any have been defined. */ + if (opt.grouplist) + remusr = expand_group (rcpts); + else + remusr = rcpts; - compliance_failure(); - } - } - else if( (use & PUBKEY_USAGE_ENC) && !opt.no_encrypt_to ) { - pk = xcalloc (1, sizeof *pk ); - pk->req_usage = use; - /* We can encrypt-to a disabled key */ - if( (rc = get_pubkey_byname( pk, rov->d, NULL, NULL, 1 )) ) { - free_public_key( pk ); pk = NULL; - log_error(_("%s: skipped: %s\n"), rov->d, gpg_strerror (rc) ); - write_status_text_and_buffer (STATUS_INV_RECP, "0 ", - rov->d, strlen (rov->d), -1); - goto fail; + /* Check whether there are any recipients in the list and build the + * list of the encrypt-to ones (we always trust them). */ + for ( rov = remusr; rov; rov = rov->next ) + { + if ( !(rov->flags & 1) ) + { + /* This is a regular recipient; i.e. not an encrypt-to + one. */ + any_recipients = 1; + + /* Hidden recipients are not allowed while in PGP mode, + issue a warning and switch into GnuPG mode. */ + if ((rov->flags&2) && (PGP2 || PGP6 || PGP7 || PGP8)) + { + log_info(_("you may not use %s while in %s mode\n"), + "--hidden-recipient", + compliance_option_string()); + + compliance_failure(); } - 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) { - free_public_key(pk); pk = NULL; - log_info(_("%s: skipped: public key already present\n"), - rov->d); - } - else { - PK_LIST r; - r = xmalloc ( sizeof *r ); - r->pk = pk; pk = NULL; - r->next = pk_list; - r->flags = (rov->flags&2)?1:0; - pk_list = r; - - if(r->flags&1 && (PGP2 || PGP6 || PGP7 || PGP8)) - { - log_info(_("you may not use %s while in %s mode\n"), - "--hidden-encrypt-to", - compliance_option_string()); - - compliance_failure(); - } - } - } - else { - free_public_key( pk ); pk = NULL; - log_error(_("%s: skipped: %s\n"), rov->d, gpg_strerror (rc) ); - write_status_text_and_buffer (STATUS_INV_RECP, "0 ", - rov->d, strlen (rov->d), -1); - goto fail; - } - } + } + else if ( (use & PUBKEY_USAGE_ENC) && !opt.no_encrypt_to ) + { + /* Encryption has been requested and --encrypt-to has not + been disabled. Check this encrypt-to key. */ + pk = xmalloc_clear( sizeof *pk ); + pk->req_usage = use; + + /* We explicitly allow encrypt-to to an disabled key; thus + we pass 1 as last argument. */ + if ( (rc = get_pubkey_byname ( pk, rov->d, NULL, NULL, 1 )) ) + { + free_public_key ( pk ); pk = NULL; + log_error (_("%s: skipped: %s\n"), rov->d, g10_errstr(rc) ); + write_status_text_and_buffer (STATUS_INV_RECP, "0 ", + rov->d, strlen (rov->d), -1); + goto fail; + } + else if ( !(rc=openpgp_pk_test_algo2 (pk->pubkey_algo, use)) ) + { + /* Skip the actual key if the key is already present + * in the list. Add it to our list if not. */ + if (key_present_in_pk_list(pk_list, pk) == 0) + { + free_public_key (pk); pk = NULL; + log_info (_("%s: skipped: public key already present\n"), + rov->d); + } + else + { + PK_LIST r; + r = xmalloc( sizeof *r ); + r->pk = pk; pk = NULL; + r->next = pk_list; + r->flags = (rov->flags&2)?1:0; + pk_list = r; + + /* Hidden encrypt-to recipients are not allowed while + in PGP mode, issue a warning and switch into + GnuPG mode. */ + if ((r->flags&1) && (PGP2 || PGP6 || PGP7 || PGP8)) + { + log_info(_("you may not use %s while in %s mode\n"), + "--hidden-encrypt-to", + compliance_option_string()); + + compliance_failure(); + } + } + } + else + { + /* The public key is not usable for encryption or not + available. */ + free_public_key( pk ); pk = NULL; + log_error(_("%s: skipped: %s\n"), rov->d, g10_errstr(rc) ); + write_status_text_and_buffer (STATUS_INV_RECP, "0 ", + rov->d, strlen (rov->d), -1); + goto fail; + } + } } - if( !any_recipients && !opt.batch ) { /* ask */ - int have_def_rec; - char *answer=NULL; - STRLIST backlog=NULL; - - def_rec = default_recipient(); - have_def_rec = !!def_rec; - if( !have_def_rec ) - tty_printf(_( - "You did not specify a user ID. (you may use \"-r\")\n")); - for(;;) { - rc = 0; - xfree (answer); - if( have_def_rec ) { - answer = def_rec; - def_rec = NULL; - } - else if (backlog) { - answer = strlist_pop (&backlog); - } - else { - answer = cpr_get_utf8("pklist.user_id.enter", - _("\nEnter the user ID. End with an empty line: ")); - trim_spaces(answer); - cpr_kill_prompt(); - } - if( !answer || !*answer ) { - xfree (answer); - break; - } - if(expand_id(answer,&backlog,0)) - continue; - if( pk ) - free_public_key( pk ); - pk = xcalloc (1, sizeof *pk ); - pk->req_usage = use; - rc = get_pubkey_byname( pk, answer, NULL, NULL, 0 ); - if( rc ) - tty_printf(_("No such user ID.\n")); - else if( !(rc=openpgp_pk_test_algo (pk->pubkey_algo, use)) ) { - if( have_def_rec ) { - if (key_present_in_pk_list(pk_list, pk) == 0) { - free_public_key(pk); pk = NULL; - log_info(_("skipped: public key " - "already set as default recipient\n") ); - } - else { - PK_LIST r = xmalloc ( sizeof *r ); - r->pk = pk; pk = NULL; - r->next = pk_list; - r->flags = 0; /* no throwing default ids */ - pk_list = r; - } - any_recipients = 1; - continue; - } - else { - int trustlevel; + /* If we don't have any recipients yet and we are not in batch mode + drop into interactive selection mode. */ + if ( !any_recipients && !opt.batch ) + { + int have_def_rec; + char *answer = NULL; + STRLIST backlog = NULL; + + if (pk_list) + any_recipients = 1; + def_rec = default_recipient(); + have_def_rec = !!def_rec; + if ( !have_def_rec ) + tty_printf(_("You did not specify a user ID. (you may use \"-r\")\n")); + + for (;;) + { + rc = 0; + xfree(answer); + if ( have_def_rec ) + { + /* A default recipient is taken as the first entry. */ + answer = def_rec; + def_rec = NULL; + } + else if (backlog) + { + /* This is part of our trick to expand and display groups. */ + answer = pop_strlist (&backlog); + } + else + { + /* Show the list of already collected recipients and ask + for more. */ + PK_LIST iter; + + tty_printf("\n"); + tty_printf(_("Current recipients:\n")); + for (iter=pk_list;iter;iter=iter->next) + { + u32 keyid[2]; + + keyid_from_pk(iter->pk,keyid); + tty_printf("%4u%c/%s %s \"", + nbits_from_pk(iter->pk), + pubkey_letter(iter->pk->pubkey_algo), + keystr(keyid), + datestr_from_pk(iter->pk)); + + if (iter->pk->user_id) + tty_print_utf8_string(iter->pk->user_id->name, + iter->pk->user_id->len); + else + { + size_t n; + char *p = get_user_id( keyid, &n ); + tty_print_utf8_string( p, n ); + xfree(p); + } + tty_printf("\"\n"); + } + + answer = cpr_get_utf8("pklist.user_id.enter", + _("\nEnter the user ID. " + "End with an empty line: ")); + trim_spaces(answer); + cpr_kill_prompt(); + } + + if ( !answer || !*answer ) + { + xfree(answer); + break; /* No more recipients entered - get out of loop. */ + } + + /* Do group expand here too. The trick here is to continue + the loop if any expansion occured. The code above will + then list all expanded keys. */ + if (expand_id(answer,&backlog,0)) + continue; + + /* Get and check key for the current name. */ + if (pk) + free_public_key (pk); + pk = xmalloc_clear( sizeof *pk ); + pk->req_usage = use; + rc = get_pubkey_byname( pk, answer, NULL, NULL, 0 ); + if (rc) + tty_printf(_("No such user ID.\n")); + else if ( !(rc=check_pubkey_algo2(pk->pubkey_algo, use)) ) + { + if ( have_def_rec ) + { + /* No validation for a default recipient. */ + if (!key_present_in_pk_list(pk_list, pk)) + { + free_public_key (pk); pk = NULL; + log_info (_("skipped: public key " + "already set as default recipient\n") ); + } + else + { + PK_LIST r = xmalloc (sizeof *r); + r->pk = pk; pk = NULL; + r->next = pk_list; + r->flags = 0; /* No throwing default ids. */ + pk_list = r; + } + any_recipients = 1; + continue; + } + else + { /* Check validity of this key. */ + int trustlevel; - trustlevel = get_validity (pk, pk->user_id); - if( (trustlevel & TRUST_FLAG_DISABLED) ) { - tty_printf(_("Public key is disabled.\n") ); - } - else if( do_we_trust_pre( pk, trustlevel ) ) { - /* Skip the actual key if the key is already present - * in the list */ - if (key_present_in_pk_list(pk_list, pk) == 0) { - free_public_key(pk); pk = NULL; - log_info(_("skipped: public key already set\n") ); - } - else { - PK_LIST r; - u32 keyid[2]; - - keyid_from_pk( pk, keyid); - tty_printf("Added %4u%c/%08lX %s \"", - nbits_from_pk( pk ), - pubkey_letter( pk->pubkey_algo ), - (ulong)keyid[1], - datestr_from_pk( pk ) ); - if(pk->user_id) - tty_print_utf8_string(pk->user_id->name, - pk->user_id->len); - else - { - size_t n; - char *p = get_user_id( keyid, &n ); - tty_print_utf8_string( p, n ); - xfree (p); - } - tty_printf("\"\n"); - - r = xmalloc ( sizeof *r ); - r->pk = pk; pk = NULL; - r->next = pk_list; - r->flags = 0; /* no throwing interactive ids */ - pk_list = r; - } - any_recipients = 1; - continue; - } - } - } - xfree (def_rec); def_rec = NULL; - have_def_rec = 0; - } - if( pk ) { - free_public_key( pk ); - pk = NULL; - } + trustlevel = get_validity (pk, pk->user_id); + if ( (trustlevel & TRUST_FLAG_DISABLED) ) + { + tty_printf (_("Public key is disabled.\n") ); + } + else if ( do_we_trust_pre (pk, trustlevel) ) + { + /* Skip the actual key if the key is already + * present in the list */ + if (!key_present_in_pk_list(pk_list, pk)) + { + free_public_key(pk); pk = NULL; + log_info(_("skipped: public key already set\n") ); + } + else + { + PK_LIST r; + r = xmalloc( sizeof *r ); + r->pk = pk; pk = NULL; + r->next = pk_list; + r->flags = 0; /* No throwing interactive ids. */ + pk_list = r; + } + any_recipients = 1; + continue; + } + } + } + xfree(def_rec); def_rec = NULL; + have_def_rec = 0; + } + if ( pk ) + { + free_public_key( pk ); + pk = NULL; + } } - else if( !any_recipients && (def_rec = default_recipient()) ) { - pk = xcalloc (1, sizeof *pk ); - pk->req_usage = use; - /* The default recipient may be disabled */ - rc = get_pubkey_byname( pk, def_rec, NULL, NULL, 1 ); - if( rc ) - log_error(_("unknown default recipient `%s'\n"), def_rec ); - else if( !(rc=openpgp_pk_test_algo (pk->pubkey_algo, use)) ) { - /* Mark any_recipients here since the default recipient + else if ( !any_recipients && (def_rec = default_recipient()) ) + { + /* We are in batch mode and have only a default recipient. */ + pk = xmalloc_clear( sizeof *pk ); + pk->req_usage = use; + + /* The default recipient is allowed to be disabled; thus pass 1 + as last argument. */ + rc = get_pubkey_byname (pk, def_rec, NULL, NULL, 1); + if (rc) + log_error(_("unknown default recipient \"%s\"\n"), def_rec ); + else if ( !(rc=check_pubkey_algo2(pk->pubkey_algo, use)) ) + { + /* Mark any_recipients here since the default recipient would have been used if it wasn't already there. It doesn't really matter if we got this key from the default recipient or an encrypt-to. */ - any_recipients = 1; - if (key_present_in_pk_list(pk_list, pk) == 0) - log_info(_("skipped: public key already set as default recipient\n")); - else { - PK_LIST r = xmalloc ( sizeof *r ); - r->pk = pk; pk = NULL; - r->next = pk_list; - r->flags = 0; /* no throwing default ids */ - pk_list = r; - } - } - if( pk ) { - free_public_key( pk ); - pk = NULL; - } - xfree (def_rec); def_rec = NULL; + any_recipients = 1; + if (!key_present_in_pk_list(pk_list, pk)) + log_info (_("skipped: public key already set " + "as default recipient\n")); + else + { + PK_LIST r = xmalloc( sizeof *r ); + r->pk = pk; pk = NULL; + r->next = pk_list; + r->flags = 0; /* No throwing default ids. */ + pk_list = r; + } + } + if ( pk ) + { + free_public_key( pk ); + pk = NULL; + } + xfree(def_rec); def_rec = NULL; } - else { - any_recipients = 0; - for(; remusr; remusr = remusr->next ) { - if( (remusr->flags & 1) ) - continue; /* encrypt-to keys are already handled */ - - pk = xcalloc (1, sizeof *pk ); - pk->req_usage = use; - if( (rc = get_pubkey_byname( pk, remusr->d, NULL, NULL, 0 )) ) { - free_public_key( pk ); pk = NULL; - log_error(_("%s: skipped: %s\n"), remusr->d, gpg_strerror (rc) ); - write_status_text_and_buffer (STATUS_INV_RECP, "0 ", - remusr->d, strlen (remusr->d), - -1); - goto fail; - } - else if( !(rc=openpgp_pk_test_algo (pk->pubkey_algo, use )) ) { - int trustlevel; - - trustlevel = get_validity (pk, pk->user_id); - if( (trustlevel & TRUST_FLAG_DISABLED) ) { - free_public_key(pk); pk = NULL; - log_info(_("%s: skipped: public key is disabled\n"), - remusr->d); - write_status_text_and_buffer (STATUS_INV_RECP, "0 ", - remusr->d, - strlen (remusr->d), - -1); - rc = gpg_error (GPG_ERR_UNUSABLE_PUBKEY); - goto fail; - } - else if( do_we_trust_pre( pk, trustlevel ) ) { - /* note: do_we_trust may have changed the trustlevel */ - - /* We have at least one valid recipient. It doesn't matters - * if this recipient is already present. */ - any_recipients = 1; - - /* Skip the actual key if the key is already present - * in the list */ - if (key_present_in_pk_list(pk_list, pk) == 0) { - free_public_key(pk); pk = NULL; - log_info(_("%s: skipped: public key already present\n"), - remusr->d); - } - else { - PK_LIST r; - r = xmalloc ( sizeof *r ); - r->pk = pk; pk = NULL; - r->next = pk_list; - r->flags = (remusr->flags&2)?1:0; - pk_list = r; - } - } - else { /* we don't trust this pk */ - free_public_key( pk ); pk = NULL; - write_status_text_and_buffer (STATUS_INV_RECP, "10 ", - remusr->d, - strlen (remusr->d), - -1); - rc = gpg_error (GPG_ERR_UNUSABLE_PUBKEY); - goto fail; - } - } - else { - free_public_key( pk ); pk = NULL; - write_status_text_and_buffer (STATUS_INV_RECP, "0 ", - remusr->d, - strlen (remusr->d), - -1); - log_error(_("%s: skipped: %s\n"), remusr->d, gpg_strerror (rc) ); - goto fail; - } - } + else + { + /* General case: Check all keys. */ + any_recipients = 0; + for (; remusr; remusr = remusr->next ) + { + if ( (remusr->flags & 1) ) + continue; /* encrypt-to keys are already handled. */ + + pk = xmalloc_clear( sizeof *pk ); + pk->req_usage = use; + if ( (rc = get_pubkey_byname( pk, remusr->d, NULL, NULL, 0 )) ) + { + /* Key not found or other error. */ + free_public_key( pk ); pk = NULL; + log_error(_("%s: skipped: %s\n"), remusr->d, g10_errstr(rc) ); + write_status_text_and_buffer (STATUS_INV_RECP, "0 ", + remusr->d, strlen (remusr->d), + -1); + goto fail; + } + else if ( !(rc=check_pubkey_algo2(pk->pubkey_algo, use )) ) + { + /* Key found and usable. Check validity. */ + int trustlevel; + + trustlevel = get_validity (pk, pk->user_id); + if ( (trustlevel & TRUST_FLAG_DISABLED) ) + { + /*Key has been disabled. */ + free_public_key(pk); pk = NULL; + log_info(_("%s: skipped: public key is disabled\n"), + remusr->d); + write_status_text_and_buffer (STATUS_INV_RECP, "0 ", + remusr->d, + strlen (remusr->d), + -1); + rc=G10ERR_UNU_PUBKEY; + goto fail; + } + else if ( do_we_trust_pre( pk, trustlevel ) ) + { + /* Note: do_we_trust may have changed the trustlevel */ + + /* We have at least one valid recipient. It doesn't + * matters if this recipient is already present. */ + any_recipients = 1; + + /* Skip the actual key if the key is already present + * in the list */ + if (!key_present_in_pk_list(pk_list, pk)) + { + free_public_key(pk); pk = NULL; + log_info(_("%s: skipped: public key already present\n"), + remusr->d); + } + else + { + PK_LIST r; + r = xmalloc( sizeof *r ); + r->pk = pk; pk = NULL; + r->next = pk_list; + r->flags = (remusr->flags&2)?1:0; + pk_list = r; + } + } + else + { /* We don't trust this key. */ + free_public_key( pk ); pk = NULL; + write_status_text_and_buffer (STATUS_INV_RECP, "10 ", + remusr->d, + strlen (remusr->d), + -1); + rc=G10ERR_UNU_PUBKEY; + goto fail; + } + } + else + { + /* Key found but not usable for us (e.g. sign-only key). */ + free_public_key( pk ); pk = NULL; + write_status_text_and_buffer (STATUS_INV_RECP, "0 ", + remusr->d, + strlen (remusr->d), + -1); + log_error(_("%s: skipped: %s\n"), remusr->d, g10_errstr(rc) ); + goto fail; + } + } } - - if( !rc && !any_recipients ) { - log_error(_("no valid addressees\n")); - write_status_text (STATUS_NO_RECP, "0"); - rc = GPG_ERR_NO_USER_ID; + + if ( !rc && !any_recipients ) + { + log_error(_("no valid addressees\n")); + write_status_text (STATUS_NO_RECP, "0"); + rc = G10ERR_NO_USER_ID; } - + fail: - if( rc ) - release_pk_list( pk_list ); - else - *ret_pk_list = pk_list; - if(opt.grouplist) - free_strlist(remusr); - return rc; + if ( rc ) + release_pk_list( pk_list ); + else + *ret_pk_list = pk_list; + if (opt.grouplist) + free_strlist(remusr); + return rc; } @@ -1136,16 +1195,18 @@ algo_available( preftype_t preftype, int algo, void *hint ) && algo != CIPHER_ALGO_CAST5)) return 0; - if((PGP7 || PGP8) && (algo != CIPHER_ALGO_IDEA - && algo != CIPHER_ALGO_3DES - && algo != CIPHER_ALGO_CAST5 - && algo != CIPHER_ALGO_AES - && algo != CIPHER_ALGO_AES192 - && algo != CIPHER_ALGO_AES256 - && algo != CIPHER_ALGO_TWOFISH)) + if(PGP7 && (algo != CIPHER_ALGO_IDEA + && algo != CIPHER_ALGO_3DES + && algo != CIPHER_ALGO_CAST5 + && algo != CIPHER_ALGO_AES + && algo != CIPHER_ALGO_AES192 + && algo != CIPHER_ALGO_AES256 + && algo != CIPHER_ALGO_TWOFISH)) return 0; - return algo && !gcry_cipher_test_algo (algo); + /* PGP8 supports all the ciphers we do.. */ + + return algo && !openpgp_cipher_test_algo ( algo ); } else if( preftype == PREFTYPE_HASH ) { @@ -1164,14 +1225,16 @@ algo_available( preftype_t preftype, int algo, void *hint ) && algo != DIGEST_ALGO_SHA256)) return 0; - return algo && !gcry_md_test_algo( algo ); + return algo && !openpgp_md_test_algo (algo); } else if( preftype == PREFTYPE_ZIP ) { - if((PGP6 || PGP7 || PGP8) && (algo != COMPRESS_ALGO_NONE - && algo != COMPRESS_ALGO_ZIP)) + if((PGP6 || PGP7) && (algo != COMPRESS_ALGO_NONE + && algo != COMPRESS_ALGO_ZIP)) return 0; + /* PGP8 supports all the compression algos we do */ + return !check_compress_algo( algo ); } else @@ -1362,7 +1425,7 @@ select_mdc_from_pklist (PK_LIST pk_list) int mdc; if (pkr->pk->user_id) /* selected by user ID */ - mdc = pkr->pk->user_id->mdc_feature; + mdc = pkr->pk->user_id->flags.mdc; else mdc = pkr->pk->mdc_feature; if (!mdc) diff --git a/g10/plaintext.c b/g10/plaintext.c index d84a523fe..bd908e551 100644 --- a/g10/plaintext.c +++ b/g10/plaintext.c @@ -1,6 +1,6 @@ /* plaintext.c - process plaintext packets - * Copyright (C) 1998, 1999, 2000, 2001, 2002, - * 2003 Free Software Foundation, Inc. + * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, + * 2005, 2006 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -16,7 +16,8 @@ * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. */ #include <config.h> @@ -25,13 +26,13 @@ #include <string.h> #include <errno.h> #include <assert.h> +#include <sys/types.h> #ifdef HAVE_DOSISH_SYSTEM #include <fcntl.h> /* for setmode() */ #endif #include "gpg.h" #include "util.h" -#include "memory.h" #include "options.h" #include "packet.h" #include "ttyio.h" @@ -41,7 +42,6 @@ #include "i18n.h" - /**************** * Handle a plaintext packet. If MFX is not NULL, update the MDs * Note: we should use the filter stuff here, but we have to add some @@ -50,27 +50,41 @@ */ int handle_plaintext( PKT_plaintext *pt, md_filter_context_t *mfx, - int nooutput, int clearsig, int *create_failed ) + int nooutput, int clearsig ) { char *fname = NULL; FILE *fp = NULL; + static off_t count=0; int rc = 0; int c; - int convert = pt->mode == 't'; + int convert = (pt->mode == 't' || pt->mode == 'u'); #ifdef __riscos__ int filetype = 0xfff; #endif - int dummy_create_failed; - if (!create_failed) - create_failed = &dummy_create_failed; - *create_failed = 0; + /* Let people know what the plaintext info is. This allows the + receiving program to try and do something different based on + the format code (say, recode UTF-8 to local). */ + if(!nooutput && is_status_enabled()) + { + char status[50]; + + sprintf(status,"%X %lu ",(byte)pt->mode,(ulong)pt->timestamp); + write_status_text_and_buffer(STATUS_PLAINTEXT, + status,pt->name,pt->namelen,0); + + if(!pt->is_partial) + { + sprintf(status,"%lu",(ulong)pt->len); + write_status_text(STATUS_PLAINTEXT_LENGTH,status); + } + } /* create the filename as C string */ if( nooutput ) ; else if( opt.outfile ) { - fname = xmalloc ( strlen( opt.outfile ) + 1); + fname = xmalloc( strlen( opt.outfile ) + 1); strcpy(fname, opt.outfile ); } else if( pt->namelen == 8 && !memcmp( pt->name, "_CONSOLE", 8 ) ) { @@ -82,8 +96,7 @@ handle_plaintext( PKT_plaintext *pt, md_filter_context_t *mfx, if( !fname ) fname = ask_outfile_name( pt->name, pt->namelen ); if( !fname ) { - *create_failed = 1; - rc = GPG_ERR_GENERAL; + rc = gpg_error (GPG_ERR_GENERAL) /* Can't create file. */ goto leave; } } @@ -93,20 +106,20 @@ handle_plaintext( PKT_plaintext *pt, md_filter_context_t *mfx, if( nooutput ) ; - else if( !*fname || (*fname=='-' && !fname[1])) { - /* no filename or "-" given; write to stdout */ + else if ( iobuf_is_pipe_filename (fname) || !*fname) + { + /* No filename or "-" given; write to stdout. */ fp = stdout; #ifdef HAVE_DOSISH_SYSTEM setmode ( fileno(fp) , O_BINARY ); #endif - } + } else { while( !overwrite_filep (fname) ) { char *tmp = ask_outfile_name (NULL, 0); if ( !tmp || !*tmp ) { xfree (tmp); - *create_failed = 1; - rc = GPG_ERR_GENERAL; + rc = G10ERR_CREATE_FILE; goto leave; } xfree (fname); @@ -117,26 +130,34 @@ handle_plaintext( PKT_plaintext *pt, md_filter_context_t *mfx, #ifndef __riscos__ if( fp || nooutput ) ; + else if (is_secured_filename (fname)) + { + errno = EPERM; + log_error(_("error creating `%s': %s\n"), fname, strerror(errno) ); + rc = G10ERR_CREATE_FILE; + goto leave; + } else if( !(fp = fopen(fname,"wb")) ) { - rc = gpg_error_from_errno (errno); log_error(_("error creating `%s': %s\n"), fname, strerror(errno) ); - *create_failed = 1; + rc = G10ERR_CREATE_FILE; goto leave; } #else /* __riscos__ */ - /* Convert all '.' in fname to '/' -- we don't create directories! */ - for( c=0; fname[c]; ++c ) - if( fname[c] == '.' ) - fname[c] = '/'; + /* If no output filename was given, i.e. we constructed it, + convert all '.' in fname to '/' but not vice versa as + we don't create directories! */ + if( !opt.outfile ) + for( c=0; fname[c]; ++c ) + if( fname[c] == '.' ) + fname[c] = '/'; if( fp || nooutput ) ; else { fp = fopen(fname,"wb"); if( !fp ) { - rc == gpg_error_from_errno (errno); log_error(_("error creating `%s': %s\n"), fname, strerror(errno) ); - *create_failed = 1; + rc = G10ERR_CREATE_FILE; if (errno == 106) log_info("Do output file and input file have the same name?\n"); goto leave; @@ -156,15 +177,21 @@ handle_plaintext( PKT_plaintext *pt, md_filter_context_t *mfx, #endif /* __riscos__ */ if( !pt->is_partial ) { - /* we have an actual length (which might be zero). */ - assert( !clearsig ); + /* We have an actual length (which might be zero). */ + + if (clearsig) { + log_error ("clearsig encountered while not expected\n"); + rc = G10ERR_UNEXPECTED; + goto leave; + } + if( convert ) { /* text mode */ for( ; pt->len; pt->len-- ) { if( (c = iobuf_get(pt->buf)) == -1 ) { - rc = gpg_error_from_errno (errno); - log_error("Problem reading source (%u bytes remaining)\n", - (unsigned)pt->len); - goto leave; + rc = gpg_error_from_errno (errno); + log_error ("problem reading source (%u bytes remaining)\n", + (unsigned)pt->len); + goto leave; } if( mfx->md ) gcry_md_putc (mfx->md, c ); @@ -172,65 +199,93 @@ handle_plaintext( PKT_plaintext *pt, md_filter_context_t *mfx, if( c == '\r' ) /* convert to native line ending */ continue; /* fixme: this hack might be too simple */ #endif - if( fp ) { - if( putc( c, fp ) == EOF ) { - rc = gpg_error_from_errno (errno); + if( fp ) + { + if(opt.max_output && (++count)>opt.max_output) + { + log_error("Error writing to `%s': %s\n", + fname,"exceeded --max-output limit\n"); + rc = gpg_error (GPG_ERR_GENERAL); + goto leave; + } + else if( putc( c, fp ) == EOF ) + { log_error("Error writing to `%s': %s\n", fname, strerror(errno) ); + rc = G10ERR_WRITE_FILE; goto leave; - } - } + } + } } } else { /* binary mode */ - byte *buffer = xmalloc ( 32768 ); + byte *buffer = xmalloc( 32768 ); while( pt->len ) { int len = pt->len > 32768 ? 32768 : pt->len; len = iobuf_read( pt->buf, buffer, len ); if( len == -1 ) { - rc = gpg_error_from_errno (errno); log_error("Problem reading source (%u bytes remaining)\n", (unsigned)pt->len); - xfree ( buffer ); + rc = G10ERR_READ_FILE; + xfree( buffer ); goto leave; } if( mfx->md ) - gcry_md_write( mfx->md, buffer, len ); - if( fp ) { - if( fwrite( buffer, 1, len, fp ) != len ) { - rc = gpg_error_from_errno (errno); + gcry_md_write ( mfx->md, buffer, len ); + if( fp ) + { + if(opt.max_output && (count+=len)>opt.max_output) + { + log_error("Error writing to `%s': %s\n", + fname,"exceeded --max-output limit\n"); + rc = G10ERR_WRITE_FILE; + xfree( buffer ); + goto leave; + } + else if( fwrite( buffer, 1, len, fp ) != len ) + { log_error("Error writing to `%s': %s\n", fname, strerror(errno) ); - xfree ( buffer ); + rc = G10ERR_WRITE_FILE; + xfree( buffer ); goto leave; - } - } + } + } pt->len -= len; } - xfree ( buffer ); + xfree( buffer ); } } else if( !clearsig ) { if( convert ) { /* text mode */ while( (c = iobuf_get(pt->buf)) != -1 ) { if( mfx->md ) - gcry_md_putc (mfx->md, c ); + md_putc(mfx->md, c ); #ifndef HAVE_DOSISH_SYSTEM if( convert && c == '\r' ) continue; /* fixme: this hack might be too simple */ #endif - if( fp ) { - if( putc( c, fp ) == EOF ) { - rc = gpg_error_from_errno (errno); + if( fp ) + { + if(opt.max_output && (++count)>opt.max_output) + { + log_error("Error writing to `%s': %s\n", + fname,"exceeded --max-output limit\n"); + rc = G10ERR_WRITE_FILE; + goto leave; + } + else if( putc( c, fp ) == EOF ) + { log_error("Error writing to `%s': %s\n", fname, strerror(errno) ); + rc = G10ERR_WRITE_FILE; goto leave; - } - } + } + } } } else { /* binary mode */ - byte *buffer = xmalloc ( 32768 ); + byte *buffer = xmalloc( 32768 ); int eof; for( eof=0; !eof; ) { /* Why do we check for len < 32768: @@ -245,18 +300,27 @@ handle_plaintext( PKT_plaintext *pt, md_filter_context_t *mfx, if( len < 32768 ) eof = 1; if( mfx->md ) - gcry_md_write( mfx->md, buffer, len ); - if( fp ) { - if( fwrite( buffer, 1, len, fp ) != len ) { - rc = gpg_error_from_errno (errno); + md_write( mfx->md, buffer, len ); + if( fp ) + { + if(opt.max_output && (count+=len)>opt.max_output) + { log_error("Error writing to `%s': %s\n", - fname, strerror(errno) ); - xfree ( buffer ); + fname,"exceeded --max-output limit\n"); + rc = G10ERR_WRITE_FILE; + xfree( buffer ); goto leave; + } + else if( fwrite( buffer, 1, len, fp ) != len ) { + log_error("Error writing to `%s': %s\n", + fname, strerror(errno) ); + rc = G10ERR_WRITE_FILE; + xfree( buffer ); + goto leave; } - } + } } - xfree ( buffer ); + xfree( buffer ); } pt->buf = NULL; } @@ -264,19 +328,28 @@ handle_plaintext( PKT_plaintext *pt, md_filter_context_t *mfx, int state = 0; while( (c = iobuf_get(pt->buf)) != -1 ) { - if( fp ) { - if( putc( c, fp ) == EOF ) { - rc = gpg_error_from_errno (errno); + if( fp ) + { + if(opt.max_output && (++count)>opt.max_output) + { log_error("Error writing to `%s': %s\n", - fname, strerror(errno) ); + fname,"exceeded --max-output limit\n"); + rc = G10ERR_WRITE_FILE; goto leave; - } - } + } + else if( putc( c, fp ) == EOF ) + { + log_error("Error writing to `%s': %s\n", + fname, strerror(errno) ); + rc = G10ERR_WRITE_FILE; + goto leave; + } + } if( !mfx->md ) continue; if( state == 2 ) { - gcry_md_putc (mfx->md, '\r' ); - gcry_md_putc (mfx->md, '\n' ); + md_putc(mfx->md, '\r' ); + md_putc(mfx->md, '\n' ); state = 0; } if( !state ) { @@ -285,18 +358,18 @@ handle_plaintext( PKT_plaintext *pt, md_filter_context_t *mfx, else if( c == '\n' ) state = 2; else - gcry_md_putc (mfx->md, c ); + md_putc(mfx->md, c ); } else if( state == 1 ) { if( c == '\n' ) state = 2; else { - gcry_md_putc (mfx->md, '\r' ); + md_putc(mfx->md, '\r' ); if( c == '\r' ) state = 1; else { state = 0; - gcry_md_putc (mfx->md, c ); + md_putc(mfx->md, c ); } } } @@ -305,9 +378,9 @@ handle_plaintext( PKT_plaintext *pt, md_filter_context_t *mfx, } if( fp && fp != stdout && fclose(fp) ) { - rc = gpg_error_from_errno (errno); log_error("Error closing `%s': %s\n", fname, strerror(errno) ); fp = NULL; + rc = G10ERR_WRITE_FILE; goto leave; } fp = NULL; @@ -315,12 +388,12 @@ handle_plaintext( PKT_plaintext *pt, md_filter_context_t *mfx, leave: if( fp && fp != stdout ) fclose(fp); - xfree (fname); + xfree(fname); return rc; } static void -do_hash( MD_HANDLE md, MD_HANDLE md2, iobuf_t fp, int textmode ) +do_hash( gcry_md_hd_t md, gcry_md_hd_t md2, IOBUF fp, int textmode ) { text_filter_context_t tfx; int c; @@ -365,12 +438,12 @@ do_hash( MD_HANDLE md, MD_HANDLE md2, iobuf_t fp, int textmode ) * INFILE is the name of the input file. */ int -ask_for_detached_datafile( MD_HANDLE md, MD_HANDLE md2, +ask_for_detached_datafile (gcry_md_hd_t md, gcry_md_hd_t md2, const char *inname, int textmode ) { progress_filter_context_t pfx; char *answer = NULL; - iobuf_t fp; + IOBUF fp; int rc = 0; fp = open_sigfile( inname, &pfx ); /* open default file */ @@ -379,24 +452,37 @@ ask_for_detached_datafile( MD_HANDLE md, MD_HANDLE md2, int any=0; tty_printf(_("Detached signature.\n")); do { - xfree (answer); - answer = cpr_get("detached_signature.filename", + char *name; + xfree(answer); + tty_enable_completion(NULL); + name = cpr_get("detached_signature.filename", _("Please enter name of data file: ")); + tty_disable_completion(); cpr_kill_prompt(); + answer=make_filename(name,(void *)NULL); + xfree(name); + if( any && !*answer ) { - rc = GPG_ERR_GENERAL; + rc = gpg_error (GPG_ERR_GENERAL); /*G10ERR_READ_FILE*/ goto leave; } fp = iobuf_open(answer); + if (fp && is_secured_file (iobuf_get_fd (fp))) + { + iobuf_close (fp); + fp = NULL; + errno = EPERM; + } if( !fp && errno == ENOENT ) { tty_printf("No such file, try again or hit enter to quit.\n"); any++; } - else if( !fp ) { - rc = gpg_error_from_errno (errno); - log_error("can't open `%s': %s\n", answer, strerror(errno) ); + else if( !fp ) + { + log_error(_("can't open `%s': %s\n"), answer, strerror(errno)); + rc = G10ERR_READ_FILE; goto leave; - } + } } while( !fp ); } @@ -410,7 +496,7 @@ ask_for_detached_datafile( MD_HANDLE md, MD_HANDLE md2, iobuf_close(fp); leave: - xfree (answer); + xfree(answer); return rc; } @@ -421,11 +507,11 @@ ask_for_detached_datafile( MD_HANDLE md, MD_HANDLE md2, * If FILES is NULL, hash stdin. */ int -hash_datafiles( MD_HANDLE md, MD_HANDLE md2, STRLIST files, +hash_datafiles( gcry_md_hd_t md, gcry_md_hd_t md2, STRLIST files, const char *sigfilename, int textmode ) { progress_filter_context_t pfx; - iobuf_t fp; + IOBUF fp; STRLIST sl; if( !files ) { @@ -437,17 +523,22 @@ hash_datafiles( MD_HANDLE md, MD_HANDLE md2, STRLIST files, return 0; } log_error (_("no signed data\n")); - return GPG_ERR_NO_DATA; + return gpg_error (GPG_ERR_NO_DATA); } for (sl=files; sl; sl = sl->next ) { fp = iobuf_open( sl->d ); + if (fp && is_secured_file (iobuf_get_fd (fp))) + { + iobuf_close (fp); + fp = NULL; + errno = EPERM; + } if( !fp ) { - int tmperr = gpg_error_from_errno (errno); log_error(_("can't open signed data `%s'\n"), print_fname_stdin(sl->d)); - return tmperr; + return G10ERR_OPEN_FILE; } handle_progress (&pfx, fp, sl->d); do_hash( md, md2, fp, textmode ); diff --git a/g10/progress.c b/g10/progress.c index 9d9805065..148bf7e2d 100644 --- a/g10/progress.c +++ b/g10/progress.c @@ -1,4 +1,4 @@ -/* progress.c +/* progress.c - emit progress status lines * Copyright (C) 2003 Free Software Foundation, Inc. * * This file is part of GnuPG. @@ -15,7 +15,8 @@ * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. */ #include <config.h> @@ -33,7 +34,7 @@ */ int progress_filter (void *opaque, int control, - iobuf_t a, byte *buf, size_t *ret_len) + IOBUF a, byte *buf, size_t *ret_len) { int rc = 0; progress_filter_context_t *pfx = opaque; @@ -96,7 +97,7 @@ progress_filter (void *opaque, int control, } void -handle_progress (progress_filter_context_t *pfx, iobuf_t inp, const char *name) +handle_progress (progress_filter_context_t *pfx, IOBUF inp, const char *name) { off_t filesize = 0; @@ -106,8 +107,8 @@ handle_progress (progress_filter_context_t *pfx, iobuf_t inp, const char *name) if (!is_status_enabled ()) return; - if (name && *name && !(*name == '-' && !name[1])) - filesize = iobuf_get_filelength (inp); + if ( !iobuf_is_pipe_filename (name) && *name ) + filesize = iobuf_get_filelength (inp, NULL); else if (opt.set_filesize) filesize = opt.set_filesize; diff --git a/g10/pubkey-enc.c b/g10/pubkey-enc.c index 4b45b9f5c..5af0d5f1d 100644 --- a/g10/pubkey-enc.c +++ b/g10/pubkey-enc.c @@ -1,6 +1,6 @@ /* pubkey-enc.c - public key encoded packet handling * Copyright (C) 1998, 1999, 2000, 2001, 2002, - * 2003 Free Software Foundation, Inc. + * 2006 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -16,7 +16,8 @@ * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. */ #include <config.h> @@ -27,9 +28,7 @@ #include "gpg.h" #include "util.h" -#include "memory.h" #include "packet.h" -#include "mpi.h" #include "keydb.h" #include "trustdb.h" #include "cipher.h" @@ -38,7 +37,7 @@ #include "main.h" #include "i18n.h" #include "pkglue.h" -#include "call-agent.h" + static int get_it( PKT_pubkey_enc *k, DEK *dek, PKT_secret_key *sk, u32 *keyid ); @@ -77,12 +76,12 @@ get_session_key( PKT_pubkey_enc *k, DEK *dek ) PKT_secret_key *sk = NULL; int rc; - rc = openpgp_pk_test_algo (k->pubkey_algo, PUBKEY_USAGE_ENC); + rc = openpgp_pk_test_algo2 (k->pubkey_algo, PUBKEY_USAGE_ENC); if( rc ) goto leave; if( (k->keyid[0] || k->keyid[1]) && !opt.try_all_secrets ) { - sk = xcalloc (1, sizeof *sk ); + sk = xmalloc_clear( sizeof *sk ); sk->pubkey_algo = k->pubkey_algo; /* we want a pubkey with this algo*/ if( !(rc = get_seckey( sk, k->keyid )) ) rc = get_it( k, dek, sk, k->keyid ); @@ -95,34 +94,49 @@ get_session_key( PKT_pubkey_enc *k, DEK *dek ) for(;;) { if( sk ) free_secret_key( sk ); - sk = xcalloc (1, sizeof *sk ); + sk = xmalloc_clear( sizeof *sk ); rc=enum_secret_keys( &enum_context, sk, 1, 0); if( rc ) { - rc = GPG_ERR_NO_SECKEY; + rc = G10ERR_NO_SECKEY; break; } if( sk->pubkey_algo != k->pubkey_algo ) continue; keyid_from_sk( sk, keyid ); - log_info(_("anonymous recipient; trying secret key %08lX ...\n"), - (ulong)keyid[1] ); + log_info(_("anonymous recipient; trying secret key %s ...\n"), + keystr(keyid)); if(!opt.try_all_secrets && !is_status_enabled()) { p=get_last_passphrase(); set_next_passphrase(p); - xfree (p); + xfree(p); } rc = check_secret_key( sk, opt.try_all_secrets?1:-1 ); /* ask only once */ if( !rc ) + { rc = get_it( k, dek, sk, keyid ); - if( !rc ) { + /* Successfully checked the secret key (either it was + a card, had no passphrase, or had the right + passphrase) but couldn't decrypt the session key, + so thus that key is not the anonymous recipient. + Move the next passphrase into last for the next + round. We only do this if the secret key was + successfully checked as in the normal case, + check_secret_key handles this for us via + passphrase_to_dek */ + if(rc) + next_to_last_passphrase(); + } + + if( !rc ) + { log_info(_("okay, we are the anonymous recipient.\n") ); break; - } + } } enum_secret_keys( &enum_context, NULL, 0, 0 ); /* free context */ } @@ -138,15 +152,17 @@ static int get_it( PKT_pubkey_enc *enc, DEK *dek, PKT_secret_key *sk, u32 *keyid ) { int rc; - gcry_mpi_t plain_dek = NULL; + gcry_mpi_t plain_dek = NULL; byte *frame = NULL; unsigned n, nframe; u16 csum, csum2; + int card = 0; if (sk->is_protected && sk->protect.s2k.mode == 1002) - { /* FIXME: Note that we do only support RSA for now. */ - char *rbuf; + { /* Note, that we only support RSA for now. */ +#ifdef ENABLE_CARD_SUPPORT + unsigned char *rbuf; size_t rbuflen; char *snbuf; unsigned char *indata = NULL; @@ -154,9 +170,8 @@ get_it( PKT_pubkey_enc *enc, DEK *dek, PKT_secret_key *sk, u32 *keyid ) snbuf = serialno_and_fpr_from_sk (sk->protect.iv, sk->protect.ivlen, sk); - if (gcry_mpi_aprint (GCRYMPI_FMT_USG, &indata, &indatalen, - enc->data[0])) - BUG(); + if (gcry_mpi_aprint (GCRYMPI_FMT_USG, &indata, &indatalen, enc->data[0])) + BUG (); rc = agent_scd_pkdecrypt (snbuf, indata, indatalen, &rbuf, &rbuflen); xfree (snbuf); @@ -167,148 +182,145 @@ get_it( PKT_pubkey_enc *enc, DEK *dek, PKT_secret_key *sk, u32 *keyid ) frame = rbuf; nframe = rbuflen; card = 1; +#else + rc = gpg_error (GPG_ERR_NOT_SUPPORTED); + goto leave; +#endif /*!ENABLE_CARD_SUPPORT*/ } else { - rc = pk_decrypt (sk->pubkey_algo, &plain_dek, enc->data, sk->skey); + rc = pk_decrypt (sk->pubkey_algo, &plain_dek, enc->data, sk->skey ); if( rc ) - goto leave; + goto leave; if (gcry_mpi_aprint (GCRYMPI_FMT_USG, &frame, &nframe, plain_dek)) BUG(); gcry_mpi_release (plain_dek); plain_dek = NULL; } - - /* Now get the DEK (data encryption key) from the frame - * - * Old versions encode the DEK in in this format (msb is left): - * - * 0 1 DEK(16 bytes) CSUM(2 bytes) 0 RND(n bytes) 2 - * - * Later versions encode the DEK like this: - * - * 0 2 RND(n bytes) 0 A DEK(k bytes) CSUM(2 bytes) - * - * (mpi_get_buffer already removed the leading zero). - * - * RND are non-zero randow bytes. - * A is the cipher algorithm - * DEK is the encryption key (session key) with length k - * CSUM - */ - if( DBG_CIPHER ) - log_printhex ("DEK frame:", frame, nframe ); - n=0; - if (!card) - { - if( n + 7 > nframe ) - { rc = GPG_ERR_WRONG_SECKEY; goto leave; } - if( frame[n] == 1 && frame[nframe-1] == 2 ) { - log_info(_("old encoding of the DEK is not supported\n")); - rc = GPG_ERR_CIPHER_ALGO; - goto leave; + /* Now get the DEK (data encryption key) from the frame + * + * Old versions encode the DEK in in this format (msb is left): + * + * 0 1 DEK(16 bytes) CSUM(2 bytes) 0 RND(n bytes) 2 + * + * Later versions encode the DEK like this: + * + * 0 2 RND(n bytes) 0 A DEK(k bytes) CSUM(2 bytes) + * + * (mpi_get_buffer already removed the leading zero). + * + * RND are non-zero randow bytes. + * A is the cipher algorithm + * DEK is the encryption key (session key) with length k + * CSUM + */ + if( DBG_CIPHER ) + log_hexdump("DEK frame:", frame, nframe ); + n=0; + if (!card) + { + if( n + 7 > nframe ) + { rc = G10ERR_WRONG_SECKEY; goto leave; } + if( frame[n] == 1 && frame[nframe-1] == 2 ) { + log_info(_("old encoding of the DEK is not supported\n")); + rc = G10ERR_CIPHER_ALGO; + goto leave; + } + if( frame[n] != 2 ) /* somethink is wrong */ + { rc = G10ERR_WRONG_SECKEY; goto leave; } + for(n++; n < nframe && frame[n]; n++ ) /* skip the random bytes */ + ; + n++; /* and the zero byte */ } - if( frame[n] != 2 ) /* somethink is wrong */ - { rc = GPG_ERR_WRONG_SECKEY; goto leave; } - for(n++; n < nframe && frame[n]; n++ ) /* skip the random bytes */ - ; - n++; /* and the zero byte */ - } - if( n + 4 > nframe ) - { rc = GPG_ERR_WRONG_SECKEY; goto leave; } - dek->keylen = nframe - (n+1) - 2; - dek->algo = frame[n++]; - if( dek->algo == CIPHER_ALGO_IDEA ) - write_status(STATUS_RSA_OR_IDEA); - rc = openpgp_cipher_test_algo (dek->algo); - if( rc ) { - if( !opt.quiet && gpg_err_code (rc) == GPG_ERR_CIPHER_ALGO ) { - log_info(_("cipher algorithm %d%s is unknown or disabled\n"), - dek->algo, dek->algo == CIPHER_ALGO_IDEA? " (IDEA)":""); - if(dek->algo==CIPHER_ALGO_IDEA) - idea_cipher_warn(0); + if( n + 4 > nframe ) + { rc = G10ERR_WRONG_SECKEY; goto leave; } + + dek->keylen = nframe - (n+1) - 2; + dek->algo = frame[n++]; + if( dek->algo == CIPHER_ALGO_IDEA ) + write_status(STATUS_RSA_OR_IDEA); + rc = openpgp_cipher_test_algo (dek->algo); + if( rc ) { + if( !opt.quiet && gpg_err_code (rc) == GPG_ERR_CIPHER_ALGO ) { + log_info(_("cipher algorithm %d%s is unknown or disabled\n"), + dek->algo, dek->algo == CIPHER_ALGO_IDEA? " (IDEA)":""); + if(dek->algo==CIPHER_ALGO_IDEA) + idea_cipher_warn(0); + } + dek->algo = 0; + goto leave; } - dek->algo = 0; - goto leave; - } - if( dek->keylen != gcry_cipher_get_algo_keylen (dek->algo) ) { - rc = GPG_ERR_WRONG_SECKEY; - goto leave; - } - - /* copy the key to DEK and compare the checksum */ - csum = frame[nframe-2] << 8; - csum |= frame[nframe-1]; - memcpy( dek->key, frame+n, dek->keylen ); - for( csum2=0, n=0; n < dek->keylen; n++ ) - csum2 += dek->key[n]; - if( csum != csum2 ) { - rc = GPG_ERR_WRONG_SECKEY; - goto leave; - } - if( DBG_CIPHER ) - log_printhex ("DEK is:", dek->key, dek->keylen ); - /* check that the algo is in the preferences and whether it has expired */ - { - PKT_public_key *pk = NULL; - KBNODE pkb = get_pubkeyblock (keyid); - - if( !pkb ) { - rc = -1; - log_error("oops: public key not found for preference check\n"); + if ( dek->keylen != gcry_cipher_get_algo_keylen (dek->algo) ) { + rc = GPG_ERR_WRONG_SECKEY; + goto leave; } - else if( pkb->pkt->pkt.public_key->selfsigversion > 3 - && dek->algo != CIPHER_ALGO_3DES - && !is_algo_in_prefs( pkb, PREFTYPE_SYM, dek->algo ) ) { - /* Don't print a note while we are not on verbose mode, - * the cipher is blowfish and the preferences have twofish - * listed */ - if( opt.verbose || dek->algo != CIPHER_ALGO_BLOWFISH - || !is_algo_in_prefs( pkb, PREFTYPE_SYM, CIPHER_ALGO_TWOFISH)) - log_info(_( - "NOTE: cipher algorithm %d not found in preferences\n"), - dek->algo ); + + /* copy the key to DEK and compare the checksum */ + csum = frame[nframe-2] << 8; + csum |= frame[nframe-1]; + memcpy( dek->key, frame+n, dek->keylen ); + for( csum2=0, n=0; n < dek->keylen; n++ ) + csum2 += dek->key[n]; + if( csum != csum2 ) { + rc = G10ERR_WRONG_SECKEY; + goto leave; } + if( DBG_CIPHER ) + log_hexdump("DEK is:", dek->key, dek->keylen ); + /* check that the algo is in the preferences and whether it has expired */ + { + PKT_public_key *pk = NULL; + KBNODE pkb = get_pubkeyblock (keyid); - if (!rc) { - KBNODE k; + if( !pkb ) { + rc = -1; + log_error("oops: public key not found for preference check\n"); + } + else if(pkb->pkt->pkt.public_key->selfsigversion > 3 + && dek->algo != CIPHER_ALGO_3DES + && !opt.quiet + && !is_algo_in_prefs( pkb, PREFTYPE_SYM, dek->algo )) + log_info (_("WARNING: cipher algorithm %s not found in recipient" + " preferences\n"), gcry_cipher_algo_name (dek->algo)); + if (!rc) { + KBNODE k; - for (k=pkb; 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 (aki[0]==keyid[0] && aki[1]==keyid[1]) { - pk = k->pkt->pkt.public_key; - break; - } + for (k=pkb; 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 (aki[0]==keyid[0] && aki[1]==keyid[1]) { + pk = k->pkt->pkt.public_key; + break; + } + } + } + if (!pk) + BUG (); + if ( pk->expiredate && pk->expiredate <= make_timestamp() ) { + log_info(_("NOTE: secret key %s expired at %s\n"), + keystr(keyid), asctimestamp( pk->expiredate) ); + } } - } - if (!pk) - BUG (); - if ( pk->expiredate && pk->expiredate <= make_timestamp() ) { - log_info(_("NOTE: secret key %08lX expired at %s\n"), - (ulong)keyid[1], asctimestamp( pk->expiredate) ); - } - } - if ( pk && pk->is_revoked ) { - log_info( _("NOTE: key has been revoked") ); - putc( '\n', log_get_stream() ); - show_revocation_reason( pk, 1 ); - } + if ( pk && pk->is_revoked ) { + log_info( _("NOTE: key has been revoked") ); + putc( '\n', log_get_stream() ); + show_revocation_reason( pk, 1 ); + } - release_kbnode (pkb); - rc = 0; - } + release_kbnode (pkb); + rc = 0; + } - leave: - gcry_mpi_release (plain_dek); - xfree (frame); - return rc; + leave: + gcry_mpi_release (plain_dek); + xfree (frame); + return rc; } @@ -324,21 +336,21 @@ get_override_session_key( DEK *dek, const char *string ) int i; if ( !string ) - return GPG_ERR_BAD_KEY; + return G10ERR_BAD_KEY; dek->algo = atoi(string); if ( dek->algo < 1 ) - return GPG_ERR_BAD_KEY; + return G10ERR_BAD_KEY; if ( !(s = strchr ( string, ':' )) ) - return GPG_ERR_BAD_KEY; + return G10ERR_BAD_KEY; s++; for(i=0; i < DIM(dek->key) && *s; i++, s +=2 ) { int c = hextobyte ( s ); if (c == -1) - return GPG_ERR_BAD_KEY; + return G10ERR_BAD_KEY; dek->key[i] = c; } if ( *s ) - return GPG_ERR_BAD_KEY; + return G10ERR_BAD_KEY; dek->keylen = i; return 0; } diff --git a/g10/revoke.c b/g10/revoke.c index 161bd2b82..34f9f5c85 100644 --- a/g10/revoke.c +++ b/g10/revoke.c @@ -1,5 +1,6 @@ /* revoke.c - * Copyright (C) 1998, 1999, 2000, 2001, 2002 Free Software Foundation, Inc. + * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, + * 2004 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -15,7 +16,8 @@ * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. */ #include <config.h> @@ -26,11 +28,11 @@ #include <assert.h> #include <ctype.h> +#include "gpg.h" #include "options.h" #include "packet.h" #include "errors.h" #include "keydb.h" -#include "memory.h" #include "util.h" #include "main.h" #include "ttyio.h" @@ -59,15 +61,15 @@ revocation_reason_build_cb( PKT_signature *sig, void *opaque ) ud = native_to_utf8( reason->desc ); buflen += strlen(ud); } - buffer = xmalloc ( buflen ); + buffer = xmalloc( buflen ); *buffer = reason->code; if( ud ) { memcpy(buffer+1, ud, strlen(ud) ); - xfree ( ud ); + xfree( ud ); } build_sig_subpkt( sig, SIGSUBPKT_REVOC_REASON, buffer, buflen ); - xfree ( buffer ); + xfree( buffer ); return 0; } @@ -76,7 +78,7 @@ revocation_reason_build_cb( PKT_signature *sig, void *opaque ) and pick a user ID that has a uid signature, and include it if possible. */ static int -export_minimal_pk(iobuf_t out,KBNODE keyblock, +export_minimal_pk(IOBUF out,KBNODE keyblock, PKT_signature *revsig,PKT_signature *revkey) { KBNODE node; @@ -89,8 +91,8 @@ export_minimal_pk(iobuf_t out,KBNODE keyblock, node=find_kbnode(keyblock,PKT_PUBLIC_KEY); if(!node) { - log_error(_("key incomplete\n")); - return GPG_ERR_GENERAL; + log_error("key incomplete\n"); + return G10ERR_GENERAL; } keyid_from_pk(node->pkt->pkt.public_key,keyid); @@ -99,7 +101,7 @@ export_minimal_pk(iobuf_t out,KBNODE keyblock, rc=build_packet(out,&pkt); if(rc) { - log_error(_("build_packet failed: %s\n"), gpg_strerror (rc) ); + log_error(_("build_packet failed: %s\n"), g10_errstr(rc) ); return rc; } @@ -113,7 +115,7 @@ export_minimal_pk(iobuf_t out,KBNODE keyblock, rc=build_packet(out,&pkt); if(rc) { - log_error(_("build_packet failed: %s\n"), gpg_strerror (rc) ); + log_error(_("build_packet failed: %s\n"), g10_errstr(rc) ); return rc; } } @@ -125,7 +127,7 @@ export_minimal_pk(iobuf_t out,KBNODE keyblock, rc=build_packet(out,&pkt); if(rc) { - log_error(_("build_packet failed: %s\n"), gpg_strerror (rc) ); + log_error(_("build_packet failed: %s\n"), g10_errstr(rc) ); return rc; } } @@ -142,8 +144,8 @@ export_minimal_pk(iobuf_t out,KBNODE keyblock, break; else { - log_error(_("key %08lX incomplete\n"),(ulong)keyid[1]); - return GPG_ERR_GENERAL; + log_error(_("key %s has no user IDs\n"),keystr(keyid)); + return G10ERR_GENERAL; } } @@ -171,7 +173,7 @@ export_minimal_pk(iobuf_t out,KBNODE keyblock, rc=build_packet(out,&pkt); if(rc) { - log_error(_("build_packet failed: %s\n"), gpg_strerror (rc) ); + log_error(_("build_packet failed: %s\n"), g10_errstr(rc) ); return rc; } @@ -183,7 +185,7 @@ export_minimal_pk(iobuf_t out,KBNODE keyblock, rc=build_packet(out,&pkt); if(rc) { - log_error(_("build_packet failed: %s\n"), gpg_strerror (rc) ); + log_error(_("build_packet failed: %s\n"), g10_errstr(rc) ); return rc; } } @@ -195,39 +197,41 @@ export_minimal_pk(iobuf_t out,KBNODE keyblock, * Generate a revocation certificate for UNAME via a designated revoker */ int -gen_desig_revoke( const char *uname ) +gen_desig_revoke( const char *uname, STRLIST locusr ) { int rc = 0; armor_filter_context_t afx; PKT_public_key *pk = NULL; PKT_secret_key *sk = NULL; PKT_signature *sig = NULL; - iobuf_t out = NULL; + IOBUF out = NULL; struct revocation_reason_info *reason = NULL; KEYDB_HANDLE kdbhd; KEYDB_SEARCH_DESC desc; KBNODE keyblock=NULL,node; u32 keyid[2]; int i,any=0; + SK_LIST sk_list=NULL; - if( opt.batch ) { - log_error(_("sorry, can't do this in batch mode\n")); - return GPG_ERR_GENERAL; - } + if( opt.batch ) + { + log_error(_("can't do this in batch mode\n")); + return G10ERR_GENERAL; + } memset( &afx, 0, sizeof afx); kdbhd = keydb_new (0); classify_user_id (uname, &desc); - rc = desc.mode? keydb_search (kdbhd, &desc, 1) : GPG_ERR_INV_USER_ID; + rc = desc.mode? keydb_search (kdbhd, &desc, 1) : G10ERR_INV_USER_ID; if (rc) { - log_error (_("key `%s' not found: %s\n"),uname, gpg_strerror (rc)); + log_error (_("key \"%s\" not found: %s\n"),uname, g10_errstr (rc)); goto leave; } rc = keydb_get_keyblock (kdbhd, &keyblock ); if( rc ) { - log_error (_("error reading keyblock: %s\n"), gpg_strerror (rc) ); + log_error (_("error reading keyblock: %s\n"), g10_errstr(rc) ); goto leave; } @@ -243,6 +247,13 @@ gen_desig_revoke( const char *uname ) keyid_from_pk(pk,keyid); + if(locusr) + { + rc=build_sk_list(locusr,&sk_list,0,PUBKEY_USAGE_CERT); + if(rc) + goto leave; + } + /* Are we a designated revoker for this key? */ if(!pk->revkey && pk->numrevkeys) @@ -250,12 +261,39 @@ gen_desig_revoke( const char *uname ) for(i=0;i<pk->numrevkeys;i++) { + SK_LIST list; + if(sk) free_secret_key(sk); - sk=xcalloc (1,sizeof(*sk)); + if(sk_list) + { + for(list=sk_list;list;list=list->next) + { + byte fpr[MAX_FINGERPRINT_LEN]; + size_t fprlen; + + fingerprint_from_sk(list->sk,fpr,&fprlen); + + /* Don't get involved with keys that don't have 160 + bit fingerprints */ + if(fprlen!=20) + continue; - rc=get_seckey_byfprint(sk,pk->revkey[i].fpr,MAX_FINGERPRINT_LEN); + if(memcmp(fpr,pk->revkey[i].fpr,20)==0) + break; + } + + if(list) + sk=copy_secret_key(NULL,list->sk); + else + continue; + } + else + { + sk=xmalloc_secure_clear(sizeof(*sk)); + rc=get_seckey_byfprint(sk,pk->revkey[i].fpr,MAX_FINGERPRINT_LEN); + } /* We have the revocation key */ if(!rc) @@ -275,7 +313,7 @@ gen_desig_revoke( const char *uname ) tty_printf("\n"); if( !cpr_get_answer_is_yes("gen_desig_revoke.okay", - _("Create a revocation certificate for this key? ")) ) + _("Create a designated revocation certificate for this key? (y/N) "))) continue; /* get the reason for the revocation (this is always v4) */ @@ -294,7 +332,8 @@ gen_desig_revoke( const char *uname ) goto leave; afx.what = 1; - afx.hdrlines = "Comment: A revocation certificate should follow\n"; + afx.hdrlines = "Comment: A designated revocation certificate" + " should follow\n"; iobuf_push_filter( out, armor_filter, &afx ); /* create it */ @@ -302,7 +341,7 @@ gen_desig_revoke( const char *uname ) 0, 0, 0, revocation_reason_build_cb, reason ); if( rc ) { - log_error(_("make_keysig_packet failed: %s\n"), gpg_strerror (rc)); + log_error(_("make_keysig_packet failed: %s\n"), g10_errstr(rc)); goto leave; } @@ -371,7 +410,7 @@ gen_desig_revoke( const char *uname ) } if(!any) - log_error(_("no revocation keys found for `%s'\n"),uname); + log_error(_("no revocation keys found for \"%s\"\n"),uname); leave: if( pk ) @@ -381,6 +420,8 @@ gen_desig_revoke( const char *uname ) if( sig ) free_seckey_enc( sig ); + release_sk_list(sk_list); + if( rc ) iobuf_cancel(out); else @@ -403,17 +444,18 @@ gen_revoke( const char *uname ) PKT_public_key *pk = NULL; PKT_signature *sig = NULL; u32 sk_keyid[2]; - iobuf_t out = NULL; + IOBUF out = NULL; KBNODE keyblock = NULL, pub_keyblock = NULL; KBNODE node; KEYDB_HANDLE kdbhd; struct revocation_reason_info *reason = NULL; KEYDB_SEARCH_DESC desc; - if( opt.batch ) { - log_error(_("sorry, can't do this in batch mode\n")); - return GPG_ERR_GENERAL; - } + if( opt.batch ) + { + log_error(_("can't do this in batch mode\n")); + return G10ERR_GENERAL; + } memset( &afx, 0, sizeof afx); init_packet( &pkt ); @@ -423,16 +465,17 @@ gen_revoke( const char *uname ) */ kdbhd = keydb_new (1); classify_user_id (uname, &desc); - rc = desc.mode? keydb_search (kdbhd, &desc, 1) : GPG_ERR_INV_USER_ID; - if (rc) { - log_error (_("secret key `%s' not found: %s\n"), - uname, gpg_strerror (rc)); + rc = desc.mode? keydb_search (kdbhd, &desc, 1) : G10ERR_INV_USER_ID; + if (rc) + { + log_error (_("secret key \"%s\" not found: %s\n"), + uname, g10_errstr (rc)); goto leave; - } + } rc = keydb_get_keyblock (kdbhd, &keyblock ); if( rc ) { - log_error (_("error reading keyblock: %s\n"), gpg_strerror (rc) ); + log_error (_("error reading keyblock: %s\n"), g10_errstr(rc) ); goto leave; } @@ -447,14 +490,14 @@ gen_revoke( const char *uname ) keyid_from_sk( sk, sk_keyid ); print_seckey_info (sk); - pk = xcalloc (1, sizeof *pk ); + pk = xmalloc_clear( sizeof *pk ); /* FIXME: We should get the public key direct from the secret one */ pub_keyblock=get_pubkeyblock(sk_keyid); if(!pub_keyblock) { - log_error(_("no corresponding public key: %s\n"), gpg_strerror (rc) ); + log_error(_("no corresponding public key: %s\n"), g10_errstr(rc) ); goto leave; } @@ -466,16 +509,17 @@ gen_revoke( const char *uname ) if( cmp_public_secret_key( pk, sk ) ) { log_error(_("public key does not match secret key!\n") ); - rc = GPG_ERR_GENERAL; + rc = G10ERR_GENERAL; goto leave; } tty_printf("\n"); if( !cpr_get_answer_is_yes("gen_revoke.okay", - _("Create a revocation certificate for this key? ")) ){ + _("Create a revocation certificate for this key? (y/N) ")) ) + { rc = 0; goto leave; - } + } if(sk->version>=4 || opt.force_v4_certs) { /* get the reason for the revocation */ @@ -489,13 +533,17 @@ gen_revoke( const char *uname ) switch( is_secret_key_protected( sk ) ) { case -1: log_error(_("unknown protection algorithm\n")); - rc = GPG_ERR_PUBKEY_ALGO; + rc = G10ERR_PUBKEY_ALGO; break; + case -3: + tty_printf (_("Secret parts of primary key are not available.\n")); + rc = G10ERR_NO_SECKEY; + break; case 0: tty_printf(_("NOTE: This key is not protected!\n")); break; default: - rc = check_secret_key( sk, 0 ); + rc = check_secret_key( sk, 0 ); break; } if( rc ) @@ -517,7 +565,7 @@ gen_revoke( const char *uname ) opt.force_v4_certs?4:0, 0, 0, revocation_reason_build_cb, reason ); if( rc ) { - log_error(_("make_keysig_packet failed: %s\n"), gpg_strerror (rc)); + log_error(_("make_keysig_packet failed: %s\n"), g10_errstr(rc)); goto leave; } @@ -537,7 +585,7 @@ gen_revoke( const char *uname ) rc = build_packet( out, &pkt ); if( rc ) { - log_error(_("build_packet failed: %s\n"), gpg_strerror (rc) ); + log_error(_("build_packet failed: %s\n"), g10_errstr(rc) ); goto leave; } } @@ -581,7 +629,7 @@ ask_revocation_reason( int key_rev, int cert_rev, int hint ) do { code=-1; - xfree (description); + xfree(description); description = NULL; tty_printf(_("Please select the reason for the revocation:\n")); @@ -612,7 +660,7 @@ ask_revocation_reason( int key_rev, int cert_rev, int hint ) n = -1; else n = atoi(answer); - xfree (answer); + xfree(answer); if( n == 0 ) { code = 0x00; /* no particular reason */ code_text = text_0; @@ -644,25 +692,25 @@ ask_revocation_reason( int key_rev, int cert_rev, int hint ) trim_trailing_ws( answer, strlen(answer) ); cpr_kill_prompt(); if( !*answer ) { - xfree (answer); + xfree(answer); break; } { char *p = make_printable_string( answer, strlen(answer), 0 ); - xfree (answer); + xfree(answer); answer = p; } if( !description ) - description = xstrdup (answer); + description = xstrdup(answer); else { - char *p = xmalloc ( strlen(description) + strlen(answer) + 2 ); + char *p = xmalloc( strlen(description) + strlen(answer) + 2 ); strcpy(stpcpy(stpcpy( p, description),"\n"),answer); - xfree (description); + xfree(description); description = p; } - xfree (answer); + xfree(answer); } tty_printf(_("Reason for revocation: %s\n"), code_text ); @@ -672,9 +720,9 @@ ask_revocation_reason( int key_rev, int cert_rev, int hint ) tty_printf("%s\n", description ); } while( !cpr_get_answer_is_yes("ask_revocation_reason.okay", - _("Is this okay? ")) ); + _("Is this okay? (y/N) ")) ); - reason = xmalloc ( sizeof *reason ); + reason = xmalloc( sizeof *reason ); reason->code = code; reason->desc = description; return reason; @@ -684,7 +732,7 @@ void release_revocation_reason_info( struct revocation_reason_info *reason ) { if( reason ) { - xfree ( reason->desc ); - xfree ( reason ); + xfree( reason->desc ); + xfree( reason ); } } diff --git a/g10/seckey-cert.c b/g10/seckey-cert.c index 7356cb224..382ad7534 100644 --- a/g10/seckey-cert.c +++ b/g10/seckey-cert.c @@ -1,6 +1,6 @@ /* seckey-cert.c - secret key certificate packet handling * Copyright (C) 1998, 1999, 2000, 2001, 2002, - * 2003 Free Software Foundation, Inc. + * 2006 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -16,7 +16,8 @@ * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. */ #include <config.h> @@ -27,9 +28,7 @@ #include "gpg.h" #include "util.h" -#include "memory.h" #include "packet.h" -#include "mpi.h" #include "keydb.h" #include "cipher.h" #include "main.h" @@ -42,21 +41,21 @@ static int do_check( PKT_secret_key *sk, const char *tryagain_text, int mode, int *canceled ) { + gpg_error_t err; byte *buffer; u16 csum=0; int i, res; - unsigned nbytes; - gpg_error_t rc; + unsigned int nbytes; if( sk->is_protected ) { /* remove the protection */ DEK *dek = NULL; u32 keyid[4]; /* 4! because we need two of them */ - CIPHER_HANDLE cipher_hd=NULL; + gcry_cipher_hd_t cipher_hd=NULL; PKT_secret_key *save_sk; if( sk->protect.s2k.mode == 1001 ) { log_info(_("secret key parts are not available\n")); - return GPG_ERR_GENERAL; + return G10ERR_GENERAL; } if( sk->protect.algo == CIPHER_ALGO_NONE ) BUG(); @@ -68,8 +67,14 @@ do_check( PKT_secret_key *sk, const char *tryagain_text, int mode, write_status (STATUS_RSA_OR_IDEA); idea_cipher_warn (0); } - return GPG_ERR_CIPHER_ALGO; + return G10ERR_CIPHER_ALGO; } + if(gcry_md_test_algo (sk->protect.s2k.hash_algo)) + { + log_info(_("protection digest %d is not supported\n"), + sk->protect.s2k.hash_algo); + return G10ERR_DIGEST_ALGO; + } keyid_from_sk( sk, keyid ); keyid[2] = keyid[3] = 0; if( !sk->is_primary ) { @@ -80,39 +85,45 @@ do_check( PKT_secret_key *sk, const char *tryagain_text, int mode, &sk->protect.s2k, mode, tryagain_text, canceled ); if (!dek && canceled && *canceled) - return GPG_ERR_GENERAL; + return G10ERR_GENERAL; - rc = gcry_cipher_open (&cipher_hd, sk->protect.algo, - GCRY_CIPHER_MODE_CFB, - GCRY_CIPHER_SECURE - | (sk->protect.algo >= 100 ? - 0 : GCRY_CIPHER_ENABLE_SYNC)); - if (rc) - log_fatal ("cipher open failed: %s\n", gpg_strerror (rc) ); - rc = gcry_cipher_setkey (cipher_hd, dek->key, dek->keylen); - if (rc) - log_fatal ("set key failed: %s\n", gpg_strerror (rc) ); + err = gcry_cipher_open (&cipher_hd, sk->protect.algo, + GCRY_CIPHER_MODE_CFB, + (GCRY_CIPHER_SECURE + | (sk->protect.algo >= 100 ? + 0 : GCRY_CIPHER_ENABLE_SYNC))); + if (err) + log_fatal ("cipher open failed: %s\n", gpg_strerror (err) ); - xfree (dek); + err = gcry_cipher_setkey (cipher_hd, dek->key, dek->keylen); + if (err) + log_fatal ("set key failed: %s\n", gpg_strerror (err) ); + + xfree(dek); save_sk = copy_secret_key( NULL, sk ); - gcry_cipher_setiv (cipher_hd, sk->protect.iv, sk->protect.ivlen); + + gcry_cipher_setiv ( cipher_hd, sk->protect.iv, sk->protect.ivlen ); + csum = 0; if( sk->version >= 4 ) { - int ndata; - unsigned int ndatabits; + int 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 ); + + 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 ); - gcry_mpi_release ( sk->skey[i] ); sk->skey[i] = NULL ; + data = xmalloc_secure ( ndata ); + gcry_cipher_decrypt ( cipher_hd, data, ndata, p, ndata ); + gcry_mpi_release (sk->skey[i]); sk->skey[i] = NULL ; + p = data; if (sk->protect.sha1chk) { /* This is the new SHA1 checksum method to detect @@ -126,18 +137,19 @@ do_check( PKT_secret_key *sk, const char *tryagain_text, int mode, gcry_md_hd_t h; if ( gcry_md_open (&h, DIGEST_ALGO_SHA1, 1)) - BUG(); /* algo not available */ + BUG(); /* Algo not available. */ gcry_md_write (h, data, ndata - 20); gcry_md_final (h); if (!memcmp (gcry_md_read (h, DIGEST_ALGO_SHA1), - data + ndata - 20, 20) ) { - /* digest does match. We have to keep the old + data + ndata - 20, 20) ) + { + /* Digest does match. We have to keep the old style checksum in sk->csum, so that the test used for unprotected keys does work. This test gets used when we are adding new keys. */ sk->csum = csum = checksum (data, ndata-20); - } + } gcry_md_close (h); } } @@ -156,24 +168,27 @@ do_check( PKT_secret_key *sk, const char *tryagain_text, int mode, } } } - - /* must check it here otherwise the mpi_read_xx would fail + + /* Must check it here otherwise the mpi_read_xx would fail because the length may have an arbitrary value */ if( sk->csum == csum ) { for( ; i < pubkey_get_nskey(sk->pubkey_algo); i++ ) { - assert( gcry_is_secure( p ) ); - res = gcry_mpi_scan( &sk->skey[i], GCRYMPI_FMT_PGP, - p, ndata, &nbytes); - if( res ) - log_bug ("gcry_mpi_scan failed in do_check: %s\n", - gpg_strerror (res)); + if ( gcry_mpi_scan( &sk->skey[i], GCRYMPI_FMT_PGP, + p, ndata, &nbytes)) + { + /* Checksum was okay, but not correctly + decrypted. */ + sk->csum = 0; + csum = 1; + break; + } ndata -= nbytes; p += nbytes; } /* Note: at this point ndata should be 2 for a simple checksum or 20 for the sha1 digest */ } - xfree (data); + xfree(data); } else { for(i=pubkey_get_npkey(sk->pubkey_algo); @@ -182,12 +197,12 @@ do_check( PKT_secret_key *sk, const char *tryagain_text, int mode, int ndata; unsigned int ndatabits; - assert( gcry_mpi_get_flag (sk->skey[i], GCRYMPI_FLAG_OPAQUE)); - p = gcry_mpi_get_opaque( sk->skey[i], &ndatabits ); + assert (gcry_mpi_get_flag (sk->skey[i], GCRYMPI_FLAG_OPAQUE)); + p = gcry_mpi_get_opaque (sk->skey[i], &ndatabits); ndata = (ndatabits+7)/8; assert (ndata >= 2); assert (ndata == ((p[0] << 8 | p[1]) + 7)/8 + 2); - buffer = gcry_xmalloc_secure (ndata); + buffer = xmalloc_secure (ndata); gcry_cipher_sync (cipher_hd); buffer[0] = p[0]; buffer[1] = p[1]; @@ -195,33 +210,39 @@ do_check( PKT_secret_key *sk, const char *tryagain_text, int mode, p+2, ndata-2); csum += checksum (buffer, ndata); gcry_mpi_release (sk->skey[i]); - res = gcry_mpi_scan( &sk->skey[i], GCRYMPI_FMT_USG, - buffer, ndata, &ndata ); - if( res ) - log_bug ("gcry_mpi_scan failed in do_check: %s\n", - gpg_strerror (res)); - assert (sk->skey[i]); + err = gcry_mpi_scan( &sk->skey[i], GCRYMPI_FMT_USG, + buffer, ndata, &ndata ); xfree (buffer); + if (err) + { + /* Checksum was okay, but not correctly + decrypted. */ + sk->csum = 0; + csum = 1; + break; + } /* csum += checksum_mpi (sk->skey[i]); */ } } - gcry_cipher_close (cipher_hd); - /* now let's see whether we have used the right passphrase */ + gcry_cipher_close ( cipher_hd ); + + /* Now let's see whether we have used the correct passphrase. */ if( csum != sk->csum ) { copy_secret_key( sk, save_sk ); - passphrase_clear_cache ( keyid, sk->pubkey_algo ); + passphrase_clear_cache ( keyid, NULL, sk->pubkey_algo ); + free_secret_key( save_sk ); + return G10ERR_BAD_PASS; + } + + /* The checksum may fail, so we also check the key itself. */ + res = pk_check_secret_key ( sk->pubkey_algo, sk->skey ); + if( res ) { + copy_secret_key( sk, save_sk ); + passphrase_clear_cache ( keyid, NULL, sk->pubkey_algo ); free_secret_key( save_sk ); - return gpg_error (GPG_ERR_BAD_PASSPHRASE); + return G10ERR_BAD_PASS; } - /* the checksum may fail, so we also check the key itself */ - res = pk_check_secret_key (sk->pubkey_algo, sk->skey); - if (res) { - copy_secret_key( sk, save_sk ); - passphrase_clear_cache ( keyid, sk->pubkey_algo ); - free_secret_key( save_sk ); - return gpg_error (GPG_ERR_BAD_PASSPHRASE); - } free_secret_key( save_sk ); sk->is_protected = 0; } @@ -232,7 +253,7 @@ do_check( PKT_secret_key *sk, const char *tryagain_text, int mode, csum += checksum_mpi( sk->skey[i] ); } if( csum != sk->csum ) - return GPG_ERR_CHECKSUM; + return G10ERR_CHECKSUM; } return 0; @@ -252,7 +273,7 @@ check_secret_key( PKT_secret_key *sk, int n ) int i,mode; if (sk && sk->is_protected && sk->protect.s2k.mode == 1002) - return 0; /* Let the scdaemon handle it. */ + return 0; /* Let the scdaemon handle this. */ if(n<0) { @@ -265,7 +286,7 @@ check_secret_key( PKT_secret_key *sk, int n ) if( n < 1 ) n = (opt.batch && !opt.use_agent)? 1 : 3; /* use the default value */ - for(i=0; i < n && gpg_err_code (rc) == GPG_ERR_BAD_PASSPHRASE; i++ ) { + for(i=0; i < n && gpg_err_code (rc) == G10ERR_BAD_PASS; i++ ) { int canceled = 0; const char *tryagain = NULL; if (i) { @@ -273,8 +294,7 @@ check_secret_key( PKT_secret_key *sk, int n ) log_info (_("%s ...\n"), _(tryagain)); } rc = do_check( sk, tryagain, mode, &canceled ); - if( gpg_err_code (rc) == GPG_ERR_BAD_PASSPHRASE - && is_status_enabled() ) { + if ( gpg_err_code (rc) == G10ERR_BAD_PASS && is_status_enabled () ) { u32 kid[2]; char buf[50]; @@ -296,13 +316,14 @@ check_secret_key( PKT_secret_key *sk, int n ) * check whether the secret key is protected. * Returns: 0 not protected, -1 on error or the protection algorithm * -2 indicates a card stub. + * -3 indicates a not-online stub. */ int is_secret_key_protected( PKT_secret_key *sk ) { return sk->is_protected? - sk->protect.s2k.mode == 1002? -2 - : sk->protect.algo : 0; + sk->protect.s2k.mode == 1002? -2 : + sk->protect.s2k.mode == 1001? -3 : sk->protect.algo : 0; } @@ -324,54 +345,52 @@ protect_secret_key( PKT_secret_key *sk, DEK *dek ) if( !sk->is_protected ) { /* okay, apply the protection */ gcry_cipher_hd_t cipher_hd=NULL; - if( openpgp_cipher_test_algo( sk->protect.algo ) ) - { - rc = gpg_error (GPG_ERR_CIPHER_ALGO); /* unsupport - protection - algorithm */ - } + if ( openpgp_cipher_test_algo ( sk->protect.algo ) ) { + /* Unsupport protection algorithm. */ + rc = gpg_error (GPG_ERR_CIPHER_ALGO); + } else { print_cipher_algo_note( sk->protect.algo ); - rc = gcry_cipher_open (&cipher_hd, sk->protect.algo, + + if ( gcry_cipher_open (&cipher_hd, sk->protect.algo, GCRY_CIPHER_MODE_CFB, - GCRY_CIPHER_SECURE - | (sk->protect.algo >= 100 ? - 0 : GCRY_CIPHER_ENABLE_SYNC) ); - if (rc) + (GCRY_CIPHER_SECURE + | (sk->protect.algo >= 100 ? + 0 : GCRY_CIPHER_ENABLE_SYNC))) ) BUG(); - if( gcry_cipher_setkey( cipher_hd, dek->key, dek->keylen ) ) + if ( gcry_cipher_setkey ( cipher_hd, dek->key, dek->keylen ) ) log_info(_("WARNING: Weak key detected" " - please change passphrase again.\n")); - sk->protect.ivlen = gcry_cipher_get_algo_blklen(sk->protect.algo); + sk->protect.ivlen = gcry_cipher_get_algo_blklen (sk->protect.algo); assert( sk->protect.ivlen <= DIM(sk->protect.iv) ); if( sk->protect.ivlen != 8 && sk->protect.ivlen != 16 ) BUG(); /* yes, we are very careful */ gcry_create_nonce (sk->protect.iv, sk->protect.ivlen); - gcry_cipher_setiv( cipher_hd, sk->protect.iv, sk->protect.ivlen ); + gcry_cipher_setiv (cipher_hd, sk->protect.iv, sk->protect.ivlen); if( sk->version >= 4 ) { - unsigned char *bufarr[PUBKEY_MAX_NSKEY]; + byte *bufarr[PUBKEY_MAX_NSKEY]; unsigned narr[PUBKEY_MAX_NSKEY]; unsigned nbits[PUBKEY_MAX_NSKEY]; int ndata=0; byte *p, *data; - for(j=0, i = pubkey_get_npkey(sk->pubkey_algo); - i < pubkey_get_nskey(sk->pubkey_algo); i++, j++ ) { - assert( !gcry_mpi_get_flag( sk->skey[i], - GCRYMPI_FLAG_OPAQUE )); - - if( gcry_mpi_aprint( GCRYMPI_FMT_USG, bufarr+j, + for (j=0, i = pubkey_get_npkey(sk->pubkey_algo); + i < pubkey_get_nskey(sk->pubkey_algo); i++, j++ ) + { + assert (!gcry_mpi_get_flag (sk->skey[i], + GCRYMPI_FLAG_OPAQUE)); + if (gcry_mpi_aprint (GCRYMPI_FMT_USG, bufarr+j, narr+j, sk->skey[i])) - BUG(); - - nbits[j] = gcry_mpi_get_nbits( sk->skey[i] ); + BUG(); + nbits[j] = gcry_mpi_get_nbits (sk->skey[i]); ndata += narr[j] + 2; - } - for( ; j < PUBKEY_MAX_NSKEY; j++ ) - bufarr[j] = NULL; + } + for ( ; j < PUBKEY_MAX_NSKEY; j++ ) + bufarr[j] = NULL; + ndata += opt.simple_sk_checksum? 2 : 20; /* for checksum */ - data = xmalloc_secure ( ndata ); + data = xmalloc_secure( ndata ); p = data; for(j=0; j < PUBKEY_MAX_NSKEY && bufarr[j]; j++ ) { p[0] = nbits[j] >> 8 ; @@ -379,7 +398,7 @@ protect_secret_key( PKT_secret_key *sk, DEK *dek ) p += 2; memcpy(p, bufarr[j], narr[j] ); p += narr[j]; - xfree (bufarr[j]); + xfree(bufarr[j]); } if (opt.simple_sk_checksum) { @@ -395,10 +414,10 @@ protect_secret_key( PKT_secret_key *sk, DEK *dek ) gcry_md_hd_t h; if (gcry_md_open (&h, GCRY_MD_SHA1, 1)) - BUG(); /* algo not available */ + BUG(); /* Algo not available. */ gcry_md_write (h, data, ndata - 20); gcry_md_final (h); - memcpy (p, gcry_md_read (h, GCRY_MD_SHA1), 20); + memcpy (p, gcry_md_read (h, DIGEST_ALGO_SHA1), 20); p += 20; gcry_md_close (h); sk->csum = csum = 0; @@ -406,14 +425,15 @@ protect_secret_key( PKT_secret_key *sk, DEK *dek ) } assert( p == data+ndata ); - gcry_cipher_encrypt( cipher_hd, data, ndata, NULL, 0 ); - for(i = pubkey_get_npkey(sk->pubkey_algo); - i < pubkey_get_nskey(sk->pubkey_algo); i++ ) { - gcry_mpi_release ( sk->skey[i] ); + gcry_cipher_encrypt (cipher_hd, data, ndata, NULL, 0); + for (i = pubkey_get_npkey(sk->pubkey_algo); + i < pubkey_get_nskey(sk->pubkey_algo); i++ ) + { + gcry_mpi_release (sk->skey[i]); sk->skey[i] = NULL; - } + } i = pubkey_get_npkey(sk->pubkey_algo); - sk->skey[i] = gcry_mpi_set_opaque(NULL, data, ndata*8); + sk->skey[i] = gcry_mpi_set_opaque (NULL, data, ndata*8 ); } else { csum = 0; @@ -423,30 +443,33 @@ protect_secret_key( PKT_secret_key *sk, DEK *dek ) unsigned int nbits; csum += checksum_mpi (sk->skey[i]); - if( gcry_mpi_aprint( GCRYMPI_FMT_USG, &buffer, - &nbytes, sk->skey[i] ) ) - BUG(); + + if (gcry_mpi_aprint (GCRYMPI_FMT_USG, &buffer, + &nbytes, sk->skey[i] )) + BUG(); gcry_cipher_sync (cipher_hd); - assert (!gcry_mpi_get_flag( sk->skey[i], - GCRYMPI_FLAG_OPAQUE )); - data = xmalloc (nbytes+2); - nbits = gcry_mpi_get_nbits (sk->skey[i]); + assert (!gcry_mpi_get_flag (sk->skey[i], + GCRYMPI_FLAG_OPAQUE)); + + data = xmalloc (nbytes+2); /* fixme: need xtrymalloc. */ + nbits = gcry_mpi_get_nbits (sk->skey[i]); assert (nbytes == (nbits + 7)/8); data[0] = nbits >> 8; data[1] = nbits; gcry_cipher_encrypt (cipher_hd, data+2, nbytes, buffer, nbytes); - xfree ( buffer ); + xfree( buffer ); gcry_mpi_release (sk->skey[i]); - sk->skey[i] = gcry_mpi_set_opaque (NULL, data, - (nbytes+2)*8); + sk->skey[i] = gcry_mpi_set_opaque (NULL, + data, (nbytes+2)*8 ); } sk->csum = csum; } sk->is_protected = 1; - gcry_cipher_close( cipher_hd ); + gcry_cipher_close (cipher_hd); } } return rc; } + diff --git a/g10/seskey.c b/g10/seskey.c index be2535ace..a31cbb15e 100644 --- a/g10/seskey.c +++ b/g10/seskey.c @@ -1,5 +1,6 @@ /* seskey.c - make sesssion keys etc. - * Copyright (C) 1998, 1999, 2000, 2001, 2003 Free Software Foundation, Inc. + * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, + * 2006 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -15,7 +16,8 @@ * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. */ #include <config.h> @@ -27,10 +29,9 @@ #include "gpg.h" #include "util.h" #include "cipher.h" -#include "mpi.h" #include "main.h" #include "i18n.h" -#include "options.h" + /**************** * Make a session key and put it into DEK @@ -38,35 +39,33 @@ void make_session_key( DEK *dek ) { - gcry_cipher_hd_t chd; - int i, rc; + gcry_cipher_hd_t chd; + int i, rc; - dek->keylen = gcry_cipher_get_algo_keylen (dek->algo); + dek->keylen = gcry_cipher_get_algo_keylen (dek->algo); - if (gcry_cipher_open (&chd, dek->algo, GCRY_CIPHER_MODE_CFB, + if (gcry_cipher_open (&chd, dek->algo, GCRY_CIPHER_MODE_CFB, (GCRY_CIPHER_SECURE | (dek->algo >= 100 ? 0 : GCRY_CIPHER_ENABLE_SYNC))) ) - BUG(); - - gcry_randomize (dek->key, dek->keylen, GCRY_STRONG_RANDOM ); - for (i=0; i < 16; i++ ) - { - rc = gcry_cipher_setkey (chd, dek->key, dek->keylen); - if (!rc) - { - gcry_cipher_close (chd); - return; - } - if (gpg_err_code (rc) != GPG_ERR_WEAK_KEY) - BUG(); - log_info (_("weak key created - retrying\n") ); - /* Renew the session key until we get a non-weak key. */ - gcry_randomize (dek->key, dek->keylen, GCRY_STRONG_RANDOM ); - } - - log_fatal (_("cannot avoid weak key for symmetric cipher; " - "tried %d times!\n"), i); + BUG(); + gcry_randomize (dek->key, dek->keylen, GCRY_STRONG_RANDOM ); + for (i=0; i < 16; i++ ) + { + rc = gcry_cipher_setkey (chd, dek->key, dek->keylen); + if (!rc) + { + gcry_cipher_close (chd); + return; + } + if (gpg_err_code (rc) != GPG_ERR_WEAK_KEY) + BUG(); + log_info(_("weak key created - retrying\n") ); + /* Renew the session key until we get a non-weak key. */ + gcry_randomize (dek->key, dek->keylen, GCRY_STRONG_RANDOM); + } + log_fatal (_("cannot avoid weak key for symmetric cipher; " + "tried %d times!\n"), i); } @@ -85,7 +84,7 @@ encode_session_key (DEK *dek, unsigned int nbits) u16 csum; gcry_mpi_t a; - /* the current limitation is that we can only use a session key + /* The current limitation is that we can only use a session key * whose length is a multiple of BITS_PER_MPI_LIMB * I think we can live with that. */ @@ -110,14 +109,14 @@ encode_session_key (DEK *dek, unsigned int nbits) for( p = dek->key, i=0; i < dek->keylen; i++ ) csum += *p++; - frame = gcry_xmalloc_secure ( nframe ); + frame = xmalloc_secure( nframe ); n = 0; frame[n++] = 0; frame[n++] = 2; i = nframe - 6 - dek->keylen; assert( i > 0 ); p = gcry_random_bytes_secure (i, GCRY_STRONG_RANDOM); - /* replace zero bytes by new values */ + /* Replace zero bytes by new values. */ for(;;) { int j, k; byte *pp; @@ -128,36 +127,35 @@ encode_session_key (DEK *dek, unsigned int nbits) k++; if( !k ) break; /* okay: no zero bytes */ - k += k/128; /* better get some more */ - pp = gcry_random_bytes_secure( k, GCRY_STRONG_RANDOM); - for(j=0; j < i && k ; j++ ) + k += k/128 + 3; /* better get some more */ + pp = gcry_random_bytes_secure (k, GCRY_STRONG_RANDOM); + for(j=0; j < i && k ;) { if( !p[j] ) p[j] = pp[--k]; - xfree (pp); + if (p[j]) + j++; + } + xfree(pp); } memcpy( frame+n, p, i ); - xfree (p); + xfree(p); n += i; frame[n++] = 0; frame[n++] = dek->algo; memcpy( frame+n, dek->key, dek->keylen ); n += dek->keylen; frame[n++] = csum >>8; frame[n++] = csum; - assert (n == nframe); - - if (DBG_CIPHER) - log_printhex ("encoded session key:", frame, nframe ); - + assert( n == nframe ); if (gcry_mpi_scan( &a, GCRYMPI_FMT_USG, frame, n, &nframe)) BUG(); - xfree (frame); + xfree(frame); return a; } static gcry_mpi_t do_encode_md( gcry_md_hd_t md, int algo, size_t len, unsigned nbits, - const byte *asn, size_t asnlen, int v3compathack ) + const byte *asn, size_t asnlen ) { int nframe = (nbits+7) / 8; byte *frame; @@ -170,14 +168,14 @@ do_encode_md( gcry_md_hd_t md, int algo, size_t len, unsigned nbits, /* We encode the MD in this way: * - * 0 A PAD(n bytes) 0 ASN(asnlen bytes) MD(len bytes) + * 0 1 PAD(n bytes) 0 ASN(asnlen bytes) MD(len bytes) * * PAD consists of FF bytes. */ - frame = gcry_md_is_secure (md)? xmalloc_secure (nframe): xmalloc (nframe); + frame = gcry_md_is_secure (md)? xmalloc_secure (nframe) : xmalloc (nframe); n = 0; frame[n++] = 0; - frame[n++] = v3compathack? algo : 1; /* block type */ + frame[n++] = 1; /* block type */ i = nframe - len - asnlen -3 ; assert( i > 1 ); memset( frame+n, 0xff, i ); n += i; @@ -185,36 +183,83 @@ do_encode_md( gcry_md_hd_t md, int algo, size_t len, unsigned nbits, memcpy( frame+n, asn, asnlen ); n += asnlen; memcpy( frame+n, gcry_md_read (md, algo), len ); n += len; assert( n == nframe ); + if (gcry_mpi_scan( &a, GCRYMPI_FMT_USG, frame, n, &nframe )) BUG(); - xfree (frame); + xfree(frame); + + /* Note that PGP before version 2.3 encoded the MD as: + * + * 0 1 MD(16 bytes) 0 PAD(n bytes) 1 + * + * The MD is always 16 bytes here because it's always MD5. We do + * not support pre-v2.3 signatures, but I'm including this comment + * so the information is easily found in the future. + */ + return a; } /**************** * Encode a message digest into an MPI. - * v3compathack is used to work around a bug in old GnuPG versions - * which did put the algo identifier inseatd of the block type 1 into - * the encoded value. Setting this flag forces the old behaviour. + * If it's for a DSA signature, make sure that the hash is large + * enough to fill up q. If the hash is too big, take the leftmost + * bits. */ gcry_mpi_t -encode_md_value (int pubkey_algo, gcry_md_hd_t md, int hash_algo, - unsigned int nbits, int v3compathack ) +encode_md_value (PKT_public_key *pk, PKT_secret_key *sk, + gcry_md_hd_t md, int hash_algo) { - int algo = hash_algo? hash_algo : gcry_md_get_algo (md); gcry_mpi_t frame; - - if (pubkey_algo == GCRY_PK_DSA) + + assert(hash_algo); + assert(pk || sk); + + if((pk?pk->pubkey_algo:sk->pubkey_algo) == GCRY_PK_DSA) { - size_t n = gcry_md_get_algo_dlen(hash_algo); - if (n != 20) - { - log_error (_("DSA requires the use of a 160 bit hash algorithm\n")); - return NULL; - } - if (gcry_mpi_scan( &frame, GCRYMPI_FMT_USG, - gcry_md_read (md, hash_algo), n, &n ) ) + /* It's a DSA signature, so find out the size of q. */ + + unsigned int qbytes = gcry_mpi_get_nbits (pk?pk->pkey[1]:sk->skey[1]); + size_t n; + + /* Make sure it is a multiple of 8 bits. */ + + if(qbytes%8) + { + log_error(_("DSA requires the hash length to be a" + " multiple of 8 bits\n")); + return NULL; + } + + /* Don't allow any q smaller than 160 bits. This might need a + revisit as the DSA2 design firms up, but for now, we don't + want someone to issue signatures from a key with a 16-bit q + or something like that, which would look correct but allow + trivial forgeries. Yes, I know this rules out using MD5 with + DSA. ;) */ + + if(qbytes<160) + { + log_error(_("DSA key %s uses an unsafe (%u bit) hash\n"), + pk?keystr_from_pk(pk):keystr_from_sk(sk),qbytes); + return NULL; + } + + qbytes/=8; + + /* Check if we're too short. Too long is safe as we'll + automatically left-truncate. */ + + if(gcry_md_get_algo_dlen (hash_algo) < qbytes) + { + log_error(_("DSA key %s requires a %u bit or larger hash\n"), + pk?keystr_from_pk(pk):keystr_from_sk(sk),qbytes*8); + return NULL; + } + + if (gcry_mpi_scan (&frame, GCRYMPI_FMT_USG, + gcry_md_read (md, hash_algo), n, &n)) BUG(); } else @@ -222,23 +267,19 @@ encode_md_value (int pubkey_algo, gcry_md_hd_t md, int hash_algo, gpg_error_t rc; byte *asn; size_t asnlen; - - rc = gcry_md_algo_info( algo, GCRYCTL_GET_ASNOID, NULL, &asnlen); + + rc = gcry_md_algo_info (hash_algo, GCRYCTL_GET_ASNOID, NULL, &asnlen); if (rc) - log_fatal("can't get OID of algo %d: %s\n", - algo, gpg_strerror (rc)); + log_fatal ("can't get OID of algo %d: %s\n", + hash_algo, gpg_strerror (rc)); asn = xmalloc (asnlen); - if( gcry_md_algo_info( algo, GCRYCTL_GET_ASNOID, asn, &asnlen ) ) + if ( gcry_md_algo_info (hash_algo, GCRYCTL_GET_ASNOID, asn, &asnlen) ) BUG(); - frame = do_encode_md( md, algo, gcry_md_get_algo_dlen( algo ), - nbits, asn, asnlen, v3compathack ); + frame = do_encode_md (md, hash_algo, gcry_md_get_algo_dlen (hash_algo), + gcry_mpi_get_nbits (pk?pk->pkey[0]:sk->skey[0]), + asn, asnlen); xfree (asn); } + return frame; } - - - - - - diff --git a/g10/sig-check.c b/g10/sig-check.c index b0c89cba3..1bb77f7f6 100644 --- a/g10/sig-check.c +++ b/g10/sig-check.c @@ -1,6 +1,6 @@ /* sig-check.c - Check a signature - * Copyright (C) 1998, 1999, 2000, 2001, 2002, - * 2003 Free Software Foundation, Inc. + * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, + * 2004, 2006 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -16,7 +16,8 @@ * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. */ #include <config.h> @@ -28,8 +29,6 @@ #include "gpg.h" #include "util.h" #include "packet.h" -#include "memory.h" -#include "mpi.h" #include "keydb.h" #include "cipher.h" #include "main.h" @@ -38,13 +37,17 @@ #include "options.h" #include "pkglue.h" -struct cmp_help_context_s { - PKT_signature *sig; - MD_HANDLE md; +/* Context used by the compare function. */ +struct cmp_help_context_s +{ + PKT_signature *sig; + gcry_md_hd_t md; }; -static int do_check( PKT_public_key *pk, PKT_signature *sig, MD_HANDLE digest, + +static int do_check( PKT_public_key *pk, PKT_signature *sig, + gcry_md_hd_t digest, int *r_expired, int *r_revoked, PKT_public_key *ret_pk); /**************** @@ -53,37 +56,72 @@ static int do_check( PKT_public_key *pk, PKT_signature *sig, MD_HANDLE digest, * is able to append some data, before finalizing the digest. */ int -signature_check( PKT_signature *sig, MD_HANDLE digest ) +signature_check (PKT_signature *sig, gcry_md_hd_t digest) { return signature_check2( sig, digest, NULL, NULL, NULL, NULL ); } int -signature_check2( PKT_signature *sig, MD_HANDLE digest, u32 *r_expiredate, +signature_check2 (PKT_signature *sig, gcry_md_hd_t digest, u32 *r_expiredate, int *r_expired, int *r_revoked, PKT_public_key *ret_pk ) { - PKT_public_key *pk = xcalloc (1, sizeof *pk ); + PKT_public_key *pk = xmalloc_clear( sizeof *pk ); int rc=0; - /* Sanity check that the md has a context for the hash that the - sig is expecting. This can happen if a onepass sig header does - not match the actual sig, and also if the clearsign "Hash:" - header is missing or does not match the actual sig. */ + if ( (rc=openpgp_md_test_algo(sig->digest_algo)) ) + ; /* We don't have this digest. */ + else if ((rc=openpgp_pk_test_algo(sig->pubkey_algo))) + ; /* We don't have this pubkey algo. */ + else if (!gcry_md_is_enabled (digest,sig->digest_algo)) + { + /* Sanity check that the md has a context for the hash that the + sig is expecting. This can happen if a onepass sig header does + not match the actual sig, and also if the clearsign "Hash:" + header is missing or does not match the actual sig. */ - if(!gcry_md_is_enabled (digest,sig->digest_algo)) { log_info(_("WARNING: signature digest conflict in message\n")); - rc=GPG_ERR_GENERAL; - } + rc=G10ERR_GENERAL; + } else if( get_pubkey( pk, sig->keyid ) ) - rc = GPG_ERR_NO_PUBKEY; + rc = G10ERR_NO_PUBKEY; else if(!pk->is_valid && !pk->is_primary) - rc=GPG_ERR_BAD_PUBKEY; /* you cannot have a good sig from an + rc=G10ERR_BAD_PUBKEY; /* you cannot have a good sig from an invalid subkey */ - else { - if (r_expiredate) - *r_expiredate = pk->expiredate; + else + { + if(r_expiredate) + *r_expiredate = pk->expiredate; + rc = do_check( pk, sig, digest, r_expired, r_revoked, ret_pk ); - } + + /* Check the backsig. This is a 0x19 signature from the + subkey on the primary key. The idea here is that it should + not be possible for someone to "steal" subkeys and claim + them as their own. The attacker couldn't actually use the + subkey, but they could try and claim ownership of any + signaures issued by it. */ + if(rc==0 && !pk->is_primary && pk->backsig<2) + { + if(pk->backsig==0) + { + log_info(_("WARNING: signing subkey %s is not" + " cross-certified\n"),keystr_from_pk(pk)); + log_info(_("please see %s for more information\n"), + "http://www.gnupg.org/faq/subkey-cross-certify.html"); + /* --require-cross-certification makes this warning an + error. TODO: change the default to require this + after more keys have backsigs. */ + if(opt.flags.require_cross_cert) + rc=G10ERR_GENERAL; + } + else if(pk->backsig==1) + { + log_info(_("WARNING: signing subkey %s has an invalid" + " cross-certification\n"),keystr_from_pk(pk)); + rc=G10ERR_GENERAL; + } + } + } free_public_key( pk ); @@ -96,35 +134,38 @@ signature_check2( PKT_signature *sig, MD_HANDLE digest, u32 *r_expiredate, * one second. Some remote batch processing applications might * like this feature here */ gcry_md_hd_t md; + u32 a = sig->timestamp; int i, nsig = pubkey_get_nsig( sig->pubkey_algo ); byte *p, *buffer; - gcry_md_open (&md, GCRY_MD_RMD160, 0); + if (gcry_md_open (&md, GCRY_MD_RMD160, 0)) + BUG (); + + /* FIXME: Why the hell are we updating DIGEST here??? */ gcry_md_putc( digest, sig->pubkey_algo ); gcry_md_putc( digest, sig->digest_algo ); gcry_md_putc( digest, (a >> 24) & 0xff ); gcry_md_putc( digest, (a >> 16) & 0xff ); - gcry_md_putc( digest, (a >> 8) & 0xff ); - gcry_md_putc( digest, a & 0xff ); + gcry_md_putc( digest, (a >> 8) & 0xff ); + gcry_md_putc( digest, a & 0xff ); for(i=0; i < nsig; i++ ) { size_t n; unsigned char *tmp; if (gcry_mpi_aprint (GCRYMPI_FMT_USG, &tmp, &n, sig->data[i])) - BUG(); - + BUG(); gcry_md_write (md, tmp, n); xfree (tmp); } - gcry_md_final( md ); - p = make_radix64_string( gcry_md_read( md, 0 ), 20 ); - buffer = xmalloc ( strlen(p) + 60 ); + gcry_md_final (md); + p = make_radix64_string ( gcry_md_read( md, 0 ), 20 ); + buffer = xmalloc( strlen(p) + 60 ); sprintf( buffer, "%s %s %lu", p, strtimestamp( sig->timestamp ), (ulong)sig->timestamp ); write_status_text( STATUS_SIG_ID, buffer ); - xfree (buffer); - xfree (p); + xfree(buffer); + xfree(p); gcry_md_close(md); } @@ -134,58 +175,51 @@ signature_check2( PKT_signature *sig, MD_HANDLE digest, u32 *r_expiredate, static int do_check_messages( PKT_public_key *pk, PKT_signature *sig, - int *r_expired, int *r_revoked ) + int *r_expired, int *r_revoked ) { u32 cur_time; - if (r_expired) + if(r_expired) *r_expired = 0; - if (r_revoked) + if(r_revoked) *r_revoked = 0; - if( pk->version == 4 && pk->pubkey_algo == PUBKEY_ALGO_ELGAMAL_E ) { - log_info(_("key %08lX: this is a PGP generated " - "ElGamal key which is NOT secure for signatures!\n"), - (ulong)keyid_from_pk(pk,NULL)); - return GPG_ERR_PUBKEY_ALGO; - } - if( pk->timestamp > sig->timestamp ) { + if( pk->timestamp > sig->timestamp ) + { ulong d = pk->timestamp - sig->timestamp; - log_info( d==1 - ? _("public key %08lX is %lu second newer than the signature\n") - : _("public key %08lX is %lu seconds newer than the signature\n"), - (ulong)keyid_from_pk(pk,NULL),d ); + log_info(d==1 + ?_("public key %s is %lu second newer than the signature\n") + :_("public key %s is %lu seconds newer than the signature\n"), + keystr_from_pk(pk),d ); if( !opt.ignore_time_conflict ) - return GPG_ERR_TIME_CONFLICT; /* pubkey newer than signature */ - } + return G10ERR_TIME_CONFLICT; /* pubkey newer than signature */ + } cur_time = make_timestamp(); - if( pk->timestamp > cur_time ) { + if( pk->timestamp > cur_time ) + { ulong d = pk->timestamp - cur_time; - log_info( d==1 ? _("key %08lX has been created %lu second " - "in future (time warp or clock problem)\n") - : _("key %08lX has been created %lu seconds " - "in future (time warp or clock problem)\n"), - (ulong)keyid_from_pk(pk,NULL),d ); + log_info( d==1 + ? _("key %s was created %lu second" + " in the future (time warp or clock problem)\n") + : _("key %s was created %lu seconds" + " in the future (time warp or clock problem)\n"), + keystr_from_pk(pk),d ); if( !opt.ignore_time_conflict ) - return GPG_ERR_TIME_CONFLICT; - } + return G10ERR_TIME_CONFLICT; + } if( pk->expiredate && pk->expiredate < cur_time ) { char buf[11]; - if (opt.verbose) { - u32 tmp_kid[2]; - - keyid_from_pk( pk, tmp_kid ); - log_info(_("NOTE: signature key %08lX expired %s\n"), - (ulong)tmp_kid[1], asctimestamp( pk->expiredate ) ); - } + if (opt.verbose) + log_info(_("NOTE: signature key %s expired %s\n"), + keystr_from_pk(pk), asctimestamp( pk->expiredate ) ); /* SIGEXPIRED is deprecated. Use KEYEXPIRED. */ sprintf(buf,"%lu",(ulong)pk->expiredate); write_status_text(STATUS_KEYEXPIRED,buf); write_status(STATUS_SIGEXPIRED); - if (r_expired) - *r_expired = 1; + if(r_expired) + *r_expired = 1; } if(pk->is_revoked && r_revoked) @@ -196,25 +230,21 @@ do_check_messages( PKT_public_key *pk, PKT_signature *sig, static int -do_check( PKT_public_key *pk, PKT_signature *sig, MD_HANDLE digest, +do_check( PKT_public_key *pk, PKT_signature *sig, gcry_md_hd_t digest, int *r_expired, int *r_revoked, PKT_public_key *ret_pk ) { gcry_mpi_t result = NULL; - int rc=0; + int rc = 0; struct cmp_help_context_s ctx; if( (rc=do_check_messages(pk,sig,r_expired,r_revoked)) ) return rc; - if( (rc=gcry_md_test_algo(sig->digest_algo)) ) - return rc; - if( (rc=gcry_pk_test_algo(sig->pubkey_algo)) ) - return rc; - /* make sure the digest algo is enabled (in case of a detached - signature)*/ - gcry_md_enable( digest, sig->digest_algo ); + /* Make sure the digest algo is enabled (in case of a detached + signature). */ + gcry_md_enable (digest, sig->digest_algo); - /* complete the digest */ + /* Complete the digest. */ if( sig->version >= 4 ) gcry_md_putc( digest, sig->version ); gcry_md_putc( digest, sig->sig_class ); @@ -253,38 +283,22 @@ do_check( PKT_public_key *pk, PKT_signature *sig, MD_HANDLE digest, buf[5] = n; gcry_md_write( digest, buf, 6 ); } - gcry_md_final (digest); + gcry_md_final( digest ); - result = encode_md_value( pk->pubkey_algo, digest, sig->digest_algo, - mpi_get_nbits(pk->pkey[0]), 0 ); + result = encode_md_value( pk, NULL, digest, sig->digest_algo ); if (!result) - return GPG_ERR_GENERAL; + return G10ERR_GENERAL; ctx.sig = sig; ctx.md = digest; - rc = pk_verify ( pk->pubkey_algo, result, sig->data, pk->pkey); - gcry_mpi_release ( result ); - if( (opt.emulate_bugs & EMUBUG_MDENCODE) - && gpg_err_code (rc) == GPG_ERR_BAD_SIGNATURE - && is_ELGAMAL(pk->pubkey_algo) ) { - /* In this case we try again because old GnuPG versions didn't encode - * the hash right. There is no problem with DSA however */ - result = encode_md_value( pk->pubkey_algo, digest, sig->digest_algo, - mpi_get_nbits(pk->pkey[0]), (sig->version < 5) ); - if (!result) - rc = GPG_ERR_GENERAL; - else { - ctx.sig = sig; - ctx.md = digest; - rc = pk_verify (pk->pubkey_algo, result, sig->data, pk->pkey); - } - } + rc = pk_verify( pk->pubkey_algo, result, sig->data, pk->pkey ); + gcry_mpi_release (result); - if( !rc && sig->flags.unknown_critical ) { - log_info(_("assuming bad signature from key %08lX " - "due to an unknown critical bit\n"), - (ulong)keyid_from_pk(pk,NULL)); - rc = gpg_error (GPG_ERR_BAD_SIGNATURE); - } + if( !rc && sig->flags.unknown_critical ) + { + log_info(_("assuming bad signature from key %s" + " due to an unknown critical bit\n"),keystr_from_pk(pk)); + rc = G10ERR_BAD_SIGN; + } if(!rc && ret_pk) copy_public_key(ret_pk,pk); @@ -293,6 +307,7 @@ do_check( PKT_public_key *pk, PKT_signature *sig, MD_HANDLE digest, } + static void hash_uid_node( KBNODE unode, MD_HANDLE md, PKT_signature *sig ) { @@ -342,34 +357,36 @@ cache_sig_result ( PKT_signature *sig, int result ) } } - /* Check the revocation keys to see if any of them have revoked our pk. sig is the revocation sig. pk is the key it is on. This code will need to be modified if gpg ever becomes multi-threaded. Note that this guarantees that a designated revocation sig will never be considered valid unless it is actually valid, as well as being - issued by a revocation key in a valid direct signature. Note that - this is written so that a revoked revoker can still issue + issued by a revocation key in a valid direct signature. Note also + that this is written so that a revoked revoker can still issue revocations: i.e. If A revokes B, but A is revoked, B is still revoked. I'm not completely convinced this is the proper behavior, but it matches how PGP does it. -dms */ /* Returns 0 if sig is valid (i.e. pk is revoked), non-0 if not - revoked */ + revoked. It is important that G10ERR_NO_PUBKEY is only returned + when a revocation signature is from a valid revocation key + designated in a revkey subpacket, but the revocation key itself + isn't present. */ int check_revocation_keys(PKT_public_key *pk,PKT_signature *sig) { static int busy=0; - int i,rc=GPG_ERR_GENERAL; + int i,rc=G10ERR_GENERAL; assert(IS_KEY_REV(sig)); assert((sig->keyid[0]!=pk->keyid[0]) || (sig->keyid[0]!=pk->keyid[1])); if(busy) { - /* return -1 (i.e. not revoked), but mark the pk as uncacheable - as we don't really know its revocation status until it is - checked directly. */ + /* return an error (i.e. not revoked), but mark the pk as + uncacheable as we don't really know its revocation status + until it is checked directly. */ pk->dont_cache=1; return rc; @@ -394,7 +411,8 @@ check_revocation_keys(PKT_public_key *pk,PKT_signature *sig) { gcry_md_hd_t md; - gcry_md_open (&md, sig->digest_algo,0); + if (gcry_md_open (&md, sig->digest_algo, 0)) + BUG (); hash_public_key(md,pk); rc=signature_check(sig,md); cache_sig_result(sig,rc); @@ -407,6 +425,39 @@ check_revocation_keys(PKT_public_key *pk,PKT_signature *sig) return rc; } +/* Backsigs (0x19) have the same format as binding sigs (0x18), but + this function is simpler than check_key_signature in a few ways. + For example, there is no support for expiring backsigs since it is + questionable what such a thing actually means. Note also that the + sig cache check here, unlike other sig caches in GnuPG, is not + persistent. */ +int +check_backsig(PKT_public_key *main_pk,PKT_public_key *sub_pk, + PKT_signature *backsig) +{ + gcry_md_hd_t md; + int rc; + + if(!opt.no_sig_cache && backsig->flags.checked) + { + if((rc=openpgp_md_test_algo (backsig->digest_algo))) + return rc; + + return backsig->flags.valid? 0 : gpg_error (GPG_ERR_BAD_SIGNATURE); + } + + if (gcry_md_open (&md, backsig->digest_algo,0)) + BUG (); + hash_public_key(md,main_pk); + hash_public_key(md,sub_pk); + rc=do_check(sub_pk,backsig,md,NULL,NULL,NULL); + cache_sig_result(backsig,rc); + gcry_md_close(md); + + return rc; +} + + /**************** * check the signature pointed to by NODE. This is a key signature. * If the function detects a self-signature, it uses the PK from @@ -415,7 +466,7 @@ check_revocation_keys(PKT_public_key *pk,PKT_signature *sig) int check_key_signature( KBNODE root, KBNODE node, int *is_selfsig ) { - return check_key_signature2(root, node, NULL, NULL, is_selfsig, NULL, NULL); + return check_key_signature2(root, node, NULL, NULL, is_selfsig, NULL, NULL ); } /* If check_pk is set, then use it to check the signature in node @@ -427,9 +478,9 @@ check_key_signature( KBNODE root, KBNODE node, int *is_selfsig ) int check_key_signature2( KBNODE root, KBNODE node, PKT_public_key *check_pk, PKT_public_key *ret_pk, int *is_selfsig, - u32 *r_expiredate, int *r_expired ) + u32 *r_expiredate, int *r_expired ) { - MD_HANDLE md; + gcry_md_hd_t md; PKT_public_key *pk; PKT_signature *sig; int algo; @@ -448,7 +499,10 @@ check_key_signature2( KBNODE root, KBNODE node, PKT_public_key *check_pk, sig = node->pkt->pkt.signature; algo = sig->digest_algo; - /* check whether we have cached the result of a previous signature check.*/ + /* Check whether we have cached the result of a previous signature + check. Note that we may no longer have the pubkey or hash + needed to verify a sig, but can still use the cached value. A + cache refresh detects and clears these cases. */ if ( !opt.no_sig_cache ) { if (sig->flags.checked) { /*cached status available*/ if( is_selfsig ) { @@ -458,7 +512,7 @@ check_key_signature2( KBNODE root, KBNODE node, PKT_public_key *check_pk, if( keyid[0] == sig->keyid[0] && keyid[1] == sig->keyid[1] ) *is_selfsig = 1; } - /* BUG: This is wrong for non-self-sigs. Needs to be the + /* BUG: This is wrong for non-self-sigs.. needs to be the actual pk */ if((rc=do_check_messages(pk,sig,r_expired,NULL))) return rc; @@ -466,8 +520,10 @@ check_key_signature2( KBNODE root, KBNODE node, PKT_public_key *check_pk, } } - if( (rc=gcry_md_test_algo(algo)) ) - return rc; + if( (rc=openpgp_pk_test_algo(sig->pubkey_algo)) ) + return rc; + if( (rc=openpgp_md_test_algo(algo)) ) + return rc; if( sig->sig_class == 0x20 ) { /* key revocation */ u32 keyid[2]; @@ -478,7 +534,8 @@ check_key_signature2( KBNODE root, KBNODE node, PKT_public_key *check_pk, rc=check_revocation_keys(pk,sig); else { - gcry_md_open (&md, algo, 0 ); + if (gcry_md_open (&md, algo, 0 )) + BUG (); hash_public_key( md, pk ); rc = do_check( pk, sig, md, r_expired, NULL, ret_pk ); cache_sig_result ( sig, rc ); @@ -489,20 +546,21 @@ check_key_signature2( KBNODE root, KBNODE node, PKT_public_key *check_pk, KBNODE snode = find_prev_kbnode( root, node, PKT_PUBLIC_SUBKEY ); if( snode ) { - gcry_md_open (&md, algo, 0 ); + if (gcry_md_open (&md, algo, 0)) + BUG (); hash_public_key( md, pk ); hash_public_key( md, snode->pkt->pkt.public_key ); rc = do_check( pk, sig, md, r_expired, NULL, ret_pk ); cache_sig_result ( sig, rc ); gcry_md_close(md); } - else { + else + { if (opt.verbose) - log_info (_("key %08lX: no subkey for subkey " - "revocation signature\n"), - (ulong)keyid_from_pk (pk, NULL)); - rc = GPG_ERR_SIG_CLASS; - } + log_info (_("key %s: no subkey for subkey" + " revocation signature\n"),keystr_from_pk(pk)); + rc = G10ERR_SIG_CLASS; + } } else if( sig->sig_class == 0x18 ) { /* key binding */ KBNODE snode = find_prev_kbnode( root, node, PKT_PUBLIC_SUBKEY ); @@ -515,23 +573,25 @@ check_key_signature2( KBNODE root, KBNODE node, PKT_public_key *check_pk, if( keyid[0] == sig->keyid[0] && keyid[1] == sig->keyid[1] ) *is_selfsig = 1; } - gcry_md_open (&md, algo, 0 ); + if (gcry_md_open (&md, algo, 0)) + BUG (); hash_public_key( md, pk ); hash_public_key( md, snode->pkt->pkt.public_key ); rc = do_check( pk, sig, md, r_expired, NULL, ret_pk ); cache_sig_result ( sig, rc ); gcry_md_close(md); } - else { + else + { if (opt.verbose) - log_info(_("key %08lX: no subkey for subkey " - "binding signature\n"), - (ulong)keyid_from_pk (pk, NULL)); - rc = GPG_ERR_SIG_CLASS; - } + log_info(_("key %s: no subkey for subkey" + " binding signature\n"),keystr_from_pk(pk)); + rc = G10ERR_SIG_CLASS; + } } else if( sig->sig_class == 0x1f ) { /* direct key signature */ - gcry_md_open (&md, algo, 0 ); + if (gcry_md_open (&md, algo, 0 )) + BUG (); hash_public_key( md, pk ); rc = do_check( pk, sig, md, r_expired, NULL, ret_pk ); cache_sig_result ( sig, rc ); @@ -544,7 +604,8 @@ check_key_signature2( KBNODE root, KBNODE node, PKT_public_key *check_pk, u32 keyid[2]; keyid_from_pk( pk, keyid ); - gcry_md_open (&md, algo, 0 ); + if (gcry_md_open (&md, algo, 0 )) + BUG (); hash_public_key( md, pk ); hash_uid_node( unode, md, sig ); if( keyid[0] == sig->keyid[0] && keyid[1] == sig->keyid[1] ) @@ -554,21 +615,20 @@ check_key_signature2( KBNODE root, KBNODE node, PKT_public_key *check_pk, rc = do_check( pk, sig, md, r_expired, NULL, ret_pk ); } else if (check_pk) - rc=do_check(check_pk,sig,md,r_expired, NULL, ret_pk); + rc=do_check(check_pk,sig,md,r_expired,NULL,ret_pk); else - rc = signature_check2( sig, md, r_expiredate, r_expired, - NULL, ret_pk); + rc=signature_check2(sig,md,r_expiredate,r_expired,NULL,ret_pk); cache_sig_result ( sig, rc ); gcry_md_close(md); } - else { + else + { if (!opt.quiet) - log_info ("key %08lX: no user ID for key signature packet " - "of class %02x\n", - (ulong)keyid_from_pk (pk, NULL), sig->sig_class ); - rc = GPG_ERR_SIG_CLASS; - } + log_info ("key %s: no user ID for key signature packet" + " of class %02x\n",keystr_from_pk(pk),sig->sig_class); + rc = G10ERR_SIG_CLASS; + } } return rc; diff --git a/g10/sign.c b/g10/sign.c index cd7615c00..66f8847d7 100644 --- a/g10/sign.c +++ b/g10/sign.c @@ -1,6 +1,6 @@ /* sign.c - sign data - * Copyright (C) 1998, 1999, 2000, 2001, 2002, - * 2003 Free Software Foundation, Inc. + * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, + * 2006 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -16,7 +16,8 @@ * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. */ #include <config.h> @@ -27,12 +28,12 @@ #include <assert.h> #include <unistd.h> /* need sleep() */ +#include "gpg.h" #include "options.h" #include "packet.h" #include "errors.h" #include "iobuf.h" #include "keydb.h" -#include "memory.h" #include "util.h" #include "main.h" #include "filter.h" @@ -55,9 +56,9 @@ void __stdcall Sleep(ulong); static int recipient_digest_algo=0; /**************** - * Create a notation. We assume thIt is assumed that the strings in - * the STRLISTs of the opt struct are already checked to contain only - * printable data and have a valid NAME=VALUE format. + * Create notations and other stuff. It is assumed that the stings in + * STRLIST are already checked to contain only printable data and have + * a valid NAME=VALUE format. */ static void mk_notation_policy_etc( PKT_signature *sig, @@ -65,9 +66,8 @@ mk_notation_policy_etc( PKT_signature *sig, { const char *string; char *s=NULL; - byte *buf; - unsigned n1, n2; - STRLIST nd=NULL,pu=NULL; + STRLIST pu=NULL; + struct notation *nd=NULL; struct expando_args args; memset(&args,0,sizeof(args)); @@ -80,57 +80,43 @@ mk_notation_policy_etc( PKT_signature *sig, good to do these checks anyway. */ /* notation data */ - if(IS_SIG(sig) && opt.sig_notation_data) + if(IS_SIG(sig) && opt.sig_notations) { if(sig->version<4) log_error(_("can't put notation data into v3 (PGP 2.x style) " "signatures\n")); else - nd=opt.sig_notation_data; + nd=opt.sig_notations; } - else if( IS_CERT(sig) && opt.cert_notation_data ) + else if( IS_CERT(sig) && opt.cert_notations ) { if(sig->version<4) log_error(_("can't put notation data into v3 (PGP 2.x style) " "key signatures\n")); else - nd=opt.cert_notation_data; + nd=opt.cert_notations; } - for( ; nd; nd = nd->next ) { - char *expanded; - - string = nd->d; - s = strchr( string, '=' ); - if( !s ) - BUG(); /* we have already parsed this */ - n1 = s - string; - s++; + if(nd) + { + struct notation *i; - expanded=pct_expando(s,&args); - if(!expanded) + for(i=nd;i;i=i->next) { - log_error(_("WARNING: unable to %%-expand notation " - "(too large). Using unexpanded.\n")); - expanded=xstrdup (s); + i->altvalue=pct_expando(i->value,&args); + if(!i->altvalue) + log_error(_("WARNING: unable to %%-expand notation " + "(too large). Using unexpanded.\n")); } - n2 = strlen(expanded); - buf = xmalloc ( 8 + n1 + n2 ); - buf[0] = 0x80; /* human readable */ - buf[1] = buf[2] = buf[3] = 0; - buf[4] = n1 >> 8; - buf[5] = n1; - buf[6] = n2 >> 8; - buf[7] = n2; - memcpy(buf+8, string, n1 ); - memcpy(buf+8+n1, expanded, n2 ); - build_sig_subpkt( sig, SIGSUBPKT_NOTATION - | ((nd->flags & 1)? SIGSUBPKT_FLAG_CRITICAL:0), - buf, 8+n1+n2 ); - xfree (expanded); - xfree (buf); - } + keygen_add_notations(sig,nd); + + for(i=nd;i;i=i->next) + { + xfree(i->altvalue); + i->altvalue=NULL; + } + } /* set policy URL */ if( IS_SIG(sig) && opt.sig_policy_url ) @@ -157,24 +143,23 @@ mk_notation_policy_etc( PKT_signature *sig, s=pct_expando(string,&args); if(!s) { - log_error(_("WARNING: unable to %%-expand policy url " + log_error(_("WARNING: unable to %%-expand policy URL " "(too large). Using unexpanded.\n")); - s=xstrdup (string); + s=xstrdup(string); } build_sig_subpkt(sig,SIGSUBPKT_POLICY| ((pu->flags & 1)?SIGSUBPKT_FLAG_CRITICAL:0), s,strlen(s)); - xfree (s); + xfree(s); } /* preferred keyserver URL */ if( IS_SIG(sig) && opt.sig_keyserver_url ) { if(sig->version<4) - log_info (_("can't put a preferred keyserver URL " - "into v3 signatures\n")); + log_info("can't put a preferred keyserver URL into v3 signatures\n"); else pu=opt.sig_keyserver_url; } @@ -283,99 +268,112 @@ static int do_sign( PKT_secret_key *sk, PKT_signature *sig, MD_HANDLE md, int digest_algo ) { - gcry_mpi_t frame; - byte *dp; - int rc; - - if( sk->timestamp > sig->timestamp ) { - ulong d = sk->timestamp - sig->timestamp; - log_info( d==1 ? _("key has been created %lu second " - "in future (time warp or clock problem)\n") - : _("key has been created %lu seconds " - "in future (time warp or clock problem)\n"), d ); - if( !opt.ignore_time_conflict ) - return GPG_ERR_TIME_CONFLICT; - } - - print_pubkey_algo_note(sk->pubkey_algo); - - if( !digest_algo ) - digest_algo = gcry_md_get_algo(md); - - print_digest_algo_note( digest_algo ); - dp = gcry_md_read ( md, digest_algo ); - sig->digest_algo = digest_algo; - sig->digest_start[0] = dp[0]; - sig->digest_start[1] = dp[1]; - if (sk->is_protected && sk->protect.s2k.mode == 1002) - { /* FIXME: Note that we do only support RSA for now. */ - char *rbuf; - size_t rbuflen; - char *snbuf; - - snbuf = serialno_and_fpr_from_sk (sk->protect.iv, sk->protect.ivlen, sk); - rc = agent_scd_pksign (snbuf, digest_algo, - gcry_md_read (md, digest_algo), - gcry_md_get_algo_dlen (digest_algo), - &rbuf, &rbuflen); - xfree (snbuf); - if (!rc) - { - if (gcry_mpi_scan (&sig->data[0], GCRYMPI_FMT_USG, - rbuf, rbuflen, NULL)) - BUG (); - } + gcry_mpi_t frame; + byte *dp; + int rc; + + if( sk->timestamp > sig->timestamp ) { + ulong d = sk->timestamp - sig->timestamp; + log_info( d==1 ? _("key has been created %lu second " + "in future (time warp or clock problem)\n") + : _("key has been created %lu seconds " + "in future (time warp or clock problem)\n"), d ); + if( !opt.ignore_time_conflict ) + return G10ERR_TIME_CONFLICT; } - else - { - frame = encode_md_value( sk->pubkey_algo, md, - digest_algo, mpi_get_nbits(sk->skey[0]), 0 ); - if (!frame) - return GPG_ERR_GENERAL; - rc = pk_sign( sk->pubkey_algo, sig->data, frame, sk->skey ); - gcry_mpi_release (frame); + + + print_pubkey_algo_note(sk->pubkey_algo); + + if( !digest_algo ) + digest_algo = gcry_md_get_algo (md); + + print_digest_algo_note( digest_algo ); + dp = gcry_md_read ( md, digest_algo ); + sig->digest_algo = digest_algo; + sig->digest_start[0] = dp[0]; + sig->digest_start[1] = dp[1]; + if (sk->is_protected && sk->protect.s2k.mode == 1002) + { +#ifdef ENABLE_CARD_SUPPORT + unsigned char *rbuf; + size_t rbuflen; + char *snbuf; + + snbuf = serialno_and_fpr_from_sk (sk->protect.iv, + sk->protect.ivlen, sk); + rc = agent_scd_pksign (snbuf, digest_algo, + gcry_md_read (md, digest_algo), + gcry_md_get_algo_dlen (digest_algo), + &rbuf, &rbuflen); + xfree (snbuf); + if (!rc) + { + if (gcry_mpi_scan (&sig->data[0], GCRYMPI_FMT_USG, + rbuf, rbuflen, NULL)) + BUG (); + xfree (rbuf); + } +#else + return G10ERR_UNSUPPORTED; +#endif /* ENABLE_CARD_SUPPORT */ + } + else + { + /* TODO: remove this check in the future once all the + variable-q DSA stuff makes it into the standard. */ + if(!opt.expert + && sk->pubkey_algo==PUBKEY_ALGO_DSA + && md_digest_length(digest_algo)!=20) + { + log_error(_("DSA requires the use of a 160 bit hash algorithm\n")); + return G10ERR_GENERAL; + } + + frame = encode_md_value( NULL, sk, md, digest_algo ); + if (!frame) + return G10ERR_GENERAL; + rc = pk_sign( sk->pubkey_algo, sig->data, frame, sk->skey ); + gcry_mpi_release (frame); + } + + if (!rc && !opt.no_sig_create_check) { + /* Check that the signature verification worked and nothing is + * fooling us e.g. by a bug in the signature create + * code or by deliberately introduced faults. */ + PKT_public_key *pk = xmalloc_clear (sizeof *pk); + + if( get_pubkey( pk, sig->keyid ) ) + rc = G10ERR_NO_PUBKEY; + else { + frame = encode_md_value (pk, NULL, md, sig->digest_algo ); + if (!frame) + rc = G10ERR_GENERAL; + else + rc = pk_verify (pk->pubkey_algo, frame, sig->data, pk->pkey ); + gcry_mpi_release (frame); + } + if (rc) + log_error (_("checking created signature failed: %s\n"), + g10_errstr (rc)); + free_public_key (pk); } - if (!rc && !opt.no_sig_create_check) { - /* check that the signature verification worked and nothing is - * fooling us e.g. by a bug in the signature create - * code or by deliberately introduced faults. */ - PKT_public_key *pk = xcalloc (1,sizeof *pk); - - if( get_pubkey( pk, sig->keyid ) ) - rc = GPG_ERR_NO_PUBKEY; + if( rc ) + log_error(_("signing failed: %s\n"), g10_errstr(rc) ); else { - frame = encode_md_value (pk->pubkey_algo, md, - sig->digest_algo, - mpi_get_nbits(pk->pkey[0]), 0); - if (!frame) - rc = GPG_ERR_GENERAL; - else - rc = pk_verify (pk->pubkey_algo, frame, - sig->data, pk->pkey); - gcry_mpi_release (frame); - } - if (rc) - log_error (_("checking created signature failed: %s\n"), - gpg_strerror (rc)); - free_public_key (pk); - } - if( rc ) - log_error(_("signing failed: %s\n"), gpg_strerror (rc) ); - else { - if( opt.verbose ) { - char *ustr = get_user_id_string_printable (sig->keyid); - log_info(_("%s/%s signature from: \"%s\"\n"), - gcry_pk_algo_name (sk->pubkey_algo), - gcry_md_algo_name (sig->digest_algo), - ustr ); - xfree (ustr); + if( opt.verbose ) { + char *ustr = get_user_id_string_native (sig->keyid); + log_info(_("%s/%s signature from: \"%s\"\n"), + gcry_pk_algo_name (sk->pubkey_algo), + gcry_md_algo_name (sig->digest_algo), + ustr ); + xfree(ustr); + } } - } - return rc; + return rc; } - int complete_sig( PKT_signature *sig, PKT_secret_key *sk, MD_HANDLE md ) { @@ -386,34 +384,52 @@ complete_sig( PKT_signature *sig, PKT_secret_key *sk, MD_HANDLE md ) return rc; } +/* + First try --digest-algo. If that isn't set, see if the recipient + has a preferred algorithm (which is also filtered through + --preferred-digest-prefs). If we're making a signature without a + particular recipient (i.e. signing, rather than signing+encrypting) + then take the first algorithm in --preferred-digest-prefs that is + usable for the pubkey algorithm. If --preferred-digest-prefs isn't + set, then take the OpenPGP default (i.e. SHA-1). + + Possible improvement: Use the highest-ranked usable algorithm from + the signing key prefs either before or after using the personal + list? +*/ + static int -hash_for(int pubkey_algo, int packet_version ) +hash_for(PKT_secret_key *sk) { if( opt.def_digest_algo ) return opt.def_digest_algo; else if( recipient_digest_algo ) return recipient_digest_algo; - else if(PGP2 && pubkey_algo == PUBKEY_ALGO_RSA && packet_version < 4 ) + else if(sk->pubkey_algo==PUBKEY_ALGO_DSA + || (sk->is_protected && sk->protect.s2k.mode==1002)) { - /* Old-style PGP only understands MD5 */ - return DIGEST_ALGO_MD5; - } - else if( pubkey_algo == PUBKEY_ALGO_DSA ) - { - /* We need a 160-bit hash for DSA, so we can't just take the first - in the pref list */ + /* The sk lives on a smartcard, or it's a DSA key. DSA requires + a 160-bit hash, and current smartcards only handle SHA-1 and + RIPEMD/160 (i.e. 160-bit hashes). This is correct now, but + may need revision as the cards add algorithms and/or DSA is + expanded to use larger hashes. */ if(opt.personal_digest_prefs) { prefitem_t *prefs; for(prefs=opt.personal_digest_prefs;prefs->type;prefs++) - if(gcry_md_get_algo_dlen (prefs->value) == 20) + if (gcry_md_get_algo-dlen (prefs->value) == 20) return prefs->value; } return DIGEST_ALGO_SHA1; } + else if(PGP2 && sk->pubkey_algo == PUBKEY_ALGO_RSA && sk->version < 4 ) + { + /* Old-style PGP only understands MD5. */ + return DIGEST_ALGO_MD5; + } else if( opt.personal_digest_prefs ) { /* It's not DSA, so we can use whatever the first hash algorithm @@ -469,7 +485,7 @@ print_status_sig_created ( PKT_secret_key *sk, PKT_signature *sig, int what ) * packet here in reverse order */ static int -write_onepass_sig_packets (SK_LIST sk_list, iobuf_t out, int sigclass ) +write_onepass_sig_packets (SK_LIST sk_list, IOBUF out, int sigclass ) { int skcount; SK_LIST sk_rover; @@ -489,9 +505,9 @@ write_onepass_sig_packets (SK_LIST sk_list, iobuf_t out, int sigclass ) } sk = sk_rover->sk; - ops = xcalloc (1,sizeof *ops); + ops = xmalloc_clear (sizeof *ops); ops->sig_class = sigclass; - ops->digest_algo = hash_for (sk->pubkey_algo, sk->version); + ops->digest_algo = hash_for (sk); ops->pubkey_algo = sk->pubkey_algo; keyid_from_sk (sk, ops->keyid); ops->last = (skcount == 1); @@ -503,7 +519,7 @@ write_onepass_sig_packets (SK_LIST sk_list, iobuf_t out, int sigclass ) free_packet (&pkt); if (rc) { log_error ("build onepass_sig packet failed: %s\n", - gpg_strerror (rc)); + g10_errstr(rc)); return rc; } } @@ -515,7 +531,7 @@ write_onepass_sig_packets (SK_LIST sk_list, iobuf_t out, int sigclass ) * Helper to write the plaintext (literal data) packet */ static int -write_plaintext_packet (iobuf_t out, iobuf_t inp, const char *fname, int ptmode) +write_plaintext_packet (IOBUF out, IOBUF inp, const char *fname, int ptmode) { PKT_plaintext *pt = NULL; u32 filesize; @@ -524,8 +540,8 @@ write_plaintext_packet (iobuf_t out, iobuf_t inp, const char *fname, int ptmode) if (!opt.no_literal) { if (fname || opt.set_filename) { char *s = make_basename (opt.set_filename? opt.set_filename - : fname - /*, iobuf_get_real_fname(inp)*/); + : fname, + iobuf_get_real_fname(inp)); pt = xmalloc (sizeof *pt + strlen(s) - 1); pt->namelen = strlen (s); memcpy (pt->name, s, pt->namelen); @@ -538,26 +554,33 @@ write_plaintext_packet (iobuf_t out, iobuf_t inp, const char *fname, int ptmode) } /* try to calculate the length of the data */ - if (fname && *fname && !(*fname=='-' && !fname[1])) { - 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 + if ( !iobuf_is_pipe_filename (fname) && *fname ) + { + off_t tmpsize; + int overflow; + + if( !(tmpsize = iobuf_get_filelength(inp, &overflow)) + && !overflow ) + log_info (_("WARNING: `%s' is an empty file\n"), fname); + + /* We can't encode the length of very large files because + OpenPGP uses only 32 bit for file sizes. So if the size of + a file is larger than 2^32 minus some bytes for packet + headers, we switch to partial length encoding. */ + if ( tmpsize < (IOBUF_FILELENGTH_LIMIT - 65536) ) + filesize = tmpsize; + else + filesize = 0; + + /* Because the text_filter modifies the length of the * data, it is not possible to know the used length * without a double read of the file - to avoid that - * we simple use partial length packets. - */ + * we simple use partial length packets. */ if ( ptmode == 't' ) - filesize = 0; - } - else { - filesize = opt.set_filesize? opt.set_filesize : 0; /* stdin */ - } + filesize = 0; + } + else + filesize = opt.set_filesize? opt.set_filesize : 0; /* stdin */ if (!opt.no_literal) { PACKET pkt; @@ -573,7 +596,7 @@ write_plaintext_packet (iobuf_t out, iobuf_t inp, const char *fname, int ptmode) /*cfx.datalen = filesize? calc_packet_length( &pkt ) : 0;*/ if( (rc = build_packet (out, &pkt)) ) log_error ("build_packet(PLAINTEXT) failed: %s\n", - gpg_strerror (rc) ); + g10_errstr(rc) ); pt->buf = NULL; } else { @@ -581,9 +604,10 @@ write_plaintext_packet (iobuf_t out, iobuf_t inp, const char *fname, int ptmode) int bytes_copied; while ((bytes_copied = iobuf_read(inp, copy_buffer, 4096)) != -1) - if ( (rc=iobuf_write(out, copy_buffer, bytes_copied) )) { + if (iobuf_write(out, copy_buffer, bytes_copied) == -1) { + rc = G10ERR_WRITE_FILE; log_error ("copying input to output failed: %s\n", - gpg_strerror (rc)); + g10_errstr(rc)); break; } wipememory(copy_buffer,4096); /* burn buffer */ @@ -598,7 +622,7 @@ write_plaintext_packet (iobuf_t out, iobuf_t inp, const char *fname, int ptmode) * hash which will not be changes here. */ static int -write_signature_packets (SK_LIST sk_list, iobuf_t out, MD_HANDLE hash, +write_signature_packets (SK_LIST sk_list, IOBUF out, MD_HANDLE hash, int sigclass, u32 timestamp, u32 duration, int status_letter) { @@ -614,16 +638,16 @@ write_signature_packets (SK_LIST sk_list, iobuf_t out, MD_HANDLE hash, sk = sk_rover->sk; /* build the signature packet */ - sig = xcalloc (1,sizeof *sig); + sig = xmalloc_clear (sizeof *sig); if(opt.force_v3_sigs || RFC1991) sig->version=3; else if(duration || opt.sig_policy_url - || opt.sig_notation_data || opt.sig_keyserver_url) + || opt.sig_notations || opt.sig_keyserver_url) sig->version=4; else sig->version=sk->version; keyid_from_sk (sk, sig->keyid); - sig->digest_algo = hash_for (sk->pubkey_algo, sk->version); + sig->digest_algo = hash_for(sk); sig->pubkey_algo = sk->pubkey_algo; if(timestamp) sig->timestamp = timestamp; @@ -633,7 +657,7 @@ write_signature_packets (SK_LIST sk_list, iobuf_t out, MD_HANDLE hash, sig->expiredate = sig->timestamp+duration; sig->sig_class = sigclass; - gcry_md_copy (&md, hash); + md = gcry_md_copy (hash); if (sig->version >= 4) build_sig_subpkt_from_sig (sig); @@ -642,9 +666,8 @@ write_signature_packets (SK_LIST sk_list, iobuf_t out, MD_HANDLE hash, hash_sigversion_to_magic (md, sig); gcry_md_final (md); - rc = do_sign( sk, sig, md, hash_for (sig->pubkey_algo, sk->version) ); + rc = do_sign( sk, sig, md, hash_for (sk) ); gcry_md_close (md); - if( !rc ) { /* and write it */ PACKET pkt; @@ -658,7 +681,7 @@ write_signature_packets (SK_LIST sk_list, iobuf_t out, MD_HANDLE hash, free_packet (&pkt); if (rc) log_error ("build signature packet failed: %s\n", - gpg_strerror (rc) ); + g10_errstr(rc) ); } if( rc ) return rc;; @@ -690,7 +713,7 @@ sign_file( STRLIST filenames, int detached, STRLIST locusr, text_filter_context_t tfx; progress_filter_context_t pfx; encrypt_filter_context_t efx; - iobuf_t inp = NULL, out = NULL; + IOBUF inp = NULL, out = NULL; PACKET pkt; int rc = 0; PK_LIST pk_list = NULL; @@ -715,8 +738,17 @@ sign_file( STRLIST filenames, int detached, STRLIST locusr, if( fname && filenames->next && (!detached || encryptflag) ) log_bug("multiple files can only be detached signed"); - if(opt.ask_sig_expire && !opt.force_v3_sigs && !opt.batch && !RFC1991) - duration=ask_expire_interval(1); + if(encryptflag==2 + && (rc=setup_symkey(&efx.symkey_s2k,&efx.symkey_dek))) + goto leave; + + if(!opt.force_v3_sigs && !RFC1991) + { + if(opt.ask_sig_expire && !opt.batch) + duration=ask_expire_interval(1,opt.def_sig_expire); + else + duration=parse_expire_string(opt.def_sig_expire); + } if( (rc=build_sk_list( locusr, &sk_list, 1, PUBKEY_USAGE_SIG )) ) goto leave; @@ -735,10 +767,17 @@ sign_file( STRLIST filenames, int detached, STRLIST locusr, if( multifile ) /* have list of filenames */ inp = NULL; /* we do it later */ else { - if( !(inp = iobuf_open(fname)) ) { - rc = gpg_error_from_errno (errno); - log_error("can't open %s: %s\n", fname? fname: "[stdin]", + inp = iobuf_open(fname); + if (inp && is_secured_file (iobuf_get_fd (inp))) + { + iobuf_close (inp); + inp = NULL; + errno = EPERM; + } + if( !inp ) { + log_error(_("can't open `%s': %s\n"), fname? fname: "[stdin]", strerror(errno) ); + rc = G10ERR_OPEN_FILE; goto leave; } @@ -746,11 +785,18 @@ sign_file( STRLIST filenames, int detached, STRLIST locusr, } if( outfile ) { - if( !(out = iobuf_create( outfile )) ) { - rc = gpg_error_from_errno (errno); - log_error(_("can't create %s: %s\n"), outfile, strerror(errno) ); + if (is_secured_filename ( outfile )) { + out = NULL; + errno = EPERM; + } + else + out = iobuf_create( outfile ); + if( !out ) + { + log_error(_("can't create `%s': %s\n"), outfile, strerror(errno) ); + rc = G10ERR_CREATE_FILE; goto leave; - } + } else if( opt.verbose ) log_info(_("writing to `%s'\n"), outfile ); } @@ -764,7 +810,10 @@ sign_file( STRLIST filenames, int detached, STRLIST locusr, iobuf_push_filter( inp, text_filter, &tfx ); } - gcry_md_open (&mfx.md, 0, 0); + if ( gcry_md_open (&,mfx.md, 0, 0) ) + BUG (); + if (DBG_HASHING) + gcry_md_start_debug (mfx.md, "sign"); /* If we're encrypting and signing, it is reasonable to pick the hash algorithm to use out of the recepient key prefs. */ @@ -776,10 +825,10 @@ sign_file( STRLIST filenames, int detached, STRLIST locusr, select_algo_from_prefs(pk_list,PREFTYPE_HASH, opt.def_digest_algo, NULL)!=opt.def_digest_algo) - log_info(_("forcing digest algorithm %s (%d) " - "violates recipient preferences\n"), + log_info(_("WARNING: forcing digest algorithm %s (%d)" + " violates recipient preferences\n"), gcry_md_algo_name (opt.def_digest_algo), - opt.def_digest_algo); + opt.def_digest_algo ); } else { @@ -793,8 +842,14 @@ sign_file( STRLIST filenames, int detached, STRLIST locusr, sk, but so long as there is only one signing algorithm with hash restrictions, this is ok. -dms */ + /* Current smartcards only do 160-bit hashes as well. + Note that this may well have to change as the cards add + algorithms. */ + for( sk_rover = sk_list; sk_rover; sk_rover = sk_rover->next ) - if(sk_rover->sk->pubkey_algo==PUBKEY_ALGO_DSA) + if(sk_rover->sk->pubkey_algo==PUBKEY_ALGO_DSA + || (sk_rover->sk->is_protected + && sk_rover->sk->protect.s2k.mode==1002)) hashlen=20; if((algo= @@ -806,7 +861,7 @@ sign_file( STRLIST filenames, int detached, STRLIST locusr, for( sk_rover = sk_list; sk_rover; sk_rover = sk_rover->next ) { PKT_secret_key *sk = sk_rover->sk; - gcry_md_enable (mfx.md, hash_for(sk->pubkey_algo, sk->version )); + gcry_md_enable (mfx.md, hash_for(sk)); } if( !multifile ) @@ -824,9 +879,9 @@ sign_file( STRLIST filenames, int detached, STRLIST locusr, iobuf_push_filter( out, encrypt_filter, &efx ); } - if( opt.compress && !outfile && ( !detached || opt.compress_sigs) ) + if( opt.compress_algo && !outfile && ( !detached || opt.compress_sigs) ) { - int compr_algo=opt.def_compress_algo; + int compr_algo=opt.compress_algo; /* If not forced by user */ if(compr_algo==-1) @@ -845,16 +900,13 @@ sign_file( STRLIST filenames, int detached, STRLIST locusr, else if(!opt.expert && pk_list && select_algo_from_prefs(pk_list,PREFTYPE_ZIP, compr_algo,NULL)!=compr_algo) - log_info(_("forcing compression algorithm %s (%d) " - "violates recipient preferences\n"), + log_info(_("WARNING: forcing compression algorithm %s (%d)" + " violates recipient preferences\n"), compress_algo_to_string(compr_algo),compr_algo); /* algo 0 means no compression */ if( compr_algo ) - { - zfx.algo = compr_algo; - iobuf_push_filter( out, compress_filter, &zfx ); - } + push_compress_filter(out,&zfx,compr_algo); } /* Write the one-pass signature packets if needed */ @@ -865,7 +917,9 @@ sign_file( STRLIST filenames, int detached, STRLIST locusr, goto leave; } - /* setup the inner packet */ + write_status (STATUS_BEGIN_SIGNING); + + /* Setup the inner packet. */ if( detached ) { if( multifile ) { STRLIST sl; @@ -875,12 +929,20 @@ sign_file( STRLIST filenames, int detached, STRLIST locusr, /* must walk reverse trough this list */ for( sl = strlist_last(filenames); sl; sl = strlist_prev( filenames, sl ) ) { - if( !(inp = iobuf_open(sl->d)) ) { - rc = gpg_error_from_errno (errno); - log_error(_("can't open %s: %s\n"), - sl->d, strerror(errno) ); + inp = iobuf_open(sl->d); + if (inp && is_secured_file (iobuf_get_fd (inp))) + { + iobuf_close (inp); + inp = NULL; + errno = EPERM; + } + if( !inp ) + { + log_error(_("can't open `%s': %s\n"), + sl->d,strerror(errno)); + rc = G10ERR_OPEN_FILE; goto leave; - } + } handle_progress (&pfx, inp, sl->d); if( opt.verbose ) fprintf(stderr, " `%s'", sl->d ); @@ -947,7 +1009,7 @@ clearsign_file( const char *fname, STRLIST locusr, const char *outfile ) armor_filter_context_t afx; progress_filter_context_t pfx; MD_HANDLE textmd = NULL; - iobuf_t inp = NULL, out = NULL; + IOBUF inp = NULL, out = NULL; PACKET pkt; int rc = 0; SK_LIST sk_list = NULL; @@ -959,8 +1021,13 @@ clearsign_file( const char *fname, STRLIST locusr, const char *outfile ) memset( &afx, 0, sizeof afx); init_packet( &pkt ); - if(opt.ask_sig_expire && !opt.force_v3_sigs && !opt.batch && !RFC1991) - duration=ask_expire_interval(1); + if(!opt.force_v3_sigs && !RFC1991) + { + if(opt.ask_sig_expire && !opt.batch) + duration=ask_expire_interval(1,opt.def_sig_expire); + else + duration=parse_expire_string(opt.def_sig_expire); + } if( (rc=build_sk_list( locusr, &sk_list, 1, PUBKEY_USAGE_SIG )) ) goto leave; @@ -976,20 +1043,34 @@ clearsign_file( const char *fname, STRLIST locusr, const char *outfile ) } /* prepare iobufs */ - if( !(inp = iobuf_open(fname)) ) { - rc = gpg_error_from_errno (errno); - log_error("can't open %s: %s\n", fname? fname: "[stdin]", + inp = iobuf_open(fname); + if (inp && is_secured_file (iobuf_get_fd (inp))) + { + iobuf_close (inp); + inp = NULL; + errno = EPERM; + } + if( !inp ) { + log_error(_("can't open `%s': %s\n"), fname? fname: "[stdin]", strerror(errno) ); + rc = G10ERR_OPEN_FILE; goto leave; } handle_progress (&pfx, inp, fname); if( outfile ) { - if( !(out = iobuf_create( outfile )) ) { - rc = gpg_error_from_errno (errno); - log_error(_("can't create %s: %s\n"), outfile, strerror(errno) ); + if (is_secured_filename (outfile) ) { + outfile = NULL; + errno = EPERM; + } + else + out = iobuf_create( outfile ); + if( !out ) + { + log_error(_("can't create `%s': %s\n"), outfile, strerror(errno) ); + rc = G10ERR_CREATE_FILE; goto leave; - } + } else if( opt.verbose ) log_info(_("writing to `%s'\n"), outfile ); } @@ -1000,7 +1081,7 @@ clearsign_file( const char *fname, STRLIST locusr, const char *outfile ) for( sk_rover = sk_list; sk_rover; sk_rover = sk_rover->next ) { PKT_secret_key *sk = sk_rover->sk; - if( hash_for(sk->pubkey_algo, sk->version) == DIGEST_ALGO_MD5 ) + if( hash_for(sk) == DIGEST_ALGO_MD5 ) only_md5 = 1; else { only_md5 = 0; @@ -1017,10 +1098,10 @@ clearsign_file( const char *fname, STRLIST locusr, const char *outfile ) iobuf_writestr(out, "Hash: " ); for( sk_rover = sk_list; sk_rover; sk_rover = sk_rover->next ) { PKT_secret_key *sk = sk_rover->sk; - int i = hash_for(sk->pubkey_algo, sk->version); + int i = hash_for(sk); if( !hashs_seen[ i & 0xff ] ) { - s = gcry_md_algo_name (i); + s = gcry_md_ago_name ( i ); if( s ) { hashs_seen[ i & 0xff ] = 1; if( any ) @@ -1039,13 +1120,15 @@ clearsign_file( const char *fname, STRLIST locusr, const char *outfile ) "NotDashEscaped: You need GnuPG to verify this message" LF ); iobuf_writestr(out, LF ); - gcry_md_open (&textmd, 0, 0); + if ( gcry_md_open (&textmd, 0, 0) ) + BUG (); for( sk_rover = sk_list; sk_rover; sk_rover = sk_rover->next ) { PKT_secret_key *sk = sk_rover->sk; - gcry_md_enable (textmd, hash_for(sk->pubkey_algo, sk->version)); + gcry_md_enable (textmd, hash_for(sk)); } if ( DBG_HASHING ) - gcry_md_start_debug ( textmd, "clearsign" ); + gcry_md_start_debug ( textmd, "clearsign" ); + copy_clearsig_text( out, inp, textmd, !opt.not_dash_escaped, opt.escape_from, (old_style && only_md5) ); /* fixme: check for read errors */ @@ -1083,7 +1166,7 @@ sign_symencrypt_file (const char *fname, STRLIST locusr) md_filter_context_t mfx; text_filter_context_t tfx; cipher_filter_context_t cfx; - iobuf_t inp = NULL, out = NULL; + IOBUF inp = NULL, out = NULL; PACKET pkt; STRING2KEY *s2k = NULL; int rc = 0; @@ -1099,8 +1182,13 @@ sign_symencrypt_file (const char *fname, STRLIST locusr) memset( &cfx, 0, sizeof cfx); init_packet( &pkt ); - if(opt.ask_sig_expire && !opt.force_v3_sigs && !opt.batch && !RFC1991) - duration=ask_expire_interval(1); + if(!opt.force_v3_sigs && !RFC1991) + { + if(opt.ask_sig_expire && !opt.batch) + duration=ask_expire_interval(1,opt.def_sig_expire); + else + duration=parse_expire_string(opt.def_sig_expire); + } rc = build_sk_list (locusr, &sk_list, 1, PUBKEY_USAGE_SIG); if (rc) @@ -1108,31 +1196,44 @@ sign_symencrypt_file (const char *fname, STRLIST locusr) /* prepare iobufs */ inp = iobuf_open(fname); + if (inp && is_secured_file (iobuf_get_fd (inp))) + { + iobuf_close (inp); + inp = NULL; + errno = EPERM; + } if( !inp ) { - rc = gpg_error_from_errno (errno); - log_error("can't open %s: %s\n", fname? fname: "[stdin]", - strerror(errno) ); + log_error(_("can't open `%s': %s\n"), + fname? fname: "[stdin]", strerror(errno) ); + rc = G10ERR_OPEN_FILE; goto leave; } handle_progress (&pfx, inp, fname); /* prepare key */ - s2k = xcalloc (1, sizeof *s2k ); + s2k = xmalloc_clear( sizeof *s2k ); s2k->mode = RFC1991? 0:opt.s2k_mode; - s2k->hash_algo = opt.s2k_digest_algo; + s2k->hash_algo = S2K_DIGEST_ALGO; algo = default_cipher_algo(); if (!opt.quiet || !opt.batch) log_info (_("%s encryption will be used\n"), - gcry_cipher_algo_name (algo) ); + gcry_cipher_algo_name (algo) ); cfx.dek = passphrase_to_dek( NULL, 0, algo, s2k, 2, NULL, NULL); if (!cfx.dek || !cfx.dek->keylen) { - rc = gpg_error (GPG_ERR_INV_PASSPHRASE); - log_error(_("error creating passphrase: %s\n"), gpg_strerror (rc) ); + rc = G10ERR_PASSPHRASE; + log_error(_("error creating passphrase: %s\n"), g10_errstr(rc) ); goto leave; } + /* We have no way to tell if the recipient can handle messages + with an MDC, so this defaults to no. Perhaps in a few years, + this can be defaulted to yes. Note that like regular + encrypting, --force-mdc overrides --disable-mdc. */ + if(opt.force_mdc) + cfx.dek->use_mdc=1; + /* now create the outfile */ rc = open_outfile (fname, opt.armor? 1:0, &out); if (rc) @@ -1141,11 +1242,14 @@ sign_symencrypt_file (const char *fname, STRLIST locusr) /* prepare to calculate the MD over the input */ if (opt.textmode) iobuf_push_filter (inp, text_filter, &tfx); - gcry_md_open (&mfx.md, 0, 0); + if ( gcry_md_open (&mfx.md, 0, 0) ) + BUG (); + if ( DBG_HASHING ) + gcry_md_start_debug (mfx.md, "symc-sign"); for (sk_rover = sk_list; sk_rover; sk_rover = sk_rover->next) { PKT_secret_key *sk = sk_rover->sk; - gcry_md_enable (mfx.md, hash_for (sk->pubkey_algo, sk->version )); + gcry_md_enable (mfx.md, hash_for (sk)); } iobuf_push_filter (inp, md_filter, &mfx); @@ -1157,26 +1261,23 @@ sign_symencrypt_file (const char *fname, STRLIST locusr) /* Write the symmetric key packet */ /*(current filters: armor)*/ if (!RFC1991) { - PKT_symkey_enc *enc = xcalloc (1, sizeof *enc ); + PKT_symkey_enc *enc = xmalloc_clear( sizeof *enc ); enc->version = 4; enc->cipher_algo = cfx.dek->algo; enc->s2k = *s2k; pkt.pkttype = PKT_SYMKEY_ENC; pkt.pkt.symkey_enc = enc; if( (rc = build_packet( out, &pkt )) ) - log_error("build symkey packet failed: %s\n", gpg_strerror (rc) ); - xfree (enc); + log_error("build symkey packet failed: %s\n", g10_errstr(rc) ); + xfree(enc); } /* Push the encryption filter */ iobuf_push_filter( out, cipher_filter, &cfx ); - /* Push the Zip filter */ - if (opt.compress && default_compress_algo()) - { - zfx.algo = default_compress_algo(); - iobuf_push_filter( out, compress_filter, &zfx ); - } + /* Push the compress filter */ + if (default_compress_algo()) + push_compress_filter(out,&zfx,default_compress_algo()); /* Write the one-pass signature packets */ /*(current filters: zip - encrypt - armor)*/ @@ -1187,6 +1288,8 @@ sign_symencrypt_file (const char *fname, STRLIST locusr) goto leave; } + write_status (STATUS_BEGIN_SIGNING); + /* Pipe data through all filters; i.e. write the signed stuff */ /*(current filters: zip - encrypt - armor)*/ rc = write_plaintext_packet (out, inp, fname, opt.textmode ? 't':'b'); @@ -1211,9 +1314,9 @@ sign_symencrypt_file (const char *fname, STRLIST locusr) } iobuf_close(inp); release_sk_list( sk_list ); - gcry_md_close ( mfx.md ); - xfree (cfx.dek); - xfree (s2k); + gcry_md_close( mfx.md ); + xfree(cfx.dek); + xfree(s2k); return rc; } @@ -1226,7 +1329,7 @@ sign_symencrypt_file (const char *fname, STRLIST locusr) * SIGVERSION gives the minimal required signature packet version; * this is needed so that special properties like local sign are not * applied (actually: dropped) when a v3 key is used. TIMESTAMP is - * the timestamp to use for the signature. 0 means "now". */ + * the timestamp to use for the signature. 0 means "now" */ int make_keysig_packet( PKT_signature **ret_sig, PKT_public_key *pk, PKT_user_id *uid, PKT_public_key *subpk, @@ -1241,7 +1344,7 @@ make_keysig_packet( PKT_signature **ret_sig, PKT_public_key *pk, MD_HANDLE md; assert( (sigclass >= 0x10 && sigclass <= 0x13) || sigclass == 0x1F - || sigclass == 0x20 || sigclass == 0x18 + || sigclass == 0x20 || sigclass == 0x18 || sigclass == 0x19 || sigclass == 0x30 || sigclass == 0x28 ); if (opt.force_v4_certs) @@ -1270,26 +1373,31 @@ make_keysig_packet( PKT_signature **ret_sig, PKT_public_key *pk, if(opt.cert_digest_algo) digest_algo=opt.cert_digest_algo; - else if((sk->pubkey_algo==PUBKEY_ALGO_RSA || - sk->pubkey_algo==PUBKEY_ALGO_RSA_S) && - pk->version<4 && sigversion < 4) + else if(sk->pubkey_algo==PUBKEY_ALGO_RSA + && pk->version<4 && sigversion<4) digest_algo = DIGEST_ALGO_MD5; else digest_algo = DIGEST_ALGO_SHA1; } - gcry_md_open (&md, digest_algo, 0 ); + if ( gcry_md_open (&md, digest_algo, 0 ) ) + BUG (); - /* hash the public key certificate and the user id */ + /* Hash the public key certificate. */ hash_public_key( md, pk ); - if( sigclass == 0x18 || sigclass == 0x28 ) { /* subkey binding/revocation*/ + + if( sigclass == 0x18 || sigclass == 0x19 || sigclass == 0x28 ) + { + /* hash the subkey binding/backsig/revocation */ hash_public_key( md, subpk ); - } - else if( sigclass != 0x1F && sigclass != 0x20 ) { + } + else if( sigclass != 0x1F && sigclass != 0x20 ) + { + /* hash the user id */ hash_uid (md, sigversion, uid); - } + } /* and make the signature packet */ - sig = xcalloc (1, sizeof *sig ); + sig = xmalloc_clear( sizeof *sig ); sig->version = sigversion; sig->flags.exportable=1; sig->flags.revocable=1; @@ -1305,7 +1413,7 @@ make_keysig_packet( PKT_signature **ret_sig, PKT_public_key *pk, sig->sig_class = sigclass; if( sig->version >= 4 ) build_sig_subpkt_from_sig( sig ); - mk_notation_policy_etc ( sig, pk, sk ); + mk_notation_policy_etc( sig, pk, sk ); /* Crucial that the call to mksubpkt comes LAST before the calls to finalize the sig as that makes it possible for the mksubpkt @@ -1343,8 +1451,7 @@ update_keysig_packet( PKT_signature **ret_sig, PKT_public_key *subpk, PKT_secret_key *sk, int (*mksubpkt)(PKT_signature *, void *), - void *opaque - ) + void *opaque ) { PKT_signature *sig; int rc=0; @@ -1353,11 +1460,12 @@ update_keysig_packet( PKT_signature **ret_sig, if ((!orig_sig || !pk || !sk) || (orig_sig->sig_class >= 0x10 && orig_sig->sig_class <= 0x13 && !uid) || (orig_sig->sig_class == 0x18 && !subpk)) - return GPG_ERR_GENERAL; + return G10ERR_GENERAL; - gcry_md_open (&md, orig_sig->digest_algo, 0); + if ( gcry_md_open (&md, orig_sig->digest_algo, 0 ) ) + BUG (); - /* hash the public key certificate and the user id */ + /* Hash the public key certificate and the user id. */ hash_public_key( md, pk ); if( orig_sig->sig_class == 0x18 ) @@ -1367,7 +1475,7 @@ update_keysig_packet( PKT_signature **ret_sig, /* create a new signature packet */ sig = copy_signature (NULL, orig_sig); - + /* We need to create a new timestamp so that new sig expiration calculations are done correctly... */ sig->timestamp=make_timestamp(); @@ -1398,7 +1506,7 @@ update_keysig_packet( PKT_signature **ret_sig, if (!rc) { hash_sigversion_to_magic (md, sig); - gcry_md_final (md); + md_final(md); rc = complete_sig( sig, sk, md ); } diff --git a/g10/signal.c b/g10/signal.c index 9f50fbe3a..aaeb89841 100644 --- a/g10/signal.c +++ b/g10/signal.c @@ -1,5 +1,6 @@ /* signal.c - signal handling - * Copyright (C) 1998, 1999, 2000, 2001, 2003 Free Software Foundation, Inc. + * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, + * 2005 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -15,7 +16,8 @@ * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. */ #include <config.h> @@ -26,22 +28,28 @@ #include <string.h> #include <errno.h> #include <assert.h> +#ifdef HAVE_LIBREADLINE +#include <readline/readline.h> +#include <readline/history.h> +#endif +#include "gpg.h" #include "options.h" #include "errors.h" -#include "memory.h" #include "util.h" #include "main.h" #include "ttyio.h" - +#ifdef HAVE_DOSISH_SYSTEM +void init_signals(void) {} +void pause_on_sigusr(int which) {} +#else static volatile int caught_fatal_sig = 0; static volatile int caught_sigusr1 = 0; static void init_one_signal (int sig, RETSIGTYPE (*handler)(int), int check_ign ) { -#ifndef HAVE_DOSISH_SYSTEM #if defined(HAVE_SIGACTION) && defined(HAVE_STRUCT_SIGACTION) struct sigaction oact, nact; @@ -65,20 +73,8 @@ init_one_signal (int sig, RETSIGTYPE (*handler)(int), int check_ign ) signal (sig, SIG_IGN); } #endif -#endif /*!HAVE_DOSISH_SYSTEM*/ -} - -static const char * -get_signal_name( int signum ) -{ -#if defined(SYS_SIGLIST_DECLARED) && defined(NSIG) - return (signum >= 0 && signum < NSIG) ? sys_siglist[signum] : "?"; -#else - return "some signal"; -#endif } - static RETSIGTYPE got_fatal_signal( int sig ) { @@ -89,14 +85,33 @@ got_fatal_signal( int sig ) caught_fatal_sig = 1; gcry_control (GCRYCTL_TERM_SECMEM ); - /* better don't transtale these messages */ + +#ifdef HAVE_LIBREADLINE + rl_free_line_state (); + rl_cleanup_after_signal (); +#endif + + /* Better don't translate these messages. */ write(2, "\n", 1 ); - s = "?" /* FIXME: log_get_name()*/; if( s ) write(2, s, strlen(s) ); + s = log_get_name(); if( s ) write(2, s, strlen(s) ); write(2, ": ", 2 ); - s = get_signal_name(sig); write(2, s, strlen(s) ); + +#if HAVE_DECL_SYS_SIGLIST && defined(NSIG) + s = (sig >= 0 && sig < NSIG) ? sys_siglist[sig] : "?"; + write (2, s, strlen(s) ); +#else + write (2, "signal ", 7 ); + if (sig < 0 || sig >=100) + write (2, "?", 1); + else { + if (sig >= 10) + write (2, "0123456789"+(sig/10), 1 ); + write (2, "0123456789"+(sig%10), 1 ); + } +#endif write(2, " caught ... exiting\n", 20 ); - /* reset action to default action and raise signal again */ + /* Reset action to default action and raise signal again. */ init_one_signal (sig, SIG_DFL, 0); dotlock_remove_lockfiles (); #ifdef __riscos__ @@ -116,7 +131,6 @@ got_usr_signal( int sig ) void init_signals() { -#ifndef HAVE_DOSISH_SYSTEM init_one_signal (SIGINT, got_fatal_signal, 1 ); init_one_signal (SIGHUP, got_fatal_signal, 1 ); init_one_signal (SIGTERM, got_fatal_signal, 1 ); @@ -124,14 +138,12 @@ init_signals() init_one_signal (SIGSEGV, got_fatal_signal, 1 ); init_one_signal (SIGUSR1, got_usr_signal, 0 ); init_one_signal (SIGPIPE, SIG_IGN, 0 ); -#endif } void pause_on_sigusr( int which ) { -#ifndef HAVE_DOSISH_SYSTEM #if defined(HAVE_SIGPROCMASK) && defined(HAVE_SIGSET_T) sigset_t mask, oldmask; @@ -150,16 +162,15 @@ pause_on_sigusr( int which ) while (!caught_sigusr1) sigpause(SIGUSR1); caught_sigusr1 = 0; - sigrelse(SIGUSR1); -#endif /*!HAVE_SIGPROCMASK && HAVE_SISET_T*/ -#endif + sigrelse(SIGUSR1); +#endif /*! HAVE_SIGPROCMASK && HAVE_SIGSET_T */ } - +/* Disabled - see comment in tdbio.c:tdbio_begin_transaction() */ +#if 0 static void do_block( int block ) { -#ifndef HAVE_DOSISH_SYSTEM static int is_blocked; #if defined(HAVE_SIGPROCMASK) && defined(HAVE_SIGSET_T) static sigset_t oldmask; @@ -179,14 +190,14 @@ do_block( int block ) sigprocmask( SIG_SETMASK, &oldmask, NULL ); is_blocked = 0; } -#else /*!HAVE_SIGPROCMASK*/ +#else /*! HAVE_SIGPROCMASK && HAVE_SIGSET_T */ #if defined(NSIG) -# define SIGSMAX (NSIG) +#define SIGSMAX (NSIG) #elif defined(MAXSIG) -# define SIGSMAX (MAXSIG+1) +#define SIGSMAX (MAXSIG+1) #else -# error "define SIGSMAX to the number of signals on your platform plus one" +#error "define SIGSMAX to the number of signals on your platform plus one" #endif static void (*disposition[SIGSMAX])(int); @@ -208,11 +219,9 @@ do_block( int block ) } is_blocked = 0; } -#endif /*!HAVE_SIGPROCMASK*/ -#endif /*HAVE_DOSISH_SYSTEM*/ +#endif /*! HAVE_SIGPROCMASK && HAVE_SIGSET_T */ } - void block_all_signals() { @@ -224,3 +233,6 @@ unblock_all_signals() { do_block(0); } +#endif + +#endif /* !HAVE_DOSISH_SYSTEM */ diff --git a/g10/skclist.c b/g10/skclist.c index 67d9eb2f9..1cb69074a 100644 --- a/g10/skclist.c +++ b/g10/skclist.c @@ -1,5 +1,5 @@ -/* skclist.c - * Copyright (C) 1998, 1999, 2000, 2001 Free Software Foundation, Inc. +/* skclist.c - Build a list of secret keys + * Copyright (C) 1998, 1999, 2000, 2001, 2006 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -15,7 +15,8 @@ * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. */ #include <config.h> @@ -25,11 +26,11 @@ #include <errno.h> #include <assert.h> +#include "gpg.h" #include "options.h" #include "packet.h" #include "errors.h" #include "keydb.h" -#include "memory.h" #include "util.h" #include "i18n.h" #include "cipher.h" @@ -43,11 +44,41 @@ release_sk_list( SK_LIST sk_list ) for( ; sk_list; sk_list = sk_rover ) { sk_rover = sk_list->next; free_secret_key( sk_list->sk ); - xfree ( sk_list ); + xfree( sk_list ); } } +/* Check that we are only using keys which don't have + * the string "(insecure!)" or "not secure" or "do not use" + * in one of the user ids + */ +static int +is_insecure( PKT_secret_key *sk ) +{ + u32 keyid[2]; + KBNODE node = NULL, u; + int insecure = 0; + + keyid_from_sk( sk, keyid ); + node = get_pubkeyblock( keyid ); + for ( u = node; u; u = u->next ) { + if ( u->pkt->pkttype == PKT_USER_ID ) { + PKT_user_id *id = u->pkt->pkt.user_id; + if ( id->attrib_data ) + continue; /* skip attribute packets */ + if ( strstr( id->name, "(insecure!)" ) + || strstr( id->name, "not secure" ) + || strstr( id->name, "do not use" ) ) { + insecure = 1; + break; + } + } + } + release_kbnode( node ); + + return insecure; +} static int key_present_in_sk_list(SK_LIST sk_list, PKT_secret_key *sk) @@ -77,37 +108,41 @@ build_sk_list( STRLIST locusr, SK_LIST *ret_sk_list, SK_LIST sk_list = NULL; int rc; - if( !locusr ) { /* use the default one */ + if( !locusr ) + { /* use the default one */ PKT_secret_key *sk; - sk = xcalloc (1, sizeof *sk ); + sk = xmalloc_clear( sizeof *sk ); 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_strerror (rc) ); + free_secret_key( sk ); sk = NULL; + log_error("no default secret key: %s\n", g10_errstr(rc) ); } - else if( !(rc=openpgp_pk_test_algo (sk->pubkey_algo, use)) ) { + else if( !(rc=openpgp_pk_test_algo2 (sk->pubkey_algo, use)) ) + { SK_LIST r; - if( sk->version == 4 && (use & PUBKEY_USAGE_SIG) - && sk->pubkey_algo == PUBKEY_ALGO_ELGAMAL_E ) { - log_info("this is a PGP generated " - "ElGamal key which is NOT secure for signatures!\n"); + if( random_is_faked() && !is_insecure( sk ) ) + { + log_info(_("key is not flagged as insecure - " + "can't use it with the faked RNG!\n")); free_secret_key( sk ); sk = NULL; - } - else { - r = xmalloc ( sizeof *r ); + } + else + { + r = xmalloc( sizeof *r ); r->sk = sk; sk = NULL; r->next = sk_list; r->mark = 0; sk_list = r; - } - } - else { + } + } + else + { free_secret_key( sk ); sk = NULL; - log_error("invalid default secret key: %s\n", gpg_strerror (rc) ); - } - } + log_error("invalid default secret key: %s\n", g10_errstr(rc) ); + } + } else { STRLIST locusr_orig = locusr; for(; locusr; locusr = locusr->next ) { @@ -118,36 +153,47 @@ build_sk_list( STRLIST locusr, SK_LIST *ret_sk_list, * won't catch all duplicates because the user IDs may be * specified in different ways. */ - if ( is_duplicated_entry ( locusr_orig, locusr ) ) { - log_error(_("skipped `%s': duplicated\n"), locusr->d ); + if ( is_duplicated_entry ( locusr_orig, locusr ) ) + { + log_error(_("skipped \"%s\": duplicated\n"), locusr->d ); continue; - } - sk = xcalloc (1, sizeof *sk ); + } + sk = xmalloc_clear( sizeof *sk ); sk->req_usage = use; - if( (rc = get_seckey_byname( sk, locusr->d, 0 )) ) { + if( (rc = get_seckey_byname( sk, locusr->d, 0 )) ) + { free_secret_key( sk ); sk = NULL; - log_error(_("skipped `%s': %s\n"), locusr->d, gpg_strerror (rc) ); - } + log_error(_("skipped \"%s\": %s\n"), + locusr->d, g10_errstr(rc) ); + } else if ( key_present_in_sk_list(sk_list, sk) == 0) { free_secret_key(sk); sk = NULL; log_info(_("skipped: secret key already present\n")); } - else if ( unlock && (rc = check_secret_key( sk, 0 )) ) { + else if ( unlock && (rc = check_secret_key( sk, 0 )) ) + { free_secret_key( sk ); sk = NULL; - log_error(_("skipped `%s': %s\n"), locusr->d, gpg_strerror (rc) ); - } + log_error(_("skipped \"%s\": %s\n"), + locusr->d, g10_errstr(rc) ); + } else if( !(rc=openpgp_pk_test_algo (sk->pubkey_algo, use)) ) { SK_LIST r; if( sk->version == 4 && (use & PUBKEY_USAGE_SIG) - && sk->pubkey_algo == PUBKEY_ALGO_ELGAMAL_E ) { - log_info(_("skipped `%s': this is a PGP generated " - "ElGamal key which is not secure for signatures!\n"), - locusr->d ); + && sk->pubkey_algo == PUBKEY_ALGO_ELGAMAL_E ) + { + log_info(_("skipped \"%s\": %s\n"),locusr->d, + _("this is a PGP generated Elgamal key which" + " is not secure for signatures!")); + free_secret_key( sk ); sk = NULL; + } + else if( random_is_faked() && !is_insecure( sk ) ) { + log_info(_("key is not flagged as insecure - " + "can't use it with the faked RNG!\n")); free_secret_key( sk ); sk = NULL; } else { - r = xmalloc ( sizeof *r ); + r = xmalloc( sizeof *r ); r->sk = sk; sk = NULL; r->next = sk_list; r->mark = 0; @@ -156,7 +202,7 @@ build_sk_list( STRLIST locusr, SK_LIST *ret_sk_list, } else { free_secret_key( sk ); sk = NULL; - log_error("skipped `%s': %s\n", locusr->d, gpg_strerror (rc) ); + log_error("skipped \"%s\": %s\n", locusr->d, g10_errstr(rc) ); } } } @@ -164,7 +210,7 @@ build_sk_list( STRLIST locusr, SK_LIST *ret_sk_list, if( !rc && !sk_list ) { log_error("no valid signators\n"); - rc = GPG_ERR_NO_USER_ID; + rc = G10ERR_NO_USER_ID; } if( rc ) diff --git a/g10/status.c b/g10/status.c index aa55020be..ffee8559f 100644 --- a/g10/status.c +++ b/g10/status.c @@ -1,6 +1,6 @@ -/* status.c - * Copyright (C) 1998, 1999, 2000, 2001, 2002, - * 2003 Free Software Foundation, Inc. +/* status.c - Status message and command-fd interface + * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, + * 2004, 2005, 2006 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -16,7 +16,8 @@ * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. */ #include <config.h> @@ -44,98 +45,145 @@ static FILE *statusfp; static void -progress_cb (void *ctx, const char *what, int printchar, int current, int total) +progress_cb ( void *ctx, int c ) { - char buf[150]; - - if (printchar == '\n') - printchar = 'X'; - - sprintf (buf, "%.20s %c %d %d", what, printchar, current, total); - write_status_text (STATUS_PROGRESS, buf); + char buf[50]; + + if ( c == '\n' ) + sprintf ( buf, "%.20s X 100 100", (char*)ctx ); + else + sprintf ( buf, "%.20s %c 0 0", (char*)ctx, c ); + write_status_text ( STATUS_PROGRESS, buf ); } static const char * get_status_string ( int no ) { - const char *s; - - switch( no ) { - case STATUS_ENTER : s = "ENTER"; break; - case STATUS_LEAVE : s = "LEAVE"; break; - case STATUS_ABORT : s = "ABORT"; break; - case STATUS_GOODSIG: s = "GOODSIG"; break; - case STATUS_KEYEXPIRED: s = "KEYEXPIRED"; break; - case STATUS_KEYREVOKED: s = "KEYREVOKED"; break; - case STATUS_BADSIG : s = "BADSIG"; break; - case STATUS_ERRSIG : s = "ERRSIG"; break; - case STATUS_BADARMOR : s = "BADARMOR"; break; - case STATUS_RSA_OR_IDEA : s= "RSA_OR_IDEA"; break; - case STATUS_TRUST_UNDEFINED: s = "TRUST_UNDEFINED"; break; - case STATUS_TRUST_NEVER : s = "TRUST_NEVER"; break; - case STATUS_TRUST_MARGINAL : s = "TRUST_MARGINAL"; break; - case STATUS_TRUST_FULLY : s = "TRUST_FULLY"; break; - case STATUS_TRUST_ULTIMATE : s = "TRUST_ULTIMATE"; break; - case STATUS_GET_BOOL : s = "GET_BOOL"; break; - case STATUS_GET_LINE : s = "GET_LINE"; break; - case STATUS_GET_HIDDEN : s = "GET_HIDDEN"; break; - case STATUS_GOT_IT : s = "GOT_IT"; break; - case STATUS_SHM_INFO : s = "SHM_INFO"; break; - case STATUS_SHM_GET : s = "SHM_GET"; break; - case STATUS_SHM_GET_BOOL : s = "SHM_GET_BOOL"; break; - case STATUS_SHM_GET_HIDDEN : s = "SHM_GET_HIDDEN"; break; - case STATUS_NEED_PASSPHRASE: s = "NEED_PASSPHRASE"; break; - case STATUS_VALIDSIG : s = "VALIDSIG"; break; - case STATUS_SIG_ID : s = "SIG_ID"; break; - case STATUS_ENC_TO : s = "ENC_TO"; break; - case STATUS_NODATA : s = "NODATA"; break; - case STATUS_BAD_PASSPHRASE : s = "BAD_PASSPHRASE"; break; - case STATUS_NO_PUBKEY : s = "NO_PUBKEY"; break; - case STATUS_NO_SECKEY : s = "NO_SECKEY"; break; - case STATUS_NEED_PASSPHRASE_SYM: s = "NEED_PASSPHRASE_SYM"; break; - case STATUS_DECRYPTION_FAILED: s = "DECRYPTION_FAILED"; break; - case STATUS_DECRYPTION_OKAY: s = "DECRYPTION_OKAY"; break; - case STATUS_MISSING_PASSPHRASE: s = "MISSING_PASSPHRASE"; break; - case STATUS_GOOD_PASSPHRASE : s = "GOOD_PASSPHRASE"; break; - case STATUS_GOODMDC : s = "GOODMDC"; break; - case STATUS_BADMDC : s = "BADMDC"; break; - case STATUS_ERRMDC : s = "ERRMDC"; break; - case STATUS_IMPORTED : s = "IMPORTED"; break; - case STATUS_IMPORT_OK : s = "IMPORT_OK"; break; - case STATUS_IMPORT_CHECK : s = "IMPORT_CHECK"; break; - case STATUS_IMPORT_RES : s = "IMPORT_RES"; break; - case STATUS_FILE_START : s = "FILE_START"; break; - case STATUS_FILE_DONE : s = "FILE_DONE"; break; - case STATUS_FILE_ERROR : s = "FILE_ERROR"; break; - case STATUS_BEGIN_DECRYPTION:s = "BEGIN_DECRYPTION"; break; - case STATUS_END_DECRYPTION : s = "END_DECRYPTION"; break; - case STATUS_BEGIN_ENCRYPTION:s = "BEGIN_ENCRYPTION"; break; - case STATUS_END_ENCRYPTION : s = "END_ENCRYPTION"; break; - case STATUS_DELETE_PROBLEM : s = "DELETE_PROBLEM"; break; - case STATUS_PROGRESS : s = "PROGRESS"; break; - case STATUS_SIG_CREATED : s = "SIG_CREATED"; break; - case STATUS_SESSION_KEY : s = "SESSION_KEY"; break; - case STATUS_NOTATION_NAME : s = "NOTATION_NAME" ; break; - case STATUS_NOTATION_DATA : s = "NOTATION_DATA" ; break; - case STATUS_POLICY_URL : s = "POLICY_URL" ; break; - case STATUS_BEGIN_STREAM : s = "BEGIN_STREAM"; break; - case STATUS_END_STREAM : s = "END_STREAM"; break; - case STATUS_KEY_CREATED : s = "KEY_CREATED"; break; - case STATUS_USERID_HINT : s = "USERID_HINT"; break; - case STATUS_UNEXPECTED : s = "UNEXPECTED"; break; - case STATUS_INV_RECP : s = "INV_RECP"; break; - case STATUS_NO_RECP : s = "NO_RECP"; break; - case STATUS_ALREADY_SIGNED : s = "ALREADY_SIGNED"; break; - case STATUS_SIGEXPIRED : s = "SIGEXPIRED deprecated-use-keyexpired-instead"; break; - case STATUS_EXPSIG : s = "EXPSIG"; break; - case STATUS_EXPKEYSIG : s = "EXPKEYSIG"; break; - case STATUS_REVKEYSIG : s = "REVKEYSIG"; break; - case STATUS_ATTRIBUTE : s = "ATTRIBUTE"; break; - default: s = "?"; break; + const char *s; + + switch( no ) + { + case STATUS_ENTER : s = "ENTER"; break; + case STATUS_LEAVE : s = "LEAVE"; break; + case STATUS_ABORT : s = "ABORT"; break; + case STATUS_NEWSIG : s = "NEWSIG"; break; + case STATUS_GOODSIG: s = "GOODSIG"; break; + case STATUS_KEYEXPIRED: s = "KEYEXPIRED"; break; + case STATUS_KEYREVOKED: s = "KEYREVOKED"; break; + case STATUS_BADSIG : s = "BADSIG"; break; + case STATUS_ERRSIG : s = "ERRSIG"; break; + case STATUS_BADARMOR : s = "BADARMOR"; break; + case STATUS_RSA_OR_IDEA : s= "RSA_OR_IDEA"; break; + case STATUS_TRUST_UNDEFINED: s = "TRUST_UNDEFINED"; break; + case STATUS_TRUST_NEVER : s = "TRUST_NEVER"; break; + case STATUS_TRUST_MARGINAL : s = "TRUST_MARGINAL"; break; + case STATUS_TRUST_FULLY : s = "TRUST_FULLY"; break; + case STATUS_TRUST_ULTIMATE : s = "TRUST_ULTIMATE"; break; + case STATUS_GET_BOOL : s = "GET_BOOL"; break; + case STATUS_GET_LINE : s = "GET_LINE"; break; + case STATUS_GET_HIDDEN : s = "GET_HIDDEN"; break; + case STATUS_GOT_IT : s = "GOT_IT"; break; + case STATUS_SHM_INFO : s = "SHM_INFO"; break; + case STATUS_SHM_GET : s = "SHM_GET"; break; + case STATUS_SHM_GET_BOOL : s = "SHM_GET_BOOL"; break; + case STATUS_SHM_GET_HIDDEN : s = "SHM_GET_HIDDEN"; break; + case STATUS_NEED_PASSPHRASE: s = "NEED_PASSPHRASE"; break; + case STATUS_VALIDSIG : s = "VALIDSIG"; break; + case STATUS_SIG_ID : s = "SIG_ID"; break; + case STATUS_ENC_TO : s = "ENC_TO"; break; + case STATUS_NODATA : s = "NODATA"; break; + case STATUS_BAD_PASSPHRASE : s = "BAD_PASSPHRASE"; break; + case STATUS_NO_PUBKEY : s = "NO_PUBKEY"; break; + case STATUS_NO_SECKEY : s = "NO_SECKEY"; break; + case STATUS_NEED_PASSPHRASE_SYM: s = "NEED_PASSPHRASE_SYM"; break; + case STATUS_NEED_PASSPHRASE_PIN: s = "NEED_PASSPHRASE_PIN"; break; + case STATUS_DECRYPTION_FAILED: s = "DECRYPTION_FAILED"; break; + case STATUS_DECRYPTION_OKAY: s = "DECRYPTION_OKAY"; break; + case STATUS_MISSING_PASSPHRASE: s = "MISSING_PASSPHRASE"; break; + case STATUS_GOOD_PASSPHRASE : s = "GOOD_PASSPHRASE"; break; + case STATUS_GOODMDC : s = "GOODMDC"; break; + case STATUS_BADMDC : s = "BADMDC"; break; + case STATUS_ERRMDC : s = "ERRMDC"; break; + case STATUS_IMPORTED : s = "IMPORTED"; break; + case STATUS_IMPORT_OK : s = "IMPORT_OK"; break; + case STATUS_IMPORT_CHECK : s = "IMPORT_CHECK"; break; + case STATUS_IMPORT_RES : s = "IMPORT_RES"; break; + case STATUS_FILE_START : s = "FILE_START"; break; + case STATUS_FILE_DONE : s = "FILE_DONE"; break; + case STATUS_FILE_ERROR : s = "FILE_ERROR"; break; + case STATUS_BEGIN_DECRYPTION:s = "BEGIN_DECRYPTION"; break; + case STATUS_END_DECRYPTION : s = "END_DECRYPTION"; break; + case STATUS_BEGIN_ENCRYPTION:s = "BEGIN_ENCRYPTION"; break; + case STATUS_END_ENCRYPTION : s = "END_ENCRYPTION"; break; + case STATUS_DELETE_PROBLEM : s = "DELETE_PROBLEM"; break; + case STATUS_PROGRESS : s = "PROGRESS"; break; + case STATUS_SIG_CREATED : s = "SIG_CREATED"; break; + case STATUS_SESSION_KEY : s = "SESSION_KEY"; break; + case STATUS_NOTATION_NAME : s = "NOTATION_NAME" ; break; + case STATUS_NOTATION_DATA : s = "NOTATION_DATA" ; break; + case STATUS_POLICY_URL : s = "POLICY_URL" ; break; + case STATUS_BEGIN_STREAM : s = "BEGIN_STREAM"; break; + case STATUS_END_STREAM : s = "END_STREAM"; break; + case STATUS_KEY_CREATED : s = "KEY_CREATED"; break; + case STATUS_KEY_NOT_CREATED: s = "KEY_NOT_CREATED"; break; + case STATUS_USERID_HINT : s = "USERID_HINT"; break; + case STATUS_UNEXPECTED : s = "UNEXPECTED"; break; + case STATUS_INV_RECP : s = "INV_RECP"; break; + case STATUS_NO_RECP : s = "NO_RECP"; break; + case STATUS_ALREADY_SIGNED : s = "ALREADY_SIGNED"; break; + case STATUS_SIGEXPIRED : s = "SIGEXPIRED deprecated-use-keyexpired-instead"; break; + case STATUS_EXPSIG : s = "EXPSIG"; break; + case STATUS_EXPKEYSIG : s = "EXPKEYSIG"; break; + case STATUS_REVKEYSIG : s = "REVKEYSIG"; break; + case STATUS_ATTRIBUTE : s = "ATTRIBUTE"; break; + case STATUS_CARDCTRL : s = "CARDCTRL"; break; + case STATUS_PLAINTEXT : s = "PLAINTEXT"; break; + case STATUS_PLAINTEXT_LENGTH:s = "PLAINTEXT_LENGTH"; break; + case STATUS_SIG_SUBPACKET : s = "SIG_SUBPACKET"; break; + case STATUS_SC_OP_SUCCESS : s = "SC_OP_SUCCESS"; break; + case STATUS_SC_OP_FAILURE : s = "SC_OP_FAILURE"; break; + case STATUS_BACKUP_KEY_CREATED:s="BACKUP_KEY_CREATED"; break; + case STATUS_PKA_TRUST_BAD : s = "PKA_TRUST_BAD"; break; + case STATUS_PKA_TRUST_GOOD : s = "PKA_TRUST_GOOD"; break; + case STATUS_BEGIN_SIGNING : s = "BEGIN_SIGNING"; break; + default: s = "?"; break; + } + return s; +} + + +/* Return true if the status message NO may currently be issued. We + need this to avoid syncronisation problem while auto retrieving a + key. There it may happen that a status NODATA is issued for a non + available key and the user may falsely interpret this has a missing + signature. */ +static int +status_currently_allowed (int no) +{ + if (!glo_ctrl.in_auto_key_retrieve) + return 1; /* Yes. */ + + /* We allow some statis anyway, so that import statistics are + correct and to avoid problems if the retriebval subsystem will + prompt the user. */ + switch (no) + { + case STATUS_GET_BOOL: + case STATUS_GET_LINE: + case STATUS_GET_HIDDEN: + case STATUS_GOT_IT: + case STATUS_IMPORTED: + case STATUS_IMPORT_OK: + case STATUS_IMPORT_CHECK: + case STATUS_IMPORT_RES: + return 1; /* Yes. */ + default: + break; } - return s; + return 0; /* No. */ } + void set_status_fd ( int fd ) { @@ -161,7 +209,9 @@ set_status_fd ( int fd ) fd, strerror(errno)); } last_fd = fd; - gcry_set_progress_handler (progress_cb, NULL); + register_primegen_progress ( progress_cb, "primegen" ); + register_pk_dsa_progress ( progress_cb, "pk_dsa" ); + register_pk_elg_progress ( progress_cb, "pk_elg" ); } int @@ -179,8 +229,8 @@ write_status ( int no ) void write_status_text ( int no, const char *text) { - if( !statusfp ) - return; /* not enabled */ + if( !statusfp || !status_currently_allowed (no) ) + return; /* Not enabled or allowed. */ fputs ( "[GNUPG:] ", statusfp ); fputs ( get_status_string (no), statusfp ); @@ -196,7 +246,8 @@ write_status_text ( int no, const char *text) } } putc ('\n',statusfp); - fflush (statusfp); + if ( fflush (statusfp) && opt.exit_on_status_write_error ) + g10_exit (0); } @@ -215,8 +266,8 @@ write_status_text_and_buffer ( int no, const char *string, int lower_limit = ' '; size_t n, count, dowrap; - if( !statusfp ) - return; /* not enabled */ + if( !statusfp || !status_currently_allowed (no) ) + return; /* Not enabled or allowed. */ if (wrap == -1) { lower_limit--; @@ -260,7 +311,8 @@ write_status_text_and_buffer ( int no, const char *string, } while ( len ); putc ('\n',statusfp); - fflush (statusfp); + if ( fflush (statusfp) && opt.exit_on_status_write_error ) + g10_exit (0); } void @@ -308,6 +360,9 @@ do_get_from_fd( const char *keyword, int hidden, int bool ) int i, len; char *string; + if(statusfp!=stdout) + fflush(stdout); + write_status_text( bool? STATUS_GET_BOOL : hidden? STATUS_GET_HIDDEN : STATUS_GET_LINE, keyword ); @@ -348,6 +403,10 @@ cpr_enabled() { if( opt.command_fd != -1 ) return 1; +#ifdef USE_SHM_COPROCESSING + if( opt.shm_coprocess ) + return 1; +#endif return 0; } @@ -358,6 +417,10 @@ cpr_get_no_help( const char *keyword, const char *prompt ) if( opt.command_fd != -1 ) return do_get_from_fd ( keyword, 0, 0 ); +#ifdef USE_SHM_COPROCESSING + if( opt.shm_coprocess ) + return do_shm_get( keyword, 0, 0 ); +#endif for(;;) { p = tty_get( prompt ); return p; @@ -371,10 +434,14 @@ cpr_get( const char *keyword, const char *prompt ) if( opt.command_fd != -1 ) return do_get_from_fd ( keyword, 0, 0 ); +#ifdef USE_SHM_COPROCESSING + if( opt.shm_coprocess ) + return do_shm_get( keyword, 0, 0 ); +#endif for(;;) { p = tty_get( prompt ); if( *p=='?' && !p[1] && !(keyword && !*keyword)) { - xfree (p); + xfree(p); display_online_help( keyword ); } else @@ -390,7 +457,7 @@ cpr_get_utf8( const char *keyword, const char *prompt ) p = cpr_get( keyword, prompt ); if( p ) { char *utf8 = native_to_utf8( p ); - xfree ( p ); + xfree( p ); p = utf8; } return p; @@ -403,10 +470,14 @@ cpr_get_hidden( const char *keyword, const char *prompt ) if( opt.command_fd != -1 ) return do_get_from_fd ( keyword, 1, 0 ); +#ifdef USE_SHM_COPROCESSING + if( opt.shm_coprocess ) + return do_shm_get( keyword, 1, 0 ); +#endif for(;;) { p = tty_get_hidden( prompt ); if( *p == '?' && !p[1] ) { - xfree (p); + xfree(p); display_online_help( keyword ); } else @@ -419,6 +490,10 @@ cpr_kill_prompt(void) { if( opt.command_fd != -1 ) return; +#ifdef USE_SHM_COPROCESSING + if( opt.shm_coprocess ) + return; +#endif tty_kill_prompt(); return; } @@ -431,17 +506,21 @@ cpr_get_answer_is_yes( const char *keyword, const char *prompt ) if( opt.command_fd != -1 ) return !!do_get_from_fd ( keyword, 0, 1 ); +#ifdef USE_SHM_COPROCESSING + if( opt.shm_coprocess ) + return !!do_shm_get( keyword, 0, 1 ); +#endif for(;;) { p = tty_get( prompt ); trim_spaces(p); /* it is okay to do this here */ if( *p == '?' && !p[1] ) { - xfree (p); + xfree(p); display_online_help( keyword ); } else { tty_kill_prompt(); yes = answer_is_yes(p); - xfree (p); + xfree(p); return yes; } } @@ -455,18 +534,65 @@ cpr_get_answer_yes_no_quit( const char *keyword, const char *prompt ) if( opt.command_fd != -1 ) return !!do_get_from_fd ( keyword, 0, 1 ); +#ifdef USE_SHM_COPROCESSING + if( opt.shm_coprocess ) + return !!do_shm_get( keyword, 0, 1 ); +#endif for(;;) { p = tty_get( prompt ); trim_spaces(p); /* it is okay to do this here */ if( *p == '?' && !p[1] ) { - xfree (p); + xfree(p); display_online_help( keyword ); } else { tty_kill_prompt(); yes = answer_is_yes_no_quit(p); - xfree (p); + xfree(p); return yes; } } } + + +int +cpr_get_answer_okay_cancel (const char *keyword, + const char *prompt, + int def_answer) +{ + int yes; + char *answer = NULL; + char *p; + + if( opt.command_fd != -1 ) + answer = do_get_from_fd ( keyword, 0, 0 ); +#ifdef USE_SHM_COPROCESSING + else if( opt.shm_coprocess ) + answer = do_shm_get( keyword, 0, 0 ); +#endif + + if (answer) + { + yes = answer_is_okay_cancel (answer, def_answer); + xfree (answer); + return yes; + } + + for(;;) + { + p = tty_get( prompt ); + trim_spaces(p); /* it is okay to do this here */ + if (*p == '?' && !p[1]) + { + xfree(p); + display_online_help (keyword); + } + else + { + tty_kill_prompt(); + yes = answer_is_okay_cancel (p, def_answer); + xfree(p); + return yes; + } + } +} diff --git a/g10/status.h b/g10/status.h index d8de81080..b5dbe7480 100644 --- a/g10/status.h +++ b/g10/status.h @@ -1,5 +1,6 @@ /* status.h - * Copyright (C) 1998, 1999, 2000, 2001 Free Software Foundation, Inc. + * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, + * 2004 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -15,12 +16,12 @@ * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. */ #ifndef G10_STATUS_H #define G10_STATUS_H - #define STATUS_ENTER 1 #define STATUS_LEAVE 2 #define STATUS_ABORT 3 @@ -29,7 +30,6 @@ #define STATUS_BADSIG 5 #define STATUS_ERRSIG 6 - #define STATUS_BADARMOR 7 #define STATUS_RSA_OR_IDEA 8 @@ -100,6 +100,26 @@ #define STATUS_IMPORT_OK 68 #define STATUS_IMPORT_CHECK 69 #define STATUS_REVKEYSIG 70 +#define STATUS_CARDCTRL 71 +#define STATUS_NEWSIG 72 +#define STATUS_PLAINTEXT 73 +#define STATUS_PLAINTEXT_LENGTH 74 +#define STATUS_KEY_NOT_CREATED 75 +#define STATUS_NEED_PASSPHRASE_PIN 76 +#define STATUS_SIG_SUBPACKET 77 + +/* Extra status codes for certain smartcard operations. Primary + useful to double check that change PIN worked as expected. */ +#define STATUS_SC_OP_FAILURE 79 +#define STATUS_SC_OP_SUCCESS 80 + +#define STATUS_BACKUP_KEY_CREATED 81 + +#define STATUS_PKA_TRUST_BAD 82 +#define STATUS_PKA_TRUST_GOOD 83 + +#define STATUS_BEGIN_SIGNING 84 + /*-- status.c --*/ void set_status_fd ( int fd ); @@ -119,6 +139,8 @@ char *cpr_get_hidden( const char *keyword, const char *prompt ); void cpr_kill_prompt(void); int cpr_get_answer_is_yes( const char *keyword, const char *prompt ); int cpr_get_answer_yes_no_quit( const char *keyword, const char *prompt ); - +int cpr_get_answer_okay_cancel (const char *keyword, + const char *prompt, + int def_answer); #endif /*G10_STATUS_H*/ diff --git a/g10/tdbdump.c b/g10/tdbdump.c index 5eb482959..d840c0882 100644 --- a/g10/tdbdump.c +++ b/g10/tdbdump.c @@ -1,5 +1,5 @@ /* tdbdump.c - * Copyright (C) 1998, 1999, 2000, 2001, 2003 Free Software Foundation, Inc. + * Copyright (C) 1998, 1999, 2000, 2001 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -15,7 +15,8 @@ * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. */ #include <config.h> @@ -34,7 +35,6 @@ #include "errors.h" #include "iobuf.h" #include "keydb.h" -#include "memory.h" #include "util.h" #include "trustdb.h" #include "options.h" @@ -58,7 +58,7 @@ write_record( TRUSTREC *rec ) if( !rc ) return; log_error(_("trust record %lu, type %d: write failed: %s\n"), - rec->recnum, rec->rectype, gpg_strerror (rc) ); + rec->recnum, rec->rectype, g10_errstr(rc) ); tdbio_invalid(); } @@ -132,7 +132,7 @@ import_ownertrust( const char *fname ) int rc; init_trustdb(); - if( !fname || (*fname == '-' && !fname[1]) ) { + if( iobuf_is_pipe_filename (fname) ) { fp = stdin; fname = "[stdin]"; is_stdin = 1; @@ -142,6 +142,14 @@ import_ownertrust( const char *fname ) return; } + if (is_secured_file (fileno (fp))) + { + fclose (fp); + errno = EPERM; + log_error (_("can't open `%s': %s\n"), fname, strerror(errno) ); + return; + } + while( fgets( line, DIM(line)-1, fp ) ) { TRUSTREC rec; @@ -149,24 +157,26 @@ import_ownertrust( const char *fname ) continue; n = strlen(line); if( line[n-1] != '\n' ) { - log_error (_("\b%s: line too long\n"), fname ); + log_error (_("error in `%s': %s\n"), fname, _("line too long") ); /* ... or last line does not have a LF */ break; /* can't continue */ } for(p = line; *p && *p != ':' ; p++ ) - if( !hexdigitp (p) ) + if( !hexdigitp(p) ) break; if( *p != ':' ) { - log_error (_("\b%s: error: missing colon\n"), fname ); + log_error (_("error in `%s': %s\n"), fname, _("colon missing") ); continue; } fprlen = p - line; if( fprlen != 32 && fprlen != 40 ) { - log_error (_("\b%s: error: invalid fingerprint\n"), fname ); + log_error (_("error in `%s': %s\n"), + fname, _("invalid fingerprint") ); continue; } if( sscanf(p, ":%u:", &otrust ) != 1 ) { - log_error (_("\b%s: error: no ownertrust value\n"), fname ); + log_error (_("error in `%s': %s\n"), + fname, _("ownertrust value missing")); continue; } if( !otrust ) @@ -202,11 +212,11 @@ import_ownertrust( const char *fname ) any = 1; } else /* error */ - log_error (_("\b%s: error finding trust record: %s\n"), - fname, gpg_strerror (rc)); + log_error (_("error finding trust record in `%s': %s\n"), + fname, g10_errstr(rc)); } if( ferror(fp) ) - log_error (_("\b%s: read error: %s\n"), fname, strerror(errno) ); + log_error ( _("read error in `%s': %s\n"), fname, strerror(errno) ); if( !is_stdin ) fclose(fp); @@ -215,7 +225,7 @@ import_ownertrust( const char *fname ) revalidation_mark (); rc = tdbio_sync (); if (rc) - log_error (_("trustdb: sync failed: %s\n"), gpg_strerror (rc) ); + log_error (_("trustdb: sync failed: %s\n"), g10_errstr(rc) ); } } diff --git a/g10/tdbio.c b/g10/tdbio.c index 75687a3b0..74e75b3c9 100644 --- a/g10/tdbio.c +++ b/g10/tdbio.c @@ -1,4 +1,4 @@ -/* tdbio.c +/* tdbio.c - trust databse I/O operations * Copyright (C) 1998, 1999, 2000, 2001, 2002 Free Software Foundation, Inc. * * This file is part of GnuPG. @@ -15,7 +15,8 @@ * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. */ #include <config.h> @@ -32,7 +33,6 @@ #include "gpg.h" #include "errors.h" #include "iobuf.h" -#include "memory.h" #include "util.h" #include "options.h" #include "main.h" @@ -123,21 +123,21 @@ get_record_from_cache( ulong recno ) static int write_cache_item( CACHE_CTRL r ) { + gpg_error_t err; int n; - gpg_error_t rc; if( lseek( db_fd, r->recno * TRUST_RECORD_LEN, SEEK_SET ) == -1 ) { - rc = gpg_error_from_errno (errno); + err = gpg_error_from_errno (errno); log_error(_("trustdb rec %lu: lseek failed: %s\n"), r->recno, strerror(errno) ); - return rc; + return err; } n = write( db_fd, r->data, TRUST_RECORD_LEN); if( n != TRUST_RECORD_LEN ) { - rc = gpg_error_from_errno (errno); + err = gpg_error_from_errno (errno); log_error(_("trustdb rec %lu: write failed (n=%d): %s\n"), r->recno, n, strerror(errno) ); - return rc; + return err; } r->flags.dirty = 0; return 0; @@ -191,7 +191,7 @@ put_record_into_cache( ulong recno, const char *data ) } /* see whether we reached the limit */ if( cache_entries < MAX_CACHE_ENTRIES_SOFT ) { /* no */ - r = xmalloc ( sizeof *r ); + r = xmalloc( sizeof *r ); r->flags.used = 1; r->recno = recno; memcpy( r->data, data, TRUST_RECORD_LEN ); @@ -234,7 +234,7 @@ put_record_into_cache( ulong recno, const char *data ) if( cache_entries < MAX_CACHE_ENTRIES_HARD ) { /* no */ if( opt.debug && !(cache_entries % 100) ) log_debug("increasing tdbio cache size\n"); - r = xmalloc ( sizeof *r ); + r = xmalloc( sizeof *r ); r->flags.used = 1; r->recno = recno; memcpy( r->data, data, TRUST_RECORD_LEN ); @@ -246,7 +246,7 @@ put_record_into_cache( ulong recno, const char *data ) return 0; } log_info(_("trustdb transaction too large\n")); - return GPG_ERR_RESOURCE_LIMIT; + return G10ERR_RESOURCE_LIMIT; } if( dirty_count ) { int n = dirty_count / 5; /* discard some dirty entries */ @@ -336,7 +336,6 @@ tdbio_sync() return 0; } - #if 0 /* The transaction code is disabled in the 1.2.x branch, as it is not yet used. It will be enabled in 1.3.x. */ @@ -375,11 +374,10 @@ tdbio_end_transaction() else is_locked = 1; } -#warning block_all_signals is not yet available in ../common/signals.c - /* block_all_signals(); */ + block_all_signals(); in_transaction = 0; rc = tdbio_sync(); -/* unblock_all_signals(); */ + unblock_all_signals(); if( !opt.lock_once ) { if( !release_dotlock( lockhandle ) ) is_locked = 0; @@ -410,9 +408,7 @@ tdbio_cancel_transaction() in_transaction = 0; return 0; } - -#endif /* transaction code */ - +#endif /******************************************************** @@ -502,9 +498,9 @@ tdbio_set_dbname( const char *new_dbname, int create ) if( access( fname, R_OK ) ) { if( errno != ENOENT ) { - log_error( _("%s: can't access: %s\n"), fname, strerror(errno) ); - xfree (fname); - return GPG_ERR_TRUSTDB; + log_error( _("can't access `%s': %s\n"), fname, strerror(errno) ); + xfree(fname); + return G10ERR_TRUSTDB; } if( create ) { FILE *fp; @@ -521,37 +517,42 @@ tdbio_set_dbname( const char *new_dbname, int create ) } *p = DIRSEP_C; - xfree (db_name); + xfree(db_name); db_name = fname; #ifdef __riscos__ if( !lockhandle ) lockhandle = create_dotlock( db_name ); if( !lockhandle ) - log_fatal( _("%s: can't create lock\n"), db_name ); + log_fatal( _("can't create lock for `%s'\n"), db_name ); if( make_dotlock( lockhandle, -1 ) ) - log_fatal( _("%s: can't make lock\n"), db_name ); + log_fatal( _("can't lock `%s'\n"), db_name ); #endif /* __riscos__ */ oldmask=umask(077); - fp =fopen( fname, "wb" ); + if (is_secured_filename (fname)) { + fp = NULL; + errno = EPERM; + } + else + fp =fopen( fname, "wb" ); umask(oldmask); if( !fp ) - log_fatal( _("%s: can't create: %s\n"), fname, strerror(errno) ); + log_fatal( _("can't create `%s': %s\n"), fname, strerror(errno) ); fclose(fp); db_fd = open( db_name, O_RDWR | MY_O_BINARY ); if( db_fd == -1 ) - log_fatal( _("%s: can't open: %s\n"), db_name, strerror(errno) ); + log_fatal( _("can't open `%s': %s\n"), db_name, strerror(errno) ); #ifndef __riscos__ if( !lockhandle ) lockhandle = create_dotlock( db_name ); if( !lockhandle ) - log_fatal( _("%s: can't create lock\n"), db_name ); + log_fatal( _("can't create lock for `%s'\n"), db_name ); #endif /* !__riscos__ */ rc = create_version_record (); if( rc ) log_fatal( _("%s: failed to create version record: %s"), - fname, gpg_strerror (rc)); + fname, g10_errstr(rc)); /* and read again to check that we are okay */ if( tdbio_read_record( 0, &rec, RECTYPE_VER ) ) log_fatal( _("%s: invalid trustdb created\n"), db_name ); @@ -562,7 +563,7 @@ tdbio_set_dbname( const char *new_dbname, int create ) return 0; } } - xfree (db_name); + xfree(db_name); db_name = fname; return 0; } @@ -588,30 +589,26 @@ open_db() if (!lockhandle ) lockhandle = create_dotlock( db_name ); if (!lockhandle ) - log_fatal( _("%s: can't create lock\n"), db_name ); + log_fatal( _("can't create lock for `%s'\n"), db_name ); #ifdef __riscos__ if (make_dotlock( lockhandle, -1 ) ) - log_fatal( _("%s: can't make lock\n"), db_name ); + log_fatal( _("can't lock `%s'\n"), db_name ); #endif /* __riscos__ */ db_fd = open (db_name, O_RDWR | MY_O_BINARY ); - if (db_fd == -1 && errno == EACCES) { + if (db_fd == -1 && (errno == EACCES +#ifdef EROFS + || errno == EROFS) +#endif + ) { db_fd = open (db_name, O_RDONLY | MY_O_BINARY ); if (db_fd != -1) log_info (_("NOTE: trustdb not writable\n")); } if ( db_fd == -1 ) - log_fatal( _("%s: can't open: %s\n"), db_name, strerror(errno) ); + log_fatal( _("can't open `%s': %s\n"), db_name, strerror(errno) ); + register_secured_file (db_name); - /* check whether we need to do a version migration */ - do - n = read (db_fd, buf, 5); - while (n==-1 && errno == EINTR); - if (n == 5 && !memcmp (buf, "\x01gpg\x02", 5)) - { - migrate_from_v2 (); - } - - /* read the version record */ + /* Read the version record. */ if (tdbio_read_record (0, &rec, RECTYPE_VER ) ) log_fatal( _("%s: invalid trustdb\n"), db_name ); } @@ -646,7 +643,7 @@ create_hashtable( TRUSTREC *vr, int type ) rc = tdbio_write_record( &rec ); if( rc ) log_fatal( _("%s: failed to create hashtable: %s\n"), - db_name, gpg_strerror (rc)); + db_name, g10_errstr(rc)); } /* update the version record */ rc = tdbio_write_record( vr ); @@ -654,7 +651,7 @@ create_hashtable( TRUSTREC *vr, int type ) rc = tdbio_sync(); if( rc ) log_fatal( _("%s: error updating version record: %s\n"), - db_name, gpg_strerror (rc)); + db_name, g10_errstr(rc)); } @@ -671,7 +668,7 @@ tdbio_db_matches_options() rc = tdbio_read_record( 0, &vr, RECTYPE_VER ); if( rc ) log_fatal( _("%s: error reading version record: %s\n"), - db_name, gpg_strerror (rc) ); + db_name, g10_errstr(rc) ); yes_no = vr.r.ver.marginals == opt.marginals_needed && vr.r.ver.completes == opt.completes_needed @@ -691,7 +688,7 @@ tdbio_read_model(void) rc = tdbio_read_record( 0, &vr, RECTYPE_VER ); if( rc ) log_fatal( _("%s: error reading version record: %s\n"), - db_name, gpg_strerror (rc) ); + db_name, g10_errstr(rc) ); return vr.r.ver.trust_model; } @@ -707,7 +704,7 @@ tdbio_read_nextcheck () rc = tdbio_read_record( 0, &vr, RECTYPE_VER ); if( rc ) log_fatal( _("%s: error reading version record: %s\n"), - db_name, gpg_strerror (rc) ); + db_name, g10_errstr(rc) ); return vr.r.ver.nextcheck; } @@ -721,7 +718,7 @@ tdbio_write_nextcheck (ulong stamp) rc = tdbio_read_record( 0, &vr, RECTYPE_VER ); if( rc ) log_fatal( _("%s: error reading version record: %s\n"), - db_name, gpg_strerror (rc) ); + db_name, g10_errstr(rc) ); if (vr.r.ver.nextcheck == stamp) return 0; @@ -730,7 +727,7 @@ tdbio_write_nextcheck (ulong stamp) rc = tdbio_write_record( &vr ); if( rc ) log_fatal( _("%s: error writing version record: %s\n"), - db_name, gpg_strerror (rc) ); + db_name, g10_errstr(rc) ); return 1; } @@ -751,7 +748,7 @@ get_trusthashrec(void) rc = tdbio_read_record( 0, &vr, RECTYPE_VER ); if( rc ) log_fatal( _("%s: error reading version record: %s\n"), - db_name, gpg_strerror (rc) ); + db_name, g10_errstr(rc) ); if( !vr.r.ver.trusthashtbl ) create_hashtable( &vr, 0 ); @@ -782,9 +779,8 @@ upd_hashtable( ulong table, byte *key, int keylen, ulong newrecnum ) hashrec += msb / ITEMS_PER_HTBL_RECORD; rc = tdbio_read_record( hashrec, &rec, RECTYPE_HTBL ); if( rc ) { - log_error ("upd_hashtable in `%s': read failed: %s\n", db_name, - gpg_strerror (rc) ); - return rc; + log_error("upd_hashtable: read failed: %s\n", g10_errstr(rc) ); + return rc; } item = rec.r.htbl.item[msb % ITEMS_PER_HTBL_RECORD]; @@ -792,8 +788,8 @@ upd_hashtable( ulong table, byte *key, int keylen, ulong newrecnum ) rec.r.htbl.item[msb % ITEMS_PER_HTBL_RECORD] = newrecnum; rc = tdbio_write_record( &rec ); if( rc ) { - log_error ("upd_hashtable in `%s': write htbl failed: %s\n", - db_name, gpg_strerror (rc) ); + log_error("upd_hashtable: write htbl failed: %s\n", + g10_errstr(rc) ); return rc; } } @@ -802,7 +798,7 @@ upd_hashtable( ulong table, byte *key, int keylen, ulong newrecnum ) rc = tdbio_read_record( item, &rec, 0 ); if( rc ) { log_error( "upd_hashtable: read item failed: %s\n", - gpg_strerror (rc) ); + g10_errstr(rc) ); return rc; } @@ -811,7 +807,7 @@ upd_hashtable( ulong table, byte *key, int keylen, ulong newrecnum ) level++; if( level >= keylen ) { log_error( "hashtable has invalid indirections.\n"); - return GPG_ERR_TRUSTDB; + return G10ERR_TRUSTDB; } goto next_level; } @@ -828,7 +824,7 @@ upd_hashtable( ulong table, byte *key, int keylen, ulong newrecnum ) &rec, RECTYPE_HLST); if( rc ) { log_error( "upd_hashtable: read hlst failed: %s\n", - gpg_strerror (rc) ); + g10_errstr(rc) ); return rc; } } @@ -843,7 +839,7 @@ upd_hashtable( ulong table, byte *key, int keylen, ulong newrecnum ) rc = tdbio_write_record( &rec ); if( rc ) log_error( "upd_hashtable: write hlst failed: %s\n", - gpg_strerror (rc) ); + g10_errstr(rc) ); return rc; /* done */ } } @@ -852,7 +848,7 @@ upd_hashtable( ulong table, byte *key, int keylen, ulong newrecnum ) &rec, RECTYPE_HLST ); if( rc ) { log_error( "upd_hashtable: read hlst failed: %s\n", - gpg_strerror (rc) ); + g10_errstr(rc) ); return rc; } } @@ -861,7 +857,7 @@ upd_hashtable( ulong table, byte *key, int keylen, ulong newrecnum ) rc = tdbio_write_record( &rec ); if( rc ) { log_error( "upd_hashtable: write hlst failed: %s\n", - gpg_strerror (rc) ); + g10_errstr(rc) ); return rc; } memset( &rec, 0, sizeof rec ); @@ -871,7 +867,7 @@ upd_hashtable( ulong table, byte *key, int keylen, ulong newrecnum ) rc = tdbio_write_record( &rec ); if( rc ) log_error( "upd_hashtable: write ext hlst failed: %s\n", - gpg_strerror (rc) ); + g10_errstr(rc) ); return rc; /* done */ } } /* end loop over hlst slots */ @@ -889,7 +885,7 @@ upd_hashtable( ulong table, byte *key, int keylen, ulong newrecnum ) rc = tdbio_write_record( &rec ); if( rc ) { log_error( "upd_hashtable: write new hlst failed: %s\n", - gpg_strerror (rc) ); + g10_errstr(rc) ); return rc; } /* update the hashtable record */ @@ -897,14 +893,14 @@ upd_hashtable( ulong table, byte *key, int keylen, ulong newrecnum ) rc = tdbio_write_record( &lastrec ); if( rc ) log_error( "upd_hashtable: update htbl failed: %s\n", - gpg_strerror (rc) ); + g10_errstr(rc) ); return rc; /* ready */ } else { log_error( "hashtbl %lu: %lu/%d points to an invalid record %lu\n", table, hashrec, (msb % ITEMS_PER_HTBL_RECORD), item); list_trustdb(NULL); - return GPG_ERR_TRUSTDB; + return G10ERR_TRUSTDB; } } @@ -931,8 +927,8 @@ drop_from_hashtable( ulong table, byte *key, int keylen, ulong recnum ) hashrec += msb / ITEMS_PER_HTBL_RECORD; rc = tdbio_read_record( hashrec, &rec, RECTYPE_HTBL ); if( rc ) { - log_error ("drop_from_hashtable `%s': read failed: %s\n", - db_name, gpg_strerror (rc) ); + log_error("drop_from_hashtable: read failed: %s\n", + g10_errstr(rc) ); return rc; } @@ -944,15 +940,15 @@ drop_from_hashtable( ulong table, byte *key, int keylen, ulong recnum ) rec.r.htbl.item[msb % ITEMS_PER_HTBL_RECORD] = 0; rc = tdbio_write_record( &rec ); if( rc ) - log_error ("drop_from_hashtable `%s': write htbl failed: %s\n", - db_name, gpg_strerror (rc) ); + log_error("drop_from_hashtable: write htbl failed: %s\n", + g10_errstr(rc) ); return rc; } rc = tdbio_read_record( item, &rec, 0 ); if( rc ) { log_error( "drop_from_hashtable: read item failed: %s\n", - gpg_strerror (rc) ); + g10_errstr(rc) ); return rc; } @@ -961,7 +957,7 @@ drop_from_hashtable( ulong table, byte *key, int keylen, ulong recnum ) level++; if( level >= keylen ) { log_error( "hashtable has invalid indirections.\n"); - return GPG_ERR_TRUSTDB; + return G10ERR_TRUSTDB; } goto next_level; } @@ -973,9 +969,8 @@ drop_from_hashtable( ulong table, byte *key, int keylen, ulong recnum ) rec.r.hlst.rnum[i] = 0; /* drop */ rc = tdbio_write_record( &rec ); if( rc ) - log_error ("drop_from_hashtable `%s': " - "write htbl failed: %s\n", - db_name, gpg_strerror (rc) ); + log_error("drop_from_hashtable: write htbl failed: %s\n", + g10_errstr(rc) ); return rc; } } @@ -984,7 +979,7 @@ drop_from_hashtable( ulong table, byte *key, int keylen, ulong recnum ) &rec, RECTYPE_HLST); if( rc ) { log_error( "drop_from_hashtable: read hlst failed: %s\n", - gpg_strerror (rc) ); + g10_errstr(rc) ); return rc; } } @@ -995,7 +990,7 @@ drop_from_hashtable( ulong table, byte *key, int keylen, ulong recnum ) log_error( "hashtbl %lu: %lu/%d points to wrong record %lu\n", table, hashrec, (msb % ITEMS_PER_HTBL_RECORD), item); - return GPG_ERR_TRUSTDB; + return G10ERR_TRUSTDB; } @@ -1021,8 +1016,7 @@ lookup_hashtable( ulong table, const byte *key, size_t keylen, hashrec += msb / ITEMS_PER_HTBL_RECORD; rc = tdbio_read_record( hashrec, rec, RECTYPE_HTBL ); if( rc ) { - log_error ("lookup_hashtable in `%s' failed: %s\n", - db_name, gpg_strerror (rc) ); + log_error("lookup_hashtable failed: %s\n", g10_errstr(rc) ); return rc; } @@ -1032,16 +1026,15 @@ lookup_hashtable( ulong table, const byte *key, size_t keylen, rc = tdbio_read_record( item, rec, 0 ); if( rc ) { - log_error ("hashtable `%s' read failed: %s\n", - db_name, gpg_strerror (rc) ); + log_error( "hashtable read failed: %s\n", g10_errstr(rc) ); return rc; } if( rec->rectype == RECTYPE_HTBL ) { hashrec = item; level++; if( level >= keylen ) { - log_error ("hashtable `%s' has invalid indirections\n", db_name); - return GPG_ERR_TRUSTDB; + log_error("hashtable has invalid indirections\n"); + return G10ERR_TRUSTDB; } goto next_level; } @@ -1056,7 +1049,7 @@ lookup_hashtable( ulong table, const byte *key, size_t keylen, rc = tdbio_read_record( rec->r.hlst.rnum[i], &tmp, 0 ); if( rc ) { log_error( "lookup_hashtable: read item failed: %s\n", - gpg_strerror (rc) ); + g10_errstr(rc) ); return rc; } if( (*cmpfnc)( cmpdata, &tmp ) ) { @@ -1069,7 +1062,7 @@ lookup_hashtable( ulong table, const byte *key, size_t keylen, rc = tdbio_read_record( rec->r.hlst.next, rec, RECTYPE_HLST ); if( rc ) { log_error( "lookup_hashtable: read hlst failed: %s\n", - gpg_strerror (rc) ); + g10_errstr(rc) ); return rc; } } @@ -1164,7 +1157,7 @@ tdbio_read_record( ulong recnum, TRUSTREC *rec, int expected ) { byte readbuf[TRUST_RECORD_LEN]; const byte *buf, *p; - int rc = 0; + gpg_error_t err = 0; int n, i; if( db_fd == -1 ) @@ -1172,19 +1165,19 @@ tdbio_read_record( ulong recnum, TRUSTREC *rec, int expected ) buf = get_record_from_cache( recnum ); if( !buf ) { if( lseek( db_fd, recnum * TRUST_RECORD_LEN, SEEK_SET ) == -1 ) { - rc = gpg_error_from_errno (errno); + err = gpg_error_from_errno (errno); log_error(_("trustdb: lseek failed: %s\n"), strerror(errno) ); - return rc; + return err; } n = read( db_fd, readbuf, TRUST_RECORD_LEN); if( !n ) { return -1; /* eof */ } else if( n != TRUST_RECORD_LEN ) { - rc = gpg_error_from_errno (errno); + err = gpg_error_from_errno (errno); log_error(_("trustdb: read failed (n=%d): %s\n"), n, strerror(errno) ); - return rc; + return err; } buf = readbuf; } @@ -1195,7 +1188,7 @@ tdbio_read_record( ulong recnum, TRUSTREC *rec, int expected ) if( expected && rec->rectype != expected ) { log_error("%lu: read expected rec type %d, got %d\n", recnum, expected, rec->rectype ); - return GPG_ERR_TRUSTDB; + return gpg_error (GPG_ERR_TRUSTDB); } p++; /* skip reserved byte */ switch( rec->rectype ) { @@ -1204,7 +1197,7 @@ tdbio_read_record( ulong recnum, TRUSTREC *rec, int expected ) case RECTYPE_VER: /* version record */ if( memcmp(buf+1, "gpg", 3 ) ) { log_error( _("%s: not a trustdb file\n"), db_name ); - rc = GPG_ERR_TRUSTDB; + err = gpg_error (GPG_ERR_TRUSTDB); } p += 2; /* skip "gpg" */ rec->r.ver.version = *p++; @@ -1223,12 +1216,12 @@ tdbio_read_record( ulong recnum, TRUSTREC *rec, int expected ) if( recnum ) { log_error( _("%s: version record with recnum %lu\n"), db_name, (ulong)recnum ); - rc = GPG_ERR_TRUSTDB; + err = gpg_error (GPG_ERR_TRUSTDB); } else if( rec->r.ver.version != 3 ) { log_error( _("%s: invalid file version %d\n"), db_name, rec->r.ver.version ); - rc = GPG_ERR_TRUSTDB; + err = gpg_error (GPG_ERR_TRUSTDB); } break; case RECTYPE_FREE: @@ -1263,11 +1256,11 @@ tdbio_read_record( ulong recnum, TRUSTREC *rec, int expected ) default: log_error( "%s: invalid record type %d at recnum %lu\n", db_name, rec->rectype, (ulong)recnum ); - rc = GPG_ERR_TRUSTDB; + err = gpg_error (GPG_ERR_TRUSTDB); break; } - return rc; + return err; } /**************** @@ -1379,7 +1372,7 @@ tdbio_delete_record( ulong recnum ) rc = tdbio_read_record( 0, &vr, RECTYPE_VER ); if( rc ) log_fatal( _("%s: error reading version record: %s\n"), - db_name, gpg_strerror (rc) ); + db_name, g10_errstr(rc) ); rec.recnum = recnum; rec.rectype = RECTYPE_FREE; @@ -1406,13 +1399,13 @@ tdbio_new_recnum() rc = tdbio_read_record( 0, &vr, RECTYPE_VER ); if( rc ) log_fatal( _("%s: error reading version record: %s\n"), - db_name, gpg_strerror (rc) ); + db_name, g10_errstr(rc) ); if( vr.r.ver.firstfree ) { recnum = vr.r.ver.firstfree; rc = tdbio_read_record( recnum, &rec, RECTYPE_FREE ); if( rc ) { log_error( _("%s: error reading free record: %s\n"), - db_name, gpg_strerror (rc) ); + db_name, g10_errstr(rc) ); return rc; } /* update dir record */ @@ -1420,7 +1413,7 @@ tdbio_new_recnum() rc = tdbio_write_record( &vr ); if( rc ) { log_error( _("%s: error writing dir record: %s\n"), - db_name, gpg_strerror (rc) ); + db_name, g10_errstr(rc) ); return rc; } /*zero out the new record */ @@ -1430,7 +1423,7 @@ tdbio_new_recnum() rc = tdbio_write_record( &rec ); if( rc ) log_fatal(_("%s: failed to zero a record: %s\n"), - db_name, gpg_strerror (rc)); + db_name, g10_errstr(rc)); } else { /* not found, append a new record */ offset = lseek( db_fd, 0, SEEK_END ); @@ -1460,7 +1453,7 @@ tdbio_new_recnum() if( rc ) log_fatal(_("%s: failed to append a record: %s\n"), - db_name, gpg_strerror (rc)); + db_name, g10_errstr(rc)); } return recnum ; } @@ -1508,129 +1501,3 @@ tdbio_invalid(void) g10_exit(2); } -/* - * Migrate the trustdb as just up to gpg 1.0.6 (trustdb version 2) - * to the 2.1 version as used with 1.0.6b - This is pretty trivial as needs - * only to scan the tdb and insert new the new trust records. The old ones are - * obsolte from now on - */ -static void -migrate_from_v2 () -{ - TRUSTREC rec; - int i, n; - struct { - ulong keyrecno; - byte ot; - byte okay; - byte fpr[20]; - } *ottable; - int ottable_size, ottable_used; - byte oldbuf[40]; - ulong recno; - int rc, count; - - ottable_size = 5; - ottable = xmalloc (ottable_size * sizeof *ottable); - ottable_used = 0; - - /* We have some restrictions here. We can't use the version record - * and we can't use any of the old hashtables because we dropped the - * code. So we first collect all ownertrusts and then use a second - * pass fo find the associated keys. We have to do this all without using - * the regular record read functions. - */ - - /* get all the ownertrusts */ - if (lseek (db_fd, 0, SEEK_SET ) == -1 ) - log_fatal ("migrate_from_v2: lseek failed: %s\n", strerror (errno)); - for (recno=0;;recno++) - { - do - n = read (db_fd, oldbuf, 40); - while (n==-1 && errno == EINTR); - if (!n) - break; /* eof */ - if (n != 40) - log_fatal ("migrate_vfrom_v2: read error or short read\n"); - - if (*oldbuf != 2) - continue; - - /* v2 dir record */ - if (ottable_used == ottable_size) - { - ottable_size += 1000; - ottable = xrealloc (ottable, ottable_size * sizeof *ottable); - } - ottable[ottable_used].keyrecno = buftoulong (oldbuf+6); - ottable[ottable_used].ot = oldbuf[18]; - ottable[ottable_used].okay = 0; - memset (ottable[ottable_used].fpr,0, 20); - if (ottable[ottable_used].keyrecno && ottable[ottable_used].ot) - ottable_used++; - } - log_info ("found %d ownertrust records\n", ottable_used); - - /* Read again and find the fingerprints */ - if (lseek (db_fd, 0, SEEK_SET ) == -1 ) - log_fatal ("migrate_from_v2: lseek failed: %s\n", strerror (errno)); - for (recno=0;;recno++) - { - do - n = read (db_fd, oldbuf, 40); - while (n==-1 && errno == EINTR); - if (!n) - break; /* eof */ - if (n != 40) - log_fatal ("migrate_from_v2: read error or short read\n"); - - if (*oldbuf != 3) - continue; - - /* v2 key record */ - for (i=0; i < ottable_used; i++) - { - if (ottable[i].keyrecno == recno) - { - memcpy (ottable[i].fpr, oldbuf+20, 20); - ottable[i].okay = 1; - break; - } - } - } - - /* got everything - create the v3 trustdb */ - if (ftruncate (db_fd, 0)) - log_fatal ("can't truncate `%s': %s\n", db_name, strerror (errno) ); - if (create_version_record ()) - log_fatal ("failed to recreate version record of `%s'\n", db_name); - - /* access the hash table, so it is store just after the version record, - * this is not needed put a dump is more pretty */ - get_trusthashrec (); - - /* And insert the old ownertrust values */ - count = 0; - for (i=0; i < ottable_used; i++) - { - if (!ottable[i].okay) - continue; - - memset (&rec, 0, sizeof rec); - rec.recnum = tdbio_new_recnum (); - rec.rectype = RECTYPE_TRUST; - memcpy(rec.r.trust.fingerprint, ottable[i].fpr, 20); - rec.r.trust.ownertrust = ottable[i].ot; - if (tdbio_write_record (&rec)) - log_fatal ("failed to write trust record of `%s'\n", db_name); - count++; - } - - revalidation_mark (); - rc = tdbio_sync (); - if (rc) - log_fatal ("failed to sync `%s'\n", db_name); - log_info ("migrated %d version 2 ownertrusts\n", count); - xfree (ottable); -} diff --git a/g10/tdbio.h b/g10/tdbio.h index 708e06d2b..80a70d9f4 100644 --- a/g10/tdbio.h +++ b/g10/tdbio.h @@ -15,7 +15,8 @@ * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. */ #ifndef G10_TDBIO_H diff --git a/g10/textfilter.c b/g10/textfilter.c index a3ea4b138..daa57de0a 100644 --- a/g10/textfilter.c +++ b/g10/textfilter.c @@ -1,5 +1,5 @@ /* textfilter.c - * Copyright (C) 1998, 1999, 2000, 2001 Free Software Foundation, Inc. + * Copyright (C) 1998, 1999, 2000, 2001, 2004 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -15,7 +15,8 @@ * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. */ #include <config.h> @@ -28,11 +29,11 @@ #include "gpg.h" #include "errors.h" #include "iobuf.h" -#include "memory.h" #include "util.h" #include "filter.h" #include "i18n.h" #include "options.h" +#include "status.h" #ifdef HAVE_DOSISH_SYSTEM #define LF "\r\n" @@ -62,17 +63,9 @@ len_without_trailing_chars( byte *line, unsigned len, const char *trimchars ) return mark? (mark - line) : len; } -unsigned -len_without_trailing_ws( byte *line, unsigned len ) -{ - return len_without_trailing_chars( line, len, " \t\r\n" ); -} - - - static int -standard( text_filter_context_t *tfx, iobuf_t a, +standard( text_filter_context_t *tfx, IOBUF a, byte *buf, size_t size, size_t *ret_len) { int rc=0; @@ -102,7 +95,30 @@ standard( text_filter_context_t *tfx, iobuf_t a, break; } lf_seen = tfx->buffer[tfx->buffer_len-1] == '\n'; - tfx->buffer_len = trim_trailing_ws( tfx->buffer, tfx->buffer_len ); + + /* The story behind this is that 2440 says that textmode + hashes should canonicalize line endings to CRLF and remove + spaces and tabs. 2440bis-12 says to just canonicalize to + CRLF. 1.4.0 was released using the bis-12 behavior, but it + was discovered that many mail clients do not canonicalize + PGP/MIME signature text appropriately (and were relying on + GnuPG to handle trailing spaces). So, we default to the + 2440 behavior, but use the 2440bis-12 behavior if the user + specifies --no-rfc2440-text. The default will be changed + at some point in the future when the mail clients have been + upgraded. Aside from PGP/MIME and broken mail clients, + this makes no difference to any signatures in the real + world except for a textmode detached signature. PGP always + used the 2440bis-12 behavior (ignoring 2440 itself), so + this actually makes us compatible with PGP textmode + detached signatures for the first time. */ + if(opt.rfc2440_text) + tfx->buffer_len=trim_trailing_chars(tfx->buffer,tfx->buffer_len, + " \t\r\n"); + else + tfx->buffer_len=trim_trailing_chars(tfx->buffer,tfx->buffer_len, + "\r\n"); + if( lf_seen ) { tfx->buffer[tfx->buffer_len++] = '\r'; tfx->buffer[tfx->buffer_len++] = '\n'; @@ -113,15 +129,13 @@ standard( text_filter_context_t *tfx, iobuf_t a, } - - /**************** * The filter is used to make canonical text: Lines are terminated by * CR, LF, trailing white spaces are removed. */ int text_filter( void *opaque, int control, - iobuf_t a, byte *buf, size_t *ret_len) + IOBUF a, byte *buf, size_t *ret_len) { size_t size = *ret_len; text_filter_context_t *tfx = opaque; @@ -134,7 +148,7 @@ text_filter( void *opaque, int control, if( tfx->truncated ) log_error(_("can't handle text lines longer than %d characters\n"), MAX_LINELEN ); - xfree ( tfx->buffer ); + xfree( tfx->buffer ); tfx->buffer = NULL; } else if( control == IOBUFCTRL_DESC ) @@ -148,13 +162,13 @@ text_filter( void *opaque, int control, * md is updated as required by rfc2440 */ int -copy_clearsig_text( iobuf_t out, iobuf_t inp, MD_HANDLE md, +copy_clearsig_text( IOBUF out, IOBUF inp, gcry_md_hd_t md, int escape_dash, int escape_from, int pgp2mode ) { - unsigned maxlen; + unsigned int maxlen; byte *buffer = NULL; /* malloced buffer */ - unsigned bufsize; /* and size of this buffer */ - unsigned n; + unsigned int bufsize; /* and size of this buffer */ + unsigned int n; int truncated = 0; int pending_lf = 0; @@ -164,6 +178,8 @@ copy_clearsig_text( iobuf_t out, iobuf_t inp, MD_HANDLE md, if( !escape_dash ) escape_from = 0; + write_status (STATUS_BEGIN_SIGNING); + for(;;) { maxlen = MAX_LINELEN; n = iobuf_read_line( inp, &buffer, &bufsize, &maxlen ); @@ -176,15 +192,16 @@ copy_clearsig_text( iobuf_t out, iobuf_t inp, MD_HANDLE md, /* update the message digest */ if( escape_dash ) { if( pending_lf ) { - gcry_md_putc( md, '\r' ); - gcry_md_putc( md, '\n' ); + gcry_md_putc ( md, '\r' ); + gcry_md_putc ( md, '\n' ); } - gcry_md_write( md, buffer, - len_without_trailing_chars( buffer, n, - pgp2mode? " \r\n":" \t\r\n")); + gcry_md_write ( md, buffer, + len_without_trailing_chars (buffer, n, + pgp2mode? + " \r\n":" \t\r\n")); } else - gcry_md_write( md, buffer, n ); + gcry_md_write ( md, buffer, n ); pending_lf = buffer[n-1] == '\n'; /* write the output */ diff --git a/g10/trustdb.c b/g10/trustdb.c index b3a2b369e..573c12903 100644 --- a/g10/trustdb.c +++ b/g10/trustdb.c @@ -1,6 +1,6 @@ /* trustdb.c - * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003 - * Free Software Foundation, Inc. + * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, + * 2005 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -16,7 +16,8 @@ * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. */ #include <config.h> @@ -27,7 +28,7 @@ #ifndef DISABLE_REGEX #include <sys/types.h> -#ifdef USE_GNU_REGEX +#ifdef USE_INTERNAL_REGEX #include "_regex.h" #else #include <regex.h> @@ -38,7 +39,6 @@ #include "errors.h" #include "iobuf.h" #include "keydb.h" -#include "memory.h" #include "util.h" #include "options.h" #include "packet.h" @@ -99,7 +99,7 @@ new_key_item (void) { struct key_item *k; - k = xcalloc (1,sizeof *k); + k = xmalloc_clear (sizeof *k); return k; } @@ -129,7 +129,7 @@ new_key_hash_table (void) { struct key_item **tbl; - tbl = xcalloc (1,1024 * sizeof *tbl); + tbl = xmalloc_clear (1024 * sizeof *tbl); return tbl; } @@ -206,23 +206,31 @@ release_key_array ( struct key_array *keys ) * FIXME: Should be replaced by a function to add those keys to the trustdb. */ void -register_trusted_key( const char *string ) +register_trusted_keyid(u32 *keyid) { - KEYDB_SEARCH_DESC desc; struct key_item *k; - if (classify_user_id (string, &desc) != KEYDB_SEARCH_MODE_LONG_KID ) { - log_error(_("`%s' is not a valid long keyID\n"), string ); - return; - } - k = new_key_item (); - k->kid[0] = desc.u.kid[0]; - k->kid[1] = desc.u.kid[1]; + k->kid[0] = keyid[0]; + k->kid[1] = keyid[1]; k->next = user_utk_list; user_utk_list = k; } +void +register_trusted_key( const char *string ) +{ + KEYDB_SEARCH_DESC desc; + + if (classify_user_id (string, &desc) != KEYDB_SEARCH_MODE_LONG_KID ) + { + log_error(_("`%s' is not a valid long keyID\n"), string ); + return; + } + + register_trusted_keyid(desc.u.kid); +} + /* * Helper to add a key to the global list of ultimately trusted keys. * Retruns: true = inserted, false = already in in list. @@ -247,7 +255,7 @@ add_utk (u32 *kid) k->next = utk_list; utk_list = k; if( opt.verbose > 1 ) - log_info(_("key %08lX: accepted as trusted key\n"), (ulong)kid[1]); + log_info(_("key %s: accepted as trusted key\n"), keystr(kid)); return 1; } @@ -285,8 +293,8 @@ verify_own_keys(void) fprlen = (!fpr[16] && !fpr[17] && !fpr[18] && !fpr[19])? 16:20; keyid_from_fingerprint (fpr, fprlen, kid); if (!add_utk (kid)) - log_info(_("key %08lX occurs more than once in the trustdb\n"), - (ulong)kid[1]); + log_info(_("key %s occurs more than once in the trustdb\n"), + keystr(kid)); } } @@ -299,22 +307,21 @@ verify_own_keys(void) memset (&pk, 0, sizeof pk); rc = get_pubkey (&pk, k->kid); - if (rc) { - log_info(_("key %08lX: no public key for trusted key - skipped\n"), - (ulong)k->kid[1] ); - } - else { - update_ownertrust (&pk, - ((get_ownertrust (&pk) & ~TRUST_MASK) - | TRUST_ULTIMATE )); - release_public_key_parts (&pk); - } - log_info (_("key %08lX marked as ultimately trusted\n"), - (ulong)k->kid[1]); + if (rc) + log_info(_("key %s: no public key for trusted key - skipped\n"), + keystr(k->kid)); + else + { + update_ownertrust (&pk, + ((get_ownertrust (&pk) & ~TRUST_MASK) + | TRUST_ULTIMATE )); + release_public_key_parts (&pk); + } + + log_info (_("key %s marked as ultimately trusted\n"),keystr(k->kid)); } } - /* release the helper table table */ release_key_items (user_utk_list); user_utk_list = NULL; @@ -336,7 +343,7 @@ read_record (ulong recno, TRUSTREC *rec, int rectype ) if (rc) { log_error(_("trust record %lu, req type %d: read failed: %s\n"), - recno, rec->rectype, gpg_strerror (rc) ); + recno, rec->rectype, g10_errstr(rc) ); tdbio_invalid(); } if (rectype != rec->rectype) @@ -357,7 +364,7 @@ write_record (TRUSTREC *rec) if (rc) { log_error(_("trust record %lu, type %d: write failed: %s\n"), - rec->recnum, rec->rectype, gpg_strerror (rc) ); + rec->recnum, rec->rectype, g10_errstr(rc) ); tdbio_invalid(); } } @@ -371,7 +378,7 @@ do_sync(void) int rc = tdbio_sync (); if(rc) { - log_error (_("trustdb: sync failed: %s\n"), gpg_strerror (rc) ); + log_error (_("trustdb: sync failed: %s\n"), g10_errstr(rc) ); g10_exit(2); } } @@ -381,10 +388,12 @@ trust_model_string(void) { switch(opt.trust_model) { - case TM_PGP: return "PGP"; - case TM_CLASSIC: return "classic"; - case TM_ALWAYS: return "always"; - default: return "unknown"; + case TM_CLASSIC: return "classic"; + case TM_PGP: return "PGP"; + case TM_EXTERNAL: return "external"; + case TM_ALWAYS: return "always"; + case TM_DIRECT: return "direct"; + default: return "unknown"; } } @@ -400,14 +409,13 @@ setup_trustdb( int level, const char *dbname ) if( trustdb_args.init ) return 0; trustdb_args.level = level; - trustdb_args.dbname = dbname? xstrdup (dbname): NULL; + trustdb_args.dbname = dbname? xstrdup(dbname): NULL; return 0; } void init_trustdb() { - int rc=0; int level = trustdb_args.level; const char* dbname = trustdb_args.dbname; @@ -416,26 +424,14 @@ init_trustdb() trustdb_args.init = 1; - if ( !level || level==1) + if(level==0 || level==1) { - rc = tdbio_set_dbname( dbname, !!level ); - if( !rc ) - { - if( !level ) - return; - - /* verify that our own keys are in the trustDB - * or move them to the trustdb. */ - verify_own_keys(); - - /* should we check whether there is no other ultimately trusted - * key in the database? */ - } + int rc = tdbio_set_dbname( dbname, !!level ); + if( rc ) + log_fatal("can't init trustdb: %s\n", g10_errstr(rc) ); } else BUG(); - if( rc ) - log_fatal("can't init trustdb: %s\n", gpg_strerror (rc) ); if(opt.trust_model==TM_AUTO) { @@ -444,7 +440,9 @@ init_trustdb() opt.trust_model=tdbio_read_model(); /* Sanity check this ;) */ - if(opt.trust_model!=TM_PGP && opt.trust_model!=TM_CLASSIC) + if(opt.trust_model!=TM_CLASSIC + && opt.trust_model!=TM_PGP + && opt.trust_model!=TM_EXTERNAL) { log_info(_("unable to use unknown trust model (%d) - " "assuming %s trust model\n"),opt.trust_model,"PGP"); @@ -455,14 +453,19 @@ init_trustdb() log_info(_("using %s trust model\n"),trust_model_string()); } - if((opt.trust_model==TM_PGP || opt.trust_model==TM_CLASSIC) - && !tdbio_db_matches_options()) - pending_check_trustdb=1; -} + if(opt.trust_model==TM_PGP || opt.trust_model==TM_CLASSIC) + { + /* Verify the list of ultimately trusted keys and move the + --trusted-keys list there as well. */ + if(level==1) + verify_own_keys(); + if(!tdbio_db_matches_options()) + pending_check_trustdb=1; + } +} - /*********************************************** ************* Print helpers **************** ***********************************************/ @@ -487,6 +490,37 @@ trust_letter (unsigned int value) } } +/* NOTE TO TRANSLATOR: these strings are similar to those in + trust_value_to_string(), but are a fixed length. This is needed to + make attractive information listings where columns line up + properly. The value "10" should be the length of the strings you + choose to translate to. This is the length in printable columns. + It gets passed to atoi() so everything after the number is + essentially a comment and need not be translated. Either key and + uid are both NULL, or neither are NULL. */ +const char * +uid_trust_string_fixed(PKT_public_key *key,PKT_user_id *uid) +{ + if(!key && !uid) + return _("10 translator see trustdb.c:uid_trust_string_fixed"); + else if(uid->is_revoked || (key && key->is_revoked)) + return _("[ revoked]"); + else if(uid->is_expired) + return _("[ expired]"); + else if(key) + switch(get_validity(key,uid)&TRUST_MASK) + { + case TRUST_UNKNOWN: return _("[ unknown]"); + case TRUST_EXPIRED: return _("[ expired]"); + case TRUST_UNDEFINED: return _("[ undef ]"); + case TRUST_MARGINAL: return _("[marginal]"); + case TRUST_FULLY: return _("[ full ]"); + case TRUST_ULTIMATE: return _("[ultimate]"); + } + + return "err"; +} + /* The strings here are similar to those in pkclist.c:do_edit_ownertrust() */ const char * @@ -555,7 +589,7 @@ check_trustdb () validate_keys (0); } else - log_info (_("no need for a trustdb check with \"%s\" trust model\n"), + log_info (_("no need for a trustdb check with `%s' trust model\n"), trust_model_string()); } @@ -570,7 +604,7 @@ update_trustdb() if(opt.trust_model==TM_PGP || opt.trust_model==TM_CLASSIC) validate_keys (1); else - log_info (_("no need for a trustdb update with \"%s\" trust model\n"), + log_info (_("no need for a trustdb update with `%s' trust model\n"), trust_model_string()); } @@ -591,6 +625,20 @@ trustdb_pending_check(void) return pending_check_trustdb; } +/* If the trustdb is dirty, and we're interactive, update it. + Otherwise, check it unless no-auto-check-trustdb is set. */ +void +trustdb_check_or_update(void) +{ + if(trustdb_pending_check()) + { + if(opt.interactive) + update_trustdb(); + else if(!opt.no_auto_check_trustdb) + check_trustdb(); + } +} + void read_trust_options(byte *trust_model,ulong *created,ulong *nextcheck, byte *marginals,byte *completes,byte *cert_depth) @@ -615,8 +663,6 @@ read_trust_options(byte *trust_model,ulong *created,ulong *nextcheck, *cert_depth=opts.r.ver.cert_depth; } - - /*********************************************** *********** Ownertrust et al. **************** ***********************************************/ @@ -633,7 +679,7 @@ read_trust_record (PKT_public_key *pk, TRUSTREC *rec) if (rc) { log_error ("trustdb: searching trust record failed: %s\n", - gpg_strerror (rc)); + g10_errstr (rc)); return rc; } @@ -641,7 +687,7 @@ read_trust_record (PKT_public_key *pk, TRUSTREC *rec) { log_error ("trustdb: record %lu is not a trust record\n", rec->recnum); - return GPG_ERR_TRUSTDB; + return G10ERR_TRUSTDB; } return 0; @@ -786,12 +832,11 @@ update_min_ownertrust (u32 *kid, unsigned int new_trust ) TRUSTREC rec; int rc; - pk = xcalloc (1,sizeof *pk); + pk = xmalloc_clear (sizeof *pk); rc = get_pubkey (pk, kid); if (rc) { - log_error (_("public key %08lX not found: %s\n"), - (ulong)kid[1], gpg_strerror (rc) ); + log_error(_("public key %s not found: %s\n"),keystr(kid),g10_errstr(rc)); return; } @@ -799,8 +844,9 @@ update_min_ownertrust (u32 *kid, unsigned int new_trust ) if (!rc) { if (DBG_TRUST) - log_debug ("key %08lX: update min_ownertrust from %u to %u\n", - (ulong)kid[1],(unsigned int)rec.r.trust.min_ownertrust, + log_debug ("key %08lX%08lX: update min_ownertrust from %u to %u\n", + (ulong)kid[0],(ulong)kid[1], + (unsigned int)rec.r.trust.min_ownertrust, new_trust ); if (rec.r.trust.min_ownertrust != new_trust) { @@ -927,49 +973,6 @@ update_validity (PKT_public_key *pk, PKT_user_id *uid, } -/* reset validity for all user IDs. Caller must sync. */ -static int -clear_validity (PKT_public_key *pk) -{ - TRUSTREC trec, vrec; - int rc; - ulong recno; - int any = 0; - - rc = read_trust_record (pk, &trec); - if (rc && rc != -1) - { - tdbio_invalid (); - return 0; - } - if (rc == -1) /* no record yet - no need to clear it then ;-) */ - return 0; - - /* Clear minimum ownertrust, if any */ - if(trec.r.trust.min_ownertrust) - { - trec.r.trust.min_ownertrust=0; - write_record(&trec); - } - - recno = trec.r.trust.validlist; - while (recno) - { - read_record (recno, &vrec, RECTYPE_VALID); - if ((vrec.r.valid.validity & TRUST_MASK) - || vrec.r.valid.marginal_count || vrec.r.valid.full_count) - { - vrec.r.valid.validity &= ~TRUST_MASK; - vrec.r.valid.marginal_count = vrec.r.valid.full_count = 0; - write_record (&vrec); - any = 1; - } - recno = vrec.r.valid.next; - } - - return any; -} - /*********************************************** ********* Query trustdb values ************** ***********************************************/ @@ -1010,24 +1013,10 @@ cache_disabled_value(PKT_public_key *pk) return disabled; } -/* - * Return the validity information for PK. If the namehash is not - * NULL, the validity of the corresponsing user ID is returned, - * otherwise, a reasonable value for the entire key is returned. - */ -unsigned int -get_validity (PKT_public_key *pk, PKT_user_id *uid) +void +check_trustdb_stale(void) { - static int did_nextcheck; - TRUSTREC trec, vrec; - int rc; - ulong recno; - unsigned int validity; - u32 kid[2]; - PKT_public_key *main_pk; - - if(uid) - namehash_from_uid(uid); + static int did_nextcheck=0; init_trustdb (); if (!did_nextcheck @@ -1051,16 +1040,40 @@ get_validity (PKT_public_key *pk, PKT_user_id *uid) } } } +} + +/* + * Return the validity information for PK. If the namehash is not + * NULL, the validity of the corresponsing user ID is returned, + * otherwise, a reasonable value for the entire key is returned. + */ +unsigned int +get_validity (PKT_public_key *pk, PKT_user_id *uid) +{ + TRUSTREC trec, vrec; + int rc; + ulong recno; + unsigned int validity; + u32 kid[2]; + PKT_public_key *main_pk; + + if(uid) + namehash_from_uid(uid); + + init_trustdb (); + check_trustdb_stale(); keyid_from_pk (pk, kid); if (pk->main_keyid[0] != kid[0] || pk->main_keyid[1] != kid[1]) { /* this is a subkey - get the mainkey */ - main_pk = xcalloc (1,sizeof *main_pk); + main_pk = xmalloc_clear (sizeof *main_pk); rc = get_pubkey (main_pk, pk->main_keyid); if (rc) { - log_error ("error getting main key %08lX of subkey %08lX: %s\n", - (ulong)pk->main_keyid[1], (ulong)kid[1], gpg_strerror (rc)); + char *tempkeystr=xstrdup(keystr(pk->main_keyid)); + log_error ("error getting main key %s of subkey %s: %s\n", + tempkeystr, keystr(kid), g10_errstr(rc)); + xfree(tempkeystr); validity = TRUST_UNKNOWN; goto leave; } @@ -1068,6 +1081,14 @@ get_validity (PKT_public_key *pk, PKT_user_id *uid) else main_pk = pk; + if(opt.trust_model==TM_DIRECT) + { + /* Note that this happens BEFORE any user ID stuff is checked. + The direct trust model applies to keys as a whole. */ + validity=get_ownertrust(main_pk); + goto leave; + } + rc = read_trust_record (main_pk, &trec); if (rc && rc != -1) { @@ -1249,20 +1270,19 @@ ask_ownertrust (u32 *kid,int minimum) int rc; int ot; - pk = xcalloc (1,sizeof *pk); + pk = xmalloc_clear (sizeof *pk); rc = get_pubkey (pk, kid); if (rc) { - log_error (_("public key %08lX not found: %s\n"), - (ulong)kid[1], gpg_strerror (rc) ); + log_error (_("public key %s not found: %s\n"), + keystr(kid), g10_errstr(rc) ); return TRUST_UNKNOWN; } if(opt.force_ownertrust) { - log_info("force trust for key %08lX%08lX to %s\n", - (ulong)kid[0],(ulong)kid[1], - trust_value_to_string(opt.force_ownertrust)); + log_info("force trust for key %s to %s\n", + keystr(kid),trust_value_to_string(opt.force_ownertrust)); update_ownertrust(pk,opt.force_ownertrust); ot=opt.force_ownertrust; } @@ -1390,8 +1410,9 @@ is_in_klist (struct key_item *k, PKT_signature *sig) * To do this, we first revmove all signatures which are not valid and * from the remain ones we look for the latest one. If this is not a * certification revocation signature we mark the signature by setting - * node flag bit 8. Note that flag bits 9 and 10 are used for internal - * purposes. + * node flag bit 8. Revocations are marked with flag 11, and sigs + * from unavailable keys are marked with flag 12. Note that flag bits + * 9 and 10 are used for internal purposes. */ static void mark_usable_uid_certs (KBNODE keyblock, KBNODE uidnode, @@ -1404,31 +1425,44 @@ mark_usable_uid_certs (KBNODE keyblock, KBNODE uidnode, /* first check all signatures */ for (node=uidnode->next; node; node = node->next) { - node->flag &= ~(1<<8 | 1<<9 | 1<<10); + int rc; + + node->flag &= ~(1<<8 | 1<<9 | 1<<10 | 1<<11 | 1<<12); if (node->pkt->pkttype == PKT_USER_ID || node->pkt->pkttype == PKT_PUBLIC_SUBKEY) break; /* ready */ if (node->pkt->pkttype != PKT_SIGNATURE) continue; - sig = node->pkt->pkt.signature; - if (sig->keyid[0] == main_kid[0] && sig->keyid[1] == main_kid[1]) - continue; /* ignore self-signatures */ + if (main_kid + && sig->keyid[0] == main_kid[0] && sig->keyid[1] == main_kid[1]) + continue; /* ignore self-signatures if we pass in a main_kid */ if (!IS_UID_SIG(sig) && !IS_UID_REV(sig)) continue; /* we only look at these signature classes */ - if (!is_in_klist (klist, sig)) + if(sig->sig_class>=0x11 && sig->sig_class<=0x13 && + sig->sig_class-0x10<opt.min_cert_level) + continue; /* treat anything under our min_cert_level as an + invalid signature */ + if (klist && !is_in_klist (klist, sig)) continue; /* no need to check it then */ - if (check_key_signature (keyblock, node, NULL)) - continue; /* ignore invalid signatures */ + if ((rc=check_key_signature (keyblock, node, NULL))) + { + /* we ignore anything that won't verify, but tag the + no_pubkey case */ + if(rc==G10ERR_NO_PUBKEY) + node->flag |= 1<<12; + continue; + } node->flag |= 1<<9; } /* reset the remaining flags */ for (; node; node = node->next) - node->flag &= ~(1<<8 | 1<<9 | 1 << 10); + node->flag &= ~(1<<8 | 1<<9 | 1<<10 | 1<<11 | 1<<12); /* kbnode flag usage: bit 9 is here set for signatures to consider, * bit 10 will be set by the loop to keep track of keyIDs already - * processed, bit 8 will be set for the usable signatures */ + * processed, bit 8 will be set for the usable signatures, and bit + * 11 will be set for usable revocations. */ /* for each cert figure out the latest valid one */ for (node=uidnode->next; node; node = node->next) @@ -1436,7 +1470,7 @@ mark_usable_uid_certs (KBNODE keyblock, KBNODE uidnode, KBNODE n, signode; u32 kid[2]; u32 sigdate; - + if (node->pkt->pkttype == PKT_PUBLIC_SUBKEY) break; if ( !(node->flag & (1<<9)) ) @@ -1448,6 +1482,8 @@ mark_usable_uid_certs (KBNODE keyblock, KBNODE uidnode, signode = node; sigdate = sig->timestamp; kid[0] = sig->keyid[0]; kid[1] = sig->keyid[1]; + + /* Now find the latest and greatest signature */ for (n=uidnode->next; n; n = n->next) { if (n->pkt->pkttype == PKT_PUBLIC_SUBKEY) @@ -1510,6 +1546,7 @@ mark_usable_uid_certs (KBNODE keyblock, KBNODE uidnode, sigdate = sig->timestamp; } } + sig = signode->pkt->pkt.signature; if (IS_UID_SIG (sig)) { /* this seems to be a usable one which is not revoked. @@ -1528,11 +1565,190 @@ mark_usable_uid_certs (KBNODE keyblock, KBNODE uidnode, if (expire==0 || expire > curtime ) { signode->flag |= (1<<8); /* yeah, found a good cert */ - if (expire && expire < *next_expire) + if (next_expire && expire && expire < *next_expire) *next_expire = expire; } } + else + signode->flag |= (1<<11); + } +} + +static int +clean_sigs_from_uid(KBNODE keyblock,KBNODE uidnode,int noisy,int self_only) +{ + int deleted=0; + KBNODE node; + u32 keyid[2]; + + assert(keyblock->pkt->pkttype==PKT_PUBLIC_KEY); + + keyid_from_pk(keyblock->pkt->pkt.public_key,keyid); + + /* Passing in a 0 for current time here means that we'll never weed + out an expired sig. This is correct behavior since we want to + keep the most recent expired sig in a series. */ + mark_usable_uid_certs(keyblock,uidnode,NULL,NULL,0,NULL); + + /* What we want to do here is remove signatures that are not + considered as part of the trust calculations. Thus, all invalid + signatures are out, as are any signatures that aren't the last of + a series of uid sigs or revocations It breaks down like this: + coming out of mark_usable_uid_certs, if a sig is unflagged, it is + not even a candidate. If a sig has flag 9 or 10, that means it + was selected as a candidate and vetted. If a sig has flag 8 it + is a usable signature. If a sig has flag 11 it is a usable + revocation. If a sig has flag 12 it was issued by an unavailable + key. "Usable" here means the most recent valid + signature/revocation in a series from a particular signer. + + Delete everything that isn't a usable uid sig (which might be + expired), a usable revocation, or a sig from an unavailable + key. */ + + for(node=uidnode->next; + node && node->pkt->pkttype==PKT_SIGNATURE; + node=node->next) + { + int keep=self_only?(node->pkt->pkt.signature->keyid[0]==keyid[0] + && node->pkt->pkt.signature->keyid[1]==keyid[1]):1; + + /* Keep usable uid sigs ... */ + if((node->flag & (1<<8)) && keep) + continue; + + /* ... and usable revocations... */ + if((node->flag & (1<<11)) && keep) + continue; + + /* ... and sigs from unavailable keys. */ + /* disabled for now since more people seem to want sigs from + unavailable keys removed altogether. */ + /* + if(node->flag & (1<<12)) + continue; + */ + + /* Everything else we delete */ + + /* At this point, if 12 is set, the signing key was unavailable. + If 9 or 10 is set, it's superceded. Otherwise, it's + invalid. */ + + if(noisy) + log_info("removing signature from key %s on user ID \"%s\": %s\n", + keystr(node->pkt->pkt.signature->keyid), + uidnode->pkt->pkt.user_id->name, + node->flag&(1<<12)?"key unavailable": + node->flag&(1<<9)?"signature superceded":"invalid signature"); + + delete_kbnode(node); + deleted++; + } + + return deleted; +} + +/* This is substantially easier than clean_sigs_from_uid since we just + have to establish if the uid has a valid self-sig, is not revoked, + and is not expired. Note that this does not take into account + whether the uid has a trust path to it - just whether the keyholder + themselves has certified the uid. Returns true if the uid was + compacted. To "compact" a user ID, we simply remove ALL signatures + except the self-sig that caused the user ID to be remove-worthy. + We don't actually remove the user ID packet itself since it might + be ressurected in a later merge. Note that this function requires + that the caller has already done a merge_keys_and_selfsig(). + + TODO: change the import code to allow importing a uid with only a + revocation if the uid already exists on the keyring. */ + +static int +clean_uid_from_key(KBNODE keyblock,KBNODE uidnode,int noisy) +{ + KBNODE node; + PKT_user_id *uid=uidnode->pkt->pkt.user_id; + int deleted=0; + + assert(keyblock->pkt->pkttype==PKT_PUBLIC_KEY); + assert(uidnode->pkt->pkttype==PKT_USER_ID); + + /* Skip valid user IDs, compacted user IDs, and non-self-signed user + IDs if --allow-non-selfsigned-uid is set. */ + if(uid->created || uid->flags.compacted + || (!uid->is_expired && !uid->is_revoked + && opt.allow_non_selfsigned_uid)) + return 0; + + for(node=uidnode->next; + node && node->pkt->pkttype==PKT_SIGNATURE; + node=node->next) + if(!node->pkt->pkt.signature->flags.chosen_selfsig) + { + delete_kbnode(node); + deleted=1; + uidnode->pkt->pkt.user_id->flags.compacted=1; + } + + if(noisy) + { + const char *reason; + char *user=utf8_to_native(uid->name,uid->len,0); + + if(uid->is_revoked) + reason=_("revoked"); + else if(uid->is_expired) + reason=_("expired"); + else + reason=_("invalid"); + + log_info("compacting user ID \"%s\" on key %s: %s\n", + user,keystr_from_pk(keyblock->pkt->pkt.public_key), + reason); + + xfree(user); } + + return deleted; +} + +/* Needs to be called after a merge_keys_and_selfsig() */ +void +clean_one_uid(KBNODE keyblock,KBNODE uidnode,int noisy,int self_only, + int *uids_cleaned,int *sigs_cleaned) +{ + int dummy; + + assert(keyblock->pkt->pkttype==PKT_PUBLIC_KEY); + assert(uidnode->pkt->pkttype==PKT_USER_ID); + + if(!uids_cleaned) + uids_cleaned=&dummy; + + if(!sigs_cleaned) + sigs_cleaned=&dummy; + + /* Do clean_uid_from_key first since if it fires off, we don't + have to bother with the other */ + *uids_cleaned+=clean_uid_from_key(keyblock,uidnode,noisy); + if(!uidnode->pkt->pkt.user_id->flags.compacted) + *sigs_cleaned+=clean_sigs_from_uid(keyblock,uidnode,noisy,self_only); +} + +void +clean_key(KBNODE keyblock,int noisy,int self_only, + int *uids_cleaned,int *sigs_cleaned) +{ + KBNODE uidnode; + + merge_keys_and_selfsig(keyblock); + + for(uidnode=keyblock->next; + uidnode && uidnode->pkt->pkttype!=PKT_PUBLIC_SUBKEY; + uidnode=uidnode->next) + if(uidnode->pkt->pkttype==PKT_USER_ID) + clean_one_uid(keyblock,uidnode,noisy,self_only, + uids_cleaned,sigs_cleaned); } /* Used by validate_one_keyblock to confirm a regexp within a trust @@ -1559,7 +1775,7 @@ check_regexp(const char *expr,const char *string) regfree(&pat); if(DBG_TRUST) - log_debug("regexp \"%s\" on \"%s\": %s\n",expr,string,ret==0?"YES":"NO"); + log_debug("regexp `%s' on `%s': %s\n",expr,string,ret==0?"YES":"NO"); return (ret==0); #endif @@ -1717,7 +1933,7 @@ validate_one_keyblock (KBNODE kb, struct key_item *klist, static int -search_skipfnc (void *opaque, u32 *kid) +search_skipfnc (void *opaque, u32 *kid, PKT_user_id *dummy) { return test_key_hash_table ((KeyHashTable)opaque, kid); } @@ -1747,7 +1963,7 @@ validate_key_list (KEYDB_HANDLE hd, KeyHashTable full_trust, rc = keydb_search_reset (hd); if (rc) { - log_error ("keydb_search_reset failed: %s\n", gpg_strerror (rc)); + log_error ("keydb_search_reset failed: %s\n", g10_errstr(rc)); xfree (keys); return NULL; } @@ -1764,7 +1980,7 @@ validate_key_list (KEYDB_HANDLE hd, KeyHashTable full_trust, } if (rc) { - log_error ("keydb_search_first failed: %s\n", gpg_strerror (rc)); + log_error ("keydb_search_first failed: %s\n", g10_errstr(rc)); xfree (keys); return NULL; } @@ -1777,7 +1993,7 @@ validate_key_list (KEYDB_HANDLE hd, KeyHashTable full_trust, rc = keydb_get_keyblock (hd, &keyblock); if (rc) { - log_error ("keydb_get_keyblock failed: %s\n", gpg_strerror (rc)); + log_error ("keydb_get_keyblock failed: %s\n", g10_errstr(rc)); xfree (keys); return NULL; } @@ -1833,7 +2049,7 @@ validate_key_list (KEYDB_HANDLE hd, KeyHashTable full_trust, while ( !(rc = keydb_search (hd, &desc, 1)) ); if (rc && rc != -1) { - log_error ("keydb_search_next failed: %s\n", gpg_strerror (rc)); + log_error ("keydb_search_next failed: %s\n", g10_errstr(rc)); xfree (keys); return NULL; } @@ -1844,56 +2060,40 @@ validate_key_list (KEYDB_HANDLE hd, KeyHashTable full_trust, /* Caller must sync */ static void -reset_trust_records (KEYDB_HANDLE hd, KeyHashTable exclude) +reset_trust_records(void) { - int rc; - KBNODE keyblock = NULL; - KEYDB_SEARCH_DESC desc; + TRUSTREC rec; + ulong recnum; int count = 0, nreset = 0; - - rc = keydb_search_reset (hd); - if (rc) - { - log_error ("keydb_search_reset failed: %s\n", gpg_strerror (rc)); - return; - } - memset (&desc, 0, sizeof desc); - desc.mode = KEYDB_SEARCH_MODE_FIRST; - if(exclude) - { - desc.skipfnc = search_skipfnc; - desc.skipfncvalue = exclude; - } - rc = keydb_search (hd, &desc, 1); - if (rc && rc != -1 ) - log_error ("keydb_search_first failed: %s\n", gpg_strerror (rc)); - else if (!rc) + for (recnum=1; !tdbio_read_record (recnum, &rec, 0); recnum++ ) { - desc.mode = KEYDB_SEARCH_MODE_NEXT; /* change mode */ - do - { - rc = keydb_get_keyblock (hd, &keyblock); - if (rc) - { - log_error ("keydb_get_keyblock failed: %s\n", gpg_strerror (rc)); - break; - } - count++; + if(rec.rectype==RECTYPE_TRUST) + { + count++; + if(rec.r.trust.min_ownertrust) + { + rec.r.trust.min_ownertrust=0; + write_record(&rec); + } + + } + else if(rec.rectype==RECTYPE_VALID + && ((rec.r.valid.validity&TRUST_MASK) + || rec.r.valid.marginal_count + || rec.r.valid.full_count)) + { + rec.r.valid.validity &= ~TRUST_MASK; + rec.r.valid.marginal_count=rec.r.valid.full_count=0; + nreset++; + write_record(&rec); + } - if (keyblock->pkt->pkttype == PKT_PUBLIC_KEY) /* paranoid assertion*/ - { - nreset += clear_validity (keyblock->pkt->pkt.public_key); - release_kbnode (keyblock); - } - } - while ( !(rc = keydb_search (hd, &desc, 1)) ); - if (rc && rc != -1) - log_error ("keydb_search_next failed: %s\n", gpg_strerror (rc)); } + if (opt.verbose) log_info (_("%d keys processed (%d validity counts cleared)\n"), - count, nreset); + count, nreset); } /* @@ -1932,28 +2132,35 @@ validate_keys (int interactive) KEYDB_HANDLE kdb = NULL; KBNODE node; int depth; - int key_count; int ot_unknown, ot_undefined, ot_never, ot_marginal, ot_full, ot_ultimate; KeyHashTable stored,used,full_trust; u32 start_time, next_expire; + /* Make sure we have all sigs cached. TODO: This is going to + require some architectual re-thinking, as it is agonizingly slow. + Perhaps combine this with reset_trust_records(), or only check + the caches on keys that are actually involved in the web of + trust. */ + keydb_rebuild_caches(0); + start_time = make_timestamp (); next_expire = 0xffffffff; /* set next expire to the year 2106 */ stored = new_key_hash_table (); used = new_key_hash_table (); full_trust = new_key_hash_table (); + + kdb = keydb_new (0); + reset_trust_records(); + /* Fixme: Instead of always building a UTK list, we could just build it * here when needed */ if (!utk_list) { - log_info (_("no ultimately trusted keys found\n")); + if (!opt.quiet) + log_info (_("no ultimately trusted keys found\n")); goto leave; } - kdb = keydb_new (0); - - reset_trust_records (kdb,NULL); - /* mark all UTKs as used and fully_trusted and set validity to ultimate */ for (k=utk_list; k; k = k->next) @@ -1965,7 +2172,7 @@ validate_keys (int interactive) if (!keyblock) { log_error (_("public key of ultimately" - " trusted key %08lX not found\n"), (ulong)k->kid[1]); + " trusted key %s not found\n"), keystr(k->kid)); continue; } mark_keyblock_seen (used, keyblock); @@ -1992,8 +2199,9 @@ validate_keys (int interactive) for (depth=0; depth < opt.max_cert_depth; depth++) { + int valids=0,key_count; /* See whether we should assign ownertrust values to the keys in - utk_list. */ + klist. */ ot_unknown = ot_undefined = ot_never = 0; ot_marginal = ot_full = ot_ultimate = 0; for (k=klist; k; k = k->next) @@ -2027,9 +2235,9 @@ validate_keys (int interactive) if(k->ownertrust<min) { if(DBG_TRUST) - log_debug("key %08lX: " - "overriding ownertrust \"%s\" with \"%s\"\n", - (ulong)k->kid[1], + log_debug("key %08lX%08lX:" + " overriding ownertrust `%s' with `%s'\n", + (ulong)k->kid[0],(ulong)k->kid[1], trust_value_to_string(k->ownertrust), trust_value_to_string(min)); @@ -2048,6 +2256,8 @@ validate_keys (int interactive) ot_full++; else if (k->ownertrust == TRUST_ULTIMATE) ot_ultimate++; + + valids++; } /* Find all keys which are signed by a key in kdlist */ @@ -2056,7 +2266,7 @@ validate_keys (int interactive) if (!keys) { log_error ("validate_key_list failed\n"); - rc = GPG_ERR_GENERAL; + rc = G10ERR_GENERAL; goto leave; } @@ -2070,9 +2280,9 @@ validate_keys (int interactive) for (kar=keys; kar->keyblock; kar++) store_validation_status (depth, kar->keyblock, stored); - log_info (_("checking at depth %d valid=%d" - " ot(-/q/n/m/f/u)=%d/%d/%d/%d/%d/%d\n"), - depth, key_count, ot_unknown, ot_undefined, + log_info (_("depth: %d valid: %3d signed: %3d" + " trust: %d-, %dq, %dn, %dm, %df, %du\n"), + depth, valids, key_count, ot_unknown, ot_undefined, ot_never, ot_marginal, ot_full, ot_ultimate ); /* Build a new kdlist from all fully valid keys in KEYS */ @@ -2110,7 +2320,7 @@ validate_keys (int interactive) kar->keyblock->pkt->pkt.public_key->trust_value; if(kar->keyblock->pkt->pkt.public_key->trust_regexp) k->trust_regexp= - xstrdup (kar->keyblock->pkt-> + xstrdup(kar->keyblock->pkt-> pkt.public_key->trust_regexp); k->next = klist; klist = k; @@ -2146,7 +2356,7 @@ validate_keys (int interactive) if(tdbio_update_version_record()!=0) { log_error(_("unable to update trustdb version record: " - "write failed: %s\n"), gpg_strerror (rc)); + "write failed: %s\n"), g10_errstr(rc)); tdbio_invalid(); } diff --git a/g10/trustdb.h b/g10/trustdb.h index 414c37702..2d0581f9b 100644 --- a/g10/trustdb.h +++ b/g10/trustdb.h @@ -1,6 +1,6 @@ /* trustdb.h - Trust database - * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003 - * Free Software Foundation, Inc. + * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, + * 2005 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -16,13 +16,13 @@ * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. */ #ifndef G10_TRUSTDB_H #define G10_TRUSTDB_H - /* Trust values must be sorted in ascending order */ #define TRUST_MASK 15 #define TRUST_UNKNOWN 0 /* o: not yet calculated/assigned */ @@ -38,19 +38,26 @@ #define TRUST_FLAG_DISABLED 128 /* d: key/uid disabled */ #define TRUST_FLAG_PENDING_CHECK 256 /* a check-trustdb is pending */ +#define NAMEHASH_HASH DIGEST_ALGO_RMD160 +#define NAMEHASH_LEN 20 + /*-- trustdb.c --*/ +void register_trusted_keyid(u32 *keyid); void register_trusted_key( const char *string ); void check_trustdb (void); void update_trustdb (void); int setup_trustdb( int level, const char *dbname ); void init_trustdb( void ); +void check_trustdb_stale(void); void sync_trustdb( void ); +const char *uid_trust_string_fixed(PKT_public_key *key,PKT_user_id *uid); const char *trust_value_to_string (unsigned int value); int string_to_trust_value (const char *str); void revalidation_mark (void); int trustdb_pending_check(void); +void trustdb_check_or_update(void); int cache_disabled_value(PKT_public_key *pk); @@ -75,6 +82,11 @@ const char *get_ownertrust_string (PKT_public_key *pk); void update_ownertrust (PKT_public_key *pk, unsigned int new_trust ); int clear_ownertrusts (PKT_public_key *pk); +void clean_one_uid(KBNODE keyblock,KBNODE uidnode,int noisy,int self_only, + int *uids_cleaned,int *sigs_cleaned); +void clean_key(KBNODE keyblock,int noisy,int self_only, + int *uids_cleaned,int *sigs_cleaned); + /*-- tdbdump.c --*/ void list_trustdb(const char *username); void export_ownertrust(void); diff --git a/g10/verify.c b/g10/verify.c index cfa373637..54aa76544 100644 --- a/g10/verify.c +++ b/g10/verify.c @@ -1,5 +1,5 @@ -/* verify.c - verify signed data - * Copyright (C) 1998, 1999, 2000, 2001 Free Software Foundation, Inc. +/* verify.c - Verify signed data + * Copyright (C) 1998, 1999, 2000, 2001, 2004 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -15,7 +15,8 @@ * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. */ #include <config.h> @@ -24,14 +25,13 @@ #include <string.h> #include <errno.h> #include <assert.h> -#include <unistd.h> /* for isatty() */ +#include "gpg.h" #include "options.h" #include "packet.h" #include "errors.h" #include "iobuf.h" #include "keydb.h" -#include "memory.h" #include "util.h" #include "main.h" #include "status.h" @@ -54,7 +54,7 @@ int verify_signatures( int nfiles, char **files ) { - iobuf_t fp; + IOBUF fp; armor_filter_context_t afx; progress_filter_context_t pfx; const char *sigfile; @@ -91,11 +91,17 @@ verify_signatures( int nfiles, char **files ) /* open the signature file */ fp = iobuf_open(sigfile); + if (fp && is_secured_file (iobuf_get_fd (fp))) + { + iobuf_close (fp); + fp = NULL; + errno = EPERM; + } if( !fp ) { rc = gpg_error_from_errno (errno); log_error(_("can't open `%s': %s\n"), print_fname_stdin(sigfile), strerror (errno)); - return rc; + return rc; } handle_progress (&pfx, fp, sigfile); @@ -103,12 +109,12 @@ verify_signatures( int nfiles, char **files ) iobuf_push_filter( fp, armor_filter, &afx ); sl = NULL; - for(i=1 ; i < nfiles; i++ ) + for(i=nfiles-1 ; i > 0 ; i-- ) add_to_strlist( &sl, files[i] ); rc = proc_signature_packets( NULL, fp, sl, sigfile ); free_strlist(sl); iobuf_close(fp); - if( afx.no_openpgp_data && rc == -1 ) { + if( (afx.no_openpgp_data && rc == -1) || rc == G10ERR_NO_DATA ) { log_error(_("the signature could not be verified.\n" "Please remember that the signature file (.sig or .asc)\n" "should be the first file given on the command line.\n") ); @@ -122,23 +128,31 @@ verify_signatures( int nfiles, char **files ) void print_file_status( int status, const char *name, int what ) { - char *p = xmalloc (strlen(name)+10); + char *p = xmalloc(strlen(name)+10); sprintf(p, "%d %s", what, name ); write_status_text( status, p ); - xfree (p); + xfree(p); } static int verify_one_file( const char *name ) { - iobuf_t fp; + IOBUF fp; armor_filter_context_t afx; progress_filter_context_t pfx; int rc; print_file_status( STATUS_FILE_START, name, 1 ); fp = iobuf_open(name); + if (fp) + iobuf_ioctl (fp,3,1,NULL); /* disable fd caching */ + if (fp && is_secured_file (iobuf_get_fd (fp))) + { + iobuf_close (fp); + fp = NULL; + errno = EPERM; + } if( !fp ) { rc = gpg_error_from_errno (errno); log_error(_("can't open `%s': %s\n"), @@ -179,7 +193,7 @@ verify_files( int nfiles, char **files ) lno++; if( !*line || line[strlen(line)-1] != '\n' ) { log_error(_("input line %u too long or missing LF\n"), lno ); - return GPG_ERR_GENERAL; + return G10ERR_GENERAL; } /* This code does not work on MSDOS but how cares there are * also no script languages available. We don't strip any diff --git a/include/ChangeLog b/include/ChangeLog index 5b343f5a0..0211bd618 100644 --- a/include/ChangeLog +++ b/include/ChangeLog @@ -1,3 +1,7 @@ +2006-04-18 Werner Koch <wk@g10code.com> + + * keyserver.h, i18n.h, http.h, cipher.h: Updated to gpg 1.4.3. + 2003-09-04 David Shaw <dshaw@jabberwocky.com> * cipher.h: Drop TIGER/192 support. diff --git a/include/cipher.h b/include/cipher.h index e7e36c6d5..681386c36 100644 --- a/include/cipher.h +++ b/include/cipher.h @@ -1,97 +1,101 @@ -/* cipher.h - * Copyright (C) 1998, 1999, 2000, 2001 Free Software Foundation, Inc. +/* cipher.h - Definitions for OpenPGP + * Copyright (C) 1998, 1999, 2000, 2001, 2006 Free Software Foundation, Inc. * - * This file is part of GNUPG. + * This file is part of GnuPG. * - * GNUPG is free software; you can redistribute it and/or modify + * GnuPG is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * - * GNUPG is distributed in the hope that it will be useful, + * GnuPG is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. */ #ifndef G10_CIPHER_H #define G10_CIPHER_H #include <gcrypt.h> -#define CIPHER_ALGO_NONE GCRY_CIPHER_NONE -#define CIPHER_ALGO_IDEA GCRY_CIPHER_IDEA -#define CIPHER_ALGO_3DES GCRY_CIPHER_3DES -#define CIPHER_ALGO_CAST5 GCRY_CIPHER_CAST5 -#define CIPHER_ALGO_BLOWFISH GCRY_CIPHER_BLOWFISH /* 128 bit */ -#define CIPHER_ALGO_SAFER_SK128 GCRY_CIPHER_SK128 -#define CIPHER_ALGO_DES_SK GCRY_CIPHER_DES_SK -#define CIPHER_ALGO_AES GCRY_CIPHER_AES -#define CIPHER_ALGO_AES192 GCRY_CIPHER_AES192 -#define CIPHER_ALGO_AES256 GCRY_CIPHER_AES256 +/* Macros for compatibility with older libgcrypt versions. */ +#ifndef GCRY_PK_USAGE_CERT +# define GCRY_PK_USAGE_CERT 4 +# define GCRY_PK_USAGE_AUTH 8 +# define GCRY_PK_USAGE_UNKN 128 +#endif + + +/* Constants for OpenPGP. */ + +#define CIPHER_ALGO_NONE /* 0 */ GCRY_CIPHER_NONE +#define CIPHER_ALGO_IDEA /* 1 */ GCRY_CIPHER_IDEA +#define CIPHER_ALGO_3DES /* 2 */ GCRY_CIPHER_3DES +#define CIPHER_ALGO_CAST5 /* 3 */ GCRY_CIPHER_CAST5 +#define CIPHER_ALGO_BLOWFISH /* 4 */ GCRY_CIPHER_BLOWFISH /* 128 bit */ +/* 5 & 6 are reserved */ +#define CIPHER_ALGO_AES /* 7 */ GCRY_CIPHER_AES +#define CIPHER_ALGO_AES192 /* 8 */ GCRY_CIPHER_AES192 +#define CIPHER_ALGO_AES256 /* 9 */ GCRY_CIPHER_AES256 #define CIPHER_ALGO_RIJNDAEL CIPHER_ALGO_AES #define CIPHER_ALGO_RIJNDAEL192 CIPHER_ALGO_AES192 #define CIPHER_ALGO_RIJNDAEL256 CIPHER_ALGO_AES256 -#define CIPHER_ALGO_TWOFISH GCRY_CIPHER_TWOFISH /* 256 bit */ -#define CIPHER_ALGO_DUMMY 110 /* no encryption at all */ +#define CIPHER_ALGO_TWOFISH /* 10 */ GCRY_CIPHER_TWOFISH /* 256 bit */ +#define CIPHER_ALGO_DUMMY 110 /* No encryption at all. */ -#define PUBKEY_ALGO_RSA GCRY_PK_RSA -#define PUBKEY_ALGO_RSA_E GCRY_PK_RSA_E -#define PUBKEY_ALGO_RSA_S GCRY_PK_RSA_S -#define PUBKEY_ALGO_ELGAMAL_E GCRY_PK_ELG_E -#define PUBKEY_ALGO_DSA GCRY_PK_DSA -#define PUBKEY_ALGO_ELGAMAL GCRY_PK_ELG +#define PUBKEY_ALGO_RSA /* 1 */ GCRY_PK_RSA +#define PUBKEY_ALGO_RSA_E /* 2 */ GCRY_PK_RSA_E /* RSA encrypt only. */ +#define PUBKEY_ALGO_RSA_S /* 3 */ GCRY_PK_RSA_S /* RSA sign only. */ +#define PUBKEY_ALGO_ELGAMAL_E /* 16 */ GCRY_PK_ELG_E /* Elgamal encr only */ +#define PUBKEY_ALGO_DSA /* 17 */ GCRY_PK_DSA +#define PUBKEY_ALGO_ELGAMAL /* 20 */ GCRY_PK_ELG /* Elgamal encr+sign */ -#define PUBKEY_USAGE_SIG GCRY_PK_USAGE_SIGN -#define PUBKEY_USAGE_ENC GCRY_PK_USAGE_ENCR -#define PUBKEY_USAGE_CERT 4 /* key is also good to certify other keys*/ -#define PUBKEY_USAGE_AUTH 8 +#define PUBKEY_USAGE_SIG GCRY_PK_USAGE_SIGN /* Good for signatures. */ +#define PUBKEY_USAGE_ENC GCRY_PK_USAGE_ENCR /* Good for encryption. */ +#define PUBKEY_USAGE_CERT GCRY_PK_USAGE_CERT /* Also good to certify keys. */ +#define PUBKEY_USAGE_AUTH GCRY_PK_USAGE_AUTH /* Good for authentication. */ +#define PUBKEY_USAGE_UNKNOWN GCRY_PK_USAGE_UNKN /* Unknown usage flag. */ -#define DIGEST_ALGO_MD5 GCRY_MD_MD5 -#define DIGEST_ALGO_SHA1 GCRY_MD_SHA1 -#define DIGEST_ALGO_RMD160 GCRY_MD_RMD160 -#define DIGEST_ALGO_SHA256 GCRY_MD_SHA256 -#define DIGEST_ALGO_SHA384 GCRY_MD_SHA384 -#define DIGEST_ALGO_SHA512 GCRY_MD_SHA512 +#define DIGEST_ALGO_MD5 /* 1 */ GCRY_MD_MD5 +#define DIGEST_ALGO_SHA1 /* 2 */ GCRY_MD_SHA1 +#define DIGEST_ALGO_RMD160 /* 3 */ GCRY_MD_RMD160 +/* 4, 5, 6, and 7 are reserved */ +#define DIGEST_ALGO_SHA256 /* 8 */ GCRY_MD_SHA256 +#define DIGEST_ALGO_SHA384 /* 9 */ GCRY_MD_SHA384 +#define DIGEST_ALGO_SHA512 /* 10 */ GCRY_MD_SHA512 #define COMPRESS_ALGO_NONE 0 #define COMPRESS_ALGO_ZIP 1 #define COMPRESS_ALGO_ZLIB 2 +#define COMPRESS_ALGO_BZIP2 3 #define is_RSA(a) ((a)==PUBKEY_ALGO_RSA || (a)==PUBKEY_ALGO_RSA_E \ || (a)==PUBKEY_ALGO_RSA_S ) -#define is_ELGAMAL(a) ((a)==PUBKEY_ALGO_ELGAMAL || (a)==PUBKEY_ALGO_ELGAMAL_E) +#define is_ELGAMAL(a) ((a)==PUBKEY_ALGO_ELGAMAL_E) +#define is_DSA(a) ((a)==PUBKEY_ALGO_DSA) -typedef struct { - int algo; - int keylen; - int algo_info_printed; - int use_mdc; - byte key[32]; /* this is the largest used keylen (256 bit) */ +/* The data encryption key object. */ +typedef struct +{ + int algo; + int keylen; + int algo_info_printed; + int use_mdc; + int symmetric; + byte key[32]; /* This is the largest used keylen (256 bit). */ } DEK; -#ifndef EXTERN_UNLESS_MAIN_MODULE -#if defined (__riscos__) && !defined (INCLUDED_BY_MAIN_MODULE) -#define EXTERN_UNLESS_MAIN_MODULE extern -#else -#define EXTERN_UNLESS_MAIN_MODULE -#endif -#endif -EXTERN_UNLESS_MAIN_MODULE int g10_opt_verbose; -EXTERN_UNLESS_MAIN_MODULE const char *g10_opt_homedir; - - +/* Constants to allocate static MPI arrays. */ #define PUBKEY_MAX_NPKEY 4 #define PUBKEY_MAX_NSKEY 6 #define PUBKEY_MAX_NSIG 2 #define PUBKEY_MAX_NENC 2 -#define MD_HANDLE gcry_md_hd_t -#define CIPHER_HANDLE gcry_cipher_hd_t - #endif /*G10_CIPHER_H*/ diff --git a/include/host2net.h b/include/host2net.h index 0f12a8e1d..e378bfb29 100644 --- a/include/host2net.h +++ b/include/host2net.h @@ -1,21 +1,22 @@ /* host2net.h - Some macros * Copyright (C) 1998, 1999, 2000, 2001 Free Software Foundation, Inc. * - * This file is part of GNUPG. + * This file is part of GnuPG. * - * GNUPG is free software; you can redistribute it and/or modify + * GnuPG is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * - * GNUPG is distributed in the hope that it will be useful, + * GnuPG is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. */ #ifndef G10_HOST2NET_H diff --git a/include/http.h b/include/http.h index b53ac9f9f..b9ce5b130 100644 --- a/include/http.h +++ b/include/http.h @@ -1,5 +1,6 @@ /* http.h - HTTP protocol handler - * Copyright (C) 1999, 2000, 2001 Free Software Foundation, Inc. + * Copyright (C) 1999, 2000, 2001, 2003, 2004, + * 2005 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -15,12 +16,14 @@ * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. */ + #ifndef G10_HTTP_H #define G10_HTTP_H 1 -#include "iobuf.h" +#include "../common/iobuf.h" struct uri_tuple { struct uri_tuple *next; @@ -34,6 +37,7 @@ typedef struct uri_tuple *URI_TUPLE; struct parsed_uri { /* all these pointers point into buffer; most stuff is not escaped */ char *scheme; /* pointer to the scheme string (lowercase) */ + char *auth; /* username/password for basic auth */ char *host; /* host (converted to lowercase) */ ushort port; /* port (always set if the host is set) */ char *path; /* the path */ @@ -49,19 +53,20 @@ typedef enum { HTTP_REQ_POST = 3 } HTTP_REQ_TYPE; -enum { /* put flag values into an enum, so that gdb can display them */ - HTTP_FLAG_TRY_PROXY = 1, - HTTP_FLAG_NO_SHUTDOWN = 2, - HTTP_FLAG_TRY_SRV = 3 -}; +/* put flag values into an enum, so that gdb can display them */ +enum + { + HTTP_FLAG_NO_SHUTDOWN = 1, + HTTP_FLAG_TRY_SRV = 2 + }; struct http_context { int initialized; unsigned int status_code; int sock; int in_data; - IOBUF fp_read; - IOBUF fp_write; + iobuf_t fp_read; + iobuf_t fp_write; int is_http_0_9; PARSED_URI uri; HTTP_REQ_TYPE req_type; @@ -72,11 +77,11 @@ struct http_context { typedef struct http_context *HTTP_HD; int http_open( HTTP_HD hd, HTTP_REQ_TYPE reqtype, const char *url, - unsigned int flags ); + char *auth, unsigned int flags, const char *proxy ); void http_start_data( HTTP_HD hd ); int http_wait_response( HTTP_HD hd, unsigned int *ret_status ); void http_close( HTTP_HD hd ); - -int http_open_document( HTTP_HD hd, const char *document, unsigned int flags ); +int http_open_document( HTTP_HD hd, const char *document, char *auth, + unsigned int flags, const char *proxy ); #endif /*G10_HTTP_H*/ diff --git a/include/i18n.h b/include/i18n.h index 20c2570ab..6abd2dce3 100644 --- a/include/i18n.h +++ b/include/i18n.h @@ -15,14 +15,15 @@ * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. */ #ifndef G10_I18N_H #define G10_I18N_H #ifdef USE_SIMPLE_GETTEXT -int set_gettext_file( const char *filename ); +int set_gettext_file( const char *filename, const char *regkey ); const char *gettext( const char *msgid ); #define _(a) gettext (a) diff --git a/include/keyserver.h b/include/keyserver.h index 33c1b318b..7bd12625e 100644 --- a/include/keyserver.h +++ b/include/keyserver.h @@ -1,21 +1,22 @@ /* keyserver.h * Copyright (C) 2001, 2002 Free Software Foundation, Inc. * - * This file is part of GNUPG. + * This file is part of GnuPG. * - * GNUPG is free software; you can redistribute it and/or modify + * GnuPG is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * - * GNUPG is distributed in the hope that it will be useful, + * GnuPG is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. */ #ifndef _KEYSERVER_H_ @@ -35,6 +36,7 @@ #define KEYSERVER_KEY_EXISTS 7 /* key already exists */ #define KEYSERVER_KEY_INCOMPLETE 8 /* key incomplete (EOF) */ #define KEYSERVER_UNREACHABLE 9 /* unable to contact keyserver */ +#define KEYSERVER_TIMEOUT 10 /* timeout while accessing keyserver */ /* Must be 127 due to shell internal magic. */ #define KEYSERVER_SCHEME_NOT_FOUND 127 diff --git a/include/memory.h b/include/memory.h index 959f2999e..35719da62 100644 --- a/include/memory.h +++ b/include/memory.h @@ -21,6 +21,8 @@ #ifndef G10_MEMORY_H #define G10_MEMORY_H +#error this file should not be used anymore + #ifdef M_DEBUG #ifndef STR #define STR(v) #v diff --git a/include/mpi.h b/include/mpi.h index 424e591a0..b732923a2 100644 --- a/include/mpi.h +++ b/include/mpi.h @@ -30,6 +30,8 @@ #ifndef G10_MPI_H #define G10_MPI_H +#error this file should not be used anymore + #include <gcrypt.h> #if 0 diff --git a/include/types.h b/include/types.h index dff512061..6abd500e3 100644 --- a/include/types.h +++ b/include/types.h @@ -1,21 +1,22 @@ /* types.h - some common typedefs * Copyright (C) 1998, 1999, 2000, 2001 Free Software Foundation, Inc. * - * This file is part of GNUPG. + * This file is part of GnuPG. * - * GNUPG is free software; you can redistribute it and/or modify + * GnuPG is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * - * GNUPG is distributed in the hope that it will be useful, + * GnuPG is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. */ #ifndef G10_TYPES_H diff --git a/include/util.h b/include/util.h index ca5e5e431..c579c152e 100644 --- a/include/util.h +++ b/include/util.h @@ -20,7 +20,7 @@ #ifndef G10_UTIL_H #define G10_UTIL_H -#warning oops, using old util.h +#error this file should not be used anymore #if 0 /* Dont use it anymore */ #if defined (__MINGW32__) || defined (__CYGWIN32__) diff --git a/include/zlib-riscos.h b/include/zlib-riscos.h index fad556bcb..6a27b86ef 100644 --- a/include/zlib-riscos.h +++ b/include/zlib-riscos.h @@ -15,7 +15,8 @@ * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. */ #ifndef G10_ZLIB_RISCOS_H #define G10_ZLIB_RISCOS_H diff --git a/jnlib/ChangeLog b/jnlib/ChangeLog index f0463c5b3..32549d136 100644 --- a/jnlib/ChangeLog +++ b/jnlib/ChangeLog @@ -1,3 +1,8 @@ +2006-04-18 Werner Koch <wk@g10code.com> + + * libjnlib-config.h (JNLIB_NEED_UTF8CONF): Defined. + * strlist.c (add_to_strlist2) [JNLIB_NEED_UTF8CONV]: Enabled. + 2005-06-15 Werner Koch <wk@g10code.com> * stringhelp.c (sanitize_buffer): Make P a void*. diff --git a/jnlib/libjnlib-config.h b/jnlib/libjnlib-config.h index ad7e353fd..8ae2a9ce9 100644 --- a/jnlib/libjnlib-config.h +++ b/jnlib/libjnlib-config.h @@ -1,5 +1,5 @@ /* libjnlib-config.h - local configuration of the jnlib functions - * Copyright (C) 2000, 2001 Free Software Foundation, Inc. + * Copyright (C) 2000, 2001, 2006 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -29,6 +29,9 @@ #include <gcrypt.h> /* gcry_malloc & Cie. */ #include "logging.h" +/* We require support for utf-8 conversion. */ +#define JNLIB_NEED_UTF8CONF 1 + #ifdef USE_SIMPLE_GETTEXT int set_gettext_file( const char *filename ); const char *gettext( const char *msgid ); diff --git a/jnlib/strlist.c b/jnlib/strlist.c index 49b510666..d1924c102 100644 --- a/jnlib/strlist.c +++ b/jnlib/strlist.c @@ -1,5 +1,5 @@ /* strlist.c - string helpers - * Copyright (C) 1998, 2000, 2001 Free Software Foundation, Inc. + * Copyright (C) 1998, 2000, 2001, 2006 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -26,7 +26,9 @@ #include "libjnlib-config.h" #include "strlist.h" - +#ifdef JNLIB_NEED_UTF8CONV +#include "utf8conv.h" +#endif void free_strlist( strlist_t sl ) @@ -53,26 +55,26 @@ add_to_strlist( strlist_t *list, const char *string ) return sl; } -#if 0 -/**************** - * same as add_to_strlist() but if is_utf8 is *not* set a conversion - * to UTF8 is done - */ + +/* Same as add_to_strlist() but if is_utf8 is *not* set, a conversion + to UTF-8 is done. */ +#ifdef JNLIB_NEED_UTF8CONV strlist_t add_to_strlist2( strlist_t *list, const char *string, int is_utf8 ) { - strlist_t sl; - - if( is_utf8 ) - sl = add_to_strlist( list, string ); - else { - char *p = native_to_utf8( string ); - sl = add_to_strlist( list, p ); - m_free( p ); + strlist_t sl; + + if (is_utf8) + sl = add_to_strlist( list, string ); + else + { + char *p = native_to_utf8( string ); + sl = add_to_strlist( list, p ); + jnlib_free ( p ); } - return sl; + return sl; } -#endif +#endif /* JNLIB_NEED_UTF8CONV*/ strlist_t append_to_strlist( strlist_t *list, const char *string ) diff --git a/jnlib/strlist.h b/jnlib/strlist.h index 72da241bc..47ac5bd4e 100644 --- a/jnlib/strlist.h +++ b/jnlib/strlist.h @@ -32,8 +32,7 @@ typedef struct string_list *strlist_t; void free_strlist (strlist_t sl); strlist_t add_to_strlist (strlist_t *list, const char *string); -/*strlist_t add_to_strlist2( strlist_t *list, - const char *string, int is_utf8);*/ +strlist_t add_to_strlist2( strlist_t *list, const char *string, int is_utf8); strlist_t append_to_strlist (strlist_t *list, const char *string); |