partial DSA support
[gnupg.git] / g10 / getkey.c
index 4aea8e7..300e33b 100644 (file)
@@ -1,14 +1,14 @@
 /* getkey.c -  Get a key from the database
- *     Copyright (c) 1997 by Werner Koch (dd9jn)
+ *     Copyright (C) 1998 Free Software Foundation, Inc.
  *
- * This file is part of G10.
+ * This file is part of GNUPG.
  *
- * G10 is free software; you can redistribute it and/or modify
+ * GNUPG 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,
+ * GNUPG 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.
@@ -23,6 +23,7 @@
 #include <stdlib.h>
 #include <string.h>
 #include <assert.h>
+#include <ctype.h>
 #include "util.h"
 #include "packet.h"
 #include "memory.h"
@@ -48,10 +49,18 @@ typedef struct user_id_db {
 typedef struct pkc_cache_entry {
     struct pkc_cache_entry *next;
     u32 keyid[2];
-    PKT_pubkey_cert *pkc;
+    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;
 
 static keyid_list_t unknown_keyids;
 static user_id_db_t user_id_db;
@@ -59,33 +68,107 @@ static pkc_cache_entry_t pkc_cache;
 static int pkc_cache_entries;  /* number of entries in pkc cache */
 
 
-static int scan_keyring( PKT_pubkey_cert *pkc, u32 *keyid,
+static int scan_keyring( PKT_public_cert *pkc, u32 *keyid,
                         const char *name, const char *filename );
-static int scan_secret_keyring( PKT_seckey_cert *skc, u32 *keyid,
+static int scan_secret_keyring( PKT_secret_cert *skc, u32 *keyid,
                                const char *name, const char *filename);
 
-
+/* note this function may be called before secure memory is
+ * available */
 void
 add_keyring( const char *name )
 {
     STRLIST sl;
+    int rc;
 
     /* FIXME: check wether this one is available etc */
-    /* my be we should do this later */
-    sl = m_alloc( sizeof *sl + strlen(name) );
-    strcpy(sl->d, name );
+    /* maybe we should do this later */
+    if( *name != '/' ) { /* do tilde expansion etc */
+       char *p ;
+
+       if( strchr(name, '/') )
+           p = make_filename(name, NULL);
+       else
+           p = make_filename(opt.homedir, name, NULL);
+       sl = m_alloc( sizeof *sl + strlen(p) );
+       strcpy(sl->d, p );
+       m_free(p);
+    }
+    else {
+       sl = m_alloc( sizeof *sl + strlen(name) );
+       strcpy(sl->d, name );
+    }
     sl->next = keyrings;
     keyrings = sl;
+
+    /* FIXME: We should remove much out of this module and
+     * combine it with the keyblock stuff from ringedit.c
+     * For now we will simple add the filename as keyblock resource
+     */
+    rc = add_keyblock_resource( sl->d, 0, 0 );
+    if( rc )
+       log_error("keyblock resource '%s': %s\n", sl->d, g10_errstr(rc) );
+}
+
+
+/****************
+ * Get the name of the keyrings, start with a sequence number of 0.
+ */
+const char *
+get_keyring( int sequence )
+{
+    STRLIST sl;
+
+    for(sl = keyrings; sl && sequence; sl = sl->next, sequence-- )
+       ;
+    return sl? sl->d : NULL;
+}
+
+
+void
+add_secret_keyring( const char *name )
+{
+    STRLIST sl;
+    int rc;
+
+    /* FIXME: check wether this one is available etc */
+    /* my be we should do this later */
+    if( *name != '/' ) { /* do tilde expansion etc */
+       char *p ;
+
+       if( strchr(name, '/') )
+           p = make_filename(name, NULL);
+       else
+           p = make_filename(opt.homedir, name, NULL);
+       sl = m_alloc( sizeof *sl + strlen(p) );
+       strcpy(sl->d, p );
+       m_free(p);
+    }
+    else {
+       sl = m_alloc( sizeof *sl + strlen(name) );
+       strcpy(sl->d, name );
+    }
+    sl->next = secret_keyrings;
+    secret_keyrings = sl;
+
+    /* FIXME: We should remove much out of this mpdule and
+     * combine it with the keyblock stuff from ringedit.c
+     * For now we will simple add the filename as keyblock resource
+     */
+    rc = add_keyblock_resource( sl->d, 0, 1 );
+    if( rc )
+       log_error("secret keyblock resource '%s': %s\n", sl->d, g10_errstr(rc));
 }
 
 
 void
-cache_pubkey_cert( PKT_pubkey_cert *pkc )
+cache_public_cert( PKT_public_cert *pkc )
 {
     pkc_cache_entry_t ce;
     u32 keyid[2];
 
     if( pkc->pubkey_algo == PUBKEY_ALGO_ELGAMAL
+       || pkc->pubkey_algo == PUBKEY_ALGO_DSA
        || pkc->pubkey_algo == PUBKEY_ALGO_RSA ) {
        keyid_from_pkc( pkc, keyid );
     }
@@ -95,7 +178,7 @@ cache_pubkey_cert( PKT_pubkey_cert *pkc )
     for( ce = pkc_cache; ce; ce = ce->next )
        if( ce->keyid[0] == keyid[0] && ce->keyid[1] == keyid[1] ) {
            if( DBG_CACHE )
-               log_debug("cache_pubkey_cert: already in cache\n");
+               log_debug("cache_public_cert: already in cache\n");
            return;
        }
 
@@ -106,7 +189,7 @@ cache_pubkey_cert( PKT_pubkey_cert *pkc )
            log_info("too many entries in pkc cache - disabled\n");
        }
        ce = pkc_cache;
-       free_pubkey_cert( ce->pkc );
+       free_public_cert( ce->pkc );
     }
     else {
        pkc_cache_entries++;
@@ -114,7 +197,7 @@ cache_pubkey_cert( PKT_pubkey_cert *pkc )
        ce->next = pkc_cache;
        pkc_cache = ce;
     }
-    ce->pkc = copy_pubkey_cert( NULL, pkc );
+    ce->pkc = copy_public_cert( NULL, pkc );
     ce->keyid[0] = keyid[0];
     ce->keyid[1] = keyid[1];
 }
@@ -152,7 +235,7 @@ cache_user_id( PKT_user_id *uid, u32 *keyid )
  * internal structures.
  */
 int
-get_pubkey( PKT_pubkey_cert *pkc, u32 *keyid )
+get_pubkey( PKT_public_cert *pkc, u32 *keyid )
 {
     keyid_list_t kl;
     int internal = 0;
@@ -161,15 +244,6 @@ get_pubkey( PKT_pubkey_cert *pkc, u32 *keyid )
     STRLIST sl;
 
 
-    if( opt.cache_all && !pkc_cache ) {
-       log_info("reading all entries ...\n");
-       for(sl = keyrings; sl; sl = sl->next )
-           if( !scan_keyring( NULL, NULL, NULL, sl->d ) )
-               goto leave;
-       log_info("cached %d entries\n", pkc_cache_entries);
-    }
-
-
     /* lets see wether we checked the keyid already */
     for( kl = unknown_keyids; kl; kl = kl->next )
        if( kl->keyid[0] == keyid[0] && kl->keyid[1] == keyid[1] )
@@ -179,7 +253,7 @@ get_pubkey( PKT_pubkey_cert *pkc, u32 *keyid )
     for( ce = pkc_cache; ce; ce = ce->next )
        if( ce->keyid[0] == keyid[0] && ce->keyid[1] == keyid[1] ) {
            if( pkc )
-               copy_pubkey_cert( pkc, ce->pkc );
+               copy_public_cert( pkc, ce->pkc );
            return 0;
        }
 
@@ -207,7 +281,7 @@ get_pubkey( PKT_pubkey_cert *pkc, u32 *keyid )
 
   leave:
     if( !rc )
-       cache_pubkey_cert( pkc );
+       cache_public_cert( pkc );
     if( internal )
        m_free(pkc);
     return rc;
@@ -219,13 +293,80 @@ get_pubkey( PKT_pubkey_cert *pkc, u32 *keyid )
  * first pubkey certificate which has the given name in a user_id.
  * if pkc has the pubkey algo set, the function will only return
  * a pubkey with that algo.
+ *
+ * - If the username starts with 8,9,16 or 17 hex-digits (the first one
+ *   must be in the range 0..9), this is considered a keyid; depending
+ *   on the length a short or complete one.
+ * - If the username starts with 32,33,40 or 41 hex-digits (the first one
+ *   must be in the range 0..9), this is considered a fingerprint.
+ *   (Not yet implemented)
+ * - If the username starts with a left angle, we assume it is a complete
+ *   email address and look only at this part.
+ * - If the userid start with an '=' an exact compare is done; this may
+ *   also follow the keyid in which case both parts are matched.
+ *   (Not yet implemented)
+ *
  */
 int
-get_pubkey_by_name( PKT_pubkey_cert *pkc, const char *name )
+get_pubkey_byname( PKT_public_cert *pkc, const char *name )
 {
     int internal = 0;
     int rc = 0;
     STRLIST sl;
+    const char *s;
+    u32 keyid[2] = {0}; /* init to avoid compiler warning */
+    int use_keyid=0;
+
+    /* check what kind of name it is */
+    for(s = name; *s && isspace(*s); s++ )
+       ;
+    if( isdigit( *s ) ) { /* a keyid */
+       int i;
+       char buf[9];
+
+       if( *s == '0' && s[1] == 'x' && isxdigit(s[2]) )
+           s += 2; /*kludge to allow 0x034343434 */
+       for(i=0; isxdigit(s[i]); i++ )
+           ;
+       if( s[i] && !isspace(s[i]) ) /* not terminated by EOS or blank*/
+           rc = G10ERR_INV_USER_ID;
+       else if( i == 8 || (i == 9 && *s == '0') ) { /* short keyid */
+           if( i==9 )
+               s++;
+           keyid[1] = strtoul( s, NULL, 16 );
+           use_keyid++;
+       }
+       else if( i == 16 || (i == 17 && *s == '0') ) { /* complete keyid */
+           if( i==17 )
+               s++;
+           mem2str(buf, s, 9 );
+           keyid[0] = strtoul( buf, NULL, 16 );
+           keyid[1] = strtoul( s+8, NULL, 16 );
+           return get_pubkey( pkc, keyid );
+       }
+       else
+           rc = G10ERR_INV_USER_ID;
+    }
+    else if( *s == '<' ) { /* an email address */
+       /* for now handled like a substring */
+       /* a keyserver might use this for quicker access */
+    }
+    else if( *s == '=' ) { /* exact search */
+       rc = G10ERR_INV_USER_ID; /* nox yet implemented */
+    }
+    else if( *s == '#' ) { /* use local id */
+       rc = G10ERR_INV_USER_ID; /* nox yet implemented */
+    }
+    else if( *s == '*' ) { /* substring search */
+       name++;
+    }
+    else if( !*s )  /* empty string */
+       rc = G10ERR_INV_USER_ID;
+
+    if( rc )
+       goto leave;
+
+
 
     if( !pkc ) {
        pkc = m_alloc_clear( sizeof *pkc );
@@ -234,9 +375,14 @@ get_pubkey_by_name( PKT_pubkey_cert *pkc, const char *name )
 
     /* 2. Try to get it from the keyrings */
     for(sl = keyrings; sl; sl = sl->next )
-       if( !scan_keyring( pkc, NULL, name, sl->d ) )
-           goto leave;
-
+       if( use_keyid ) {
+           if( !scan_keyring( pkc, keyid, name, sl->d ) )
+               goto leave;
+       }
+       else {
+           if( !scan_keyring( pkc, NULL, name, sl->d ) )
+               goto leave;
+       }
     /* 3. Try to get it from a key server */
 
     /* 4. not found: store it for future reference */
@@ -253,12 +399,14 @@ get_pubkey_by_name( PKT_pubkey_cert *pkc, const char *name )
  * Get a secret key and store it into skey
  */
 int
-get_seckey( PKT_seckey_cert *skc, u32 *keyid )
+get_seckey( PKT_secret_cert *skc, u32 *keyid )
 {
+    STRLIST sl;
     int rc=0;
 
-    if( !(rc=scan_secret_keyring( skc, keyid, NULL, "../keys/secring.g10" ) ) )
-       goto found;
+    for(sl = secret_keyrings; sl; sl = sl->next )
+       if( !(rc=scan_secret_keyring( skc, keyid, NULL, sl->d )) )
+           goto found;
     /* fixme: look at other places */
     goto leave;
 
@@ -274,15 +422,45 @@ get_seckey( PKT_seckey_cert *skc, u32 *keyid )
 }
 
 /****************
+ * Check wether the secret key is available
+ * Returns: 0 := key is available
+ *         G10ERR_NO_SECKEY := not availabe
+ */
+int
+seckey_available( u32 *keyid )
+{
+    PKT_secret_cert *skc;
+    STRLIST sl;
+    int rc=0;
+
+    skc = m_alloc_clear( sizeof *skc );
+    for(sl = secret_keyrings; sl; sl = sl->next )
+       if( !(rc=scan_secret_keyring( skc, keyid, NULL, sl->d )) )
+           goto found;
+    /* fixme: look at other places */
+    goto leave;
+
+  found:
+  leave:
+    free_secret_cert( skc );
+    return rc;
+}
+
+
+
+/****************
  * Get a secret key by name and store it into skc
+ * If NAME is NULL use the default certificate
  */
 int
-get_seckey_by_name( PKT_seckey_cert *skc, const char *name )
+get_seckey_byname( PKT_secret_cert *skc, const char *name, int unprotect )
 {
+    STRLIST sl;
     int rc=0;
 
-    if( !(rc=scan_secret_keyring( skc, NULL, name, "../keys/secring.g10" ) ) )
-       goto found;
+    for(sl = secret_keyrings; sl; sl = sl->next )
+       if( !(rc=scan_secret_keyring( skc, NULL, name, sl->d ) ) )
+           goto found;
     /* fixme: look at other places */
     goto leave;
 
@@ -290,33 +468,41 @@ get_seckey_by_name( PKT_seckey_cert *skc, const char *name )
     /* get the secret key (this may prompt for a passprase to
      * unlock the secret key
      */
-    if( (rc = check_secret_key( skc )) )
-       goto leave;
+    if( unprotect )
+       if( (rc = check_secret_key( skc )) )
+           goto leave;
 
   leave:
     return rc;
 }
 
 
+
+
+
 /****************
  * scan the keyring and look for either the keyid or the name.
+ * If both, keyid and name are given, look for keyid but use only
+ * the low word of it (name is only used as a flag to indicate this mode
+ * of operation).
  */
 static int
-scan_keyring( PKT_pubkey_cert *pkc, u32 *keyid,
+scan_keyring( PKT_public_cert *pkc, u32 *keyid,
              const char *name, const char *filename )
 {
+    compress_filter_context_t cfx;
     int rc=0;
     int found = 0;
     IOBUF a;
     PACKET pkt;
     int save_mode;
     u32 akeyid[2];
-    PKT_pubkey_cert *last_pk = NULL;
-
-    assert( !keyid || !name );
+    PKT_public_cert *last_pk = NULL;
+    int shortkeyid;
 
-    if( opt.cache_all && (name || keyid) )
-       return G10ERR_NO_PUBKEY;
+    shortkeyid = keyid && name;
+    if( shortkeyid )
+       name = NULL; /* not used anymore */
 
     if( !(a = iobuf_open( filename ) ) ) {
        log_debug("scan_keyring: can't open '%s'\n", filename );
@@ -325,11 +511,13 @@ scan_keyring( PKT_pubkey_cert *pkc, u32 *keyid,
 
     if( !DBG_CACHE )
        ;
+    else if( shortkeyid )
+       log_debug("scan_keyring %s for %08lx\n",  filename, (ulong)keyid[1] );
     else if( name )
        log_debug("scan_keyring %s for '%s'\n",  filename, name );
     else if( keyid )
        log_debug("scan_keyring %s for %08lx %08lx\n",  filename,
-                                               keyid[0], keyid[1] );
+                                            (ulong)keyid[0], (ulong)keyid[1] );
     else
        log_debug("scan_keyring %s (all)\n",  filename );
 
@@ -338,34 +526,49 @@ scan_keyring( PKT_pubkey_cert *pkc, u32 *keyid,
     while( (rc=parse_packet(a, &pkt)) != -1 ) {
        if( rc )
            ; /* e.g. unknown packet */
-       else if( keyid && found && pkt.pkttype == PKT_PUBKEY_CERT ) {
+       else if( keyid && found && pkt.pkttype == PKT_PUBLIC_CERT ) {
            log_error("Hmmm, pubkey without an user id in '%s'\n", filename);
            goto leave;
        }
-       else if( keyid && pkt.pkttype == PKT_PUBKEY_CERT ) {
-           switch( pkt.pkt.pubkey_cert->pubkey_algo ) {
+       else if( pkt.pkttype == PKT_COMPRESSED ) {
+           memset( &cfx, 0, sizeof cfx );
+           if( pkt.pkt.compressed->algorithm == 1 )
+               cfx.pgpmode = 1;
+           else if( pkt.pkt.compressed->algorithm != 2  ){
+               rc = G10ERR_COMPR_ALGO;
+               log_error("compressed keyring: %s\n", g10_errstr(rc) );
+               break;
+           }
+
+           pkt.pkt.compressed->buf = NULL;
+           iobuf_push_filter( a, compress_filter, &cfx );
+       }
+       else if( keyid && pkt.pkttype == PKT_PUBLIC_CERT ) {
+           switch( pkt.pkt.public_cert->pubkey_algo ) {
              case PUBKEY_ALGO_ELGAMAL:
+             case PUBKEY_ALGO_DSA:
              case PUBKEY_ALGO_RSA:
-               keyid_from_pkc( pkt.pkt.pubkey_cert, akeyid );
-               if( akeyid[0] == keyid[0] && akeyid[1] == keyid[1] ) {
-                   copy_pubkey_cert( pkc, pkt.pkt.pubkey_cert );
+               keyid_from_pkc( pkt.pkt.public_cert, akeyid );
+               if( (shortkeyid || akeyid[0] == keyid[0])
+                   && akeyid[1] == keyid[1] ) {
+                   copy_public_cert( pkc, pkt.pkt.public_cert );
                    found++;
                }
                break;
              default:
                log_error("cannot handle pubkey algo %d\n",
-                                    pkt.pkt.pubkey_cert->pubkey_algo);
+                                    pkt.pkt.public_cert->pubkey_algo);
            }
        }
        else if( keyid && found && pkt.pkttype == PKT_USER_ID ) {
            cache_user_id( pkt.pkt.user_id, keyid );
            goto leave;
        }
-       else if( name && pkt.pkttype == PKT_PUBKEY_CERT ) {
+       else if( name && pkt.pkttype == PKT_PUBLIC_CERT ) {
            if( last_pk )
-               free_pubkey_cert(last_pk);
-           last_pk = pkt.pkt.pubkey_cert;
-           pkt.pkt.pubkey_cert = NULL;
+               free_public_cert(last_pk);
+           last_pk = pkt.pkt.public_cert;
+           pkt.pkt.public_cert = NULL;
        }
        else if( name && pkt.pkttype == PKT_USER_ID ) {
            if( memistr( pkt.pkt.user_id->name, pkt.pkt.user_id->len, name )) {
@@ -378,16 +581,16 @@ scan_keyring( PKT_pubkey_cert *pkc, u32 *keyid,
                                pkt.pkt.user_id->len, pkt.pkt.user_id->name,
                                pkc->pubkey_algo, last_pk->pubkey_algo );
                else {
-                   copy_pubkey_cert( pkc, last_pk );
+                   copy_public_cert( pkc, last_pk );
                    goto leave;
                }
            }
        }
-       else if( !keyid && !name && pkt.pkttype == PKT_PUBKEY_CERT ) {
+       else if( !keyid && !name && pkt.pkttype == PKT_PUBLIC_CERT ) {
            if( last_pk )
-               free_pubkey_cert(last_pk);
-           last_pk = pkt.pkt.pubkey_cert;
-           pkt.pkt.pubkey_cert = NULL;
+               free_public_cert(last_pk);
+           last_pk = pkt.pkt.public_cert;
+           pkt.pkt.public_cert = NULL;
        }
        else if( !keyid && !name && pkt.pkttype == PKT_USER_ID ) {
            if( !last_pk )
@@ -395,11 +598,12 @@ scan_keyring( PKT_pubkey_cert *pkc, u32 *keyid,
                            pkt.pkt.user_id->len, pkt.pkt.user_id->name);
            else {
                if( last_pk->pubkey_algo == PUBKEY_ALGO_ELGAMAL
+                   || last_pk->pubkey_algo == PUBKEY_ALGO_DSA
                    || last_pk->pubkey_algo == PUBKEY_ALGO_RSA ) {
                     keyid_from_pkc( last_pk, akeyid );
                     cache_user_id( pkt.pkt.user_id, akeyid );
                }
-               cache_pubkey_cert( last_pk );
+               cache_public_cert( last_pk );
            }
        }
        free_packet(&pkt);
@@ -408,7 +612,7 @@ scan_keyring( PKT_pubkey_cert *pkc, u32 *keyid,
 
   leave:
     if( last_pk )
-       free_pubkey_cert(last_pk);
+       free_public_cert(last_pk);
     free_packet(&pkt);
     iobuf_close(a);
     set_packet_list_mode(save_mode);
@@ -422,7 +626,7 @@ scan_keyring( PKT_pubkey_cert *pkc, u32 *keyid,
  * PKT returns the secret key certificate.
  */
 static int
-scan_secret_keyring( PKT_seckey_cert *skc, u32 *keyid,
+scan_secret_keyring( PKT_secret_cert *skc, u32 *keyid,
                     const char *name, const char *filename )
 {
     int rc=0;
@@ -431,9 +635,13 @@ scan_secret_keyring( PKT_seckey_cert *skc, u32 *keyid,
     PACKET pkt;
     int save_mode;
     u32 akeyid[2];
-    PKT_seckey_cert *last_pk = NULL;
+    PKT_secret_cert *last_pk = NULL;
+    int get_first;
+    u32 dummy_keyid[2];
 
-    assert( !keyid || !name );
+    get_first = !keyid && !name;
+    if( get_first )
+       keyid = dummy_keyid;
 
     if( !(a = iobuf_open( filename ) ) ) {
        log_debug("scan_secret_keyring: can't open '%s'\n", filename );
@@ -445,33 +653,40 @@ scan_secret_keyring( PKT_seckey_cert *skc, u32 *keyid,
     while( (rc=parse_packet(a, &pkt)) != -1 ) {
        if( rc )
            ; /* e.g. unknown packet */
-       else if( keyid && found && pkt.pkttype == PKT_SECKEY_CERT ) {
+       else if( keyid && found && pkt.pkttype == PKT_SECRET_CERT ) {
            log_error("Hmmm, seckey without an user id in '%s'\n", filename);
            goto leave;
        }
-       else if( keyid && pkt.pkttype == PKT_SECKEY_CERT ) {
-           switch( pkt.pkt.seckey_cert->pubkey_algo ) {
+       else if( keyid && pkt.pkttype == PKT_SECRET_CERT ) {
+           switch( pkt.pkt.secret_cert->pubkey_algo ) {
              case PUBKEY_ALGO_ELGAMAL:
+             case PUBKEY_ALGO_DSA:
              case PUBKEY_ALGO_RSA:
-               keyid_from_skc( pkt.pkt.seckey_cert, akeyid );
-               if( akeyid[0] == keyid[0] && akeyid[1] == keyid[1] ) {
-                   copy_seckey_cert( skc, pkt.pkt.seckey_cert );
+               if( get_first ) {
+                   copy_secret_cert( skc, pkt.pkt.secret_cert );
                    found++;
                }
+               else {
+                   keyid_from_skc( pkt.pkt.secret_cert, akeyid );
+                   if( (akeyid[0] == keyid[0] && akeyid[1] == keyid[1]) ) {
+                       copy_secret_cert( skc, pkt.pkt.secret_cert );
+                       found++;
+                   }
+               }
                break;
              default:
                log_error("cannot handle pubkey algo %d\n",
-                                    pkt.pkt.seckey_cert->pubkey_algo);
+                                    pkt.pkt.secret_cert->pubkey_algo);
            }
        }
        else if( keyid && found && pkt.pkttype == PKT_USER_ID ) {
            goto leave;
        }
-       else if( name && pkt.pkttype == PKT_SECKEY_CERT ) {
+       else if( name && pkt.pkttype == PKT_SECRET_CERT ) {
            if( last_pk )
-               free_seckey_cert(last_pk);
-           last_pk = pkt.pkt.seckey_cert;
-           pkt.pkt.seckey_cert = NULL;
+               free_secret_cert(last_pk);
+           last_pk = pkt.pkt.secret_cert;
+           pkt.pkt.secret_cert = NULL;
        }
        else if( name && pkt.pkttype == PKT_USER_ID ) {
            if( memistr( pkt.pkt.user_id->name, pkt.pkt.user_id->len, name )) {
@@ -484,16 +699,16 @@ scan_secret_keyring( PKT_seckey_cert *skc, u32 *keyid,
                                pkt.pkt.user_id->len, pkt.pkt.user_id->name,
                                skc->pubkey_algo, last_pk->pubkey_algo );
                else {
-                   copy_seckey_cert( skc, last_pk );
+                   copy_secret_cert( skc, last_pk );
                    goto leave;
                }
            }
        }
-       else if( !keyid && !name && pkt.pkttype == PKT_SECKEY_CERT ) {
+       else if( !keyid && !name && pkt.pkttype == PKT_SECRET_CERT ) {
            if( last_pk )
-               free_seckey_cert(last_pk);
-           last_pk = pkt.pkt.seckey_cert;
-           pkt.pkt.seckey_cert = NULL;
+               free_secret_cert(last_pk);
+           last_pk = pkt.pkt.secret_cert;
+           pkt.pkt.secret_cert = NULL;
        }
        else if( !keyid && !name && pkt.pkttype == PKT_USER_ID ) {
            if( !last_pk )
@@ -501,6 +716,7 @@ scan_secret_keyring( PKT_seckey_cert *skc, u32 *keyid,
                            pkt.pkt.user_id->len, pkt.pkt.user_id->name);
            else {
                if( last_pk->pubkey_algo == PUBKEY_ALGO_ELGAMAL
+                  || last_pk->pubkey_algo == PUBKEY_ALGO_DSA
                   || last_pk->pubkey_algo == PUBKEY_ALGO_RSA ) {
                    keyid_from_skc( last_pk, akeyid );
                    cache_user_id( pkt.pkt.user_id, akeyid );
@@ -513,7 +729,7 @@ scan_secret_keyring( PKT_seckey_cert *skc, u32 *keyid,
 
   leave:
     if( last_pk )
-       free_seckey_cert(last_pk);
+       free_secret_cert(last_pk);
     free_packet(&pkt);
     iobuf_close(a);
     set_packet_list_mode(save_mode);
@@ -522,6 +738,69 @@ scan_secret_keyring( PKT_seckey_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.
  */
@@ -536,12 +815,34 @@ get_user_id_string( u32 *keyid )
        for(r=user_id_db; r; r = r->next )
            if( r->keyid[0] == keyid[0] && r->keyid[1] == keyid[1] ) {
                p = m_alloc( r->len + 10 );
-               sprintf(p, "%08lX %.*s", keyid[1], r->len, r->name );
+               sprintf(p, "%08lX %.*s", (ulong)keyid[1], r->len, r->name );
                return p;
            }
     } while( ++pass < 2 && !get_pubkey( NULL, keyid ) );
     p = m_alloc( 15 );
-    sprintf(p, "%08lX [?]", keyid[1] );
+    sprintf(p, "%08lX [?]", (ulong)keyid[1] );
+    return p;
+}
+
+char*
+get_user_id( u32 *keyid, size_t *rn )
+{
+    user_id_db_t r;
+    char *p;
+    int pass=0;
+    /* try it two times; second pass reads from keyrings */
+    do {
+       for(r=user_id_db; r; r = r->next )
+           if( r->keyid[0] == keyid[0] && r->keyid[1] == keyid[1] ) {
+               p = m_alloc( r->len );
+               memcpy(p, r->name, r->len );
+               *rn = r->len;
+               return p;
+           }
+    } while( ++pass < 2 && !get_pubkey( NULL, keyid ) );
+    p = m_alloc( 19 );
+    memcpy(p, "[User id not found]", 19 );
+    *rn = 19;
     return p;
 }