added more stuff

This commit is contained in:
Werner Koch 1998-01-02 20:40:10 +00:00
parent 4d2636eafe
commit b7bdef0834
23 changed files with 639 additions and 224 deletions

84
README
View File

@ -51,7 +51,6 @@
Key Generation
--------------
Create a key pair with this command:
g10 --gen-key
@ -59,7 +58,7 @@
good random numbers for prime number generation, it uses a /dev/random
which will emit only bytes if the kernel can gather enough entropy.
If you see no progress, you should start some other activities such
as a mouse moves or a "find /". Because we have no hardware device
as mouse moves or a "find /". Because we have no hardware device
to generate random we have to use this method.
Key generation shows progress by printing different characters to
@ -89,9 +88,25 @@
9) Find a generator for that prime.
Signatures
----------
To create a signature, use this:
You can sign a key with this command:
g10 --sign-key Donald
To sign the key of of "Donald" with your default userid
g10 --sign-key -u Karl -u Joe Donald
To sign the key of of "Donald" with the userids of "Karl" and "Joe".
All existing signatures are checked, if some are invalid, a menu is
offered to delete some of them, and the you are asked for every user
wether you want to sign this key.
You may remove a signature at any time by usiing the option "--edit-sig",
which also asks for the sigs to remove.
Sign
----
g10 -s file
@ -106,9 +121,9 @@
Creates a signature of file, but writes the output to the file "out".
Encryption
----------
To encrypt data use this:
Encrypt
-------
g10 -e -r heine file
@ -120,6 +135,51 @@
Ditto, but encrypts "hallo\n" and mails it as ascii armored message.
Sign and Encrypt
----------------
g10 -se -r heine file
This encrypts files with the public key of "heine" and writes it
to "file.g10" after signing it with the default user id.
g10 -se -r heine -u Suttner file
Ditto, but sign the file with the user id "Suttner"
Examine a data or key file
--------------------------
g10 --list-packets datafile
Use this to list the contents of a data file. If the file is encrypted
you are asked for the passphrase, so that G10 is able to look at the
inner structure of a encrypted packet.
Batch mode
----------
If you use the option "--batch", G10 runs in non-interactive mode and
never prompts for input data. This even does not allow to enter
passphrase; until we have a better solution (something like ssh-agent),
you can use the option "--passhrase-fd n", which works like PGPs
PGPPASSFD.
Batch mode also causes PGP to terminate as soon as a BAD signature is
detected.
Exit status
-----------
G10 returns with an exit status of 1 if in batch mode and a bad signature
has been detected or 2 or higher for all other errors. You should parse
stderr to get detailed informations about the errors.
Debug Flags
-----------
Use the option "--debug n" to output debug informations. This option
@ -146,18 +206,10 @@
I will run "indent" over the source when making a real distribution,
but for now I stick to my own formatting rules.
Compression does not work always; this is the reason that "-z 0"
is the default.
This will be cleaned up of course.
The primary FTP site is "ftp://ftp.guug.de/pub/gcrypt/"
The primary WWW page is "http://www.d.shuttle.de/isil/g10.html"
Please direct bug reports to <g10-bugs@isil.d.shuttle.de> or better
post them to the mailing list <g10@net.lut.ac.uk>.
Have fun
Werner

3
TODO
View File

@ -22,7 +22,6 @@
before we can check wether we have the pubkey or not. The one-pass
signature packets should be implemented to avoid this.
* compress does not work always!
* complete cipher/cast.c
* complete cipher/dsa.c
@ -34,3 +33,5 @@
of the userid.
[can be handles in get_pubkey_by_name()]
* armor has now some problems.

View File

@ -21,9 +21,12 @@
#include <config.h>
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <errno.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include "util.h"
#include "cipher.h"
@ -98,6 +101,7 @@ fill_buffer( byte *buffer, size_t length, int level )
static int fd_random = -1;
int fd;
int n;
int warn=0;
if( level == 2 ) {
if( fd_random == -1 )
@ -112,11 +116,34 @@ fill_buffer( byte *buffer, size_t length, int level )
do {
fd_set rfds;
struct timeval tv;
int rc;
FD_ZERO(&rfds);
FD_SET(fd, &rfds);
tv.tv_sec = 3;
tv.tv_usec = 0;
if( !(rc=select(fd+1, &rfds, NULL, NULL, &tv)) ) {
if( !warn )
tty_printf(
"\nNot enough random bytes available. Please do some other work to give
the OS a chance to collect more entropy! (Need %d more bytes)\n", length );
warn = 1;
continue;
}
else if( rc == -1 ) {
tty_printf("select() error: %s\n", strerror(errno));
continue;
}
assert( length < 200 );
do {
n = read(fd, buffer, length );
} while( n == -1 && errno == EINTR );
if( n == -1 )
log_fatal("read error on random device: %s\n", strerror(errno) );
assert( n <= length );
buffer += n;
length -= n;
} while( length );

View File

@ -30,6 +30,7 @@ g10_SOURCES = g10.c \
options.h \
openfile.c \
keyid.c \
trustdb.c \
packet.h \
parse-packet.c \
passphrase.c \

View File

@ -68,6 +68,7 @@ g10_SOURCES = g10.c \
options.h \
openfile.c \
keyid.c \
trustdb.c \
packet.h \
parse-packet.c \
passphrase.c \
@ -100,8 +101,8 @@ LINK = $(CC) $(LDFLAGS) -o $@
g10_OBJECTS = g10.o build-packet.o compress.o encode.o encr-data.o \
free-packet.o getkey.o pkclist.o skclist.o ringedit.o kbnode.o keygen.o \
mainproc.o armor.o mdfilter.o textfilter.o cipher.o elg.o rsa.o \
openfile.o keyid.o parse-packet.o passphrase.o plaintext.o pubkey-enc.o \
seckey-cert.o seskey.o sign.o comment.o sig-check.o
openfile.o keyid.o trustdb.o parse-packet.o passphrase.o plaintext.o \
pubkey-enc.o seckey-cert.o seskey.o sign.o comment.o sig-check.o
EXTRA_g10_SOURCES =
g10_LDADD = $(LDADD)
DIST_COMMON = Makefile.am Makefile.in
@ -130,7 +131,8 @@ $(srcdir)/.deps/plaintext.P $(srcdir)/.deps/pubkey-enc.P \
$(srcdir)/.deps/ringedit.P $(srcdir)/.deps/rsa.P \
$(srcdir)/.deps/seckey-cert.P $(srcdir)/.deps/seskey.P \
$(srcdir)/.deps/sig-check.P $(srcdir)/.deps/sign.P \
$(srcdir)/.deps/skclist.P $(srcdir)/.deps/textfilter.P
$(srcdir)/.deps/skclist.P $(srcdir)/.deps/textfilter.P \
$(srcdir)/.deps/trustdb.P
SOURCES = $(g10_SOURCES)
OBJECTS = $(g10_OBJECTS)

View File

@ -37,6 +37,7 @@
static int encode_simple( const char *filename, int mode );
static int write_pubkey_enc_from_list( PKC_LIST pkc_list, DEK *dek, IOBUF out );
@ -164,7 +165,7 @@ encode_crypt( const char *filename, STRLIST remusr )
cipher_filter_context_t cfx;
armor_filter_context_t afx;
compress_filter_context_t zfx;
PKC_LIST pkc_list, pkc_rover;
PKC_LIST pkc_list;
memset( &cfx, 0, sizeof cfx);
memset( &afx, 0, sizeof afx);
@ -203,31 +204,9 @@ encode_crypt( const char *filename, STRLIST remusr )
if( DBG_CIPHER )
log_hexdump("DEK is: ", cfx.dek->key, cfx.dek->keylen );
/* loop over all public key certificates */
for( pkc_rover=pkc_list; pkc_rover; pkc_rover = pkc_rover->next ) {
PKT_public_cert *pkc;
PKT_pubkey_enc *enc;
pkc = pkc_rover->pkc;
enc = m_alloc_clear( sizeof *enc );
enc->pubkey_algo = pkc->pubkey_algo;
if( enc->pubkey_algo == PUBKEY_ALGO_ELGAMAL )
g10_elg_encrypt( pkc, enc, cfx.dek );
else if( enc->pubkey_algo == PUBKEY_ALGO_RSA )
g10_rsa_encrypt( pkc, enc, cfx.dek );
else
log_bug(NULL);
/* and write it */
init_packet(&pkt);
pkt.pkttype = PKT_PUBKEY_ENC;
pkt.pkt.pubkey_enc = enc;
rc = build_packet( out, &pkt );
free_pubkey_enc(enc);
if( rc ) {
log_error("build pubkey_enc packet failed: %s\n", g10_errstr(rc) );
goto leave;
}
}
rc = write_pubkey_enc_from_list( pkc_list, cfx.dek, out );
if( rc )
goto leave;
/* setup the inner packet */
if( filename ) {
@ -276,7 +255,6 @@ encode_crypt( const char *filename, STRLIST remusr )
/****************
* Filter to do a complete public key encryption.
*/
#if 0
int
encrypt_filter( void *opaque, int control,
IOBUF a, byte *buf, size_t *ret_len)
@ -289,6 +267,24 @@ encrypt_filter( void *opaque, int control,
log_bug(NULL); /* not used */
}
else if( control == IOBUFCTRL_FLUSH ) { /* encrypt */
if( !efx->header_okay ) {
efx->cfx.dek = m_alloc_secure( sizeof *efx->cfx.dek );
efx->cfx.dek->algo = DEFAULT_CIPHER_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->pkc_list, 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 ) {
}
@ -297,5 +293,41 @@ encrypt_filter( void *opaque, int control,
}
return rc;
}
#endif
/****************
* Write pubkey-enc packets from the list of PKCs to OUT.
*/
static int
write_pubkey_enc_from_list( PKC_LIST pkc_list, DEK *dek, IOBUF out )
{
PACKET pkt;
PKT_public_cert *pkc;
PKT_pubkey_enc *enc;
int rc;
for( ; pkc_list; pkc_list = pkc_list->next ) {
pkc = pkc_list->pkc;
enc = m_alloc_clear( sizeof *enc );
enc->pubkey_algo = pkc->pubkey_algo;
if( enc->pubkey_algo == PUBKEY_ALGO_ELGAMAL )
g10_elg_encrypt( pkc, enc, dek );
else if( enc->pubkey_algo == PUBKEY_ALGO_RSA )
g10_rsa_encrypt( pkc, enc, dek );
else
log_bug(NULL);
/* and write it */
init_packet(&pkt);
pkt.pkttype = PKT_PUBKEY_ENC;
pkt.pkt.pubkey_enc = enc;
rc = build_packet( out, &pkt );
free_pubkey_enc(enc);
if( rc ) {
log_error("build pubkey_enc packet failed: %s\n", g10_errstr(rc) );
return rc;
}
}
return 0;
}

View File

@ -65,6 +65,7 @@ typedef struct {
} cipher_filter_context_t;
typedef struct {
size_t linesize;
byte *line;
@ -73,6 +74,9 @@ typedef struct {
int eof;
} text_filter_context_t;
/* encrypt_filter_context_t defined in main.h */
/*-- mdfilter.c --*/
int md_filter( void *opaque, int control, IOBUF a, byte *buf, size_t *ret_len);
void free_md_filter_context( md_filter_context_t *mfx );

View File

@ -121,7 +121,7 @@ main( int argc, char **argv )
{ 'e', "encrypt", 0, "encrypt data" },
{ 'd', "decrypt", 0, "decrypt data (default)" },
/*{ 'c', "check", 0, "check a signature (default)" }, */
{ 'l', "local-user",2, "use this user-id to sign or decrypt" },
{ 'u', "local-user",2, "use this user-id to sign or decrypt" },
{ 'r', "remote-user", 2, "use this user-id for encryption" },
{ 510, "debug" ,4|16, "set debugging flags" },
{ 511, "debug-all" ,0, "enable full debugging"},
@ -135,13 +135,17 @@ main( int argc, char **argv )
{ 518, "options" , 2, "read options from file" },
{ 519, "no-armor", 0, "\r"},
{ 520, "no-default-keyring", 0, "\r" },
{ 521, "list-packets",0,"list only the sequence of packets"},
{ 522, "no-greeting", 0, "\r" },
{ 523, "passphrase-fd",1, "\r" },
{ 524, "edit-sig" ,0, "edit a key signature" },
{0} };
ARGPARSE_ARGS pargs;
IOBUF a;
int rc;
enum { aNull, aSym, aStore, aEncr, aPrimegen, aKeygen, aSign, aSignEncr,
aTest, aPrintMDs, aSignKey, aClearsig
aTest, aPrintMDs, aSignKey, aClearsig, aListPackets, aEditSig,
} action = aNull;
int orig_argc;
char **orig_argv;
@ -158,9 +162,10 @@ main( int argc, char **argv )
int default_config =1;
int errors=0;
int default_keyring = 1;
int greeting = 1;
opt.compress = 0; /* defaults to no compression level */
opt.compress = -1; /* defaults to standard compress level */
/* check wether we have a config file on the commandline */
orig_argc = argc;
@ -221,7 +226,7 @@ main( int argc, char **argv )
/* fall trough */
case 's': action = action == aEncr? aSignEncr : aSign; break;
case 't': action = aClearsig; break;
case 'l': /* store the local users */
case 'u': /* store the local users */
sl = m_alloc( sizeof *sl + strlen(pargs.r.ret_str));
strcpy(sl->d, pargs.r.ret_str);
sl->next = locusr;
@ -233,7 +238,7 @@ main( int argc, char **argv )
sl->next = remusr;
remusr = sl;
break;
case 500: opt.batch = 1; break;
case 500: opt.batch = 1; greeting = 0; break;
case 501: opt.answer_yes = 1; break;
case 502: opt.answer_no = 1; break;
case 503: action = aKeygen; break;
@ -259,6 +264,10 @@ main( int argc, char **argv )
break;
case 519: opt.no_armor=1; opt.armor=0; break;
case 520: default_keyring = 0; break;
case 521: action = aListPackets; break;
case 522: greeting = 0; break;
case 523: set_passphrase_fd( pargs.r.ret_int ); break;
case 524: action = aEditSig; break;
default : errors++; pargs.err = configfp? 1:2; break;
}
}
@ -275,11 +284,11 @@ main( int argc, char **argv )
set_debug();
if( opt.verbose > 1 )
set_packet_list_mode(1);
if( opt.verbose && isatty(fileno(stdin)) ) {
if( greeting ) {
if( *(s=strusage(10)) )
fputs(s, stderr);
tty_printf("%s", s);
if( *(s=strusage(30)) )
fputs(s, stderr);
tty_printf("%s", s);
}
if( !sec_nrings || default_keyring ) { /* add default secret rings */
@ -347,6 +356,14 @@ main( int argc, char **argv )
log_error("sign_key('%s'): %s\n", fname_print, g10_errstr(rc) );
break;
case aEditSig: /* Edit a key signature */
if( argc != 1 )
usage(1);
/* note: fname is the user id! */
if( (rc = edit_keysigs(fname)) )
log_error("edit_keysig('%s'): %s\n", fname_print, g10_errstr(rc) );
break;
case aPrimegen:
if( argc == 1 ) {
@ -388,6 +405,8 @@ main( int argc, char **argv )
case aTest: do_test( argc? atoi(*argv): 0 ); break;
case aListPackets:
opt.list_packets=1;
default:
if( argc > 1 )
usage(1);
@ -398,6 +417,10 @@ main( int argc, char **argv )
memset( &afx, 0, sizeof afx);
iobuf_push_filter( a, armor_filter, &afx );
}
if( action == aListPackets ) {
set_packet_list_mode(1);
opt.list_packets=1;
}
proc_packets( a );
iobuf_close(a);
break;
@ -406,7 +429,7 @@ main( int argc, char **argv )
/* cleanup */
FREE_STRLIST(remusr);
FREE_STRLIST(locusr);
return 0;
return log_get_errorcount(0)? 2:0;
}
@ -509,33 +532,5 @@ do_test(int times)
m_check(NULL);
#endif
#if 0
char *array;
int i, j;
int n = 6;
int m = times;
if( m > n )
abort();
array = m_alloc_clear( n );
memset( array, 1, m );
for(i=0;; i++) {
printf("i=%3d: ", i );
for(j=0; j < n ; j++ )
if( array[j] )
putchar( 'X' );
else
putchar( '-' );
putchar('\n');
m_out_of_n( array, m, n );
for(j=0; j < n; j++ )
if( !array[j] )
break;
if( j == m )
break;
}
#endif
}

View File

@ -38,6 +38,7 @@ new_kbnode( PACKET *pkt )
n->pkt = pkt;
n->child = NULL;
n->flag = 0;
n->private_flag=0; /* kludge to delete a node */
return n;
}
@ -57,6 +58,16 @@ release_kbnode( KBNODE n )
}
/****************
* Delete NODE from ROOT, ROOT must exist!
* Note does only work with walk_kbtree!!
*/
void
delete_kbnode( KBNODE root, KBNODE node )
{
node->private_flag |= 1;
}
/****************
* Append NODE to ROOT, ROOT must exist!
*/
@ -115,27 +126,36 @@ find_kbparent( KBNODE root, KBNODE node )
*/
KBNODE
walk_kbtree( KBNODE root, KBNODE *context )
{
return walk_kbtree2( root, context, 0 );
}
KBNODE
walk_kbtree2( KBNODE root, KBNODE *context, int all )
{
KBNODE n;
if( !*context ) {
*context = root;
return root;
}
do {
if( !*context ) {
*context = root;
return root;
}
n = *context;
if( n->child ) {
n = n->child;
*context = n;
}
else if( n->next ) {
n = n->next;
*context = n;
}
else if( (n = find_kbparent( root, n )) ) {
n = n->next;
*context = n;
}
} while( !all && n && (n->private_flag & 1) );
n = *context;
if( n->child ) {
n = n->child;
*context = n;
}
else if( n->next ) {
n = n->next;
*context = n;
}
else if( (n = find_kbparent( root, n )) ) {
n = n->next;
*context = n;
}
return n;
}
@ -147,3 +167,4 @@ clear_kbnode_flags( KBNODE n )
n->flag = 0;
}
}

View File

@ -41,6 +41,7 @@ struct kbnode_struct {
KBNODE next; /* used to form a link list */
KBNODE child;
int flag;
int private_flag;
};
/****************
@ -81,6 +82,8 @@ void release_skc_list( SKC_LIST skc_list );
int build_skc_list( STRLIST locusr, SKC_LIST *ret_skc_list, int unlock );
/*-- passphrase.h --*/
void set_passphrase_fd( int fd );
int get_passphrase_fd(void);
DEK *get_passphrase_hash( u32 *keyid, char *text );
int make_dek_from_passphrase( DEK *dek, int mode );
@ -112,10 +115,12 @@ byte *fingerprint_from_pkc( PKT_public_cert *pkc, size_t *ret_len );
/*-- kbnode.c --*/
KBNODE new_kbnode( PACKET *pkt );
void release_kbnode( KBNODE n );
void delete_kbnode( KBNODE root, KBNODE node );
void add_kbnode( KBNODE root, KBNODE node );
void add_kbnode_as_child( KBNODE root, KBNODE node );
KBNODE find_kbparent( KBNODE root, KBNODE node );
KBNODE walk_kbtree( KBNODE root, KBNODE *context );
KBNODE walk_kbtree2( KBNODE root, KBNODE *context, int all );
void clear_kbnode_flags( KBNODE n );
/*-- ringedit.c --*/

View File

@ -155,7 +155,7 @@ datestr_from_pkc( PKT_public_cert *pkc )
time_t atime = pkc->timestamp;
tp = gmtime( &atime );
sprintf(buffer,"%04d-%02d-%02d", 1900+tp->tm_year, tp->tm_mon, tp->tm_mday );
sprintf(buffer,"%04d-%02d-%02d", 1900+tp->tm_year, tp->tm_mon+1, tp->tm_mday );
return buffer;
}
@ -167,7 +167,7 @@ datestr_from_skc( PKT_secret_cert *skc )
time_t atime = skc->timestamp;
tp = gmtime( &atime );
sprintf(buffer,"%04d-%02d-%02d", 1900+tp->tm_year, tp->tm_mon, tp->tm_mday );
sprintf(buffer,"%04d-%02d-%02d", 1900+tp->tm_year, tp->tm_mon+1, tp->tm_mday );
return buffer;
}
@ -179,7 +179,7 @@ datestr_from_sig( PKT_signature *sig )
time_t atime = sig->timestamp;
tp = gmtime( &atime );
sprintf(buffer,"%04d-%02d-%02d", 1900+tp->tm_year, tp->tm_mon, tp->tm_mday );
sprintf(buffer,"%04d-%02d-%02d", 1900+tp->tm_year, tp->tm_mon+1, tp->tm_mday );
return buffer;
}

View File

@ -28,15 +28,27 @@
#define DEFAULT_PUBKEY_ALGO PUBKEY_ALGO_ELGAMAL
#define DEFAULT_DIGEST_ALGO DIGEST_ALGO_RMD160
typedef struct {
int header_okay;
PKC_LIST pkc_list;
cipher_filter_context_t cfx;
} encrypt_filter_context_t;
/*-- encode.c --*/
int encode_symmetric( const char *filename );
int encode_store( const char *filename );
int encode_crypt( const char *filename, STRLIST remusr );
int encrypt_filter( void *opaque, int control,
IOBUF a, byte *buf, size_t *ret_len);
/*-- sign.c --*/
int sign_file( const char *filename, int detached, STRLIST locusr,
int encrypt, STRLIST remusr );
int sign_key( const char *username, STRLIST locusr );
int edit_keysigs( const char *username );
/*-- sig-check.c --*/
int check_key_signature( KBNODE root, KBNODE node );

View File

@ -46,7 +46,6 @@ typedef struct {
md_filter_context_t mfx;
DEK *dek;
int last_was_pubkey_enc;
int opt_list;
KBNODE cert; /* the current certificate */
int have_data;
IOBUF iobuf; /* used to get the filename etc. */
@ -198,7 +197,7 @@ proc_pubkey_enc( CTX c, PACKET *pkt )
c->last_was_pubkey_enc = 1;
enc = pkt->pkt.pubkey_enc;
printf("enc: encrypted by a pubkey with keyid %08lX\n", enc->keyid[1] );
/*printf("enc: encrypted by a pubkey with keyid %08lX\n", enc->keyid[1] );*/
if( enc->pubkey_algo == PUBKEY_ALGO_ELGAMAL
|| enc->pubkey_algo == PUBKEY_ALGO_RSA ) {
m_free(c->dek ); /* paranoid: delete a pending DEK */
@ -213,11 +212,12 @@ proc_pubkey_enc( CTX c, PACKET *pkt )
if( result == -1 )
;
else if( !result )
fputs( " DEK is good", stdout );
else if( !result ) {
if( opt.verbose > 1 )
log_info( "pubkey_enc packet: Good DEK\n" );
}
else
printf( " %s", g10_errstr(result));
putchar('\n');
log_error( "pubkey_enc packet: %s\n", g10_errstr(result));
free_packet(pkt);
}
@ -228,7 +228,7 @@ proc_encrypted( CTX c, PACKET *pkt )
{
int result = 0;
printf("dat: %sencrypted data\n", c->dek?"":"conventional ");
/*printf("dat: %sencrypted data\n", c->dek?"":"conventional ");*/
if( !c->dek && !c->last_was_pubkey_enc ) {
/* assume this is conventional encrypted data */
c->dek = m_alloc_secure( sizeof *c->dek );
@ -242,11 +242,13 @@ proc_encrypted( CTX c, PACKET *pkt )
m_free(c->dek); c->dek = NULL;
if( result == -1 )
;
else if( !result )
fputs( " encryption okay",stdout);
else
printf( " %s", g10_errstr(result));
putchar('\n');
else if( !result ) {
if( opt.verbose > 1 )
log_info("encryption okay\n");
}
else {
log_error("encryption failed: %s\n", g10_errstr(result));
}
free_packet(pkt);
c->last_was_pubkey_enc = 0;
}
@ -256,9 +258,10 @@ static void
proc_plaintext( CTX c, PACKET *pkt )
{
PKT_plaintext *pt = pkt->pkt.plaintext;
int result;
int rc;
printf("txt: plain text data name='%.*s'\n", pt->namelen, pt->name);
if( opt.verbose )
log_info("original file name='%.*s'\n", pt->namelen, pt->name);
free_md_filter_context( &c->mfx );
/* fixme: take the digest algo(s) to use from the
* onepass_sig packet (if we have these)
@ -266,12 +269,9 @@ proc_plaintext( CTX c, PACKET *pkt )
* textmode filter (sigclass 0x01)
*/
c->mfx.md = md_open(DIGEST_ALGO_RMD160, 0);
result = handle_plaintext( pt, &c->mfx );
if( !result )
fputs( " okay", stdout);
else
printf( " %s", g10_errstr(result));
putchar('\n');
rc = handle_plaintext( pt, &c->mfx );
if( rc )
log_error( "handle plaintext failed: %s\n", g10_errstr(rc));
free_packet(pkt);
c->last_was_pubkey_enc = 0;
}
@ -281,15 +281,12 @@ static void
proc_compressed( CTX c, PACKET *pkt )
{
PKT_compressed *zd = pkt->pkt.compressed;
int result;
int rc;
printf("zip: compressed data packet\n");
result = handle_compressed( zd );
if( !result )
fputs( " okay", stdout);
else
printf( " %s", g10_errstr(result));
putchar('\n');
/*printf("zip: compressed data packet\n");*/
rc = handle_compressed( zd );
if( rc )
log_error("uncompressing failed: %s\n", g10_errstr(rc));
free_packet(pkt);
c->last_was_pubkey_enc = 0;
}
@ -505,7 +502,6 @@ proc_packets( IOBUF a )
u32 keyid[2];
int newpkt;
c->opt_list = 1;
c->iobuf = a;
init_packet(pkt);
while( (rc=parse_packet(a, pkt)) != -1 ) {
@ -522,17 +518,27 @@ proc_packets( IOBUF a )
continue;
}
newpkt = -1;
switch( pkt->pkttype ) {
case PKT_PUBLIC_CERT: newpkt = add_public_cert( c, pkt ); break;
case PKT_SECRET_CERT: newpkt = add_secret_cert( c, pkt ); break;
case PKT_USER_ID: newpkt = add_user_id( c, pkt ); break;
case PKT_SIGNATURE: newpkt = add_signature( c, pkt ); break;
case PKT_PUBKEY_ENC: proc_pubkey_enc( c, pkt ); break;
case PKT_ENCRYPTED: proc_encrypted( c, pkt ); break;
case PKT_PLAINTEXT: proc_plaintext( c, pkt ); break;
case PKT_COMPRESSED: proc_compressed( c, pkt ); break;
case PKT_ONEPASS_SIG: newpkt = add_onepass_sig( c, pkt ); break;
default: newpkt = 0; break;
if( opt.list_packets ) {
switch( pkt->pkttype ) {
case PKT_PUBKEY_ENC: proc_pubkey_enc( c, pkt ); break;
case PKT_ENCRYPTED: proc_encrypted( c, pkt ); break;
case PKT_COMPRESSED: proc_compressed( c, pkt ); break;
default: newpkt = 0; break;
}
}
else {
switch( pkt->pkttype ) {
case PKT_PUBLIC_CERT: newpkt = add_public_cert( c, pkt ); break;
case PKT_SECRET_CERT: newpkt = add_secret_cert( c, pkt ); break;
case PKT_USER_ID: newpkt = add_user_id( c, pkt ); break;
case PKT_SIGNATURE: newpkt = add_signature( c, pkt ); break;
case PKT_PUBKEY_ENC: proc_pubkey_enc( c, pkt ); break;
case PKT_ENCRYPTED: proc_encrypted( c, pkt ); break;
case PKT_PLAINTEXT: proc_plaintext( c, pkt ); break;
case PKT_COMPRESSED: proc_compressed( c, pkt ); break;
case PKT_ONEPASS_SIG: newpkt = add_onepass_sig( c, pkt ); break;
default: newpkt = 0; break;
}
}
if( pkt->pkttype != PKT_SIGNATURE )
c->have_data = pkt->pkttype == PKT_PLAINTEXT;
@ -575,13 +581,16 @@ proc_tree( CTX c, KBNODE node )
KBNODE n1;
int rc;
if( opt.list_packets )
return;
if( node->pkt->pkttype == PKT_PUBLIC_CERT )
list_node( c, node );
else if( node->pkt->pkttype == PKT_SECRET_CERT )
list_node( c, node );
else if( node->pkt->pkttype == PKT_ONEPASS_SIG ) {
if( !node->child )
log_error("proc_tree: onepass_sig without followin data\n");
log_error("proc_tree: onepass_sig without data\n");
else if( node->child->pkt->pkttype != PKT_SIGNATURE )
log_error("proc_tree: onepass_sig not followed by signature\n");
else { /* check all signatures */
@ -611,6 +620,8 @@ proc_tree( CTX c, KBNODE node )
log_error("BAD signature from ");
print_keyid( stderr, sig->keyid );
putc('\n', stderr);
if( opt.batch )
exit(1);
}
else
log_error("Can't check signature made by %08lX: %s\n",

View File

@ -35,7 +35,7 @@ struct {
int fingerprint; /* list fingerprints */
int list_sigs; /* list signatures */
int no_armor;
int reserved5;
int list_packets; /* list-packets mode */
int reserved6;
int reserved7;
int reserved8;

View File

@ -210,6 +210,7 @@ struct packet_struct {
/*-- mainproc.c --*/
int proc_packets( IOBUF a );
int list_packets( IOBUF a );
/*-- parse-packet.c --*/
int set_packet_list_mode( int mode );

View File

@ -25,13 +25,27 @@
#include <assert.h>
#include "util.h"
#include "memory.h"
#include "options.h"
#include "ttyio.h"
#include "cipher.h"
#include "keydb.h"
static int pwfd = -1;
static int hash_passphrase( DEK *dek, char *pw );
void
set_passphrase_fd( int fd )
{
pwfd = fd;
}
int
get_passphrase_fd()
{
return pwfd;
}
/****************
* Get a passphrase for the secret key with KEYID, display TEXT
@ -41,35 +55,51 @@ static int hash_passphrase( DEK *dek, char *pw );
DEK *
get_passphrase_hash( u32 *keyid, char *text )
{
char *p=NULL, *pw;
char *pw;
DEK *dek;
if( keyid ) {
if( keyid && !opt.batch ) {
char *ustr;
tty_printf("\nNeed a pass phrase to unlock the secret key!\n");
tty_printf("KeyID: " );
tty_printf("Need a pass phrase to unlock the secret key for:\n");
tty_printf(" \"" );
ustr = get_user_id_string( keyid );
tty_print_string( ustr, strlen(ustr) );
m_free(ustr);
tty_printf("\n\n");
tty_printf("\"\n\n");
}
if( keyid && (p=getenv("G10PASSPHRASE")) ) {
pw = m_alloc_secure(strlen(p)+1);
strcpy(pw,p);
tty_printf("Taking it from $G10PASSPHRASE !\n", keyid[1] );
if( pwfd != -1 ) { /* read the passphrase from the given descriptor */
int i, len;
if( !opt.batch )
tty_printf("Reading from file descriptor %d ...", pwfd );
for( pw = NULL, i = len = 100; ; i++ ) {
if( i >= len-1 ) {
char *pw2 = pw;
len += 100;
pw = m_alloc_secure( len );
if( pw2 )
memcpy(pw, pw2, i );
i=0;
}
if( read( pwfd, pw+i, 1) != 1 || pw[i] == '\n' )
break;
}
pw[i] = 0;
if( !opt.batch )
tty_printf("\b\b\b \n" );
}
else
else if( opt.batch )
log_fatal("Can't query password in batchmode\n");
else {
pw = tty_get_hidden("Enter pass phrase: " );
tty_kill_prompt();
}
dek = m_alloc_secure( sizeof *dek );
dek->algo = CIPHER_ALGO_BLOWFISH;
if( hash_passphrase( dek, pw ) )
log_bug("get_passphrase_hash\n");
m_free(pw); /* is allocated in secure memory, so it will be burned */
if( !p ) {
tty_kill_prompt();
tty_printf("\n");
}
return dek;
}
@ -89,6 +119,7 @@ make_dek_from_passphrase( DEK *dek, int mode )
tty_kill_prompt();
if( mode == 2 ) {
pw2 = tty_get_hidden("Repeat pass phrase: " );
tty_kill_prompt();
if( strcmp(pw, pw2) ) {
m_free(pw2);
m_free(pw);

View File

@ -65,6 +65,7 @@ check_elg( PKT_secret_cert *cert )
unsigned nbytes;
u32 keyid[2];
ELG_secret_key skey;
char save_iv[8];
if( cert->d.elg.is_protected ) { /* remove the protection */
DEK *dek = NULL;
@ -80,6 +81,7 @@ check_elg( PKT_secret_cert *cert )
blowfish_setkey( blowfish_ctx, dek->key, dek->keylen );
m_free(dek); /* pw is in secure memory, so m_free() burns it */
blowfish_setiv( blowfish_ctx, NULL );
memcpy(save_iv, cert->d.elg.protect.blowfish.iv, 8 );
blowfish_decode_cfb( blowfish_ctx,
cert->d.elg.protect.blowfish.iv,
cert->d.elg.protect.blowfish.iv, 8 );
@ -94,6 +96,7 @@ check_elg( PKT_secret_cert *cert )
/* now let's see wether we have used the right passphrase */
if( csum != cert->d.elg.csum ) {
mpi_free(test_x);
memcpy( cert->d.elg.protect.blowfish.iv, save_iv, 8 );
return G10ERR_BAD_PASS;
}
@ -105,6 +108,7 @@ check_elg( PKT_secret_cert *cert )
memset( &skey, 0, sizeof skey );
if( !res ) {
mpi_free(test_x);
memcpy( cert->d.elg.protect.blowfish.iv, save_iv, 8 );
return G10ERR_BAD_PASS;
}
mpi_set(cert->d.elg.x, test_x);
@ -274,6 +278,8 @@ check_secret_key( PKT_secret_cert *cert )
#endif
else
rc = G10ERR_PUBKEY_ALGO;
if( get_passphrase_fd() != -1 )
break;
}
return rc;
}

View File

@ -79,6 +79,7 @@ sign_file( const char *filename, int detached, STRLIST locusr,
compress_filter_context_t zfx;
md_filter_context_t mfx;
text_filter_context_t tfx;
encrypt_filter_context_t efx;
IOBUF inp = NULL, out = NULL;
PACKET pkt;
PKT_plaintext *pt = NULL;
@ -92,6 +93,7 @@ sign_file( const char *filename, int detached, STRLIST locusr,
memset( &zfx, 0, sizeof zfx);
memset( &mfx, 0, sizeof mfx);
memset( &tfx, 0, sizeof tfx);
memset( &efx, 0, sizeof efx);
init_packet( &pkt );
if( (rc=build_skc_list( locusr, &skc_list, 1 )) )
@ -127,8 +129,9 @@ sign_file( const char *filename, int detached, STRLIST locusr,
iobuf_push_filter( out, compress_filter, &zfx );
if( encrypt ) {
/* prepare for encryption */
/* FIXME!!!!!!! */
efx.pkc_list = pkc_list;
/* fixme: set efx.cfx.datalen if known */
iobuf_push_filter( out, encrypt_filter, &efx );
}
/* loop over the secret certificates and build headers */
@ -348,7 +351,15 @@ sign_it_p( PKT_public_cert *pkc, PKT_user_id *uid )
}
static void
/****************
* Check the keysigs and set the flags to indicate errors.
* Usage of nodes flag bits:
* Bit 0 = bad signature
* 1 = no public key
* 2 = other error
* Returns true if error found.
*/
static int
check_all_keysigs( KBNODE keyblock )
{
KBNODE kbctx;
@ -384,6 +395,7 @@ check_all_keysigs( KBNODE keyblock )
m_free(p);
}
tty_printf("\n");
/* FIXME: update the trustdb */
}
}
if( inv_sigs )
@ -392,6 +404,76 @@ check_all_keysigs( KBNODE keyblock )
tty_printf("No public key for %d signatures\n", no_key );
if( oth_err )
tty_printf("%d signatures not checked due to errors\n", oth_err );
return inv_sigs || no_key || oth_err;
}
/****************
* Ask and remove invalid signatures are to be removed.
*/
static int
remove_keysigs( KBNODE keyblock, int all )
{
KBNODE kbctx;
KBNODE node;
char *answer;
int yes;
int count;
count = 0;
for( kbctx=NULL; (node=walk_kbtree( keyblock, &kbctx)) ; ) {
if( ((node->flag & 7) || all )
&& node->pkt->pkttype == PKT_SIGNATURE
&& (node->pkt->pkt.signature->sig_class&~3) == 0x10 ) {
PKT_signature *sig = node->pkt->pkt.signature;
int sigrc;
if( all ) {
/* fixme: skip self-sig */
}
tty_printf("\n \"%08lX %s ",
sig->keyid[1], datestr_from_sig(sig));
if( node->flag & 6 )
tty_printf("[User name not available] ");
else {
size_t n;
char *p = get_user_id( sig->keyid, &n );
tty_print_string( p, n );
m_free(p);
}
tty_printf("\"\n");
if( node->flag & 1 )
tty_printf("This is a BAD signature!\n");
else if( node->flag & 2 )
tty_printf("Public key not available.\n");
else if( node->flag & 4 )
tty_printf("The signature could not be checked!\n");
answer = tty_get("\nRemove this signature? ");
tty_kill_prompt();
if( answer_is_yes(answer) ) {
node->flag |= 128; /* use bit 7 to mark this node */
count++;
}
m_free(answer);
}
}
if( !count )
return 0; /* nothing to remove */
answer = tty_get("Do you really want to remove the selected signatures? ");
tty_kill_prompt();
yes = answer_is_yes(answer);
m_free(answer);
if( !yes )
return 0;
for( kbctx=NULL; (node=walk_kbtree2( keyblock, &kbctx, 1)) ; ) {
if( node->flag & 128)
delete_kbnode( keyblock, node );
}
return 1;
}
@ -414,6 +496,7 @@ sign_key( const char *username, STRLIST locusr )
PKT_public_cert *pkc;
int any;
u32 pkc_keyid[2];
char *answer;
memset( &mfx, 0, sizeof mfx);
@ -464,9 +547,16 @@ sign_key( const char *username, STRLIST locusr )
}
clear_kbnode_flags( keyblock );
check_all_keysigs( keyblock );
/* look wether we should ask to remove invalid keys */
/*+ FIXME: */
if( check_all_keysigs( keyblock ) ) {
if( !opt.batch ) {
/* ask wether we really should do anything */
answer = tty_get("To you want to remove some of the invalid sigs? ");
tty_kill_prompt();
if( answer_is_yes(answer) )
remove_keysigs( keyblock, 0 );
m_free(answer);
}
}
/* check wether we have already signed it */
for( skc_rover = skc_list; skc_rover; skc_rover = skc_rover->next ) {
@ -524,7 +614,7 @@ sign_key( const char *username, STRLIST locusr )
rc = update_keyblock( &kbpos, keyblock );
if( rc ) {
log_error("insert_keyblock failed: %s\n", g10_errstr(rc) );
log_error("update_keyblock failed: %s\n", g10_errstr(rc) );
goto leave;
}
@ -537,6 +627,75 @@ sign_key( const char *username, STRLIST locusr )
int
edit_keysigs( const char *username )
{
int rc = 0;
KBNODE keyblock = NULL;
KBNODE kbctx, node;
KBPOS kbpos;
PKT_public_cert *pkc;
int any;
u32 pkc_keyid[2];
char *answer;
/* search the userid */
rc = search_keyblock_byname( &kbpos, username );
if( rc ) {
log_error("user '%s' not found\n", username );
goto leave;
}
/* read the keyblock */
rc = read_keyblock( &kbpos, &keyblock );
if( rc ) {
log_error("error reading the certificate: %s\n", g10_errstr(rc) );
goto leave;
}
/* get the keyid from the keyblock */
for( kbctx=NULL; (node=walk_kbtree( keyblock, &kbctx)) ; ) {
if( node->pkt->pkttype == PKT_PUBLIC_CERT )
break;
}
if( !node ) {
log_error("Oops; public key not found anymore!\n");
rc = G10ERR_GENERAL;
goto leave;
}
pkc = node->pkt->pkt.public_cert;
keyid_from_pkc( pkc, pkc_keyid );
log_info("Checking signatures of this public key certificate:\n");
tty_printf("pub %4u%c/%08lX %s ",
nbits_from_pkc( pkc ),
pubkey_letter( pkc->pubkey_algo ),
pkc_keyid[1], datestr_from_pkc(pkc) );
{
size_t n;
char *p = get_user_id( pkc_keyid, &n );
tty_print_string( p, n > 40? 40 : n );
m_free(p);
tty_printf("\n");
}
clear_kbnode_flags( keyblock );
check_all_keysigs( keyblock );
if( remove_keysigs( keyblock, 1 ) ) {
rc = update_keyblock( &kbpos, keyblock );
if( rc ) {
log_error("update_keyblock failed: %s\n", g10_errstr(rc) );
goto leave;
}
}
leave:
release_kbnode( keyblock );
return rc;
}
/****************
* Create a signature packet for the given public key certificate
* and the user id and return it in ret_sig. User signature class SIGCLASS

35
g10/trustdb.c Normal file
View File

@ -0,0 +1,35 @@
/* trustdb.c
* Copyright (c) 1997 by Werner Koch (dd9jn)
*
* This file is part of G10.
*
* G10 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.
*
* G10 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
*/
#include <config.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <assert.h>
#include "errors.h"
#include "iobuf.h"
#include "keydb.h"
#include "memory.h"
#include "util.h"

View File

@ -56,7 +56,8 @@ typedef struct {
} ARGPARSE_OPTS;
/*-- logger.c --*/
void set_log_pid( int pid );
void log_set_pid( int pid );
int log_get_errorcount( int clear );
void printstr( int level, const char *fmt, ... );
void log_bug( const char *fmt, ... );
void log_fatal( const char *fmt, ... );

View File

@ -450,6 +450,7 @@ iobuf_push_filter( IOBUF a,
/* remove the filter stuff from the new stream */
a->filter = NULL;
a->filter_ov = NULL;
a->filter_eof = 0;
if( a->usage == 2 ) { /* allocate a fresh buffer for the original stream */
b->d.buf = m_alloc( a->d.size );
b->d.len = 0;
@ -539,7 +540,7 @@ iobuf_pop_filter( IOBUF a, int (*f)(void *opaque, int control,
m_free(b);
}
else if( !b->chain ) { /* remove the last iobuf from the chain */
log_bug("Ohh jeee, trying to a head filter\n");
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");
@ -833,13 +834,13 @@ iobuf_set_block_mode( IOBUF a, size_t n )
/****************
* checks wether the stream is in block mode
* Checks wether the stream is in block mode
* Note: This does not work if other filters are pushed on the stream.
*/
int
iobuf_in_block_mode( IOBUF a )
{
for(; a; a = a->chain )
if( a->filter == block_filter )
if( a && a->filter == block_filter )
return 1; /* yes */
return 0; /* no */
}

View File

@ -26,10 +26,10 @@
#include "util.h"
static char pidstring[15];
static int errorcount;
void
set_log_pid( int pid )
log_set_pid( int pid )
{
if( pid )
sprintf(pidstring,"[%u]", (unsigned)pid );
@ -37,6 +37,15 @@ set_log_pid( int pid )
*pidstring = 0;
}
int
log_get_errorcount( int clear)
{
int n = errorcount;
if( clear )
errorcount = 0;
return n;
}
/****************
* General interface for printing a line
@ -90,6 +99,7 @@ log_error( const char *fmt, ... )
va_start( arg_ptr, fmt ) ;
vfprintf(stderr,fmt,arg_ptr) ;
va_end(arg_ptr);
errorcount++;
}
void

View File

@ -30,41 +30,19 @@
#include "memory.h"
#include "ttyio.h"
static FILE *ttyfp = NULL;
static int last_prompt_len;
static FILE *
open_tty(struct termios *termsave )
{
struct termios term;
FILE *tty = fopen("/dev/tty", "r");
if( !tty )
log_fatal("cannot open /dev/tty: %s\n", strerror(errno) );
if( termsave ) { /* hide input */
if( tcgetattr(fileno(tty), termsave) )
log_fatal("tcgetattr() failed: %s\n", strerror(errno) );
term = *termsave;
term.c_lflag &= ~(ECHO | ECHOE | ECHOK | ECHONL);
if( tcsetattr( fileno(tty), TCSAFLUSH, &term ) )
log_fatal("tcsetattr() failed: %s\n", strerror(errno) );
}
return tty;
}
static void
close_tty( FILE *tty, struct termios *termsave )
init_ttyfp()
{
if( termsave ) {
if( tcsetattr(fileno(tty), TCSAFLUSH, termsave) )
log_error("tcsetattr() failed: %s\n", strerror(errno) );
putc('\n', stderr);
}
fclose(tty);
}
if( ttyfp )
return;
ttyfp = fopen("/dev/tty", "r+");
if( !ttyfp )
log_fatal("cannot open /dev/tty: %s\n", strerror(errno) );
}
void
@ -72,10 +50,13 @@ tty_printf( const char *fmt, ... )
{
va_list arg_ptr;
if( !ttyfp )
init_ttyfp();
va_start( arg_ptr, fmt ) ;
last_prompt_len += vfprintf(stderr,fmt,arg_ptr) ;
last_prompt_len += vfprintf(ttyfp,fmt,arg_ptr) ;
va_end(arg_ptr);
fflush(stderr);
fflush(ttyfp);
}
@ -85,18 +66,21 @@ tty_printf( const char *fmt, ... )
void
tty_print_string( byte *p, size_t n )
{
if( !ttyfp )
init_ttyfp();
for( ; n; n--, p++ )
if( iscntrl( *p ) ) {
putc('\\', stderr);
putc('\\', ttyfp);
if( *p == '\n' )
putc('n', stderr);
putc('n', ttyfp);
else if( !*p )
putc('0', stderr);
putc('0', ttyfp);
else
fprintf(stderr, "x%02x", *p );
fprintf(ttyfp, "x%02x", *p );
}
else
putc(*p, stderr);
putc(*p, ttyfp);
}
@ -107,17 +91,36 @@ static char *
do_get( const char *prompt, int hidden )
{
char *buf;
byte cbuf[1];
int c, n, i;
FILE *fp;
struct termios termsave;
if( !ttyfp )
init_ttyfp();
last_prompt_len = 0;
tty_printf( prompt );
buf = m_alloc(n=50);
i = 0;
fp = open_tty(hidden? &termsave: NULL);
while( (c=getc(fp)) != EOF && c != '\n' ) {
last_prompt_len++;
if( hidden ) {
struct termios term;
if( tcgetattr(fileno(ttyfp), &termsave) )
log_fatal("tcgetattr() failed: %s\n", strerror(errno) );
term = termsave;
term.c_lflag &= ~(ECHO | ECHOE | ECHOK | ECHONL);
if( tcsetattr( fileno(ttyfp), TCSAFLUSH, &term ) )
log_fatal("tcsetattr() failed: %s\n", strerror(errno) );
}
/* fixme: How can we avoid that the \n is echoed w/o disabling
* canonical mode - w/o this kill_prompt can't work */
while( read(fileno(ttyfp), cbuf, 1) == 1 && *cbuf != '\n' ) {
if( !hidden )
last_prompt_len++;
c = *cbuf;
if( c == '\t' )
c = ' ';
else if( iscntrl(c) )
@ -128,7 +131,11 @@ do_get( const char *prompt, int hidden )
}
buf[i++] = c;
}
close_tty(fp, hidden? &termsave: NULL);
if( hidden ) {
if( tcsetattr(fileno(ttyfp), TCSAFLUSH, &termsave) )
log_error("tcsetattr() failed: %s\n", strerror(errno) );
}
buf[i] = 0;
return buf;
}
@ -151,15 +158,16 @@ void
tty_kill_prompt()
{
int i;
#if 0
if( !ttyfp )
init_ttyfp();
if( !last_prompt_len )
return;
fputc('\r', ttyfp);
for(i=0; i < last_prompt_len; i ++ )
fputc('\b', stderr);
for(i=0; i < last_prompt_len; i ++ )
fputc(' ', stderr);
for(i=0; i < last_prompt_len; i ++ )
fputc('\b', stderr);
#endif
fputc(' ', ttyfp);
fputc('\r', ttyfp);
last_prompt_len = 0;
fflush(stderr);
fflush(ttyfp);
}