gpg: Make function mk_datestr public.
[gnupg.git] / g10 / armor.c
index e9efa77..cc80968 100644 (file)
@@ -1,12 +1,12 @@
 /* armor.c - Armor flter
- * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005,
- *               2006 Free Software Foundation, Inc.
+ * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006,
+ *               2007 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
+ * the Free Software Foundation; either version 3 of the License, or
  * (at your option) any later version.
  *
  * GnuPG is distributed in the hope that it will be useful,
@@ -15,9 +15,7 @@
  * 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.
+ * along with this program; if not, see <https://www.gnu.org/licenses/>.
  */
 
 #include <config.h>
 #include <stdlib.h>
 #include <string.h>
 #include <errno.h>
-#include <assert.h>
 #include <ctype.h>
 
 #include "gpg.h"
-#include "errors.h"
-#include "iobuf.h"
-#include "util.h"
+#include "../common/status.h"
+#include "../common/iobuf.h"
+#include "../common/util.h"
 #include "filter.h"
 #include "packet.h"
 #include "options.h"
 #include "main.h"
-#include "status.h"
-#include "i18n.h"
+#include "../common/i18n.h"
 
 #define MAX_LINELEN 20000
 
@@ -114,6 +110,54 @@ static char *tail_strings[] = {
 };
 
 
+static int armor_filter ( void *opaque, int control,
+                          iobuf_t chain, byte *buf, size_t *ret_len);
+
+
+
+\f
+/* Create a new context for armor filters.  */
+armor_filter_context_t *
+new_armor_context (void)
+{
+  armor_filter_context_t *afx;
+
+  afx = xcalloc (1, sizeof *afx);
+  afx->refcount = 1;
+
+  return afx;
+}
+
+/* Release an armor filter context.  Passing NULL is explicitly
+   allowed and a no-op.  */
+void
+release_armor_context (armor_filter_context_t *afx)
+{
+  if (!afx)
+    return;
+  log_assert (afx->refcount);
+  if ( --afx->refcount )
+    return;
+  xfree (afx);
+}
+
+/* Push the armor filter onto the iobuf stream IOBUF.  */
+int
+push_armor_filter (armor_filter_context_t *afx, iobuf_t iobuf)
+{
+  int rc;
+
+  afx->refcount++;
+  rc = iobuf_push_filter (iobuf, armor_filter, afx);
+  if (rc)
+    afx->refcount--;
+  return rc;
+}
+
+
+
+
+
 static void
 initialize(void)
 {
@@ -145,38 +189,84 @@ initialize(void)
     is_initialized=1;
 }
 
-/****************
- * Check whether this is an armored file or not See also
- * parse-packet.c for details on this code For unknown historic
- * reasons we use a string here but only the first byte will be used.
+
+/*
+ * Check whether this is an armored file.  See also
+ * parse-packet.c for details on this code.
+ *
+ * Note that the buffer BUF needs to be at least 2 bytes long.  If in
+ * doubt that the second byte to 0.
+ *
  * Returns: True if it seems to be armored
  */
 static int
-is_armored( const byte *buf )
+is_armored (const byte *buf)
 {
-    int ctb, pkttype;
+  int ctb, pkttype;
+  int indeterminate_length_allowed;
 
     ctb = *buf;
     if( !(ctb & 0x80) )
-       return 1; /* invalid packet: assume it is armored */
+      /* The most significant bit of the CTB must be set.  Since it is
+         cleared, this is not a binary OpenPGP message.  Assume it is
+         armored.  */
+      return 1;
+
     pkttype =  ctb & 0x40 ? (ctb & 0x3f) : ((ctb>>2)&0xf);
     switch( pkttype ) {
-      case PKT_MARKER:
+      case PKT_PUBKEY_ENC:
+      case PKT_SIGNATURE:
       case PKT_SYMKEY_ENC:
       case PKT_ONEPASS_SIG:
-      case PKT_PUBLIC_KEY:
       case PKT_SECRET_KEY:
-      case PKT_PUBKEY_ENC:
-      case PKT_SIGNATURE:
-      case PKT_COMMENT:
-      case PKT_OLD_COMMENT:
-      case PKT_PLAINTEXT:
+      case PKT_PUBLIC_KEY:
+      case PKT_SECRET_SUBKEY:
+      case PKT_MARKER:
+      case PKT_RING_TRUST:
+      case PKT_USER_ID:
+      case PKT_PUBLIC_SUBKEY:
+      case PKT_ATTRIBUTE:
+      case PKT_MDC:
+       indeterminate_length_allowed = 0;
+        break;
+
       case PKT_COMPRESSED:
       case PKT_ENCRYPTED:
-       return 0; /* seems to be a regular packet: not armored */
+      case PKT_ENCRYPTED_MDC:
+      case PKT_PLAINTEXT:
+      case PKT_OLD_COMMENT:
+      case PKT_COMMENT:
+      case PKT_GPG_CONTROL:
+       indeterminate_length_allowed = 1;
+        break;
+
+      default:
+        /* Invalid packet type.  */
+        return 1;
     }
 
-    return 1;
+    if (! indeterminate_length_allowed)
+      /* It is only legal to use an indeterminate length with a few
+         packet types.  If a packet uses an indeterminate length, but
+         that is not allowed, then the data is not valid binary
+         OpenPGP data.  */
+      {
+        int new_format;
+        int indeterminate_length;
+
+        new_format = !! (ctb & (1 << 6));
+        if (new_format)
+          indeterminate_length = (buf[1] >= 224 && buf[1] < 255);
+        else
+          indeterminate_length = (ctb & 3) == 3;
+
+        if (indeterminate_length)
+          return 1;
+      }
+
+    /* The first CTB seems legit.  It is probably not armored
+       data.  */
+    return 0;
 }
 
 
@@ -188,15 +278,17 @@ is_armored( const byte *buf )
 int
 use_armor_filter( IOBUF a )
 {
-    byte buf[1];
+    byte buf[2];
     int n;
 
     /* fixme: there might be a problem with iobuf_peek */
-    n = iobuf_peek(a, buf, 1 );
+    n = iobuf_peek (a, buf, 2);
     if( n == -1 )
        return 0; /* EOF, doesn't matter whether armored or not */
     if( !n )
        return 1; /* can't check it: try armored */
+    if (n != 2)
+       return 0; /* short buffer */
     return is_armored(buf);
 }
 
@@ -228,7 +320,7 @@ parse_hash_header( const char *line )
        return 0; /* too short or too long */
     if( memcmp( line, "Hash:", 5 ) )
        return 0; /* invalid header */
-    s = line+5;
+
     for(s=line+5;;s=s2) {
        for(; *s && (*s==' ' || *s == '\t'); s++ )
            ;
@@ -240,8 +332,6 @@ parse_hash_header( const char *line )
            found |= 1;
        else if( !strncmp( s, "SHA1", s2-s ) )
            found |= 2;
-       else if( !strncmp( s, "MD5", s2-s ) )
-           found |= 4;
        else if( !strncmp( s, "SHA224", s2-s ) )
            found |= 8;
        else if( !strncmp( s, "SHA256", s2-s ) )
@@ -262,7 +352,19 @@ parse_hash_header( const char *line )
     return found;
 }
 
+/* Returns true if this is a valid armor tag as per RFC-2440bis-21. */
+static int
+is_armor_tag(const char *line)
+{
+  if(strncmp(line,"Version",7)==0
+     || strncmp(line,"Comment",7)==0
+     || strncmp(line,"MessageID",9)==0
+     || strncmp(line,"Hash",4)==0
+     || strncmp(line,"Charset",7)==0)
+    return 1;
 
+  return 0;
+}
 
 /****************
  * Check whether this is a armor line.
@@ -292,7 +394,8 @@ is_armor_header( byte *line, unsigned len )
        --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. */
+       refers to all text or just non-whitespace text.  4880 clarified
+       this was only non-whitespace text. */
 
     if(RFC2440)
       {
@@ -356,9 +459,9 @@ parse_header_line( armor_filter_context_t *afx, byte *line, unsigned int len )
     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);
+       log_error (_("invalid armor header: "));
+       es_write_sanitized (log_get_stream (), line, len, NULL, NULL);
+       log_printf ("\n");
        return -1;
       }
 
@@ -368,20 +471,36 @@ parse_header_line( armor_filter_context_t *afx, byte *line, unsigned int len )
 
     if( opt.verbose ) {
        log_info(_("armor header: "));
-       print_string( stderr, line, len, 0 );
-       putc('\n', stderr);
+       es_write_sanitized (log_get_stream (), line, len, NULL, NULL);
+       log_printf ("\n");
     }
 
-    if( afx->in_cleartext ) {
+    if( afx->in_cleartext )
+      {
        if( (hashes=parse_hash_header( line )) )
-           afx->hashes |= hashes;
+         afx->hashes |= hashes;
        else if( strlen(line) > 15 && !memcmp( line, "NotDashEscaped:", 15 ) )
-           afx->not_dash_escaped = 1;
-       else {
+         afx->not_dash_escaped = 1;
+       else
+         {
            log_error(_("invalid clearsig header\n"));
            return -1;
-       }
-    }
+         }
+      }
+    else if(!is_armor_tag(line))
+      {
+       /* Section 6.2: "Unknown keys should be reported to the user,
+          but OpenPGP should continue to process the message."  Note
+          that in a clearsigned message this applies to the signature
+          part (i.e. "BEGIN PGP SIGNATURE") and not the signed data
+          ("BEGIN PGP SIGNED MESSAGE").  The only key allowed in the
+          signed data section is "Hash". */
+
+       log_info(_("unknown armor header: "));
+       es_write_sanitized (log_get_stream (), line, len, NULL, NULL);
+       log_printf ("\n");
+      }
+
     return 1;
 }
 
@@ -417,7 +536,7 @@ check_input( armor_filter_context_t *afx, IOBUF a )
     /* (the line is always a C string but maybe longer) */
     if( *line == '\n' || ( len && (*line == '\r' && line[1]=='\n') ) )
        ;
-    else if( !is_armored( line ) ) {
+    else if (len >= 2 && !is_armored (line)) {
        afx->inp_checked = 1;
        afx->inp_bypass = 1;
        return 0;
@@ -461,7 +580,7 @@ check_input( armor_filter_context_t *afx, IOBUF a )
        i = parse_header_line( afx, line, len );
        if( i <= 0 ) {
            if (i && RFC2440)
-               rc = G10ERR_INVALID_ARMOR;
+               rc = GPG_ERR_INV_ARMOR;
            break;
        }
     }
@@ -566,8 +685,9 @@ fake_packet( armor_filter_context_t *afx, IOBUF a,
                    if( type != BEGIN_SIGNATURE )
                      {
                        log_info(_("unexpected armor: "));
-                       print_string( stderr, p, n, 0 );
-                       putc('\n', stderr);
+                       es_write_sanitized (log_get_stream (), p, n,
+                                            NULL, NULL);
+                       log_printf ("\n");
                      }
 
                    lastline = 1;
@@ -577,9 +697,9 @@ fake_packet( armor_filter_context_t *afx, IOBUF a,
            else if(!afx->not_dash_escaped)
              {
                /* Bad dash-escaping. */
-               log_info(_("invalid dash escaped line: "));
-               print_string( stderr, p, n, 0 );
-               putc('\n', stderr);
+               log_info (_("invalid dash escaped line: "));
+               es_write_sanitized (log_get_stream (), p, n, NULL, NULL);
+               log_printf ("\n");
              }
          }
 
@@ -588,10 +708,9 @@ fake_packet( armor_filter_context_t *afx, IOBUF a,
          {
            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[afx->buffer_pos], n-afx->buffer_pos,
-                                  afx->pgp2mode ? " \r\n" : " \t\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
@@ -722,8 +841,27 @@ radix64_read( armor_filter_context_t *afx, IOBUF a, size_t *retn,
                    goto again;
                }
            }
-           else if(n==0)
-             onlypad=1;
+
+            /* Occasionally a bug MTA will leave the = escaped as
+               =3D.  If the 4 characters following that are valid
+               Radix64 characters and they are following by a new
+               line, assume that this is the case and skip the
+               3D.  */
+            if (afx->buffer_pos + 6 < afx->buffer_len
+                && afx->buffer[afx->buffer_pos + 0] == '3'
+                && afx->buffer[afx->buffer_pos + 1] == 'D'
+                && asctobin[afx->buffer[afx->buffer_pos + 2]] != 255
+                && asctobin[afx->buffer[afx->buffer_pos + 3]] != 255
+                && asctobin[afx->buffer[afx->buffer_pos + 4]] != 255
+                && asctobin[afx->buffer[afx->buffer_pos + 5]] != 255
+                && afx->buffer[afx->buffer_pos + 6] == '\n')
+              {
+                afx->buffer_pos += 2;
+                afx->qp_detected = 1;
+              }
+
+           if (!n)
+             onlypad = 1;
 
            if( idx == 1 )
                buf[n++] = val;
@@ -841,11 +979,11 @@ radix64_read( armor_filter_context_t *afx, IOBUF a, size_t *retn,
                    rc = 0;
                else if( rc == 2 ) {
                    log_error(_("premature eof (in trailer)\n"));
-                   rc = G10ERR_INVALID_ARMOR;
+                   rc = GPG_ERR_INVALID_ARMOR;
                }
                else {
                    log_error(_("error in trailer line\n"));
-                   rc = G10ERR_INVALID_ARMOR;
+                   rc = GPG_ERR_INVALID_ARMOR;
                }
 #endif
            }
@@ -862,7 +1000,7 @@ radix64_read( armor_filter_context_t *afx, IOBUF a, size_t *retn,
 /****************
  * This filter is used to handle the armor stuff
  */
-int
+static int
 armor_filter( void *opaque, int control,
             IOBUF a, byte *buf, size_t *ret_len)
 {
@@ -887,17 +1025,20 @@ armor_filter( void *opaque, int control,
     if( control == IOBUFCTRL_UNDERFLOW && afx->inp_bypass ) {
        n = 0;
        if( afx->buffer_len ) {
+            /* Copy the data from AFX->BUFFER to BUF.  */
            for(; n < size && afx->buffer_pos < afx->buffer_len; n++ )
                buf[n++] = afx->buffer[afx->buffer_pos++];
            if( afx->buffer_pos >= afx->buffer_len )
                afx->buffer_len = 0;
        }
+        /* If there is still space in BUF, read directly into it.  */
        for(; n < size; n++ ) {
            if( (c=iobuf_get(a)) == -1 )
                break;
            buf[n] = c & 0xff;
        }
        if( !n )
+            /* We didn't get any data.  EOF.  */
            rc = -1;
        *ret_len = n;
     }
@@ -905,7 +1046,7 @@ armor_filter( void *opaque, int control,
         /* 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 ) 
+       if( size < PARTIAL_CHUNK+1 )
            BUG(); /* supplied buffer too short */
 
        if( afx->faked )
@@ -924,7 +1065,7 @@ armor_filter( void *opaque, int control,
                unsigned int hashes = afx->hashes;
                 const byte *sesmark;
                 size_t sesmarklen;
-                
+
                 sesmark = get_session_marker( &sesmarklen );
                 if ( sesmarklen > 20 )
                     BUG();
@@ -932,28 +1073,21 @@ armor_filter( void *opaque, int control,
                /* the buffer is at least 15+n*15 bytes long, so it
                 * is easy to construct the packets */
 
-               hashes &= 1|2|4|8|16|32|64;
+               hashes &= 1|2|8|16|32|64;
                if( !hashes ) {
-                   hashes |= 4;  /* default to MD 5 */
-                   /* This is non-ideal since PGP 5-8 have the same
-                      end-of-line bugs as PGP 2. However, we only
-                      enable pgp2mode if there is no Hash: header. */
-                   if( opt.pgp2_workarounds )
-                       afx->pgp2mode = 1;
+                   hashes |= 2;  /* Default to SHA-1. */
                }
                n=0;
                 /* 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;
-                buf[n++] = CTRLPKT_CLEARSIGN_START; 
+                buf[n++] = CTRLPKT_CLEARSIGN_START;
                 buf[n++] = afx->not_dash_escaped? 0:1; /* sigclass */
                 if( hashes & 1 )
                     buf[n++] = DIGEST_ALGO_RMD160;
                 if( hashes & 2 )
                     buf[n++] = DIGEST_ALGO_SHA1;
-                if( hashes & 4 )
-                    buf[n++] = DIGEST_ALGO_MD5;
                 if( hashes & 8 )
                     buf[n++] = DIGEST_ALGO_SHA224;
                 if( hashes & 16 )
@@ -1000,10 +1134,24 @@ armor_filter( void *opaque, int control,
            iobuf_writestr(a, head_strings[afx->what] );
            iobuf_writestr(a, "-----" );
            iobuf_writestr(a,afx->eol);
-           if( !opt.no_version )
+           if (opt.emit_version)
              {
-               iobuf_writestr(a, "Version: GnuPG v"  VERSION " ("
-                              PRINTABLE_OS_NAME ")" );
+               iobuf_writestr (a, "Version: "GNUPG_NAME" v");
+                for (s=VERSION; *s && *s != '.'; s++)
+                  iobuf_writebyte (a, *s);
+                if (opt.emit_version > 1 && *s)
+                  {
+                    iobuf_writebyte (a, *s++);
+                    for (; *s && *s != '.'; s++)
+                      iobuf_writebyte (a, *s);
+                    if (opt.emit_version > 2)
+                      {
+                        for (; *s && *s != '-' && !spacep (s); s++)
+                          iobuf_writebyte (a, *s);
+                        if (opt.emit_version > 3)
+                          iobuf_writestr (a, " (" PRINTABLE_OS_NAME ")");
+                      }
+                  }
                iobuf_writestr(a,afx->eol);
              }
 
@@ -1105,21 +1253,20 @@ armor_filter( void *opaque, int control,
            crc = afx->crc;
            idx = afx->idx;
            idx2 = afx->idx2;
-           for(i=0; i < idx; i++ )
-               radbuf[i] = afx->radbuf[i];
            if( idx ) {
-               c = bintoasc[(*radbuf>>2)&077];
+               c = bintoasc[(afx->radbuf[0]>>2)&077];
                iobuf_put(a, c);
                if( idx == 1 ) {
-                   c = bintoasc[((*radbuf << 4) & 060) & 077];
+                   c = bintoasc[((afx->radbuf[0] << 4) & 060) & 077];
                    iobuf_put(a, c);
                    iobuf_put(a, '=');
                    iobuf_put(a, '=');
                }
                else { /* 2 */
-                   c = bintoasc[(((*radbuf<<4)&060)|((radbuf[1]>>4)&017))&077];
+                   c = bintoasc[(((afx->radbuf[0]<<4)&060)
+                                  |((afx->radbuf[1]>>4)&017))&077];
                    iobuf_put(a, c);
-                   c = bintoasc[((radbuf[1] << 2) & 074) & 077];
+                   c = bintoasc[((afx->radbuf[1] << 2) & 074) & 077];
                    iobuf_put(a, c);
                    iobuf_put(a, '=');
                }
@@ -1146,7 +1293,7 @@ armor_filter( void *opaque, int control,
            c = bintoasc[radbuf[2]&077];
            iobuf_put(a, c);
            iobuf_writestr(a,afx->eol);
-           /* and the the trailer */
+           /* and the trailer */
            if( afx->what >= DIM(tail_strings) )
                log_bug("afx->what=%d", afx->what);
            iobuf_writestr(a, "-----");
@@ -1168,9 +1315,10 @@ armor_filter( void *opaque, int control,
                        "probably a buggy MTA has been used\n") );
        xfree( afx->buffer );
        afx->buffer = NULL;
+        release_armor_context (afx);
     }
     else if( control == IOBUFCTRL_DESC )
-       *(char**)buf = "armor_filter";
+        mem2str (buf, "armor_filter", *ret_len);
     return rc;
 }
 
@@ -1206,7 +1354,7 @@ make_radix64_string( const byte *data, size_t len )
 
 /***********************************************
  *  For the pipemode command we can't use the armor filter for various
- *  reasons, so we use this new unarmor_pump stuff to remove the armor 
+ *  reasons, so we use this new unarmor_pump stuff to remove the armor
  */
 
 enum unarmor_state_e {
@@ -1214,7 +1362,7 @@ enum unarmor_state_e {
     STA_bypass,
     STA_wait_newline,
     STA_wait_dash,
-    STA_first_dash, 
+    STA_first_dash,
     STA_compare_header,
     STA_found_header_wait_newline,
     STA_skip_header_lines,
@@ -1253,12 +1401,12 @@ unarmor_pump_release (UnarmorPump x)
     xfree (x);
 }
 
-/* 
+/*
  * Get the next character from the ascii armor taken from the IOBUF
  * created earlier by unarmor_pump_new().
  * Return:  c = Character
  *        256 = ignore this value
- *         -1 = End of current armor 
+ *         -1 = End of current armor
  *         -2 = Premature EOF (not used)
  *         -3 = Invalid armor
  */
@@ -1269,9 +1417,10 @@ unarmor_pump (UnarmorPump x, int c)
 
     switch (x->state) {
       case STA_init:
-        { 
-            byte tmp[1];
-            tmp[0] = c; 
+        {
+            byte tmp[2];
+            tmp[0] = c;
+            tmp[1] = 0;
             if ( is_armored (tmp) )
                 x->state = c == '-'? STA_first_dash : STA_wait_newline;
             else {
@@ -1289,15 +1438,15 @@ unarmor_pump (UnarmorPump x, int c)
       case STA_wait_dash:
         x->state = c == '-'? STA_first_dash : STA_wait_newline;
         break;
-      case STA_first_dash: /* just need for initalization */
+      case STA_first_dash: /* just need for initialization */
         x->pos = 0;
-        x->state = STA_compare_header;
+        x->state = STA_compare_header; /* fall through */
       case STA_compare_header:
         if ( "-----BEGIN PGP SIGNATURE-----"[++x->pos] == c ) {
-            if ( x->pos == 28 ) 
+            if ( x->pos == 28 )
                 x->state = STA_found_header_wait_newline;
         }
-        else 
+        else
             x->state = c == '\n'? STA_wait_dash : STA_wait_newline;
         break;
       case STA_found_header_wait_newline:
@@ -1344,7 +1493,7 @@ unarmor_pump (UnarmorPump x, int c)
                 break;
             }
         }
-        
+
         switch(x->pos) {
           case 0:
             x->val = c << 2;
@@ -1372,7 +1521,7 @@ unarmor_pump (UnarmorPump x, int c)
         /* assume that we are at the next line */
         x->state = STA_read_crc;
         x->pos = 0;
-        x->mycrc = 0;
+        x->mycrc = 0; /* fall through */
       case STA_read_crc:
         if( (c = asctobin[c]) == 255 ) {
             rval = -1; /* ready */
@@ -1385,7 +1534,7 @@ unarmor_pump (UnarmorPump x, int c)
             x->state = STA_ready; /* not sure whether this is correct */
             break;
         }
-        
+
         switch(x->pos) {
           case 0:
             x->val = c << 2;