diff --git a/g10/armor.c b/g10/armor.c index be03692f1..1ae3c6062 100644 --- a/g10/armor.c +++ b/g10/armor.c @@ -1,1551 +1,1551 @@ /* armor.c - Armor flter * 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 3 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, see . */ #include #include #include #include #include #include #include #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 "status.h" #include "i18n.h" #define MAX_LINELEN 20000 #define CRCINIT 0xB704CE #define CRCPOLY 0X864CFB #define CRCUPDATE(a,c) do { \ a = ((a) << 8) ^ crc_table[((a)&0xff >> 16) ^ (c)]; \ a &= 0x00ffffff; \ } while(0) static u32 crc_table[256]; static byte bintoasc[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" "abcdefghijklmnopqrstuvwxyz" "0123456789+/"; static byte asctobin[256]; /* runtime initialized */ static int is_initialized; typedef enum { fhdrHASArmor = 0, fhdrNOArmor, fhdrINIT, fhdrINITCont, fhdrINITSkip, fhdrCHECKBegin, fhdrWAITHeader, fhdrWAITClearsig, fhdrSKIPHeader, fhdrCLEARSIG, fhdrREADClearsig, fhdrNullClearsig, fhdrEMPTYClearsig, fhdrCHECKClearsig, fhdrCHECKClearsig2, fhdrCHECKDashEscaped, fhdrCHECKDashEscaped2, fhdrCHECKDashEscaped3, fhdrREADClearsigNext, fhdrENDClearsig, fhdrENDClearsigHelp, fhdrTESTSpaces, fhdrCLEARSIGSimple, fhdrCLEARSIGSimpleNext, fhdrTEXT, fhdrTEXTSimple, fhdrERROR, fhdrERRORShow, fhdrEOF } fhdr_state_t; /* if we encounter this armor string with this index, go * into a mode which fakes packets and wait for the next armor */ #define BEGIN_SIGNATURE 2 #define BEGIN_SIGNED_MSG_IDX 3 static char *head_strings[] = { "BEGIN PGP MESSAGE", "BEGIN PGP PUBLIC KEY BLOCK", "BEGIN PGP SIGNATURE", "BEGIN PGP SIGNED MESSAGE", "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[] = { "END PGP MESSAGE", "END PGP PUBLIC KEY BLOCK", "END PGP SIGNATURE", "END dummy", "END PGP ARMORED FILE", "END PGP PRIVATE KEY BLOCK", "END PGP SECRET KEY BLOCK", NULL }; /* 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; /* In contrast to 2.0, we use in 1.4 heap based contexts only in a very few places and in general keep the stack based contexts. A REFCOUNT of 0 indicates a stack based context and thus we don't do anything in this case. */ if (!afx->refcount) return; 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; if (!afx->refcount) return iobuf_push_filter (iobuf, armor_filter, afx); afx->refcount++; rc = iobuf_push_filter (iobuf, armor_filter, afx); if (rc) afx->refcount--; return rc; } static void initialize(void) { int i, j; u32 t; byte *s; /* init the crc lookup table */ crc_table[0] = 0; for(i=j=0; j < 128; j++ ) { t = crc_table[j]; if( t & 0x00800000 ) { t <<= 1; crc_table[i++] = t ^ CRCPOLY; crc_table[i++] = t; } else { t <<= 1; crc_table[i++] = t; crc_table[i++] = t ^ CRCPOLY; } } /* build the helptable for radix64 to bin conversion */ for(i=0; i < 256; i++ ) asctobin[i] = 255; /* used to detect invalid characters */ for(s=bintoasc,i=0; *s; s++,i++ ) asctobin[*s] = i; 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. * Returns: True if it seems to be armored */ static int is_armored( const byte *buf ) { int ctb, pkttype; ctb = *buf; if( !(ctb & 0x80) ) return 1; /* invalid packet: assume it is armored */ pkttype = ctb & 0x40 ? (ctb & 0x3f) : ((ctb>>2)&0xf); switch( pkttype ) { case PKT_MARKER: 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_COMPRESSED: case PKT_ENCRYPTED: return 0; /* seems to be a regular packet: not armored */ } return 1; } /**************** * Try to check whether 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; /* fixme: there might be a problem with iobuf_peek */ n = iobuf_peek(a, buf, 1 ); if( n == -1 ) return 0; /* EOF, doesn't matter whether armored or not */ if( !n ) return 1; /* can't check it: try armored */ return is_armored(buf); } static void invalid_armor(void) { write_status(STATUS_BADARMOR); g10_exit(1); /* stop here */ } /**************** * check whether 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 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, "SHA224", s2-s ) ) found |= 8; else if( !strncmp( s, "SHA256", s2-s ) ) found |= 16; else if( !strncmp( s, "SHA384", s2-s ) ) found |= 32; else if( !strncmp( s, "SHA512", s2-s ) ) found |= 64; else return 0; for(; *s2 && (*s2==' ' || *s2 == '\t'); s2++ ) ; if( *s2 && *s2 != ',' ) return 0; if( *s2 ) s2++; } 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. * returns: -1 if it is not a armor header or the index number of the * armor header. */ static int is_armor_header( byte *line, unsigned len ) { const char *s; byte *save_p, *p; int save_c; int i; if( len < 15 ) return -1; /* too short */ if( memcmp( line, "-----", 5 ) ) return -1; /* no */ p = strstr( line+5, "-----"); if( !p ) return -1; save_p = p; p += 5; /* 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. 4880 clarified this was only 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 ) return -1; /* garbage after dashes */ save_c = *save_p; *save_p = 0; p = line+5; for(i=0; (s=head_strings[i]); i++ ) if( !strcmp(s, p) ) break; *save_p = save_c; if( !s ) return -1; /* unknown armor line */ if( opt.verbose > 1 ) log_info(_("armor: %s\n"), head_strings[i]); return i; } /* Helper to parse a "KEY FAILED " line and return the error code. LINEPTR points right behind "KEY ". */ int parse_key_failed_line (const void *lineptr, unsigned int len) { const byte *line = lineptr; int code = 0; for (; len && !spacep (line); len--, line++) ; for (; len && spacep (line); len--, line++) ; if (len > 7 && !memcmp (line, "FAILED ", 7)) { line += 7; len -= 7; for (; len && digitp (line); len--, line++) { code *= 10; code += atoi_1 (line); } } return code; } /**************** * Parse a header lines * Return 0: Empty line (end of header lines) * -1: invalid header line * >0: Good header line */ static int parse_header_line( armor_filter_context_t *afx, byte *line, unsigned int len ) { byte *p; int hashes=0; unsigned int len2; 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 */ } /* 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 || (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: ")); print_string( stderr, line, len, 0 ); putc('\n', stderr); } if( afx->in_cleartext ) { if( (hashes=parse_hash_header( line )) ) afx->hashes |= hashes; else if( strlen(line) > 15 && !memcmp( line, "NotDashEscaped:", 15 ) ) 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: ")); print_string( stderr, line, len, 0 ); putc('\n', stderr); } return 1; } /* figure out whether the data is armored or not */ static int check_input( armor_filter_context_t *afx, IOBUF a ) { int rc = 0; int i; byte *line; unsigned len; unsigned maxlen; int hdr_line = -1; /* read the first line to see whether this is armored data */ maxlen = MAX_LINELEN; len = afx->buffer_len = iobuf_read_line( a, &afx->buffer, &afx->buffer_size, &maxlen ); line = afx->buffer; if( !maxlen ) { /* line has been truncated: assume not armored */ afx->inp_checked = 1; afx->inp_bypass = 1; return 0; } if( !len ) { return -1; /* eof */ } /* (the line is always a C string but maybe longer) */ if( *line == '\n' || ( len && (*line == '\r' && line[1]=='\n') ) ) ; else if( !is_armored( line ) ) { afx->inp_checked = 1; afx->inp_bypass = 1; return 0; } /* find the armor header */ while(len) { i = is_armor_header( line, len ); if (i == -1 && afx->only_keyblocks && !afx->key_failed_code && len > 4 && !memcmp (line, "KEY ", 4)) { /* This is probably input from a keyserver helper and we have not yet seen an error line. */ afx->key_failed_code = parse_key_failed_line (line+4, len-4); } if( i >= 0 && !(afx->only_keyblocks && i != 1 && i != 5 && i != 6 )) { hdr_line = i; if( hdr_line == BEGIN_SIGNED_MSG_IDX ) { if( afx->in_cleartext ) { log_error(_("nested clear text signatures\n")); rc = G10ERR_INVALID_ARMOR; } afx->in_cleartext = 1; } break; } /* read the next line (skip all truncated lines) */ do { maxlen = MAX_LINELEN; afx->buffer_len = iobuf_read_line( a, &afx->buffer, &afx->buffer_size, &maxlen ); line = afx->buffer; len = afx->buffer_len; } while( !maxlen ); } /* Parse the header lines. */ while(len) { /* Read the next line (skip all truncated lines). */ do { maxlen = MAX_LINELEN; afx->buffer_len = iobuf_read_line( a, &afx->buffer, &afx->buffer_size, &maxlen ); line = afx->buffer; len = afx->buffer_len; } while( !maxlen ); i = parse_header_line( afx, line, len ); if( i <= 0 ) { if (i && RFC2440) rc = G10ERR_INVALID_ARMOR; break; } } if( rc ) invalid_armor(); else if( afx->in_cleartext ) afx->faked = 1; else { afx->inp_checked = 1; afx->crc = CRCINIT; afx->idx = 0; afx->radbuf[0] = 0; } return rc; } #define PARTIAL_CHUNK 512 #define PARTIAL_POW 9 /**************** * Fake a literal data packet and wait for the next armor line * fixme: empty line handling and null length clear text signature are * not implemented/checked. */ static int fake_packet( armor_filter_context_t *afx, IOBUF a, size_t *retn, byte *buf, size_t size ) { int rc = 0; size_t len = 0; int lastline = 0; unsigned maxlen, n; byte *p; byte tempbuf[PARTIAL_CHUNK]; size_t tempbuf_len=0; 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 { /* 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; afx->buffer_pos = 0; afx->buffer_len = iobuf_read_line( a, &afx->buffer, &afx->buffer_size, &maxlen ); if( !afx->buffer_len ) { rc = -1; /* eof (should not happen) */ continue; } if( !maxlen ) afx->truncated++; 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[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 * the original type of lineending - handling of the removed * trailing spaces seems to be impossible in our method * of faking a packet; either we have to use a temporary file * or calculate the hash here in this module and somehow find * a way to send the hash down the processing line (well, a special * faked packet could do the job). */ if( crlf ) afx->buffer[afx->buffer_len++] = '\r'; afx->buffer[afx->buffer_len++] = '\n'; afx->buffer[afx->buffer_len] = '\0'; } } if( lastline ) { /* write last (ending) length header */ 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; /* and now read the header lines */ afx->buffer_pos = 0; for(;;) { int i; /* read the next line (skip all truncated lines) */ do { maxlen = MAX_LINELEN; afx->buffer_len = iobuf_read_line( a, &afx->buffer, &afx->buffer_size, &maxlen ); } while( !maxlen ); p = afx->buffer; n = afx->buffer_len; if( !n ) { rc = -1; break; /* eof */ } i = parse_header_line( afx, p , n ); if( i <= 0 ) { if( i ) invalid_armor(); break; } } afx->inp_checked = 1; afx->crc = CRCINIT; afx->idx = 0; afx->radbuf[0] = 0; } *retn = len; return rc; } static int invalid_crc(void) { if ( opt.ignore_crc_error ) return 0; log_inc_errorcount(); return G10ERR_INVALID_ARMOR; } static int radix64_read( armor_filter_context_t *afx, IOBUF a, size_t *retn, byte *buf, size_t size ) { byte val; int c=0, c2; /*init c because gcc is not clever enough for the continue*/ int checkcrc=0; int rc = 0; size_t n = 0; int idx, i, onlypad=0; u32 crc; crc = afx->crc; idx = afx->idx; val = afx->radbuf[0]; for( n=0; n < size; ) { if( afx->buffer_pos < afx->buffer_len ) c = afx->buffer[afx->buffer_pos++]; else { /* read the next line */ unsigned maxlen = MAX_LINELEN; afx->buffer_pos = 0; afx->buffer_len = iobuf_read_line( a, &afx->buffer, &afx->buffer_size, &maxlen ); if( !maxlen ) afx->truncated++; if( !afx->buffer_len ) break; /* eof */ continue; } again: if( c == '\n' || c == ' ' || c == '\r' || c == '\t' ) continue; else if( c == '=' ) { /* Pad character: stop or CRC sum starts. */ /* some mailers leave quoted-printable encoded characters * so we try to workaround this */ if( afx->buffer_pos+2 < afx->buffer_len ) { int cc1, cc2, cc3; cc1 = afx->buffer[afx->buffer_pos]; cc2 = afx->buffer[afx->buffer_pos+1]; cc3 = afx->buffer[afx->buffer_pos+2]; if( isxdigit(cc1) && isxdigit(cc2) && strchr( "=\n\r\t ", cc3 )) { /* well it seems to be the case - adjust */ c = isdigit(cc1)? (cc1 - '0'): (ascii_toupper(cc1)-'A'+10); c <<= 4; c |= isdigit(cc2)? (cc2 - '0'): (ascii_toupper(cc2)-'A'+10); afx->buffer_pos += 2; afx->qp_detected = 1; goto again; } } if (!n) onlypad = 1; if( idx == 1 ) buf[n++] = val; checkcrc++; break; } else if( (c = asctobin[(c2=c)]) == 255 ) { log_error(_("invalid radix64 character %02X skipped\n"), c2); continue; } switch(idx) { case 0: val = c << 2; break; case 1: val |= (c>>4)&3; buf[n++]=val;val=(c<<4)&0xf0;break; case 2: val |= (c>>2)&15; buf[n++]=val;val=(c<<6)&0xc0;break; case 3: val |= c&0x3f; buf[n++] = val; break; } idx = (idx+1) % 4; } for(i=0; i < n; i++ ) crc = (crc << 8) ^ crc_table[((crc >> 16)&0xff) ^ buf[i]]; crc &= 0x00ffffff; afx->crc = crc; afx->idx = idx; afx->radbuf[0] = val; if( checkcrc ) { afx->any_data = 1; afx->inp_checked=0; afx->faked = 0; for(;;) { /* skip lf and pad characters */ if( afx->buffer_pos < afx->buffer_len ) c = afx->buffer[afx->buffer_pos++]; else { /* read the next line */ unsigned maxlen = MAX_LINELEN; afx->buffer_pos = 0; afx->buffer_len = iobuf_read_line( a, &afx->buffer, &afx->buffer_size, &maxlen ); if( !maxlen ) afx->truncated++; if( !afx->buffer_len ) break; /* eof */ continue; } if( c == '\n' || c == ' ' || c == '\r' || c == '\t' || c == '=' ) continue; break; } if( c == -1 ) log_error(_("premature eof (no CRC)\n")); else { u32 mycrc = 0; idx = 0; do { if( (c = asctobin[c]) == 255 ) break; switch(idx) { case 0: val = c << 2; break; case 1: val |= (c>>4)&3; mycrc |= val << 16;val=(c<<4)&0xf0;break; case 2: val |= (c>>2)&15; mycrc |= val << 8;val=(c<<6)&0xc0;break; case 3: val |= c&0x3f; mycrc |= val; break; } for(;;) { if( afx->buffer_pos < afx->buffer_len ) c = afx->buffer[afx->buffer_pos++]; else { /* read the next line */ unsigned maxlen = MAX_LINELEN; afx->buffer_pos = 0; afx->buffer_len = iobuf_read_line( a, &afx->buffer, &afx->buffer_size, &maxlen ); if( !maxlen ) afx->truncated++; if( !afx->buffer_len ) break; /* eof */ continue; } break; } if( !afx->buffer_len ) break; /* eof */ } while( ++idx < 4 ); 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"), (ulong)afx->crc, (ulong)mycrc); rc = invalid_crc(); } else { rc = 0; /* FIXME: Here we should emit another control packet, * so that we know in mainproc that we are processing * a clearsign message */ #if 0 for(rc=0;!rc;) { rc = 0 /*check_trailer( &fhdr, c )*/; if( !rc ) { if( (c=iobuf_get(a)) == -1 ) rc = 2; } } if( rc == -1 ) rc = 0; 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 } } } if( !n && !onlypad ) rc = -1; *retn = n; return rc; } /**************** * This filter is used to handle the armor stuff */ int armor_filter( void *opaque, int control, IOBUF a, byte *buf, size_t *ret_len) { size_t size = *ret_len; armor_filter_context_t *afx = opaque; int rc=0, i, c; byte radbuf[3]; 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 ); if( control == IOBUFCTRL_UNDERFLOW && afx->inp_bypass ) { n = 0; if( afx->buffer_len ) { 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; } for(; n < size; n++ ) { if( (c=iobuf_get(a)) == -1 ) break; buf[n] = c & 0xff; } if( !n ) rc = -1; *ret_len = n; } else if( control == IOBUFCTRL_UNDERFLOW ) { /* 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 ) rc = fake_packet( afx, a, &n, buf, size ); else if( !afx->inp_checked ) { rc = check_input( afx, a ); if( afx->inp_bypass ) { for(n=0; n < size && afx->buffer_pos < afx->buffer_len; ) buf[n++] = afx->buffer[afx->buffer_pos++]; if( afx->buffer_pos >= afx->buffer_len ) afx->buffer_len = 0; if( !n ) rc = -1; } else if( afx->faked ) { unsigned int hashes = afx->hashes; const byte *sesmark; size_t sesmarklen; sesmark = get_session_marker( &sesmarklen ); if ( sesmarklen > 20 ) BUG(); /* 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; 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; } 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++] = 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 ) buf[n++] = DIGEST_ALGO_SHA256; if( hashes & 32 ) buf[n++] = DIGEST_ALGO_SHA384; if( hashes & 64 ) buf[n++] = DIGEST_ALGO_SHA512; buf[1] = n - 2; /* ...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; } else if( !rc ) rc = radix64_read( afx, a, &n, buf, size ); } 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 && !afx->cancel ) { if( !afx->status ) { /* write the header line */ const char *s; 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, "-----" ); iobuf_writestr(a,afx->eol); if (opt.emit_version) { iobuf_writestr (a, "Version: GnuPG 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); } /* write the comment strings */ for(s=comment->d;comment;comment=comment->next,s=comment->d) { 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_writestr(a,afx->eol); } if ( afx->hdrlines ) { for ( s = afx->hdrlines; *s; s++ ) { #ifdef HAVE_DOSISH_SYSTEM if ( *s == '\n' ) iobuf_put( a, '\r'); #endif iobuf_put(a, *s ); } } iobuf_writestr(a,afx->eol); afx->status++; afx->idx = 0; afx->idx2 = 0; afx->crc = CRCINIT; } crc = afx->crc; idx = afx->idx; idx2 = afx->idx2; for(i=0; i < idx; i++ ) radbuf[i] = afx->radbuf[i]; for(i=0; i < size; i++ ) crc = (crc << 8) ^ crc_table[((crc >> 16)&0xff) ^ buf[i]]; crc &= 0x00ffffff; for( ; size; buf++, size-- ) { radbuf[idx++] = *buf; if( idx > 2 ) { idx = 0; c = bintoasc[(*radbuf >> 2) & 077]; iobuf_put(a, c); c = bintoasc[(((*radbuf<<4)&060)|((radbuf[1] >> 4)&017))&077]; iobuf_put(a, c); c = bintoasc[(((radbuf[1]<<2)&074)|((radbuf[2]>>6)&03))&077]; 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,afx->eol); idx2=0; } } } for(i=0; i < idx; i++ ) afx->radbuf[i] = radbuf[i]; afx->idx = idx; afx->idx2 = idx2; afx->crc = crc; } else if( control == IOBUFCTRL_INIT ) { if( !is_initialized ) 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; } else if( control == IOBUFCTRL_FREE ) { if( afx->cancel ) ; else if( afx->status ) { /* pad, write cecksum, and bottom line */ 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]; iobuf_put(a, c); if( idx == 1 ) { c = bintoasc[((*radbuf << 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]; iobuf_put(a, c); c = bintoasc[((radbuf[1] << 2) & 074) & 077]; iobuf_put(a, c); iobuf_put(a, '='); } 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,afx->eol); /* write the CRC */ iobuf_put(a, '='); radbuf[0] = crc >>16; radbuf[1] = crc >> 8; radbuf[2] = crc; c = bintoasc[(*radbuf >> 2) & 077]; iobuf_put(a, c); c = bintoasc[(((*radbuf<<4)&060)|((radbuf[1] >> 4)&017))&077]; iobuf_put(a, c); c = bintoasc[(((radbuf[1]<<2)&074)|((radbuf[2]>>6)&03))&077]; iobuf_put(a, c); c = bintoasc[radbuf[2]&077]; iobuf_put(a, c); 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, "-----" ); iobuf_writestr(a,afx->eol); } else if( !afx->any_data && !afx->inp_bypass ) { log_error(_("no valid OpenPGP data found.\n")); afx->no_openpgp_data = 1; write_status_text( STATUS_NODATA, "1" ); } if( afx->truncated ) log_info(_("invalid armor: line longer than %d characters\n"), MAX_LINELEN ); /* issue an error to enforce dissemination of correct software */ if( afx->qp_detected ) log_error(_("quoted printable character in armor - " "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; } /**************** * create a radix64 encoded string. */ char * make_radix64_string( const byte *data, size_t len ) { char *buffer, *p; 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]; *p++ = bintoasc[(((data[1]<<2)&074)|((data[2]>>6)&03))&077]; *p++ = bintoasc[data[2]&077]; } if( len == 2 ) { *p++ = bintoasc[(data[0] >> 2) & 077]; *p++ = bintoasc[(((data[0] <<4)&060)|((data[1] >> 4)&017))&077]; *p++ = bintoasc[((data[1]<<2)&074)]; } else if( len == 1 ) { *p++ = bintoasc[(data[0] >> 2) & 077]; *p++ = bintoasc[(data[0] <<4)&060]; } *p = 0; return buffer; } /*********************************************** * 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 */ enum unarmor_state_e { STA_init = 0, STA_bypass, STA_wait_newline, STA_wait_dash, STA_first_dash, STA_compare_header, STA_found_header_wait_newline, STA_skip_header_lines, STA_skip_header_lines_non_ws, STA_read_data, STA_wait_crc, STA_read_crc, STA_ready }; struct unarmor_pump_s { enum unarmor_state_e state; byte val; int checkcrc; int pos; /* counts from 0..3 */ u32 crc; u32 mycrc; /* the one store in the data */ }; UnarmorPump unarmor_pump_new (void) { UnarmorPump x; if( !is_initialized ) initialize(); x = xmalloc_clear (sizeof *x); return x; } void 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 * -2 = Premature EOF (not used) * -3 = Invalid armor */ int unarmor_pump (UnarmorPump x, int c) { int rval = 256; /* default is to ignore the return value */ switch (x->state) { case STA_init: { byte tmp[1]; tmp[0] = c; if ( is_armored (tmp) ) x->state = c == '-'? STA_first_dash : STA_wait_newline; else { x->state = STA_bypass; return c; } } break; case STA_bypass: return c; /* return here to avoid crc calculation */ case STA_wait_newline: if (c == '\n') x->state = STA_wait_dash; break; case STA_wait_dash: x->state = c == '-'? STA_first_dash : STA_wait_newline; break; case STA_first_dash: /* just need for initalization */ x->pos = 0; x->state = STA_compare_header; case STA_compare_header: if ( "-----BEGIN PGP SIGNATURE-----"[++x->pos] == c ) { if ( x->pos == 28 ) x->state = STA_found_header_wait_newline; } else x->state = c == '\n'? STA_wait_dash : STA_wait_newline; break; case STA_found_header_wait_newline: /* to make CR,LF issues easier we simply allow for white space behind the 5 dashes */ if ( c == '\n' ) x->state = STA_skip_header_lines; else if ( c != '\r' && c != ' ' && c != '\t' ) x->state = STA_wait_dash; /* garbage after the header line */ break; case STA_skip_header_lines: /* i.e. wait for one empty line */ if ( c == '\n' ) { x->state = STA_read_data; x->crc = CRCINIT; x->val = 0; x->pos = 0; } else if ( c != '\r' && c != ' ' && c != '\t' ) x->state = STA_skip_header_lines_non_ws; break; case STA_skip_header_lines_non_ws: /* like above but we already encountered non white space */ if ( c == '\n' ) x->state = STA_skip_header_lines; break; case STA_read_data: /* fixme: we don't check for the trailing dash lines but rely * on the armor stop characters */ if( c == '\n' || c == ' ' || c == '\r' || c == '\t' ) break; /* skip all kind of white space */ if( c == '=' ) { /* pad character: stop */ if( x->pos == 1 ) /* in this case val has some value */ rval = x->val; x->state = STA_wait_crc; break; } { int c2; if( (c = asctobin[(c2=c)]) == 255 ) { log_error(_("invalid radix64 character %02X skipped\n"), c2); break; } } switch(x->pos) { case 0: x->val = c << 2; break; case 1: x->val |= (c>>4)&3; rval = x->val; x->val = (c<<4)&0xf0; break; case 2: x->val |= (c>>2)&15; rval = x->val; x->val = (c<<6)&0xc0; break; case 3: x->val |= c&0x3f; rval = x->val; break; } x->pos = (x->pos+1) % 4; break; case STA_wait_crc: if( c == '\n' || c == ' ' || c == '\r' || c == '\t' || c == '=' ) break; /* skip ws and pad characters */ /* assume that we are at the next line */ x->state = STA_read_crc; x->pos = 0; x->mycrc = 0; case STA_read_crc: if( (c = asctobin[c]) == 255 ) { rval = -1; /* ready */ if( x->crc != x->mycrc ) { log_info (_("CRC error; %06lX - %06lX\n"), (ulong)x->crc, (ulong)x->mycrc); if ( invalid_crc() ) rval = -3; } x->state = STA_ready; /* not sure whether this is correct */ break; } switch(x->pos) { case 0: x->val = c << 2; break; case 1: x->val |= (c>>4)&3; x->mycrc |= x->val << 16; x->val = (c<<4)&0xf0; break; case 2: x->val |= (c>>2)&15; x->mycrc |= x->val << 8; x->val = (c<<6)&0xc0; break; case 3: x->val |= c&0x3f; x->mycrc |= x->val; break; } x->pos = (x->pos+1) % 4; break; case STA_ready: rval = -1; break; } if ( !(rval & ~255) ) { /* compute the CRC */ x->crc = (x->crc << 8) ^ crc_table[((x->crc >> 16)&0xff) ^ rval]; x->crc &= 0x00ffffff; } return rval; } diff --git a/g10/cipher.c b/g10/cipher.c index 0c51100cc..45e1963f5 100644 --- a/g10/cipher.c +++ b/g10/cipher.c @@ -1,151 +1,151 @@ /* cipher.c - En-/De-ciphering filter * Copyright (C) 1998, 1999, 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 3 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, see . */ #include #include #include #include #include #include #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 "status.h" #define MIN_PARTIAL_SIZE 512 static void write_header( cipher_filter_context_t *cfx, IOBUF a ) { PACKET pkt; PKT_encrypted ed; byte temp[18]; unsigned blocksize; unsigned nprefix; blocksize = cipher_get_blocksize( cfx->dek->algo ); if( blocksize < 8 || blocksize > 16 ) log_fatal("unsupported blocksize %u\n", blocksize ); memset( &ed, 0, sizeof ed ); ed.len = cfx->datalen; ed.extralen = blocksize+2; ed.new_ctb = !ed.len && !RFC1991; if( cfx->dek->use_mdc ) { ed.mdc_method = DIGEST_ALGO_SHA1; cfx->mdc_hash = md_open( DIGEST_ALGO_SHA1, 0 ); if ( DBG_HASHING ) md_start_debug( cfx->mdc_hash, "creatmdc" ); } { char buf[20]; sprintf (buf, "%d %d", ed.mdc_method, cfx->dek->algo); write_status_text (STATUS_BEGIN_ENCRYPTION, buf); } init_packet( &pkt ); pkt.pkttype = cfx->dek->use_mdc? PKT_ENCRYPTED_MDC : PKT_ENCRYPTED; pkt.pkt.encrypted = &ed; if( build_packet( a, &pkt )) log_bug("build_packet(ENCR_DATA) failed\n"); nprefix = blocksize; randomize_buffer( temp, nprefix, 1 ); temp[nprefix] = temp[nprefix-2]; temp[nprefix+1] = temp[nprefix-1]; print_cipher_algo_note( cfx->dek->algo ); cfx->cipher_hd = cipher_open( cfx->dek->algo, cfx->dek->use_mdc? CIPHER_MODE_CFB : CIPHER_MODE_AUTO_CFB, 1 ); /* log_hexdump( "thekey", cfx->dek->key, cfx->dek->keylen );*/ cipher_setkey( cfx->cipher_hd, cfx->dek->key, cfx->dek->keylen ); cipher_setiv( cfx->cipher_hd, NULL, 0 ); /* log_hexdump( "prefix", temp, nprefix+2 ); */ if( cfx->mdc_hash ) /* hash the "IV" */ md_write( cfx->mdc_hash, temp, nprefix+2 ); cipher_encrypt( cfx->cipher_hd, temp, temp, nprefix+2); cipher_sync( cfx->cipher_hd ); iobuf_write(a, temp, nprefix+2); cfx->header=1; } /**************** * This filter is used to en/de-cipher data with a conventional algorithm */ int cipher_filter( void *opaque, int control, IOBUF a, byte *buf, size_t *ret_len) { size_t size = *ret_len; cipher_filter_context_t *cfx = opaque; int rc=0; if( control == IOBUFCTRL_UNDERFLOW ) { /* decrypt */ rc = -1; /* not yet used */ } else if( control == IOBUFCTRL_FLUSH ) { /* encrypt */ assert(a); if( !cfx->header ) { write_header( cfx, a ); } if( cfx->mdc_hash ) md_write( cfx->mdc_hash, buf, size ); cipher_encrypt( cfx->cipher_hd, buf, buf, size); if( iobuf_write( a, buf, size ) ) rc = G10ERR_WRITE_FILE; } else if( control == IOBUFCTRL_FREE ) { if( cfx->mdc_hash ) { byte *hash; int hashlen = md_digest_length( 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; md_putc( cfx->mdc_hash, temp[0] ); md_putc( cfx->mdc_hash, temp[1] ); md_final( cfx->mdc_hash ); hash = md_read( cfx->mdc_hash, 0 ); memcpy(temp+2, hash, 20); cipher_encrypt( cfx->cipher_hd, temp, temp, 22 ); md_close( cfx->mdc_hash ); cfx->mdc_hash = NULL; if( iobuf_write( a, temp, 22 ) ) log_error("writing MDC packet failed\n" ); } cipher_close(cfx->cipher_hd); } else if( control == IOBUFCTRL_DESC ) { - *(char**)buf = "cipher_filter"; + mem2str (buf, "cipher_filter", *ret_len); } return rc; } diff --git a/g10/compress-bz2.c b/g10/compress-bz2.c index baef92f3f..6c5bd66b3 100644 --- a/g10/compress-bz2.c +++ b/g10/compress-bz2.c @@ -1,252 +1,252 @@ /* compress.c - bzip2 compress filter * Copyright (C) 2003, 2004 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 3 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, see . */ #include #include #include /* Early versions of bzlib (1.0) require stdio.h */ #include #include "util.h" #include "memory.h" #include "packet.h" #include "filter.h" #include "main.h" #include "options.h" /* Note that the code in compress.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 */ static void init_compress( compress_filter_context_t *zfx, bz_stream *bzs ) { int rc; int level; if( opt.bz2_compress_level >= 1 && opt.bz2_compress_level <= 9 ) level = opt.bz2_compress_level; else if( opt.bz2_compress_level == -1 ) level = 6; /* no particular reason, but it seems reasonable */ else { log_error("invalid compression level; using default level\n"); level = 6; } if((rc=BZ2_bzCompressInit(bzs,level,0,0))!=BZ_OK) log_fatal("bz2lib problem: %d\n",rc); zfx->outbufsize = 8192; zfx->outbuf = xmalloc( zfx->outbufsize ); } static int do_compress(compress_filter_context_t *zfx, bz_stream *bzs, int flush, IOBUF a) { int zrc; unsigned n; do { bzs->next_out = zfx->outbuf; bzs->avail_out = zfx->outbufsize; if( DBG_FILTER ) log_debug("enter bzCompress: avail_in=%u, avail_out=%u, flush=%d\n", (unsigned)bzs->avail_in, (unsigned)bzs->avail_out, flush ); zrc = BZ2_bzCompress( bzs, flush ); if( zrc == BZ_STREAM_END && flush == BZ_FINISH ) ; else if( zrc != BZ_RUN_OK && zrc != BZ_FINISH_OK ) log_fatal("bz2lib deflate problem: rc=%d\n", zrc ); n = zfx->outbufsize - bzs->avail_out; if( DBG_FILTER ) log_debug("leave bzCompress:" " avail_in=%u, avail_out=%u, n=%u, zrc=%d\n", (unsigned)bzs->avail_in, (unsigned)bzs->avail_out, (unsigned)n, zrc ); if( iobuf_write( a, zfx->outbuf, n ) ) { log_debug("bzCompress: iobuf_write failed\n"); return G10ERR_WRITE_FILE; } } while( bzs->avail_in || (flush == BZ_FINISH && zrc != BZ_STREAM_END) ); return 0; } static void init_uncompress( compress_filter_context_t *zfx, bz_stream *bzs ) { int rc; if((rc=BZ2_bzDecompressInit(bzs,0,opt.bz2_decompress_lowmem))!=BZ_OK) log_fatal("bz2lib problem: %d\n",rc); zfx->inbufsize = 2048; zfx->inbuf = xmalloc( zfx->inbufsize ); bzs->avail_in = 0; } static int do_uncompress( compress_filter_context_t *zfx, bz_stream *bzs, IOBUF a, size_t *ret_len ) { int zrc; int rc=0; size_t n; int nread, count; int refill = !bzs->avail_in; int eofseen = 0; if( DBG_FILTER ) log_debug("begin bzDecompress: avail_in=%u, avail_out=%u, inbuf=%u\n", (unsigned)bzs->avail_in, (unsigned)bzs->avail_out, (unsigned)zfx->inbufsize ); do { if( bzs->avail_in < zfx->inbufsize && refill ) { n = bzs->avail_in; if( !n ) bzs->next_in = zfx->inbuf; count = zfx->inbufsize - n; nread = iobuf_read( a, zfx->inbuf + n, count ); if( nread == -1 ) { eofseen = 1; nread = 0; } n += nread; bzs->avail_in = n; } if (!eofseen) refill = 1; if( DBG_FILTER ) log_debug("enter bzDecompress: avail_in=%u, avail_out=%u\n", (unsigned)bzs->avail_in, (unsigned)bzs->avail_out); zrc=BZ2_bzDecompress(bzs); if( DBG_FILTER ) log_debug("leave bzDecompress: avail_in=%u, avail_out=%u, zrc=%d\n", (unsigned)bzs->avail_in, (unsigned)bzs->avail_out, zrc); if( zrc == BZ_STREAM_END ) rc = -1; /* eof */ else if( zrc != BZ_OK && zrc != BZ_PARAM_ERROR ) log_fatal("bz2lib inflate problem: rc=%d\n", zrc ); else if (zrc == BZ_OK && eofseen && !bzs->avail_in && bzs->avail_out > 0) { log_error ("unexpected EOF in bz2lib\n"); rc = G10ERR_READ_FILE; break; } } while( bzs->avail_out && zrc != BZ_STREAM_END && zrc != BZ_PARAM_ERROR ); /* I'm not completely happy with the two uses of BZ_PARAM_ERROR here. The corresponding zlib function is Z_BUF_ERROR, which covers a narrower scope than BZ_PARAM_ERROR. -dshaw */ *ret_len = zfx->outbufsize - bzs->avail_out; if( DBG_FILTER ) log_debug("do_uncompress: returning %u bytes\n", (unsigned)*ret_len ); return rc; } int compress_filter_bz2( void *opaque, int control, IOBUF a, byte *buf, size_t *ret_len) { size_t size = *ret_len; compress_filter_context_t *zfx = opaque; bz_stream *bzs = zfx->opaque; int rc=0; if( control == IOBUFCTRL_UNDERFLOW ) { if( !zfx->status ) { bzs = zfx->opaque = xmalloc_clear( sizeof *bzs ); init_uncompress( zfx, bzs ); zfx->status = 1; } bzs->next_out = buf; bzs->avail_out = size; zfx->outbufsize = size; /* needed only for calculation */ rc = do_uncompress( zfx, bzs, a, ret_len ); } else if( control == IOBUFCTRL_FLUSH ) { if( !zfx->status ) { PACKET pkt; PKT_compressed cd; if( zfx->algo != COMPRESS_ALGO_BZIP2 ) BUG(); memset( &cd, 0, sizeof cd ); cd.len = 0; cd.algorithm = zfx->algo; init_packet( &pkt ); pkt.pkttype = PKT_COMPRESSED; pkt.pkt.compressed = &cd; if( build_packet( a, &pkt )) log_bug("build_packet(PKT_COMPRESSED) failed\n"); bzs = zfx->opaque = xmalloc_clear( sizeof *bzs ); init_compress( zfx, bzs ); zfx->status = 2; } bzs->next_in = buf; bzs->avail_in = size; rc = do_compress( zfx, bzs, BZ_RUN, a ); } else if( control == IOBUFCTRL_FREE ) { if( zfx->status == 1 ) { BZ2_bzDecompressEnd(bzs); xfree(bzs); zfx->opaque = NULL; xfree(zfx->outbuf); zfx->outbuf = NULL; } else if( zfx->status == 2 ) { bzs->next_in = buf; bzs->avail_in = 0; do_compress( zfx, bzs, BZ_FINISH, a ); BZ2_bzCompressEnd(bzs); xfree(bzs); zfx->opaque = NULL; xfree(zfx->outbuf); zfx->outbuf = NULL; } if (zfx->release) zfx->release (zfx); } else if( control == IOBUFCTRL_DESC ) - *(char**)buf = "compress_filter"; + mem2str (buf, "compress_filter", *ret_len); return rc; } diff --git a/g10/compress.c b/g10/compress.c index 07c9e5e9c..4598affff 100644 --- a/g10/compress.c +++ b/g10/compress.c @@ -1,367 +1,367 @@ /* compress.c - compress filter * Copyright (C) 1998, 1999, 2000, 2001, 2002, * 2003 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 3 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, see . */ /* 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 #include #include #include #include #include #include #include #if defined(__riscos__) && defined(USE_ZLIBRISCOS) # include "zlib-riscos.h" #endif #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; #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_level >= 1 && opt.compress_level <= 9 ) level = opt.compress_level; else if( opt.compress_level == -1 ) level = Z_DEFAULT_COMPRESSION; 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 ) ) != Z_OK ) { log_fatal("zlib problem: %s\n", zs->msg? zs->msg : rc == Z_MEM_ERROR ? "out of core" : rc == Z_VERSION_ERROR ? "invalid lib version" : "unknown error" ); } zfx->outbufsize = 8192; zfx->outbuf = xmalloc( zfx->outbufsize ); } static int do_compress( compress_filter_context_t *zfx, z_stream *zs, int flush, IOBUF a ) { int zrc; unsigned n; do { #ifndef __riscos__ zs->next_out = zfx->outbuf; #else /* __riscos__ */ zs->next_out = (Bytef *) zfx->outbuf; #endif /* __riscos__ */ zs->avail_out = zfx->outbufsize; if( DBG_FILTER ) log_debug("enter deflate: avail_in=%u, avail_out=%u, flush=%d\n", (unsigned)zs->avail_in, (unsigned)zs->avail_out, flush ); zrc = deflate( zs, flush ); if( zrc == Z_STREAM_END && flush == Z_FINISH ) ; else if( zrc != Z_OK ) { if( zs->msg ) log_fatal("zlib deflate problem: %s\n", zs->msg ); else log_fatal("zlib deflate problem: rc=%d\n", zrc ); } n = zfx->outbufsize - zs->avail_out; if( DBG_FILTER ) log_debug("leave deflate: " "avail_in=%u, avail_out=%u, n=%u, zrc=%d\n", (unsigned)zs->avail_in, (unsigned)zs->avail_out, (unsigned)n, zrc ); if( iobuf_write( a, zfx->outbuf, n ) ) { log_debug("deflate: iobuf_write failed\n"); return G10ERR_WRITE_FILE; } } while( zs->avail_in || (flush == Z_FINISH && zrc != Z_STREAM_END) ); return 0; } static void init_uncompress( compress_filter_context_t *zfx, z_stream *zs ) { int rc; /**************** * PGP uses a windowsize of 13 bits. Using a negative value for * it forces zlib not to expect a zlib header. This is a * undocumented feature Peter Gutmann told me about. * * We must use 15 bits for the inflator because CryptoEx uses 15 * bits thus the output would get scrambled w/o error indication * if we would use 13 bits. For the uncompressing this does not * matter at all. */ if( (rc = zfx->algo == 1? inflateInit2( zs, -15) : inflateInit( zs )) != Z_OK ) { log_fatal("zlib problem: %s\n", zs->msg? zs->msg : rc == Z_MEM_ERROR ? "out of core" : rc == Z_VERSION_ERROR ? "invalid lib version" : "unknown error" ); } zfx->inbufsize = 2048; zfx->inbuf = xmalloc( zfx->inbufsize ); zs->avail_in = 0; } static int do_uncompress( compress_filter_context_t *zfx, z_stream *zs, IOBUF a, size_t *ret_len ) { int zrc; int rc = 0; int leave = 0; size_t n; int nread, count; int refill = !zs->avail_in; if( DBG_FILTER ) log_debug("begin inflate: avail_in=%u, avail_out=%u, inbuf=%u\n", (unsigned)zs->avail_in, (unsigned)zs->avail_out, (unsigned)zfx->inbufsize ); do { if( zs->avail_in < zfx->inbufsize && refill ) { n = zs->avail_in; if( !n ) #ifndef __riscos__ zs->next_in = zfx->inbuf; #else /* __riscos__ */ zs->next_in = (Bytef *) zfx->inbuf; #endif /* __riscos__ */ count = zfx->inbufsize - n; nread = iobuf_read( a, zfx->inbuf + n, count ); if( nread == -1 ) nread = 0; n += nread; /* Algo 1 has no zlib header which requires us to to give * inflate an extra dummy byte to read. To be on the safe * side we allow for up to 4 ff bytes. */ if( nread < count && zfx->algo == 1 && zfx->algo1hack < 4) { *(zfx->inbuf + n) = 0xFF; zfx->algo1hack++; n++; leave = 1; } zs->avail_in = n; } refill = 1; if( DBG_FILTER ) log_debug("enter inflate: avail_in=%u, avail_out=%u\n", (unsigned)zs->avail_in, (unsigned)zs->avail_out); #ifdef Z_SYNC_FLUSH zrc = inflate( zs, Z_SYNC_FLUSH ); #else zrc = inflate( zs, Z_PARTIAL_FLUSH ); #endif if( DBG_FILTER ) log_debug("leave inflate: avail_in=%u, avail_out=%u, zrc=%d\n", (unsigned)zs->avail_in, (unsigned)zs->avail_out, zrc); if( zrc == Z_STREAM_END ) rc = -1; /* eof */ else if( zrc != Z_OK && zrc != Z_BUF_ERROR ) { if( zs->msg ) log_fatal("zlib inflate problem: %s\n", zs->msg ); else log_fatal("zlib inflate problem: rc=%d\n", zrc ); } } while (zs->avail_out && zrc != Z_STREAM_END && zrc != Z_BUF_ERROR && !leave); *ret_len = zfx->outbufsize - zs->avail_out; if( DBG_FILTER ) log_debug("do_uncompress: returning %u bytes\n", (unsigned)*ret_len ); return rc; } static int compress_filter( void *opaque, int control, IOBUF a, byte *buf, size_t *ret_len) { size_t size = *ret_len; compress_filter_context_t *zfx = opaque; z_stream *zs = zfx->opaque; int rc=0; if( control == IOBUFCTRL_UNDERFLOW ) { if( !zfx->status ) { zs = zfx->opaque = xmalloc_clear( sizeof *zs ); init_uncompress( zfx, zs ); zfx->status = 1; } #ifndef __riscos__ zs->next_out = buf; #else /* __riscos__ */ zs->next_out = (Bytef *) buf; #endif /* __riscos__ */ zs->avail_out = size; zfx->outbufsize = size; /* needed only for calculation */ rc = do_uncompress( zfx, zs, a, ret_len ); } else if( control == IOBUFCTRL_FLUSH ) { if( !zfx->status ) { PACKET pkt; PKT_compressed cd; if(zfx->algo != COMPRESS_ALGO_ZIP && zfx->algo != COMPRESS_ALGO_ZLIB) BUG(); memset( &cd, 0, sizeof cd ); cd.len = 0; cd.algorithm = zfx->algo; init_packet( &pkt ); pkt.pkttype = PKT_COMPRESSED; pkt.pkt.compressed = &cd; if( build_packet( a, &pkt )) log_bug("build_packet(PKT_COMPRESSED) failed\n"); zs = zfx->opaque = xmalloc_clear( sizeof *zs ); init_compress( zfx, zs ); zfx->status = 2; } #ifndef __riscos__ zs->next_in = buf; #else /* __riscos__ */ zs->next_in = (Bytef *) buf; #endif /* __riscos__ */ zs->avail_in = size; rc = do_compress( zfx, zs, Z_NO_FLUSH, a ); } else if( control == IOBUFCTRL_FREE ) { if( zfx->status == 1 ) { inflateEnd(zs); xfree(zs); zfx->opaque = NULL; xfree(zfx->outbuf); zfx->outbuf = NULL; } else if( zfx->status == 2 ) { #ifndef __riscos__ zs->next_in = buf; #else /* __riscos__ */ zs->next_in = (Bytef *) buf; #endif /* __riscos__ */ zs->avail_in = 0; do_compress( zfx, zs, Z_FINISH, a ); deflateEnd(zs); xfree(zs); zfx->opaque = NULL; xfree(zfx->outbuf); zfx->outbuf = NULL; } if (zfx->release) zfx->release (zfx); } else if( control == IOBUFCTRL_DESC ) - *(char**)buf = "compress_filter"; + mem2str (buf, "compress_filter", *ret_len); return rc; } static void release_context (compress_filter_context_t *ctx) { xfree (ctx); } /**************** * Handle a compressed packet */ int handle_compressed( void *procctx, PKT_compressed *cd, int (*callback)(IOBUF, void *), void *passthru ) { compress_filter_context_t *cfx; int rc; if(check_compress_algo(cd->algorithm)) return G10ERR_COMPR_ALGO; cfx = xmalloc_clear (sizeof *cfx); cfx->release = release_context; cfx->algo = cd->algorithm; push_compress_filter(cd->buf,cfx,cd->algorithm); if( callback ) rc = callback(cd->buf, passthru ); else rc = proc_packets(procctx, cd->buf); cd->buf = NULL; 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/encode.c b/g10/encode.c index a579c428d..93c70a70d 100644 --- a/g10/encode.c +++ b/g10/encode.c @@ -1,861 +1,861 @@ /* encode.c - encode data * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 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 3 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, see . */ #include #include #include #include #include #include #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" #include "trustdb.h" #include "i18n.h" #include "status.h" 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 * stdin if FILENAME is NULL. */ int encode_symmetric( const char *filename ) { return encode_simple( filename, 1, 0 ); } /**************** * Encode FILENAME as a literal data packet only. Take input from * stdin if FILENAME is NULL. */ int encode_store( const char *filename ) { return encode_simple( filename, 0, 0 ); } static void encode_seskey( DEK *dek, DEK **seskey, byte *enckey ) { CIPHER_HANDLE hd; byte buf[33]; 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 );*/ } buf[0] = (*seskey)->algo; memcpy( buf + 1, (*seskey)->key, (*seskey)->keylen ); hd = cipher_open( dek->algo, CIPHER_MODE_CFB, 1 ); cipher_setkey( hd, dek->key, dek->keylen ); cipher_setiv( hd, NULL, 0 ); cipher_encrypt( hd, buf, buf, (*seskey)->keylen + 1 ); 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) { /* RFC-1991 and 2440 don't have MDC */ if(RFC1991 || RFC2440) return 0; /* --force-mdc overrides --disable-mdc */ if(opt.force_mdc) return 1; if(opt.disable_mdc) return 0; /* Do the keys really support MDC? */ 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 of the AESes or TWOFISH are in the prefs, we assume that the user 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. */ 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(cipher_get_blocksize(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 use_seskey ) { IOBUF inp, out; PACKET pkt; PKT_plaintext *pt = NULL; STRING2KEY *s2k = NULL; byte enckey[33]; int rc = 0; int seskeylen = 0; u32 filesize; cipher_filter_context_t cfx; armor_filter_context_t afx; compress_filter_context_t zfx; text_filter_context_t tfx; progress_filter_context_t pfx; int do_compress = !RFC1991 && default_compress_algo(); memset( &cfx, 0, sizeof cfx); memset( &afx, 0, sizeof afx); memset( &zfx, 0, sizeof zfx); memset( &tfx, 0, sizeof tfx); init_packet(&pkt); /* prepare iobufs */ 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 ) { log_error(_("can't open `%s': %s\n"), filename? filename: "[stdin]", strerror(errno) ); return G10ERR_OPEN_FILE; } handle_progress (&pfx, inp, filename); if( opt.textmode ) iobuf_push_filter( inp, text_filter, &tfx ); /* 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 && use_seskey ) use_seskey = 0; cfx.dek = NULL; if( mode ) { s2k = xmalloc_clear( sizeof *s2k ); s2k->mode = RFC1991? 0:opt.s2k_mode; 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 = G10ERR_PASSPHRASE; xfree(cfx.dek); xfree(s2k); iobuf_close(inp); log_error(_("error creating passphrase: %s\n"), g10_errstr(rc) ); return rc; } 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 ( use_seskey ) { DEK *dek = NULL; seskeylen = cipher_get_keylen( default_cipher_algo() ) / 8; encode_seskey( cfx.dek, &dek, enckey ); xfree( cfx.dek ); cfx.dek = dek; } if(opt.verbose) log_info(_("using cipher %s\n"), cipher_algo_to_string(cfx.dek->algo)); cfx.dek->use_mdc=use_mdc(NULL,cfx.dek->algo); } if (do_compress && cfx.dek && cfx.dek->use_mdc && is_file_compressed(filename, &rc)) { if (opt.verbose) log_info(_("`%s' already compressed\n"), filename); do_compress = 0; } if( rc || (rc = open_outfile( filename, opt.armor? 1:0, &out )) ) { iobuf_cancel(inp); xfree(cfx.dek); xfree(s2k); return rc; } if( opt.armor ) iobuf_push_filter( out, armor_filter, &afx ); if( s2k && !RFC1991 ) { PKT_symkey_enc *enc = xmalloc_clear( sizeof *enc + seskeylen + 1 ); enc->version = 4; enc->cipher_algo = cfx.dek->algo; enc->s2k = *s2k; 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", g10_errstr(rc) ); xfree(enc); } if (!opt.no_literal) pt=setup_plaintext_name(filename,inp); /* Note that PGP 5 has problems decrypting symmetrically encrypted data if the file length is in the inner packet. It works when only partial length headers are use. In the past, we always used partial body length here, but since PGP 2, PGP 6, and PGP 7 need the file length, and nobody should be using PGP 5 nowadays anyway, this is now set to the file length. Note also that this only applies to the RFC-1991 style symmetric messages, and not the RFC-2440 style. PGP 6 and 7 work with either partial length or fixed length with the new style messages. */ if ( !iobuf_is_pipe_filename (filename) && *filename && !opt.textmode ) { off_t tmpsize; int overflow; if ( !(tmpsize = iobuf_get_filelength(inp, &overflow)) && !overflow && opt.verbose) 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) ) filesize = tmpsize; else filesize = 0; } else filesize = opt.set_filesize ? opt.set_filesize : 0; /* stdin */ if (!opt.no_literal) { pt->timestamp = make_timestamp(); pt->mode = opt.textmode? 't' : 'b'; pt->len = filesize; pt->new_ctb = !pt->len && !RFC1991; pt->buf = inp; pkt.pkttype = PKT_PLAINTEXT; pkt.pkt.plaintext = pt; cfx.datalen = filesize && !do_compress ? calc_packet_length( &pkt ) : 0; } else { cfx.datalen = filesize && !do_compress ? filesize : 0; pkt.pkttype = 0; pkt.pkt.generic = NULL; } /* register the cipher filter */ if( mode ) iobuf_push_filter( out, cipher_filter, &cfx ); /* register the compress filter */ if( do_compress ) { if (cfx.dek && cfx.dek->use_mdc) zfx.new_ctb = 1; 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", g10_errstr(rc) ); } else { /* user requested not to create a literal packet, * so we copy the plain data */ byte copy_buffer[4096]; int bytes_copied; while ((bytes_copied = iobuf_read(inp, copy_buffer, 4096)) != -1) 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 */ } /* finish the stuff */ iobuf_close(inp); if (rc) iobuf_cancel(out); else { iobuf_close(out); /* fixme: check returncode */ if (mode) write_status( STATUS_END_ENCRYPTION ); } if (pt) pt->buf = NULL; free_packet(&pkt); 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, int use_symkey ) { 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; armor_filter_context_t afx; compress_filter_context_t zfx; text_filter_context_t tfx; progress_filter_context_t pfx; PK_LIST pk_list,work_list; int do_compress = opt.compress_algo && !RFC1991; memset( &cfx, 0, sizeof cfx); memset( &afx, 0, sizeof afx); memset( &zfx, 0, sizeof zfx); 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; if(PGP2) { for(work_list=pk_list; work_list; work_list=work_list->next) if(!(is_RSA(work_list->pk->pubkey_algo) && nbits_from_pk(work_list->pk)<=2048)) { log_info(_("you can only encrypt to RSA keys of 2048 bits or " "less in --pgp2 mode\n")); compliance_failure(); break; } } /* prepare iobufs */ 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 ) { log_error(_("can't open `%s': %s\n"), filename? filename: "[stdin]", strerror(errno) ); rc = G10ERR_OPEN_FILE; goto leave; } else if( opt.verbose ) log_info(_("reading from `%s'\n"), filename? filename: "[stdin]"); handle_progress (&pfx, inp, filename); if( opt.textmode ) iobuf_push_filter( inp, text_filter, &tfx ); if( (rc = open_outfile( filename, opt.armor? 1:0, &out )) ) goto leave; if( opt.armor ) iobuf_push_filter( out, armor_filter, &afx ); /* create a session key */ 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 mixing v3 and v4 keys, as v4 keys have an implicit preference entry for 3DES, and the pk_list cannot be empty. In this case, use 3DES anyway as it's the safest choice - perhaps the v3 key is being used in an OpenPGP implementation and we know that the implementation behind any v4 key can handle 3DES. */ if( cfx.dek->algo == -1 ) { cfx.dek->algo = CIPHER_ALGO_3DES; if( PGP2 ) { log_info(_("unable to use the IDEA cipher for all of the keys " "you are encrypting to.\n")); compliance_failure(); } } } else { if(!opt.expert && select_algo_from_prefs(pk_list,PREFTYPE_SYM, opt.def_cipher_algo,NULL)!=opt.def_cipher_algo) log_info(_("WARNING: forcing symmetric cipher %s (%d)" " violates recipient preferences\n"), cipher_algo_to_string(opt.def_cipher_algo), opt.def_cipher_algo); cfx.dek->algo = opt.def_cipher_algo; } cfx.dek->use_mdc=use_mdc(pk_list,cfx.dek->algo); /* Only do the is-file-already-compressed check if we are using a MDC. This forces compressed files to be re-compressed if we do not have a MDC to give some protection against chosen ciphertext attacks. */ if (do_compress && cfx.dek->use_mdc && is_file_compressed(filename, &rc2) ) { if (opt.verbose) log_info(_("`%s' already compressed\n"), filename); do_compress = 0; } if (rc2) { rc = rc2; goto leave; } make_session_key( cfx.dek ); if( DBG_CIPHER ) log_hexdump("DEK is: ", cfx.dek->key, cfx.dek->keylen ); rc = write_pubkey_enc_from_list( pk_list, cfx.dek, out ); 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) pt=setup_plaintext_name(filename,inp); if (!iobuf_is_pipe_filename (filename) && *filename && !opt.textmode ) { off_t tmpsize; int overflow; if ( !(tmpsize = iobuf_get_filelength(inp, &overflow)) && !overflow && opt.verbose) 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) ) filesize = tmpsize; else filesize = 0; } else filesize = opt.set_filesize ? opt.set_filesize : 0; /* stdin */ if (!opt.no_literal) { pt->timestamp = make_timestamp(); pt->mode = opt.textmode ? 't' : 'b'; pt->len = filesize; pt->new_ctb = !pt->len && !RFC1991; pt->buf = inp; pkt.pkttype = PKT_PLAINTEXT; pkt.pkt.plaintext = pt; cfx.datalen = filesize && !do_compress? calc_packet_length( &pkt ) : 0; } else cfx.datalen = filesize && !do_compress ? filesize : 0; /* register the cipher filter */ iobuf_push_filter( out, cipher_filter, &cfx ); /* register the compress filter */ if( do_compress ) { int compr_algo = opt.compress_algo; if(compr_algo==-1) { if((compr_algo= select_algo_from_prefs(pk_list,PREFTYPE_ZIP,-1,NULL))==-1) compr_algo=DEFAULT_COMPRESS_ALGO; /* Theoretically impossible to get here since uncompressed is implicit. */ } else if(!opt.expert && select_algo_from_prefs(pk_list,PREFTYPE_ZIP, compr_algo,NULL)!=compr_algo) 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 ) { if (cfx.dek && cfx.dek->use_mdc) zfx.new_ctb = 1; 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", g10_errstr(rc) ); } else { /* user requested not to create a literal packet, so we copy the plain data */ byte copy_buffer[4096]; int bytes_copied; while ((bytes_copied = iobuf_read(inp, copy_buffer, 4096)) != -1) 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 */ } /* finish the stuff */ leave: iobuf_close(inp); if( rc ) iobuf_cancel(out); else { iobuf_close(out); /* fixme: check returncode */ write_status( STATUS_END_ENCRYPTION ); } if( pt ) pt->buf = NULL; free_packet(&pkt); xfree(cfx.dek); xfree(symkey_dek); xfree(symkey_s2k); release_pk_list( pk_list ); return rc; } /**************** * Filter to do a complete public key encryption. */ int encrypt_filter( void *opaque, int control, IOBUF a, byte *buf, size_t *ret_len) { size_t size = *ret_len; encrypt_filter_context_t *efx = opaque; int rc=0; if( control == IOBUFCTRL_UNDERFLOW ) { /* decrypt */ BUG(); /* not used */ } else if( control == IOBUFCTRL_FLUSH ) { /* encrypt */ if( !efx->header_okay ) { 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 = select_algo_from_prefs(efx->pk_list,PREFTYPE_SYM,-1,NULL); if( efx->cfx.dek->algo == -1 ) { /* because 3DES is implicitly in the prefs, this can only * happen if we do not have any public keys in the list */ efx->cfx.dek->algo = DEFAULT_CIPHER_ALGO; } } else { if(!opt.expert && select_algo_from_prefs(efx->pk_list,PREFTYPE_SYM, opt.def_cipher_algo, NULL)!=opt.def_cipher_algo) log_info(_("forcing symmetric cipher %s (%d) " "violates recipient preferences\n"), cipher_algo_to_string(opt.def_cipher_algo), opt.def_cipher_algo); efx->cfx.dek->algo = opt.def_cipher_algo; } efx->cfx.dek->use_mdc = use_mdc(efx->pk_list,efx->cfx.dek->algo); make_session_key( efx->cfx.dek ); if( DBG_CIPHER ) log_hexdump("DEK is: ", efx->cfx.dek->key, efx->cfx.dek->keylen ); rc = write_pubkey_enc_from_list( efx->pk_list, efx->cfx.dek, a ); 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; } rc = iobuf_write( a, buf, size ); } else if( control == IOBUFCTRL_FREE ) { xfree(efx->symkey_dek); xfree(efx->symkey_s2k); } else if( control == IOBUFCTRL_DESC ) { - *(char**)buf = "encrypt_filter"; + mem2str (buf, "encrypt_filter", *ret_len); } return rc; } /**************** * 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 out ) { PACKET pkt; PKT_public_key *pk; PKT_pubkey_enc *enc; int rc; for( ; pk_list; pk_list = pk_list->next ) { MPI frame; pk = pk_list->pk; print_pubkey_algo_note( pk->pubkey_algo ); 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)); if(opt.throw_keyid && (PGP2 || PGP6 || PGP7 || PGP8)) { log_info(_("you may not use %s while in %s mode\n"), "--throw-keyid",compliance_option_string()); compliance_failure(); } /* Okay, what's going on: We have the session key somewhere in * the structure DEK and want to encode this session key in * an integer value of n bits. pubkey_nbits gives us the * number of bits we have to use. We then encode the session * key in some way and we get it back in the big intger value * FRAME. Then we use FRAME, the public key PK->PKEY and the * 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 * 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 = pubkey_encrypt( pk->pubkey_algo, enc->data, frame, pk->pkey ); mpi_free( frame ); if( rc ) log_error("pubkey_encrypt failed: %s\n", g10_errstr(rc) ); else { if( opt.verbose ) { char *ustr = get_user_id_string_native (enc->keyid); log_info(_("%s/%s encrypted for: \"%s\"\n"), pubkey_algo_to_string(enc->pubkey_algo), cipher_algo_to_string(dek->algo), ustr ); xfree(ustr); } /* and write it */ init_packet(&pkt); pkt.pkttype = PKT_PUBKEY_ENC; pkt.pkt.pubkey_enc = enc; rc = build_packet( out, &pkt ); if( rc ) log_error("build_packet(pubkey_enc) failed: %s\n", g10_errstr(rc)); } free_pubkey_enc(enc); if( rc ) return rc; } return 0; } void encode_crypt_files(int nfiles, char **files, STRLIST remusr) { int rc = 0; if (opt.outfile) { log_error(_("--output doesn't work for this command\n")); return; } if (!nfiles) { char line[2048]; unsigned int lno = 0; while ( 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); return; } line[strlen(line)-1] = '\0'; print_file_status(STATUS_FILE_START, line, 2); 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 ); iobuf_ioctl( NULL, 2, 0, NULL); /* Invalidate entire cache. */ } } else { while (nfiles--) { print_file_status(STATUS_FILE_START, *files, 2); 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 ); iobuf_ioctl( NULL, 2, 0, NULL); /* Invalidate entire cache. */ files++; } } } diff --git a/g10/encr-data.c b/g10/encr-data.c index c65aa11b5..baa0606fe 100644 --- a/g10/encr-data.c +++ b/g10/encr-data.c @@ -1,336 +1,336 @@ /* encr-data.c - process an encrypted data packet * Copyright (C) 1998, 1999, 2000, 2001, 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 3 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, see . */ #include #include #include #include #include #include "util.h" #include "memory.h" #include "packet.h" #include "mpi.h" #include "cipher.h" #include "options.h" #include "i18n.h" #include "status.h" 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 a, byte *buf, size_t *ret_len); typedef struct { CIPHER_HANDLE cipher_hd; MD_HANDLE mdc_hash; char defer[22]; int defer_filled; int eof_seen; int refcount; } *decode_filter_ctx_t; /* Helper to release the decode context. */ static void release_dfx_context (decode_filter_ctx_t dfx) { if (!dfx) return; assert (dfx->refcount); if ( !--dfx->refcount ) { cipher_close (dfx->cipher_hd); dfx->cipher_hd = NULL; md_close (dfx->mdc_hash); dfx->mdc_hash = NULL; xfree (dfx); } } /**************** * Decrypt the data, specified by ED with the key DEK. */ int decrypt_data( void *procctx, PKT_encrypted *ed, DEK *dek ) { decode_filter_ctx_t dfx; byte *p; int rc=0, c, i; byte temp[32]; unsigned blocksize; unsigned nprefix; dfx = xcalloc (1, sizeof *dfx); dfx->refcount = 1; if( opt.verbose && !dek->algo_info_printed ) { const char *s = cipher_algo_to_string( dek->algo ); if( s ) log_info(_("%s encrypted data\n"), s ); else log_info(_("encrypted with unknown algorithm %d\n"), dek->algo ); dek->algo_info_printed = 1; } { char buf[20]; snprintf (buf, sizeof buf, "%d %d", ed->mdc_method, dek->algo); write_status_text (STATUS_DECRYPTION_INFO, buf); } if (opt.show_session_key) { char *buf = xmalloc (dek->keylen*2 + 20); sprintf (buf, "%d:", dek->algo); for (i=0; i < dek->keylen; i++ ) sprintf(buf+strlen(buf), "%02X", dek->key[i] ); log_info ("session key: `%s'\n", buf); write_status_text (STATUS_SESSION_KEY, buf); } if( (rc=check_cipher_algo(dek->algo)) ) goto leave; blocksize = cipher_get_blocksize(dek->algo); if( !blocksize || blocksize > 16 ) log_fatal("unsupported blocksize %u\n", blocksize ); nprefix = blocksize; if( ed->len && ed->len < (nprefix+2) ) BUG(); if( ed->mdc_method ) { dfx->mdc_hash = md_open ( ed->mdc_method, 0 ); if ( DBG_HASHING ) md_start_debug (dfx->mdc_hash, "checkmdc"); } dfx->cipher_hd = cipher_open ( dek->algo, ed->mdc_method? CIPHER_MODE_CFB : CIPHER_MODE_AUTO_CFB, 1 ); /* log_hexdump( "thekey", dek->key, dek->keylen );*/ rc = cipher_setkey ( dfx->cipher_hd, dek->key, dek->keylen ); if( rc == G10ERR_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; } cipher_setiv ( dfx->cipher_hd, NULL, 0 ); if( ed->len ) { for(i=0; i < (nprefix+2) && ed->len; i++, ed->len-- ) { if( (c=iobuf_get(ed->buf)) == -1 ) break; else temp[i] = c; } } else { for(i=0; i < (nprefix+2); i++ ) if( (c=iobuf_get(ed->buf)) == -1 ) break; else temp[i] = c; } cipher_decrypt ( dfx->cipher_hd, temp, temp, nprefix+2); cipher_sync ( dfx->cipher_hd ); p = temp; /* log_hexdump( "prefix", temp, nprefix+2 ); */ if(dek->symmetric && (p[nprefix-2] != p[nprefix] || p[nprefix-1] != p[nprefix+1]) ) { rc = G10ERR_BAD_KEY; goto leave; } if ( dfx->mdc_hash ) md_write ( dfx->mdc_hash, temp, nprefix+2 ); dfx->refcount++; if ( ed->mdc_method ) iobuf_push_filter( ed->buf, mdc_decode_filter, dfx ); else iobuf_push_filter( ed->buf, decode_filter, dfx ); proc_packets( procctx, ed->buf ); ed->buf = NULL; if( ed->mdc_method && dfx->eof_seen == 2 ) rc = G10ERR_INVALID_PACKET; else if( ed->mdc_method ) { /* check the mdc */ /* We used to let parse-packet.c handle the MDC packet but this turned out to be a problem with compressed packets: With old style packets there is no length information available and the decompressor uses an implicit end. However we can't know this implicit end beforehand (:-) and thus may feed the decompressor with more bytes than actually needed. It would be possible to unread the extra bytes but due to our weird iobuf system any unread is non reliable due to filters already popped off. The easy and sane solution is to care about the MDC packet only here and never pass it to the packet parser. Fortunatley the OpenPGP spec requires a strict format for the MDC packet so that we know that 22 bytes are appended. */ int datalen = md_digest_length( ed->mdc_method ); assert (dfx->cipher_hd); assert (dfx->mdc_hash); cipher_decrypt ( dfx->cipher_hd, dfx->defer, dfx->defer, 22); md_write ( dfx->mdc_hash, dfx->defer, 2); md_final ( dfx->mdc_hash ); if ( dfx->defer[0] != '\xd3' || dfx->defer[1] != '\x14' || datalen != 20 || memcmp (md_read (dfx->mdc_hash, 0 ), dfx->defer+2, datalen)) rc = G10ERR_BAD_SIGN; /*log_hexdump("MDC calculated:",md_read( dfx->mdc_hash, 0), datalen);*/ /*log_hexdump("MDC message :", dfx->defer, 20);*/ } leave: release_dfx_context (dfx); return rc; } /* I think we should merge this with cipher_filter */ static int mdc_decode_filter( void *opaque, int control, IOBUF a, byte *buf, size_t *ret_len) { decode_filter_ctx_t dfx = opaque; size_t n, size = *ret_len; int rc = 0; int c; if( control == IOBUFCTRL_UNDERFLOW && dfx->eof_seen ) { *ret_len = 0; rc = -1; } else if( control == IOBUFCTRL_UNDERFLOW ) { assert(a); assert( size > 44 ); /* get at least 20 bytes and put it somewhere ahead in the buffer */ for(n=22; n < 44 ; n++ ) { if( (c = iobuf_get(a)) == -1 ) break; buf[n] = c; } if( n == 44 ) { /* we have enough stuff - flush the deferred stuff */ /* (we have asserted that the buffer is large enough) */ if( !dfx->defer_filled ) { /* the first time */ memcpy(buf, buf+22, 22 ); n = 22; } else { memcpy(buf, dfx->defer, 22 ); } /* now fill up */ for(; n < size; n++ ) { if( (c = iobuf_get(a)) == -1 ) break; buf[n] = c; } /* Move the last 22 bytes back to the defer buffer. */ /* (okay, we are wasting 22 bytes of supplied buffer) */ n -= 22; memcpy( dfx->defer, buf+n, 22 ); dfx->defer_filled = 1; } else if( !dfx->defer_filled ) { /* eof seen buf empty defer */ /* this is bad because there is an incomplete hash */ n -= 22; memcpy(buf, buf+22, n ); dfx->eof_seen = 2; /* eof with incomplete hash */ } else { /* eof seen */ memcpy (buf, dfx->defer, 22 ); n -= 22; memcpy( dfx->defer, buf+n, 22 ); dfx->eof_seen = 1; /* normal eof */ } if( n ) { if (dfx->cipher_hd) cipher_decrypt( dfx->cipher_hd, buf, buf, n); if (dfx->mdc_hash) md_write( dfx->mdc_hash, buf, n ); } else { assert( dfx->eof_seen ); rc = -1; /* eof */ } *ret_len = n; } else if ( control == IOBUFCTRL_FREE ) { release_dfx_context (dfx); } else if( control == IOBUFCTRL_DESC ) { - *(char**)buf = "mdc_decode_filter"; + mem2str (buf, "mdc_decode_filter", *ret_len); } return rc; } static int 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; int rc = 0; if( control == IOBUFCTRL_UNDERFLOW ) { assert(a); n = iobuf_read( a, buf, size ); if (n == (size_t)(-1)) n = 0; if( n ) { if (fc->cipher_hd) cipher_decrypt( fc->cipher_hd, buf, buf, n); } else rc = -1; /* eof */ *ret_len = n; } else if ( control == IOBUFCTRL_FREE ) { release_dfx_context (fc); } else if( control == IOBUFCTRL_DESC ) { - *(char**)buf = "decode_filter"; + mem2str (buf, "decode_filter", *ret_len); } return rc; } diff --git a/g10/mdfilter.c b/g10/mdfilter.c index 9c0059e05..bb47f98cd 100644 --- a/g10/mdfilter.c +++ b/g10/mdfilter.c @@ -1,75 +1,75 @@ /* mdfilter.c - filter data and calculate a message digest * Copyright (C) 1998, 1999, 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 3 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, see . */ #include #include #include #include #include #include #include "errors.h" #include "iobuf.h" #include "memory.h" #include "util.h" #include "filter.h" /**************** * This filter is used to collect a message digest */ int md_filter( void *opaque, int control, IOBUF a, byte *buf, size_t *ret_len) { size_t size = *ret_len; md_filter_context_t *mfx = opaque; int i, rc=0; if( control == IOBUFCTRL_UNDERFLOW ) { if( mfx->maxbuf_size && size > mfx->maxbuf_size ) size = mfx->maxbuf_size; i = iobuf_read( a, buf, size ); if( i == -1 ) i = 0; if( i ) { md_write(mfx->md, buf, i ); if( mfx->md2 ) md_write(mfx->md2, buf, i ); } else rc = -1; /* eof */ *ret_len = i; } else if( control == IOBUFCTRL_DESC ) - *(char**)buf = "md_filter"; + mem2str (buf, "md_filter", *ret_len); return rc; } void free_md_filter_context( md_filter_context_t *mfx ) { md_close(mfx->md); md_close(mfx->md2); mfx->md = NULL; mfx->md2 = NULL; mfx->maxbuf_size = 0; } diff --git a/g10/pipemode.c b/g10/pipemode.c index 077f967cd..60c802060 100644 --- a/g10/pipemode.c +++ b/g10/pipemode.c @@ -1,315 +1,315 @@ /* 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 3 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, see . */ #include #include #include #include #include #include #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 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"; + mem2str (buf, "pipemode_filter", *ret_len); return rc; } void run_in_pipemode(void) { IOBUF fp; armor_filter_context_t afx; struct pipemode_context_s stx; 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); proc_packets( NULL, fp ); write_status (STATUS_END_STREAM); } while ( !stx.stop ); } diff --git a/g10/progress.c b/g10/progress.c index 8c8265f36..cea080c0e 100644 --- a/g10/progress.c +++ b/g10/progress.c @@ -1,118 +1,118 @@ /* progress.c * Copyright (C) 2003 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. */ #include #include #include "iobuf.h" #include "filter.h" #include "status.h" #include "util.h" #include "options.h" /**************** * The filter is used to report progress to the user. */ int progress_filter (void *opaque, int control, IOBUF a, byte *buf, size_t *ret_len) { int rc = 0; progress_filter_context_t *pfx = opaque; if (control == IOBUFCTRL_INIT) { char buffer[50]; pfx->last = 0; pfx->offset = 0; pfx->last_time = make_timestamp (); sprintf (buffer, "%.20s ? %lu %lu", pfx->what? pfx->what : "?", pfx->offset, pfx->total); write_status_text (STATUS_PROGRESS, buffer); } else if (control == IOBUFCTRL_UNDERFLOW) { u32 timestamp = make_timestamp (); int len = iobuf_read (a, buf, *ret_len); if (len >= 0) { pfx->offset += len; *ret_len = len; } else { *ret_len = 0; rc = -1; } if ((len == -1 && pfx->offset != pfx->last) || timestamp - pfx->last_time > 0) { char buffer[50]; sprintf (buffer, "%.20s ? %lu %lu", pfx->what? pfx->what : "?", pfx->offset, pfx->total); write_status_text (STATUS_PROGRESS, buffer); pfx->last = pfx->offset; pfx->last_time = timestamp; } } else if (control == IOBUFCTRL_FREE) { /* Note, that we must always dealloc resources of a filter within the filter handler and not anywhere else. (We set it to NULL and check all uses just in case.) */ xfree (pfx->what); pfx->what = NULL; } else if (control == IOBUFCTRL_DESC) - *(char**)buf = "progress_filter"; + mem2str (buf, "progress_filter", *ret_len); return rc; } void handle_progress (progress_filter_context_t *pfx, IOBUF inp, const char *name) { off_t filesize = 0; if (!opt.enable_progress_filter) return; if (!is_status_enabled ()) return; if ( !iobuf_is_pipe_filename (name) && *name ) filesize = iobuf_get_filelength (inp, NULL); else if (opt.set_filesize) filesize = opt.set_filesize; /* register the progress filter */ pfx->what = xstrdup (name ? name : "stdin"); pfx->total = filesize; iobuf_push_filter (inp, progress_filter, pfx); } diff --git a/g10/textfilter.c b/g10/textfilter.c index dc72a56fc..79f2f67dc 100644 --- a/g10/textfilter.c +++ b/g10/textfilter.c @@ -1,249 +1,249 @@ /* textfilter.c * Copyright (C) 1998, 1999, 2000, 2001, 2004 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 3 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, see . */ #include #include #include #include #include #include #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" #else #define LF "\n" #endif #define MAX_LINELEN 19995 /* a little bit smaller than in armor.c */ /* to make sure that a warning is displayed while */ /* creating a message */ static unsigned len_without_trailing_chars( byte *line, unsigned len, const char *trimchars ) { byte *p, *mark; unsigned n; for(mark=NULL, p=line, n=0; n < len; n++, p++ ) { if( strchr( trimchars, *p ) ) { if( !mark ) mark = p; } else mark = NULL; } return mark? (mark - line) : len; } static int standard( text_filter_context_t *tfx, IOBUF a, byte *buf, size_t size, size_t *ret_len) { int rc=0; size_t len = 0; unsigned maxlen; assert( size > 10 ); size -= 2; /* reserve 2 bytes to append CR,LF */ while( !rc && len < size ) { int lf_seen; while( len < size && tfx->buffer_pos < tfx->buffer_len ) buf[len++] = tfx->buffer[tfx->buffer_pos++]; if( len >= size ) continue; /* read the next line */ maxlen = MAX_LINELEN; tfx->buffer_pos = 0; tfx->buffer_len = iobuf_read_line( a, &tfx->buffer, &tfx->buffer_size, &maxlen ); if( !maxlen ) tfx->truncated++; if( !tfx->buffer_len ) { if( !len ) rc = -1; /* eof */ break; } lf_seen = tfx->buffer[tfx->buffer_len-1] == '\n'; /* 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'; } } *ret_len = len; return rc; } /**************** * 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 a, byte *buf, size_t *ret_len) { size_t size = *ret_len; text_filter_context_t *tfx = opaque; int rc=0; if( control == IOBUFCTRL_UNDERFLOW ) { rc = standard( tfx, a, buf, size, ret_len ); } else if( control == IOBUFCTRL_FREE ) { if( tfx->truncated ) log_error(_("can't handle text lines longer than %d characters\n"), MAX_LINELEN ); xfree( tfx->buffer ); tfx->buffer = NULL; } else if( control == IOBUFCTRL_DESC ) - *(char**)buf = "text_filter"; + mem2str (buf, "text_filter", *ret_len); return rc; } /**************** * Copy data from INP to OUT and do some escaping if requested. * md is updated as required by rfc2440 */ int copy_clearsig_text( IOBUF out, IOBUF inp, MD_HANDLE md, int escape_dash, int escape_from, int pgp2mode ) { unsigned maxlen; byte *buffer = NULL; /* malloced buffer */ unsigned bufsize; /* and size of this buffer */ unsigned n; int truncated = 0; int pending_lf = 0; if( !opt.pgp2_workarounds ) pgp2mode = 0; if( !escape_dash ) escape_from = 0; write_status (STATUS_BEGIN_SIGNING); for(;;) { maxlen = MAX_LINELEN; n = iobuf_read_line( inp, &buffer, &bufsize, &maxlen ); if( !maxlen ) truncated++; if( !n ) break; /* read_line has returned eof */ /* update the message digest */ if( escape_dash ) { if( pending_lf ) { md_putc( md, '\r' ); md_putc( md, '\n' ); } md_write( md, buffer, len_without_trailing_chars( buffer, n, pgp2mode? " \r\n":" \t\r\n")); } else md_write( md, buffer, n ); pending_lf = buffer[n-1] == '\n'; /* write the output */ if( ( escape_dash && *buffer == '-') || ( escape_from && n > 4 && !memcmp(buffer, "From ", 5 ) ) ) { iobuf_put( out, '-' ); iobuf_put( out, ' ' ); } #if 0 /*defined(HAVE_DOSISH_SYSTEM)*/ /* We don't use this anymore because my interpretation of rfc2440 7.1 * is that there is no conversion needed. If one decides to * clearsign a unix file on a DOS box he will get a mixed line endings. * If at some point it turns out, that a conversion is a nice feature * we can make an option out of it. */ /* make sure the lines do end in CR,LF */ if( n > 1 && ( (buffer[n-2] == '\r' && buffer[n-1] == '\n' ) || (buffer[n-2] == '\n' && buffer[n-1] == '\r'))) { iobuf_write( out, buffer, n-2 ); iobuf_put( out, '\r'); iobuf_put( out, '\n'); } else if( n && buffer[n-1] == '\n' ) { iobuf_write( out, buffer, n-1 ); iobuf_put( out, '\r'); iobuf_put( out, '\n'); } else iobuf_write( out, buffer, n ); #else iobuf_write( out, buffer, n ); #endif } /* at eof */ if( !pending_lf ) { /* make sure that the file ends with a LF */ iobuf_writestr( out, LF ); if( !escape_dash ) md_putc( md, '\n' ); } if( truncated ) log_info(_("input line longer than %d characters\n"), MAX_LINELEN ); return 0; /* okay */ } diff --git a/include/iobuf.h b/include/iobuf.h index 9515a0e10..030f8c8e9 100644 --- a/include/iobuf.h +++ b/include/iobuf.h @@ -1,161 +1,160 @@ /* iobuf.h - I/O buffer * Copyright (C) 1998, 1999, 2000, 2001, 2004 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 3 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, see . */ #ifndef G10_IOBUF_H #define G10_IOBUF_H #include "types.h" #define DBG_IOBUF iobuf_debug_mode #define IOBUFCTRL_INIT 1 #define IOBUFCTRL_FREE 2 #define IOBUFCTRL_UNDERFLOW 3 #define IOBUFCTRL_FLUSH 4 #define IOBUFCTRL_DESC 5 #define IOBUFCTRL_CANCEL 6 #define IOBUFCTRL_USER 16 typedef struct iobuf_struct *IOBUF; typedef struct iobuf_struct *iobuf_t; /* fixme: we should hide most of this stuff */ struct iobuf_struct { int use; /* 1 input , 2 output, 3 temp */ off_t nlimit; off_t nbytes; /* used together with nlimit */ off_t ntotal; /* total bytes read (position of stream) */ int nofast; /* used by the iobuf_get() */ void *directfp; struct { size_t size; /* allocated size */ size_t start; /* number of invalid bytes at the begin of the buffer */ size_t len; /* currently filled to this size */ byte *buf; } d; int filter_eof; int error; int (*filter)( void *opaque, int control, IOBUF chain, byte *buf, size_t *len); void *filter_ov; /* value for opaque */ int filter_ov_owner; char *real_fname; IOBUF chain; /* next iobuf used for i/o if any (passed to filter) */ int no, subno; - const char *desc; void *opaque; /* can be used to hold any information */ /* this value is copied to all instances */ struct { size_t size; /* allocated size */ size_t start; /* number of invalid bytes at the begin of the buffer */ size_t len; /* currently filled to this size */ byte *buf; } unget; }; #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 iobuf_debug_mode; void iobuf_enable_special_filenames ( int yes ); int iobuf_is_pipe_filename (const char *fname); IOBUF iobuf_alloc(int use, size_t bufsize); IOBUF iobuf_temp(void); IOBUF iobuf_temp_with_content( const char *buffer, size_t length ); IOBUF iobuf_open( const char *fname ); IOBUF iobuf_fdopen( int fd, const char *mode ); IOBUF iobuf_sockopen( int fd, const char *mode ); IOBUF iobuf_create( const char *fname ); IOBUF iobuf_append( const char *fname ); IOBUF iobuf_openrw( const char *fname ); int iobuf_ioctl ( IOBUF a, int cmd, int intval, void *ptrval ); int iobuf_close( IOBUF iobuf ); int iobuf_cancel( IOBUF iobuf ); int iobuf_push_filter( IOBUF a, int (*f)(void *opaque, int control, IOBUF chain, byte *buf, size_t *len), void *ov ); int iobuf_push_filter2( IOBUF a, int (*f)(void *opaque, int control, IOBUF chain, byte *buf, size_t *len), void *ov, int rel_ov ); int iobuf_flush(IOBUF a); void iobuf_clear_eof(IOBUF a); #define iobuf_set_error(a) do { (a)->error = 1; } while(0) #define iobuf_error(a) ((a)->error) void iobuf_set_limit( IOBUF a, off_t nlimit ); off_t iobuf_tell( IOBUF a ); int iobuf_seek( IOBUF a, off_t newpos ); int iobuf_readbyte(IOBUF a); int iobuf_read(IOBUF a, byte *buf, unsigned buflen ); unsigned iobuf_read_line( IOBUF a, byte **addr_of_buffer, unsigned *length_of_buffer, unsigned *max_length ); int iobuf_peek(IOBUF a, byte *buf, unsigned buflen ); int iobuf_writebyte(IOBUF a, unsigned c); int iobuf_write(IOBUF a, byte *buf, unsigned buflen ); int iobuf_writestr(IOBUF a, const char *buf ); void iobuf_flush_temp( IOBUF temp ); int iobuf_write_temp( IOBUF a, IOBUF temp ); size_t iobuf_temp_to_buffer( IOBUF a, byte *buffer, size_t buflen ); void iobuf_unget_and_close_temp( IOBUF a, IOBUF temp ); int iobuf_get_fd (IOBUF a); off_t iobuf_get_filelength (IOBUF a, int *overflow); #define IOBUF_FILELENGTH_LIMIT 0xffffffff const char *iobuf_get_real_fname( IOBUF a ); const char *iobuf_get_fname( IOBUF a ); void iobuf_set_partial_block_mode( IOBUF a, size_t len ); int iobuf_translate_file_handle ( int fd, int for_write ); /* Get a byte form the iobuf; must check for eof prior to this function. * This function returns values in the range 0 .. 255 or -1 to indicate EOF * iobuf_get_noeof() does not return -1 to indicate EOF, but masks the * returned value to be in the range 0..255. */ #define iobuf_get(a) \ ( ((a)->nofast || (a)->d.start >= (a)->d.len )? \ iobuf_readbyte((a)) : ( (a)->nbytes++, (a)->d.buf[(a)->d.start++] ) ) #define iobuf_get_noeof(a) (iobuf_get((a))&0xff) /* write a byte to the iobuf and return true on write error * This macro does only write the low order byte */ #define iobuf_put(a,c) iobuf_writebyte(a,c) #define iobuf_where(a) "[don't know]" #define iobuf_id(a) ((a)->no) #define iobuf_get_temp_buffer(a) ( (a)->d.buf ) #define iobuf_get_temp_length(a) ( (a)->d.len ) #define iobuf_is_temp(a) ( (a)->use == 3 ) void iobuf_skip_rest (IOBUF a, unsigned long n, int partial); #endif /*G10_IOBUF_H*/ diff --git a/util/iobuf.c b/util/iobuf.c index a330460d6..539356e3c 100644 --- a/util/iobuf.c +++ b/util/iobuf.c @@ -1,2356 +1,2372 @@ /* iobuf.c - file handling * Copyright (C) 1998, 1999, 2000, 2001, 2003, 2004, 2005, 2006, 2008, * 2009 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 3 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, see . */ #include #include #include #include #include #include #include #include #include #include #include #ifdef HAVE_DOSISH_SYSTEM #include #endif #ifdef __riscos__ #include #include #endif /* __riscos__ */ #include "memory.h" #include "util.h" #include "dynload.h" #include "iobuf.h" #ifdef __VMS # include "vms.h" # define open open_vms #endif /* def __VMS */ /* The size of the internal buffers. NOTE: If you change this value you MUST also adjust the regression test "armored_key_8192" and "nopad_armored_msg" in armor.test! */ #define IOBUF_BUFFER_SIZE 8192 #undef FILE_FILTER_USES_STDIO /* To avoid a potential DoS with compression packets we better limit the number of filters in a chain. */ #define MAX_NESTING_FILTER 64 #ifdef HAVE_DOSISH_SYSTEM #define USE_SETMODE 1 #endif #ifdef FILE_FILTER_USES_STDIO #define my_fileno(a) fileno ((a)) #define my_fopen_ro(a,b) fopen ((a),(b)) #define my_fopen(a,b) fopen ((a),(b)) typedef FILE *FILEP_OR_FD; #define INVALID_FP NULL #define FILEP_OR_FD_FOR_STDIN (stdin) #define FILEP_OR_FD_FOR_STDOUT (stdout) typedef struct { FILE *fp; /* open file handle */ int keep_open; int no_cache; int print_only_name; /* flags indicating that fname is not a real file*/ char fname[1]; /* name of the file */ } file_filter_ctx_t ; #else #define my_fileno(a) (a) #define my_fopen_ro(a,b) fd_cache_open ((a),(b)) #define my_fopen(a,b) direct_open ((a),(b)) #ifdef HAVE_DOSISH_SYSTEM typedef HANDLE FILEP_OR_FD; #define INVALID_FP ((HANDLE)-1) #define FILEP_OR_FD_FOR_STDIN (GetStdHandle (STD_INPUT_HANDLE)) #define FILEP_OR_FD_FOR_STDOUT (GetStdHandle (STD_OUTPUT_HANDLE)) #undef USE_SETMODE #else typedef int FILEP_OR_FD; #define INVALID_FP (-1) #define FILEP_OR_FD_FOR_STDIN (0) #define FILEP_OR_FD_FOR_STDOUT (1) #endif typedef struct { FILEP_OR_FD fp; /* open file handle */ int keep_open; int no_cache; int eof_seen; int print_only_name; /* flags indicating that fname is not a real file*/ char fname[1]; /* name of the file */ } file_filter_ctx_t ; struct close_cache_s { struct close_cache_s *next; FILEP_OR_FD fp; char fname[1]; }; typedef struct close_cache_s *CLOSE_CACHE; static CLOSE_CACHE close_cache; #endif #ifdef _WIN32 typedef struct { int sock; int keep_open; int no_cache; int eof_seen; int print_only_name; /* flags indicating that fname is not a real file*/ char fname[1]; /* name of the file */ } sock_filter_ctx_t ; #endif /*_WIN32*/ /* The first partial length header block must be of size 512 * to make it easier (and efficienter) we use a min. block size of 512 * for all chunks (but the last one) */ #define OP_MIN_PARTIAL_CHUNK 512 #define OP_MIN_PARTIAL_CHUNK_2POW 9 typedef struct { int use; size_t size; size_t count; int partial; /* 1 = partial header, 2 in last partial packet */ char *buffer; /* used for partial header */ size_t buflen; /* used size of buffer */ int first_c; /* of partial header (which is > 0)*/ int eof; } block_filter_ctx_t; static int special_names_enabled; static int underflow(IOBUF a); static int translate_file_handle ( int fd, int for_write ); #ifndef FILE_FILTER_USES_STDIO /* This is a replacement for strcmp. Under W32 it does not distinguish between backslash and slash. */ static int fd_cache_strcmp (const char *a, const char *b) { #ifdef HAVE_DOSISH_SYSTEM for (; *a && *b; a++, b++) { if (*a != *b && !((*a == '/' && *b == '\\') || (*a == '\\' && *b == '/')) ) break; } return *(const unsigned char *)a - *(const unsigned char *)b; #else return strcmp (a, b); #endif } /* * Invalidate (i.e. close) a cached iobuf or all iobufs if NULL is * used for FNAME. */ static int fd_cache_invalidate (const char *fname) { CLOSE_CACHE cc; int err=0; if (!fname) { if( DBG_IOBUF ) log_debug ("fd_cache_invalidate (all)\n"); for (cc=close_cache; cc; cc = cc->next ) { if ( cc->fp != INVALID_FP ) { #ifdef HAVE_DOSISH_SYSTEM CloseHandle (cc->fp); #else close(cc->fp); #endif cc->fp = INVALID_FP; } } return err; } if( DBG_IOBUF ) log_debug ("fd_cache_invalidate (%s)\n", fname); for (cc=close_cache; cc; cc = cc->next ) { if ( cc->fp != INVALID_FP && !fd_cache_strcmp (cc->fname, fname) ) { if( DBG_IOBUF ) log_debug (" did (%s)\n", cc->fname); #ifdef HAVE_DOSISH_SYSTEM if(CloseHandle (cc->fp)==0) err=-1; #else err=close(cc->fp); #endif cc->fp = INVALID_FP; } } return err; } static int fd_cache_synchronize(const char *fname) { int err=0; #ifndef HAVE_DOSISH_SYSTEM CLOSE_CACHE cc; if( DBG_IOBUF ) log_debug ("fd_cache_synchronize (%s)\n", fname); for (cc=close_cache; cc; cc = cc->next ) { if ( cc->fp != INVALID_FP && !fd_cache_strcmp (cc->fname, fname) ) { if( DBG_IOBUF ) log_debug (" did (%s)\n", cc->fname); err=fsync(cc->fp); } } #endif return err; } static FILEP_OR_FD direct_open (const char *fname, const char *mode) { #ifdef HAVE_DOSISH_SYSTEM unsigned long da, cd, sm; HANDLE hfile; /* Note, that we do not handle all mode combinations */ /* According to the ReactOS source it seems that open() of the * standard MSW32 crt does open the file in share mode which is * something new for MS applications ;-) */ if ( strchr (mode, '+') ) { fd_cache_invalidate (fname); da = GENERIC_READ|GENERIC_WRITE; cd = OPEN_EXISTING; sm = FILE_SHARE_READ | FILE_SHARE_WRITE; } else if ( strchr (mode, 'w') ) { fd_cache_invalidate (fname); da = GENERIC_WRITE; cd = CREATE_ALWAYS; sm = FILE_SHARE_WRITE; } else { da = GENERIC_READ; cd = OPEN_EXISTING; sm = FILE_SHARE_READ; } hfile = CreateFile (fname, da, sm, NULL, cd, FILE_ATTRIBUTE_NORMAL, NULL); return hfile; #else int oflag; int cflag = S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH; /* Note, that we do not handle all mode combinations */ if ( strchr (mode, '+') ) { fd_cache_invalidate (fname); oflag = O_RDWR; } else if ( strchr (mode, 'w') ) { fd_cache_invalidate (fname); oflag = O_WRONLY | O_CREAT | O_TRUNC; } else { oflag = O_RDONLY; } #ifdef O_BINARY if (strchr (mode, 'b')) oflag |= O_BINARY; #endif #ifndef __riscos__ return open (fname, oflag, cflag ); #else { struct stat buf; int rc = stat( fname, &buf ); /* Don't allow iobufs on directories */ if( !rc && S_ISDIR(buf.st_mode) && !S_ISREG(buf.st_mode) ) return __set_errno( EISDIR ); else return open( fname, oflag, cflag ); } #endif #endif } /* * Instead of closing an FD we keep it open and cache it for later reuse * Note that this caching strategy only works if the process does not chdir. */ static void fd_cache_close (const char *fname, FILEP_OR_FD fp) { CLOSE_CACHE cc; assert (fp); if ( !fname || !*fname ) { #ifdef HAVE_DOSISH_SYSTEM CloseHandle (fp); #else close(fp); #endif if( DBG_IOBUF ) log_debug ("fd_cache_close (%d) real\n", (int)fp); return; } /* try to reuse a slot */ for (cc=close_cache; cc; cc = cc->next ) { if ( cc->fp == INVALID_FP && !fd_cache_strcmp (cc->fname, fname) ) { cc->fp = fp; if( DBG_IOBUF ) log_debug ("fd_cache_close (%s) used existing slot\n", fname); return; } } /* add a new one */ if( DBG_IOBUF ) log_debug ("fd_cache_close (%s) new slot created\n", fname); cc = xmalloc_clear (sizeof *cc + strlen (fname)); strcpy (cc->fname, fname); cc->fp = fp; cc->next = close_cache; close_cache = cc; } /* * Do an direct_open on FNAME but first try to reuse one from the fd_cache */ static FILEP_OR_FD fd_cache_open (const char *fname, const char *mode) { CLOSE_CACHE cc; assert (fname); for (cc=close_cache; cc; cc = cc->next ) { if ( cc->fp != INVALID_FP && !fd_cache_strcmp (cc->fname, fname) ) { FILEP_OR_FD fp = cc->fp; cc->fp = INVALID_FP; if( DBG_IOBUF ) log_debug ("fd_cache_open (%s) using cached fp\n", fname); #ifdef HAVE_DOSISH_SYSTEM if (SetFilePointer (fp, 0, NULL, FILE_BEGIN) == 0xffffffff ) { log_error ("rewind file failed on handle %p: %s\n", fp, w32_strerror (errno)); fp = INVALID_FP; } #else if ( lseek (fp, 0, SEEK_SET) == (off_t)-1 ) { log_error("can't rewind fd %d: %s\n", fp, strerror(errno) ); fp = INVALID_FP; } #endif return fp; } } if( DBG_IOBUF ) log_debug ("fd_cache_open (%s) not cached\n", fname); return direct_open (fname, mode); } #endif /*FILE_FILTER_USES_STDIO*/ /**************** * Read data from a file into buf which has an allocated length of *LEN. * return the number of read bytes in *LEN. OPAQUE is the FILE * of * the stream. A is not used. * control may be: * IOBUFCTRL_INIT: called just before the function is linked into the * list of function. This can be used to prepare internal * data structures of the function. * IOBUFCTRL_FREE: called just before the function is removed from the * list of functions and can be used to release internal * data structures or close a file etc. * IOBUFCTRL_UNDERFLOW: called by iobuf_underflow to fill the buffer * with new stuff. *RET_LEN is the available size of the * buffer, and should be set to the number of bytes * which were put into the buffer. The function * returns 0 to indicate success, -1 on EOF and * G10ERR_xxxxx for other errors. * * IOBUFCTRL_FLUSH: called by iobuf_flush() to write out the collected stuff. * *RET_LAN is the number of bytes in BUF. * * IOBUFCTRL_CANCEL: send to all filters on behalf of iobuf_cancel. The * filter may take appropriate action on this message. */ static int file_filter(void *opaque, int control, IOBUF chain, byte *buf, size_t *ret_len) { file_filter_ctx_t *a = opaque; FILEP_OR_FD f = a->fp; size_t size = *ret_len; size_t nbytes = 0; int rc = 0; #ifdef FILE_FILTER_USES_STDIO if( control == IOBUFCTRL_UNDERFLOW ) { assert( size ); /* need a buffer */ if ( feof(f)) { /* On terminals you could easiely read as many EOFs as you call */ rc = -1; /* fread() or fgetc() repeatly. Every call will block until you press */ *ret_len = 0; /* CTRL-D. So we catch this case before we call fread() again. */ } else { clearerr( f ); nbytes = fread( buf, 1, size, f ); if( feof(f) && !nbytes ) { rc = -1; /* okay: we can return EOF now. */ } else if( ferror(f) && errno != EPIPE ) { log_error("%s: read error: %s\n", a->fname, strerror(errno)); rc = G10ERR_READ_FILE; } *ret_len = nbytes; } } else if( control == IOBUFCTRL_FLUSH ) { if( size ) { clearerr( f ); nbytes = fwrite( buf, 1, size, f ); if( ferror(f) ) { log_error("%s: write error: %s\n", a->fname, strerror(errno)); rc = G10ERR_WRITE_FILE; } } *ret_len = nbytes; } else if( control == IOBUFCTRL_INIT ) { a->keep_open = a->no_cache = 0; } else if( control == IOBUFCTRL_DESC ) { - *(char**)buf = "file_filter"; + mem2str (buf, "file_filter", *ret_len); } else if( control == IOBUFCTRL_FREE ) { if( f != stdin && f != stdout ) { if( DBG_IOBUF ) log_debug("%s: close fd %d\n", a->fname, fileno(f) ); if (!a->keep_open) fclose(f); } f = NULL; xfree(a); /* we can free our context now */ } #else /* !stdio implementation */ if( control == IOBUFCTRL_UNDERFLOW ) { assert( size ); /* need a buffer */ if ( a->eof_seen) { rc = -1; *ret_len = 0; } else { #ifdef HAVE_DOSISH_SYSTEM unsigned long nread; nbytes = 0; if ( !ReadFile ( f, buf, size, &nread, NULL ) ) { if ((int)GetLastError () != ERROR_BROKEN_PIPE) { log_error ("%s: read error: %s\n", a->fname, w32_strerror (0)); rc = G10ERR_READ_FILE; } } else if ( !nread ) { a->eof_seen = 1; rc = -1; } else { nbytes = nread; } #else int n; nbytes = 0; do { n = read ( f, buf, size ); } while (n == -1 && errno == EINTR ); if ( n == -1 ) { /* error */ if (errno != EPIPE) { log_error("%s: read error: %s\n", a->fname, strerror(errno)); rc = G10ERR_READ_FILE; } } else if ( !n ) { /* eof */ a->eof_seen = 1; rc = -1; } else { nbytes = n; } #endif *ret_len = nbytes; } } else if( control == IOBUFCTRL_FLUSH ) { if( size ) { #ifdef HAVE_DOSISH_SYSTEM byte *p = buf; unsigned long n; nbytes = size; do { if (size && !WriteFile (f, p, nbytes, &n, NULL)) { log_error ("%s: write error: %s\n", a->fname, w32_strerror (0)); rc = G10ERR_WRITE_FILE; break; } p += n; nbytes -= n; } while ( nbytes ); nbytes = p - buf; #else byte *p = buf; int n; nbytes = size; do { do { n = write ( f, p, nbytes ); } while ( n == -1 && errno == EINTR ); if ( n > 0 ) { p += n; nbytes -= n; } } while ( n != -1 && nbytes ); if( n == -1 ) { log_error("%s: write error: %s\n", a->fname, strerror(errno)); rc = G10ERR_WRITE_FILE; } nbytes = p - buf; #endif } *ret_len = nbytes; } else if ( control == IOBUFCTRL_INIT ) { a->eof_seen = 0; a->keep_open = 0; a->no_cache = 0; } else if ( control == IOBUFCTRL_DESC ) { - *(char**)buf = "file_filter(fd)"; + mem2str (buf, "file_filter(fd)", *ret_len); } else if ( control == IOBUFCTRL_FREE ) { #ifdef HAVE_DOSISH_SYSTEM if ( f != FILEP_OR_FD_FOR_STDIN && f != FILEP_OR_FD_FOR_STDOUT ) { if( DBG_IOBUF ) log_debug("%s: close handle %p\n", a->fname, f ); if (!a->keep_open) fd_cache_close (a->no_cache?NULL:a->fname, f); } #else if ( (int)f != 0 && (int)f != 1 ) { if( DBG_IOBUF ) log_debug("%s: close fd %d\n", a->fname, f ); if (!a->keep_open) fd_cache_close (a->no_cache?NULL:a->fname, f); } f = INVALID_FP; #endif xfree (a); /* we can free our context now */ } #endif /* !stdio implementation */ return rc; } #ifdef _WIN32 /* Becuase sockets are an special object under Lose32 we have to * use a special filter */ static int sock_filter (void *opaque, int control, IOBUF chain, byte *buf, size_t *ret_len) { sock_filter_ctx_t *a = opaque; size_t size = *ret_len; size_t nbytes = 0; int rc = 0; if( control == IOBUFCTRL_UNDERFLOW ) { assert( size ); /* need a buffer */ if ( a->eof_seen) { rc = -1; *ret_len = 0; } else { int nread; nread = recv ( a->sock, buf, size, 0 ); if ( nread == SOCKET_ERROR ) { int ec = (int)WSAGetLastError (); log_error("socket read error: ec=%d\n", ec); rc = G10ERR_READ_FILE; } else if ( !nread ) { a->eof_seen = 1; rc = -1; } else { nbytes = nread; } *ret_len = nbytes; } } else if( control == IOBUFCTRL_FLUSH ) { if( size ) { byte *p = buf; int n; nbytes = size; do { n = send (a->sock, p, nbytes, 0); if ( n == SOCKET_ERROR ) { int ec = (int)WSAGetLastError (); log_error("socket write error: ec=%d\n", ec); rc = G10ERR_WRITE_FILE; break; } p += n; nbytes -= n; } while ( nbytes ); nbytes = p - buf; } *ret_len = nbytes; } else if ( control == IOBUFCTRL_INIT ) { a->eof_seen = 0; a->keep_open = 0; a->no_cache = 0; } else if ( control == IOBUFCTRL_DESC ) { - *(char**)buf = "sock_filter"; + mem2str (buf, "sock_filter", *ret_len); } else if ( control == IOBUFCTRL_FREE ) { if (!a->keep_open) closesocket (a->sock); xfree (a); /* we can free our context now */ } return rc; } #endif /*_WIN32*/ /**************** * This is used to implement the block write mode. * Block reading is done on a byte by byte basis in readbyte(), * without a filter */ static int block_filter(void *opaque, int control, IOBUF chain, byte *buf, size_t *ret_len) { block_filter_ctx_t *a = opaque; size_t size = *ret_len; int c, needed, rc = 0; char *p; if( control == IOBUFCTRL_UNDERFLOW ) { size_t n=0; p = buf; assert( size ); /* need a buffer */ if( a->eof ) /* don't read any further */ rc = -1; while( !rc && size ) { if( !a->size ) { /* get the length bytes */ if( a->partial == 2 ) { a->eof = 1; if( !n ) rc = -1; break; } else if( a->partial ) { /* These OpenPGP introduced huffman like encoded length * bytes are really a mess :-( */ if( a->first_c ) { c = a->first_c; a->first_c = 0; } else if( (c = iobuf_get(chain)) == -1 ) { log_error("block_filter: 1st length byte missing\n"); rc = G10ERR_READ_FILE; break; } if( c < 192 ) { a->size = c; a->partial = 2; if( !a->size ) { a->eof = 1; if( !n ) rc = -1; break; } } else if( c < 224 ) { a->size = (c - 192) * 256; if( (c = iobuf_get(chain)) == -1 ) { log_error("block_filter: 2nd length byte missing\n"); rc = G10ERR_READ_FILE; break; } a->size += c + 192; a->partial = 2; if( !a->size ) { a->eof = 1; if( !n ) rc = -1; break; } } else if( c == 255 ) { a->size = iobuf_get(chain) << 24; a->size |= iobuf_get(chain) << 16; a->size |= iobuf_get(chain) << 8; if( (c = iobuf_get(chain)) == -1 ) { log_error("block_filter: invalid 4 byte length\n"); rc = G10ERR_READ_FILE; break; } a->size |= c; a->partial = 2; if( !a->size ) { a->eof = 1; if( !n ) rc = -1; break; } } else { /* next partial body length */ a->size = 1 << (c & 0x1f); } /* log_debug("partial: ctx=%p c=%02x size=%u\n", a, c, a->size);*/ } else BUG(); } while( !rc && size && a->size ) { needed = size < a->size ? size : a->size; c = iobuf_read( chain, p, needed ); if( c < needed ) { if( c == -1 ) c = 0; log_error("block_filter %p: read error (size=%lu,a->size=%lu)\n", a, (ulong)size+c, (ulong)a->size+c); rc = G10ERR_READ_FILE; } else { size -= c; a->size -= c; p += c; n += c; } } } *ret_len = n; } else if( control == IOBUFCTRL_FLUSH ) { if( a->partial ) { /* the complicated openpgp scheme */ size_t blen, n, nbytes = size + a->buflen; assert( a->buflen <= OP_MIN_PARTIAL_CHUNK ); if( nbytes < OP_MIN_PARTIAL_CHUNK ) { /* not enough to write a partial block out; so we store it*/ if( !a->buffer ) a->buffer = xmalloc( OP_MIN_PARTIAL_CHUNK ); memcpy( a->buffer + a->buflen, buf, size ); a->buflen += size; } else { /* okay, we can write out something */ /* do this in a loop to use the most efficient block lengths */ p = buf; do { /* find the best matching block length - this is limited * by the size of the internal buffering */ for( blen=OP_MIN_PARTIAL_CHUNK*2, c=OP_MIN_PARTIAL_CHUNK_2POW+1; blen <= nbytes; blen *=2, c++ ) ; blen /= 2; c--; /* write the partial length header */ assert( c <= 0x1f ); /*;-)*/ c |= 0xe0; iobuf_put( chain, c ); if( (n=a->buflen) ) { /* write stuff from the buffer */ assert( n == OP_MIN_PARTIAL_CHUNK); if( iobuf_write(chain, a->buffer, n ) ) rc = G10ERR_WRITE_FILE; a->buflen = 0; nbytes -= n; } if( (n = nbytes) > blen ) n = blen; if( n && iobuf_write(chain, p, n ) ) rc = G10ERR_WRITE_FILE; p += n; nbytes -= n; } while( !rc && nbytes >= OP_MIN_PARTIAL_CHUNK ); /* store the rest in the buffer */ if( !rc && nbytes ) { assert( !a->buflen ); assert( nbytes < OP_MIN_PARTIAL_CHUNK ); if( !a->buffer ) a->buffer = xmalloc( OP_MIN_PARTIAL_CHUNK ); memcpy( a->buffer, p, nbytes ); a->buflen = nbytes; } } } else BUG(); } else if( control == IOBUFCTRL_INIT ) { if( DBG_IOBUF ) log_debug("init block_filter %p\n", a ); if( a->partial ) a->count = 0; else if( a->use == 1 ) a->count = a->size = 0; else a->count = a->size; /* force first length bytes */ a->eof = 0; a->buffer = NULL; a->buflen = 0; } else if( control == IOBUFCTRL_DESC ) { - *(char**)buf = "block_filter"; + mem2str (buf, "block_filter", *ret_len); } else if( control == IOBUFCTRL_FREE ) { if( a->use == 2 ) { /* write the end markers */ if( a->partial ) { u32 len; /* write out the remaining bytes without a partial header * the length of this header may be 0 - but if it is * the first block we are not allowed to use a partial header * and frankly we can't do so, because this length must be * a power of 2. This is _really_ complicated because we * have to check the possible length of a packet prior * to it's creation: a chain of filters becomes complicated * and we need a lot of code to handle compressed packets etc. * :-((((((( */ /* construct header */ len = a->buflen; /*log_debug("partial: remaining length=%u\n", len );*/ if( len < 192 ) rc = iobuf_put(chain, len ); else if( len < 8384 ) { if( !(rc=iobuf_put( chain, ((len-192) / 256) + 192)) ) rc = iobuf_put( chain, ((len-192) % 256)); } else { /* use a 4 byte header */ if( !(rc=iobuf_put( chain, 0xff )) ) if( !(rc=iobuf_put( chain, (len >> 24)&0xff )) ) if( !(rc=iobuf_put( chain, (len >> 16)&0xff )) ) if( !(rc=iobuf_put( chain, (len >> 8)&0xff ))) rc=iobuf_put( chain, len & 0xff ); } if( !rc && len ) rc = iobuf_write(chain, a->buffer, len ); if( rc ) { log_error("block_filter: write error: %s\n",strerror(errno)); rc = G10ERR_WRITE_FILE; } xfree( a->buffer ); a->buffer = NULL; a->buflen = 0; } else BUG(); } else if( a->size ) { log_error("block_filter: pending bytes!\n"); } if( DBG_IOBUF ) log_debug("free block_filter %p\n", a ); xfree(a); /* we can free our context now */ } return rc; } +#define MAX_IOBUF_DESC 32 +/* + * Fill the buffer by the description of iobuf A. + * The buffer size should be MAX_IOBUF_DESC (or larger). + * Returns BUF as (const char *). + */ +static const char * +iobuf_desc (iobuf_t a, byte *buf) +{ + size_t len = MAX_IOBUF_DESC; + + if (! a || ! a->filter) + memcpy (buf, "?", 2); + else + a->filter (a->filter_ov, IOBUFCTRL_DESC, NULL, buf, &len); + + return buf; +} static void print_chain( IOBUF a ) { if( !DBG_IOBUF ) return; for(; a; a = a->chain ) { - size_t dummy_len = 0; - const char *desc = "[none]"; - - if( a->filter ) - a->filter( a->filter_ov, IOBUFCTRL_DESC, NULL, - (byte*)&desc, &dummy_len ); + byte desc[MAX_IOBUF_DESC]; log_debug("iobuf chain: %d.%d `%s' filter_eof=%d start=%d len=%d\n", - a->no, a->subno, desc?desc:"?", a->filter_eof, - (int)a->d.start, (int)a->d.len ); + a->no, a->subno, iobuf_desc (a, desc), a->filter_eof, + (int)a->d.start, (int)a->d.len ); } } int iobuf_print_chain( IOBUF a ) { print_chain(a); return 0; } /**************** * Allocate a new io buffer, with no function assigned. * Use is the desired usage: 1 for input, 2 for output, 3 for temp buffer * BUFSIZE is a suggested buffer size. */ IOBUF iobuf_alloc(int use, size_t bufsize) { IOBUF a; static int number=0; a = xmalloc_clear(sizeof *a); a->use = use; a->d.buf = xmalloc( bufsize ); a->d.size = bufsize; a->no = ++number; a->subno = 0; a->opaque = NULL; a->real_fname = NULL; return a; } int iobuf_close ( IOBUF a ) { IOBUF a2; size_t dummy_len=0; int rc=0; if( a && a->directfp ) { fclose( a->directfp ); xfree( a->real_fname ); if( DBG_IOBUF ) log_debug("iobuf_close -> %p\n", a->directfp ); return 0; } for( ; a && !rc ; a = a2 ) { + byte desc[MAX_IOBUF_DESC]; a2 = a->chain; if( a->use == 2 && (rc=iobuf_flush(a)) ) log_error("iobuf_flush failed on close: %s\n", g10_errstr(rc)); if( DBG_IOBUF ) log_debug("iobuf-%d.%d: close `%s'\n", a->no, a->subno, - a->desc?a->desc:"?"); + iobuf_desc (a, desc)); if( a->filter && (rc = a->filter(a->filter_ov, IOBUFCTRL_FREE, a->chain, NULL, &dummy_len)) ) log_error("IOBUFCTRL_FREE failed on close: %s\n", g10_errstr(rc) ); xfree(a->real_fname); if (a->d.buf) { memset (a->d.buf, 0, a->d.size); /* erase the buffer */ xfree(a->d.buf); } xfree(a); } return rc; } int iobuf_cancel( IOBUF a ) { const char *s; IOBUF a2; int rc; #if defined(HAVE_DOSISH_SYSTEM) || defined(__riscos__) char *remove_name = NULL; #endif if( a && a->use == 2 ) { s = iobuf_get_real_fname(a); if( s && *s ) { #if defined(HAVE_DOSISH_SYSTEM) || defined(__riscos__) remove_name = xstrdup ( s ); #else remove(s); #endif } } /* send a cancel message to all filters */ for( a2 = a; a2 ; a2 = a2->chain ) { size_t dummy; if( a2->filter ) a2->filter( a2->filter_ov, IOBUFCTRL_CANCEL, a2->chain, NULL, &dummy ); } rc = iobuf_close(a); #if defined(HAVE_DOSISH_SYSTEM) || defined(__riscos__) if ( remove_name ) { /* Argg, MSDOS does not allow to remove open files. So * we have to do it here */ remove ( remove_name ); xfree ( remove_name ); } #endif return rc; } /**************** * create a temporary iobuf, which can be used to collect stuff * in an iobuf and later be written by iobuf_write_temp() to another * iobuf. */ IOBUF iobuf_temp() { IOBUF a; a = iobuf_alloc(3, IOBUF_BUFFER_SIZE ); return a; } IOBUF iobuf_temp_with_content( const char *buffer, size_t length ) { IOBUF a; a = iobuf_alloc(3, length ); memcpy( a->d.buf, buffer, length ); a->d.len = length; return a; } void iobuf_enable_special_filenames ( int yes ) { special_names_enabled = yes; } /* * see whether the filename has the for "-&nnnn", where n is a * non-zero number. * Returns this number or -1 if it is not the case. */ static int check_special_filename ( const char *fname ) { if ( special_names_enabled && fname && *fname == '-' && fname[1] == '&' ) { int i; fname += 2; for (i=0; digitp (fname+i); i++ ) ; if ( !fname[i] ) return atoi (fname); } return -1; } /* This fucntion returns true if FNAME indicates a PIPE (stdout or stderr) or a special file name if those are enabled. */ int iobuf_is_pipe_filename (const char *fname) { if (!fname || (*fname=='-' && !fname[1]) ) return 1; return check_special_filename (fname) != -1; } /**************** * Create a head iobuf for reading from a file * returns: NULL if an error occures and sets errno */ IOBUF iobuf_open( const char *fname ) { IOBUF a; FILEP_OR_FD fp; file_filter_ctx_t *fcx; size_t len = 0; int print_only = 0; int fd; if( !fname || (*fname=='-' && !fname[1]) ) { fp = FILEP_OR_FD_FOR_STDIN; #ifdef USE_SETMODE setmode ( my_fileno(fp) , O_BINARY ); #endif fname = "[stdin]"; print_only = 1; } else if ( (fd = check_special_filename ( fname )) != -1 ) return iobuf_fdopen ( translate_file_handle (fd,0), "rb" ); else if( (fp = my_fopen_ro(fname, "rb")) == INVALID_FP ) return NULL; a = iobuf_alloc(1, IOBUF_BUFFER_SIZE ); fcx = xmalloc( sizeof *fcx + strlen(fname) ); fcx->fp = fp; fcx->print_only_name = print_only; strcpy(fcx->fname, fname ); if( !print_only ) a->real_fname = xstrdup( fname ); a->filter = file_filter; a->filter_ov = fcx; - file_filter( fcx, IOBUFCTRL_DESC, NULL, (byte*)&a->desc, &len ); file_filter( fcx, IOBUFCTRL_INIT, NULL, NULL, &len ); if( DBG_IOBUF ) log_debug("iobuf-%d.%d: open `%s' fd=%d\n", a->no, a->subno, fname, (int)my_fileno(fcx->fp) ); return a; } /**************** * Create a head iobuf for reading from a file * returns: NULL if an error occures and sets errno */ IOBUF iobuf_fdopen( int fd, const char *mode ) { IOBUF a; FILEP_OR_FD fp; file_filter_ctx_t *fcx; size_t len; #ifdef FILE_FILTER_USES_STDIO if( !(fp = fdopen(fd, mode)) ) return NULL; #else fp = (FILEP_OR_FD)fd; #endif a = iobuf_alloc( strchr( mode, 'w')? 2:1, IOBUF_BUFFER_SIZE ); fcx = xmalloc( sizeof *fcx + 20 ); fcx->fp = fp; fcx->print_only_name = 1; sprintf(fcx->fname, "[fd %d]", fd ); a->filter = file_filter; a->filter_ov = fcx; - file_filter( fcx, IOBUFCTRL_DESC, NULL, (byte*)&a->desc, &len ); file_filter( fcx, IOBUFCTRL_INIT, NULL, NULL, &len ); if( DBG_IOBUF ) log_debug("iobuf-%d.%d: fdopen `%s'\n", a->no, a->subno, fcx->fname ); iobuf_ioctl (a,3,1,NULL); /* disable fd caching */ return a; } IOBUF iobuf_sockopen ( int fd, const char *mode ) { IOBUF a; #ifdef _WIN32 sock_filter_ctx_t *scx; size_t len; a = iobuf_alloc( strchr( mode, 'w')? 2:1, IOBUF_BUFFER_SIZE ); scx = xmalloc( sizeof *scx + 25 ); scx->sock = fd; scx->print_only_name = 1; sprintf(scx->fname, "[sock %d]", fd ); a->filter = sock_filter; a->filter_ov = scx; - sock_filter( scx, IOBUFCTRL_DESC, NULL, (byte*)&a->desc, &len ); sock_filter( scx, IOBUFCTRL_INIT, NULL, NULL, &len ); if( DBG_IOBUF ) log_debug("iobuf-%d.%d: sockopen `%s'\n", a->no, a->subno, scx->fname); iobuf_ioctl (a,3,1,NULL); /* disable fd caching */ #else a = iobuf_fdopen (fd, mode); #endif return a; } /**************** * create an iobuf for writing to a file; the file will be created. */ IOBUF iobuf_create( const char *fname ) { IOBUF a; FILEP_OR_FD fp; file_filter_ctx_t *fcx; size_t len; int print_only = 0; int fd; + byte desc[MAX_IOBUF_DESC]; if( !fname || (*fname=='-' && !fname[1]) ) { fp = FILEP_OR_FD_FOR_STDOUT; #ifdef USE_SETMODE setmode ( my_fileno(fp) , O_BINARY ); #endif fname = "[stdout]"; print_only = 1; } else if ( (fd = check_special_filename ( fname )) != -1 ) return iobuf_fdopen ( translate_file_handle (fd, 1), "wb" ); else if( (fp = my_fopen(fname, "wb")) == INVALID_FP ) return NULL; a = iobuf_alloc(2, IOBUF_BUFFER_SIZE ); fcx = xmalloc( sizeof *fcx + strlen(fname) ); fcx->fp = fp; fcx->print_only_name = print_only; strcpy(fcx->fname, fname ); if( !print_only ) a->real_fname = xstrdup( fname ); a->filter = file_filter; a->filter_ov = fcx; - file_filter( fcx, IOBUFCTRL_DESC, NULL, (byte*)&a->desc, &len ); file_filter( fcx, IOBUFCTRL_INIT, NULL, NULL, &len ); if( DBG_IOBUF ) log_debug("iobuf-%d.%d: create `%s'\n", a->no, a->subno, - a->desc?a->desc:"?" ); + iobuf_desc (a, desc)); return a; } /**************** * append to an iobuf; if the file does not exist, create it. * cannot be used for stdout. * Note: This is not used. */ #if 0 /* not used */ IOBUF iobuf_append( const char *fname ) { IOBUF a; FILE *fp; file_filter_ctx_t *fcx; size_t len; + byte desc[MAX_IOBUF_DESC]; if( !fname ) return NULL; else if( !(fp = my_fopen(fname, "ab")) ) return NULL; a = iobuf_alloc(2, IOBUF_BUFFER_SIZE ); fcx = xmalloc( sizeof *fcx + strlen(fname) ); fcx->fp = fp; strcpy(fcx->fname, fname ); a->real_fname = xstrdup( fname ); a->filter = file_filter; a->filter_ov = fcx; - file_filter( fcx, IOBUFCTRL_DESC, NULL, (byte*)&a->desc, &len ); file_filter( fcx, IOBUFCTRL_INIT, NULL, NULL, &len ); if( DBG_IOBUF ) log_debug("iobuf-%d.%d: append `%s'\n", a->no, a->subno, - a->desc?a->desc:"?" ); + iobuf_desc (a, desc)); return a; } #endif IOBUF iobuf_openrw( const char *fname ) { IOBUF a; FILEP_OR_FD fp; file_filter_ctx_t *fcx; size_t len; + byte desc[MAX_IOBUF_DESC]; if( !fname ) return NULL; else if( (fp = my_fopen(fname, "r+b")) == INVALID_FP ) return NULL; a = iobuf_alloc(2, IOBUF_BUFFER_SIZE ); fcx = xmalloc( sizeof *fcx + strlen(fname) ); fcx->fp = fp; strcpy(fcx->fname, fname ); a->real_fname = xstrdup( fname ); a->filter = file_filter; a->filter_ov = fcx; - file_filter( fcx, IOBUFCTRL_DESC, NULL, (byte*)&a->desc, &len ); file_filter( fcx, IOBUFCTRL_INIT, NULL, NULL, &len ); if( DBG_IOBUF ) log_debug("iobuf-%d.%d: openrw `%s'\n", a->no, a->subno, - a->desc?a->desc:"?"); + iobuf_desc (a, desc)); return a; } int iobuf_ioctl ( IOBUF a, int cmd, int intval, void *ptrval ) { + byte desc[MAX_IOBUF_DESC]; + if ( cmd == 1 ) { /* keep system filepointer/descriptor open */ if( DBG_IOBUF ) log_debug("iobuf-%d.%d: ioctl `%s' keep=%d\n", a? a->no:-1, a?a->subno:-1, - a&&a->desc?a->desc:"?", intval ); + iobuf_desc (a, desc), intval ); for( ; a; a = a->chain ) if( !a->chain && a->filter == file_filter ) { file_filter_ctx_t *b = a->filter_ov; b->keep_open = intval; return 0; } #ifdef _WIN32 else if( !a->chain && a->filter == sock_filter ) { sock_filter_ctx_t *b = a->filter_ov; b->keep_open = intval; return 0; } #endif } else if ( cmd == 2 ) { /* invalidate cache */ if( DBG_IOBUF ) log_debug("iobuf-*.*: ioctl `%s' invalidate\n", ptrval? (char*)ptrval:"[all]"); if ( !a && !intval ) { #ifndef FILE_FILTER_USES_STDIO return fd_cache_invalidate (ptrval); #endif return 0; } } else if ( cmd == 3 ) { /* disallow/allow caching */ if( DBG_IOBUF ) log_debug("iobuf-%d.%d: ioctl `%s' no_cache=%d\n", a? a->no:-1, a?a->subno:-1, - a&&a->desc?a->desc:"?", intval ); + iobuf_desc (a, desc), intval ); for( ; a; a = a->chain ) if( !a->chain && a->filter == file_filter ) { file_filter_ctx_t *b = a->filter_ov; b->no_cache = intval; return 0; } #ifdef _WIN32 else if( !a->chain && a->filter == sock_filter ) { sock_filter_ctx_t *b = a->filter_ov; b->no_cache = intval; return 0; } #endif } else if(cmd==4) { /* Do a fsync on the open fd and return any errors to the caller of iobuf_ioctl */ if( DBG_IOBUF ) log_debug("iobuf-*.*: ioctl `%s' fsync\n", ptrval? (char*)ptrval:""); if(!a && !intval && ptrval) { #ifndef FILE_FILTER_USES_STDIO return fd_cache_synchronize (ptrval); #else return 0; #endif } } return -1; } /**************** * Register an i/o filter. */ int iobuf_push_filter( IOBUF a, int (*f)(void *opaque, int control, IOBUF chain, byte *buf, size_t *len), void *ov ) { return iobuf_push_filter2( a, f, ov, 0 ); } int iobuf_push_filter2( IOBUF a, int (*f)(void *opaque, int control, IOBUF chain, byte *buf, size_t *len), void *ov, int rel_ov ) { IOBUF b; size_t dummy_len=0; int rc=0; if( a->directfp ) BUG(); if( a->use == 2 && (rc=iobuf_flush(a)) ) return rc; if (a->subno >= MAX_NESTING_FILTER) { log_error ("i/o filter too deeply nested - corrupted data?\n"); return G10ERR_UNEXPECTED; } /* make a copy of the current stream, so that * A is the new stream and B the original one. * The contents of the buffers are transferred to the * new stream. */ b = xmalloc(sizeof *b); memcpy(b, a, sizeof *b ); /* fixme: it is stupid to keep a copy of the name at every level * but we need the name somewhere because the name known by file_filter * may have been released when we need the name of the file */ b->real_fname = a->real_fname? xstrdup(a->real_fname):NULL; /* remove the filter stuff from the new stream */ a->filter = NULL; a->filter_ov = NULL; a->filter_ov_owner = 0; a->filter_eof = 0; if( a->use == 3 ) a->use = 2; /* make a write stream from a temp stream */ if( a->use == 2 ) { /* allocate a fresh buffer for the original stream */ b->d.buf = xmalloc( a->d.size ); b->d.len = 0; b->d.start = 0; } else { /* allocate a fresh buffer for the new stream */ a->d.buf = xmalloc( a->d.size ); a->d.len = 0; a->d.start = 0; } /* disable nlimit for the new stream */ a->ntotal = b->ntotal + b->nbytes; a->nlimit = a->nbytes = 0; a->nofast &= ~1; /* make a link from the new stream to the original stream */ a->chain = b; a->opaque = b->opaque; /* setup the function on the new stream */ a->filter = f; a->filter_ov = ov; a->filter_ov_owner = rel_ov; a->subno = b->subno + 1; - f( ov, IOBUFCTRL_DESC, NULL, (byte*)&a->desc, &dummy_len ); if( DBG_IOBUF ) { + byte desc[MAX_IOBUF_DESC]; + log_debug("iobuf-%d.%d: push `%s'\n", a->no, a->subno, - a->desc?a->desc:"?" ); + iobuf_desc (a, desc)); print_chain( a ); } /* now we can initialize the new function if we have one */ if( a->filter && (rc = a->filter(a->filter_ov, IOBUFCTRL_INIT, a->chain, NULL, &dummy_len)) ) log_error("IOBUFCTRL_INIT failed: %s\n", g10_errstr(rc) ); return rc; } /**************** * Remove an i/o filter. */ static int pop_filter( IOBUF a, int (*f)(void *opaque, int control, IOBUF chain, byte *buf, size_t *len), void *ov ) { IOBUF b; size_t dummy_len=0; int rc=0; + byte desc[MAX_IOBUF_DESC]; if( a->directfp ) BUG(); if( DBG_IOBUF ) log_debug("iobuf-%d.%d: pop `%s'\n", a->no, a->subno, - a->desc?a->desc:"?" ); + iobuf_desc (a, desc)); if( !a->filter ) { /* this is simple */ b = a->chain; assert(b); xfree(a->d.buf); xfree(a->real_fname); memcpy(a,b, sizeof *a); xfree(b); return 0; } for(b=a ; b; b = b->chain ) if( b->filter == f && (!ov || b->filter_ov == ov) ) break; if( !b ) log_bug("pop_filter(): filter function not found\n"); /* flush this stream if it is an output stream */ if( a->use == 2 && (rc=iobuf_flush(b)) ) { log_error("iobuf_flush failed in pop_filter: %s\n", g10_errstr(rc)); return rc; } /* and tell the filter to free it self */ if( b->filter && (rc = b->filter(b->filter_ov, IOBUFCTRL_FREE, b->chain, NULL, &dummy_len)) ) { log_error("IOBUFCTRL_FREE failed: %s\n", g10_errstr(rc) ); return rc; } if( b->filter_ov && b->filter_ov_owner ) { xfree( b->filter_ov ); b->filter_ov = NULL; } /* and see how to remove it */ if( a == b && !b->chain ) log_bug("can't remove the last filter from the chain\n"); else if( a == b ) { /* remove the first iobuf from the chain */ /* everything from b is copied to a. This is save because * a flush has been done on the to be removed entry */ b = a->chain; xfree(a->d.buf); xfree(a->real_fname); memcpy(a,b, sizeof *a); xfree(b); if( DBG_IOBUF ) log_debug("iobuf-%d.%d: popped filter\n", a->no, a->subno ); } else if( !b->chain ) { /* remove the last iobuf from the chain */ log_bug("Ohh jeee, trying to remove a head filter\n"); } else { /* remove an intermediate iobuf from the chain */ log_bug("Ohh jeee, trying to remove an intermediate filter\n"); } return rc; } /**************** * read underflow: read more bytes into the buffer and return * the first byte or -1 on EOF. */ static int underflow(IOBUF a) { size_t len; int rc; assert( a->d.start == a->d.len ); if( a->use == 3 ) return -1; /* EOF because a temp buffer can't do an underflow */ if( a->filter_eof ) { if( a->chain ) { + byte desc[MAX_IOBUF_DESC]; + IOBUF b = a->chain; if( DBG_IOBUF ) log_debug("iobuf-%d.%d: pop `%s' in underflow\n", - a->no, a->subno, a->desc?a->desc:"?" ); + a->no, a->subno, iobuf_desc (a, desc) ); xfree(a->d.buf); xfree(a->real_fname); memcpy(a, b, sizeof *a); xfree(b); print_chain(a); } else a->filter_eof = 0; /* for the top level filter */ if( DBG_IOBUF ) log_debug("iobuf-%d.%d: underflow: eof (due to filter eof)\n", a->no, a->subno ); return -1; /* return one(!) EOF */ } if( a->error ) { if( DBG_IOBUF ) log_debug("iobuf-%d.%d: error\n", a->no, a->subno ); return -1; } if( a->directfp ) { FILE *fp = a->directfp; len = fread( a->d.buf, 1, a->d.size, fp); if( len < a->d.size ) { if( ferror(fp) ) a->error = 1; } a->d.len = len; a->d.start = 0; return len? a->d.buf[a->d.start++] : -1; } if( a->filter ) { len = a->d.size; if( DBG_IOBUF ) log_debug("iobuf-%d.%d: underflow: req=%lu\n", a->no, a->subno, (ulong)len ); rc = a->filter( a->filter_ov, IOBUFCTRL_UNDERFLOW, a->chain, a->d.buf, &len ); if( DBG_IOBUF ) { log_debug("iobuf-%d.%d: underflow: got=%lu rc=%d\n", a->no, a->subno, (ulong)len, rc ); /* if( a->no == 1 ) */ /* log_hexdump (" data:", a->d.buf, len); */ } if( a->use == 1 && rc == -1 ) { /* EOF: we can remove the filter */ size_t dummy_len=0; /* and tell the filter to free itself */ if( (rc = a->filter(a->filter_ov, IOBUFCTRL_FREE, a->chain, NULL, &dummy_len)) ) log_error("IOBUFCTRL_FREE failed: %s\n", g10_errstr(rc) ); if( a->filter_ov && a->filter_ov_owner ) { xfree( a->filter_ov ); a->filter_ov = NULL; } a->filter = NULL; - a->desc = NULL; a->filter_ov = NULL; a->filter_eof = 1; if( !len && a->chain ) { IOBUF b = a->chain; if( DBG_IOBUF ) log_debug("iobuf-%d.%d: pop in underflow (!len)\n", a->no, a->subno); xfree(a->d.buf); xfree(a->real_fname); memcpy(a,b, sizeof *a); xfree(b); print_chain(a); } } else if( rc ) a->error = 1; if( !len ) { if( DBG_IOBUF ) log_debug("iobuf-%d.%d: underflow: eof\n", a->no, a->subno ); return -1; } a->d.len = len; a->d.start = 0; return a->d.buf[a->d.start++]; } else { if( DBG_IOBUF ) log_debug("iobuf-%d.%d: underflow: eof (no filter)\n", a->no, a->subno ); return -1; /* no filter; return EOF */ } } int iobuf_flush(IOBUF a) { size_t len; int rc; if( a->directfp ) return 0; if( a->use == 3 ) { /* increase the temp buffer */ char *newbuf; size_t newsize = a->d.size + IOBUF_BUFFER_SIZE; if( DBG_IOBUF ) log_debug("increasing temp iobuf from %lu to %lu\n", (ulong)a->d.size, (ulong)newsize ); newbuf = xmalloc( newsize ); memcpy( newbuf, a->d.buf, a->d.len ); xfree(a->d.buf); a->d.buf = newbuf; a->d.size = newsize; return 0; } else if( a->use != 2 ) log_bug("flush on non-output iobuf\n"); else if( !a->filter ) log_bug("iobuf_flush: no filter\n"); len = a->d.len; rc = a->filter( a->filter_ov, IOBUFCTRL_FLUSH, a->chain, a->d.buf, &len ); if( !rc && len != a->d.len ) { log_info("iobuf_flush did not write all!\n"); rc = G10ERR_WRITE_FILE; } else if( rc ) a->error = 1; a->d.len = 0; return rc; } /**************** * Read a byte from the iobuf; returns -1 on EOF */ int iobuf_readbyte(IOBUF a) { int c; /* nlimit does not work together with unget */ /* nbytes is also not valid! */ if( a->unget.buf ) { if( a->unget.start < a->unget.len ) return a->unget.buf[a->unget.start++]; xfree(a->unget.buf); a->unget.buf = NULL; a->nofast &= ~2; } if( a->nlimit && a->nbytes >= a->nlimit ) return -1; /* forced EOF */ if( a->d.start < a->d.len ) { c = a->d.buf[a->d.start++]; } else if( (c=underflow(a)) == -1 ) return -1; /* EOF */ a->nbytes++; return c; } int iobuf_read(IOBUF a, byte *buf, unsigned buflen ) { int c, n; if( a->unget.buf || a->nlimit ) { /* handle special cases */ for(n=0 ; n < buflen; n++ ) { if( (c = iobuf_readbyte(a)) == -1 ) { if( !n ) return -1; /* eof */ break; } else if( buf ) *buf = c; if( buf ) buf++; } return n; } n = 0; do { if( n < buflen && a->d.start < a->d.len ) { unsigned size = a->d.len - a->d.start; if( size > buflen - n ) size = buflen - n; if( buf ) memcpy( buf, a->d.buf + a->d.start, size ); n += size; a->d.start += size; if( buf ) buf += size; } if( n < buflen ) { if( (c=underflow(a)) == -1 ) { a->nbytes += n; return n? n : -1/*EOF*/; } if( buf ) *buf++ = c; n++; } } while( n < buflen ); a->nbytes += n; return n; } /**************** * Have a look at the iobuf. * NOTE: This only works in special cases. */ int iobuf_peek(IOBUF a, byte *buf, unsigned buflen ) { int n=0; if( a->filter_eof ) return -1; if( !(a->d.start < a->d.len) ) { if( underflow(a) == -1 ) return -1; /* and unget this character */ assert(a->d.start == 1); a->d.start = 0; } for(n=0 ; n < buflen && (a->d.start+n) < a->d.len ; n++, buf++ ) *buf = a->d.buf[n]; return n; } int iobuf_writebyte(IOBUF a, unsigned c) { if( a->directfp ) BUG(); if( a->d.len == a->d.size ) if( iobuf_flush(a) ) return -1; assert( a->d.len < a->d.size ); a->d.buf[a->d.len++] = c; return 0; } int iobuf_write(IOBUF a, byte *buf, unsigned buflen ) { if( a->directfp ) BUG(); do { if( buflen && a->d.len < a->d.size ) { unsigned size = a->d.size - a->d.len; if( size > buflen ) size = buflen; memcpy( a->d.buf + a->d.len, buf, size ); buflen -= size; buf += size; a->d.len += size; } if( buflen ) { if( iobuf_flush(a) ) return -1; } } while( buflen ); return 0; } int iobuf_writestr(IOBUF a, const char *buf ) { for( ; *buf; buf++ ) if( iobuf_writebyte(a, *buf) ) return -1; return 0; } /**************** * copy the contents of TEMP to A. */ int iobuf_write_temp( IOBUF a, IOBUF temp ) { while( temp->chain ) pop_filter( temp, temp->filter, NULL ); return iobuf_write(a, temp->d.buf, temp->d.len ); } /**************** * copy the contents of the temp io stream to BUFFER. */ size_t iobuf_temp_to_buffer( IOBUF a, byte *buffer, size_t buflen ) { size_t n = a->d.len; if( n > buflen ) n = buflen; memcpy( buffer, a->d.buf, n ); return n; } /**************** * Call this function to terminate processing of the temp stream * without closing it. This removes all filters from the stream * makes sure that iobuf_get_temp_{buffer,length}() returns correct * values. */ void iobuf_flush_temp( IOBUF temp ) { while( temp->chain ) pop_filter( temp, temp->filter, NULL ); } /**************** * Set a limit on how many bytes may be read from the input stream A. * Setting the limit to 0 disables this feature. */ void iobuf_set_limit( IOBUF a, off_t nlimit ) { if( nlimit ) a->nofast |= 1; else a->nofast &= ~1; a->nlimit = nlimit; a->ntotal += a->nbytes; a->nbytes = 0; } /* Return the length of an open file A. IF OVERFLOW is not NULL it will be set to true if the file is larger than what off_t can cope with. The function return 0 on error or on overflow condition. */ off_t iobuf_get_filelength (IOBUF a, int *overflow ) { struct stat st; if (overflow) *overflow = 0; if (a->directfp) { FILE *fp = a->directfp; #ifdef __VMS /* 2009-02-19 SMS. * On VMS, use a VMS-specific method to determine file size. * For some non-UNIX-like file formats, the fstat() result * will not agree with the C Standard I/O functions such as * getc() and fread(), so these must be detected and handled * specially. */ return vms_file_size (fileno( fp)); #else /*!__VMS */ if( !fstat(fileno(fp), &st) ) return st.st_size; log_error("fstat() failed: %s\n", strerror(errno) ); return 0; #endif /*!__VMS */ } /* Hmmm: file_filter may have already been removed */ for( ; a; a = a->chain ) if( !a->chain && a->filter == file_filter ) { file_filter_ctx_t *b = a->filter_ov; FILEP_OR_FD fp = b->fp; #if defined(HAVE_DOSISH_SYSTEM) && !defined(FILE_FILTER_USES_STDIO) ulong size; static int (* __stdcall get_file_size_ex) (void *handle, LARGE_INTEGER *size); static int get_file_size_ex_initialized; if (!get_file_size_ex_initialized) { void *handle; handle = dlopen ("kernel32.dll", RTLD_LAZY); if (handle) { get_file_size_ex = dlsym (handle, "GetFileSizeEx"); if (!get_file_size_ex) dlclose (handle); } get_file_size_ex_initialized = 1; } if (get_file_size_ex) { /* This is a newer system with GetFileSizeEx; we use this then becuase it seem that GetFileSize won't return a proper error in case a file is larger than 4GB. */ LARGE_INTEGER size; if (get_file_size_ex (fp, &size)) { if (!size.u.HighPart) return size.u.LowPart; if (overflow) *overflow = 1; return 0; } } else { if ((size=GetFileSize (fp, NULL)) != 0xffffffff) return size; } log_error ("GetFileSize for handle %p failed: %s\n", fp, w32_strerror (0)); #elif defined(__VMS) return vms_file_size (my_fileno (fp)); #else if( !fstat(my_fileno(fp), &st) ) return st.st_size; log_error("fstat() failed: %s\n", strerror(errno) ); #endif break; } return 0; } /* Return the file descriptor of the underlying file or -1 if it is not available. */ int iobuf_get_fd (IOBUF a) { if (a->directfp) return fileno ( (FILE*)a->directfp ); for ( ; a; a = a->chain ) if (!a->chain && a->filter == file_filter) { file_filter_ctx_t *b = a->filter_ov; FILEP_OR_FD fp = b->fp; return my_fileno (fp); } return -1; } /**************** * Tell the file position, where the next read will take place */ off_t iobuf_tell( IOBUF a ) { return a->ntotal + a->nbytes; } #if !defined(HAVE_FSEEKO) && !defined(fseeko) #ifdef HAVE_LIMITS_H # include #endif #ifndef LONG_MAX # define LONG_MAX ((long) ((unsigned long) -1 >> 1)) #endif #ifndef LONG_MIN # define LONG_MIN (-1 - LONG_MAX) #endif /**************** * A substitute for fseeko, for hosts that don't have it. */ static int fseeko( FILE *stream, off_t newpos, int whence ) { while( newpos != (long) newpos ) { long pos = newpos < 0 ? LONG_MIN : LONG_MAX; if( fseek( stream, pos, whence ) != 0 ) return -1; newpos -= pos; whence = SEEK_CUR; } return fseek( stream, (long)newpos, whence ); } #endif /**************** * This is a very limited implementation. It simply discards all internal * buffering and removes all filters but the first one. */ int iobuf_seek( IOBUF a, off_t newpos ) { file_filter_ctx_t *b = NULL; if( a->directfp ) { FILE *fp = a->directfp; if( fseeko( fp, newpos, SEEK_SET ) ) { log_error("can't seek: %s\n", strerror(errno) ); return -1; } clearerr(fp); } else { for( ; a; a = a->chain ) { if( !a->chain && a->filter == file_filter ) { b = a->filter_ov; break; } } if( !a ) return -1; #ifdef FILE_FILTER_USES_STDIO if( fseeko( b->fp, newpos, SEEK_SET ) ) { log_error("can't fseek: %s\n", strerror(errno) ); return -1; } #else #ifdef HAVE_DOSISH_SYSTEM if (SetFilePointer (b->fp, newpos, NULL, FILE_BEGIN) == 0xffffffff ) { log_error ("SetFilePointer failed on handle %p: %s\n", b->fp, w32_strerror (0)); return -1; } #else if ( lseek (b->fp, newpos, SEEK_SET) == (off_t)-1 ) { log_error("can't lseek: %s\n", strerror(errno) ); return -1; } #endif #endif } a->d.len = 0; /* discard buffer */ a->d.start = 0; a->nbytes = 0; a->nlimit = 0; a->nofast &= ~1; a->ntotal = newpos; a->error = 0; /* remove filters, but the last */ if( a->chain ) log_debug("pop_filter called in iobuf_seek - please report\n"); while( a->chain ) pop_filter( a, a->filter, NULL ); return 0; } /**************** * Retrieve the real filename */ const char * iobuf_get_real_fname( IOBUF a ) { if( a->real_fname ) return a->real_fname; /* the old solution */ for( ; a; a = a->chain ) if( !a->chain && a->filter == file_filter ) { file_filter_ctx_t *b = a->filter_ov; return b->print_only_name? NULL : b->fname; } return NULL; } /**************** * Retrieve the filename */ const char * iobuf_get_fname( IOBUF a ) { for( ; a; a = a->chain ) if( !a->chain && a->filter == file_filter ) { file_filter_ctx_t *b = a->filter_ov; return b->fname; } return NULL; } /**************** * enable partial block mode as described in the OpenPGP draft. * LEN is the first length byte on read, but ignored on writes. */ void iobuf_set_partial_block_mode( IOBUF a, size_t len ) { block_filter_ctx_t *ctx = xmalloc_clear( sizeof *ctx ); assert( a->use == 1 || a->use == 2 ); ctx->use = a->use; if( !len ) { if( a->use == 1 ) log_debug("pop_filter called in set_partial_block_mode" " - please report\n"); pop_filter(a, block_filter, NULL ); } else { ctx->partial = 1; ctx->size = 0; ctx->first_c = len; iobuf_push_filter(a, block_filter, ctx ); } } /**************** * Same as fgets() but if the buffer is too short a larger one will * be allocated up to some limit *max_length. * A line is considered a byte stream ending in a LF. * Returns the length of the line. EOF is indicated by a line of * length zero. The last LF may be missing due to an EOF. * is max_length is zero on return, the line has been truncated. * * Note: The buffer is allocated with enough space to append a CR,LF,EOL */ unsigned iobuf_read_line( IOBUF a, byte **addr_of_buffer, unsigned *length_of_buffer, unsigned *max_length ) { int c; char *buffer = *addr_of_buffer; unsigned length = *length_of_buffer; unsigned nbytes = 0; unsigned maxlen = *max_length; char *p; if( !buffer ) { /* must allocate a new buffer */ length = 256; buffer = xmalloc( length ); *addr_of_buffer = buffer; *length_of_buffer = length; } length -= 3; /* reserve 3 bytes (cr,lf,eol) */ p = buffer; while( (c=iobuf_get(a)) != -1 ) { if( nbytes == length ) { /* increase the buffer */ if( length > maxlen ) { /* this is out limit */ /* skip the rest of the line */ while( c != '\n' && (c=iobuf_get(a)) != -1 ) ; *p++ = '\n'; /* always append a LF (we have reserved space) */ nbytes++; *max_length = 0; /* indicate truncation */ break; } length += 3; /* correct for the reserved byte */ length += length < 1024? 256 : 1024; buffer = xrealloc( buffer, length ); *addr_of_buffer = buffer; *length_of_buffer = length; length -= 3; /* and reserve again */ p = buffer + nbytes; } *p++ = c; nbytes++; if( c == '\n' ) break; } *p = 0; /* make sure the line is a string */ return nbytes; } /* This is the non iobuf specific function */ int iobuf_translate_file_handle ( int fd, int for_write ) { #ifdef _WIN32 { int x; if ( fd <= 2 ) return fd; /* do not do this for error, stdin, stdout, stderr */ x = _open_osfhandle ( fd, for_write? 1:0 ); if (x==-1 ) log_error ("failed to translate osfhandle %p\n", (void*)fd ); else { /*log_info ("_open_osfhandle %p yields %d%s\n", (void*)fd, x, for_write? " for writing":"" );*/ fd = x; } } #endif return fd; } static int translate_file_handle ( int fd, int for_write ) { #ifdef _WIN32 #ifdef FILE_FILTER_USES_STDIO fd = iobuf_translate_file_handle (fd, for_write); #else { int x; if ( fd == 0 ) x = (int)GetStdHandle (STD_INPUT_HANDLE); else if (fd == 1) x = (int)GetStdHandle (STD_OUTPUT_HANDLE); else if (fd == 2) x = (int)GetStdHandle (STD_ERROR_HANDLE); else x = fd; if (x == -1) log_debug ("GetStdHandle(%d) failed: %s\n", fd, w32_strerror (0)); fd = x; } #endif #endif return fd; } void iobuf_skip_rest(IOBUF a, unsigned long n, int partial) { if ( partial ) { for (;;) { if (a->nofast || a->d.start >= a->d.len) { if (iobuf_readbyte (a) == -1) { break; } } else { unsigned long count = a->d.len - a->d.start; a->nbytes += count; a->d.start = a->d.len; } } } else { unsigned long remaining = n; while (remaining > 0) { if (a->nofast || a->d.start >= a->d.len) { if (iobuf_readbyte (a) == -1) { break; } --remaining; } else { unsigned long count = a->d.len - a->d.start; if (count > remaining) { count = remaining; } a->nbytes += count; a->d.start += count; remaining -= count; } } } }