partial DSA support
[gnupg.git] / g10 / getkey.c
index 2195762..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"
@@ -51,6 +52,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;
 
@@ -65,7 +73,8 @@ static int scan_keyring( PKT_public_cert *pkc, 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 )
 {
@@ -73,19 +82,32 @@ add_keyring( const char *name )
     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 mpdule and
+    /* 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( name, 0 );
+    rc = add_keyblock_resource( sl->d, 0, 0 );
     if( rc )
-       log_error("keyblock resource '%s': %s\n", name, g10_errstr(rc) );
+       log_error("keyblock resource '%s': %s\n", sl->d, g10_errstr(rc) );
 }
 
 
@@ -111,10 +133,31 @@ add_secret_keyring( const char *name )
 
     /* 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 );
+    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));
 }
 
 
@@ -125,6 +168,7 @@ cache_public_cert( PKT_public_cert *pkc )
     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 );
     }
@@ -200,15 +244,6 @@ get_pubkey( PKT_public_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] )
@@ -258,6 +293,19 @@ get_pubkey( PKT_public_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_byname( PKT_public_cert *pkc, const char *name )
@@ -265,6 +313,60 @@ 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 );
@@ -273,9 +375,14 @@ get_pubkey_byname( PKT_public_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 */
@@ -315,6 +422,33 @@ get_seckey( PKT_secret_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
  */
@@ -343,13 +477,20 @@ get_seckey_byname( PKT_secret_cert *skc, const char *name, int unprotect )
 }
 
 
+
+
+
 /****************
  * 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_public_cert *pkc, u32 *keyid,
              const char *name, const char *filename )
 {
+    compress_filter_context_t cfx;
     int rc=0;
     int found = 0;
     IOBUF a;
@@ -357,11 +498,11 @@ scan_keyring( PKT_public_cert *pkc, u32 *keyid,
     int save_mode;
     u32 akeyid[2];
     PKT_public_cert *last_pk = NULL;
+    int shortkeyid;
 
-    assert( !keyid || !name );
-
-    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 );
@@ -370,11 +511,13 @@ scan_keyring( PKT_public_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 );
 
@@ -387,12 +530,27 @@ scan_keyring( PKT_public_cert *pkc, u32 *keyid,
            log_error("Hmmm, pubkey without an user id in '%s'\n", filename);
            goto leave;
        }
+       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.public_cert, akeyid );
-               if( akeyid[0] == keyid[0] && akeyid[1] == keyid[1] ) {
+               if( (shortkeyid || akeyid[0] == keyid[0])
+                   && akeyid[1] == keyid[1] ) {
                    copy_public_cert( pkc, pkt.pkt.public_cert );
                    found++;
                }
@@ -440,6 +598,7 @@ scan_keyring( PKT_public_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 );
@@ -501,6 +660,7 @@ scan_secret_keyring( PKT_secret_cert *skc, u32 *keyid,
        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:
                if( get_first ) {
                    copy_secret_cert( skc, pkt.pkt.secret_cert );
@@ -556,6 +716,7 @@ scan_secret_keyring( PKT_secret_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 );
@@ -577,6 +738,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.
  */
@@ -591,12 +815,12 @@ 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;
 }