diff --git a/NEWS b/NEWS index 41d9b4fd6..253e8a844 100644 --- a/NEWS +++ b/NEWS @@ -1,7 +1,14 @@ + * It's now 64 bit clean and runs fine on an alpha--linux. + + * Key generation is much faster now. I fixed this by using not + so strong random number for the primes (this was a bug because the + ElGamal primes are public parameters and it does not make sense + to generate them from strong random). The real secret is the x value + which is still generated from strong (okay: /dev/random) random bits. + * added option "--status-fd": see g10/OPTIONS - * We have secure memeory on systems which support mlock(). It is not complete yet, because we do not have signal handler which does a cleanup in very case. diff --git a/TODO b/TODO index 18f0314cb..5a8fc7914 100644 --- a/TODO +++ b/TODO @@ -27,3 +27,7 @@ we have a self-signature -> put this stuff into a kind of directory record, as it does not belong to the pubkey record? + * Have no prototype for stpcpy() when using glibc 2; must switch on + the GNU extensions or see how configure can fix it. + + diff --git a/acconfig.h b/acconfig.h index 728d4d1eb..f8ddcd581 100644 --- a/acconfig.h +++ b/acconfig.h @@ -20,6 +20,10 @@ #ifndef G10_CONFIG_H #define G10_CONFIG_H +/* need this, because some autoconf tests rely on this (e.g. stpcpy) + * and it should be used for new programs + */ +#define _GNU_SOURCE 1 @TOP@ diff --git a/config.h.in b/config.h.in index 723e53f52..31e7990c3 100644 --- a/config.h.in +++ b/config.h.in @@ -21,6 +21,10 @@ #ifndef G10_CONFIG_H #define G10_CONFIG_H +/* need this, because some autoconf tests rely on this (e.g. stpcpy) + * and it should be used for new programs + */ +#define _GNU_SOURCE 1 /* Define to empty if the keyword does not work. */ diff --git a/g10/armor.c b/g10/armor.c index 7269b325a..dbe3406bf 100644 --- a/g10/armor.c +++ b/g10/armor.c @@ -32,6 +32,8 @@ #include "filter.h" #include "packet.h" #include "options.h" +#include "main.h" +#include "status.h" @@ -65,8 +67,12 @@ typedef enum { fhdrEMPTYClearsig, fhdrCHECKClearsig, fhdrCHECKClearsig2, + fhdrCHECKDashEscaped, + fhdrCHECKDashEscaped2, + fhdrCHECKDashEscaped3, fhdrREADClearsigNext, fhdrENDClearsig, + fhdrTESTSpaces, fhdrTEXT, fhdrERROR, fhdrERRORShow, @@ -92,8 +98,8 @@ static char *tail_strings[] = { }; -static fhdr_state_t find_header( fhdr_state_t state, - byte *buf, size_t *r_buflen, IOBUF a, size_t n); +static fhdr_state_t find_header( fhdr_state_t state, byte *buf, + size_t *r_buflen, IOBUF a, size_t n, unsigned *r_empty); static void @@ -156,26 +162,36 @@ is_armored( byte *buf ) return 1; } +static void +invalid_armor(void) +{ + write_status(STATUS_BADARMOR); + g10_exit(1); /* stop here */ +} + /**************** * parse an ascii armor. * Returns: the state, * the remaining bytes in BUF are returned in RBUFLEN. + * r_empty return the # of empty lines before the buffer */ static fhdr_state_t -find_header( fhdr_state_t state, byte *buf, size_t *r_buflen, IOBUF a, size_t n) +find_header( fhdr_state_t state, byte *buf, size_t *r_buflen, + IOBUF a, size_t n, unsigned *r_empty) { int c, i; const char *s; - char *p; + byte *p; size_t buflen; int cont; int clearsig=0; int hdr_line=0; + unsigned empty = 0; buflen = *r_buflen; assert(buflen >= 100 ); - buflen--; + buflen -= 3; /* reserved room for CR,LF and one extra */ do { switch( state ) { @@ -183,7 +199,7 @@ find_header( fhdr_state_t state, byte *buf, size_t *r_buflen, IOBUF a, size_t n) /* read 28 bytes, which is the bare minimum for a BEGIN... * and check wether this has a Armor. */ c = 0; - for(n=0; n < 28 && (c=iobuf_get(a)) != -1 && c != '\n'; ) + for(n=0; n < 28 && (c=iobuf_get2(a)) != -1 && c != '\n'; ) buf[n++] = c; if( n < 28 || c == -1 ) state = fhdrNOArmor; /* too short */ @@ -197,27 +213,27 @@ find_header( fhdr_state_t state, byte *buf, size_t *r_buflen, IOBUF a, size_t n) n = 0; case fhdrINITCont: /* read more stuff into buffer */ c = 0; - for(; n < buflen && (c=iobuf_get(a)) != -1 && c != '\n'; ) + for(; n < buflen && (c=iobuf_get2(a)) != -1 && c != '\n'; ) buf[n++] = c; state = c == '\n' ? fhdrCHECKBegin : c == -1 ? fhdrEOF : fhdrINITSkip; break; case fhdrINITSkip: - while( (c=iobuf_get(a)) != -1 && c != '\n' ) + while( (c=iobuf_get2(a)) != -1 && c != '\n' ) ; state = c == -1? fhdrEOF : fhdrINIT; break; case fhdrSKIPHeader: - while( (c=iobuf_get(a)) != -1 && c != '\n' ) + while( (c=iobuf_get2(a)) != -1 && c != '\n' ) ; state = c == -1? fhdrEOF : fhdrWAITHeader; break; case fhdrWAITHeader: /* wait for Header lines */ c = 0; - for(n=0; n < buflen && (c=iobuf_get(a)) != -1 && c != '\n'; ) + for(n=0; n < buflen && (c=iobuf_get2(a)) != -1 && c != '\n'; ) buf[n++] = c; buf[n] = 0; if( n < buflen || c == '\n' ) { @@ -229,21 +245,21 @@ find_header( fhdr_state_t state, byte *buf, size_t *r_buflen, IOBUF a, size_t n) state = fhdrWAITHeader; } else - state = fhdrTEXT; + state = fhdrCHECKDashEscaped3; } else if( !n || (buf[0] == '\r' && !buf[1]) ) { /* empty line */ if( clearsig ) state = fhdrWAITClearsig; else { /* this is not really correct: if we do not have - * a clearsig and not armor lines we are not allowed + * a clearsig and no armor lines we are not allowed * to have an empty line */ n = 0; state = fhdrTEXT; } } else { - log_debug("invalid armor header: "); + log_error("invalid armor header: "); print_string( stderr, buf, n ); putc('\n', stderr); state = fhdrERROR; @@ -265,17 +281,21 @@ find_header( fhdr_state_t state, byte *buf, size_t *r_buflen, IOBUF a, size_t n) case fhdrWAITClearsig: /* skip all empty lines (for clearsig) */ c = 0; - for(n=0; n < buflen && (c=iobuf_get(a)) != -1 && c != '\n'; ) + for(n=0; n < buflen && (c=iobuf_get2(a)) != -1 && c != '\n'; ) buf[n++] = c; if( n < buflen || c == '\n' ) { buf[n] = 0; if( !n || (buf[0]=='\r' && !buf[1]) ) /* empty line */ ; else - state = fhdrTEXT; + state = fhdrCHECKDashEscaped3; } - else + else { + /* fixme: we should check wether this linee continues + * it is poosible that we have only read ws until here + * and more stuff is to come */ state = fhdrEOF; + } break; case fhdrENDClearsig: @@ -313,17 +333,17 @@ find_header( fhdr_state_t state, byte *buf, size_t *r_buflen, IOBUF a, size_t n) case fhdrEMPTYClearsig: case fhdrREADClearsig: /* we are at the start of a line: read a clearsig into the buffer - * we have to look for a the header line or dashd escaped text*/ + * we have to look for a the header line or dashed escaped text*/ n = 0; c = 0; - for(; n < buflen && (c=iobuf_get(a)) != -1 && c != '\n'; ) + while( n < buflen && (c=iobuf_get2(a)) != -1 && c != '\n' ) buf[n++] = c; buf[n] = 0; if( c == -1 ) state = fhdrEOF; else if( !n || ( buf[0]=='\r' && !buf[1] ) ) { state = fhdrEMPTYClearsig; - /* FIXME: handle it */ + empty++; } else if( c == '\n' ) state = fhdrCHECKClearsig2; @@ -331,43 +351,86 @@ find_header( fhdr_state_t state, byte *buf, size_t *r_buflen, IOBUF a, size_t n) state = fhdrCHECKClearsig; break; + case fhdrCHECKDashEscaped3: + if( !(n > 1 && buf[0] == '-' && buf[1] == ' ' ) ) { + state = fhdrTEXT; + break; + } + /* fall through */ + case fhdrCHECKDashEscaped2: + case fhdrCHECKDashEscaped: + /* check dash escaped line */ + if( buf[2] == '-' || ( n > 6 && !memcmp(buf+2, "From ", 5))) { + for(i=2; i < n; i++ ) + buf[i-2] = buf[i]; + n -= 2; + buf[n] = 0; /* not really needed */ + state = state == fhdrCHECKDashEscaped3 ? fhdrTEXT : + state == fhdrCHECKDashEscaped2 ? + fhdrREADClearsig : fhdrTESTSpaces; + } + else { + log_error("invalid dash escaped line: "); + print_string( stderr, buf, n ); + putc('\n', stderr); + state = fhdrERROR; + } + break; + case fhdrCHECKClearsig: case fhdrCHECKClearsig2: /* check the clearsig line */ if( n > 15 && !memcmp(buf, "-----", 5 ) ) state = fhdrENDClearsig; - else if( buf[0] == '-' && buf[1] == ' ' ) { - /* dash escaped line */ - if( buf[2] == '-' || ( n > 6 && !memcmp(buf+2, "From ", 5))) { - for(i=2; i < n; i++ ) - buf[i-2] = buf[i]; - n -= 2; - buf[n] = 0; /* not really needed */ - state = state == fhdrCHECKClearsig2 ? - fhdrREADClearsig : fhdrREADClearsigNext; - /* FIXME: add the lf to the buffer */ - } - else { - log_debug("invalid dash escaped line: "); - print_string( stderr, buf, n ); - putc('\n', stderr); - state = fhdrERROR; - } - } + else if( buf[0] == '-' && buf[1] == ' ' ) + state = fhdrCHECKDashEscaped; else { state = state == fhdrCHECKClearsig2 ? - fhdrREADClearsig : fhdrREADClearsigNext; - /* FIXME: add the lf to the buffer */ + fhdrREADClearsig : fhdrTESTSpaces; } break; case fhdrREADClearsigNext: /* Read to the end of the line, do not care about checking * for dashed escaped text of headers */ + c = 0; + n = 0; + while( n < buflen && (c=iobuf_get2(a)) != -1 && c != '\n' ) + buf[n++] = c; + buf[n] = 0; + if( c == -1 ) + state = fhdrEOF; + else if( c == '\n' ) + state = fhdrREADClearsig; + else + state = fhdrTESTSpaces; break; + case fhdrTESTSpaces: { + /* but must check wether the rest of the line + * does only contain white spaces; this is problematic + * since we may have to restore the stuffs. simply + * counting spaces is not enough, because it may be a + * mix of different white space chacters */ + IOBUF b = iobuf_temp(); + while( (c=iobuf_get2(a)) != -1 && c != '\n' ) { + iobuf_put(b,c); + if( c != ' ' && c != '\t' && c != '\r' ) + break; + } + if( c == '\n' ) { + /* okay we can skip the rest of the line */ + iobuf_close(b); + state = fhdrREADClearsig; + } + else { + iobuf_unget_and_close_temp(a,b); + state = fhdrREADClearsigNext; + } + } break; + case fhdrERRORShow: - log_debug("invalid clear text header: "); + log_error("invalid clear text header: "); print_string( stderr, buf, n ); putc('\n', stderr); state = fhdrERROR; @@ -386,6 +449,10 @@ find_header( fhdr_state_t state, byte *buf, size_t *r_buflen, IOBUF a, size_t n) case fhdrEMPTYClearsig: case fhdrCHECKClearsig: case fhdrCHECKClearsig2: + case fhdrCHECKDashEscaped: + case fhdrCHECKDashEscaped2: + case fhdrCHECKDashEscaped3: + case fhdrTESTSpaces: case fhdrERRORShow: cont = 1; break; @@ -395,7 +462,28 @@ find_header( fhdr_state_t state, byte *buf, size_t *r_buflen, IOBUF a, size_t n) if( clearsig && state == fhdrTEXT ) state = fhdrCLEARSIG; + + if( state == fhdrCLEARSIG || state == fhdrREADClearsig ) { + /* append CR,LF after removing trailing wspaces */ + for(p=buf+n-1; n; n--, p-- ) { + assert( *p != '\n' ); + if( *p != ' ' && *p != '\t' && *p != '\r' ) { + p[1] = '\r'; + p[2] = '\n'; + n += 2; + break; + } + } + if( !n ) { + buf[0] = '\r'; + buf[1] = '\n'; + n = 2; + } + } + + *r_buflen = n; + *r_empty = empty; return state; } @@ -407,12 +495,13 @@ check_input( armor_filter_context_t *afx, IOBUF a ) int rc = 0; size_t n; fhdr_state_t state = afx->parse_state; + unsigned emplines; if( state != fhdrENDClearsig ) state = fhdrHASArmor; n = DIM(afx->helpbuf); - state = find_header( state, afx->helpbuf, &n, a, afx->helplen ); + state = find_header( state, afx->helpbuf, &n, a, afx->helplen, &emplines); switch( state ) { case fhdrNOArmor: afx->inp_checked = 1; @@ -421,6 +510,9 @@ check_input( armor_filter_context_t *afx, IOBUF a ) break; case fhdrERROR: + invalid_armor(); + break; + case fhdrEOF: rc = -1; break; @@ -458,12 +550,21 @@ fake_packet( armor_filter_context_t *afx, IOBUF a, size_t len = 0; size_t n, nn; fhdr_state_t state = afx->parse_state; + unsigned emplines = afx->empty; size = 100; /* FIXME: only used for testing (remove it) */ len = 2; /* reserve 2 bytes for the length header */ - size -= 2; /* and 2 for the term header */ + size -= 3; /* and 1 for empline handling and 2 for the term header */ while( !rc && len < size ) { + if( emplines ) { + while( emplines && len < size ) { + buf[len++] = '\r'; + buf[len++] = '\n'; + emplines--; + } + continue; + } if( afx->helpidx < afx->helplen ) { /* flush the last buffer */ n = afx->helplen; for(nn=afx->helpidx; len < size && nn < n ; nn++ ) @@ -478,14 +579,19 @@ fake_packet( armor_filter_context_t *afx, IOBUF a, /* read a new one */ n = DIM(afx->helpbuf); afx->helpidx = 0; - state = find_header( state, afx->helpbuf, &n, a, 0 ); + state = find_header( state, afx->helpbuf, &n, a, 0, &emplines ); switch( state) { case fhdrERROR: + invalid_armor(); + break; + case fhdrEOF: rc = -1; break; case fhdrCLEARSIG: + BUG(); + case fhdrREADClearsig: case fhdrREADClearsigNext: afx->helplen = n; @@ -509,6 +615,7 @@ fake_packet( armor_filter_context_t *afx, IOBUF a, } afx->parse_state = state; + afx->empty = emplines; *retn = len; return rc; } @@ -600,6 +707,8 @@ radix64_read( armor_filter_context_t *afx, IOBUF a, size_t *retn, log_error("CRC error; %06lx - %06lx\n", (ulong)afx->crc, (ulong)mycrc); else { + rc = 0; + #if 0 for(rc=0;!rc;) { rc = 0 /*check_trailer( &fhdr, c )*/; if( !rc ) { @@ -615,6 +724,7 @@ radix64_read( armor_filter_context_t *afx, IOBUF a, size_t *retn, log_error("premature eof (in Trailer)\n"); else log_error("error in trailer line\n"); + #endif } } } @@ -641,6 +751,12 @@ armor_filter( void *opaque, int control, int idx, idx2; size_t n=0; u32 crc; + static FILE *fp ; + + if( !fp ) { + fp = fopen("armor.out", "w"); + assert(fp); + } if( DBG_FILTER ) log_debug("armor-filter: control: %d\n", control ); @@ -655,8 +771,8 @@ armor_filter( void *opaque, int control, *ret_len = n; } else if( control == IOBUFCTRL_UNDERFLOW ) { - if( size < 20 ) - BUG(); /* supplied buffer maybe too short */ + if( size < 30 ) + BUG(); /* supplied buffer too short */ if( afx->inp_eof ) { *ret_len = 0; @@ -672,22 +788,36 @@ armor_filter( void *opaque, int control, if( afx->inp_bypass ) ; else if( afx->faked ) { - /* the buffer is at least 20 bytes long, so it - * is easy to construct a packet */ - buf[0] = 0xaf; /* old packet format, type 11, var length */ - buf[1] = 0; /* set the length header */ - buf[2] = 6; - buf[3] = 't'; /* canonical text */ - buf[4] = 0; /* namelength */ - buf[5] = buf[6] = buf[7] = buf[8] = 0; /* timestamp */ - n = 9; + /* the buffer is at least 30 bytes long, so it + * is easy to construct the packets */ + + /* first a onepass signature packet */ + buf[0] = 0x90; /* old packet forma, type 4, 1 length byte */ + buf[1] = 13; /* length */ + buf[2] = 3; /* version */ + buf[3] = 0x01; /* sigclass 0x01 (data in canonical text mode)*/ + buf[4] = 0; /* digest algo (don't know) */ + buf[5] = 0; /* public key algo (don't know) */ + memset(buf+6, 0, 8); /* don't know the keyid */ + buf[14] = 1; /* this is the last one */ + + /* followed by a plaintext packet */ + buf[15] = 0xaf; /* old packet format, type 11, var length */ + buf[16] = 0; /* set the length header */ + buf[17] = 6; + buf[18] = 't'; /* canonical text mode */ + buf[19] = 0; /* namelength */ + memset(buf+20, 0, 4); /* timestamp */ + n = 24; } else if( !rc ) rc = radix64_read( afx, a, &n, buf, size ); } else rc = radix64_read( afx, a, &n, buf, size ); - + if( n ) + if( fwrite(buf, n, 1, fp ) != 1 ) + BUG(); *ret_len = n; } else if( control == IOBUFCTRL_FLUSH ) { diff --git a/g10/filter.h b/g10/filter.h index b81a78db6..b49ce28b7 100644 --- a/g10/filter.h +++ b/g10/filter.h @@ -35,6 +35,7 @@ typedef struct { u32 crc; byte helpbuf[100]; int helpidx, helplen; + unsigned empty; /* empty line counter */ int faked; int parse_state; int inp_checked; /* set if inp has been checked */ @@ -64,11 +65,10 @@ typedef struct { typedef struct { - size_t linesize; - byte *line; - size_t linelen; - size_t pos; int eof; + size_t idx; + size_t len; + byte buf[256]; } text_filter_context_t; diff --git a/g10/g10.c b/g10/g10.c index bbc46285d..b64896912 100644 --- a/g10/g10.c +++ b/g10/g10.c @@ -43,7 +43,7 @@ enum cmd_values { aNull = 0, aSym, aStore, aEncr, aKeygen, aSign, aSignEncr, - aSignKey, aClearsig, aListPackets, aEditSig, + aSignKey, aClearsign, aListPackets, aEditSig, aKMode, aKModeC, aChangePass, aImport, aExport, aTest }; @@ -146,9 +146,9 @@ set_cmd( enum cmd_values *ret_cmd, enum cmd_values new_cmd ) cmd = aSignEncr; else if( cmd == aKMode && new_cmd == aSym ) cmd = aKModeC; - else if( ( cmd == aSign && new_cmd == aClearsig ) - || ( cmd == aClearsig && new_cmd == aSign ) ) - cmd = aClearsig; + else if( ( cmd == aSign && new_cmd == aClearsign ) + || ( cmd == aClearsign && new_cmd == aSign ) ) + cmd = aClearsign; else { log_error(_("conflicting commands\n")); g10_exit(2); @@ -210,6 +210,7 @@ main( int argc, char **argv ) { 536, "marginals-needed", 1, N_("(default is 3)")}, { 537, "export", 0, N_("export all or the given keys") }, { 538, "trustdb-name", 2, "\r" }, + { 539, "clearsign", 0, N_("make a clear text signature") }, {0} }; ARGPARSE_ARGS pargs; @@ -303,7 +304,7 @@ main( int argc, char **argv ) case 'e': set_cmd( &cmd, aEncr); break; case 'b': detached_sig = 1; /* fall trough */ case 's': set_cmd( &cmd, aSign ); break; - case 't': set_cmd( &cmd , aClearsig); break; + case 't': set_cmd( &cmd , aClearsign); break; case 'u': /* store the local users */ sl = m_alloc( sizeof *sl + strlen(pargs.r.ret_str)); strcpy(sl->d, pargs.r.ret_str); @@ -362,6 +363,7 @@ main( int argc, char **argv ) case 536: opt.marginals_needed = pargs.r.ret_int; break; case 537: set_cmd( &cmd, aExport); break; case 538: trustdb_name = pargs.r.ret_str; break; + case 539: set_cmd( &cmd, aClearsign); break; default : errors++; pargs.err = configfp? 1:2; break; } } @@ -454,24 +456,24 @@ main( int argc, char **argv ) switch( cmd ) { case aStore: /* only store the file */ if( argc > 1 ) - usage(1); + wrong_args(_("--store [filename]")); if( (rc = encode_store(fname)) ) - log_error("encode_store('%s'): %s\n", + log_error("%s: store failed: %s\n", fname_print, g10_errstr(rc) ); break; case aSym: /* encrypt the given file only with the symmetric cipher */ if( argc > 1 ) - usage(1); + wrong_args(_("--symmetric [filename]")); if( (rc = encode_symmetric(fname)) ) - log_error("encode_symmetric('%s'): %s\n", fname_print, g10_errstr(rc) ); + log_error("%s: symmetric encryption failed: %s\n", fname_print, g10_errstr(rc) ); break; case aEncr: /* encrypt the given file */ if( argc > 1 ) - usage(1); + wrong_args(_("--encrypt [filename]")); if( (rc = encode_crypt(fname,remusr)) ) - log_error("encode_crypt('%s'): %s\n", fname_print, g10_errstr(rc) ); + log_error("%s: encryption failed: %s\n", fname_print, g10_errstr(rc) ); break; case aSign: /* sign the given file */ @@ -482,20 +484,20 @@ main( int argc, char **argv ) } else { if( argc > 1 ) - usage(1); + wrong_args(_("--sign [filename]")); if( argc ) { sl = m_alloc_clear( sizeof *sl + strlen(fname)); strcpy(sl->d, fname); } } if( (rc = sign_file( sl, detached_sig, locusr, 0, NULL, NULL)) ) - log_error("sign_file: %s\n", g10_errstr(rc) ); + log_error("signing failed: %s\n", g10_errstr(rc) ); free_strlist(sl); break; case aSignEncr: /* sign and encrypt the given file */ if( argc > 1 ) - usage(1); + wrong_args(_("--sign --encrypt [filename]")); if( argc ) { sl = m_alloc_clear( sizeof *sl + strlen(fname)); strcpy(sl->d, fname); @@ -503,33 +505,40 @@ main( int argc, char **argv ) else sl = NULL; if( (rc = sign_file(sl, detached_sig, locusr, 1, remusr, NULL)) ) - log_error("sign_file('%s'): %s\n", fname_print, g10_errstr(rc) ); + log_error("%s: sign+encrypt failed: %s\n", fname_print, g10_errstr(rc) ); free_strlist(sl); break; + case aClearsign: /* make a clearsig */ + if( argc > 1 ) + wrong_args(_("--clearsign [filename]")); + if( (rc = clearsign_file(fname, locusr, NULL)) ) + log_error("%s: clearsign failed: %s\n", fname_print, g10_errstr(rc) ); + break; + case aSignKey: /* sign the key given as argument */ if( argc != 1 ) - usage(1); + wrong_args(_("--sign-key username")); /* note: fname is the user id! */ if( (rc = sign_key(fname, locusr)) ) - log_error("sign_key('%s'): %s\n", fname_print, g10_errstr(rc) ); + log_error("%s: sign key failed: %s\n", fname_print, g10_errstr(rc) ); break; case aEditSig: /* Edit a key signature */ if( argc != 1 ) - usage(1); + wrong_args(_("--edit-sig username")); /* note: fname is the user id! */ if( (rc = edit_keysigs(fname)) ) - log_error("edit_keysig('%s'): %s\n", fname_print, g10_errstr(rc) ); + log_error("%s: edit signature failed: %s\n", fname_print, g10_errstr(rc) ); break; case aChangePass: /* Chnage the passphrase */ if( argc > 1 ) /* no arg: use default, 1 arg use this one */ - usage(1); + wrong_args(_("--change-passphrase [username]")); /* note: fname is the user id! */ if( (rc = change_passphrase(fname)) ) - log_error("change_passphrase('%s'): %s\n", fname_print, + log_error("%s: change passphrase failed: %s\n", fname_print, g10_errstr(rc) ); break; @@ -562,18 +571,18 @@ main( int argc, char **argv ) iobuf_close(a); } else - usage(1); + wrong_args(_("-k[v][v][v][c] [keyring]")); break; case aKeygen: /* generate a key (interactive) */ if( argc ) - usage(1); + wrong_args(_("--gen-key")); generate_keypair(); break; case aImport: if( !argc ) - usage(1); + wrong_args(_("nyi")); for( ; argc; argc--, argv++ ) { rc = import_pubkeys( *argv ); if( rc ) @@ -594,7 +603,7 @@ main( int argc, char **argv ) opt.list_packets=1; default: if( argc > 1 ) - usage(1); + wrong_args(_("[filename]")); if( !(a = iobuf_open(fname)) ) log_fatal(_("can't open '%s'\n"), fname_print); if( !opt.no_armor ) { diff --git a/g10/main.h b/g10/main.h index 5548f8112..67091e7b4 100644 --- a/g10/main.h +++ b/g10/main.h @@ -50,6 +50,7 @@ int encrypt_filter( void *opaque, int control, /*-- sign.c --*/ int sign_file( STRLIST filenames, int detached, STRLIST locusr, int encrypt, STRLIST remusr, const char *outfile ); +int clearsign_file( const char *fname, STRLIST locusr, const char *outfile ); int sign_key( const char *username, STRLIST locusr ); int edit_keysigs( const char *username ); int change_passphrase( const char *username ); diff --git a/g10/mainproc.c b/g10/mainproc.c index 70a50c615..cae7ddda8 100644 --- a/g10/mainproc.c +++ b/g10/mainproc.c @@ -274,6 +274,7 @@ proc_plaintext( CTX c, PACKET *pkt ) * textmode filter (sigclass 0x01) */ c->mfx.md = md_open( DIGEST_ALGO_RMD160, 0); + md_enable( c->mfx.md, DIGEST_ALGO_MD5 ); rc = handle_plaintext( pt, &c->mfx ); if( rc ) log_error( "handle plaintext failed: %s\n", g10_errstr(rc)); @@ -325,6 +326,11 @@ do_check_sig( CTX c, KBNODE node ) if( sig->sig_class == 0x00 ) { md = md_copy( c->mfx.md ); } + else if( sig->sig_class == 0x01 ) { + /* how do we know that we have to hash the (already hashed) text + * in canonical mode ??? (calculating both modes???) */ + md = md_copy( c->mfx.md ); + } else if( (sig->sig_class&~3) == 0x10 ) { /* classes 0x10 .. 0x13 */ if( c->cert->pkt->pkttype == PKT_PUBLIC_CERT ) { KBNODE n1 = find_kbparent( c->cert, node ); diff --git a/g10/parse-packet.c b/g10/parse-packet.c index 886bc66e3..f364d3b7b 100644 --- a/g10/parse-packet.c +++ b/g10/parse-packet.c @@ -829,6 +829,8 @@ parse_plaintext( IOBUF inp, int pkttype, unsigned long pktlen, PACKET *pkt ) pktlen = 0; if( list_mode ) { + /* a value if 'c' is used by armor to indicate a faked packet + * it should be considered as 't' */ printf(":literal data packet:\n" "\tmode %c, created %lu, name=\"", mode >= ' ' && mode <'z'? mode : '?', diff --git a/g10/sign.c b/g10/sign.c index ee9b98254..d06b91990 100644 --- a/g10/sign.c +++ b/g10/sign.c @@ -301,6 +301,145 @@ sign_file( STRLIST filenames, int detached, STRLIST locusr, +static int +write_dash_escaped( IOBUF inp, IOBUF out, MD_HANDLE md ) +{ + int c; + int lastlf = 1; + + while( (c = iobuf_get(inp)) != -1 ) { + /* Note: We don't escape "From " because the MUA should cope with it */ + if( lastlf && c == '-' ) { + iobuf_put( out, c ); + iobuf_put( out, ' ' ); + } + + md_putc(md, c ); + iobuf_put( out, c ); + lastlf = c == '\n'; + } + return 0; /* fixme: add error handling */ +} + + +/**************** + * make a clear signature. note that opt.armor is not needed + */ +int +clearsign_file( const char *fname, STRLIST locusr, const char *outfile ) +{ + armor_filter_context_t afx; + compress_filter_context_t zfx; + text_filter_context_t tfx; + MD_HANDLE textmd; + IOBUF inp = NULL, out = NULL; + PACKET pkt; + int rc = 0; + SKC_LIST skc_list = NULL; + SKC_LIST skc_rover = NULL; + + memset( &afx, 0, sizeof afx); + memset( &zfx, 0, sizeof zfx); + memset( &tfx, 0, sizeof tfx); + init_packet( &pkt ); + + if( (rc=build_skc_list( locusr, &skc_list, 1 )) ) + goto leave; + + /* prepare iobufs */ + if( !(inp = iobuf_open(fname)) ) { + log_error("can't open %s: %s\n", fname? fname: "[stdin]", + strerror(errno) ); + rc = G10ERR_OPEN_FILE; + goto leave; + } + + if( outfile ) { + if( !(out = iobuf_create( outfile )) ) { + log_error("can't create %s: %s\n", outfile, strerror(errno) ); + rc = G10ERR_CREATE_FILE; + goto leave; + } + else if( opt.verbose ) + log_info("writing to '%s'\n", outfile ); + } + else if( !(out = open_outfile( fname, 1 )) ) { + rc = G10ERR_CREATE_FILE; + goto leave; + } + + iobuf_writestr(out, "-----BEGIN PGP SIGNED MESSAGE-----\n\n" ); + + textmd = md_open(DIGEST_ALGO_RMD160, 0); + iobuf_push_filter( inp, text_filter, &tfx ); + rc = write_dash_escaped( inp, out, textmd ); + if( rc ) + goto leave; + + iobuf_writestr(out, "\n\n" ); + afx.what = 2; + iobuf_push_filter( out, armor_filter, &afx ); + + /* loop over the secret certificates */ + for( skc_rover = skc_list; skc_rover; skc_rover = skc_rover->next ) { + PKT_secret_cert *skc; + PKT_signature *sig; + MD_HANDLE md; + + skc = skc_rover->skc; + + /* build the signature packet */ + sig = m_alloc_clear( sizeof *sig ); + sig->pubkey_algo = skc->pubkey_algo; + sig->timestamp = make_timestamp(); + sig->sig_class = 0x01; + + md = md_copy( textmd ); + md_putc( md, sig->sig_class ); + { u32 a = sig->timestamp; + md_putc( md, (a >> 24) & 0xff ); + md_putc( md, (a >> 16) & 0xff ); + md_putc( md, (a >> 8) & 0xff ); + md_putc( md, a & 0xff ); + } + md_final( md ); + + if( sig->pubkey_algo == PUBKEY_ALGO_ELGAMAL ) + g10_elg_sign( skc, sig, md, DIGEST_ALGO_RMD160 ); + else if( sig->pubkey_algo == PUBKEY_ALGO_RSA ) + g10_rsa_sign( skc, sig, md, DIGEST_ALGO_RMD160 ); + else + BUG(); + + md_close( md ); + + /* and write it */ + init_packet(&pkt); + pkt.pkttype = PKT_SIGNATURE; + pkt.pkt.signature = sig; + rc = build_packet( out, &pkt ); + free_packet( &pkt ); + if( rc ) { + log_error("build signature packet failed: %s\n", g10_errstr(rc) ); + goto leave; + } + } + + + leave: + if( rc ) + iobuf_cancel(out); + else + iobuf_close(out); + iobuf_close(inp); + md_close( textmd ); + release_skc_list( skc_list ); + return rc; +} + + + + static void show_fingerprint( PKT_public_cert *pkc ) { diff --git a/g10/status.c b/g10/status.c index baae1e96e..47cc7bd53 100644 --- a/g10/status.c +++ b/g10/status.c @@ -49,6 +49,7 @@ write_status( int no ) case STATUS_GOODSIG: s = "GOODSIG\n"; break; case STATUS_BADSIG : s = "BADSIG\n"; break; case STATUS_ERRSIG : s = "ERRSIG\n"; break; + case STATUS_BADARMOR : s = "BADARMOR\n"; break; default: s = "?\n"; break; } diff --git a/g10/status.h b/g10/status.h index 55eef32bb..f8e3f60f1 100644 --- a/g10/status.h +++ b/g10/status.h @@ -30,6 +30,7 @@ #define STATUS_ERRSIG 6 +#define STATUS_BADARMOR 7 diff --git a/g10/textfilter.c b/g10/textfilter.c index e57b429e8..4734fe4ca 100644 --- a/g10/textfilter.c +++ b/g10/textfilter.c @@ -32,6 +32,69 @@ #include "filter.h" + + +static int +read_line( byte *buf, size_t *r_buflen, IOBUF a ) +{ + int c; + int rc = 0; + byte *p; + size_t buflen; + int no_lf=0; + size_t n; + + buflen = *r_buflen; + assert(buflen >= 20 ); + buflen -= 3; /* leave some room for CR,LF and one extra */ + + for(c=0, n=0; n < buflen && (c=iobuf_get2(a)) != -1 && c != '\n'; ) + buf[n++] = c; + buf[n] = 0; + if( c == -1 ) + rc = -1; + else if( c != '\n' ) { + IOBUF b = iobuf_temp(); + while( (c=iobuf_get2(a)) != -1 && c != '\n' ) { + iobuf_put(b,c); + if( c != ' ' && c != '\t' && c != '\r' ) + break; + } + if( c == '\n' ) { /* okay we can skip the rest of the line */ + iobuf_close(b); + } + else { + iobuf_unget_and_close_temp(a,b); + no_lf = 1; + } + } + + if( !no_lf ) { + /* append CR,LF after removing trailing wspaces */ + for(p=buf+n-1; n; n--, p-- ) { + assert( *p != '\n' ); + if( *p != ' ' && *p != '\t' && *p != '\r' ) { + p[1] = '\r'; + p[2] = '\n'; + n += 2; + break; + } + } + if( !n ) { + buf[0] = '\r'; + buf[1] = '\n'; + n = 2; + } + } + + + *r_buflen = n; + return rc; +} + + + + /**************** * The filter is used to make canonical text: Lines are terminated by * CR, LF, trailing white spaces are removed. @@ -42,60 +105,31 @@ text_filter( void *opaque, int control, { size_t size = *ret_len; text_filter_context_t *tfx = opaque; - int i, c, rc=0; - byte *p; + int rc=0; + size_t len, n, nn; if( control == IOBUFCTRL_UNDERFLOW ) { - for(i=0; i < size; i++ ) { - if( !tfx->linelen && !tfx->eof ) { /* read a complete line */ - for(;;) { - if( (c = iobuf_get(a)) == -1 ) { - tfx->eof=1; - break; - } - if( c == '\n' ) - break; - if( tfx->linelen >= tfx->linesize ) { - tfx->linesize += 500; - tfx->line = m_realloc( tfx->line, tfx->linesize ); - } - tfx->line[tfx->linelen++] = c; - } - /* remove trailing white spaces */ - p = tfx->line + tfx->linelen - 1; - for( ; p >= tfx->line; p--, tfx->linelen-- ) { - if( *p != ' ' && *p == '\t' && *p != '\r' ) - break; - } - if( tfx->linelen+2 >= tfx->linesize ) { - tfx->linesize += 10; - tfx->line = m_realloc( tfx->line, tfx->linesize ); - } - tfx->line[tfx->linelen++] = '\r'; - tfx->line[tfx->linelen++] = '\n'; - tfx->pos=0; + assert( size > 30 ); + len = 0; + while( !rc && len < size ) { + if( tfx->idx < tfx->len ) { /* flush the last buffer */ + n = tfx->len; + for(nn=tfx->idx; len < size && nn < n ; nn++ ) + buf[len++] = tfx->buf[nn]; + tfx->idx = nn; + continue; } - if( tfx->pos < tfx->linelen ) - buf[i] = tfx->line[tfx->pos++]; - else if( tfx->eof ) - break; - else - tfx->linelen = 0; + if( tfx->eof ) { + rc = -1; + continue; + } + n = DIM(tfx->buf); + tfx->idx = 0; + if( read_line( tfx->buf, &n, a ) == -1 ) + tfx->eof = 1; + tfx->len = n; } - if( !i ) - rc = -1; - *ret_len = i; - } - else if( control == IOBUFCTRL_INIT ) { - tfx->linesize = 500; - tfx->line = m_alloc(tfx->linesize); - tfx->linelen = 0; - tfx->pos = 0; - tfx->eof = 0; - } - else if( control == IOBUFCTRL_FREE ) { - m_free( tfx->line ); - tfx->line = NULL; + *ret_len = len; } else if( control == IOBUFCTRL_DESC ) *(char**)buf = "text_filter"; diff --git a/include/iobuf.h b/include/iobuf.h index 81e23f267..e5ce81a3f 100644 --- a/include/iobuf.h +++ b/include/iobuf.h @@ -56,6 +56,12 @@ struct iobuf_struct { 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; }; int iobuf_debug_mode; @@ -88,6 +94,7 @@ int iobuf_writestr(IOBUF a, const char *buf ); 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 ); u32 iobuf_get_filelength( IOBUF a ); const char *iobuf_get_fname( IOBUF a ); @@ -106,6 +113,10 @@ int iobuf_in_block_mode( IOBUF a ); iobuf_readbyte((a)) : ( (a)->nbytes++, (a)->d.buf[(a)->d.start++] ) ) #define iobuf_get_noeof(a) (iobuf_get((a))&0xff) +/* use this if you have ungetted stuff */ +#define iobuf_get2(a) \ + ( ( (a)->unget.buf || (a)->nlimit || (a)->d.start >= (a)->d.len )? \ + iobuf_readbyte((a)) : ( (a)->nbytes++, (a)->d.buf[(a)->d.start++] ) ) /* write a byte to the iobuf and return true on write error * This macro does only write the low order byte diff --git a/util/iobuf.c b/util/iobuf.c index 72a14f503..d429d4c1e 100644 --- a/util/iobuf.c +++ b/util/iobuf.c @@ -700,6 +700,15 @@ 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++]; + m_free(a->unget.buf); + a->unget.buf = NULL; + } + if( a->nlimit && a->nbytes >= a->nlimit ) return -1; /* forced EOF */ @@ -770,6 +779,27 @@ iobuf_temp_to_buffer( IOBUF a, byte *buffer, size_t buflen ) return n; } +/**************** + * unget the contents of the temp io stream to A and close temp + * Could be optimized!! + */ +void +iobuf_unget_and_close_temp( IOBUF a, IOBUF temp ) +{ + if( a->unget.buf ) { + if( a->unget.start < a->unget.len ) + log_fatal("cannot do any more ungets on this buffer\n"); + /* not yet cleaned up; do it now */ + m_free(a->unget.buf); + a->unget.buf = NULL; + } + a->unget.size = temp->d.len; + a->unget.buf = m_alloc( a->unget.size ); + a->unget.len = temp->d.len; + memcpy( a->unget.buf, temp->d.buf, a->unget.len ); + iobuf_close(temp); +} + /**************** * Set a limit, how much bytes may be read from the input stream A.