1
0
mirror of git://git.gnupg.org/gnupg.git synced 2025-04-17 15:44:34 +02:00

armor rewritten, but still buggy

This commit is contained in:
Werner Koch 1998-02-04 18:54:31 +00:00
parent 899b8378ec
commit 9886ad8098
16 changed files with 518 additions and 135 deletions

9
NEWS
View File

@ -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 * added option "--status-fd": see g10/OPTIONS
* We have secure memeory on systems which support mlock(). * We have secure memeory on systems which support mlock().
It is not complete yet, because we do not have signal handler It is not complete yet, because we do not have signal handler
which does a cleanup in very case. which does a cleanup in very case.

4
TODO
View File

@ -27,3 +27,7 @@
we have a self-signature -> put this stuff into a kind of directory we have a self-signature -> put this stuff into a kind of directory
record, as it does not belong to the pubkey record? 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.

View File

@ -20,6 +20,10 @@
#ifndef G10_CONFIG_H #ifndef G10_CONFIG_H
#define 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@ @TOP@

View File

@ -21,6 +21,10 @@
#ifndef G10_CONFIG_H #ifndef G10_CONFIG_H
#define 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. */ /* Define to empty if the keyword does not work. */

View File

@ -32,6 +32,8 @@
#include "filter.h" #include "filter.h"
#include "packet.h" #include "packet.h"
#include "options.h" #include "options.h"
#include "main.h"
#include "status.h"
@ -65,8 +67,12 @@ typedef enum {
fhdrEMPTYClearsig, fhdrEMPTYClearsig,
fhdrCHECKClearsig, fhdrCHECKClearsig,
fhdrCHECKClearsig2, fhdrCHECKClearsig2,
fhdrCHECKDashEscaped,
fhdrCHECKDashEscaped2,
fhdrCHECKDashEscaped3,
fhdrREADClearsigNext, fhdrREADClearsigNext,
fhdrENDClearsig, fhdrENDClearsig,
fhdrTESTSpaces,
fhdrTEXT, fhdrTEXT,
fhdrERROR, fhdrERROR,
fhdrERRORShow, fhdrERRORShow,
@ -92,8 +98,8 @@ static char *tail_strings[] = {
}; };
static fhdr_state_t find_header( fhdr_state_t state, static fhdr_state_t find_header( fhdr_state_t state, byte *buf,
byte *buf, size_t *r_buflen, IOBUF a, size_t n); size_t *r_buflen, IOBUF a, size_t n, unsigned *r_empty);
static void static void
@ -156,26 +162,36 @@ is_armored( byte *buf )
return 1; return 1;
} }
static void
invalid_armor(void)
{
write_status(STATUS_BADARMOR);
g10_exit(1); /* stop here */
}
/**************** /****************
* parse an ascii armor. * parse an ascii armor.
* Returns: the state, * Returns: the state,
* the remaining bytes in BUF are returned in RBUFLEN. * the remaining bytes in BUF are returned in RBUFLEN.
* r_empty return the # of empty lines before the buffer
*/ */
static fhdr_state_t 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; int c, i;
const char *s; const char *s;
char *p; byte *p;
size_t buflen; size_t buflen;
int cont; int cont;
int clearsig=0; int clearsig=0;
int hdr_line=0; int hdr_line=0;
unsigned empty = 0;
buflen = *r_buflen; buflen = *r_buflen;
assert(buflen >= 100 ); assert(buflen >= 100 );
buflen--; buflen -= 3; /* reserved room for CR,LF and one extra */
do { do {
switch( state ) { 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... /* read 28 bytes, which is the bare minimum for a BEGIN...
* and check wether this has a Armor. */ * and check wether this has a Armor. */
c = 0; 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; buf[n++] = c;
if( n < 28 || c == -1 ) if( n < 28 || c == -1 )
state = fhdrNOArmor; /* too short */ 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; n = 0;
case fhdrINITCont: /* read more stuff into buffer */ case fhdrINITCont: /* read more stuff into buffer */
c = 0; 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; buf[n++] = c;
state = c == '\n' ? fhdrCHECKBegin : state = c == '\n' ? fhdrCHECKBegin :
c == -1 ? fhdrEOF : fhdrINITSkip; c == -1 ? fhdrEOF : fhdrINITSkip;
break; break;
case fhdrINITSkip: case fhdrINITSkip:
while( (c=iobuf_get(a)) != -1 && c != '\n' ) while( (c=iobuf_get2(a)) != -1 && c != '\n' )
; ;
state = c == -1? fhdrEOF : fhdrINIT; state = c == -1? fhdrEOF : fhdrINIT;
break; break;
case fhdrSKIPHeader: case fhdrSKIPHeader:
while( (c=iobuf_get(a)) != -1 && c != '\n' ) while( (c=iobuf_get2(a)) != -1 && c != '\n' )
; ;
state = c == -1? fhdrEOF : fhdrWAITHeader; state = c == -1? fhdrEOF : fhdrWAITHeader;
break; break;
case fhdrWAITHeader: /* wait for Header lines */ case fhdrWAITHeader: /* wait for Header lines */
c = 0; 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++] = c;
buf[n] = 0; buf[n] = 0;
if( n < buflen || c == '\n' ) { 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; state = fhdrWAITHeader;
} }
else else
state = fhdrTEXT; state = fhdrCHECKDashEscaped3;
} }
else if( !n || (buf[0] == '\r' && !buf[1]) ) { /* empty line */ else if( !n || (buf[0] == '\r' && !buf[1]) ) { /* empty line */
if( clearsig ) if( clearsig )
state = fhdrWAITClearsig; state = fhdrWAITClearsig;
else { else {
/* this is not really correct: if we do not have /* 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 */ * to have an empty line */
n = 0; n = 0;
state = fhdrTEXT; state = fhdrTEXT;
} }
} }
else { else {
log_debug("invalid armor header: "); log_error("invalid armor header: ");
print_string( stderr, buf, n ); print_string( stderr, buf, n );
putc('\n', stderr); putc('\n', stderr);
state = fhdrERROR; 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) */ case fhdrWAITClearsig: /* skip all empty lines (for clearsig) */
c = 0; 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++] = c;
if( n < buflen || c == '\n' ) { if( n < buflen || c == '\n' ) {
buf[n] = 0; buf[n] = 0;
if( !n || (buf[0]=='\r' && !buf[1]) ) /* empty line */ if( !n || (buf[0]=='\r' && !buf[1]) ) /* empty line */
; ;
else 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; state = fhdrEOF;
}
break; break;
case fhdrENDClearsig: 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 fhdrEMPTYClearsig:
case fhdrREADClearsig: case fhdrREADClearsig:
/* we are at the start of a line: read a clearsig into the buffer /* 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; n = 0;
c = 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++] = c;
buf[n] = 0; buf[n] = 0;
if( c == -1 ) if( c == -1 )
state = fhdrEOF; state = fhdrEOF;
else if( !n || ( buf[0]=='\r' && !buf[1] ) ) { else if( !n || ( buf[0]=='\r' && !buf[1] ) ) {
state = fhdrEMPTYClearsig; state = fhdrEMPTYClearsig;
/* FIXME: handle it */ empty++;
} }
else if( c == '\n' ) else if( c == '\n' )
state = fhdrCHECKClearsig2; 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; state = fhdrCHECKClearsig;
break; break;
case fhdrCHECKClearsig: case fhdrCHECKDashEscaped3:
case fhdrCHECKClearsig2: if( !(n > 1 && buf[0] == '-' && buf[1] == ' ' ) ) {
/* check the clearsig line */ state = fhdrTEXT;
if( n > 15 && !memcmp(buf, "-----", 5 ) ) break;
state = fhdrENDClearsig; }
else if( buf[0] == '-' && buf[1] == ' ' ) { /* fall through */
/* dash escaped line */ case fhdrCHECKDashEscaped2:
case fhdrCHECKDashEscaped:
/* check dash escaped line */
if( buf[2] == '-' || ( n > 6 && !memcmp(buf+2, "From ", 5))) { if( buf[2] == '-' || ( n > 6 && !memcmp(buf+2, "From ", 5))) {
for(i=2; i < n; i++ ) for(i=2; i < n; i++ )
buf[i-2] = buf[i]; buf[i-2] = buf[i];
n -= 2; n -= 2;
buf[n] = 0; /* not really needed */ buf[n] = 0; /* not really needed */
state = state == fhdrCHECKClearsig2 ? state = state == fhdrCHECKDashEscaped3 ? fhdrTEXT :
fhdrREADClearsig : fhdrREADClearsigNext; state == fhdrCHECKDashEscaped2 ?
/* FIXME: add the lf to the buffer */ fhdrREADClearsig : fhdrTESTSpaces;
} }
else { else {
log_debug("invalid dash escaped line: "); log_error("invalid dash escaped line: ");
print_string( stderr, buf, n ); print_string( stderr, buf, n );
putc('\n', stderr); putc('\n', stderr);
state = fhdrERROR; 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] == ' ' )
state = fhdrCHECKDashEscaped;
else { else {
state = state == fhdrCHECKClearsig2 ? state = state == fhdrCHECKClearsig2 ?
fhdrREADClearsig : fhdrREADClearsigNext; fhdrREADClearsig : fhdrTESTSpaces;
/* FIXME: add the lf to the buffer */
} }
break; break;
case fhdrREADClearsigNext: case fhdrREADClearsigNext:
/* Read to the end of the line, do not care about checking /* Read to the end of the line, do not care about checking
* for dashed escaped text of headers */ * 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; 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: case fhdrERRORShow:
log_debug("invalid clear text header: "); log_error("invalid clear text header: ");
print_string( stderr, buf, n ); print_string( stderr, buf, n );
putc('\n', stderr); putc('\n', stderr);
state = fhdrERROR; 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 fhdrEMPTYClearsig:
case fhdrCHECKClearsig: case fhdrCHECKClearsig:
case fhdrCHECKClearsig2: case fhdrCHECKClearsig2:
case fhdrCHECKDashEscaped:
case fhdrCHECKDashEscaped2:
case fhdrCHECKDashEscaped3:
case fhdrTESTSpaces:
case fhdrERRORShow: case fhdrERRORShow:
cont = 1; cont = 1;
break; 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 ) if( clearsig && state == fhdrTEXT )
state = fhdrCLEARSIG; 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_buflen = n;
*r_empty = empty;
return state; return state;
} }
@ -407,12 +495,13 @@ check_input( armor_filter_context_t *afx, IOBUF a )
int rc = 0; int rc = 0;
size_t n; size_t n;
fhdr_state_t state = afx->parse_state; fhdr_state_t state = afx->parse_state;
unsigned emplines;
if( state != fhdrENDClearsig ) if( state != fhdrENDClearsig )
state = fhdrHASArmor; state = fhdrHASArmor;
n = DIM(afx->helpbuf); 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 ) { switch( state ) {
case fhdrNOArmor: case fhdrNOArmor:
afx->inp_checked = 1; afx->inp_checked = 1;
@ -421,6 +510,9 @@ check_input( armor_filter_context_t *afx, IOBUF a )
break; break;
case fhdrERROR: case fhdrERROR:
invalid_armor();
break;
case fhdrEOF: case fhdrEOF:
rc = -1; rc = -1;
break; break;
@ -458,12 +550,21 @@ fake_packet( armor_filter_context_t *afx, IOBUF a,
size_t len = 0; size_t len = 0;
size_t n, nn; size_t n, nn;
fhdr_state_t state = afx->parse_state; fhdr_state_t state = afx->parse_state;
unsigned emplines = afx->empty;
size = 100; /* FIXME: only used for testing (remove it) */ size = 100; /* FIXME: only used for testing (remove it) */
len = 2; /* reserve 2 bytes for the length header */ 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 ) { 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 */ if( afx->helpidx < afx->helplen ) { /* flush the last buffer */
n = afx->helplen; n = afx->helplen;
for(nn=afx->helpidx; len < size && nn < n ; nn++ ) 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 */ /* read a new one */
n = DIM(afx->helpbuf); n = DIM(afx->helpbuf);
afx->helpidx = 0; afx->helpidx = 0;
state = find_header( state, afx->helpbuf, &n, a, 0 ); state = find_header( state, afx->helpbuf, &n, a, 0, &emplines );
switch( state) { switch( state) {
case fhdrERROR: case fhdrERROR:
invalid_armor();
break;
case fhdrEOF: case fhdrEOF:
rc = -1; rc = -1;
break; break;
case fhdrCLEARSIG: case fhdrCLEARSIG:
BUG();
case fhdrREADClearsig: case fhdrREADClearsig:
case fhdrREADClearsigNext: case fhdrREADClearsigNext:
afx->helplen = n; afx->helplen = n;
@ -509,6 +615,7 @@ fake_packet( armor_filter_context_t *afx, IOBUF a,
} }
afx->parse_state = state; afx->parse_state = state;
afx->empty = emplines;
*retn = len; *retn = len;
return rc; 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", log_error("CRC error; %06lx - %06lx\n",
(ulong)afx->crc, (ulong)mycrc); (ulong)afx->crc, (ulong)mycrc);
else { else {
rc = 0;
#if 0
for(rc=0;!rc;) { for(rc=0;!rc;) {
rc = 0 /*check_trailer( &fhdr, c )*/; rc = 0 /*check_trailer( &fhdr, c )*/;
if( !rc ) { 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"); log_error("premature eof (in Trailer)\n");
else else
log_error("error in trailer line\n"); log_error("error in trailer line\n");
#endif
} }
} }
} }
@ -641,6 +751,12 @@ armor_filter( void *opaque, int control,
int idx, idx2; int idx, idx2;
size_t n=0; size_t n=0;
u32 crc; u32 crc;
static FILE *fp ;
if( !fp ) {
fp = fopen("armor.out", "w");
assert(fp);
}
if( DBG_FILTER ) if( DBG_FILTER )
log_debug("armor-filter: control: %d\n", control ); log_debug("armor-filter: control: %d\n", control );
@ -655,8 +771,8 @@ armor_filter( void *opaque, int control,
*ret_len = n; *ret_len = n;
} }
else if( control == IOBUFCTRL_UNDERFLOW ) { else if( control == IOBUFCTRL_UNDERFLOW ) {
if( size < 20 ) if( size < 30 )
BUG(); /* supplied buffer maybe too short */ BUG(); /* supplied buffer too short */
if( afx->inp_eof ) { if( afx->inp_eof ) {
*ret_len = 0; *ret_len = 0;
@ -672,22 +788,36 @@ armor_filter( void *opaque, int control,
if( afx->inp_bypass ) if( afx->inp_bypass )
; ;
else if( afx->faked ) { else if( afx->faked ) {
/* the buffer is at least 20 bytes long, so it /* the buffer is at least 30 bytes long, so it
* is easy to construct a packet */ * is easy to construct the packets */
buf[0] = 0xaf; /* old packet format, type 11, var length */
buf[1] = 0; /* set the length header */ /* first a onepass signature packet */
buf[2] = 6; buf[0] = 0x90; /* old packet forma, type 4, 1 length byte */
buf[3] = 't'; /* canonical text */ buf[1] = 13; /* length */
buf[4] = 0; /* namelength */ buf[2] = 3; /* version */
buf[5] = buf[6] = buf[7] = buf[8] = 0; /* timestamp */ buf[3] = 0x01; /* sigclass 0x01 (data in canonical text mode)*/
n = 9; 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 ) else if( !rc )
rc = radix64_read( afx, a, &n, buf, size ); rc = radix64_read( afx, a, &n, buf, size );
} }
else else
rc = radix64_read( afx, a, &n, buf, size ); rc = radix64_read( afx, a, &n, buf, size );
if( n )
if( fwrite(buf, n, 1, fp ) != 1 )
BUG();
*ret_len = n; *ret_len = n;
} }
else if( control == IOBUFCTRL_FLUSH ) { else if( control == IOBUFCTRL_FLUSH ) {

View File

@ -35,6 +35,7 @@ typedef struct {
u32 crc; u32 crc;
byte helpbuf[100]; byte helpbuf[100];
int helpidx, helplen; int helpidx, helplen;
unsigned empty; /* empty line counter */
int faked; int faked;
int parse_state; int parse_state;
int inp_checked; /* set if inp has been checked */ int inp_checked; /* set if inp has been checked */
@ -64,11 +65,10 @@ typedef struct {
typedef struct { typedef struct {
size_t linesize;
byte *line;
size_t linelen;
size_t pos;
int eof; int eof;
size_t idx;
size_t len;
byte buf[256];
} text_filter_context_t; } text_filter_context_t;

View File

@ -43,7 +43,7 @@
enum cmd_values { aNull = 0, enum cmd_values { aNull = 0,
aSym, aStore, aEncr, aKeygen, aSign, aSignEncr, aSym, aStore, aEncr, aKeygen, aSign, aSignEncr,
aSignKey, aClearsig, aListPackets, aEditSig, aSignKey, aClearsign, aListPackets, aEditSig,
aKMode, aKModeC, aChangePass, aImport, aKMode, aKModeC, aChangePass, aImport,
aExport, aExport,
aTest }; aTest };
@ -146,9 +146,9 @@ set_cmd( enum cmd_values *ret_cmd, enum cmd_values new_cmd )
cmd = aSignEncr; cmd = aSignEncr;
else if( cmd == aKMode && new_cmd == aSym ) else if( cmd == aKMode && new_cmd == aSym )
cmd = aKModeC; cmd = aKModeC;
else if( ( cmd == aSign && new_cmd == aClearsig ) else if( ( cmd == aSign && new_cmd == aClearsign )
|| ( cmd == aClearsig && new_cmd == aSign ) ) || ( cmd == aClearsign && new_cmd == aSign ) )
cmd = aClearsig; cmd = aClearsign;
else { else {
log_error(_("conflicting commands\n")); log_error(_("conflicting commands\n"));
g10_exit(2); g10_exit(2);
@ -210,6 +210,7 @@ main( int argc, char **argv )
{ 536, "marginals-needed", 1, N_("(default is 3)")}, { 536, "marginals-needed", 1, N_("(default is 3)")},
{ 537, "export", 0, N_("export all or the given keys") }, { 537, "export", 0, N_("export all or the given keys") },
{ 538, "trustdb-name", 2, "\r" }, { 538, "trustdb-name", 2, "\r" },
{ 539, "clearsign", 0, N_("make a clear text signature") },
{0} }; {0} };
ARGPARSE_ARGS pargs; ARGPARSE_ARGS pargs;
@ -303,7 +304,7 @@ main( int argc, char **argv )
case 'e': set_cmd( &cmd, aEncr); break; case 'e': set_cmd( &cmd, aEncr); break;
case 'b': detached_sig = 1; /* fall trough */ case 'b': detached_sig = 1; /* fall trough */
case 's': set_cmd( &cmd, aSign ); break; 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 */ case 'u': /* store the local users */
sl = m_alloc( sizeof *sl + strlen(pargs.r.ret_str)); sl = m_alloc( sizeof *sl + strlen(pargs.r.ret_str));
strcpy(sl->d, 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 536: opt.marginals_needed = pargs.r.ret_int; break;
case 537: set_cmd( &cmd, aExport); break; case 537: set_cmd( &cmd, aExport); break;
case 538: trustdb_name = pargs.r.ret_str; 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; default : errors++; pargs.err = configfp? 1:2; break;
} }
} }
@ -454,24 +456,24 @@ main( int argc, char **argv )
switch( cmd ) { switch( cmd ) {
case aStore: /* only store the file */ case aStore: /* only store the file */
if( argc > 1 ) if( argc > 1 )
usage(1); wrong_args(_("--store [filename]"));
if( (rc = encode_store(fname)) ) if( (rc = encode_store(fname)) )
log_error("encode_store('%s'): %s\n", log_error("%s: store failed: %s\n",
fname_print, g10_errstr(rc) ); fname_print, g10_errstr(rc) );
break; break;
case aSym: /* encrypt the given file only with the symmetric cipher */ case aSym: /* encrypt the given file only with the symmetric cipher */
if( argc > 1 ) if( argc > 1 )
usage(1); wrong_args(_("--symmetric [filename]"));
if( (rc = encode_symmetric(fname)) ) 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; break;
case aEncr: /* encrypt the given file */ case aEncr: /* encrypt the given file */
if( argc > 1 ) if( argc > 1 )
usage(1); wrong_args(_("--encrypt [filename]"));
if( (rc = encode_crypt(fname,remusr)) ) 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; break;
case aSign: /* sign the given file */ case aSign: /* sign the given file */
@ -482,20 +484,20 @@ main( int argc, char **argv )
} }
else { else {
if( argc > 1 ) if( argc > 1 )
usage(1); wrong_args(_("--sign [filename]"));
if( argc ) { if( argc ) {
sl = m_alloc_clear( sizeof *sl + strlen(fname)); sl = m_alloc_clear( sizeof *sl + strlen(fname));
strcpy(sl->d, fname); strcpy(sl->d, fname);
} }
} }
if( (rc = sign_file( sl, detached_sig, locusr, 0, NULL, NULL)) ) 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); free_strlist(sl);
break; break;
case aSignEncr: /* sign and encrypt the given file */ case aSignEncr: /* sign and encrypt the given file */
if( argc > 1 ) if( argc > 1 )
usage(1); wrong_args(_("--sign --encrypt [filename]"));
if( argc ) { if( argc ) {
sl = m_alloc_clear( sizeof *sl + strlen(fname)); sl = m_alloc_clear( sizeof *sl + strlen(fname));
strcpy(sl->d, fname); strcpy(sl->d, fname);
@ -503,33 +505,40 @@ main( int argc, char **argv )
else else
sl = NULL; sl = NULL;
if( (rc = sign_file(sl, detached_sig, locusr, 1, remusr, 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); free_strlist(sl);
break; 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 */ case aSignKey: /* sign the key given as argument */
if( argc != 1 ) if( argc != 1 )
usage(1); wrong_args(_("--sign-key username"));
/* note: fname is the user id! */ /* note: fname is the user id! */
if( (rc = sign_key(fname, locusr)) ) 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; break;
case aEditSig: /* Edit a key signature */ case aEditSig: /* Edit a key signature */
if( argc != 1 ) if( argc != 1 )
usage(1); wrong_args(_("--edit-sig username"));
/* note: fname is the user id! */ /* note: fname is the user id! */
if( (rc = edit_keysigs(fname)) ) 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; break;
case aChangePass: /* Chnage the passphrase */ case aChangePass: /* Chnage the passphrase */
if( argc > 1 ) /* no arg: use default, 1 arg use this one */ 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! */ /* note: fname is the user id! */
if( (rc = change_passphrase(fname)) ) 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) ); g10_errstr(rc) );
break; break;
@ -562,18 +571,18 @@ main( int argc, char **argv )
iobuf_close(a); iobuf_close(a);
} }
else else
usage(1); wrong_args(_("-k[v][v][v][c] [keyring]"));
break; break;
case aKeygen: /* generate a key (interactive) */ case aKeygen: /* generate a key (interactive) */
if( argc ) if( argc )
usage(1); wrong_args(_("--gen-key"));
generate_keypair(); generate_keypair();
break; break;
case aImport: case aImport:
if( !argc ) if( !argc )
usage(1); wrong_args(_("nyi"));
for( ; argc; argc--, argv++ ) { for( ; argc; argc--, argv++ ) {
rc = import_pubkeys( *argv ); rc = import_pubkeys( *argv );
if( rc ) if( rc )
@ -594,7 +603,7 @@ main( int argc, char **argv )
opt.list_packets=1; opt.list_packets=1;
default: default:
if( argc > 1 ) if( argc > 1 )
usage(1); wrong_args(_("[filename]"));
if( !(a = iobuf_open(fname)) ) if( !(a = iobuf_open(fname)) )
log_fatal(_("can't open '%s'\n"), fname_print); log_fatal(_("can't open '%s'\n"), fname_print);
if( !opt.no_armor ) { if( !opt.no_armor ) {

View File

@ -50,6 +50,7 @@ int encrypt_filter( void *opaque, int control,
/*-- sign.c --*/ /*-- sign.c --*/
int sign_file( STRLIST filenames, int detached, STRLIST locusr, int sign_file( STRLIST filenames, int detached, STRLIST locusr,
int encrypt, STRLIST remusr, const char *outfile ); 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 sign_key( const char *username, STRLIST locusr );
int edit_keysigs( const char *username ); int edit_keysigs( const char *username );
int change_passphrase( const char *username ); int change_passphrase( const char *username );

View File

@ -274,6 +274,7 @@ proc_plaintext( CTX c, PACKET *pkt )
* textmode filter (sigclass 0x01) * textmode filter (sigclass 0x01)
*/ */
c->mfx.md = md_open( DIGEST_ALGO_RMD160, 0); c->mfx.md = md_open( DIGEST_ALGO_RMD160, 0);
md_enable( c->mfx.md, DIGEST_ALGO_MD5 );
rc = handle_plaintext( pt, &c->mfx ); rc = handle_plaintext( pt, &c->mfx );
if( rc ) if( rc )
log_error( "handle plaintext failed: %s\n", g10_errstr(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 ) { if( sig->sig_class == 0x00 ) {
md = md_copy( c->mfx.md ); 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 */ else if( (sig->sig_class&~3) == 0x10 ) { /* classes 0x10 .. 0x13 */
if( c->cert->pkt->pkttype == PKT_PUBLIC_CERT ) { if( c->cert->pkt->pkttype == PKT_PUBLIC_CERT ) {
KBNODE n1 = find_kbparent( c->cert, node ); KBNODE n1 = find_kbparent( c->cert, node );

View File

@ -829,6 +829,8 @@ parse_plaintext( IOBUF inp, int pkttype, unsigned long pktlen, PACKET *pkt )
pktlen = 0; pktlen = 0;
if( list_mode ) { 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" printf(":literal data packet:\n"
"\tmode %c, created %lu, name=\"", "\tmode %c, created %lu, name=\"",
mode >= ' ' && mode <'z'? mode : '?', mode >= ' ' && mode <'z'? mode : '?',

View File

@ -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 static void
show_fingerprint( PKT_public_cert *pkc ) show_fingerprint( PKT_public_cert *pkc )
{ {

View File

@ -49,6 +49,7 @@ write_status( int no )
case STATUS_GOODSIG: s = "GOODSIG\n"; break; case STATUS_GOODSIG: s = "GOODSIG\n"; break;
case STATUS_BADSIG : s = "BADSIG\n"; break; case STATUS_BADSIG : s = "BADSIG\n"; break;
case STATUS_ERRSIG : s = "ERRSIG\n"; break; case STATUS_ERRSIG : s = "ERRSIG\n"; break;
case STATUS_BADARMOR : s = "BADARMOR\n"; break;
default: s = "?\n"; break; default: s = "?\n"; break;
} }

View File

@ -30,6 +30,7 @@
#define STATUS_ERRSIG 6 #define STATUS_ERRSIG 6
#define STATUS_BADARMOR 7

View File

@ -32,6 +32,69 @@
#include "filter.h" #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 * The filter is used to make canonical text: Lines are terminated by
* CR, LF, trailing white spaces are removed. * CR, LF, trailing white spaces are removed.
@ -42,60 +105,31 @@ text_filter( void *opaque, int control,
{ {
size_t size = *ret_len; size_t size = *ret_len;
text_filter_context_t *tfx = opaque; text_filter_context_t *tfx = opaque;
int i, c, rc=0; int rc=0;
byte *p; size_t len, n, nn;
if( control == IOBUFCTRL_UNDERFLOW ) { if( control == IOBUFCTRL_UNDERFLOW ) {
for(i=0; i < size; i++ ) { assert( size > 30 );
if( !tfx->linelen && !tfx->eof ) { /* read a complete line */ len = 0;
for(;;) { while( !rc && len < size ) {
if( (c = iobuf_get(a)) == -1 ) { if( tfx->idx < tfx->len ) { /* flush the last buffer */
tfx->eof=1; n = tfx->len;
break; for(nn=tfx->idx; len < size && nn < n ; nn++ )
buf[len++] = tfx->buf[nn];
tfx->idx = nn;
continue;
} }
if( c == '\n' ) if( tfx->eof ) {
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;
}
if( tfx->pos < tfx->linelen )
buf[i] = tfx->line[tfx->pos++];
else if( tfx->eof )
break;
else
tfx->linelen = 0;
}
if( !i )
rc = -1; rc = -1;
*ret_len = i; continue;
} }
else if( control == IOBUFCTRL_INIT ) { n = DIM(tfx->buf);
tfx->linesize = 500; tfx->idx = 0;
tfx->line = m_alloc(tfx->linesize); if( read_line( tfx->buf, &n, a ) == -1 )
tfx->linelen = 0; tfx->eof = 1;
tfx->pos = 0; tfx->len = n;
tfx->eof = 0;
} }
else if( control == IOBUFCTRL_FREE ) { *ret_len = len;
m_free( tfx->line );
tfx->line = NULL;
} }
else if( control == IOBUFCTRL_DESC ) else if( control == IOBUFCTRL_DESC )
*(char**)buf = "text_filter"; *(char**)buf = "text_filter";

View File

@ -56,6 +56,12 @@ struct iobuf_struct {
const char *desc; const char *desc;
void *opaque; /* can be used to hold any information */ void *opaque; /* can be used to hold any information */
/* this value is copied to all instances */ /* 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; int iobuf_debug_mode;
@ -88,6 +94,7 @@ int iobuf_writestr(IOBUF a, const char *buf );
int iobuf_write_temp( IOBUF a, IOBUF temp ); int iobuf_write_temp( IOBUF a, IOBUF temp );
size_t iobuf_temp_to_buffer( IOBUF a, byte *buffer, size_t buflen ); 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 ); u32 iobuf_get_filelength( IOBUF a );
const char *iobuf_get_fname( 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++] ) ) iobuf_readbyte((a)) : ( (a)->nbytes++, (a)->d.buf[(a)->d.start++] ) )
#define iobuf_get_noeof(a) (iobuf_get((a))&0xff) #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 /* write a byte to the iobuf and return true on write error
* This macro does only write the low order byte * This macro does only write the low order byte

View File

@ -700,6 +700,15 @@ iobuf_readbyte(IOBUF a)
{ {
int c; 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 ) if( a->nlimit && a->nbytes >= a->nlimit )
return -1; /* forced EOF */ return -1; /* forced EOF */
@ -770,6 +779,27 @@ iobuf_temp_to_buffer( IOBUF a, byte *buffer, size_t buflen )
return n; 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. * Set a limit, how much bytes may be read from the input stream A.