diff --git a/README b/README index f166cb774..fa8596bea 100644 --- a/README +++ b/README @@ -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 or better post them to the mailing list . - Have fun - - Werner diff --git a/TODO b/TODO index 6948e4737..41065cf28 100644 --- a/TODO +++ b/TODO @@ -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. + diff --git a/cipher/random.c b/cipher/random.c index 83efc4b78..f865693b4 100644 --- a/cipher/random.c +++ b/cipher/random.c @@ -21,9 +21,12 @@ #include #include #include +#include #include +#include #include #include +#include #include #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 ); diff --git a/g10/Makefile.am b/g10/Makefile.am index 02639904a..901370ffb 100644 --- a/g10/Makefile.am +++ b/g10/Makefile.am @@ -30,6 +30,7 @@ g10_SOURCES = g10.c \ options.h \ openfile.c \ keyid.c \ + trustdb.c \ packet.h \ parse-packet.c \ passphrase.c \ diff --git a/g10/Makefile.in b/g10/Makefile.in index 40cf3aeed..531b1b8e9 100644 --- a/g10/Makefile.in +++ b/g10/Makefile.in @@ -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) diff --git a/g10/encode.c b/g10/encode.c index b73ee28d7..b0b148dd1 100644 --- a/g10/encode.c +++ b/g10/encode.c @@ -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; +} diff --git a/g10/filter.h b/g10/filter.h index 11c05e4a4..83ed3d153 100644 --- a/g10/filter.h +++ b/g10/filter.h @@ -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 ); diff --git a/g10/g10.c b/g10/g10.c index 1df800803..45fc6b66a 100644 --- a/g10/g10.c +++ b/g10/g10.c @@ -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 } - diff --git a/g10/kbnode.c b/g10/kbnode.c index 844bafe6c..19e0870df 100644 --- a/g10/kbnode.c +++ b/g10/kbnode.c @@ -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; } } + diff --git a/g10/keydb.h b/g10/keydb.h index f0d8b517b..cbca04cb9 100644 --- a/g10/keydb.h +++ b/g10/keydb.h @@ -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 --*/ diff --git a/g10/keyid.c b/g10/keyid.c index 307e28c61..5848459c8 100644 --- a/g10/keyid.c +++ b/g10/keyid.c @@ -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; } diff --git a/g10/main.h b/g10/main.h index 0d00dd19a..15caaab9a 100644 --- a/g10/main.h +++ b/g10/main.h @@ -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 ); diff --git a/g10/mainproc.c b/g10/mainproc.c index 338ce3d2d..b46cb1344 100644 --- a/g10/mainproc.c +++ b/g10/mainproc.c @@ -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", diff --git a/g10/options.h b/g10/options.h index c1aea3f95..31aa95522 100644 --- a/g10/options.h +++ b/g10/options.h @@ -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; diff --git a/g10/packet.h b/g10/packet.h index f7dbf9734..4a5a5a3cc 100644 --- a/g10/packet.h +++ b/g10/packet.h @@ -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 ); diff --git a/g10/passphrase.c b/g10/passphrase.c index a974ca9f5..df8ac7176 100644 --- a/g10/passphrase.c +++ b/g10/passphrase.c @@ -25,13 +25,27 @@ #include #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); diff --git a/g10/seckey-cert.c b/g10/seckey-cert.c index 85d93b4f9..c1ea596ac 100644 --- a/g10/seckey-cert.c +++ b/g10/seckey-cert.c @@ -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; } diff --git a/g10/sign.c b/g10/sign.c index 67e229f29..a8541391d 100644 --- a/g10/sign.c +++ b/g10/sign.c @@ -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 diff --git a/g10/trustdb.c b/g10/trustdb.c new file mode 100644 index 000000000..00774f572 --- /dev/null +++ b/g10/trustdb.c @@ -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 +#include +#include +#include +#include +#include + +#include "errors.h" +#include "iobuf.h" +#include "keydb.h" +#include "memory.h" +#include "util.h" + + + diff --git a/include/util.h b/include/util.h index 6a07fcb73..c32f74a70 100644 --- a/include/util.h +++ b/include/util.h @@ -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, ... ); diff --git a/util/iobuf.c b/util/iobuf.c index 58b0d7785..981fb2efb 100644 --- a/util/iobuf.c +++ b/util/iobuf.c @@ -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 */ } diff --git a/util/logger.c b/util/logger.c index 8993ba43d..c32332b48 100644 --- a/util/logger.c +++ b/util/logger.c @@ -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 diff --git a/util/ttyio.c b/util/ttyio.c index c8a4e8f68..31b80e68a 100644 --- a/util/ttyio.c +++ b/util/ttyio.c @@ -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); }