import works
[gnupg.git] / g10 / armor.c
index e397346..03a393b 100644 (file)
@@ -36,8 +36,6 @@
 #include "status.h"
 
 
-
-
 #define CRCINIT 0xB704CE
 #define CRCPOLY 0X864CFB
 #define CRCUPDATE(a,c) do {                                                \
@@ -89,12 +87,15 @@ static char *head_strings[] = {
     "BEGIN PGP PUBLIC KEY BLOCK",
     "BEGIN PGP SIGNATURE",
     "BEGIN PGP SIGNED MESSAGE",
+    "BEGIN PGP ARMORED FILE",
     NULL
 };
 static char *tail_strings[] = {
     "END PGP MESSAGE",
     "END PGP PUBLIC KEY BLOCK",
     "END PGP SIGNATURE",
+    "END dummy",
+    "END PGP ARMORED FILE",
     NULL
 };
 
@@ -163,6 +164,29 @@ is_armored( byte *buf )
     return 1;
 }
 
+
+/****************
+ * Try to check wether the iobuf is armored
+ * Returns true if this may be the case; the caller should use the
+ *        filter to do further processing.
+ */
+int
+use_armor_filter( IOBUF a )
+{
+    byte buf[1];
+    int n;
+
+    n = iobuf_peek(a, buf, 1 );
+    if( n == -1 )
+       return 0; /* EOF, doesn't matter wether armored or not */
+    if( !n )
+       return 1; /* can't check it: try armored */
+    return is_armored(buf);
+}
+
+
+
+
 static void
 invalid_armor(void)
 {
@@ -172,6 +196,52 @@ invalid_armor(void)
 
 
 /****************
+ * check wether the armor header is valid on a signed message.
+ * this is for security reasons: the header lines are not included in the
+ * hash and by using some creative formatting rules, Mallory could fake
+ * any text at the beginning of a document; assuming it is read with
+ * a simple viewer. We do only allow the Hash Header.
+ */
+static int
+parse_hash_header( const char *line )
+{
+    const char *s, *s2;
+    unsigned found = 0;
+
+    if( strlen(line) < 6  || strlen(line) > 60 )
+       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++ )
+           ;
+       if( !*s )
+           break;
+       for(s2=s+1; *s2 && *s2!=' ' && *s2 != '\t' && *s2 != ','; s2++ )
+           ;
+       if( !strncmp( s, "RIPEMD160", s2-s ) )
+           found |= 1;
+       else if( !strncmp( s, "SHA1", s2-s ) )
+           found |= 2;
+       else if( !strncmp( s, "MD5", s2-s ) )
+           found |= 4;
+       else if( !strncmp( s, "MD2", s2-s ) )
+           found |= 8;
+       else
+           return 0;
+       for(; *s2 && (*s2==' ' || *s2 == '\t'); s2++ )
+           ;
+       if( *s2 && *s2 != ',' )
+           return 0;
+       if( *s2 )
+           s2++;
+    }
+    return found;
+}
+
+
+/****************
  * parse an ascii armor.
  * Returns: the state,
  *         the remaining bytes in BUF are returned in RBUFLEN.
@@ -181,7 +251,7 @@ static fhdr_state_t
 find_header( fhdr_state_t state, byte *buf, size_t *r_buflen,
                                        IOBUF a, size_t n, unsigned *r_empty)
 {
-    int c, i;
+    int c=0, i;
     const char *s;
     byte *p;
     size_t buflen;
@@ -197,15 +267,19 @@ find_header( fhdr_state_t state, byte *buf, size_t *r_buflen,
     do {
        switch( state ) {
          case fhdrHASArmor:
-           /* read 28 bytes, which is the bare minimum for a BEGIN...
-            * and check wether this has a Armor. */
+           /* read at least the first byte to check wether it is armored
+            * or not */
            c = 0;
            for(n=0; n < 28 && (c=iobuf_get2(a)) != -1 && c != '\n'; )
                buf[n++] = c;
-           if( n < 28 || c == -1 )
+           if( !n && c == '\n' )
+               state = fhdrCHECKBegin;
+           else if( !n  || c == -1 )
                state = fhdrNOArmor; /* too short */
            else if( !is_armored( buf ) )
                state = fhdrNOArmor;
+           else if( c == '\n' )
+               state = fhdrCHECKBegin;
            else
                state = fhdrINITCont;
            break;
@@ -221,8 +295,12 @@ find_header( fhdr_state_t state, byte *buf, size_t *r_buflen,
            break;
 
          case fhdrINITSkip:
-           while( (c=iobuf_get2(a)) != -1 && c != '\n' )
-               ;
+           if( c == '\n' )
+               n = 0;
+           else {
+               while( (c=iobuf_get2(a)) != -1 && c != '\n' )
+                   ;
+           }
            state =  c == -1? fhdrEOF : fhdrINIT;
            break;
 
@@ -240,10 +318,17 @@ find_header( fhdr_state_t state, byte *buf, size_t *r_buflen,
            if( n < buflen || c == '\n' ) {
                if( n && buf[0] != '\r') { /* maybe a header */
                    if( strchr( buf, ':') ) { /* yes */
-                       log_debug("armor header: ");
-                       print_string( stderr, buf, n );
-                       putc('\n', stderr);
-                       state = fhdrWAITHeader;
+                       if( opt.verbose ) {
+                           log_info("armor header: ");
+                           print_string( stderr, buf, n );
+                           putc('\n', stderr);
+                       }
+                       if( clearsig && !parse_hash_header( buf ) ) {
+                           log_error("invalid clearsig header\n");
+                           state = fhdrERROR;
+                       }
+                       else
+                           state = fhdrWAITHeader;
                    }
                    else
                        state = fhdrCHECKDashEscaped3;
@@ -268,9 +353,11 @@ find_header( fhdr_state_t state, byte *buf, size_t *r_buflen,
            }
            else if( c != -1 ) {
                if( strchr( buf, ':') ) { /* buffer to short, but this is okay*/
-                   log_debug("armor header: ");
-                   print_string( stderr, buf, n );
-                   fputs("[...]\n", stderr);  /* indicate it is truncated */
+                   if( opt.verbose ) {
+                       log_info("armor header: ");
+                       print_string( stderr, buf, n );
+                       fputs("[...]\n", stderr);  /* indicate it is truncated */
+                   }
                    state = fhdrSKIPHeader;  /* skip rest of line */
                }
                else /* line too long */
@@ -327,7 +414,8 @@ find_header( fhdr_state_t state, byte *buf, size_t *r_buflen,
            state = fhdrWAITHeader;
            if( hdr_line == BEGIN_SIGNED_MSG_IDX )
                clearsig = 1;
-           log_debug("armor: %s\n", head_strings[hdr_line]);
+           if( opt.verbose > 1 )
+               log_info("armor: %s\n", head_strings[hdr_line]);
            break;
 
          case fhdrCLEARSIG:
@@ -379,15 +467,24 @@ find_header( fhdr_state_t state, byte *buf, size_t *r_buflen,
            break;
 
          case fhdrCHECKClearsig:
-         case fhdrCHECKClearsig2:
            /* check the clearsig line */
            if( n > 15 && !memcmp(buf, "-----", 5 ) )
                state = fhdrENDClearsig;
            else if( buf[0] == '-' && buf[1] == ' ' )
                state = fhdrCHECKDashEscaped;
            else {
-               state = state == fhdrCHECKClearsig2 ?
-                                 fhdrREADClearsig : fhdrTESTSpaces;
+               state = fhdrTESTSpaces;
+           }
+           break;
+
+         case fhdrCHECKClearsig2:
+           /* check the clearsig line */
+           if( n > 15 && !memcmp(buf, "-----", 5 ) )
+               state = fhdrENDClearsig;
+           else if( buf[0] == '-' && buf[1] == ' ' )
+               state = fhdrCHECKDashEscaped2;
+           else {
+               state = fhdrREADClearsig;
            }
            break;
 
@@ -605,7 +702,6 @@ fake_packet( armor_filter_context_t *afx, IOBUF a,
            break;
 
          case fhdrENDClearsig:
-           log_debug("endclearsig: emplines=%u n=%u\n", emplines, n );
            assert( emplines );
            emplines--; /* don't count the last one */
            state = fhdrENDClearsigHelp;
@@ -760,12 +856,14 @@ armor_filter( void *opaque, int control,
     int  idx, idx2;
     size_t n=0;
     u32 crc;
+  #if 0
     static FILE *fp ;
 
     if( !fp ) {
        fp = fopen("armor.out", "w");
        assert(fp);
     }
+  #endif
 
     if( DBG_FILTER )
        log_debug("armor-filter: control: %d\n", control );
@@ -794,8 +892,14 @@ armor_filter( void *opaque, int control,
            rc = fake_packet( afx, a, &n, buf, size );
        else if( !afx->inp_checked ) {
            rc = check_input( afx, a );
-           if( afx->inp_bypass )
-               ;
+           if( afx->inp_bypass ) {
+               for( n=0; n < size && n < afx->helplen; n++ )
+                   buf[n] = afx->helpbuf[n];
+               if( !n )
+                   rc = -1;
+               assert( n == afx->helplen );
+               afx->helplen = 0;
+           }
            else if( afx->faked ) {
                /* the buffer is at least 30 bytes long, so it
                 * is easy to construct the packets */
@@ -824,9 +928,11 @@ armor_filter( void *opaque, int control,
        }
        else
            rc = radix64_read( afx, a, &n, buf, size );
+      #if 0
        if( n )
            if( fwrite(buf, n, 1, fp ) != 1 )
                BUG();
+      #endif
        *ret_len = n;
     }
     else if( control == IOBUFCTRL_FLUSH ) {
@@ -836,8 +942,9 @@ armor_filter( void *opaque, int control,
            iobuf_writestr(a, "-----");
            iobuf_writestr(a, head_strings[afx->what] );
            iobuf_writestr(a, "-----\n");
-           iobuf_writestr(a, "Version: G10 pre-release "  VERSION "\n");
-           iobuf_writestr(a, "Comment: This is an alpha test version!\n\n");
+           iobuf_writestr(a, "Version: G10 v"  VERSION " ("
+                                           PRINTABLE_OS_NAME ")\n");
+           iobuf_writestr(a, "Comment: This is an alpha version!\n\n");
            afx->status++;
            afx->idx = 0;
            afx->idx2 = 0;
@@ -936,3 +1043,5 @@ armor_filter( void *opaque, int control,
     return rc;
 }
 
+
+