*** empty log message ***
authorWerner Koch <wk@gnupg.org>
Tue, 13 Jan 1998 19:04:23 +0000 (19:04 +0000)
committerWerner Koch <wk@gnupg.org>
Tue, 13 Jan 1998 19:04:23 +0000 (19:04 +0000)
16 files changed:
cipher/blowfish.c
doc/DETAILS
g10/encode.c
g10/free-packet.c
g10/g10.c
g10/getkey.c
g10/keydb.h
g10/main.h
g10/options.h
g10/packet.h
g10/pkclist.c
g10/sign.c
g10/trustdb.c
g10/trustdb.h
include/util.h
util/strgutil.c

index 72d617a..5dbaf71 100644 (file)
@@ -26,6 +26,7 @@
  * key   "abcdefghijklmnopqrstuvwxyz";
  * plain  "BLOWFISH"
  * cipher 32 4E D0 FE F4 13 A2 03
+ *
  */
 
 #include <config.h>
@@ -385,6 +386,9 @@ selftest()
     BLOWFISH_context c;
     byte plain[] = "BLOWFISH";
     byte buffer[8];
+    byte plain3[] = { 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10 };
+    byte key3[] = { 0x41, 0x79, 0x6E, 0xA0, 0x52, 0x61, 0x6E, 0xE4 };
+    byte cipher3[] = { 0xE1, 0x13, 0xF4, 0x10, 0x2C, 0xFC, 0xCE, 0x43 };
 
     blowfish_setkey( &c, "abcdefghijklmnopqrstuvwxyz", 26 );
     encrypt_block( &c, buffer, plain );
@@ -393,6 +397,14 @@ selftest()
     decrypt_block( &c, buffer, buffer );
     if( memcmp( buffer, plain, 8 ) )
        log_bug("blowfish failed\n");
+
+    blowfish_setkey( &c, key3, 8 );
+    encrypt_block( &c, buffer, plain3 );
+    if( memcmp( buffer, cipher3, 8 ) )
+       log_error("wrong blowfish encryption (3)\n");
+    decrypt_block( &c, buffer, buffer );
+    if( memcmp( buffer, plain3, 8 ) )
+       log_bug("blowfish failed (3)\n");
 }
 
 
index a3a2773..81aff29 100644 (file)
@@ -36,7 +36,7 @@ Record type 1:
             against the pubring)
      1 u32  Local-Id-Counter.  Used to keep track of Local-IDs.
            32 bits are enough numbers for all practial purposes; if this
-           counter rolls over (due to deleted keyblock,an d new ones),
+           counter rolls over (due to deleted keyblock and new ones),
            the software should reassign new Local-Ids to the whole
            database (not expected to ever occur).
      1 byte marginals needed
@@ -49,6 +49,7 @@ Record type 1:
 Record type 2:
 --------------
     Informations about a public key certificate.
+    These are static values which are never changed without user interaction.
 
      1 byte value 2
      1 byte   reserved
@@ -57,7 +58,7 @@ Record type 2:
             and usefull if we have duplicate keyids
             It is not defined, how an implementaion selects such
             a Local-Id, but it may use the local-ID counter from
-            record type 1
+            record type 1, or simply use the offset of Record type 2
      8 bytes keyid (of the primary key)
      1 byte pubkey algorithm
      1 byte reserved
@@ -67,9 +68,9 @@ Record type 2:
               0 = undefined (not yet initialized)
               1 = unknown owner (could not initialize it)
               2 = do not trust this owner
-              3 = usually trust this owner
-              4 = always trust this owner
-              5 = ultimately trust this owner.  This can only be set if
+              4 = usually trust this owner
+              5 = always trust this owner
+              7 = ultimately trust this owner.  This can only be set if
                   we have control over the secret key too.
              Bit 3: set if key is revoked; do not use it.
              Bit 7-4: reserved
index 688cbfc..ff12507 100644 (file)
@@ -159,7 +159,7 @@ encode_crypt( const char *filename, STRLIST remusr )
 {
     IOBUF inp = NULL, out = NULL;
     PACKET pkt;
-    PKT_plaintext *pt;
+    PKT_plaintext *pt = NULL;
     int rc = 0;
     u32 filesize;
     cipher_filter_context_t cfx;
@@ -244,7 +244,8 @@ encode_crypt( const char *filename, STRLIST remusr )
        iobuf_cancel(out);
     else
        iobuf_close(out); /* fixme: check returncode */
-    pt->buf = NULL;
+    if( pt )
+       pt->buf = NULL;
     free_packet(&pkt);
     m_free(cfx.dek);
     release_pkc_list( pkc_list );
index fb5949a..e2efa5a 100644 (file)
@@ -57,18 +57,24 @@ free_seckey_enc( PKT_signature *enc )
 }
 
 void
-free_public_cert( PKT_public_cert *cert )
+release_public_cert_parts( PKT_public_cert *cert )
 {
     if( cert->pubkey_algo == PUBKEY_ALGO_ELGAMAL ) {
-       mpi_free( cert->d.elg.p );
-       mpi_free( cert->d.elg.g );
-       mpi_free( cert->d.elg.y );
+       mpi_free( cert->d.elg.p ); cert->d.elg.p = NULL;
+       mpi_free( cert->d.elg.g ); cert->d.elg.g = NULL;
+       mpi_free( cert->d.elg.y ); cert->d.elg.y = NULL;
     }
     else if( cert->pubkey_algo == PUBKEY_ALGO_RSA ) {
-       mpi_free( cert->d.rsa.rsa_n );
-       mpi_free( cert->d.rsa.rsa_e );
+       mpi_free( cert->d.rsa.rsa_n ); cert->d.rsa.rsa_n = NULL;
+       mpi_free( cert->d.rsa.rsa_e ); cert->d.rsa.rsa_e = NULL;
     }
-    md_close( cert->mfx.md );
+    md_close( cert->mfx.md ); cert->mfx.md = NULL;
+}
+
+void
+free_public_cert( PKT_public_cert *cert )
+{
+    release_public_cert_parts( cert );
     m_free(cert);
 }
 
@@ -92,22 +98,28 @@ copy_public_cert( PKT_public_cert *d, PKT_public_cert *s )
 }
 
 void
-free_secret_cert( PKT_secret_cert *cert )
+release_secret_cert_parts( PKT_secret_cert *cert )
 {
     if( cert->pubkey_algo == PUBKEY_ALGO_ELGAMAL ) {
-       mpi_free( cert->d.elg.p );
-       mpi_free( cert->d.elg.g );
-       mpi_free( cert->d.elg.y );
-       mpi_free( cert->d.elg.x );
+       mpi_free( cert->d.elg.p ); cert->d.elg.p = NULL;
+       mpi_free( cert->d.elg.g ); cert->d.elg.g = NULL;
+       mpi_free( cert->d.elg.y ); cert->d.elg.y = NULL;
+       mpi_free( cert->d.elg.x ); cert->d.elg.x = NULL;
     }
     else if( cert->pubkey_algo == PUBKEY_ALGO_RSA ) {
-       mpi_free( cert->d.rsa.rsa_n );
-       mpi_free( cert->d.rsa.rsa_e );
-       mpi_free( cert->d.rsa.rsa_d );
-       mpi_free( cert->d.rsa.rsa_p );
-       mpi_free( cert->d.rsa.rsa_q );
-       mpi_free( cert->d.rsa.rsa_u );
+       mpi_free( cert->d.rsa.rsa_n ); cert->d.rsa.rsa_n = NULL;
+       mpi_free( cert->d.rsa.rsa_e ); cert->d.rsa.rsa_e = NULL;
+       mpi_free( cert->d.rsa.rsa_d ); cert->d.rsa.rsa_d = NULL;
+       mpi_free( cert->d.rsa.rsa_p ); cert->d.rsa.rsa_p = NULL;
+       mpi_free( cert->d.rsa.rsa_q ); cert->d.rsa.rsa_q = NULL;
+       mpi_free( cert->d.rsa.rsa_u ); cert->d.rsa.rsa_u = NULL;
     }
+}
+
+void
+free_secret_cert( PKT_secret_cert *cert )
+{
+    release_secret_cert_parts( cert );
     m_free(cert);
 }
 
@@ -240,4 +252,34 @@ free_packet( PACKET *pkt )
     pkt->pkt.generic = NULL;
 }
 
+/****************
+ * Returns 0 if they match.
+ */
+int
+cmp_public_secret_cert( PKT_public_cert *pkc, PKT_secret_cert *skc )
+{
+    if( pkc->timestamp != skc->timestamp )
+       return -1;
+    if( pkc->valid_days != skc->valid_days )
+       return -1;
+    if( pkc->pubkey_algo != skc->pubkey_algo )
+       return -1;
+
+    if( pkc->pubkey_algo == PUBKEY_ALGO_ELGAMAL ) {
+       if( mpi_cmp( pkc->d.elg.p , skc->d.elg.p ) )
+           return -1;
+       if( mpi_cmp( pkc->d.elg.g , skc->d.elg.g ) )
+           return -1;
+       if( mpi_cmp( pkc->d.elg.y , skc->d.elg.y ) )
+           return -1;
+    }
+    else if( pkc->pubkey_algo == PUBKEY_ALGO_RSA ) {
+       if( mpi_cmp( pkc->d.rsa.rsa_n , skc->d.rsa.rsa_n ) )
+           return -1;
+       if( mpi_cmp( pkc->d.rsa.rsa_e , skc->d.rsa.rsa_e ) )
+           return -1;
+    }
+
+    return 0;
+}
 
index e5c7139..2002e69 100644 (file)
--- a/g10/g10.c
+++ b/g10/g10.c
@@ -424,17 +424,36 @@ main( int argc, char **argv )
        break;
 
       case aSign: /* sign the given file */
-       if( argc > 1 )
-           usage(1);
-       if( (rc = sign_file(fname, detached_sig, locusr, 0, NULL)) )
-           log_error("sign_file('%s'): %s\n", fname_print, g10_errstr(rc) );
+       sl = NULL;
+       if( detached_sig ) { /* sign all files */
+           for( ; argc; argc--, argv++ )
+               add_to_strlist( &sl, *argv );
+       }
+       else {
+           if( argc > 1 )
+               usage(1);
+           if( argc ) {
+               sl = m_alloc_clear( sizeof *sl + strlen(fname));
+               strcpy(sl->d, fname);
+           }
+       }
+       if( (rc = sign_file( sl, detached_sig, locusr, 0, NULL, NULL)) )
+           log_error("sign_file: %s\n", g10_errstr(rc) );
+       free_strlist(sl);
        break;
 
       case aSignEncr: /* sign and encrypt the given file */
        if( argc > 1 )
            usage(1);
-       if( (rc = sign_file(fname, detached_sig, locusr, 1, remusr)) )
+       if( argc ) {
+           sl = m_alloc_clear( sizeof *sl + strlen(fname));
+           strcpy(sl->d, fname);
+       }
+       else
+           sl = NULL;
+       if( (rc = sign_file(sl, detached_sig, locusr, 1, remusr, NULL)) )
            log_error("sign_file('%s'): %s\n", fname_print, g10_errstr(rc) );
+       free_strlist(sl);
        break;
 
 
index 3ec6a6b..b1cb64a 100644 (file)
@@ -51,6 +51,13 @@ typedef struct pkc_cache_entry {
     PKT_public_cert *pkc;
 } *pkc_cache_entry_t;
 
+typedef struct enum_seckey_context {
+    int eof;
+    STRLIST sl;
+    IOBUF iobuf;
+} enum_seckey_context_t;
+
+
 static STRLIST keyrings;
 static STRLIST secret_keyrings;
 
@@ -351,6 +358,9 @@ get_seckey_byname( PKT_secret_cert *skc, const char *name, int unprotect )
 }
 
 
+
+
+
 /****************
  * scan the keyring and look for either the keyid or the name.
  */
@@ -585,6 +595,69 @@ scan_secret_keyring( PKT_secret_cert *skc, u32 *keyid,
 
 
 /****************
+ * Enumerate all secret keys.  Caller must use these procedure:
+ *  1) create a void pointer and initialize it to NULL
+ *  2) pass this void pointer by reference to this function
+ *     and provide space for the secret key (pass a buffer for skc)
+ *  3) call this function as long as it does not return -1
+ *     to indicate EOF.
+ *  4) Always call this function a last time with SKC set to NULL,
+ *     so that can free it's context.
+ *
+ * Return
+ */
+int
+enum_secret_keys( void **context, PKT_secret_cert *skc )
+{
+    int rc=0;
+    PACKET pkt;
+    int save_mode;
+    enum_seckey_context_t *c = *context;
+
+    if( !c ) { /* make a new context */
+       c = m_alloc_clear( sizeof *c );
+       *context = c;
+       c->sl = secret_keyrings;
+    }
+
+    if( !skc ) { /* free the context */
+       m_free( c );
+       *context = NULL;
+       return 0;
+    }
+
+    if( c->eof )
+       return -1;
+
+    for( ; c->sl; c->sl = c->sl->next ) {
+       if( !c->iobuf ) {
+           if( !(c->iobuf = iobuf_open( c->sl->d ) ) ) {
+               log_error("enum_secret_keys: can't open '%s'\n", c->sl->d );
+               continue; /* try next file */
+           }
+       }
+
+       save_mode = set_packet_list_mode(0);
+       init_packet(&pkt);
+       while( (rc=parse_packet(c->iobuf, &pkt)) != -1 ) {
+           if( rc )
+               ; /* e.g. unknown packet */
+           else if( pkt.pkttype == PKT_SECRET_CERT ) {
+               copy_secret_cert( skc, pkt.pkt.secret_cert );
+               set_packet_list_mode(save_mode);
+               return 0; /* found */
+           }
+           free_packet(&pkt);
+       }
+       set_packet_list_mode(save_mode);
+       iobuf_close(c->iobuf); c->iobuf = NULL;
+    }
+    c->eof = 1;
+    return -1;
+}
+
+
+/****************
  * Return a string with a printable representation of the user_id.
  * this string must be freed by m_free.
  */
index 6770f86..8eb4947 100644 (file)
@@ -97,6 +97,7 @@ int get_pubkey( PKT_public_cert *pkc, u32 *keyid );
 int get_pubkey_byname( PKT_public_cert *pkc, const char *name );
 int get_seckey( PKT_secret_cert *skc, u32 *keyid );
 int get_seckey_byname( PKT_secret_cert *skc, const char *name, int unlock );
+int enum_secret_keys( void **context, PKT_secret_cert *skc );
 char*get_user_id_string( u32 *keyid );
 char*get_user_id( u32 *keyid, size_t *rn );
 
index a10ce4b..a1a13e4 100644 (file)
@@ -41,8 +41,8 @@ int encrypt_filter( void *opaque, int control,
 
 
 /*-- sign.c --*/
-int sign_file( const char *filename, int detached, STRLIST locusr,
-              int encrypt, STRLIST remusr );
+int sign_file( STRLIST filenames, int detached, STRLIST locusr,
+              int encrypt, STRLIST remusr, const char *outfile );
 int sign_key( const char *username, STRLIST locusr );
 int edit_keysigs( const char *username );
 int change_passphrase( const char *username );
index 0df1f41..3b72547 100644 (file)
@@ -58,11 +58,13 @@ struct {
 #define DBG_MEMORY_VALUE  32   /* debug memory allocation stuff */
 #define DBG_CACHE_VALUE   64   /* debug the cacheing */
 #define DBG_MEMSTAT_VALUE 128  /* show memory statistics */
+#define DBG_TRUST_VALUE   256  /* debug the trustdb */
 
 
 #define DBG_PACKET (opt.debug & DBG_PACKET_VALUE)
 #define DBG_FILTER (opt.debug & DBG_FILTER_VALUE)
 #define DBG_CACHE  (opt.debug & DBG_CACHE_VALUE)
+#define DBG_TRUST  (opt.debug & DBG_TRUST_VALUE)
 
 
 #endif /*G10_OPTIONS_H*/
index e82248a..6326c43 100644 (file)
@@ -98,6 +98,7 @@ typedef struct {
     u16     valid_days;     /* valid for this number of days */
     byte    pubkey_algo;    /* algorithm used for public key scheme */
     md_filter_context_t mfx;
+    u32     local_id;      /* internal use, valid if > 0 */
     union {
       struct {
        MPI p;              /* prime */
@@ -225,13 +226,16 @@ void hash_public_cert( MD_HANDLE md, PKT_public_cert *pkc );
 /*-- free-packet.c --*/
 void free_pubkey_enc( PKT_pubkey_enc *enc );
 void free_seckey_enc( PKT_signature *enc );
+void release_public_cert_parts( PKT_public_cert *cert );
 void free_public_cert( PKT_public_cert *cert );
+void release_secret_cert_parts( PKT_secret_cert *cert );
 void free_secret_cert( PKT_secret_cert *cert );
 void free_user_id( PKT_user_id *uid );
 void free_comment( PKT_comment *rem );
 void free_packet( PACKET *pkt );
 PKT_public_cert *copy_public_cert( PKT_public_cert *d, PKT_public_cert *s );
 PKT_secret_cert *copy_secret_cert( PKT_secret_cert *d, PKT_secret_cert *s );
+int cmp_public_secret_cert( PKT_public_cert *pkc, PKT_secret_cert *skc );
 
 
 /*-- sig-check.c --*/
index f753eb6..3fd2254 100644 (file)
 static int
 do_we_trust( PKT_public_cert *pkc, int trustlevel )
 {
+    int rc;
+
+    if( trustlevel & TRUST_NO_PUBKEY ) {
+       /* No pubkey in trustDB: Insert and check again */
+       rc = insert_trust_record( pkc );
+       if( rc ) {
+           log_error("failed to insert it into the trustdb: %s\n",
+                                                     g10_errstr(rc) );
+           return 0; /* no */
+       }
+       rc = check_pkc_trust( pkc, &trustlevel );
+       if( rc )
+           log_fatal("trust check after insert failed: %s\n",
+                                                     g10_errstr(rc) );
+       if( trustlevel & TRUST_NO_PUBKEY )
+           log_bug(NULL);
+    }
+
+
     /* Eventuell fragen falls der trustlevel nicht ausreichend ist */
 
 
@@ -90,6 +109,7 @@ build_pkc_list( STRLIST remusr, PKC_LIST *ret_pkc_list )
                                                      remusr->d, g10_errstr(rc) );
                }
                else if( do_we_trust( pkc, trustlevel ) ) {
+                   /* note: do_we_trust may have changed the trustlevel */
                    PKC_LIST r;
 
                    r = m_alloc( sizeof *r );
index 04ae72a..9e8275a 100644 (file)
@@ -64,17 +64,22 @@ complete_sig( PKT_signature *sig, PKT_secret_cert *skc, MD_HANDLE md )
 
 
 /****************
- * Sign the file with name FILENAME.  If DETACHED has the value true,
- * make a detached signature.  If FILENAME is NULL read from stdin
+ * Sign the files whose names are in FILENAME.
+ * If DETACHED has the value true,
+ * make a detached signature.  If FILENAMES->d is NULL read from stdin
  * and ignore the detached mode.  Sign the file with all secret keys
  * which can be taken from LOCUSR, if this is NULL, use the default one
  * If ENCRYPT is true, use REMUSER (or ask if it is NULL) to encrypt the
  * signed data for these users.
+ * If OUTFILE is not NULL; this file is used for output and the function
+ * does not ask for overwrite permission; output is then always
+ * uncompressed, non-armored and in binary mode.
  */
 int
-sign_file( const char *filename, int detached, STRLIST locusr,
-          int encrypt, STRLIST remusr )
+sign_file( STRLIST filenames, int detached, STRLIST locusr,
+          int encrypt, STRLIST remusr, const char *outfile )
 {
+    const char *fname;
     armor_filter_context_t afx;
     compress_filter_context_t zfx;
     md_filter_context_t mfx;
@@ -88,6 +93,7 @@ sign_file( const char *filename, int detached, STRLIST locusr,
     PKC_LIST pkc_list = NULL;
     SKC_LIST skc_list = NULL;
     SKC_LIST skc_rover = NULL;
+    int multifile = 0;
 
     memset( &afx, 0, sizeof afx);
     memset( &zfx, 0, sizeof zfx);
@@ -96,6 +102,16 @@ sign_file( const char *filename, int detached, STRLIST locusr,
     memset( &efx, 0, sizeof efx);
     init_packet( &pkt );
 
+    if( filenames ) {
+       fname = filenames->d;
+       multifile = !!filenames->next;
+    }
+    else
+       fname = NULL;
+
+    if( fname && filenames->next && (!detached || encrypt) )
+       log_bug("multiple files can only be detached signed");
+
     if( (rc=build_skc_list( locusr, &skc_list, 1 )) )
        goto leave;
     if( encrypt ) {
@@ -104,28 +120,40 @@ sign_file( const char *filename, int detached, STRLIST locusr,
     }
 
     /* prepare iobufs */
-    if( !(inp = iobuf_open(filename)) ) {
-       log_error("can't open %s: %s\n", filename? filename: "[stdin]",
+    if( multifile )  /* have list of filenames */
+       inp = NULL; /* we do it later */
+    else 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( !(out = open_outfile( filename, opt.armor? 1: detached? 2:0 )) ) {
+    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, opt.armor? 1: detached? 2:0 )) ) {
        rc = G10ERR_CREATE_FILE;
        goto leave;
     }
 
     /* prepare to calculate the MD over the input */
-    if( opt.textmode && opt.armor )
+    if( opt.textmode && opt.armor && !outfile )
        iobuf_push_filter( inp, text_filter, &tfx );
     mfx.md = md_open(DIGEST_ALGO_RMD160, 0);
-    iobuf_push_filter( inp, md_filter, &mfx );
+    if( !multifile )
+       iobuf_push_filter( inp, md_filter, &mfx );
 
-    if( opt.armor )
+    if( opt.armor && !outfile  )
        iobuf_push_filter( out, armor_filter, &afx );
     write_comment( out, "#Created by G10 pre-release " VERSION );
-    if( opt.compress )
+    if( opt.compress && !outfile )
        iobuf_push_filter( out, compress_filter, &zfx );
 
     if( encrypt ) {
@@ -141,7 +169,7 @@ sign_file( const char *filename, int detached, STRLIST locusr,
 
        skc = skc_rover->skc;
        ops = m_alloc_clear( sizeof *ops );
-       ops->sig_class = opt.textmode? 0x01 : 0x00;
+       ops->sig_class = opt.textmode && !outfile ? 0x01 : 0x00;
        ops->digest_algo = DIGEST_ALGO_RMD160;
        ops->pubkey_algo = skc->pubkey_algo;
        keyid_from_skc( skc, ops->keyid );
@@ -161,17 +189,40 @@ sign_file( const char *filename, int detached, STRLIST locusr,
 
     /* setup the inner packet */
     if( detached ) {
-       /* read, so that the filter can calculate the digest */
-       while( iobuf_get(inp) != -1 )
-           ;
+       if( multifile ) {
+           STRLIST sl = filenames;
+
+           if( opt.verbose )
+               log_info("signing:" );
+           for(; sl; sl = sl->next ) {
+               if( !(inp = iobuf_open(sl->d)) ) {
+                   log_error("can't open %s: %s\n", sl->d, strerror(errno) );
+                   rc = G10ERR_OPEN_FILE;
+                   goto leave;
+               }
+               if( opt.verbose )
+                   fprintf(stderr, " '%s'", sl->d );
+               iobuf_push_filter( inp, md_filter, &mfx );
+               while( iobuf_get(inp) != -1 )
+                   ;
+               iobuf_close(inp); inp = NULL;
+           }
+           if( opt.verbose )
+               putc( '\n', stderr );
+       }
+       else {
+           /* read, so that the filter can calculate the digest */
+           while( iobuf_get(inp) != -1 )
+               ;
+       }
     }
     else {
-       if( filename ) {
-           pt = m_alloc( sizeof *pt + strlen(filename) - 1 );
-           pt->namelen = strlen(filename);
-           memcpy(pt->name, filename, pt->namelen );
+       if( fname ) {
+           pt = m_alloc( sizeof *pt + strlen(fname) - 1 );
+           pt->namelen = strlen(fname);
+           memcpy(pt->name, fname, pt->namelen );
            if( !(filesize = iobuf_get_filelength(inp)) )
-               log_info("warning: '%s' is an empty file\n", filename );
+               log_info("warning: '%s' is an empty file\n", fname );
        }
        else { /* no filename */
            pt = m_alloc( sizeof *pt - 1 );
@@ -179,7 +230,7 @@ sign_file( const char *filename, int detached, STRLIST locusr,
            filesize = 0; /* stdin */
        }
        pt->timestamp = make_timestamp();
-       pt->mode = opt.textmode? 't':'b';
+       pt->mode = opt.textmode && !outfile ? 't':'b';
        pt->len = filesize;
        pt->buf = inp;
        pkt.pkttype = PKT_PLAINTEXT;
@@ -203,7 +254,7 @@ sign_file( const char *filename, int detached, STRLIST locusr,
        sig = m_alloc_clear( sizeof *sig );
        sig->pubkey_algo = skc->pubkey_algo;
        sig->timestamp = make_timestamp();
-       sig->sig_class = opt.textmode? 0x01 : 0x00;
+       sig->sig_class = opt.textmode && !outfile? 0x01 : 0x00;
 
        md = md_copy( mfx.md );
        md_putc( md, sig->sig_class );
index 46c9500..1dfaaa5 100644 (file)
@@ -25,6 +25,7 @@
 #include <unistd.h>
 #include <errno.h>
 #include <assert.h>
+#include <fcntl.h>
 
 #include "errors.h"
 #include "iobuf.h"
@@ -41,7 +42,6 @@ struct trust_record {
     byte rectype;
     byte reserved;
     union {
-       byte raw[TRUST_RECORD_LEN-2];
        struct {            /* version record: */
            byte magic[2];
            byte version;   /* should be 1 */
@@ -76,10 +76,32 @@ struct trust_record {
        } cache;
     } r;
 };
+typedef struct trust_record TRUSTREC;
 
 
+static void create_db( const char *fname );
+static void open_db(void);
+static int  read_record( u32 recnum, TRUSTREC *rec );
+static u32 new_local_id(void);
 
 static char *db_name;
+static int  db_fd = -1;
+static int no_io_dbg = 0;
+
+#define buftou32( p )  ((*(byte*)(p) << 24) | (*((byte*)(p)+1)<< 16) | \
+                      (*((byte*)(p)+2) << 8) | (*((byte*)(p)+3)))
+#define buftou16( p )  ((*((byte*)(p)) << 8) | (*((byte*)(p)+1)))
+#define u32tobuf( p, a ) do {                          \
+                           ((byte*)p)[0] = a >> 24;    \
+                           ((byte*)p)[1] = a >> 16;    \
+                           ((byte*)p)[2] = a >>  8;    \
+                           ((byte*)p)[3] = a      ;    \
+                       } while(0)
+#define u16tobuf( p, a ) do {                          \
+                           ((byte*)p)[0] = a >>  8;    \
+                           ((byte*)p)[1] = a      ;    \
+                       } while(0)
+
 
 /**************************************************
  ************** read and write helpers ************
@@ -147,7 +169,7 @@ create_db( const char *fname )
     fwrite_32( fp, make_timestamp() ); /* created */
     fwrite_32( fp, 0 ); /* not yet modified */
     fwrite_32( fp, 0 ); /* not yet validated*/
-    fwrite_32( fp, 0 ); /* local-id-counter */
+    fwrite_32( fp, 0 ); /* local-id-counter (not used) */
     fwrite_8( fp, 3 ); /* marginals needed */
     fwrite_8( fp, 1 ); /* completes needed */
     fwrite_8( fp, 4 ); /* max_cet_depth */
@@ -155,14 +177,255 @@ create_db( const char *fname )
     fclose(fp);
 }
 
+static void
+open_db()
+{
+    TRUSTREC rec;
+    assert( db_fd == -1 );
+
+    db_fd = open( db_name, O_RDWR );
+    if( db_fd == -1 )
+       log_fatal("can't open %s: %s\n", db_name, strerror(errno) );
+    if( read_record( 0, &rec ) )
+       log_fatal("TrustDB %s is invalid\n", db_name );
+    /* fixme: check ->locked and other stuff */
+}
 
 
+/****************
+ * read the record with number recnum
+ * returns: -1 on error, 0 on success
+ */
+static int
+read_record( u32 recnum, TRUSTREC *rec )
+{
+    byte buf[TRUST_RECORD_LEN], *p;
+    int rc = 0;
+    int n;
+
+    if( db_fd == -1 )
+       open_db();
+    if( DBG_TRUST && !no_io_dbg )
+       log_debug("trustdb: read_record(%lu)\n", (ulong)recnum);
+    if( lseek( db_fd, recnum * TRUST_RECORD_LEN, SEEK_SET ) == -1 ) {
+       log_error("trustdb: lseek failed: %s\n", strerror(errno) );
+       return G10ERR_READ_FILE;
+    }
+    n = read( db_fd, buf, TRUST_RECORD_LEN);
+    if( !n ) {
+       if( DBG_TRUST )
+           log_debug("trustdb: no record at %lu\n", (ulong)recnum );
+       return -1; /* eof */
+    }
+    else if( n != TRUST_RECORD_LEN ) {
+       log_error("trustdb: read failed (n=%d): %s\n", n, strerror(errno) );
+       return G10ERR_READ_FILE;
+    }
+    p = buf;
+    rec->rectype = *p++;
+    rec->reserved = *p++;
+    switch( rec->rectype ) {
+      case 0:  /* unused record */
+       break;
+      case 1: /* version record */
+       rec->r.version.magic[0] = *p++;
+       rec->r.version.magic[1] = *p++;
+       rec->r.version.version  = *p++;
+       memcpy( rec->r.version.reserved, p, 3); p += 3;
+       rec->r.version.locked   = buftou32(p); p += 4;
+       rec->r.version.created  = buftou32(p); p += 4;
+       rec->r.version.modified = buftou32(p); p += 4;
+       rec->r.version.validated= buftou32(p); p += 4;
+       rec->r.version.local_id_counter = buftou32(p); p += 4;
+       rec->r.version.marginals_needed = *p++;
+       rec->r.version.completes_needed = *p++;
+       rec->r.version.max_cert_depth = *p++;
+       if( recnum ) {
+           log_error("%s: version record with recnum %lu\n",
+                                                   db_name, (ulong)recnum );
+           rc = G10ERR_TRUSTDB;
+       }
+       if( rec->reserved != 'g' || rec->r.version.magic[0] != '1'
+                                 || rec->r.version.magic[1] != '0' ) {
+           log_error("%s: not a trustdb file\n", db_name );
+           rc = G10ERR_TRUSTDB;
+       }
+       if( rec->r.version.version != 1 ) {
+           log_error("%s: invalid file version %d\n",
+                                      db_name, rec->r.version.version );
+           rc = G10ERR_TRUSTDB;
+       }
+       break;
+      case 2:
+       rec->r.pubkey.local_id = buftou32(p); p += 4;
+       rec->r.pubkey.keyid[0] = buftou32(p); p += 4;
+       rec->r.pubkey.keyid[1] = buftou32(p); p += 4;
+       rec->r.pubkey.algo = *p++;
+       rec->r.pubkey.reserved = *p++;
+       memcpy( rec->r.pubkey.fingerprint, p, 20); p += 20;
+       rec->r.pubkey.ownertrust = *p++;
+       if( rec->r.pubkey.local_id != recnum ) {
+           log_error("%s: pubkey local_id != recnum (%lu,%lu)\n",
+                                       db_name,
+                                       (ulong)rec->r.pubkey.local_id,
+                                       (ulong)recnum );
+           rc = G10ERR_TRUSTDB;
+       }
+       break;
+      case 3:
+       rec->r.cache.local_id = buftou32(p); p += 4;
+       rec->r.cache.keyid[0] = buftou32(p); p += 4;
+       rec->r.cache.keyid[1] = buftou32(p); p += 4;
+       rec->r.cache.valid = *p++;
+       rec->r.cache.reserved = *p++;
+       memcpy(rec->r.cache.blockhash, p, 20); p += 20;
+       rec->r.cache.n_untrusted = *p++;
+       rec->r.cache.n_marginal = *p++;
+       rec->r.cache.n_fully = *p++;
+       rec->r.cache.trustlevel = *p++;
+       break;
+      default:
+       log_error("%s: invalid record type %d at recnum %lu\n",
+                                       db_name, rec->rectype, (ulong)recnum );
+       rc = G10ERR_TRUSTDB;
+       break;
+    }
 
 
+    return rc;
+}
 
+/****************
+ * Write the record at RECNUM
+ */
+static int
+write_record( u32 recnum, TRUSTREC *rec )
+{
+    byte buf[TRUST_RECORD_LEN], *p;
+    int rc = 0;
+    int n;
+
+    if( db_fd == -1 )
+       open_db();
+
+    if( DBG_TRUST && !no_io_dbg )
+       log_debug("trustdb: write_record(%lu)\n", (ulong)recnum);
+    memset(buf, 0, TRUST_RECORD_LEN);
+    p = buf;
+    *p++ = rec->rectype;
+    *p++ = rec->reserved;
+    switch( rec->rectype ) {
+      case 0:  /* unused record */
+       break;
+      case 1: /* version record */
+       log_bug(NULL);
+       break;
+      case 2:
+       u32tobuf(p, rec->r.pubkey.local_id); p += 4;
+       u32tobuf(p, rec->r.pubkey.keyid[0]); p += 4;
+       u32tobuf(p, rec->r.pubkey.keyid[1]); p += 4;
+       *p++ = rec->r.pubkey.algo;
+       *p++ = rec->r.pubkey.reserved;
+       memcpy( p, rec->r.pubkey.fingerprint, 20); p += 20;
+       *p++ = rec->r.pubkey.ownertrust;
+       assert( rec->r.pubkey.local_id == recnum );
+       break;
+      case 3:
+       u32tobuf(p, rec->r.cache.local_id); p += 4;
+       u32tobuf(p, rec->r.cache.keyid[0]); p += 4;
+       u32tobuf(p, rec->r.cache.keyid[1]); p += 4;
+       *p++ = rec->r.cache.valid;
+       *p++ = rec->r.cache.reserved;
+       memcpy(p, rec->r.cache.blockhash, 20); p += 20;
+       *p++ = rec->r.cache.n_untrusted;
+       *p++ = rec->r.cache.n_marginal;
+       *p++ = rec->r.cache.n_fully;
+       *p++ = rec->r.cache.trustlevel;
+       break;
+      default:
+       log_bug(NULL);
+    }
 
+    if( lseek( db_fd, recnum * TRUST_RECORD_LEN, SEEK_SET ) == -1 ) {
+       log_error("trustdb: lseek failed: %s\n", strerror(errno) );
+       return G10ERR_WRITE_FILE;
+    }
+    n = write( db_fd, buf, TRUST_RECORD_LEN);
+    if( n != TRUST_RECORD_LEN ) {
+       log_error("trustdb: write failed (n=%d): %s\n", n, strerror(errno) );
+       return G10ERR_WRITE_FILE;
+    }
 
+    return rc;
+}
 
+static u32
+new_local_id()
+{
+    off_t offset;
+    u32 recnum;
+
+    /* fixme: look for unused records */
+    offset = lseek( db_fd, 0, SEEK_END );
+    if( offset == -1 )
+       log_fatal("trustdb: lseek to end failed: %s\n", strerror(errno) );
+    recnum = offset / TRUST_RECORD_LEN;
+    assert(recnum); /* this is will never be the first record */
+    return recnum ;
+}
+
+/****************
+ * Scan the trustdb for a record of type RECTYPE which maches PKC
+ * The local_id is set to the correct value
+ */
+static int
+scan_record( PKT_public_cert *pkc, TRUSTREC *rec, int rectype )
+{
+    u32 recnum;
+    u32 keyid[2];
+    byte *fingerprint;
+    size_t fingerlen;
+    int dbg = DBG_TRUST;
+    int rc;
+
+    assert( rectype == 2 || rectype == 3 );
+
+    if( DBG_TRUST )
+       log_debug("trustdb: scan_record\n");
+    keyid_from_pkc( pkc, keyid );
+    fingerprint = fingerprint_from_pkc( pkc, &fingerlen );
+    assert( fingerlen == 20 || fingerlen == 16 );
+
+    no_io_dbg = 1;
+    for(recnum=1; !(rc=read_record( recnum, rec)); recnum++ ) {
+       if( rec->rectype != rectype )
+           continue;
+       if( rec->rectype == 2 ) {
+           if( rec->r.pubkey.keyid[0] == keyid[0]
+               && rec->r.pubkey.keyid[1] == keyid[1]
+               && rec->r.pubkey.algo     == pkc->pubkey_algo
+               && !memcmp(rec->r.pubkey.fingerprint, fingerprint, fingerlen)
+             ) { /* found */
+               /* store the local_id */
+               if( pkc->local_id && pkc->local_id != recnum )
+                   log_error("%s: found record, but local_id from mem does "
+                             "not match recnum (%lu,%lu)\n", db_name,
+                                        (ulong)pkc->local_id, (ulong)recnum );
+               pkc->local_id = recnum;
+               no_io_dbg = 0;
+               return 0;
+           }
+       }
+       else
+           log_bug("not yet implemented\n");
+    }
+    no_io_dbg = 0;
+    if( DBG_TRUST )
+       log_debug("trustdb: scan_record: eof or error\n");
+    if( rc != -1 )
+       log_error("%s: scan_record failed: %s\n",db_name, g10_errstr(rc) );
+    return rc;
+}
 
 
 
@@ -171,8 +434,151 @@ create_db( const char *fname )
  ************* trust logic  *******************
  ***********************************************/
 
+/****************
+ * Verify, that all our public keys are in the trustDB and marked as
+ * ultimately trusted.
+ */
+static int
+verify_own_certs()
+{
+    int rc;
+    void *enum_context = NULL;
+    PKT_secret_cert *skc = m_alloc_clear( sizeof *skc );
+    PKT_public_cert *pkc = m_alloc_clear( sizeof *pkc );
+    u32 keyid[2];
+    int trust;
+
+    while( !(rc=enum_secret_keys( &enum_context, skc) ) ) {
+       /* fixme: to be sure that it is a secret key of our own,
+        *        we should check it, but this needs a passphrase
+        *        for every key and this boring for the user.
+        *        Solution:  Sign the secring and the trustring
+        *                   and verify this signature during
+        *                   startup
+        */
+
+       keyid_from_skc( skc, keyid );
+
+       if( DBG_TRUST )
+           log_debug("checking secret key %08lX\n", (ulong)keyid[1] );
+
+       /* look wether we can access the public key of this secret key */
+       rc = get_pubkey( pkc, keyid );
+       if( rc ) {
+           log_error("keyid %08lX: secret key without public key\n",
+                                                           (ulong)keyid[1] );
+           goto leave;
+       }
+       if( cmp_public_secret_cert( pkc, skc ) ) {
+           log_error("keyid %08lX: secret and public key don't match\n",
+                                                           (ulong)keyid[1] );
+           rc = G10ERR_GENERAL;
+           goto leave;
+       }
+       /* look into the trustdb */
+       rc = check_pkc_trust( pkc, &trust );
+       if( rc ) {
+           log_info("keyid %08lX: problem in trustdb: %s\n", (ulong)keyid[1],
+                                                             g10_errstr(rc) );
+           goto leave;
+       }
+       if( trust & TRUST_NO_PUBKEY ) {
+           log_info("keyid %08lX: not yet in trustdb\n", (ulong)keyid[1] );
+           /* FIXME: insert */
+       }
+       else if( (trust & TRUST_MASK) != TRUST_ULT_TRUST )  {
+           log_error("keyid %08lX: not marked as ultimately trusted\n",
+                                                          (ulong)keyid[1] );
+           /* FIXME: mark */
+       }
+
+       release_secret_cert_parts( skc );
+       release_public_cert_parts( pkc );
+    }
+    if( rc != -1 )
+       log_error("enum_secret_keys failed: %s\n", g10_errstr(rc) );
+    else
+       rc = 0;
+
+  leave:
+    free_secret_cert( skc );
+    free_public_cert( pkc );
+    return rc;
+}
+
 
 
+/****************
+ * Check all the sigs of the given keyblock and mark them
+ * as checked.
+ */
+static int
+check_sigs( KBNODE keyblock )
+{
+    KBNODE kbctx;
+    KBNODE node;
+    int rc;
+
+    for( kbctx=NULL; (node=walk_kbtree( keyblock, &kbctx)) ; ) {
+       if( node->pkt->pkttype == PKT_SIGNATURE
+           && (node->pkt->pkt.signature->sig_class&~3) == 0x10 ) {
+           PKT_signature *sig = node->pkt->pkt.signature;
+
+           rc = check_key_signature( keyblock, node );
+           if( !rc )
+               node->flag |= 1; /* mark signature valid */
+           if( DBG_TRUST )
+               log_debug("trustdb: sig from %08lX: %s\n",
+                                               rc? g10_errstr(rc): "okay" );
+       }
+    }
+    return 0;
+}
+
+
+/****************
+ * Recursive check the signatures.
+ */
+static int
+walk( KBNODE keyblock, int levels )
+{
+    KBNODE kbctx, node;
+
+    check_sigs( keyblock );
+    if( levels ) { /* check the next level */
+       for( kbctx=NULL; (node=walk_kbtree( keyblock, &kbctx)) ; ) {
+           if( node->pkt->pkttype == PKT_SIGNATURE && (node->flag & 1) ) {
+               /* read the keyblock for this signator */
+
+               /* and check his signatures */
+               /*walk( his_keyblock, levels-1)*/
+           }
+       }
+    }
+
+}
+
+
+
+
+
+/****************
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ */
+static int
+check_trust()
+{
+}
+
 
 
 /*********************************************************
@@ -181,11 +587,13 @@ create_db( const char *fname )
 
 /****************
  * Perform some checks over the trustdb
- *  level 0: used on initial program startup
+ *  level 0: used for initial program startup
  */
 int
 check_trustdb( int level )
 {
+    int rc=0;
+
     if( !level ) {
        char *fname = make_filename("~/.g10", "trustDB", NULL );
        if( access( fname, R_OK ) ) {
@@ -200,13 +608,22 @@ check_trustdb( int level )
        db_name = fname;
 
        /* we can verify a signature about our local data (secring and trustdb)
-        * in ~/.g10/ here
-        */
+        * in ~/.g10/ here */
+       rc = verify_private_data();
+       if( !rc ) {
+           /* verify, that our own certificates are in the trustDB
+            * or move them to the trustdb. */
+           rc = verify_own_certs();
+
+           /* should we check wether there is no other ultimately trusted
+            * key in the database? */
+
+       }
     }
     else
        log_bug(NULL);
 
-    return 0;
+    return rc;
 }
 
 
@@ -234,12 +651,171 @@ check_trustdb( int level )
 int
 check_pkc_trust( PKT_public_cert *pkc, int *r_trustlevel )
 {
+    TRUSTREC rec;
     int trustlevel = 0;
+    int rc=0;
 
     if( opt.verbose )
        log_info("check_pkc_trust() called.\n");
 
+    /* get the pubkey record */
+    if( pkc->local_id ) {
+       if( read_record( pkc->local_id, &rec ) ) {
+           log_error("check_pkc_trust: read record failed\n");
+           return G10ERR_TRUSTDB;
+       }
+    }
+    else { /* no local_id: scan the trustdb */
+       if( (rc=scan_record( pkc, &rec, 2 )) && rc != -1 ) {
+           log_error("check_pkc_trust: scan_record(2) failed: %s\n",
+                                                           g10_errstr(rc));
+           return G10ERR_TRUSTDB;
+       }
+       else if( rc == -1 ) {
+           log_error("check_pkc_trust: pubkey not in TrustDB\n");
+           trustlevel = TRUST_NO_PUBKEY;
+           goto leave;
+       }
+    }
+    /* fixme: do some additional checks on the pubkey record */
+
+
+  leave:
+    if( opt.verbose )
+       log_info("check_pkc_trust() returns trustlevel %04x.\n", trustlevel);
     *r_trustlevel = trustlevel;
     return 0;
 }
 
+
+/****************
+ * Insert a trust record into the TrustDB
+ * This function failes if this record already exists.
+ */
+int
+insert_trust_record( PKT_public_cert *pkc )
+{
+    TRUSTREC rec;
+    u32 keyid[2];
+    u32 recnum;
+    byte *fingerprint;
+    size_t fingerlen;
+
+
+    if( DBG_TRUST )
+       log_debug("trustdb: insert_record\n");
+
+    assert( !pkc->local_id );
+
+    keyid_from_pkc( pkc, keyid );
+    fingerprint = fingerprint_from_pkc( pkc, &fingerlen );
+
+    /* FIXME: check that we do not have this record. */
+
+    recnum = new_local_id();
+    /* build record */
+    memset( &rec, 0, sizeof rec );
+    rec.rectype = 2; /* the pubkey record */
+    rec.r.pubkey.local_id = recnum;
+    rec.r.pubkey.keyid[0] = keyid[0];
+    rec.r.pubkey.keyid[1] = keyid[1];
+    rec.r.pubkey.algo = pkc->pubkey_algo;
+    memcpy(rec.r.pubkey.fingerprint, fingerprint, fingerlen );
+    rec.r.pubkey.ownertrust = 0;
+    if( write_record( recnum, &rec ) ) {
+       log_error("insert_trust_record: write failed\n");
+       return G10ERR_TRUSTDB;
+    }
+
+    pkc->local_id = recnum;
+
+    return 0;
+}
+
+
+int
+update_trust_record( PKT_public_cert *pkc, int new_trust )
+{
+    TRUSTREC rec;
+    u32 keyid[2];
+    u32 recnum;
+
+    if( DBG_TRUST )
+       log_debug("trustdb: update_record\n");
+
+    assert( pkc->local_id );
+
+    if( read_record( pkc->local_id, &rec ) ) {
+       log_error("update_trust_record: read failed\n");
+       return G10ERR_TRUSTDB;
+    }
+    /* check keyid, fingerprint etc ? */
+
+    recnum = new_local_id();
+    /* build record */
+    memset( &rec, 0, sizeof rec );
+    rec.rectype = 2; /* the pubkey record */
+    rec.r.pubkey.local_id = recnum;
+    rec.r.pubkey.keyid[0] = keyid[0];
+    rec.r.pubkey.keyid[1] = keyid[1];
+    rec.r.pubkey.algo = pkc->pubkey_algo;
+    memcpy(rec.r.pubkey.fingerprint, fingerprint, fingerlen );
+    rec.r.pubkey.ownertrust = 0;
+    if( write_record( recnum, &rec ) ) {
+       log_error("insert_trust_record: write failed\n");
+       return G10ERR_TRUSTDB;
+    }
+
+    pkc->local_id = recnum;
+
+    return 0;
+}
+
+
+int
+verify_private_data()
+{
+    int rc = 0;
+    char *sigfile = make_filename("~/.g10", "sig", NULL );
+
+    if( access( sigfile, R_OK ) ) {
+       if( errno != ENOENT ) {
+           log_error("can't access %s: %s\n", sigfile, strerror(errno) );
+           rc = G10ERR_TRUSTDB;
+           goto leave;
+       }
+       log_info("private data signature missing; creating ...\n");
+       rc = sign_private_data();
+       if( rc ) {
+           log_error("error creating %s: %s\n", sigfile, g10_errstr(rc) );
+           goto leave;
+       }
+    }
+
+    /* FIXME: verify this signature */
+
+  leave:
+    m_free(sigfile);
+    return rc;
+}
+
+
+int
+sign_private_data()
+{
+    int rc;
+    char *sigfile = make_filename("~/.g10", "sig", NULL );
+    char *secring = make_filename("~/.g10", "secring.g10", NULL );
+    STRLIST list = NULL;
+
+    add_to_strlist( &list, db_name );
+    add_to_strlist( &list, secring );
+
+    rc = sign_file( list, 1, NULL, 0, NULL, sigfile);
+
+    m_free(sigfile);
+    m_free(secring);
+    free_strlist(list);
+    return rc;
+}
+
index 01f1268..aff668e 100644 (file)
 #ifndef G10_TRUSTDB_H
 #define G10_TRUSTDB_H
 
+
+
+#define TRUST_MASK     0x07 /* for the trust leveles */
+#define TRUST_UNKNOWN    1  /* unknown            */
+#define TRUST_NO_TRUST   2  /* not trusted        */
+#define TRUST_MARG_TRUST  4  /* marginally trusted */
+#define TRUST_FULL_TRUST  5  /* fully trusted     */
+#define TRUST_ULT_TRUST   7  /* ultimately trusted */
+ /* other bits used with the trustlevel */
+#define TRUST_NO_PUBKEY 0x10 /* we do not have the pubkey in out trustDB */
+
+
 /*-- trustdb.c --*/
 int check_trustdb( int level );
 int check_pkc_trust( PKT_public_cert *pkc, int *r_trustlevel );
+int verify_private_data(void);
+int sign_private_data(void);
 
 #endif /*G10_TRUSTDB_H*/
index 34015ff..7f25930 100644 (file)
@@ -91,6 +91,7 @@ int answer_is_yes( const char *s );
 /*-- strgutil.c --*/
 void free_strlist( STRLIST sl );
 #define FREE_STRLIST(a) do { free_strlist((a)); (a) = NULL ; } while(0)
+void add_to_strlist( STRLIST *list, const char *string );
 char *memistr( char *buf, size_t buflen, const char *sub );
 char *trim_spaces( char *string );
 int string_count_chr( const char *string, int c );
index ecdcb75..cb80c57 100644 (file)
@@ -37,6 +37,18 @@ free_strlist( STRLIST sl )
     }
 }
 
+
+void
+add_to_strlist( STRLIST *list, const char *string )
+{
+    STRLIST sl;
+
+    sl = m_alloc( sizeof *sl + strlen(string));
+    strcpy(sl->d, string);
+    sl->next = *list;
+    *list = sl;
+}
+
 /****************
  * look for the substring SUB in buffer and return a pointer to that
  * substring in BUF or NULL if not found.