X-Git-Url: http://git.gnupg.org/cgi-bin/gitweb.cgi?p=gnupg.git;a=blobdiff_plain;f=g10%2Farmor.c;h=fb74655952a74cdfb1fe717d6f113df03bebf3bd;hp=8f22f316c8e00ec86580c64af0e35c4bef581a97;hb=29beea6462cca32d3278b0f7f9364ff4342327b8;hpb=0173cd5a9810622e38b76123528e83024fb59a0c diff --git a/g10/armor.c b/g10/armor.c index 8f22f316c..fb7465595 100644 --- a/g10/armor.c +++ b/g10/armor.c @@ -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 . */ #include @@ -25,11 +23,10 @@ #include #include #include -#include #include #include "gpg.h" -#include "errors.h" +#include "status.h" #include "iobuf.h" #include "util.h" #include "filter.h" @@ -139,7 +136,7 @@ release_armor_context (armor_filter_context_t *afx) { if (!afx) return; - assert (afx->refcount); + log_assert (afx->refcount); if ( --afx->refcount ) return; xfree (afx); @@ -149,7 +146,7 @@ release_armor_context (armor_filter_context_t *afx) int push_armor_filter (armor_filter_context_t *afx, iobuf_t iobuf) { - int rc; + int rc; afx->refcount++; rc = iobuf_push_filter (iobuf, armor_filter, afx); @@ -195,36 +192,77 @@ initialize(void) /**************** * 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. + * parse-packet.c for details on this code. * Returns: True if it seems to be armored */ static int 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; } @@ -276,7 +314,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++ ) ; @@ -288,8 +326,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 ) ) @@ -310,7 +346,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. @@ -340,7 +388,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) { @@ -404,9 +453,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; } @@ -416,20 +465,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; } @@ -509,7 +574,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; } } @@ -614,8 +679,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; @@ -625,9 +691,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"); } } @@ -636,10 +702,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 @@ -770,8 +835,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; @@ -889,11 +973,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 } @@ -953,7 +1037,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 ) @@ -972,7 +1056,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(); @@ -980,28 +1064,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 ) @@ -1048,10 +1125,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); } @@ -1153,21 +1244,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, '='); } @@ -1219,7 +1309,7 @@ armor_filter( void *opaque, int control, release_armor_context (afx); } else if( control == IOBUFCTRL_DESC ) - *(char**)buf = "armor_filter"; + mem2str (buf, "armor_filter", *ret_len); return rc; } @@ -1255,7 +1345,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 { @@ -1263,7 +1353,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, @@ -1302,12 +1392,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 */ @@ -1318,9 +1408,9 @@ unarmor_pump (UnarmorPump x, int c) switch (x->state) { case STA_init: - { + { byte tmp[1]; - tmp[0] = c; + tmp[0] = c; if ( is_armored (tmp) ) x->state = c == '-'? STA_first_dash : STA_wait_newline; else { @@ -1343,10 +1433,10 @@ unarmor_pump (UnarmorPump x, int c) x->state = STA_compare_header; 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: @@ -1393,7 +1483,7 @@ unarmor_pump (UnarmorPump x, int c) break; } } - + switch(x->pos) { case 0: x->val = c << 2; @@ -1434,7 +1524,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;