last local commit
[gnupg.git] / g10 / armor.c
index 541ba3e..aa56090 100644 (file)
@@ -34,6 +34,7 @@
 #include "options.h"
 #include "main.h"
 #include "status.h"
+#include "i18n.h"
 
 
 #define CRCINIT 0xB704CE
@@ -51,7 +52,7 @@ static int is_initialized;
 
 
 typedef enum {
-    fhdrHASArmor,
+    fhdrHASArmor = 0,
     fhdrNOArmor,
     fhdrINIT,
     fhdrINITCont,
@@ -62,6 +63,7 @@ typedef enum {
     fhdrSKIPHeader,
     fhdrCLEARSIG,
     fhdrREADClearsig,
+    fhdrNullClearsig,
     fhdrEMPTYClearsig,
     fhdrCHECKClearsig,
     fhdrCHECKClearsig2,
@@ -87,7 +89,9 @@ static char *head_strings[] = {
     "BEGIN PGP PUBLIC KEY BLOCK",
     "BEGIN PGP SIGNATURE",
     "BEGIN PGP SIGNED MESSAGE",
-    "BEGIN PGP ARMORED FILE",
+    "BEGIN PGP ARMORED FILE",       /* gnupg extension */
+    "BEGIN PGP PRIVATE KEY BLOCK",
+    "BEGIN PGP SECRET KEY BLOCK",   /* only used by pgp2 */
     NULL
 };
 static char *tail_strings[] = {
@@ -96,6 +100,8 @@ static char *tail_strings[] = {
     "END PGP SIGNATURE",
     "END dummy",
     "END PGP ARMORED FILE",
+    "END PGP PRIVATE KEY BLOCK",
+    "END PGP SECRET KEY BLOCK",
     NULL
 };
 
@@ -103,7 +109,8 @@ static char *tail_strings[] = {
 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 *r_hashes );
+                                unsigned *r_empty, int *r_hashes,
+                                int only_keyblocks );
 
 
 static void
@@ -153,11 +160,13 @@ is_armored( byte *buf )
     pkttype =  ctb & 0x40 ? (ctb & 0x3f) : ((ctb>>2)&0xf);
     switch( pkttype ) {
       case PKT_MARKER:
-      case PKT_PUBLIC_CERT:
-      case PKT_SECRET_CERT:
+      case PKT_SYMKEY_ENC:
+      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_COMPRESSED:
       case PKT_ENCRYPTED:
@@ -252,7 +261,8 @@ parse_hash_header( const char *line )
  */
 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 *r_hashes )
+            IOBUF a, size_t n, unsigned *r_empty, int *r_hashes,
+            int only_keyblocks )
 {
     int c=0, i;
     const char *s;
@@ -265,7 +275,7 @@ find_header( fhdr_state_t state, byte *buf, size_t *r_buflen,
 
     buflen = *r_buflen;
     assert(buflen >= 100 );
-    buflen -= 3; /* reserved room for CR,LF and one extra */
+    buflen -= 4; /* reserved room for CR,LF, and two extra */
 
     do {
        switch( state ) {
@@ -273,14 +283,17 @@ find_header( fhdr_state_t state, byte *buf, size_t *r_buflen,
            /* read at least the first byte to check whether it is armored
             * or not */
            c = 0;
-           for(n=0; n < 28 && (c=iobuf_get2(a)) != -1 && c != '\n'; )
+           for(n=0; n < 28 && (c=iobuf_get(a)) != -1 && c != '\n'; )
                buf[n++] = c;
            if( !n && c == '\n' )
                state = fhdrCHECKBegin;
            else if( !n  || c == -1 )
                state = fhdrNOArmor; /* too short */
-           else if( !is_armored( buf ) )
+           else if( !is_armored( buf ) ) {
                state = fhdrNOArmor;
+               if( c == '\n' )
+                   buf[n++] = c;
+           }
            else if( c == '\n' )
                state = fhdrCHECKBegin;
            else
@@ -291,7 +304,7 @@ find_header( fhdr_state_t state, byte *buf, size_t *r_buflen,
            n = 0;
          case fhdrINITCont: /* read more stuff into buffer */
            c = 0;
-           for(; n < buflen && (c=iobuf_get2(a)) != -1 && c != '\n'; )
+           for(; n < buflen && (c=iobuf_get(a)) != -1 && c != '\n'; )
                buf[n++] = c;
            state = c == '\n' ? fhdrCHECKBegin :
                     c == -1  ? fhdrEOF : fhdrINITSkip;
@@ -301,36 +314,36 @@ find_header( fhdr_state_t state, byte *buf, size_t *r_buflen,
            if( c == '\n' )
                n = 0;
            else {
-               while( (c=iobuf_get2(a)) != -1 && c != '\n' )
+               while( (c=iobuf_get(a)) != -1 && c != '\n' )
                    ;
            }
            state =  c == -1? fhdrEOF : fhdrINIT;
            break;
 
          case fhdrSKIPHeader:
-           while( (c=iobuf_get2(a)) != -1 && c != '\n' )
+           while( (c=iobuf_get(a)) != -1 && c != '\n' )
                ;
            state =  c == -1? fhdrEOF : fhdrWAITHeader;
            break;
 
          case fhdrWAITHeader: /* wait for Header lines */
            c = 0;
-           for(n=0; n < buflen && (c=iobuf_get2(a)) != -1 && c != '\n'; )
+           for(n=0; n < buflen && (c=iobuf_get(a)) != -1 && c != '\n'; )
                buf[n++] = c;
            buf[n] = 0;
            if( n < buflen || c == '\n' ) {
                if( n && buf[0] != '\r') { /* maybe a header */
                    if( strchr( buf, ':') ) { /* yes */
-                       int hashes;
+                       int hashes=0;
                        if( buf[n-1] == '\r' )
                            buf[--n] = 0;
                        if( opt.verbose ) {
-                           log_info("armor header: ");
+                           log_info(_("armor header: "));
                            print_string( stderr, buf, n, 0 );
                            putc('\n', stderr);
                        }
                        if( clearsig && !(hashes=parse_hash_header( buf )) ) {
-                           log_error("invalid clearsig header\n");
+                           log_error(_("invalid clearsig header\n"));
                            state = fhdrERROR;
                        }
                        else {
@@ -339,6 +352,8 @@ find_header( fhdr_state_t state, byte *buf, size_t *r_buflen,
                                *r_hashes |= hashes;
                        }
                    }
+                   else if( clearsig && n > 15 && !memcmp(buf, "-----", 5 ) )
+                       state = fhdrNullClearsig;
                    else
                        state = fhdrCHECKDashEscaped3;
                }
@@ -354,7 +369,7 @@ find_header( fhdr_state_t state, byte *buf, size_t *r_buflen,
                    }
                }
                else {
-                   log_error("invalid armor header: ");
+                   log_error(_("invalid armor header: "));
                    print_string( stderr, buf, n, 0 );
                    putc('\n', stderr);
                    state = fhdrERROR;
@@ -363,7 +378,7 @@ 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*/
                    if( opt.verbose ) {
-                       log_info("armor header: ");
+                       log_info(_("armor header: "));
                        print_string( stderr, buf, n, 0 );
                        fputs("[...]\n", stderr);  /* indicate it is truncated */
                    }
@@ -376,14 +391,14 @@ find_header( fhdr_state_t state, byte *buf, size_t *r_buflen,
                state = fhdrEOF;
            break;
 
-         case fhdrWAITClearsig: /* skip all empty lines (for clearsig) */
+         case fhdrWAITClearsig: /* skip the empty line (for clearsig) */
            c = 0;
-           for(n=0; n < buflen && (c=iobuf_get2(a)) != -1 && c != '\n'; )
+           for(n=0; n < buflen && (c=iobuf_get(a)) != -1 && c != '\n'; )
                buf[n++] = c;
            if( n < buflen || c == '\n' ) {
                buf[n] = 0;
-               if( !n || (buf[0]=='\r' && !buf[1]) ) /* empty line */
-                   ;
+               if( n > 15 && !memcmp(buf, "-----", 5 ) )
+                   state = fhdrNullClearsig;
                else
                    state = fhdrCHECKDashEscaped3;
            }
@@ -395,6 +410,10 @@ find_header( fhdr_state_t state, byte *buf, size_t *r_buflen,
            }
            break;
 
+         case fhdrNullClearsig: /* zero length cleartext */
+           state = fhdrENDClearsig;
+           break;
+
          case fhdrENDClearsig:
          case fhdrCHECKBegin:
            state = state == fhdrCHECKBegin ? fhdrINITSkip : fhdrERRORShow;
@@ -418,13 +437,16 @@ find_header( fhdr_state_t state, byte *buf, size_t *r_buflen,
                    break;
            if( !s )
                break; /* unknown begin line */
+           if( only_keyblocks && i != 1 && i != 5 && i != 6 )
+               break; /* not a keyblock armor */
+
            /* found the begin line */
            hdr_line = i;
            state = fhdrWAITHeader;
            if( hdr_line == BEGIN_SIGNED_MSG_IDX )
                clearsig = 1;
            if( opt.verbose > 1 )
-               log_info("armor: %s\n", head_strings[hdr_line]);
+               log_info(_("armor: %s\n"), head_strings[hdr_line]);
            break;
 
          case fhdrCLEARSIG:
@@ -434,7 +456,7 @@ find_header( fhdr_state_t state, byte *buf, size_t *r_buflen,
             * we have to look for a header line or dashed escaped text*/
            n = 0;
            c = 0;
-           while( n < buflen && (c=iobuf_get2(a)) != -1 && c != '\n' )
+           while( n < buflen && (c=iobuf_get(a)) != -1 && c != '\n' )
                buf[n++] = c;
            buf[n] = 0;
            if( c == -1 )
@@ -468,7 +490,7 @@ find_header( fhdr_state_t state, byte *buf, size_t *r_buflen,
                                 fhdrREADClearsig : fhdrTESTSpaces;
            }
            else {
-               log_error("invalid dash escaped line: ");
+               log_error(_("invalid dash escaped line: "));
                print_string( stderr, buf, n, 0 );
                putc('\n', stderr);
                state = fhdrERROR;
@@ -502,7 +524,7 @@ find_header( fhdr_state_t state, byte *buf, size_t *r_buflen,
             * for dashed escaped text of headers */
            c = 0;
            n = 0;
-           while( n < buflen && (c=iobuf_get2(a)) != -1 && c != '\n' )
+           while( n < buflen && (c=iobuf_get(a)) != -1 && c != '\n' )
                buf[n++] = c;
            buf[n] = 0;
            if( c == -1 )
@@ -520,7 +542,7 @@ find_header( fhdr_state_t state, byte *buf, size_t *r_buflen,
             * counting spaces is not enough, because it may be a
             * mix of different white space characters */
            IOBUF b = iobuf_temp();
-           while( (c=iobuf_get2(a)) != -1 && c != '\n' ) {
+           while( (c=iobuf_get(a)) != -1 && c != '\n' ) {
                iobuf_put(b,c);
                if( c != ' ' && c != '\t' && c != '\r' )
                    break;
@@ -537,7 +559,7 @@ find_header( fhdr_state_t state, byte *buf, size_t *r_buflen,
          } break;
 
          case fhdrERRORShow:
-           log_error("invalid clear text header: ");
+           log_error(_("invalid clear text header: "));
            print_string( stderr, buf, n, 0 );
            putc('\n', stderr);
            state = fhdrERROR;
@@ -588,7 +610,6 @@ find_header( fhdr_state_t state, byte *buf, size_t *r_buflen,
        }
     }
 
-
     *r_buflen = n;
     *r_empty = empty;
     return state;
@@ -609,7 +630,8 @@ check_input( armor_filter_context_t *afx, IOBUF a )
 
     n = DIM(afx->helpbuf);
     state = find_header( state, afx->helpbuf, &n, a,
-                               afx->helplen, &emplines, &afx->hashes);
+                               afx->helplen, &emplines, &afx->hashes,
+                               afx->only_keyblocks );
     switch( state ) {
       case fhdrNOArmor:
        afx->inp_checked = 1;
@@ -625,6 +647,7 @@ check_input( armor_filter_context_t *afx, IOBUF a )
        rc = -1;
        break;
 
+      case fhdrNullClearsig:
       case fhdrCLEARSIG: /* start fake package mode (for clear signatures) */
        afx->helplen = n;
        afx->helpidx = 0;
@@ -677,7 +700,8 @@ fake_packet( armor_filter_context_t *afx, IOBUF a,
            rc = -1;
            continue;
        }
-       if( afx->helpidx < afx->helplen ) { /* flush the last buffer */
+       if( state != fhdrNullClearsig
+           && afx->helpidx < afx->helplen ) { /* flush the last buffer */
            n = afx->helplen;
            for(nn=afx->helpidx; len < size && nn < n ; nn++ )
                buf[len++] = afx->helpbuf[nn];
@@ -691,8 +715,10 @@ fake_packet( armor_filter_context_t *afx, IOBUF a,
        /* read a new one */
        n = DIM(afx->helpbuf);
        afx->helpidx = 0;
-       state = find_header( state, afx->helpbuf, &n, a, 0,
-                                               &emplines, &afx->hashes );
+       state = find_header( state, afx->helpbuf, &n, a,
+                             state == fhdrNullClearsig? afx->helplen:0,
+                                               &emplines, &afx->hashes,
+                                               afx->only_keyblocks );
        switch( state) {
          case fhdrERROR:
            invalid_armor();
@@ -711,13 +737,6 @@ fake_packet( armor_filter_context_t *afx, IOBUF a,
            break;
 
          case fhdrENDClearsig:
-           /* FIXME: this is wrong: Only the last CRLF should
-            * not be included in the hash, muts rewrite the FSM again
-            * This proble does only occur if the last line does not end
-            * in with a LF?
-            */
-           if( emplines )
-               emplines--; /* don't count the last one */
            state = fhdrENDClearsigHelp;
            afx->helplen = n;
            break;
@@ -728,8 +747,10 @@ fake_packet( armor_filter_context_t *afx, IOBUF a,
     buf[0] = (len-2) >> 8;
     buf[1] = (len-2);
     if( state == fhdrENDClearsig ) { /* write last (ending) length header */
-       buf[len++] = 0;
-       buf[len++] = 0;
+       if( buf[0] || buf[1] ) { /* write only if length of text is > 0 */
+           buf[len++] = 0;
+           buf[len++] = 0;
+       }
        rc = 0;
     }
 
@@ -770,7 +791,7 @@ radix64_read( armor_filter_context_t *afx, IOBUF 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) {
@@ -788,7 +809,10 @@ radix64_read( armor_filter_context_t *afx, IOBUF a, size_t *retn,
     afx->idx = idx;
     afx->radbuf[0] = val;
     if( checkcrc ) {
-       afx->inp_eof = 1; /*assume eof */
+       afx->any_data = 1;
+       afx->inp_checked=0;
+       afx->faked = 0;
+       afx->parse_state = 0;
        for(;;) { /* skip lf and pad characters */
            if( afx->helpidx < afx->helplen )
                c = afx->helpbuf[afx->helpidx++];
@@ -800,7 +824,7 @@ radix64_read( armor_filter_context_t *afx, IOBUF a, size_t *retn,
            break;
        }
        if( c == -1 )
-           log_error("premature eof (no CRC)\n");
+           log_error(_("premature eof (no CRC)\n"));
        else {
            u32 mycrc = 0;
            idx = 0;
@@ -818,13 +842,19 @@ radix64_read( armor_filter_context_t *afx, IOBUF a, size_t *retn,
                else if( (c=iobuf_get(a)) == -1 )
                    break;
            } while( ++idx < 4 );
-           if( c == -1 )
-               log_error("premature eof (in CRC)\n");
-           else if( idx != 4 )
-               log_error("malformed CRC\n");
-           else if( mycrc != afx->crc )
-               log_error("CRC error; %06lx - %06lx\n",
+           if( c == -1 ) {
+               log_error(_("premature eof (in CRC)\n"));
+               rc = G10ERR_INVALID_ARMOR;
+           }
+           else if( idx != 4 ) {
+               log_error(_("malformed CRC\n"));
+               rc = G10ERR_INVALID_ARMOR;
+           }
+           else if( mycrc != afx->crc ) {
+               log_error(_("CRC error; %06lx - %06lx\n"),
                                    (ulong)afx->crc, (ulong)mycrc);
+               rc = G10ERR_INVALID_ARMOR;
+           }
            else {
                rc = 0;
              #if 0
@@ -839,10 +869,14 @@ radix64_read( armor_filter_context_t *afx, IOBUF a, size_t *retn,
                }
                if( rc == -1 )
                    rc = 0;
-               else if( rc == 2 )
-                   log_error("premature eof (in Trailer)\n");
-               else
-                   log_error("error in trailer line\n");
+               else if( rc == 2 ) {
+                   log_error(_("premature eof (in Trailer)\n"));
+                   rc = G10ERR_INVALID_ARMOR;
+               }
+               else {
+                   log_error(_("error in trailer line\n"));
+                   rc = G10ERR_INVALID_ARMOR;
+               }
              #endif
            }
        }
@@ -855,7 +889,6 @@ radix64_read( armor_filter_context_t *afx, IOBUF a, size_t *retn,
     return rc;
 }
 
-
 /****************
  * This filter is used to handle the armor stuff
  */
@@ -895,13 +928,6 @@ armor_filter( void *opaque, int control,
        if( size < 15+(4*15) )  /* need space for up to 4 onepass_sigs */
            BUG(); /* supplied buffer too short */
 
-       if( afx->inp_eof ) {
-           *ret_len = 0;
-           if( DBG_FILTER )
-               log_debug("armor-filter: eof due to inp_eof flag\n" );
-           return -1;
-       }
-
        if( afx->faked )
            rc = fake_packet( afx, a, &n, buf, size );
        else if( !afx->inp_checked ) {
@@ -984,7 +1010,25 @@ armor_filter( void *opaque, int control,
            iobuf_writestr(a, "-----\n");
            iobuf_writestr(a, "Version: GNUPG v"  VERSION " ("
                                            PRINTABLE_OS_NAME ")\n");
-           iobuf_writestr(a, "Comment: This is an alpha version!\n");
+
+           if( opt.comment_string ) {
+               const char *s = opt.comment_string;
+               iobuf_writestr(a, "Comment: " );
+               for( ; *s; s++ ) {
+                   if( *s == '\n' )
+                       iobuf_writestr(a, "\\n" );
+                   else if( *s == '\r' )
+                       iobuf_writestr(a, "\\r" );
+                   else if( *s == '\v' )
+                       iobuf_writestr(a, "\\v" );
+                   else
+                       iobuf_put(a, *s );
+               }
+               iobuf_put(a, '\n' );
+           }
+           else
+               iobuf_writestr(a,
+                   "Comment: For info finger gcrypt@ftp.guug.de\n");
            if( afx->hdrlines )
                iobuf_writestr(a, afx->hdrlines);
            iobuf_put(a, '\n');
@@ -1083,6 +1127,8 @@ armor_filter( void *opaque, int control,
            iobuf_writestr(a, tail_strings[afx->what] );
            iobuf_writestr(a, "-----\n");
        }
+       else if( !afx->any_data && !afx->inp_bypass )
+           log_error(_("no valid RFC1991 or OpenPGP data found.\n"));
     }
     else if( control == IOBUFCTRL_DESC )
        *(char**)buf = "armor_filter";