From ed17c7afd07814c8a3315660045420257b57eb1a Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Thu, 6 Sep 2001 17:10:00 +0000 Subject: [PATCH] Revamped the keyring code --- ChangeLog | 4 + INSTALL | 5 + NEWS | 4 + TODO | 4 +- checks/ChangeLog | 4 + checks/genkey1024.test | 114 +-- configure.ac | 39 - doc/ChangeLog | 4 + doc/DETAILS | 4 + doc/gpg.sgml | 2 - doc/gpgv.sgml | 2 - g10/ChangeLog | 54 ++ g10/Makefile.am | 5 +- g10/delkey.c | 43 +- g10/export.c | 43 +- g10/g10.c | 13 +- g10/getkey.c | 709 +++++----------- g10/global.h | 29 + g10/gpgv.c | 4 +- g10/import.c | 124 +-- g10/keydb.c | 623 ++++++++++++++ g10/keydb.h | 85 +- g10/keyedit.c | 74 +- g10/keygen.c | 126 ++- g10/keyid.c | 8 +- g10/keylist.c | 43 +- g10/keyring.c | 1061 ++++++++++++++++++++++++ g10/keyring.h | 44 + g10/keyring.o | Bin 0 -> 56784 bytes g10/mainproc.c | 2 +- g10/options.h | 1 + g10/packet.h | 8 +- g10/parse-packet.c | 19 +- g10/passphrase.c | 4 +- g10/pkclist.c | 44 +- g10/revoke.c | 38 +- g10/ringedit.c | 1796 ---------------------------------------- g10/status.c | 1 + g10/status.h | 2 + g10/tdbdump.c | 2 +- g10/trustdb.c | 26 +- scripts/autogen.sh | 2 +- tools/gpgsplit.c | 1 - util/ChangeLog | 4 + util/miscutil.c | 4 +- 45 files changed, 2464 insertions(+), 2764 deletions(-) create mode 100644 g10/global.h create mode 100644 g10/keydb.c create mode 100644 g10/keyring.c create mode 100644 g10/keyring.h create mode 100644 g10/keyring.o delete mode 100644 g10/ringedit.c diff --git a/ChangeLog b/ChangeLog index ce427a664..e2ae693fd 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,7 @@ +2001-09-03 Werner Koch + + * configure.ac: Removed GDBM tests. + 2001-08-23 Werner Koch * configure.in (AC_FUNC_FSEEKO): Add. diff --git a/INSTALL b/INSTALL index 02510633d..4e443b27a 100644 --- a/INSTALL +++ b/INSTALL @@ -102,6 +102,11 @@ Specific problems on some machines not build. In this case try to run configure using: CFLAGS="-g -O2 -mcpu=powerpc" ./configure + * Compaq C V6.2 for alpha: + + You may want to use the option "-msg-disable ptrmismatch1" + to get rid of the sign/unsigned char mismatch warnings. + The Random Device diff --git a/NEWS b/NEWS index 14fc3e6e2..1de9000b6 100644 --- a/NEWS +++ b/NEWS @@ -33,6 +33,10 @@ UID, encoded using %XX escaping (but with spaces left as spaces, so that it should not break too much) + * Support for GDBM based keyrings has been removed. + + * The entire keyring management has been revamped. + Noteworthy changes in version 1.0.6 (2001-05-29) ------------------------------------------------ diff --git a/TODO b/TODO index fb0e332e6..ed9f2fe2f 100644 --- a/TODO +++ b/TODO @@ -73,11 +73,11 @@ * Concatenated encryption messages don't work corectly - only the first one is processed. - * Add status message for reasons why a key was not selected. - * Add option to put the list of recipients (from the encryption layer) into the signatures notation data. + * Allow to update key signatyres, add status that a key is already signed. + Scheduled for 1.1 ----------------- * export by user-IDs does only export the first matching name which leads diff --git a/checks/ChangeLog b/checks/ChangeLog index b403d324d..48d76fb98 100644 --- a/checks/ChangeLog +++ b/checks/ChangeLog @@ -1,3 +1,7 @@ +2001-09-06 Werner Koch + + * genkey1024.test: Simplified by using a parameter file. + 2001-05-30 Werner Koch * multisig.test (IFS): Reset IFS just before the test. diff --git a/checks/genkey1024.test b/checks/genkey1024.test index 4eb8370ae..e9852d7b6 100755 --- a/checks/genkey1024.test +++ b/checks/genkey1024.test @@ -2,99 +2,25 @@ . $srcdir/defs.inc || exit 3 - -if (expect -v) < /dev/null > /dev/null 2>&1 ; then - : -else - echo "\"expect\" needed but not found - test skipped" - exit 0 -fi - -LANG= -LANGUAGE= - -expect - </dev/null -#set timeout -1 -set timeout 8 -match_max 100000 -spawn ../g10/gpg --no-batch --quick-random --homedir . --gen-key -expect { - -exact "Please select what kind of key you want:\r - (1) DSA and ElGamal (default)\r - (2) DSA (sign only)\r - (4) ElGamal (sign and encrypt)\r -Your selection? " { send -- "1\r" } - timeout { exit 1 } } -expect { - -exact "1\r -\r \rDSA keypair will have 1024 bits.\r -About to generate a new ELG-E keypair.\r - minimum keysize is 768 bits\r - default keysize is 1024 bits\r - highest suggested keysize is 2048 bits\r -What keysize do you want? (1024) " { send -- "\r" } - timeout { exit 1 } } -expect { - -exact "\r -\r \rRequested keysize is 1024 bits\r -Please specify how long the key should be valid.\r - 0 = key does not expire\r - = key expires in n days\r - w = key expires in n weeks\r - m = key expires in n months\r - y = key expires in n years\r -Key is valid for? (0) " { send -- "1\r" } - timeout { exit 1 } } -expect { - -exact "1\r -\r \rKey expires at " { } - timeout { exit 1 } } -expect { - -re "(.*)\r -" {} - timeout { exit 1 } } -expect { - -exact "Is this correct (y/n)? " { send -- "y\r" } - timeout { exit 1 } } -expect { - -exact "y\r -\r \r\r -You need a User-ID to identify your key; the software constructs the user id\r -from Real Name, Comment and Email Address in this form:\r - \"Heinrich Heine (Der Dichter) \"\r -\r -Real name: " { send -- "Harry H.\r" } - timeout { exit 1 } } -expect { - -exact "Harry H.\r -\r \rEmail address: " { send -- "hh@ddorf.de\r" } - timeout { exit 1 } } -expect { - -exact "hh@ddorf.de\r -\r \rComment: " { send -- "a test\r" } - timeout { exit 1 } } -expect { - -exact "a test\r -\r \rYou selected this USER-ID:\r - \"Harry H. (a test) (INSECURE!)\"\r -\r -Change (N)ame, (C)omment, (E)mail or (O)kay/(Q)uit? " { send -- "o\r" } - timeout { exit 1 } } -expect { - -exact "o\r -\r \rYou need a Passphrase to protect your secret key.\r -\r -Enter passphrase: " { sleep 1; send -- "abc\r" } - timeout { exit 1 } } -expect { - -ex "\r \rRepeat passphrase: " { sleep 1; send -- "abc\r" } - timeout { exit 1 } } -set timeout 600 -expect { - -re "^.*\r\npublic and secret key" { exit 0 } - eof { exit 1 } -} -exit 1 +../g10/gpg --quiet --batch --quick-random --homedir . --gen-key < + + * gpg.sgml, gpgv.sgml: Removed GDBM stuff. + 2001-08-29 Werner Koch * faq.raw: Described how to delete a secret key w/o a public key diff --git a/doc/DETAILS b/doc/DETAILS index cc326d80b..807f374d7 100644 --- a/doc/DETAILS +++ b/doc/DETAILS @@ -283,6 +283,10 @@ more arguments in future versions. END_STREAM Issued by pipemode. + INV_RECP + Issued for each unusable recipient. The only reason code + currently defined is 0 for "No specific reason given". + Key generation ============== diff --git a/doc/gpg.sgml b/doc/gpg.sgml index 1a1d71aa7..f27249971 100644 --- a/doc/gpg.sgml +++ b/doc/gpg.sgml @@ -839,8 +839,6 @@ does not contain a slash, it is assumed to be in the home-directory ("~/.gnupg" if --homedir is not used). The filename may be prefixed with a scheme: "gnupg-ring:" is the default one. -"gnupg-gdbm:" may be used for a GDBM ring. Note that GDBM -is experimental and likely to be removed in future versions. It might make sense to use it together with --no-default-keyring. diff --git a/doc/gpgv.sgml b/doc/gpgv.sgml index 757c8fe7c..4119b41dc 100644 --- a/doc/gpgv.sgml +++ b/doc/gpgv.sgml @@ -119,8 +119,6 @@ does not contain a slash, it is assumed to be in the home-directory ("~/.gnupg" if --homedir is not used). The filename may be prefixed with a scheme: "gnupg-ring:" is the default one. -"gnupg-gdbm:" may be used for a GDBM ring. Note that GDBM -is experimental and likely to be removed in future versions. It might make sense to use it together with --no-default-keyring. diff --git a/g10/ChangeLog b/g10/ChangeLog index aca8f7bb3..6a2025723 100644 --- a/g10/ChangeLog +++ b/g10/ChangeLog @@ -1,3 +1,57 @@ +2001-09-06 Werner Koch + + * getkey.c (fixup_uidnode): Features flag is now a bit vector. + * keygen.c (add_feature_mdc): Ditto. + + Revamped the entire key I/O code to be prepared for other ways of + key storages and to get rid of the existing shit. GDBM support has + gone. + * keydb.c: New + * keyring.c, keyring.h: New. + * ringedit.c: Removed. Moved some stuff to keyring.c + * getkey.c: Changed everything related to the key retrieving + functions which are now using the keydb_ functions. + (prepare_search, word_match_chars, word_match) + (prepare_word_match, compare_name): Moved to keyring.c + (get_pubkey_byname): Removed ctx arg and add ret_kdbhd + arg. Changed all callers. + (key_byname): Use get_pubkey_end to release the context and take + new ret_kbdhd arg. Changed all callers. + (classify_user_id2): Fill the 16 byte fingerprint up with 4 null + bytes not with zero bytes of value 4, tsss. + * import.c (import_one): Updated to use the new keydb interface. + (import_secret_one): Ditto. + (import_revoke_cert): Ditto. + * delkey.c (do_delete_key): Ditto. + * keyedit.c (keyedit_menu): Ditto. + (get_keyblock_byname): Removed. + * revoke.c (gen_revoke): Ditto. + * export.c (do_export_stream): Ditto. + * trustdb.c (update_trustdb): Ditto. + * g10.c, gpgv.c (main): Renamed add_keyblock_resource to + keydb_add_resource. + * Makefile.am: Added and removed files. + + * keydb.h: Moved KBNODE typedef and MAX_FINGERPRINT_LEN to + * global.h: this new header. + +2001-09-03 Werner Koch + + * passphrase.c (agent_get_passphrase): Changed nread to size_t. + (passphrase_clear_cache): Ditto. + + * keyid.c (mk_datestr): Avoid trigraphs. + (fingerprint_from_pk): Cache the keyid in the pk. + + * options.h: Add opt.with_fingerprint so that we know whether the + corresponding options was used. + * g10.c (main): Set it here. + * pkclist.c (check_signatures_trust): Always print fingerprint + when this option is used. Mixed a minor memory leak. + + * status.c, status.h: New status INV_RECP. + * pkclist.c (build_pk_list): Issue this status. + 2001-08-31 Werner Koch * parse-packet.c (parse_key,parse_pubkeyenc) diff --git a/g10/Makefile.am b/g10/Makefile.am index f77d8d32a..b7ead7e76 100644 --- a/g10/Makefile.am +++ b/g10/Makefile.am @@ -28,14 +28,15 @@ needed_libs = ../cipher/libcipher.a ../mpi/libmpi.a ../util/libutil.a bin_PROGRAMS = gpg gpgv common_source = \ + global.h \ build-packet.c \ compress.c \ filter.h \ free-packet.c \ getkey.c \ - ringedit.c \ + keydb.c keydb.h \ + keyring.c keyring.h \ seskey.c \ - keydb.h \ kbnode.c \ main.h \ mainproc.c \ diff --git a/g10/delkey.c b/g10/delkey.c index 777d4c174..57ba41b67 100644 --- a/g10/delkey.c +++ b/g10/delkey.c @@ -52,37 +52,35 @@ do_delete_key( const char *username, int secret, int *r_sec_avail ) int rc = 0; KBNODE keyblock = NULL; KBNODE node; - KBPOS kbpos; + KEYDB_HANDLE hd = keydb_new (secret); PKT_public_key *pk = NULL; PKT_secret_key *sk = NULL; u32 keyid[2]; int okay=0; int yes; + KEYDB_SEARCH_DESC desc; *r_sec_avail = 0; - /* search the userid */ - if (secret - && classify_user_id (username, keyid, NULL, NULL, NULL) == 11) { - /* if the user supplied a long keyID we use the direct search - methods which allows us to delete a key if the - corresponding secret key is missing */ - rc = find_secret_keyblock_direct (&kbpos, keyid); - } - else if (secret) - rc = find_secret_keyblock_byname (&kbpos, username); - else - rc = find_keyblock_byname (&kbpos, username); - if( rc ) { - log_error(_("%s: user not found\n"), username ); + /* search the userid */ + memset (&desc, 0, sizeof desc); + desc.mode = classify_user_id (username, + desc.u.kid, + desc.u.fpr, + &desc.u.name, + NULL); + + rc = desc.mode? keydb_search (hd, &desc, 1):G10ERR_INV_USER_ID; + if (rc) { + log_error (_("key `%s' not found: %s\n"), username, g10_errstr (rc)); write_status_text( STATUS_DELETE_PROBLEM, "1" ); goto leave; } /* read the keyblock */ - rc = read_keyblock( &kbpos, &keyblock ); - if( rc ) { - log_error("%s: read problem: %s\n", username, g10_errstr(rc) ); + rc = keydb_get_keyblock (hd, &keyblock ); + if (rc) { + log_error (_("error reading keyblock: %s\n"), g10_errstr(rc) ); goto leave; } @@ -159,15 +157,16 @@ do_delete_key( const char *username, int secret, int *r_sec_avail ) if( okay ) { - rc = delete_keyblock( &kbpos ); - if( rc ) { - log_error("delete_keyblock failed: %s\n", g10_errstr(rc) ); + rc = keydb_delete_keyblock (hd); + if (rc) { + log_error (_("deleting keyblock failed: %s\n"), g10_errstr(rc) ); goto leave; } } leave: - release_kbnode( keyblock ); + keydb_release (hd); + release_kbnode (keyblock); return rc; } diff --git a/g10/export.c b/g10/export.c index 05efef0d0..138701158 100644 --- a/g10/export.c +++ b/g10/export.c @@ -112,58 +112,56 @@ do_export_stream( IOBUF out, STRLIST users, int secret, int onlyrfc, int *any ) PACKET pkt; KBNODE keyblock = NULL; KBNODE kbctx, node; - KBPOS kbpos; + KEYDB_HANDLE kdbhd; STRLIST sl; int all = !users; + int all_first = 1; *any = 0; memset( &zfx, 0, sizeof zfx); init_packet( &pkt ); + kdbhd = keydb_new (secret); + if( opt.compress_keys && opt.compress ) iobuf_push_filter( out, compress_filter, &zfx ); - if( all ) { - rc = enum_keyblocks( secret?5:0, &kbpos, &keyblock ); - if( rc ) { - if( rc != -1 ) - log_error("enum_keyblocks(open) failed: %s\n", g10_errstr(rc) ); - goto leave; - } - all = 2; - } - /* use the correct sequence. strlist_last,prev do work correctly with * NULL pointers :-) */ for( sl=strlist_last(users); sl || all ; sl=strlist_prev( users, sl )) { if( all ) { /* get the next user */ - rc = enum_keyblocks( 1, &kbpos, &keyblock ); + rc = all_first ? keydb_search_first (kdbhd) + : keydb_search_next (kdbhd); + all_first = 0; if( rc == -1 ) /* EOF */ break; if( rc ) { - log_error("enum_keyblocks(read) failed: %s\n", g10_errstr(rc)); + log_error ("error searching key: %s\n", g10_errstr(rc)); break; } } else { - /* search the userid */ - rc = secret? find_secret_keyblock_byname( &kbpos, sl->d ) - : find_keyblock_byname( &kbpos, sl->d ); + KEYDB_SEARCH_DESC desc; + + memset (&desc, 0, sizeof desc); + desc.mode = classify_user_id (sl->d, desc.u.kid, desc.u.fpr, + &desc.u.name, NULL); + rc = desc.mode? keydb_search (kdbhd, &desc, 1):G10ERR_INV_USER_ID; if( rc ) { - log_error(_("%s: user not found: %s\n"), sl->d, g10_errstr(rc)); + log_error (_("key `%s' not found: %s\n"), + sl->d, g10_errstr (rc)); rc = 0; continue; } - /* read the keyblock */ - rc = read_keyblock( &kbpos, &keyblock ); } + /* read the keyblock */ + rc = keydb_get_keyblock (kdbhd, &keyblock ); if( rc ) { - log_error(_("certificate read problem: %s\n"), g10_errstr(rc)); + log_error (_("error reading keyblock: %s\n"), g10_errstr(rc) ); goto leave; } - /* do not export keys which are incompatible with rfc2440 */ if( onlyrfc && (node = find_kbnode( keyblock, PKT_PUBLIC_KEY )) ) { PKT_public_key *pk = node->pkt->pkt.public_key; @@ -229,8 +227,7 @@ do_export_stream( IOBUF out, STRLIST users, int secret, int onlyrfc, int *any ) rc = 0; leave: - if( all == 2 ) - enum_keyblocks( 2, &kbpos, &keyblock ); /* close */ + keydb_release (kdbhd); release_kbnode( keyblock ); if( !*any ) log_info(_("WARNING: nothing exported\n")); diff --git a/g10/g10.c b/g10/g10.c index 7892b3e25..1646853a8 100644 --- a/g10/g10.c +++ b/g10/g10.c @@ -886,7 +886,8 @@ main( int argc, char **argv ) break; #endif /* __riscos__ */ case oWithFingerprint: - with_fpr=1; /*fall thru*/ + opt.with_fingerprint = 1; + with_fpr=1; /*fall thru*/ case oFingerprint: opt.fingerprint++; break; case oSecretKeyring: append_to_strlist( &sec_nrings, pargs.r.ret_str); break; case oOptions: @@ -1225,13 +1226,13 @@ main( int argc, char **argv ) && !(cmd == aKMode && argc == 2 ) ) { if( !sec_nrings || default_keyring ) /* add default secret rings */ - add_keyblock_resource("secring" EXTSEP_S "gpg", 0, 1); + keydb_add_resource ("secring" EXTSEP_S "gpg", 0, 1); for(sl = sec_nrings; sl; sl = sl->next ) - add_keyblock_resource( sl->d, 0, 1 ); + keydb_add_resource ( sl->d, 0, 1 ); if( !nrings || default_keyring ) /* add default ring */ - add_keyblock_resource("pubring" EXTSEP_S "gpg", 0, 0); + keydb_add_resource ("pubring" EXTSEP_S "gpg", 0, 0); for(sl = nrings; sl; sl = sl->next ) - add_keyblock_resource( sl->d, 0, 0 ); + keydb_add_resource ( sl->d, 0, 0 ); } FREE_STRLIST(nrings); FREE_STRLIST(sec_nrings); @@ -1445,7 +1446,7 @@ main( int argc, char **argv ) else { /* add keyring (default keyrings are not registered in this * special case */ - add_keyblock_resource( argv[1], 0, 0 ); + keydb_add_resource( argv[1], 0, 0 ); sl = NULL; if (**argv) add_to_strlist2( &sl, *argv, utf8_strings ); diff --git a/g10/getkey.c b/g10/getkey.c index 8293fc673..5b013efa4 100644 --- a/g10/getkey.c +++ b/g10/getkey.c @@ -34,7 +34,6 @@ #include "trustdb.h" #include "i18n.h" - #define MAX_UNK_CACHE_ENTRIES 1000 /* we use a linked list - so I guess * this is a reasonable limit */ #define MAX_PK_CACHE_ENTRIES 200 @@ -45,60 +44,6 @@ #endif -/* A map of the all characters valid used for word_match() - * Valid characters are in in this table converted to uppercase. - * because the upper 128 bytes have special meaning, we assume - * that they are all valid. - * Note: We must use numerical values here in case that this program - * will be converted to those little blue HAL9000s with their strange - * EBCDIC character set (user ids are UTF-8). - * wk 2000-04-13: Hmmm, does this really make sense, given the fact that - * we can run gpg now on a S/390 running GNU/Linux, where the code - * translation is done by the device drivers? - */ -static const byte word_match_chars[256] = { - /* 00 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - /* 08 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - /* 10 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - /* 18 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - /* 20 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - /* 28 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - /* 30 */ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, - /* 38 */ 0x38, 0x39, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - /* 40 */ 0x00, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, - /* 48 */ 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, - /* 50 */ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, - /* 58 */ 0x58, 0x59, 0x5a, 0x00, 0x00, 0x00, 0x00, 0x00, - /* 60 */ 0x00, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, - /* 68 */ 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, - /* 70 */ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, - /* 78 */ 0x58, 0x59, 0x5a, 0x00, 0x00, 0x00, 0x00, 0x00, - /* 80 */ 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, - /* 88 */ 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, - /* 90 */ 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, - /* 98 */ 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, - /* a0 */ 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, - /* a8 */ 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, - /* b0 */ 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, - /* b8 */ 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, - /* c0 */ 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, - /* c8 */ 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, - /* d0 */ 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, - /* d8 */ 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, - /* e0 */ 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, - /* e8 */ 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, - /* f0 */ 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, - /* f8 */ 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff -}; - -typedef struct { - int mode; - u32 keyid[2]; - byte fprint[20]; - char *namebuf; - const char *name; -} getkey_item_t; - struct getkey_ctx_s { int exact; KBNODE keyblock; @@ -107,10 +52,10 @@ struct getkey_ctx_s { int last_rc; int req_usage; int req_algo; - ulong count; + KEYDB_HANDLE kr_handle; int not_allocated; int nitems; - getkey_item_t items[1]; + KEYDB_SEARCH_DESC items[1]; }; #if 0 @@ -157,11 +102,8 @@ typedef struct user_id_db { static user_id_db_t user_id_db; static int uid_cache_entries; /* number of entries in uid cache */ - - -static char* prepare_word_match( const byte *name ); -static int lookup( GETKEY_CTX ctx, KBNODE *ret_kb, int secmode ); static void merge_selfsigs( KBNODE keyblock ); +static int lookup( GETKEY_CTX ctx, KBNODE *ret_keyblock, int secmode ); #if 0 @@ -426,10 +368,11 @@ get_pubkey( PKT_public_key *pk, u32 *keyid ) memset( &ctx, 0, sizeof ctx ); ctx.exact = 1; /* use the key ID exactly as given */ ctx.not_allocated = 1; + ctx.kr_handle = keydb_new (0); ctx.nitems = 1; - ctx.items[0].mode = 11; - ctx.items[0].keyid[0] = keyid[0]; - ctx.items[0].keyid[1] = keyid[1]; + ctx.items[0].mode = KEYDB_SEARCH_MODE_LONG_KID; + ctx.items[0].u.kid[0] = keyid[0]; + ctx.items[0].u.kid[1] = keyid[1]; ctx.req_algo = pk->req_algo; ctx.req_usage = pk->req_usage; rc = lookup( &ctx, &kb, 0 ); @@ -480,12 +423,13 @@ get_pubkeyblock( u32 *keyid ) KBNODE keyblock = NULL; memset( &ctx, 0, sizeof ctx ); - /* co need to set exact here because we want the entire block */ + /* no need to set exact here because we want the entire block */ ctx.not_allocated = 1; + ctx.kr_handle = keydb_new (0); ctx.nitems = 1; - ctx.items[0].mode = 11; - ctx.items[0].keyid[0] = keyid[0]; - ctx.items[0].keyid[1] = keyid[1]; + ctx.items[0].mode = KEYDB_SEARCH_MODE_LONG_KID; + ctx.items[0].u.kid[0] = keyid[0]; + ctx.items[0].u.kid[1] = keyid[1]; rc = lookup( &ctx, &keyblock, 0 ); get_pubkey_end( &ctx ); @@ -508,10 +452,11 @@ get_seckey( PKT_secret_key *sk, u32 *keyid ) memset( &ctx, 0, sizeof ctx ); ctx.exact = 1; /* use the key ID exactly as given */ ctx.not_allocated = 1; + ctx.kr_handle = keydb_new (1); ctx.nitems = 1; - ctx.items[0].mode = 11; - ctx.items[0].keyid[0] = keyid[0]; - ctx.items[0].keyid[1] = keyid[1]; + ctx.items[0].mode = KEYDB_SEARCH_MODE_LONG_KID; + ctx.items[0].u.kid[0] = keyid[0]; + ctx.items[0].u.kid[1] = keyid[1]; ctx.req_algo = sk->req_algo; ctx.req_usage = sk->req_usage; rc = lookup( &ctx, &kb, 1 ); @@ -542,8 +487,13 @@ get_seckey( PKT_secret_key *sk, u32 *keyid ) int seckey_available( u32 *keyid ) { - KBPOS dummy_kbpos; - return find_secret_keyblock_direct (&dummy_kbpos, keyid)? G10ERR_NO_SECKEY:0; + int rc; + KEYDB_HANDLE hd = keydb_new (1); + rc = keydb_search_kid (hd, keyid); + if ( rc == -1 ) + rc = G10ERR_NO_SECKEY; + keydb_release (hd); + return rc; } @@ -638,36 +588,36 @@ classify_user_id2( const char *name, u32 *keyid, byte *fprint, return 0; case '.': /* an email address, compare from end */ - mode = 5; + mode = KEYDB_SEARCH_MODE_MAILEND; s++; break; case '<': /* an email address */ - mode = 3; + mode = KEYDB_SEARCH_MODE_MAIL; break; case '@': /* part of an email address */ - mode = 4; + mode = KEYDB_SEARCH_MODE_MAILSUB; s++; break; case '=': /* exact compare */ - mode = 1; + mode = KEYDB_SEARCH_MODE_EXACT; s++; break; case '*': /* case insensitive substring search */ - mode = 2; + mode = KEYDB_SEARCH_MODE_SUBSTR; s++; break; case '+': /* compare individual words */ - mode = 6; + mode = KEYDB_SEARCH_MODE_WORDS; s++; break; case '#': /* local user id */ - mode = 12; + mode = KEYDB_SEARCH_MODE_TDBIDX; s++; if (keyid) { if (keyid_from_lid(strtoul(s, NULL, 10), keyid)) @@ -696,7 +646,7 @@ classify_user_id2( const char *name, u32 *keyid, byte *fprint, fprint[i]= 0; } s = se + 1; - mode = 21; + mode = KEYDB_SEARCH_MODE_FPR; } break; @@ -732,7 +682,7 @@ classify_user_id2( const char *name, u32 *keyid, byte *fprint, keyid[0] = 0; keyid[1] = strtoul( s, NULL, 16 ); } - mode = 10; + mode = KEYDB_SEARCH_MODE_SHORT_KID; } else if (hexlength == 16 || (!hexprefix && hexlength == 17 && *s == '0')) { @@ -743,7 +693,7 @@ classify_user_id2( const char *name, u32 *keyid, byte *fprint, mem2str(buf, s, 9 ); keyid[0] = strtoul( buf, NULL, 16 ); keyid[1] = strtoul( s+8, NULL, 16 ); - mode = 11; + mode = KEYDB_SEARCH_MODE_LONG_KID; } else if (hexlength == 32 || (!hexprefix && hexlength == 33 && *s == '0')) { @@ -752,7 +702,7 @@ classify_user_id2( const char *name, u32 *keyid, byte *fprint, if (hexlength == 33) s++; if (fprint) { - memset(fprint+16, 4, 0); + memset(fprint+16, 0, 4); for (i=0; i < 16; i++, s+=2) { int c = hextobyte(s); if (c == -1) @@ -760,7 +710,7 @@ classify_user_id2( const char *name, u32 *keyid, byte *fprint, fprint[i] = c; } } - mode = 16; + mode = KEYDB_SEARCH_MODE_FPR16; } else if (hexlength == 40 || (!hexprefix && hexlength == 41 && *s == '0')) { @@ -776,14 +726,14 @@ classify_user_id2( const char *name, u32 *keyid, byte *fprint, fprint[i] = c; } } - mode = 20; + mode = KEYDB_SEARCH_MODE_FPR20; } else { if (hexprefix) /* This was a hex number with a prefix */ return 0; /* and a wrong length */ *force_exact = 0; - mode = 2; /* Default is case insensitive substring search */ + mode = KEYDB_SEARCH_MODE_SUBSTR; /* default mode */ } } @@ -815,7 +765,7 @@ classify_user_id( const char *name, u32 *keyid, byte *fprint, static int key_byname( GETKEY_CTX *retctx, STRLIST namelist, PKT_public_key *pk, PKT_secret_key *sk, int secmode, - KBNODE *ret_kb ) + KBNODE *ret_kb, KEYDB_HANDLE *ret_kdbhd ) { int rc = 0; int n; @@ -824,24 +774,27 @@ key_byname( GETKEY_CTX *retctx, STRLIST namelist, KBNODE help_kb = NULL; int exact; - if( retctx ) /* reset the returned context in case of error */ + if( retctx ) {/* reset the returned context in case of error */ + assert (!ret_kdbhd); /* not allowed because the handle is + stored in the context */ *retctx = NULL; + } + if (ret_kdbhd) + *ret_kdbhd = NULL; /* build the search context */ - /* Performance hint: Use a static buffer if there is only one name */ - /* and we don't have mode 6 */ for(n=0, r=namelist; r; r = r->next ) n++; - ctx = m_alloc_clear( sizeof *ctx + (n-1)*sizeof ctx->items ); + ctx = m_alloc_clear (sizeof *ctx + (n-1)*sizeof ctx->items ); ctx->nitems = n; for(n=0, r=namelist; r; r = r->next, n++ ) { int mode = classify_user_id2 ( r->d, - ctx->items[n].keyid, - ctx->items[n].fprint, - &ctx->items[n].name, + ctx->items[n].u.kid, + ctx->items[n].u.fpr, + &ctx->items[n].u.name, NULL, &exact ); - + if ( exact ) ctx->exact = 1; ctx->items[n].mode = mode; @@ -849,14 +802,9 @@ key_byname( GETKEY_CTX *retctx, STRLIST namelist, m_free( ctx ); return G10ERR_INV_USER_ID; } - if( ctx->items[n].mode == 6 ) { - ctx->items[n].namebuf = prepare_word_match(ctx->items[n].name); - ctx->items[n].name = ctx->items[n].namebuf; - } } - - + ctx->kr_handle = keydb_new (secmode); if ( !ret_kb ) ret_kb = &help_kb; @@ -883,29 +831,34 @@ key_byname( GETKEY_CTX *retctx, STRLIST namelist, release_kbnode ( help_kb ); - if( retctx ) /* caller wants the context */ + if (retctx) /* caller wants the context */ *retctx = ctx; else { - /* Hmmm, why not get_pubkey-end here?? */ - enum_keyblocks( 2, &ctx->kbpos, NULL ); - memset (&ctx->kbpos, 0, sizeof ctx->kbpos); - for(n=0; n < ctx->nitems; n++ ) - m_free( ctx->items[n].namebuf ); - m_free( ctx ); + if (ret_kdbhd) { + *ret_kdbhd = ctx->kr_handle; + ctx->kr_handle = NULL; + } + get_pubkey_end (ctx); } return rc; } +/* + * Find a public key from NAME and returh the keyblock or the key. + * If ret_kdb is not NULL, the KEYDB handle used to locate this keyblock is + * returned and the caller is responsible for closing it. + */ int -get_pubkey_byname( GETKEY_CTX *retctx, PKT_public_key *pk, - const char *name, KBNODE *ret_keyblock ) +get_pubkey_byname (PKT_public_key *pk, + const char *name, KBNODE *ret_keyblock, + KEYDB_HANDLE *ret_kdbhd ) { int rc; STRLIST namelist = NULL; add_to_strlist( &namelist, name ); - rc = key_byname( retctx, namelist, pk, NULL, 0, ret_keyblock ); + rc = key_byname( NULL, namelist, pk, NULL, 0, ret_keyblock, ret_kdbhd); free_strlist( namelist ); return rc; } @@ -914,7 +867,7 @@ int get_pubkey_bynames( GETKEY_CTX *retctx, PKT_public_key *pk, STRLIST names, KBNODE *ret_keyblock ) { - return key_byname( retctx, names, pk, NULL, 0, ret_keyblock ); + return key_byname( retctx, names, pk, NULL, 0, ret_keyblock, NULL); } int @@ -934,12 +887,8 @@ void get_pubkey_end( GETKEY_CTX ctx ) { if( ctx ) { - int n; - - enum_keyblocks( 2, &ctx->kbpos, NULL ); memset (&ctx->kbpos, 0, sizeof ctx->kbpos); - for(n=0; n < ctx->nitems; n++ ) - m_free( ctx->items[n].namebuf ); + keydb_release (ctx->kr_handle); if( !ctx->not_allocated ) m_free( ctx ); } @@ -967,9 +916,10 @@ get_pubkey_byfprint( PKT_public_key *pk, memset( &ctx, 0, sizeof ctx ); ctx.exact = 1 ; ctx.not_allocated = 1; + ctx.kr_handle = keydb_new (0); ctx.nitems = 1; ctx.items[0].mode = fprint_len; - memcpy( ctx.items[0].fprint, fprint, fprint_len ); + memcpy( ctx.items[0].u.fpr, fprint, fprint_len ); rc = lookup( &ctx, &kb, 0 ); if (!rc && pk ) pk_from_block ( &ctx, pk, kb ); @@ -996,9 +946,11 @@ get_keyblock_byfprint( KBNODE *ret_keyblock, const byte *fprint, memset( &ctx, 0, sizeof ctx ); ctx.not_allocated = 1; + ctx.kr_handle = keydb_new (0); ctx.nitems = 1; - ctx.items[0].mode = fprint_len; - memcpy( ctx.items[0].fprint, fprint, fprint_len ); + ctx.items[0].mode = fprint_len==16? KEYDB_SEARCH_MODE_FPR16 + : KEYDB_SEARCH_MODE_FPR20; + memcpy( ctx.items[0].u.fpr, fprint, fprint_len ); rc = lookup( &ctx, ret_keyblock, 0 ); get_pubkey_end( &ctx ); } @@ -1025,10 +977,11 @@ get_keyblock_bylid( KBNODE *ret_keyblock, ulong lid ) memset( &ctx, 0, sizeof ctx ); ctx.exact = 1; ctx.not_allocated = 1; + ctx.kr_handle = keydb_new (0); ctx.nitems = 1; - ctx.items[0].mode = 12; - ctx.items[0].keyid[0] = kid[0]; - ctx.items[0].keyid[1] = kid[1]; + ctx.items[0].mode = KEYDB_SEARCH_MODE_LONG_KID; + ctx.items[0].u.kid[0] = kid[0]; + ctx.items[0].u.kid[1] = kid[1]; rc = lookup( &ctx, ret_keyblock, 0 ); get_pubkey_end( &ctx ); @@ -1053,7 +1006,7 @@ get_seckey_byname2( GETKEY_CTX *retctx, if( !name && opt.def_secret_key && *opt.def_secret_key ) { add_to_strlist( &namelist, opt.def_secret_key ); - rc = key_byname( retctx, namelist, NULL, sk, 1, retblock ); + rc = key_byname( retctx, namelist, NULL, sk, 1, retblock, NULL ); } else if( !name ) { /* use the first one as default key */ struct getkey_ctx_s ctx; @@ -1063,8 +1016,9 @@ get_seckey_byname2( GETKEY_CTX *retctx, assert (!retblock); memset( &ctx, 0, sizeof ctx ); ctx.not_allocated = 1; + ctx.kr_handle = keydb_new (1); ctx.nitems = 1; - ctx.items[0].mode = 15; + ctx.items[0].mode = KEYDB_SEARCH_MODE_FIRST; rc = lookup( &ctx, &kb, 1 ); if (!rc && sk ) sk_from_block ( &ctx, sk, kb ); @@ -1073,7 +1027,7 @@ get_seckey_byname2( GETKEY_CTX *retctx, } else { add_to_strlist( &namelist, name ); - rc = key_byname( retctx, namelist, NULL, sk, 1, retblock ); + rc = key_byname( retctx, namelist, NULL, sk, 1, retblock, NULL ); } free_strlist( namelist ); @@ -1095,7 +1049,7 @@ int get_seckey_bynames( GETKEY_CTX *retctx, PKT_secret_key *sk, STRLIST names, KBNODE *ret_keyblock ) { - return key_byname( retctx, names, NULL, sk, 1, ret_keyblock ); + return key_byname( retctx, names, NULL, sk, 1, ret_keyblock, NULL ); } @@ -1119,150 +1073,6 @@ get_seckey_end( GETKEY_CTX ctx ) } - -/******************************************************* - ************** compare functions ********************** - *******************************************************/ - -/**************** - * Do a word match (original user id starts with a '+'). - * The pattern is already tokenized to a more suitable format: - * There are only the real words in it delimited by one space - * and all converted to uppercase. - * - * Returns: 0 if all words match. - * - * Note: This algorithm is a straightforward one and not very - * fast. It works for UTF-8 strings. The uidlen should - * be removed but due to the fact that old versions of - * pgp don't use UTF-8 we still use the length; this should - * be fixed in parse-packet (and replace \0 by some special - * UTF-8 encoding) - */ -static int -word_match( const byte *uid, size_t uidlen, const byte *pattern ) -{ - size_t wlen, n; - const byte *p; - const byte *s; - - for( s=pattern; *s; ) { - do { - /* skip leading delimiters */ - while( uidlen && !word_match_chars[*uid] ) - uid++, uidlen--; - /* get length of the word */ - n = uidlen; p = uid; - while( n && word_match_chars[*p] ) - p++, n--; - wlen = p - uid; - /* and compare against the current word from pattern */ - for(n=0, p=uid; n < wlen && s[n] != ' ' && s[n] ; n++, p++ ) { - if( word_match_chars[*p] != s[n] ) - break; - } - if( n == wlen && (s[n] == ' ' || !s[n]) ) - break; /* found */ - uid += wlen; - uidlen -= wlen; - } while( uidlen ); - if( !uidlen ) - return -1; /* not found */ - - /* advance to next word in pattern */ - for(; *s != ' ' && *s ; s++ ) - ; - if( *s ) - s++ ; - } - return 0; /* found */ -} - -/**************** - * prepare word word_match; that is parse the name and - * build the pattern. - * caller has to free the returned pattern - */ -static char* -prepare_word_match( const byte *name ) -{ - byte *pattern, *p; - int c; - - /* the original length is always enough for the pattern */ - p = pattern = m_alloc(strlen(name)+1); - do { - /* skip leading delimiters */ - while( *name && !word_match_chars[*name] ) - name++; - /* copy as long as we don't have a delimiter and convert - * to uppercase. - * fixme: how can we handle utf8 uppercasing */ - for( ; *name && (c=word_match_chars[*name]); name++ ) - *p++ = c; - *p++ = ' '; /* append pattern delimiter */ - } while( *name ); - p[-1] = 0; /* replace last pattern delimiter by EOS */ - - return pattern; -} - - - - - -static int -compare_name( const char *uid, size_t uidlen, const char *name, int mode ) -{ - int i; - const char *s, *se; - - if( mode == 1 ) { /* exact match */ - for(i=0; name[i] && uidlen; i++, uidlen-- ) - if( uid[i] != name[i] ) - break; - if( !uidlen && !name[i] ) - return 0; /* found */ - } - else if( mode == 2 ) { /* case insensitive substring */ - if( ascii_memistr( uid, uidlen, name ) ) - return 0; - } - else if( mode >= 3 && mode <= 5 ) { /* look at the email address */ - for( i=0, s= uid; i < uidlen && *s != '<'; s++, i++ ) - ; - if( i < uidlen ) { - /* skip opening delim and one char and look for the closing one*/ - s++; i++; - for( se=s+1, i++; i < uidlen && *se != '>'; se++, i++ ) - ; - if( i < uidlen ) { - i = se - s; - if( mode == 3 ) { /* exact email address */ - if( strlen(name)-2 == i - && !ascii_memcasecmp( s, name+1, i) ) - return 0; - } - else if( mode == 4 ) { /* email substring */ - if( ascii_memistr( s, i, name ) ) - return 0; - } - else { /* email from end */ - /* nyi */ - } - } - } - } - else if( mode == 6 ) - return word_match( uid, uidlen, name ); - else - BUG(); - - return -1; /* not found */ -} - - - /************************************************ ************* Merging stuff ******************** @@ -1446,14 +1256,8 @@ fixup_uidnode ( KBNODE uidnode, KBNODE signode, u32 keycreated ) /* see whether we have the MDC feature */ uid->mdc_feature = 0; p = parse_sig_subpkt (sig->hashed, SIGSUBPKT_FEATURES, &n); - if (!p) - n=0; - for (; n; n--, p++) { - if (*p == 1) { - uid->mdc_feature = 1; - break; - } - } + if (p && n && (p[0] & 0x01)) + uid->mdc_feature = 1; } @@ -1936,8 +1740,9 @@ merge_public_with_secret ( KBNODE pubblock, KBNODE secblock ) /* This function checks that for every public subkey a corresponding * secret subkey is avalable and deletes the public subkey otherwise. - * We need this function becuase we can'tdelete it later when we + * We need this function because we can't delete it later when we * actually merge the secret parts into the pubring. + & The function also plays some games with the node flags. */ static void premerge_public_with_secret ( KBNODE pubblock, KBNODE secblock ) @@ -1948,6 +1753,7 @@ premerge_public_with_secret ( KBNODE pubblock, KBNODE secblock ) assert ( secblock->pkt->pkttype == PKT_SECRET_KEY ); for (pub=pubblock,last=NULL; pub; last = pub, pub = pub->next ) { + pub->flag &= ~3; /* reset bits 0 and 1 */ if ( pub->pkt->pkttype == PKT_PUBLIC_SUBKEY ) { KBNODE sec; PKT_public_key *pk = pub->pkt->pkt.public_key; @@ -1962,6 +1768,8 @@ premerge_public_with_secret ( KBNODE pubblock, KBNODE secblock ) Fix the pubkey usage */ pk->pubkey_usage &= ~PUBKEY_USAGE_SIG; } + /* transfer flag bits 0 and 1 to the pubblock */ + pub->flag |= (sec->flag &3); break; } } @@ -1994,80 +1802,6 @@ premerge_public_with_secret ( KBNODE pubblock, KBNODE secblock ) -/************************************************ - ************* Find stuff *********************** - ************************************************/ - -static PKT_user_id * -find_by_name( KBNODE keyblock, const char *name, int mode ) -{ - KBNODE k; - - for(k=keyblock; k; k = k->next ) { - if( k->pkt->pkttype == PKT_USER_ID - && !compare_name( k->pkt->pkt.user_id->name, - k->pkt->pkt.user_id->len, name, mode)) { - return k->pkt->pkt.user_id; - } - } - - return NULL; -} - - - -static KBNODE -find_by_keyid( KBNODE keyblock, u32 *keyid, int mode ) -{ - KBNODE k; - - for(k=keyblock; k; k = k->next ) { - if( k->pkt->pkttype == PKT_PUBLIC_KEY - || k->pkt->pkttype == PKT_PUBLIC_SUBKEY ) { - u32 aki[2]; - keyid_from_pk( k->pkt->pkt.public_key, aki ); - if( aki[1] == keyid[1] && ( mode == 10 || aki[0] == keyid[0] ) ) { - return k; /* found */ - } - } - } - return NULL; -} - - - -static KBNODE -find_by_fpr( KBNODE keyblock, const char *name, int mode ) -{ - KBNODE k; - - for(k=keyblock; k; k = k->next ) { - if( k->pkt->pkttype == PKT_PUBLIC_KEY - || k->pkt->pkttype == PKT_PUBLIC_SUBKEY ) { - byte afp[MAX_FINGERPRINT_LEN]; - size_t an; - - fingerprint_from_pk(k->pkt->pkt.public_key, afp, &an ); - if ( mode == 21 ) { - /* Unified fingerprint. The fingerprint is always 20 bytes*/ - while ( an < 20 ) - afp[an++] = 0; - if ( !memcmp( afp, name, 20 ) ) - return k; - } - else { - if( an == mode && !memcmp( afp, name, an) ) { - return k; - } - } - } - } - return NULL; -} - - - - /* See see whether the key fits * our requirements and in case we do not * request the primary key, we should select @@ -2098,24 +1832,40 @@ find_by_fpr( KBNODE keyblock, const char *name, int mode ) */ static int -finish_lookup( GETKEY_CTX ctx, KBNODE foundk, PKT_user_id *foundu ) +finish_lookup (GETKEY_CTX ctx) { KBNODE keyblock = ctx->keyblock; KBNODE k; + KBNODE foundk = NULL; + PKT_user_id *foundu = NULL; #define USAGE_MASK (PUBKEY_USAGE_SIG|PUBKEY_USAGE_ENC) unsigned int req_usage = ( ctx->req_usage & USAGE_MASK ); u32 latest_date; KBNODE latest_key; u32 curtime = make_timestamp (); - assert( !foundk || foundk->pkt->pkttype == PKT_PUBLIC_KEY - || foundk->pkt->pkttype == PKT_PUBLIC_SUBKEY ); assert( keyblock->pkt->pkttype == PKT_PUBLIC_KEY ); ctx->found_key = NULL; - if (!ctx->exact) - foundk = NULL; + if (ctx->exact) { + for (k=keyblock; k; k = k->next) { + if ( (k->flag & 1) ) { + assert ( k->pkt->pkttype == PKT_PUBLIC_KEY + || k->pkt->pkttype == PKT_PUBLIC_SUBKEY ); + foundk = k; + break; + } + } + } + + for (k=keyblock; k; k = k->next) { + if ( (k->flag & 2) ) { + assert (k->pkt->pkttype == PKT_USER_ID); + foundu = k->pkt->pkt.user_id; + break; + } + } if ( DBG_CACHE ) log_debug( "finish_lookup: checking key %08lX (%s)(req_usage=%x)\n", @@ -2255,105 +2005,76 @@ finish_lookup( GETKEY_CTX ctx, KBNODE foundk, PKT_user_id *foundu ) return 1; /* found */ } - + static int lookup( GETKEY_CTX ctx, KBNODE *ret_keyblock, int secmode ) { int rc; - int oldmode = set_packet_list_mode(0); KBNODE secblock = NULL; /* helper */ int no_suitable_key = 0; - - if( !ctx->count ) /* first time */ - rc = enum_keyblocks( secmode? 5:0, &ctx->kbpos, NULL ); - else - rc = 0; - if( !rc ) { - while( !(rc = enum_keyblocks( 1, &ctx->kbpos, &ctx->keyblock )) ) { - int n; - getkey_item_t *item; - - if ( secmode ) { - /* find the correspondig public key and use this - * this one for the selection process */ - u32 aki[2]; - KBNODE k = ctx->keyblock; - - if ( k->pkt->pkttype != PKT_SECRET_KEY ) - BUG(); - keyid_from_sk( k->pkt->pkt.secret_key, aki ); - k = get_pubkeyblock( aki ); - if( !k ) { - if (!opt.quiet) - log_info(_("key %08lX: secret key without public key " - "- skipped\n"), (ulong)aki[1] ); - goto skip; - } - secblock = ctx->keyblock; - ctx->keyblock = k; - premerge_public_with_secret ( ctx->keyblock, secblock ); - } - - - /* loop over all the user ids we want to look for */ - item = ctx->items; - for(n=0; n < ctx->nitems; n++, item++ ) { - KBNODE k = NULL; - int found = 0; - PKT_user_id *found_uid = NULL; - if( item->mode < 10 ) { - found_uid = find_by_name( ctx->keyblock, - item->name, item->mode ); - found = !!found_uid; - } - else if( item->mode >= 10 && item->mode <= 12 ) { - k = find_by_keyid( ctx->keyblock, - item->keyid, item->mode ); - found = !!k; - } - else if( item->mode == 15 ) { - found = 1; - } - else if( item->mode == 16 || item->mode == 20 - || item->mode == 21 ) { - k = find_by_fpr( ctx->keyblock, - item->fprint, item->mode ); - found = !!k; - } - else - BUG(); - if( found ) { - /* this keyblock looks fine - do further investigation */ - merge_selfsigs ( ctx->keyblock ); - if ( finish_lookup( ctx, k, found_uid ) ) { - no_suitable_key = 0; - if ( secmode ) { - merge_public_with_secret ( ctx->keyblock, - secblock); - release_kbnode (secblock); - secblock = NULL; - } - goto found; - } - else - no_suitable_key = 1; - } - } - skip: - /* release resources and try the next keyblock */ + rc = 0; + while (!(rc = keydb_search (ctx->kr_handle, ctx->items, ctx->nitems))) { + rc = keydb_get_keyblock (ctx->kr_handle, &ctx->keyblock); + if (rc) { + log_error ("keydb_get_keyblock failed: %s\n", g10_errstr(rc)); + rc = 0; + goto skip; + } + + if ( secmode ) { + /* find the correspondig public key and use this + * this one for the selection process */ + u32 aki[2]; + KBNODE k = ctx->keyblock; + + if (k->pkt->pkttype != PKT_SECRET_KEY) + BUG(); + + keyid_from_sk (k->pkt->pkt.secret_key, aki); + k = get_pubkeyblock (aki); + if( !k ) { + if (!opt.quiet) + log_info(_("key %08lX: secret key without public key " + "- skipped\n"), (ulong)aki[1] ); + goto skip; + } + secblock = ctx->keyblock; + ctx->keyblock = k; + + premerge_public_with_secret ( ctx->keyblock, secblock ); + } + + /* warning: node flag bits 0 and 1 should be preserved by + * merge_selfsigs. For secret keys, premerge did tranfer the + * keys to the keyblock */ + merge_selfsigs ( ctx->keyblock ); + if ( finish_lookup (ctx) ) { + no_suitable_key = 0; if ( secmode ) { - release_kbnode( secblock ); + merge_public_with_secret ( ctx->keyblock, + secblock); + release_kbnode (secblock); secblock = NULL; } - release_kbnode( ctx->keyblock ); - ctx->keyblock = NULL; - } - found: - ; + goto found; + } + else + no_suitable_key = 1; + + skip: + /* release resources and continue search */ + if ( secmode ) { + release_kbnode( secblock ); + secblock = NULL; + } + release_kbnode( ctx->keyblock ); + ctx->keyblock = NULL; } + + found: if( rc && rc != -1 ) - log_error("enum_keyblocks failed: %s\n", g10_errstr(rc)); + log_error("enum_keyblocks_read failed: %s\n", g10_errstr(rc)); if( !rc ) { *ret_keyblock = ctx->keyblock; /* return the keyblock */ @@ -2370,29 +2091,8 @@ lookup( GETKEY_CTX ctx, KBNODE *ret_keyblock, int secmode ) } release_kbnode( ctx->keyblock ); ctx->keyblock = NULL; - set_packet_list_mode(oldmode); - #if 0 - if( opt.debug & DBG_MEMSTAT_VALUE ) { - static int initialized; - - if( !initialized ) { - initialized = 1; - atexit( print_stats ); - } - - assert( ctx->mode < DIM(lkup_stats) ); - lkup_stats[ctx->mode].any = 1; - if( !rc ) - lkup_stats[ctx->mode].okay_count++; - else if ( rc == G10ERR_NO_PUBKEY || rc == G10ERR_NO_SECKEY ) - lkup_stats[ctx->mode].nokey_count++; - else - lkup_stats[ctx->mode].error_count++; - } - #endif ctx->last_rc = rc; - ctx->count++; return rc; } @@ -2414,33 +2114,32 @@ lookup( GETKEY_CTX ctx, KBNODE *ret_keyblock, int secmode ) * to indicate EOF. * 4) Always call this function a last time with SK set to NULL, * so that can free it's context. - * - * */ int enum_secret_keys( void **context, PKT_secret_key *sk, int with_subkeys ) { int rc=0; - PACKET pkt; - int save_mode; struct { int eof; - int sequence; - const char *name; - IOBUF iobuf; + int first; + KEYDB_HANDLE hd; + KBNODE keyblock; + KBNODE node; } *c = *context; if( !c ) { /* make a new context */ c = m_alloc_clear( sizeof *c ); *context = c; - c->sequence = 0; - c->name = enum_keyblock_resources( &c->sequence, 1 ); + c->hd = keydb_new (1); + c->first = 1; + c->keyblock = NULL; + c->node = NULL; } if( !sk ) { /* free the context */ - if( c->iobuf ) - iobuf_close(c->iobuf); + keydb_release (c->hd); + release_kbnode (c->keyblock); m_free( c ); *context = NULL; return 0; @@ -2449,33 +2148,33 @@ enum_secret_keys( void **context, PKT_secret_key *sk, int with_subkeys ) if( c->eof ) return -1; - /* FIXME: This assumes a plain keyring file */ - for( ; c->name; c->name = enum_keyblock_resources( &c->sequence, 1 ) ) { - if( !c->iobuf ) { - if( !(c->iobuf = iobuf_open( c->name ) ) ) { - log_error("enum_secret_keys: can't open `%s'\n", c->name ); - continue; /* try next file */ - } - } + do { + /* get the next secret key from the current keyblock */ + for (; c->node; c->node = c->node->next) { + if (c->node->pkt->pkttype == PKT_SECRET_KEY + || (with_subkeys + && c->node->pkt->pkttype == PKT_SECRET_SUBKEY) ) { + copy_secret_key (sk, c->node->pkt->pkt.secret_key ); + c->node = c->node->next; + return 0; /* found */ + } + } + release_kbnode (c->keyblock); + c->keyblock = c->node = NULL; + + rc = c->first? keydb_search_first (c->hd) : keydb_search_next (c->hd); + c->first = 0; + if (rc) { + keydb_release (c->hd); c->hd = NULL; + c->eof = 1; + return -1; /* eof */ + } + + rc = keydb_get_keyblock (c->hd, &c->keyblock); + c->node = c->keyblock; + } while (!rc); - 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_KEY - || ( with_subkeys && pkt.pkttype == PKT_SECRET_SUBKEY ) ) { - copy_secret_key( sk, pkt.pkt.secret_key ); - 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 rc; /* error */ } diff --git a/g10/global.h b/g10/global.h new file mode 100644 index 000000000..3c4e59ec4 --- /dev/null +++ b/g10/global.h @@ -0,0 +1,29 @@ +/* global.h - Local typedefs and constants + * Copyright (C) 2001 Free Software Foundation, Inc. + * + * This file is part of GnuPG. + * + * 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. + * + * 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. + * + * 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 + */ + +#ifndef GPG_GLOBAL_H +#define GPG_GLOBAL_H + +#define MAX_FINGERPRINT_LEN 20 + +typedef struct kbnode_struct *KBNODE; +typedef struct keydb_search_desc KEYDB_SEARCH_DESC; + +#endif /*GPG_GLOBAL_H*/ diff --git a/g10/gpgv.c b/g10/gpgv.c index 64dcfe64b..7d0ecc86f 100644 --- a/g10/gpgv.c +++ b/g10/gpgv.c @@ -189,9 +189,9 @@ main( int argc, char **argv ) set_packet_list_mode(1); if( !nrings ) /* no keyring given: use default one */ - add_keyblock_resource("trustedkeys" EXTSEP_S "gpg", 0, 0); + keydb_add_resource ("trustedkeys" EXTSEP_S "gpg", 0, 0); for(sl = nrings; sl; sl = sl->next ) - add_keyblock_resource( sl->d, 0, 0 ); + keydb_add_resource (sl->d, 0, 0 ); FREE_STRLIST(nrings); diff --git a/g10/import.c b/g10/import.c index c3a35aa8a..88abd30c1 100644 --- a/g10/import.c +++ b/g10/import.c @@ -379,7 +379,6 @@ import_one( const char *fname, KBNODE keyblock, int fast ) PKT_public_key *pk_orig; KBNODE node, uidnode; KBNODE keyblock_orig = NULL; - KBPOS kbpos; u32 keyid[2]; int rc = 0; int new_key = 0; @@ -443,21 +442,20 @@ import_one( const char *fname, KBNODE keyblock, int fast ) stats.skipped_new_keys++; } else if( rc ) { /* insert this key */ - /* get default resource */ - if( get_keyblock_handle( NULL, 0, &kbpos ) ) { - log_error(_("no default public keyring\n")); + KEYDB_HANDLE hd = keydb_new (0); + + rc = keydb_locate_writable (hd, NULL); + if (rc) { + log_error (_("no writable keyring found: %s\n"), g10_errstr (rc)); return G10ERR_GENERAL; } if( opt.verbose > 1 ) - log_info( _("writing to `%s'\n"), - keyblock_resource_name(&kbpos) ); - if( (rc=lock_keyblock( &kbpos )) ) - log_error(_("can't lock keyring `%s': %s\n"), - keyblock_resource_name(&kbpos), g10_errstr(rc) ); - else if( (rc=insert_keyblock( &kbpos, keyblock )) ) - log_error( _("error writing keyring `%s': %s\n"), - keyblock_resource_name(&kbpos), g10_errstr(rc) ); - unlock_keyblock( &kbpos ); + log_info (_("writing to `%s'\n"), keydb_get_resource_name (hd) ); + rc = keydb_insert_keyblock (hd, keyblock ); + if (rc) + log_error (_("error writing keyring `%s': %s\n"), + keydb_get_resource_name (hd), g10_errstr(rc)); + keydb_release (hd); /* we are ready */ if( !opt.quiet ) log_info( _("key %08lX: public key imported\n"), (ulong)keyid[1]); @@ -472,8 +470,10 @@ import_one( const char *fname, KBNODE keyblock, int fast ) new_key = 1; } else { /* merge */ + KEYDB_HANDLE hd; int n_uids, n_sigs, n_subk; + /* Compare the original against the new key; just to be sure nothing * weird is going on */ if( cmp_public_keys( pk_orig, pk ) ) { @@ -484,16 +484,27 @@ import_one( const char *fname, KBNODE keyblock, int fast ) } /* now read the original keyblock */ - rc = find_keyblock_bypk( &kbpos, pk_orig ); + hd = keydb_new (0); + { + byte afp[MAX_FINGERPRINT_LEN]; + size_t an; + + fingerprint_from_pk (pk_orig, afp, &an); + while (an < MAX_FINGERPRINT_LEN) + afp[an++] = 0; + rc = keydb_search_fpr (hd, afp); + } if( rc ) { - log_error( _("key %08lX: can't locate original keyblock: %s\n"), + log_error (_("key %08lX: can't locate original keyblock: %s\n"), (ulong)keyid[1], g10_errstr(rc)); + keydb_release (hd); goto leave; } - rc = read_keyblock( &kbpos, &keyblock_orig ); - if( rc ) { - log_error( _("key %08lX: can't read original keyblock: %s\n"), + rc = keydb_get_keyblock (hd, &keyblock_orig ); + if (rc) { + log_error (_("key %08lX: can't read original keyblock: %s\n"), (ulong)keyid[1], g10_errstr(rc)); + keydb_release (hd); goto leave; } @@ -504,18 +515,18 @@ import_one( const char *fname, KBNODE keyblock, int fast ) n_uids = n_sigs = n_subk = 0; rc = merge_blocks( fname, keyblock_orig, keyblock, keyid, &n_uids, &n_sigs, &n_subk ); - if( rc ) + if( rc ) { + keydb_release (hd); goto leave; + } if( n_uids || n_sigs || n_subk ) { mod_key = 1; /* keyblock_orig has been updated; write */ - if( (rc=lock_keyblock( &kbpos )) ) - log_error( _("can't lock keyring `%s': %s\n"), - keyblock_resource_name(&kbpos), g10_errstr(rc) ); - else if( (rc=update_keyblock( &kbpos, keyblock_orig )) ) - log_error( _("error writing keyring `%s': %s\n"), - keyblock_resource_name(&kbpos), g10_errstr(rc) ); - unlock_keyblock( &kbpos ); + rc = keydb_update_keyblock (hd, keyblock_orig); + if (rc) + log_error (_("error writing keyring `%s': %s\n"), + keydb_get_resource_name (hd), g10_errstr(rc) ); + keydb_release (hd); hd = NULL; /* we are ready */ if( !opt.quiet ) { if( n_uids == 1 ) @@ -582,7 +593,6 @@ import_secret_one( const char *fname, KBNODE keyblock, int allow ) { PKT_secret_key *sk; KBNODE node, uidnode; - KBPOS kbpos; u32 keyid[2]; int rc = 0; @@ -623,18 +633,20 @@ import_secret_one( const char *fname, KBNODE keyblock, int allow ) /* do we have this key already in one of our secrings ? */ rc = seckey_available( keyid ); if( rc == G10ERR_NO_SECKEY && !opt.merge_only ) { /* simply insert this key */ + KEYDB_HANDLE hd = keydb_new (1); + /* get default resource */ - if( get_keyblock_handle( NULL, 1, &kbpos ) ) { - log_error("no default secret keyring\n"); + rc = keydb_locate_writable (hd, NULL); + if (rc) { + log_error (_("no default secret keyring: %s\n"), g10_errstr (rc)); + keydb_release (hd); return G10ERR_GENERAL; } - if( (rc=lock_keyblock( &kbpos )) ) - log_error( _("can't lock keyring `%s': %s\n"), - keyblock_resource_name(&kbpos), g10_errstr(rc) ); - else if( (rc=insert_keyblock( &kbpos, keyblock )) ) - log_error( _("error writing keyring `%s': %s\n"), - keyblock_resource_name(&kbpos), g10_errstr(rc) ); - unlock_keyblock( &kbpos ); + rc = keydb_insert_keyblock (hd, keyblock ); + if (rc) + log_error (_("error writing keyring `%s': %s\n"), + keydb_get_resource_name (hd), g10_errstr(rc) ); + keydb_release (hd); /* we are ready */ if( !opt.quiet ) log_info( _("key %08lX: secret key imported\n"), (ulong)keyid[1]); @@ -661,7 +673,7 @@ import_revoke_cert( const char *fname, KBNODE node ) { PKT_public_key *pk=NULL; KBNODE onode, keyblock = NULL; - KBPOS kbpos; + KEYDB_HANDLE hd = NULL; u32 keyid[2]; int rc = 0; @@ -687,16 +699,25 @@ import_revoke_cert( const char *fname, KBNODE node ) } /* read the original keyblock */ - rc = find_keyblock_bypk( &kbpos, pk ); - if( rc ) { - log_error( _("key %08lX: can't locate original keyblock: %s\n"), - (ulong)keyid[1], g10_errstr(rc)); + hd = keydb_new (0); + { + byte afp[MAX_FINGERPRINT_LEN]; + size_t an; + + fingerprint_from_pk (pk, afp, &an); + while (an < MAX_FINGERPRINT_LEN) + afp[an++] = 0; + rc = keydb_search_fpr (hd, afp); + } + if (rc) { + log_error (_("key %08lX: can't locate original keyblock: %s\n"), + (ulong)keyid[1], g10_errstr(rc)); goto leave; } - rc = read_keyblock( &kbpos, &keyblock ); - if( rc ) { - log_error( _("key %08lX: can't read original keyblock: %s\n"), - (ulong)keyid[1], g10_errstr(rc)); + rc = keydb_get_keyblock (hd, &keyblock ); + if (rc) { + log_error (_("key %08lX: can't read original keyblock: %s\n"), + (ulong)keyid[1], g10_errstr(rc)); goto leave; } @@ -729,13 +750,11 @@ import_revoke_cert( const char *fname, KBNODE node ) insert_kbnode( keyblock, clone_kbnode(node), 0 ); /* and write the keyblock back */ - if( (rc=lock_keyblock( &kbpos )) ) - log_error( _("can't lock keyring `%s': %s\n"), - keyblock_resource_name(&kbpos), g10_errstr(rc) ); - else if( (rc=update_keyblock( &kbpos, keyblock )) ) - log_error( _("error writing keyring `%s': %s\n"), - keyblock_resource_name(&kbpos), g10_errstr(rc) ); - unlock_keyblock( &kbpos ); + rc = keydb_update_keyblock (hd, keyblock ); + if (rc) + log_error (_("error writing keyring `%s': %s\n"), + keydb_get_resource_name (hd), g10_errstr(rc) ); + keydb_release (hd); hd = NULL; /* we are ready */ if( !opt.quiet ) log_info( _("key %08lX: revocation certificate imported\n"), @@ -752,6 +771,7 @@ import_revoke_cert( const char *fname, KBNODE node ) } leave: + keydb_release (hd); release_kbnode( keyblock ); free_public_key( pk ); return rc; diff --git a/g10/keydb.c b/g10/keydb.c new file mode 100644 index 000000000..e26a6f75a --- /dev/null +++ b/g10/keydb.c @@ -0,0 +1,623 @@ +/* keydb.c - key database dispatcher + * Copyright (C) 2001 Free Software Foundation, Inc. + * + * This file is part of GnuPG. + * + * 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. + * + * 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. + * + * 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 +#include +#include + +#include "util.h" +#include "options.h" +#include "main.h" /*try_make_homedir ()*/ +#include "packet.h" +#include "keyring.h" +#include "keydb.h" +#include "i18n.h" + +static int active_handles; + +typedef enum { + KEYDB_RESOURCE_TYPE_NONE = 0, + KEYDB_RESOURCE_TYPE_KEYRING +} KeydbResourceType; +#define MAX_KEYDB_RESOURCES 1 + +struct resource_item { + KeydbResourceType type; + union { + KEYRING_HANDLE kr; + } u; +}; + + +struct keydb_handle { + int locked; + int found; + int current; + struct resource_item active[MAX_KEYDB_RESOURCES]; +}; + + +static int lock_all (KEYDB_HANDLE hd); +static void unlock_all (KEYDB_HANDLE hd); + + +/* + * Register a resource (which currently may only be a keyring file). + * The first keyring which is added by this function is + * created if it does not exist. + * Note: this function may be called before secure memory is + * available. + */ +int +keydb_add_resource (const char *url, int force, int secret) +{ + static int any_secret, any_public; + const char *resname = url; + IOBUF iobuf = NULL; + char *filename = NULL; + int rc = 0; + KeydbResourceType rt = KEYDB_RESOURCE_TYPE_NONE; + const char *created_fname = NULL; + + /* Do we have an URL? + * gnupg-ring:filename := this is a plain keyring + * filename := See what is is, but create as plain keyring. + */ + if (strlen (resname) > 11) { + if (!strncmp( resname, "gnupg-ring:", 11) ) { + rt = KEYDB_RESOURCE_TYPE_KEYRING; + resname += 11; + } + #if !defined(HAVE_DRIVE_LETTERS) && !defined(__riscos__) + else if (strchr (resname, ':')) { + log_error ("invalid key resource URL `%s'\n", url ); + rc = G10ERR_GENERAL; + goto leave; + } + #endif /* !HAVE_DRIVE_LETTERS && !__riscos__ */ + } + + if (*resname != DIRSEP_C ) { /* do tilde expansion etc */ + if (strchr(resname, DIRSEP_C) ) + filename = make_filename (resname, NULL); + else + filename = make_filename (opt.homedir, resname, NULL); + } + else + filename = m_strdup (resname); + + if (!force) + force = secret? !any_secret : !any_public; + + /* see whether we can determine the filetype */ + if (rt == KEYDB_RESOURCE_TYPE_NONE) { + FILE *fp = fopen( filename, "rb" ); + + if (fp) { + u32 magic; + + if (fread( &magic, 4, 1, fp) == 1 ) { + if (magic == 0x13579ace || magic == 0xce9a5713) + ; /* GDBM magic - no more support */ + else + rt = KEYDB_RESOURCE_TYPE_KEYRING; + } + else /* maybe empty: assume ring */ + rt = KEYDB_RESOURCE_TYPE_KEYRING; + fclose( fp ); + } + else /* no file yet: create ring */ + rt = KEYDB_RESOURCE_TYPE_KEYRING; + } + + switch (rt) { + case KEYDB_RESOURCE_TYPE_NONE: + log_error ("unknown type of key resource `%s'\n", url ); + rc = G10ERR_GENERAL; + goto leave; + + case KEYDB_RESOURCE_TYPE_KEYRING: + iobuf = iobuf_open (filename); + if (!iobuf && !force) { + rc = G10ERR_OPEN_FILE; + goto leave; + } + + if (!iobuf) { + char *last_slash_in_filename; + + last_slash_in_filename = strrchr (filename, DIRSEP_C); + *last_slash_in_filename = 0; + + if (access(filename, F_OK)) { + /* on the first time we try to create the default homedir and + * in this case the process will be terminated, so that on the + * next invocation it can read the options file in on startup + */ + try_make_homedir (filename); + rc = G10ERR_OPEN_FILE; + *last_slash_in_filename = DIRSEP_C; + goto leave; + } + + *last_slash_in_filename = DIRSEP_C; + + iobuf = iobuf_create (filename); + if (!iobuf) { + log_error ( _("error creating keyring `%s': %s\n"), + filename, strerror(errno)); + rc = G10ERR_OPEN_FILE; + goto leave; + } + else { + #ifndef HAVE_DOSISH_SYSTEM + if (secret && !opt.preserve_permissions) { + if (chmod (filename, S_IRUSR | S_IWUSR) ) { + log_error (_("changing permission of " + " `%s' failed: %s\n"), + filename, strerror(errno) ); + rc = G10ERR_WRITE_FILE; + goto leave; + } + } + #endif + if (!opt.quiet) + log_info (_("keyring `%s' created\n"), filename); + created_fname = filename; + } + } + iobuf_close (iobuf); + iobuf = NULL; + if (created_fname) /* must invalidate that ugly cache */ + iobuf_ioctl (NULL, 2, 0, (char*)created_fname); + keyring_register_filename (filename, secret); + break; + + default: + log_error ("resource type of `%s' not supported\n", url); + rc = G10ERR_GENERAL; + goto leave; + } + + /* fixme: check directory permissions and print a warning */ + + leave: + if (rc) + log_error ("keyblock resource `%s': %s\n", filename, g10_errstr(rc)); + else if (secret) + any_secret = 1; + else + any_public = 1; + m_free (filename); + return rc; +} + + + + +KEYDB_HANDLE +keydb_new (int secret) +{ + KEYDB_HANDLE hd; + int i=0; + + hd = m_alloc_clear (sizeof *hd); + hd->found = -1; + + hd->active[i].type = KEYDB_RESOURCE_TYPE_KEYRING; + hd->active[i].u.kr = keyring_new (secret); + if (!hd->active[i].u.kr) { + m_free (hd); + return NULL; + } + i++; + + + assert (i <= MAX_KEYDB_RESOURCES); + active_handles++; + return hd; +} + +void +keydb_release (KEYDB_HANDLE hd) +{ + int i; + + if (!hd) + return; + assert (active_handles > 0); + active_handles--; + + unlock_all (hd); + for (i=0; i < MAX_KEYDB_RESOURCES; i++) { + switch (hd->active[i].type) { + case KEYDB_RESOURCE_TYPE_NONE: + break; + case KEYDB_RESOURCE_TYPE_KEYRING: + keyring_release (hd->active[i].u.kr); + break; + } + } + + m_free (hd); +} + + +/* + * Return the name of the current resource. This is function first + * looks for the last found found, then for the current search + * position, and last returns the first available resource. The + * returned string is only valid as long as the handle exists. This + * function does only return NULL if no handle is specified, in all + * other error cases an empty string is returned. + */ +const char * +keydb_get_resource_name (KEYDB_HANDLE hd) +{ + int idx; + const char *s = NULL; + + if (!hd) + return NULL; + + if ( hd->found >= 0 && hd->found < MAX_KEYDB_RESOURCES) + idx = hd->found; + else if ( hd->current >= 0 && hd->current < MAX_KEYDB_RESOURCES) + idx = hd->current; + else + idx = 0; + + switch (hd->active[idx].type) { + case KEYDB_RESOURCE_TYPE_NONE: + s = NULL; + break; + case KEYDB_RESOURCE_TYPE_KEYRING: + s = keyring_get_resource_name (hd->active[idx].u.kr); + break; + } + + return s? s: ""; +} + + + +static int +lock_all (KEYDB_HANDLE hd) +{ + int i, rc = 0; + + for (i=0; !rc && i < MAX_KEYDB_RESOURCES; i++) { + switch (hd->active[i].type) { + case KEYDB_RESOURCE_TYPE_NONE: + break; + case KEYDB_RESOURCE_TYPE_KEYRING: + rc = keyring_lock (hd->active[i].u.kr, 1); + break; + } + } + + if (rc) { + /* revert the alreadt set locks */ + for (i--; i >= 0; i--) { + switch (hd->active[i].type) { + case KEYDB_RESOURCE_TYPE_NONE: + break; + case KEYDB_RESOURCE_TYPE_KEYRING: + keyring_lock (hd->active[i].u.kr, 0); + break; + } + } + } + else + hd->locked = 1; + + return rc; +} + +static void +unlock_all (KEYDB_HANDLE hd) +{ + int i; + + if (!hd->locked) + return; + + for (i=MAX_KEYDB_RESOURCES-1; i >= 0; i--) { + switch (hd->active[i].type) { + case KEYDB_RESOURCE_TYPE_NONE: + break; + case KEYDB_RESOURCE_TYPE_KEYRING: + keyring_lock (hd->active[i].u.kr, 0); + break; + } + } + hd->locked = 0; +} + + +/* + * Return the last found keyring. Caller must free it. + * The returned keyblock has the kbode flag bit 0 set for the node with + * the public key used to locate the keyblock or flag bit 1 set for + * the user ID node. + */ +int +keydb_get_keyblock (KEYDB_HANDLE hd, KBNODE *ret_kb) +{ + int rc = 0; + + if (!hd) + return G10ERR_INV_ARG; + + if ( hd->found < 0 || hd->found >= MAX_KEYDB_RESOURCES) + return -1; /* nothing found */ + + switch (hd->active[hd->found].type) { + case KEYDB_RESOURCE_TYPE_NONE: + rc = G10ERR_GENERAL; /* oops */ + break; + case KEYDB_RESOURCE_TYPE_KEYRING: + rc = keyring_get_keyblock (hd->active[hd->found].u.kr, ret_kb); + break; + } + + return rc; +} + +/* + * update the current keyblock with KB + */ +int +keydb_update_keyblock (KEYDB_HANDLE hd, KBNODE kb) +{ + int rc = 0; + + if (!hd) + return G10ERR_INV_ARG; + + if ( hd->found < 0 || hd->found >= MAX_KEYDB_RESOURCES) + return -1; /* nothing found */ + + if( opt.dry_run ) + return 0; + + rc = lock_all (hd); + if (rc) + return rc; + + switch (hd->active[hd->found].type) { + case KEYDB_RESOURCE_TYPE_NONE: + rc = G10ERR_GENERAL; /* oops */ + break; + case KEYDB_RESOURCE_TYPE_KEYRING: + rc = keyring_update_keyblock (hd->active[hd->found].u.kr, kb); + break; + } + + unlock_all (hd); + return rc; +} + + +/* + * Insert a new KB into one of the resources. + */ +int +keydb_insert_keyblock (KEYDB_HANDLE hd, KBNODE kb) +{ + int rc = -1; + int idx; + + if (!hd) + return G10ERR_INV_ARG; + + if( opt.dry_run ) + return 0; + + if ( hd->found >= 0 && hd->found < MAX_KEYDB_RESOURCES) + idx = hd->found; + else if ( hd->current >= 0 && hd->current < MAX_KEYDB_RESOURCES) + idx = hd->current; + else + return G10ERR_GENERAL; + + rc = lock_all (hd); + if (rc) + return rc; + + switch (hd->active[idx].type) { + case KEYDB_RESOURCE_TYPE_NONE: + rc = G10ERR_GENERAL; /* oops */ + break; + case KEYDB_RESOURCE_TYPE_KEYRING: + rc = keyring_insert_keyblock (hd->active[idx].u.kr, kb); + break; + } + + unlock_all (hd); + return rc; +} + + +/* + * The current keyblock will be deleted. + */ +int +keydb_delete_keyblock (KEYDB_HANDLE hd) +{ + int rc = -1; + + if (!hd) + return G10ERR_INV_ARG; + + if ( hd->found < 0 || hd->found >= MAX_KEYDB_RESOURCES) + return -1; /* nothing found */ + + if( opt.dry_run ) + return 0; + + rc = lock_all (hd); + if (rc) + return rc; + + switch (hd->active[hd->found].type) { + case KEYDB_RESOURCE_TYPE_NONE: + rc = G10ERR_GENERAL; /* oops */ + break; + case KEYDB_RESOURCE_TYPE_KEYRING: + rc = keyring_delete_keyblock (hd->active[hd->found].u.kr); + break; + } + + unlock_all (hd); + return rc; +} + + +/* + * Locate the default writable key resource, so that the next + * operation (which is only releavnt for inserts) will be onde on this + * resource. + */ +int +keydb_locate_writable (KEYDB_HANDLE hd, const char *reserved) +{ + int rc; + + if (!hd) + return G10ERR_INV_ARG; + + rc = keydb_search_reset (hd); + if (!rc) { + /* fixme: set forward to a writable one */ + } + return rc; +} + + +/* + * Start the next search on this handle right at the beginning + */ +int +keydb_search_reset (KEYDB_HANDLE hd) +{ + int i, rc = 0; + + if (!hd) + return G10ERR_INV_ARG; + + hd->current = 0; + hd->found = -1; + /* and reset all resources */ + for (i=0; !rc && i < MAX_KEYDB_RESOURCES; i++) { + switch (hd->active[i].type) { + case KEYDB_RESOURCE_TYPE_NONE: + break; + case KEYDB_RESOURCE_TYPE_KEYRING: + rc = keyring_search_reset (hd->active[i].u.kr); + break; + } + } + return rc; +} + + +/* + * Search through all keydb resources, starting at the current position, + * for a keyblock which contains one of the keys described in the DESC array. + */ +int +keydb_search (KEYDB_HANDLE hd, KEYDB_SEARCH_DESC *desc, size_t ndesc) +{ + int rc = -1; + + if (!hd) + return G10ERR_INV_ARG; + + while (rc == -1 && hd->current >= 0 && hd->current < MAX_KEYDB_RESOURCES) { + switch (hd->active[hd->current].type) { + case KEYDB_RESOURCE_TYPE_NONE: + rc = -1; /* no resource = eof */ + break; + case KEYDB_RESOURCE_TYPE_KEYRING: + rc = keyring_search (hd->active[hd->current].u.kr, desc, ndesc); + break; + } + if (rc == -1) /* EOF -> switch to next resource */ + hd->current++; + else if (!rc) + hd->found = hd->current; + } + + return rc; +} + + +int +keydb_search_first (KEYDB_HANDLE hd) +{ + KEYDB_SEARCH_DESC desc; + + memset (&desc, 0, sizeof desc); + desc.mode = KEYDB_SEARCH_MODE_FIRST; + return keydb_search (hd, &desc, 1); +} + +int +keydb_search_next (KEYDB_HANDLE hd) +{ + KEYDB_SEARCH_DESC desc; + + memset (&desc, 0, sizeof desc); + desc.mode = KEYDB_SEARCH_MODE_NEXT; + return keydb_search (hd, &desc, 1); +} + +int +keydb_search_kid (KEYDB_HANDLE hd, u32 *kid) +{ + KEYDB_SEARCH_DESC desc; + + memset (&desc, 0, sizeof desc); + desc.mode = KEYDB_SEARCH_MODE_LONG_KID; + desc.u.kid[0] = kid[0]; + desc.u.kid[1] = kid[1]; + return keydb_search (hd, &desc, 1); +} + +int +keydb_search_fpr (KEYDB_HANDLE hd, const byte *fpr) +{ + KEYDB_SEARCH_DESC desc; + + memset (&desc, 0, sizeof desc); + desc.mode = KEYDB_SEARCH_MODE_FPR; + memcpy (desc.u.fpr, fpr, MAX_FINGERPRINT_LEN); + return keydb_search (hd, &desc, 1); +} + + + diff --git a/g10/keydb.h b/g10/keydb.h index 8438a3f9d..7d20b65c5 100644 --- a/g10/keydb.h +++ b/g10/keydb.h @@ -21,15 +21,11 @@ #ifndef G10_KEYDB_H #define G10_KEYDB_H -#ifdef HAVE_LIBGDBM - #include -#endif - #include "types.h" +#include "global.h" #include "packet.h" #include "cipher.h" -#define MAX_FINGERPRINT_LEN 20 #define IS_KEY_SIG(s) ((s)->sig_class == 0x1f) #define IS_UID_SIG(s) (((s)->sig_class & ~3) == 0x10) @@ -50,7 +46,6 @@ typedef struct getkey_ctx_s *GETKEY_CTX; * This structure is also used to bind arbitrary packets together. */ -typedef struct kbnode_struct *KBNODE; struct kbnode_struct { KBNODE next; PACKET *pkt; @@ -65,8 +60,7 @@ struct kbnode_struct { enum resource_type { rt_UNKNOWN = 0, - rt_RING = 1, - rt_GDBM = 2 + rt_RING = 1 }; @@ -81,10 +75,6 @@ struct keyblock_pos_struct { unsigned count; /* length of the keyblock in packets */ IOBUF fp; /* used by enum_keyblocks */ int secret; /* working on a secret keyring */ - #ifdef HAVE_LIBGDBM - GDBM_FILE dbf; - byte keybuf[21]; - #endif PACKET *pkt; /* ditto */ int valid; }; @@ -118,6 +108,52 @@ struct pubkey_find_info { }; +typedef struct keydb_handle *KEYDB_HANDLE; + +typedef enum { + KEYDB_SEARCH_MODE_NONE, + KEYDB_SEARCH_MODE_EXACT, + KEYDB_SEARCH_MODE_SUBSTR, + KEYDB_SEARCH_MODE_MAIL, + KEYDB_SEARCH_MODE_MAILSUB, + KEYDB_SEARCH_MODE_MAILEND, + KEYDB_SEARCH_MODE_WORDS, + KEYDB_SEARCH_MODE_SHORT_KID, + KEYDB_SEARCH_MODE_LONG_KID, + KEYDB_SEARCH_MODE_TDBIDX, + KEYDB_SEARCH_MODE_FPR16, + KEYDB_SEARCH_MODE_FPR20, + KEYDB_SEARCH_MODE_FPR, + KEYDB_SEARCH_MODE_FIRST, + KEYDB_SEARCH_MODE_NEXT +} KeydbSearchMode; + +struct keydb_search_desc { + KeydbSearchMode mode; + union { + const char *name; + char fpr[MAX_FINGERPRINT_LEN]; + u32 kid[2]; + } u; +}; + +/*-- keydb.c --*/ +int keydb_add_resource (const char *url, int force, int secret); +KEYDB_HANDLE keydb_new (int secret); +void keydb_release (KEYDB_HANDLE hd); +const char *keydb_get_resource_name (KEYDB_HANDLE hd); +int keydb_get_keyblock (KEYDB_HANDLE hd, KBNODE *ret_kb); +int keydb_update_keyblock (KEYDB_HANDLE hd, KBNODE kb); +int keydb_insert_keyblock (KEYDB_HANDLE hd, KBNODE kb); +int keydb_delete_keyblock (KEYDB_HANDLE hd); +int keydb_locate_writable (KEYDB_HANDLE hd, const char *reserved); +int keydb_search_reset (KEYDB_HANDLE hd); +int keydb_search (KEYDB_HANDLE hd, KEYDB_SEARCH_DESC *desc, size_t ndesc); +int keydb_search_first (KEYDB_HANDLE hd); +int keydb_search_next (KEYDB_HANDLE hd); +int keydb_search_kid (KEYDB_HANDLE hd, u32 *kid); +int keydb_search_fpr (KEYDB_HANDLE hd, const byte *fpr); + /*-- pkclist.c --*/ int check_signatures_trust( PKT_signature *sig ); @@ -147,8 +183,8 @@ void cache_public_key( PKT_public_key *pk ); void getkey_disable_caches(void); int get_pubkey( PKT_public_key *pk, u32 *keyid ); KBNODE get_pubkeyblock( u32 *keyid ); -int get_pubkey_byname( GETKEY_CTX *rx, PKT_public_key *pk, - const char *name, KBNODE *ret_keyblock ); +int get_pubkey_byname( PKT_public_key *pk, const char *name, + KBNODE *ret_keyblock, KEYDB_HANDLE *ret_kdbhd); int get_pubkey_bynames( GETKEY_CTX *rx, PKT_public_key *pk, STRLIST names, KBNODE *ret_keyblock ); int get_pubkey_next( GETKEY_CTX ctx, PKT_public_key *pk, KBNODE *ret_keyblock ); @@ -213,25 +249,4 @@ void clear_kbnode_flags( KBNODE n ); int commit_kbnode( KBNODE *root ); void dump_kbnode( KBNODE node ); -/*-- ringedit.c --*/ -const char *enum_keyblock_resources( int *sequence, int secret ); -int add_keyblock_resource( const char *resname, int force, int secret ); -const char *keyblock_resource_name( KBPOS *kbpos ); -int get_keyblock_handle( const char *filename, int secret, KBPOS *kbpos ); -char *get_writable_keyblock_file( int secret ); -int find_keyblock( PUBKEY_FIND_INFO info, KBPOS *kbpos ); -int find_keyblock_byname( KBPOS *kbpos, const char *username ); -int find_keyblock_bypk( KBPOS *kbpos, PKT_public_key *pk ); -int find_secret_keyblock_bypk( KBPOS *kbpos, PKT_public_key *pk ); -int find_secret_keyblock_byname( KBPOS *kbpos, const char *username ); -int find_secret_keyblock_direct (KBPOS *kbpos, u32 *keyid); -int lock_keyblock( KBPOS *kbpos ); -void unlock_keyblock( KBPOS *kbpos ); -int read_keyblock( KBPOS *kbpos, KBNODE *ret_root ); -int enum_keyblocks( int mode, KBPOS *kbpos, KBNODE *ret_root ); -int insert_keyblock( KBPOS *kbpos, KBNODE root ); -int delete_keyblock( KBPOS *kbpos ); -int update_keyblock( KBPOS *kbpos, KBNODE root ); - - #endif /*G10_KEYDB_H*/ diff --git a/g10/keyedit.c b/g10/keyedit.c index 875e229e0..033220de1 100644 --- a/g10/keyedit.c +++ b/g10/keyedit.c @@ -84,30 +84,6 @@ struct sign_attrib { -static int -get_keyblock_byname( KBNODE *keyblock, KBPOS *kbpos, const char *username ) -{ - int rc; - - *keyblock = NULL; - /* search the userid */ - rc = find_keyblock_byname( kbpos, username ); - if( rc ) { - log_error(_("%s: user not found\n"), username ); - return rc; - } - - /* read the keyblock */ - rc = read_keyblock( kbpos, keyblock ); - if( rc ) - log_error("%s: keyblock read problem: %s\n", username, g10_errstr(rc)); - else - merge_keys_and_selfsig( *keyblock ); - - return rc; -} - - /**************** * Print information about a signature, check it and return true * if the signature is okay. NODE must be a signature packet. @@ -626,9 +602,9 @@ keyedit_menu( const char *username, STRLIST locusr, STRLIST commands, enum cmdids cmd = 0; int rc = 0; KBNODE keyblock = NULL; - KBPOS keyblockpos; + KEYDB_HANDLE kdbhd = NULL; KBNODE sec_keyblock = NULL; - KBPOS sec_keyblockpos; + KEYDB_HANDLE sec_kdbhd = NULL; KBNODE cur_keyblock; char *answer = NULL; int redisplay = 1; @@ -651,7 +627,7 @@ keyedit_menu( const char *username, STRLIST locusr, STRLIST commands, } /* get the public key */ - rc = get_keyblock_byname( &keyblock, &keyblockpos, username ); + rc = get_pubkey_byname (NULL, username, &keyblock, &kdbhd); if( rc ) goto leave; if( fix_keyblock( keyblock ) ) @@ -662,18 +638,34 @@ keyedit_menu( const char *username, STRLIST locusr, STRLIST commands, if( !sign_mode ) {/* see whether we have a matching secret key */ PKT_public_key *pk = keyblock->pkt->pkt.public_key; - rc = find_secret_keyblock_bypk( &sec_keyblockpos, pk ); - if( !rc ) { - rc = read_keyblock( &sec_keyblockpos, &sec_keyblock ); - if( rc ) { - log_error("%s: secret keyblock read problem: %s\n", + sec_kdbhd = keydb_new (1); + { + byte afp[MAX_FINGERPRINT_LEN]; + size_t an; + + fingerprint_from_pk (pk, afp, &an); + while (an < MAX_FINGERPRINT_LEN) + afp[an++] = 0; + rc = keydb_search_fpr (sec_kdbhd, afp); + } + if (!rc) { + rc = keydb_get_keyblock (sec_kdbhd, &sec_keyblock); + if (rc) { + log_error (_("error reading secret keyblock `%s': %s\n"), username, g10_errstr(rc)); - goto leave; } - merge_keys_and_selfsig( sec_keyblock ); - if( fix_keyblock( sec_keyblock ) ) - sec_modified++; + else { + merge_keys_and_selfsig( sec_keyblock ); + if( fix_keyblock( sec_keyblock ) ) + sec_modified++; + } } + + if (rc) { + sec_keyblock = NULL; + keydb_release (sec_kdbhd); sec_kdbhd = NULL; + rc = 0; + } } if( sec_keyblock ) { @@ -1012,23 +1004,24 @@ keyedit_menu( const char *username, STRLIST locusr, STRLIST commands, do_cmd_save: if( modified || sec_modified ) { if( modified ) { - rc = update_keyblock( &keyblockpos, keyblock ); + rc = keydb_update_keyblock (kdbhd, keyblock); if( rc ) { log_error(_("update failed: %s\n"), g10_errstr(rc) ); break; } } if( sec_modified ) { - rc = update_keyblock( &sec_keyblockpos, sec_keyblock ); + rc = keydb_update_keyblock (sec_kdbhd, sec_keyblock ); if( rc ) { - log_error(_("update secret failed: %s\n"), - g10_errstr(rc) ); + log_error( _("update secret failed: %s\n"), + g10_errstr(rc) ); break; } } } else tty_printf(_("Key not changed so no update needed.\n")); + /* TODO: we should keep track whether we have changed * something relevant to the trustdb */ if( !modified && sign_mode ) @@ -1051,6 +1044,7 @@ keyedit_menu( const char *username, STRLIST locusr, STRLIST commands, leave: release_kbnode( keyblock ); release_kbnode( sec_keyblock ); + keydb_release (kdbhd); m_free(answer); } diff --git a/g10/keygen.c b/g10/keygen.c index 37d4a0740..8a29d7711 100644 --- a/g10/keygen.c +++ b/g10/keygen.c @@ -284,22 +284,22 @@ static void add_feature_mdc (PKT_signature *sig) { const byte *s; - size_t i, n; + size_t n; char *buf; s = parse_sig_subpkt (sig->hashed, SIGSUBPKT_FEATURES, &n ); - if (!s) - n = 0; - - for (i=0; i < n; i++ ) { - if (s[i] == 1) - return; /* already set */ + if (s && n && (s[0] & 0x01)) + return; /* already set */ + if (!s || !n) { /* create a new one */ + n = 1; + buf = m_alloc (n); } - - buf = m_alloc (n+1); - buf[0] = 1; /* MDC feature */ - memcpy (buf+1, s, n); - build_sig_subpkt (sig, SIGSUBPKT_FEATURES, buf, n+1); + else { + buf = m_alloc (n); + memcpy (buf, s, n); + } + buf[0] |= 0x01; /* MDC feature */ + build_sig_subpkt (sig, SIGSUBPKT_FEATURES, buf, n); m_free (buf); } @@ -1769,8 +1769,6 @@ static void do_generate_keypair( struct para_data_s *para, struct output_control_s *outctrl ) { - char *pub_fname = NULL; - char *sec_fname = NULL; KBNODE pub_root = NULL; KBNODE sec_root = NULL; PKT_secret_key *sk = NULL; @@ -1823,20 +1821,14 @@ do_generate_keypair( struct para_data_s *para, &outctrl->sec.afx ); } } - pub_fname = outctrl->pub.fname; /* only for info output */ - sec_fname = outctrl->sec.fname; /* only for info output */ assert( outctrl->pub.stream ); assert( outctrl->sec.stream ); - } - else { - pub_fname = get_writable_keyblock_file( 0 ); - sec_fname = get_writable_keyblock_file( 1 ); + if( opt.verbose ) { + log_info(_("writing public key to `%s'\n"), outctrl->pub.fname ); + log_info(_("writing secret key to `%s'\n"), outctrl->sec.fname ); + } } - if( opt.verbose ) { - log_info(_("writing public key to `%s'\n"), pub_fname ); - log_info(_("writing secret key to `%s'\n"), sec_fname ); - } /* we create the packets as a tree of kbnodes. Because the structure * we create is known in advance we simply generate a linked list @@ -1895,46 +1887,47 @@ do_generate_keypair( struct para_data_s *para, } else if( !rc ) { /* write to the standard keyrings */ - KBPOS pub_kbpos; - KBPOS sec_kbpos; - int rc1 = -1; - int rc2 = -1; + KEYDB_HANDLE pub_hd = keydb_new (0); + KEYDB_HANDLE sec_hd = keydb_new (1); - /* we can now write the certificates */ - if( get_keyblock_handle( pub_fname, 0, &pub_kbpos ) ) { - if( add_keyblock_resource( pub_fname, 1, 0 ) ) { - log_error("can add keyblock file `%s'\n", pub_fname ); - rc = G10ERR_CREATE_FILE; - } - else if( get_keyblock_handle( pub_fname, 0, &pub_kbpos ) ) { - log_error("can get keyblock handle for `%s'\n", pub_fname ); - rc = G10ERR_CREATE_FILE; - } - } - if( rc ) - ; - else if( get_keyblock_handle( sec_fname, 1, &sec_kbpos ) ) { - if( add_keyblock_resource( sec_fname, 1, 1 ) ) { - log_error("can add keyblock file `%s'\n", sec_fname ); - rc = G10ERR_CREATE_FILE; - } - else if( get_keyblock_handle( sec_fname, 1, &sec_kbpos ) ) { - log_error("can get keyblock handle for `%s'\n", sec_fname ); - rc = G10ERR_CREATE_FILE; - } - } + /* FIXME: we may have to create the keyring first */ + rc = keydb_locate_writable (pub_hd, NULL); + if (rc) + log_error (_("no writable public keyring found: %s\n"), + g10_errstr (rc)); - if( rc ) - ; - else if( (rc=rc1=lock_keyblock( &pub_kbpos )) ) - log_error("can't lock public keyring: %s\n", g10_errstr(rc) ); - else if( (rc=rc2=lock_keyblock( &sec_kbpos )) ) - log_error("can't lock secret keyring: %s\n", g10_errstr(rc) ); - else if( (rc=insert_keyblock( &pub_kbpos, pub_root )) ) - log_error("can't write public key: %s\n", g10_errstr(rc) ); - else if( (rc=insert_keyblock( &sec_kbpos, sec_root )) ) - log_error("can't write secret key: %s\n", g10_errstr(rc) ); - else { + if (!rc) { + rc = keydb_locate_writable (sec_hd, NULL); + if (rc) + log_error (_("no writable secret keyring found: %s\n"), + g10_errstr (rc)); + } + + if (!rc && opt.verbose) { + log_info(_("writing public key to `%s'\n"), + keydb_get_resource_name (pub_hd)); + log_info(_("writing secret key to `%s'\n"), + keydb_get_resource_name (sec_hd)); + } + + if (!rc) { + rc = keydb_insert_keyblock (pub_hd, pub_root); + if (rc) + log_error (_("error writing public keyring `%s': %s\n"), + keydb_get_resource_name (pub_hd), g10_errstr(rc)); + } + + if (!rc) { + rc = keydb_insert_keyblock (sec_hd, sec_root); + if (rc) + log_error (_("error writing secret keyring `%s': %s\n"), + keydb_get_resource_name (pub_hd), g10_errstr(rc)); + } + + keydb_release (pub_hd); + keydb_release (sec_hd); + + if (!rc) { int no_enc_rsa = get_parameter_algo(para, pKEYTYPE) == PUBKEY_ALGO_RSA && get_parameter_uint( para, pKEYUSAGE ) @@ -1953,11 +1946,6 @@ do_generate_keypair( struct para_data_s *para, "secondary key for this purpose.\n") ); } } - - if( !rc1 ) - unlock_keyblock( &pub_kbpos ); - if( !rc2 ) - unlock_keyblock( &sec_kbpos ); } if( rc ) { @@ -1973,10 +1961,6 @@ do_generate_keypair( struct para_data_s *para, release_kbnode( sec_root ); if( sk ) /* the unprotected secret key */ free_secret_key(sk); - if( !outctrl->use_files ) { - m_free(pub_fname); - m_free(sec_fname); - } } diff --git a/g10/keyid.c b/g10/keyid.c index b933e7ab8..59f3883c0 100644 --- a/g10/keyid.c +++ b/g10/keyid.c @@ -277,7 +277,7 @@ mk_datestr (char *buffer, time_t atime) struct tm *tp; if ( atime < 0 ) /* 32 bit time_t and after 2038-01-19 */ - strcpy (buffer, "????-??-??"); /* mark this as invalid */ + strcpy (buffer, "????" "-??" "-??"); /* mark this as invalid */ else { tp = gmtime (&atime); sprintf (buffer,"%04d-%02d-%02d", @@ -401,9 +401,9 @@ byte * fingerprint_from_pk( PKT_public_key *pk, byte *array, size_t *ret_len ) { byte *p, *buf; - const char *dp; + const byte *dp; size_t len; - unsigned n; + unsigned int n; if( pk->version < 4 && is_RSA(pk->pubkey_algo) ) { /* RSA in version 3 packets is special */ @@ -434,6 +434,8 @@ fingerprint_from_pk( PKT_public_key *pk, byte *array, size_t *ret_len ) if( !array ) array = m_alloc( len ); memcpy(array, dp, len ); + pk->keyid[0] = dp[12] << 24 | dp[13] << 16 | dp[14] << 8 | dp[15] ; + pk->keyid[1] = dp[16] << 24 | dp[17] << 16 | dp[18] << 8 | dp[19] ; md_close(md); } diff --git a/g10/keylist.c b/g10/keylist.c index f445d67b6..01f208d8c 100644 --- a/g10/keylist.c +++ b/g10/keylist.c @@ -67,41 +67,50 @@ secret_key_list( STRLIST list ) static void list_all( int secret ) { - KBPOS kbpos; + KEYDB_HANDLE hd; KBNODE keyblock = NULL; int rc=0; - int lastresno; + const char *lastresname, *resname; - rc = enum_keyblocks( secret? 5:0, &kbpos, &keyblock ); + hd = keydb_new (secret); + if (!hd) + rc = G10ERR_GENERAL; + else + rc = keydb_search_first (hd); if( rc ) { if( rc != -1 ) - log_error("enum_keyblocks(open) failed: %s\n", g10_errstr(rc) ); + log_error("keydb_search_first failed: %s\n", g10_errstr(rc) ); goto leave; } - lastresno = -1; - while( !(rc = enum_keyblocks( 1, &kbpos, &keyblock )) ) { - if( lastresno != kbpos.resno ) { - const char *s = keyblock_resource_name( &kbpos ); + lastresname = NULL; + do { + rc = keydb_get_keyblock (hd, &keyblock); + if (rc) { + log_error ("keydb_get_keyblock failed: %s\n", g10_errstr(rc)); + goto leave; + } + resname = keydb_get_resource_name (hd); + if (lastresname != resname ) { int i; - lastresno = kbpos.resno; - printf("%s\n", s ); - for(i=strlen(s); i; i-- ) + printf("%s\n", resname ); + for(i=strlen(resname); i; i-- ) putchar('-'); putchar('\n'); + lastresname = resname; } merge_keys_and_selfsig( keyblock ); list_keyblock( keyblock, secret ); - release_kbnode( keyblock ); keyblock = NULL; - } - + release_kbnode( keyblock ); + keyblock = NULL; + } while (!(rc = keydb_search_next (hd))); if( rc && rc != -1 ) - log_error("enum_keyblocks(read) failed: %s\n", g10_errstr(rc)); + log_error ("keydb_search_next failed: %s\n", g10_errstr(rc)); leave: - enum_keyblocks( 2, &kbpos, &keyblock ); /* close */ - release_kbnode( keyblock ); + release_kbnode (keyblock); + keydb_release (hd); } diff --git a/g10/keyring.c b/g10/keyring.c new file mode 100644 index 000000000..c63f02b84 --- /dev/null +++ b/g10/keyring.c @@ -0,0 +1,1061 @@ +/* keyring.c - keyring file handling + * Copyright (C) 2001 Free Software Foundation, Inc. + * + * This file is part of GnuPG. + * + * 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. + * + * 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. + * + * 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 +#include + +#include "util.h" +#include "keyring.h" +#include "packet.h" +#include "keydb.h" +#include "options.h" +#include "i18n.h" + +typedef struct keyring_name *KR_NAME; +struct keyring_name { + struct keyring_name *next; + int secret; + DOTLOCK lockhd; + int is_locked; + char fname[1]; +}; +typedef struct keyring_name const * CONST_KR_NAME; + +static KR_NAME kr_names; +static int active_handles; + +struct keyring_handle { + int secret; /* this is for a secret keyring */ + struct { + CONST_KR_NAME kr; + IOBUF iobuf; + int eof; + int error; + } current; + struct { + CONST_KR_NAME kr; + off_t offset; + size_t pk_no; + size_t uid_no; + unsigned int n_packets; /*used for delete and update*/ + } found; + struct { + char *name; + char *pattern; + } word_match; +}; + +static int do_copy (int mode, const char *fname, KBNODE root, int secret, + off_t start_offset, unsigned int n_packets ); + + + +/* + * Register a filename for plain keyring files + */ +void +keyring_register_filename (const char *fname, int secret) +{ + KR_NAME kr; + + if (active_handles) + BUG (); /* We don't allow that */ + + for (kr=kr_names; kr; kr = kr->next) { + if ( !compare_filenames (kr->fname, fname) ) + return; /* already registered */ + } + + kr = m_alloc (sizeof *kr + strlen (fname)); + strcpy (kr->fname, fname); + kr->secret = !!secret; + kr->lockhd = NULL; + kr->is_locked = 0; + kr->next = kr_names; + kr_names = kr; +} + + + + + +KEYRING_HANDLE +keyring_new (int secret) +{ + KEYRING_HANDLE hd; + + hd = m_alloc_clear (sizeof *hd); + hd->secret = !!secret; + active_handles++; + return hd; +} + +void +keyring_release (KEYRING_HANDLE hd) +{ + if (!hd) + return; + assert (active_handles > 0); + active_handles--; + m_free (hd->word_match.name); + m_free (hd->word_match.pattern); + m_free (hd); +} + +static CONST_KR_NAME +next_kr (CONST_KR_NAME start, int secret) +{ + start = start? start->next : kr_names; + while ( start && start->secret != secret ) + start = start->next; + return start; +} + +const char * +keyring_get_resource_name (KEYRING_HANDLE hd) +{ + const char *fname; + + if (!hd) + fname = NULL; + else if (hd->found.kr) + fname = hd->found.kr->fname; + else if (hd->current.kr) + fname = hd->current.kr->fname; + else { + CONST_KR_NAME kr = next_kr (NULL, hd->secret); + fname = kr? kr->fname:NULL; + } + + return fname; +} + + +/* + * Lock the keyring with the given handle, or unlok if yes is false. + * We ignore the handle and lock all registered files. + */ +int +keyring_lock (KEYRING_HANDLE hd, int yes) +{ + KR_NAME kr; + int rc = 0; + + if (yes) { + /* first make sure the lock handles are created */ + for (kr=kr_names; kr; kr = kr->next) { + if (!kr->lockhd) { + kr->lockhd = create_dotlock( kr->fname ); + if (!kr->lockhd) { + log_info ("can't allocate lock for `%s'\n", kr->fname ); + rc = G10ERR_GENERAL; + } + } + } + if (rc) + return rc; + + /* and now set the locks */ + for (kr=kr_names; kr; kr = kr->next) { + if (kr->is_locked) + ; + else if (make_dotlock (kr->lockhd, -1) ) { + log_info ("can't lock `%s'\n", kr->fname ); + rc = G10ERR_GENERAL; + } + else + kr->is_locked = 1; + } + } + + if (rc || !yes) { + for (kr=kr_names; kr; kr = kr->next) { + if (!kr->is_locked) + ; + else if (release_dotlock (kr->lockhd)) + log_info ("can't unlock `%s'\n", kr->fname ); + else + kr->is_locked = 0; + } + } + + return rc; +} + + + +/* + * Return the last found keyring. Caller must free it. + * The returned keyblock has the kbode flag bit 0 set for the node with + * the public key used to locate the keyblock or flag bit 1 set for + * the user ID node. + */ +int +keyring_get_keyblock (KEYRING_HANDLE hd, KBNODE *ret_kb) +{ + PACKET *pkt; + int rc; + KBNODE keyblock = NULL, node; + IOBUF a; + int in_cert = 0; + int pk_no = 0; + int uid_no = 0; + + if (!hd->found.kr) + return -1; /* no successful search */ + + a = iobuf_open (hd->found.kr->fname); + if (!a) { + log_error ("can't open `%s'\n", hd->found.kr->fname); + return G10ERR_KEYRING_OPEN; + } + + if (iobuf_seek (a, hd->found.offset) ) { + log_error ("can't seek `%s'\n", hd->found.kr->fname); + iobuf_close(a); + return G10ERR_KEYRING_OPEN; + } + + pkt = m_alloc (sizeof *pkt); + init_packet (pkt); + hd->found.n_packets = 0;; + while ((rc=parse_packet (a, pkt)) != -1) { + hd->found.n_packets++; + if (rc == G10ERR_UNKNOWN_PACKET) { + free_packet (pkt); + init_packet (pkt); + continue; + } + if (rc) { + log_error ("keyring_get_keyblock: read error: %s\n", + g10_errstr(rc) ); + rc = G10ERR_INV_KEYRING; + break; + } + if (pkt->pkttype == PKT_COMPRESSED) { + log_error ("skipped compressed packet in keyring\n"); + free_packet(pkt); + init_packet(pkt); + continue; + } + + if (in_cert && (pkt->pkttype == PKT_PUBLIC_KEY + || pkt->pkttype == PKT_SECRET_KEY)) { + hd->found.n_packets--; /* fix counter */ + break; /* ready */ + } + + in_cert = 1; + node = new_kbnode (pkt); + if (!keyblock) + keyblock = node; + else + add_kbnode (keyblock, node); + + if ( pkt->pkttype == PKT_PUBLIC_KEY + || pkt->pkttype == PKT_PUBLIC_SUBKEY + || pkt->pkttype == PKT_SECRET_KEY + || pkt->pkttype == PKT_SECRET_SUBKEY) { + if (++pk_no == hd->found.pk_no) + node->flag |= 1; + } + else if ( pkt->pkttype == PKT_USER_ID) { + if (++uid_no == hd->found.uid_no) + node->flag |= 2; + } + + pkt = m_alloc (sizeof *pkt); + init_packet(pkt); + } + + if (rc == -1 && keyblock) + rc = 0; /* got the entire keyblock */ + + if (rc || !ret_kb) + release_kbnode (keyblock); + else + *ret_kb = keyblock; + + free_packet (pkt); + m_free (pkt); + iobuf_close(a); + return rc; +} + +int +keyring_update_keyblock (KEYRING_HANDLE hd, KBNODE kb) +{ + int rc; + + if (!hd->found.kr) + return -1; /* no successful prior search */ + + if (!hd->found.n_packets) { + /* need to know the number of packets - do a dummy get_keyblock*/ + rc = keyring_get_keyblock (hd, NULL); + if (rc) { + log_error ("re-reading keyblock failed: %s\n", g10_errstr (rc)); + return rc; + } + if (!hd->found.n_packets) + BUG (); + } + + /* do the update */ + rc = do_copy (3, hd->found.kr->fname, kb, hd->secret, + hd->found.offset, hd->found.n_packets ); + if (!rc) { + /* better reset the found info */ + hd->found.kr = NULL; + hd->found.offset = 0; + } + return rc; +} + +int +keyring_insert_keyblock (KEYRING_HANDLE hd, KBNODE kb) +{ + int rc; + const char *fname; + + if (!hd) + fname = NULL; + else if (hd->found.kr) + fname = hd->found.kr->fname; + else if (hd->current.kr) + fname = hd->current.kr->fname; + else { + CONST_KR_NAME kr = next_kr (NULL, hd->secret); + fname = kr? kr->fname:NULL; + } + + if (!fname) + return G10ERR_GENERAL; + + /* do the insert */ + rc = do_copy (1, fname, kb, hd->secret, 0, 0 ); + return rc; +} + +int +keyring_delete_keyblock (KEYRING_HANDLE hd) +{ + int rc; + + if (!hd->found.kr) + return -1; /* no successful prior search */ + + if (!hd->found.n_packets) { + /* need to know the number of packets - do a dummy get_keyblock*/ + rc = keyring_get_keyblock (hd, NULL); + if (rc) { + log_error ("re-reading keyblock failed: %s\n", g10_errstr (rc)); + return rc; + } + if (!hd->found.n_packets) + BUG (); + } + + /* do the delete */ + rc = do_copy (2, hd->found.kr->fname, NULL, hd->secret, + hd->found.offset, hd->found.n_packets ); + if (!rc) { + /* better reset the found info */ + hd->found.kr = NULL; + hd->found.offset = 0; + } + return rc; +} + + + +/* + * Start the next search on this handle right at the beginning + */ +int +keyring_search_reset (KEYRING_HANDLE hd) +{ + assert (hd); + + hd->current.kr = NULL; + iobuf_close (hd->current.iobuf); + hd->current.iobuf = NULL; + hd->current.eof = 0; + hd->current.error = 0; + + hd->found.kr = NULL; + hd->found.offset = 0; + return 0; +} + + +static int +prepare_search (KEYRING_HANDLE hd) +{ + if (hd->current.error) + return hd->current.error; /* still in error state */ + + if (hd->current.kr && !hd->current.eof) { + assert ( hd->current.iobuf); + return 0; /* okay */ + } + + if (!hd->current.kr && hd->current.eof) + return -1; /* still EOF */ + + if (!hd->current.kr) { /* start search with first keyring */ + hd->current.kr = next_kr (NULL, hd->secret); + if (!hd->current.kr) { + hd->current.eof = 1; + return -1; /* no keyring available */ + } + assert (!hd->current.iobuf); + } + else { /* EOF on last keyring - switch to next one */ + iobuf_close (hd->current.iobuf); + hd->current.iobuf = NULL; + hd->current.kr = next_kr (hd->current.kr, hd->secret); + if (!hd->current.kr) { + hd->current.eof = 1; + return -1; + } + } + + hd->current.eof = 0; + hd->current.iobuf = iobuf_open (hd->current.kr->fname); + if (!hd->current.iobuf) { + log_error ("can't open `%s'\n", hd->current.kr->fname ); + return (hd->current.error = G10ERR_OPEN_FILE); + } + + return 0; +} + + +/* A map of the all characters valid used for word_match() + * Valid characters are in in this table converted to uppercase. + * because the upper 128 bytes have special meaning, we assume + * that they are all valid. + * Note: We must use numerical values here in case that this program + * will be converted to those little blue HAL9000s with their strange + * EBCDIC character set (user ids are UTF-8). + * wk 2000-04-13: Hmmm, does this really make sense, given the fact that + * we can run gpg now on a S/390 running GNU/Linux, where the code + * translation is done by the device drivers? + */ +static const byte word_match_chars[256] = { + /* 00 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* 08 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* 10 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* 18 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* 20 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* 28 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* 30 */ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, + /* 38 */ 0x38, 0x39, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* 40 */ 0x00, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, + /* 48 */ 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, + /* 50 */ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, + /* 58 */ 0x58, 0x59, 0x5a, 0x00, 0x00, 0x00, 0x00, 0x00, + /* 60 */ 0x00, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, + /* 68 */ 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, + /* 70 */ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, + /* 78 */ 0x58, 0x59, 0x5a, 0x00, 0x00, 0x00, 0x00, 0x00, + /* 80 */ 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, + /* 88 */ 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, + /* 90 */ 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, + /* 98 */ 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, + /* a0 */ 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, + /* a8 */ 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, + /* b0 */ 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, + /* b8 */ 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, + /* c0 */ 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, + /* c8 */ 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, + /* d0 */ 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, + /* d8 */ 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, + /* e0 */ 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, + /* e8 */ 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, + /* f0 */ 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, + /* f8 */ 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff +}; + +/**************** + * Do a word match (original user id starts with a '+'). + * The pattern is already tokenized to a more suitable format: + * There are only the real words in it delimited by one space + * and all converted to uppercase. + * + * Returns: 0 if all words match. + * + * Note: This algorithm is a straightforward one and not very + * fast. It works for UTF-8 strings. The uidlen should + * be removed but due to the fact that old versions of + * pgp don't use UTF-8 we still use the length; this should + * be fixed in parse-packet (and replace \0 by some special + * UTF-8 encoding) + */ +static int +word_match( const byte *uid, size_t uidlen, const byte *pattern ) +{ + size_t wlen, n; + const byte *p; + const byte *s; + + for( s=pattern; *s; ) { + do { + /* skip leading delimiters */ + while( uidlen && !word_match_chars[*uid] ) + uid++, uidlen--; + /* get length of the word */ + n = uidlen; p = uid; + while( n && word_match_chars[*p] ) + p++, n--; + wlen = p - uid; + /* and compare against the current word from pattern */ + for(n=0, p=uid; n < wlen && s[n] != ' ' && s[n] ; n++, p++ ) { + if( word_match_chars[*p] != s[n] ) + break; + } + if( n == wlen && (s[n] == ' ' || !s[n]) ) + break; /* found */ + uid += wlen; + uidlen -= wlen; + } while( uidlen ); + if( !uidlen ) + return -1; /* not found */ + + /* advance to next word in pattern */ + for(; *s != ' ' && *s ; s++ ) + ; + if( *s ) + s++ ; + } + return 0; /* found */ +} + +/**************** + * prepare word word_match; that is parse the name and + * build the pattern. + * caller has to free the returned pattern + */ +static char* +prepare_word_match (const byte *name) +{ + byte *pattern, *p; + int c; + + /* the original length is always enough for the pattern */ + p = pattern = m_alloc(strlen(name)+1); + do { + /* skip leading delimiters */ + while( *name && !word_match_chars[*name] ) + name++; + /* copy as long as we don't have a delimiter and convert + * to uppercase. + * fixme: how can we handle utf8 uppercasing */ + for( ; *name && (c=word_match_chars[*name]); name++ ) + *p++ = c; + *p++ = ' '; /* append pattern delimiter */ + } while( *name ); + p[-1] = 0; /* replace last pattern delimiter by EOS */ + + return pattern; +} + + + + +static int +compare_name (int mode, const char *name, const char *uid, size_t uidlen) +{ + int i; + const char *s, *se; + + if (mode == KEYDB_SEARCH_MODE_EXACT) { + for (i=0; name[i] && uidlen; i++, uidlen--) + if (uid[i] != name[i]) + break; + if (!uidlen && !name[i]) + return 0; /* found */ + } + else if (mode == KEYDB_SEARCH_MODE_SUBSTR) { + if (ascii_memistr( uid, uidlen, name )) + return 0; + } + else if ( mode == KEYDB_SEARCH_MODE_MAIL + || mode == KEYDB_SEARCH_MODE_MAILSUB + || mode == KEYDB_SEARCH_MODE_MAILEND) { + for (i=0, s= uid; i < uidlen && *s != '<'; s++, i++) + ; + if (i < uidlen) { + /* skip opening delim and one char and look for the closing one*/ + s++; i++; + for (se=s+1, i++; i < uidlen && *se != '>'; se++, i++) + ; + if (i < uidlen) { + i = se - s; + if (mode == KEYDB_SEARCH_MODE_MAIL) { + if( strlen(name)-2 == i + && !ascii_memcasecmp( s, name+1, i) ) + return 0; + } + else if (mode == KEYDB_SEARCH_MODE_MAILSUB) { + if( ascii_memistr( s, i, name ) ) + return 0; + } + else { /* email from end */ + /* nyi */ + } + } + } + } + else if (mode == KEYDB_SEARCH_MODE_WORDS) + return word_match (uid, uidlen, name); + else + BUG(); + + return -1; /* not found */ +} + + +/* + * Search through the keyring(s), starting at the current position, + * for a keyblock which contains one of the keys described in the DESC array. + */ +int +keyring_search (KEYRING_HANDLE hd, KEYDB_SEARCH_DESC *desc, size_t ndesc) +{ + int rc; + PACKET pkt; + int save_mode; + off_t offset, main_offset; + size_t n; + int need_uid, need_words, need_keyid, need_fpr; + int pk_no, uid_no; + int initial_skip; + PKT_user_id *uid = NULL; + PKT_public_key *pk = NULL; + PKT_secret_key *sk = NULL; + + /* figure out what information we need */ + need_uid = need_words = need_keyid = need_fpr = 0; + for (n=0; n < ndesc; n++) { + switch (desc[n].mode) { + case KEYDB_SEARCH_MODE_EXACT: + case KEYDB_SEARCH_MODE_SUBSTR: + case KEYDB_SEARCH_MODE_MAIL: + case KEYDB_SEARCH_MODE_MAILSUB: + case KEYDB_SEARCH_MODE_MAILEND: + need_uid = 1; + break; + case KEYDB_SEARCH_MODE_WORDS: + need_uid = 1; + need_words = 1; + break; + case KEYDB_SEARCH_MODE_SHORT_KID: + case KEYDB_SEARCH_MODE_LONG_KID: + need_keyid = 1; + break; + case KEYDB_SEARCH_MODE_FPR16: + case KEYDB_SEARCH_MODE_FPR20: + case KEYDB_SEARCH_MODE_FPR: + need_fpr = 1; + break; + case KEYDB_SEARCH_MODE_FIRST: + /* always restart the search in this mode */ + keyring_search_reset (hd); + break; + default: break; + } + } + + rc = prepare_search (hd); + if (rc) + return rc; + +#if 0 + if (need_words) { + BUG(); + if ( !hd->word_match.name || strcmp (hd->word_match.name, name) ) { + /* name changed */ + m_free (hd->word_match.name); + m_free (hd->word_match.pattern); + hd->word_match.name = m_strdup (name); + hd->word_match.pattern = prepare_word_match (name); + } + name = hd->word_match.pattern; + } +#endif + + init_packet(&pkt); + save_mode = set_packet_list_mode(0); + + hd->found.kr = NULL; + main_offset = 0; + pk_no = uid_no = 0; + initial_skip = 1; /* skip until we see the start of a keyblock */ + while (!(rc=search_packet (hd->current.iobuf, &pkt, &offset, need_uid))) { + byte afp[MAX_FINGERPRINT_LEN]; + size_t an; + u32 aki[2]; + + if (pkt.pkttype == PKT_PUBLIC_KEY + || pkt.pkttype == PKT_SECRET_KEY) { + main_offset = offset; + pk_no = uid_no = 0; + initial_skip = 0; + } + if (initial_skip) { + free_packet (&pkt); + continue; + } + + pk = NULL; + sk = NULL; + uid = NULL; + if ( pkt.pkttype == PKT_PUBLIC_KEY + || pkt.pkttype == PKT_PUBLIC_SUBKEY) { + pk = pkt.pkt.public_key; + ++pk_no; + + if (need_fpr) { + fingerprint_from_pk (pk, afp, &an); + while (an < 20) /* fill up to 20 bytes */ + afp[an++] = 0; + } + if (need_keyid) + keyid_from_pk (pk, aki); + } + else if (pkt.pkttype == PKT_USER_ID) { + uid = pkt.pkt.user_id; + ++uid_no; + } + else if ( pkt.pkttype == PKT_SECRET_KEY + || pkt.pkttype == PKT_SECRET_SUBKEY) { + sk = pkt.pkt.secret_key; + ++pk_no; + + if (need_fpr) { + fingerprint_from_sk (sk, afp, &an); + while (an < 20) /* fill up to 20 bytes */ + afp[an++] = 0; + } + if (need_keyid) + keyid_from_sk (sk, aki); + } + + for (n=0; n < ndesc; n++) { + switch (desc[n].mode) { + case KEYDB_SEARCH_MODE_NONE: + BUG (); + break; + case KEYDB_SEARCH_MODE_TDBIDX: + BUG(); + break; + case KEYDB_SEARCH_MODE_EXACT: + case KEYDB_SEARCH_MODE_SUBSTR: + case KEYDB_SEARCH_MODE_MAIL: + case KEYDB_SEARCH_MODE_MAILSUB: + case KEYDB_SEARCH_MODE_MAILEND: + case KEYDB_SEARCH_MODE_WORDS: + if ( uid && !compare_name (desc[n].mode, + desc[n].u.name, + uid->name, uid->len)) + goto found; + break; + + case KEYDB_SEARCH_MODE_SHORT_KID: + if ((pk||sk) && desc[n].u.kid[1] == aki[1]) + goto found; + break; + case KEYDB_SEARCH_MODE_LONG_KID: + if ((pk||sk) && desc[n].u.kid[0] == aki[0] + && desc[n].u.kid[1] == aki[1]) + goto found; + break; + case KEYDB_SEARCH_MODE_FPR16: break; + if ((pk||sk) && !memcmp (desc[n].u.fpr, afp, 16)) + goto found; + break; + case KEYDB_SEARCH_MODE_FPR20: + case KEYDB_SEARCH_MODE_FPR: + if ((pk||sk) && !memcmp (desc[n].u.fpr, afp, 20)) + goto found; + break; + case KEYDB_SEARCH_MODE_FIRST: + case KEYDB_SEARCH_MODE_NEXT: + if (pk||sk) + goto found; + break; + default: + rc = G10ERR_INV_ARG; + goto found; + } + } + + free_packet (&pkt); + } + found: + if( !rc ) { + hd->found.offset = main_offset; + hd->found.kr = hd->current.kr; + hd->found.pk_no = (pk||sk)? pk_no : 0; + hd->found.uid_no = uid? uid_no : 0; + } + else if (rc == -1) + hd->current.eof = 1; + else + hd->current.error = rc; + + free_packet(&pkt); + set_packet_list_mode(save_mode); + return rc; +} + + + +/**************** + * Perform insert/delete/update operation. + * mode 1 = insert + * 2 = delete + * 3 = update + */ +static int +do_copy (int mode, const char *fname, KBNODE root, int secret, + off_t start_offset, unsigned int n_packets ) +{ + IOBUF fp, newfp; + int rc=0; + char *bakfname = NULL; + char *tmpfname = NULL; + + /* open the source file */ + fp = iobuf_open (fname); + if (mode == 1 && !fp && errno == ENOENT) { + /* insert mode but file does not exist: create a new file */ + KBNODE kbctx, node; + + newfp = iobuf_create (fname); + if( !newfp ) { + log_error (_("%s: can't create: %s\n"), + fname, strerror(errno)); + return G10ERR_OPEN_FILE; + } + if( !opt.quiet ) + log_info(_("%s: keyring created\n"), fname ); + + kbctx=NULL; + while ( (node = walk_kbnode( root, &kbctx, 0 )) ) { + if( (rc = build_packet( newfp, node->pkt )) ) { + log_error("build_packet(%d) failed: %s\n", + node->pkt->pkttype, g10_errstr(rc) ); + iobuf_cancel(newfp); + return G10ERR_WRITE_FILE; + } + } + if( iobuf_close(newfp) ) { + log_error ("%s: close failed: %s\n", fname, strerror(errno)); + return G10ERR_CLOSE_FILE; + } + if (chmod( fname, S_IRUSR | S_IWUSR )) { + log_error("%s: chmod failed: %s\n", fname, strerror(errno) ); + return G10ERR_WRITE_FILE; + } + return 0; /* ready */ + } + + if( !fp ) { + log_error ("%s: can't open: %s\n", fname, strerror(errno) ); + rc = G10ERR_OPEN_FILE; + goto leave; + } + + /* create the new file */ + #ifdef USE_ONLY_8DOT3 + /* Here is another Windoze bug?: + * you cant rename("pubring.gpg.tmp", "pubring.gpg"); + * but rename("pubring.gpg.tmp", "pubring.aaa"); + * works. So we replace .gpg by .bak or .tmp + */ + if( strlen (fname) > 4 + && !strcmp (fname+strlen(fname)-4, EXTSEP_S "gpg") ) { + bakfname = m_alloc( strlen (fname) + 1 ); + strcpy(bakfname, fname); + strcpy(bakfname+strlen(fname)-4, EXTSEP_S "bak"); + tmpfname = m_alloc( strlen( fname ) + 1 ); + strcpy(tmpfname,fname); + strcpy(tmpfname+strlen(fname)-4, EXTSEP_S "tmp"); + } + else { /* file does not end with gpg; hmmm */ + bakfname = m_alloc( strlen( fname ) + 5 ); + strcpy(stpcpy(bakfname, fname), EXTSEP_S "bak"); + tmpfname = m_alloc( strlen( fname ) + 5 ); + strcpy(stpcpy(tmpfname, fname), EXTSEP_S "tmp"); + } + #else + bakfname = m_alloc( strlen( fname ) + 2 ); + strcpy(stpcpy(bakfname,fname),"~"); + tmpfname = m_alloc( strlen( fname ) + 5 ); + strcpy(stpcpy(tmpfname,fname), EXTSEP_S "tmp"); + #endif + newfp = iobuf_create (tmpfname); + if (!newfp) { + log_error ("%s: can't create: %s\n", tmpfname, strerror(errno) ); + iobuf_close(fp); + rc = G10ERR_OPEN_FILE; + goto leave; + } + + if( mode == 1 ) { /* insert */ + /* copy everything to the new file */ + rc = copy_all_packets (fp, newfp); + if( rc != -1 ) { + log_error("%s: copy to `%s' failed: %s\n", + fname, tmpfname, g10_errstr(rc) ); + iobuf_close(fp); + iobuf_cancel(newfp); + goto leave; + } + rc = 0; + } + + if( mode == 2 || mode == 3 ) { /* delete or update */ + /* copy first part to the new file */ + rc = copy_some_packets( fp, newfp, start_offset ); + if( rc ) { /* should never get EOF here */ + log_error ("%s: copy to `%s' failed: %s\n", + fname, tmpfname, g10_errstr(rc) ); + iobuf_close(fp); + iobuf_cancel(newfp); + goto leave; + } + /* skip this keyblock */ + assert( n_packets ); + rc = skip_some_packets( fp, n_packets ); + if( rc ) { + log_error("%s: skipping %u packets failed: %s\n", + fname, n_packets, g10_errstr(rc)); + iobuf_close(fp); + iobuf_cancel(newfp); + goto leave; + } + } + + if( mode == 1 || mode == 3 ) { /* insert or update */ + KBNODE kbctx, node; + + /* append the new data */ + kbctx=NULL; + while( (node = walk_kbnode( root, &kbctx, 0 )) ) { + if( (rc = build_packet( newfp, node->pkt )) ) { + log_error("build_packet(%d) failed: %s\n", + node->pkt->pkttype, g10_errstr(rc) ); + iobuf_close(fp); + iobuf_cancel(newfp); + rc = G10ERR_WRITE_FILE; + goto leave; + } + } + } + + if( mode == 2 || mode == 3 ) { /* delete or update */ + /* copy the rest */ + rc = copy_all_packets( fp, newfp ); + if( rc != -1 ) { + log_error("%s: copy to `%s' failed: %s\n", + fname, tmpfname, g10_errstr(rc) ); + iobuf_close(fp); + iobuf_cancel(newfp); + goto leave; + } + rc = 0; + } + + /* close both files */ + if( iobuf_close(fp) ) { + log_error("%s: close failed: %s\n", fname, strerror(errno) ); + rc = G10ERR_CLOSE_FILE; + goto leave; + } + if( iobuf_close(newfp) ) { + log_error("%s: close failed: %s\n", tmpfname, strerror(errno) ); + rc = G10ERR_CLOSE_FILE; + goto leave; + } + /* if the new file is a secring, restrict the permissions */ + #ifndef HAVE_DOSISH_SYSTEM + if( secret && !opt.preserve_permissions ) { + if( chmod( tmpfname, S_IRUSR | S_IWUSR ) ) { + log_error("%s: chmod failed: %s\n", + tmpfname, strerror(errno) ); + rc = G10ERR_WRITE_FILE; + goto leave; + } + } + #endif + + /* rename and make backup file */ + if( !secret ) { /* but not for secret keyrings */ + iobuf_ioctl (NULL, 2, 0, (char *)bakfname ); + iobuf_ioctl (NULL, 2, 0, (char *)fname ); + #if defined(HAVE_DOSISH_SYSTEM) || defined(__riscos__) + remove( bakfname ); + #endif + if( rename( fname, bakfname ) ) { + log_error("%s: rename to `%s' failed: %s\n", + fname, bakfname, strerror(errno) ); + rc = G10ERR_RENAME_FILE; + goto leave; + } + } + iobuf_ioctl (NULL, 2, 0, (char*)tmpfname ); + iobuf_ioctl (NULL, 2, 0, (char*)fname ); + #if defined(HAVE_DOSISH_SYSTEM) || defined(__riscos__) + remove( fname ); + #endif + if( rename( tmpfname, fname ) ) { + log_error("%s: rename to `%s' failed: %s\n", + tmpfname, fname,strerror(errno) ); + rc = G10ERR_RENAME_FILE; + if( secret ) { + log_info(_( + "WARNING: 2 files with confidential information exists.\n")); + log_info(_("%s is the unchanged one\n"), fname ); + log_info(_("%s is the new one\n"), tmpfname ); + log_info(_("Please fix this possible security flaw\n")); + } + goto leave; + } + + leave: + m_free(bakfname); + m_free(tmpfname); + return rc; +} + + + + + diff --git a/g10/keyring.h b/g10/keyring.h new file mode 100644 index 000000000..253b86eab --- /dev/null +++ b/g10/keyring.h @@ -0,0 +1,44 @@ +/* keyring.h - Keyring operations + * Copyright (C) 2001 Free Software Foundation, Inc. + * + * This file is part of GnuPG. + * + * 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. + * + * 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. + * + * 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 + */ + +#ifndef GPG_KEYRING_H +#define GPG_KEYRING_H 1 + +#include "global.h" + + +typedef struct keyring_handle *KEYRING_HANDLE; + +void keyring_register_filename (const char *fname, int secret); + + +KEYRING_HANDLE keyring_new (int secret); +void keyring_release (KEYRING_HANDLE hd); +const char *keyring_get_resource_name (KEYRING_HANDLE hd); +int keyring_lock (KEYRING_HANDLE hd, int yes); +int keyring_get_keyblock (KEYRING_HANDLE hd, KBNODE *ret_kb); +int keyring_update_keyblock (KEYRING_HANDLE hd, KBNODE kb); +int keyring_insert_keyblock (KEYRING_HANDLE hd, KBNODE kb); +int keyring_delete_keyblock (KEYRING_HANDLE hd); +int keyring_search_reset (KEYRING_HANDLE hd); +int keyring_search (KEYRING_HANDLE hd, KEYDB_SEARCH_DESC *desc, size_t ndesc); + + +#endif /*GPG_KEYRING_H*/ diff --git a/g10/keyring.o b/g10/keyring.o new file mode 100644 index 0000000000000000000000000000000000000000..e0a98900da8995860081fcd6e89fd1114c63babe GIT binary patch literal 56784 zcmd_Tdw88i)iyqR?*OIkMa#KzGGKugN}F?&hC|b2w+%fc=Fpatn;dqU-6q*#A9_Hm zkx(#%DzBg@qE-b#K}6-KsFflG1q4I|L8-4*T3ZmIV$njr`(AT+X78kauP=Z6e%JMF zt}yqrX3fl+nKf(H9G<5ii!PoY3Wb#Yg;YcZPfG20OvjW-FilNT6D_NRtwVnr+%s|c zvgYCJxWRJ@NXr~Q_{jclqE)Tk$069ce`I82(>nTv;y-}qwo&x4Qb9s0-JYX5^sJo3g}|9ECo&5@dIXe$_V6K7EL(YQ0pmn{6Z+E&Lkek?}}+ zhN&1}k%5JXXcjD4550xFhoj?$TPJSZl08JE-5nlA_L+mK8Vj<2e|+nvHA>0{&$x}x zW+xOsyZa2=p7V}Es`m}gD?o4r8fBzpmD>4DG+FVp$RV<+aq`g9R(|$V@yH+>>&nkU zLf09UgC8EJvNIsqru5`xBHK_cS_a*bJ%%may};J($J7qQNGCnEab(iQFZeml8~pG@ zm90RURU;$mai+@bR%pdO_H=hVKdaq`pr@FvasKt7fj5S<<3@o|H}U zTx0WuWRe}8b#^};+LzPkCs0>6jN2C7PnK=by*hYZ2YVO{FJPOFe}Ia&YkpXIa&U{$ z(V^%*-7cr1Y;ZE7dkCqdst=3@+I0ldnEt^~7+x@Cc=-uUjl-?`n!NV7#7lpZO^+H5 zNB3zp*LgjPh5;ucbiXuqZ?VijDg+vQq;OX~cuaXi%ct#{BT4qI(k&&UWm^QS7!cG; zwQa#P2D@12;pNjd%?k}Jm^S!snEGQhbJrt?+mty?ZG0>H@!`nezHwR9cW*asgQa&ik|$Xv@*qJ=gZc?iD%>B;4P9DX86b{GT(tAU6)rpEA4*DmYRxlASm_UzB!L zWM?U~b!aP$?a|xjABED@w)sC`rs|Lf*!R0JPWP`;{BNWb5;Uy}?Y#d`C?8F$*uTL&qub+?Ncq2MkL6x_ ztYcGr_}{e0DrK7^ymKl<`kyq1KNnb{QI|CcSJ`&J+sBG+;Jm>~yEu+}hla@tlq znN7W|R=0nxZvR?o|KctAbI))sEa{|;|3Het4;QG+bg{OpX}u?Hd;rw$+bPG_oiO)X zVZ?wMlSW3{X9I7Kj11Klh)K&VVn&%}oyqrHCXd@pFb|Ech~zYqF4$RavnqsA9A-?q zsP^u9GZk|{pfG1k%^Z_Cw0O(zQ>DF-o8btID%yz6@&NxQEn??G@jIfEcX3Qxdw_`} zb1LS^N%w`nza4@PoIP1(j~a=N8(A_jGjVtcB0{8P_6#qXi0Oahh1J-!`c1U95e9G!c6&<&t{6j^dlBgc~nt$swuyw7JSDJ zNB3WIREUfd!H6Ow;K}^4>h0`dFc{Yq9D8QeMn9KRPO&(-ThJHS9HXH#q7v{);^HgTWr27sbjmG47+Gb2F2#n=8z8mIf-%)y$mihqg|Cb8sY*EgIf1(Qn;N zHSbhCpIrrMFuhEi+o(-A+GaU4d>RECBL`X__IGcGPtm=B|8tg|_C?giwzBQ}kHE*-7QF>>!ok_BgPV15j}B-85cd!R^g(oT zRrHoL<=a$n5ggIsf`=Cl?wh!F?#R@Q`%xx1(a>sl(RJIR+M=Ik^nF|O9*H@|jz6f} zl5tweU{q?WFPWUWgL)Vp*SND36U0#T2{AU4Hr@uGWLtCxMc8!7j=)f2CgFl%DEb(b zciTK|7^7Rrw=GJ)1i_s;xLXG_daz8BH!XR3XvybxeH;=GFWIrFhEB-ZlZT^E54S!y z6x}k^`pBr1%wTZC?etmT{I>3B+Ep#}-OPw>QPXPDl!I>{3}@u+!?G6*FWEV?Wb;V$ z?!gWBsLWaDo=ws1jhnK2mbr=#N4F2|J96#eLvK7g-b%AKH z;uq0DlLqIa0kuD~>8Rs36&y*^Snw0LV3V$%fGB7|5ju_xm*Ww=(oesmmpqM-817RUHLdDyFW5W9=K^Yt4u%g-gMMYK}LDc3*X@d*$a`T zs_t+k_#6`~@)DfK+O|&IlzF&d(}q0{QrY(3uHO85=Ca*WrM}EN&b+g+-A4?)wJrL@ zS>V~lam08k_@pZyrQZnsS+c_^02;gd7i=gF*Wu_xaQ{EoLI=T8Vh%ihrWPuTTLN~fEM4P zGBB)nO6*jORmvyvQyIVe{klF+&;^W++csC0T)Ph)Uh)JdiSy~gZi?=(s)3o~G3}EN zN1xbq>pG0cocU#j*cdJ$Fid3Gv@N=mPcqmi>THIhd!b~58^(nuZES}k(RtZ~P!I5} zgVo_l8_&lx)3i?1#fdi#zZ+}F{h*JO3Nwbu#70n79n{?e6*D#hnd^eYH`w*FgOQcq zgvd^%CxJY5nk&XggT!VPxapGpyMz6Rk+lWJ^Nzj_9?JY$0cC^>3#)F|f!i22l6?v? zYIfnx%+z9w&?7}gcF56v4@8j12iP6EPJ*l)?@*Hgv;eCou{xJC50uN#TrS5jm*)?X zY48~YWskcR6pThK)BOx0g!rnmpmxdoE6b!TpGpEst+I~%39O~VvY($>8?p{W51+Vuc?2RfFGb>E5# z%7|Mw9NlZBVJ{Bu8_855y{2I>?rdU{cmK<_2nM2`J+SW8UCWU>3PWq5lg15hd3Pwf zgNxg%{{RZR@4L3l;pmj%R;;p?IU$FrK^WjUMMrm{cql}E(N^G~=$DZ5;D$n#eP8z! zNYVWRTT*u+9;H>fAF{OJ)2q-MYd)1bvs}d6pR#g--yoh2BRJ!4~U>kkx1+rEzfZ4>w1(n4v&@2 zsi|viTiw5~l4uC8R#aK<(Nq#OABKh)VbE2RuKdnel{9>#D#>ivRUd;0=wnPPo6!uW zU2V3o2;Lm>e87NawLEC=osBw&zm3U2v!de*VQfd2fX3RR8;cER>x7oy6n{+5xc8xA zYSTlfGmwpKgjBDh;+aLo1}n9>;1g-3Cg8affBc<^Ka0zO&n6MSI{Xa~;D;m*Z}RX@ zJp8tYD^TzNp831N!%umb`+Wn#q0~tj78cieIN{+z4}ZzSKlboG4<8RLw0WNA;e>}b z0AFEb81gVlUo~_ef7autPJ#4Y(AW6EGCoX z8J4t#I-TH?1wSbG6v0mk{(#^ef;p7o%nFvH7j*>mZ$MFFl$tKML~xDZQo$<(mkGX1 zFf|7YUcnWDzbd#=@b?8*3I3(vYQb*`K1cAOaCHXMxq{y>_&mYog69feB)CR!yWn|( zFBe=Z_&ULLf^QQX75qcN^9BD(Fi$I#+9P7A@TUbg z3;u@S7Qv4QZWX*;@G`;s1g{W08H02{T_pGn!5Dg0B_K|Ji!^_)o&a zWuQ`#$;@ZA;A4SpTg~ur)WhqAe?<5{C3p&~rT*EnZt?gZ^f0+B&#MQZA96V5S!8ky z_=kjklF*+Lto!BYBLbPv@bGya_RIBg`T#t)fj%H5DRtG60sf)j1rqa$;PV9^b5tPn z0>Ldpzg+MIg6|c4iQv}+Un=;tqnWl%@P`BoR1=N~%Dv~<0Dlw9o&n9G3g1V3r+Et6 zs!r(TLjS&C-M_yStnJ<&!BxU@2$uK*YR;&9P66HO!!qHiAH|aq`ZtCCNx_L}yMFyh89I!EJ(5f)j#o6x<{Dhk`E?{3pRF!H2z{d8P$d2+jzO3Em)B z6_V$p8bd^@ho=i&kJEDmHz8K3Cc&+O6Tr3|NU&+IJ^i?wZE}R@O3mx`&H^2Nawa{9=OPyqKR&`sL}TSdC;x=e-NINBtb2fBJi&S-*YZ;o#0)98wLOHJkl=` z{N=gCT9-($vfYa;21vl~lv-jmFn-m7^*q}ySjg&L!N?fDXFd9fbxb>7=(7cDo4HnS zlhF4GzEJR0XpRBZF1Q-W2GnZ7KNp-5T)2Srs|9}#O*WwPT($#4WkB5{^cPUffOmq1@9Al0h(_>y({>B!2;EzsK$VXsueEd zfT|p2pVxqH`|G2^g9#JAy9MjSWy^wmIz2pPIq4r1rn3aM2wovrr~QiHi{;@dk3MAu z)5e9qT(IW z<9I%#u5vi0KI8DE>Sl-A)YlwtcRO}JS`XL$u_o?v?C)F_y_p4JJzRb)FOq-G! za*4>lO60#taE}Tb{w*eCupNK12=Jq9#9iQ@YFhTAz&IuMWLx9Ow$_tvy*iAU;8%xz zDy=ERA2zEH;*Xe}LgEc($By`;&;sm3ikw;3=9Gu+e1phyh2q`ETST6Zi#*>oIMA^x zT-rJ{=;_;)>Ime+U!B^hj?xhLD)oNgEmH2sh5D4iVZ%ciPR8F$pifo9LLC9#tgi9e zWLR{Dk#*{mUb&z0+UGhoMN*UJ(^9VH|BUgSiq1%z5_4YKA=XQiPe`4mzAL~mz3u&jC(NYV%8J2_kxzymG&2RPUdz+k5 zfDLu|hBvOi>DBiRw=%B7{!AH}@vBqca`M!vyFA_aw!%~-Kjx_|J^8<@V9E{sJ{5EL zewA?eL8YxY`M(d1(7!tM19gML52?>P{3G=xhwoFjI{dJ@!{M#!UWXr1KX&+0^;3tp znHiPkKCWJM^sQ>I!%wPzI{cL4owp0nwsX}jqQjknZ*^mBi{WLk9e?EKkNrj52mYz* zw?exHc(Zyz`s*_W2mSS1ufKlh+2NPfk&>D+Z#P~Z@gGG08C9qL=-HVa&TiDHKP$L# z@~cy?m>m@I?^F$rz6(~?^6W7?DCBuneag{a^X&6q)wdn}9rd8YBkE~~Rp?g^PYCUB zcw*=khu=_p9DY;nb9k>BXWFn%{asCR_$_sm!~akxI=oMv;_yG!nGWw)We$f!*1M@w zkBoiSZlCZRAKK>VCxm|P@QI=496l+;Gijan{h=KWpAy>X@CQPB9iAF02wVQs zLZ>-=dZ^gp>7i1G&j_94@QhH4!!tt{J3K2Ccep6TgL^Gcafml0H7*HV4SXjQYA*Er z5D@Pd{GyDVt%6??oBSm3fcl-{t;635{dYqDi_m|s_JRJU;Fr~2;7}*|x2xBI#|!?0 z+5^mTxA1@Fw;ds6;BPAah>rvRRK-(NrK*58tMU+SW}U&o7_1OF`RY_vXaWI#b!txN za1DXa4xQlexuFj@d|oK-YGJ9f+Mn|s;1$J^_sL#>Q4+Z0Hap-zSUlO{> z;fB!d4mXCr1KfbR%tgKEBV8zXb|_+awwRE?cKnf_zp3~m?gal-)gtXP2)tP}OFMkh z;Glh4LhMui>ePx*;Qw403Ve)M+(c417{LD5jXKX)iM;=xlV&>1^RQwS? zhO|>vucUnic(dy9^rY9r{hrQT=IKn@Ylp0-GiyAZS?lS{I!|XV_jKmNp3Z#C)0vNl zK1qS`gMStJw1&V}hJrcts?cqYeszd`?;Fx4J)$%32<|(e&XAwKsrVy4Bu>oNFtBe4 zyjgw9YlrJRJ^3$BPp%KmU;_N=)D0oM^FjQX5SIG#gRd2eI(%bjvBRGaH9LHh=l~z< z)E7eC2xmfuxsbu?JmpN{uL|_3>MNm6C~gDrX0_Rq^DCa5U-jg?)zjgxhfYR5{MD&% zhUzo~z9V$L!*_*R9R7Bw!{P6RRyllM=vs&G58deS1EIjTc`$UBqklgX_%lBUJ>=*= z4sCUKOXz0~|0ML1!;gmk=7KPp`7@X=vxg~+c? z9TUdV+Tdfu>|g%s)ceA{8Uh~|zS80Mh5yUp9jZ zdjwAiM}Xe~9#9j*oD=crY!~r%{4pQ?sN2LN;Ge3>!#AMOkLzL|l}R6w2XmI!7w3de zClS9ob#8c`hQRZ}*s_sdovICA>hQd9hr@N@^$thF8yub=-stec@Q}mxVa{2nL!P;+ zB789LY=gtVHG*do%5RGa8EnTN)AGkU5}yzLsj4wNZM;$+0^Y0|q>if$rjD&Mn0mO; z;G=-~t3%pACtJe7db2frtY!hvvhZmRw}#Jfcv*Ok!z;qUd~`uL81EN(x_YtKhL?mB zE^RCv%JP_tx#p}_|bI}*n=Pw&f_B#z`2Y+Ai z2SvBH3T_g8c*Jy>^|1y|5FnOh>2e#5Uo%&q3R72p;hvz!{Rd0;k628RIZwp`Q@ayTbzxeCx@R3 zzv=KV!vA#mX-_wv^YE|3N1FDpQ@`=_ZNdv!!L*DJG?!- z#Nj`LwWmq`{|;Z|=sQHObUuFyuXglT!hH_!3}+nP72fFZU&5bucu)8%4!;_{!{OJ$ zKXCZ3;Vllo9{#Dre+vieuQ$THU#aDJGyJr}e-HoG;kUw^D>cu)@E;w1JG{%`e}@0* z@V~-`h!(T%?}U$W_}%ac4u>MM91ch3I$RLB)Zs%SYaN~xxz6DuBab+IOyp^Yr$pXx z__)Ykhfj=rfD;dXb!uwlREJNCobT}TNTb7NMlN>vtjMJf&y1{eI27T_B0A5K$mI@~ zMy_(WJi=@Bnx`W26NhII7iv?dBehj!q@TTz7z>NlP0qz8zt3D~V zu2<-v3O@w;M-1jWgPRP#2l!^e*M;u}zTMzEf$tUkU*X$<9~1oP@GZd43BEqO8Te(v zH-v8nW=m|*&7uAx^rypI7r!O=necYt5y8)fpZECfys#ZbuuL7?{n_mgey@y-=FNGAw{`H3bwxfS8a*(qL zpO1V1_=|>z_Y?WU9&f0^@!SzQ8~8!PQ|5TS;MuF2BTF6s7bC3>e<`xY;V(xvI=ne@ zt;1i7+~Dx7k((U;dgMC}-xhh$;crBK~r{M9{zb`#L=IK6qxZ_r=E+P*V4i|jR;Yh)K z4j)v&7ymTR!38fn+#T8B@Ty4UV9T>QGRfg5BPTfgi%7M@Pe;yo_?bwv!_P)8bokJM zn8V`>`W!y2V1vVx3cld*;RQc*_=ti>96qw(Nr#Uvc*Wsk3ML$4Wj?lGhQm_|>K#6@ zV6np|6|8Xh{RP}t;jc~=79@eU!Y1I{Kf*oRCxPbzv#1w^-W1{f?GC}s5zbTiybbtU zB0IggiTM>^@id?=jBqV{IPhFxQXo@o*)U2wTD~qZ1MIn@QnA6 z7TNtrWmUh2vg+SM+7<61ZP$B9o0YVm&*@*=7hA^Y3%2KWLmzw(X_?O!lTSY0?$Is& z^m{04^Lt49?e~!OhwmZn6OtBl3dYw9z;o5MobhGn8n%y}JBT^P`J0MA;y)v8p*j71 zSFql>J9ZW67YlE>;2Ob;1g{j_E*J&?zx9HV0e+trtXIx$X{Dx_6U~(1B2^$<`aQc2 zI*Y60>r;uol`}iktnOrQeAe34v(_e4t7on3%l5B4E1hX;?}^V^SzI*B<6PO%Q4;G& z_Vy=w;+->9qAxQiGp%Sw@$_?25>yuzl@z%jo{DF?U$yG!Zc7P&iRLevUR_jOTr#^_ z^(6aN7TVlO|7W>n`_hS(eeq70uFOm4WGE_v6s1lGTh^$=<^MtA9BC_pgvDN|0jw?x zO81bdwB$8Pn%P07K}M{K*Pc0frMr`Pfa4H8K5;xH815&SVxk zrZp(&^h!oav-t5r=N6ZcflAmDU+1b^j1Ej!rs8cqb3_wo6wN3tsZR8^tu)W2B_sq1 zJ+W*TRv5W#-hTPEM`lm276C2KC_zoz7o+jDq-&CiPF+W|Gi9rqu4c}h)sgJ$O01mO zO^i|28ShHtVOBPsnw9A5=*f1*XQeZpiDW*pE8dpLrsAWS*QaN7jFk(=o-1a1B9oq#S>GSX z^dFWGi)CX*gDOoK#l_PV$)d3+yK)9}2W6w}NYrAUi3GpP?t^Bnw(P8zp(ne# zELGP^3heR7tdo(s-^o#;J5bs_?bH%Q>Mj&|swdHF^VU@q0ZDc(R-$Vubq_N4D%q8G z1A3y z1G=ktnWvzX%c@nG)=#MqTnHmmnODn1e@86nv@)%pG5{ct*KK9GJEd&Ibo6CxW?tQO zgzLI8-KV0=UFqCZx>u!HY*dg=_pWq#SGp_F6O>l&>9LJ+>B_Y>YgOq>26U~>LWkN0 zbhZODQl!Yl(j9F*ZMNmgbxTNnGqG%LoNfzKPNqNO5_!6vM!)sM`%Ej9J2l9eG{hSB zXSy*JcIFsmJF5sox}zHoTsL%1P^`Z#)z&+wWm<7D4RE^5(Nl?JDv??5PpZ&xlYgwM zEuEn@F(rq9j1jnwnQWRVN;xJl>ms-QM4vF~kzyxfmr-Xt-9g_Fxv*sspdrs-h(=qN z&gF}xGpTGxW)7scg0yAOVreY~nlfElfl(OGv~_e-nz%2%sak)sC(&W+rRA|P(iXZY zFrsw@5`EoqG=#L4$*row)U6}gZ%cq{q=_rDZK+Nw`y81!VTNr>K&aJX2h#D3$*H(X zoD-C9Bj>Q{=z5w4I1DpNN{k%B;pJOWE>cvhoR;8pjSQ$E2%uFqmUcw+2V{;H1Q;3V z;*wab0~08Ek|b-(NL<^N$Y7Zu^pfeOJ%Zwxp)|fJQsz5mVbrKI+uLhfSy~`SYOM#k zwNb&Sv4ZwyGx2p+(qfJ(vOyWrB~Z@Z7(AeOYE8T|w`Y5?ILTsB;%gbXlWY6psoW}% zygJcmGq9c6o6`#HPO<4ZV&&AA1DB$qvp8%-ZZF@wIw$)&w1sC9J%DLRZT6|P#y0Em zgdR%6F^Hws=13*;j8Hkq<0%gLoI ze$$GBfp(%t!MT@BP(Z`{FEDHRE$V zd-(CA7a*|(7-VMcA~Vl_=Ws1;+my1Nj^xJTlQfr_RLqAxJN3T*}!q zjkSo)rd+Z?3Yls+FY0Wdypl~$JFP@Fudxkoy$9QF|C^rw-=rzzoGz=vcr3P{wqfbA zSi_<@MKkQf(gxDz6nliad2>p<$0gAvb4uNFbV*~&1+n_2jjb(n${clm>*B?+hSnBR z%N@0T@#5%$n#G2@BA_p=ZCKK{INB05QL}?6lcX-%T-#LN*wWB6r!t6K+7fMAiVUMo zO^B$PQLRk%(A%zh_06%xHO(zvmFgEPZD@+>RL!Uxml)DHu`YN)u&7*C!Ju+6MyE+u zx?M5{I?hC2A``IFsRSGBgs@|t$zzYhO>miuwMHMnQ5JU2?ZPiky_O`CYU@h}Nn}Dt z37olg8ixx|~+w3_&@$D%Hu*s*-F}v%o`Y(|SzPEYnKRUQ$LmjfBiL|VlxAk>mqmDUnJuEqJMU2VBC?fXwI^&(OWc#W(RMMI%Yzy?Y^_qmzx?%vWw=Kq% zUJ87npaNZ~WN%C-Lvg6Ltwko8Lv~&Z^;ASdm8_%UdYBPt3FoT#&F1^ny@|e9Up&rA zPyi#7@vbpRw5=N}x>8yY(Lw4=(s`d|!tl}tk>!EMpNV&6?XG2MrSbFqZY(P{KE7>j zb`&-ltWt4{K~_A;Y^FWir5i&`0UB0(dC?mkiw3X^W(tbMx_a7H<~xyzzBS2?woD?~ zrzb(zPqsTSVaHQ_Z9Q1YqKBmmU6;rl)vwFZ!$W2(*F8CN82d)1BvEWh1U<#L%Byvv zuPdq5lba))o%`4j5S%-cR_6?fs%p(!2|C*9oM~6YVkiW2^I6!#VG_)bm@Xr9nmw-OL>#xphzN?zeqjW+41*-zuB47K+mA>@1lEyH|Y zV%;BOxG+|v)k!z~oYsn7qNhCiOSX%}V-j`LE;;R62lgjB)?~P9%=Tn-5ztG_xi%)b zTAT{|i2aaE0(*V*D=aOZN+na`T3AY7lD!Y_0%cgQx<&ih>Eg1=oN=X%0k~uZh7BIs zM-0rmjgj#QO+<`YQrMXGHV!l$WdlvjA2hN-e+xZSs9uM8JG=6W1h+?b&1Iy%v4=nvj3u8>v9$V{*G)4-QAPzMfTho znNePbHA+QwY>ga;x>QiFB1B@gsjSv#6jmKE_5-U@WIX=zib^t?{go2qcTxXml+V_? zNP#@`(JP8%-vNGmTW2w9DzkaTY%L^-C6+k5NKY8tfj8BhU8=Lp_Q5g>X9;aYAXd3- zSYyQCXprqkYll8Ei$OF64Tr1-aRian3ft93I|z|s|0EP25UnR$vEb5r=-4pZ3!Au7 z9$PvdUmbH?;zA))j78^gAusGur{e(^+Enri{J@T6a&;nZB+K@7$TTIcFfyY()RN+q zQ22~&`HW)HkpRnH!>FapP;Db#xd`7^0MzM5YG^Wllqe5MV)2#Af$4vWhy8#O{ z+5}N(I+d7SkO%Zjv{hu+ae5{dfsL`FygCkTq|-T9P-N7RdLk6Tj5oz|P-&Vmg)=P( zAyr_>WihGkxpE6uH|JpqPoi0X${ff!4&QXGkLu4RgYr9yF}20|PtUrw&eYwBBK%`G)8t<9Y9^%e>O z)Q8(6S($$DEOWRtiEz~ON@ie zS$0M=(V1jVaxJ}gFo~5A-^k98jwuVpuuL<|Ov&Cg1=F}912!oFH0c=(;albm?=&tU z*h43GrjG`uVdQwgKO16C^g*Q!q}qcD@00~_nkNlr!=2}Y$Z0}On%%KIh|~)?BF+Y< z`5<$$kaKN!hzlacMy@!`cZbZN(#)ZJn00%q4I+CU=^+(})|`nwwsd=l4rXzdWrsr9 z<8o4U#pu_%gK?5|-LT!Df`f6=b@ga%v*!4fR9!#JX3Cz6lWMAm1`3WHN!K-m@1X3_ zE2*Z2*a`MB)-}YUNTuCrESSxCJ}30x{F-##c~}~$;QX3&UCUB5pFPPTRaX+{!MU2j zBV-JS;Ox0nZ*`(u(JSuY8&s1m#1LjHxZcvFzoOao7Bcvaa4G@niWAEJF)&K!D4XkUac)wU)vCT_(VZ+Zv&t6CaeafkeQD6q?{&)vw z_)?kS)0LSuF(@8qT|2pe(if1}K3uowWG9Y>mnSD+WLZU18Dt5YC36naBTwqLEC8`P zVl&0&O()h#>D5#*nR7)B1uEA!LZk{H4z0ju>B2=4mW~NQCfD4SkvxK?$;q7EFY&om zq0z@*Z(BzyNsq}ax^$O}=Ku4tI(C z*l_Tq<{;ov`8v&=GX&sYh^K%-PG^tnVaEoNriw;m2@EmC+2EEc#Q~|93lusaZ6uhj z3<|38;EHVV=rn zsjyz850HN&Wq{5b3-^^LmPDGA1m(Kj^5jA~2w&ax2*lX_k( z^Q>f!ylrXRn;2z|We7uM^Tjjb=?XT~p%Y+SlVyKQj$VZrnu7XxZWlP0}tRCctcIiKcK))Sey`LXvSJ zlsnRmO%09Fro|V;Y8K=CIa(KMxuB7ENsu+oFozM=_mwgO*ohD;Q97)2A1bObe_)yr z_ocELqcU)hFf@={wl<)UIIjXf6jW4M&x7Jf8D*vY)TaqBG!v6s=RiO(ax%iDKW|W?c*T8t?MH8^&KgPX%k28_P zUNCs;8|JmnM=2Gu;%6X^146X=y0oy_eF2s8)Iw&?-Ch*uq{;!*D+^3)0y@ zEA_G0>Cb5*CTtQj$qX+m8zSu{sYzbhlooduvJzgcPykvPoA2gXJ3F!;j`YwK=^y9sRO1jD(*s^1!Hzwb5b>ne0B2aG`HhHX zn?kdSKr;6uV>k7~Tal(Q*(EXk=z18#>*)3tpg3RL*oWBIckQ^>xP!#_XoE?Ey(|&hs8C%BQi`78hkC``d6+!AfOz=d*n)dHoalLxZu!^j=!n z?&->YK3ldrM|0!7$<%r%BvhE2uI=JZmCNDfxP)s}NSF0tB z^&Gcy!G(b+148UJ)HE$C}hj9UCY(B$wk_YB-u{z zOeAS~kAVXiTMXK|kql>3TnmUP$*ZmVgKXtMUJ66YpqCPz@av`Zg%z94DZ9|nRdgG| zUKjOZ95{$i?z9@P@ z-Mm~tT@YJXv$RfbZdT}B zG6cFX*#(oNGA){mIjSGWtFm3<%|jiDeq34Cla_TH5Ut6xxL{230H!X=rm4^l0|Jzj zE~{+oS!vHwr6(Y(Sa@%2lLe~WmBf8ls-8^oG?rpmF>l3sJ7o>&Og}P##lzub{&oyz zV2*O0ft2$!vbMf)VYJC>1$ql;IB`ABUZh8q5T76<`^en1N!)s@F%c8G; zrMdW|ljt#?=3&r=u2V++m`N* zVK}zQNtuXd){@&juu%_4MKDl#|9RR4#+)@eEw8AVS}BK&48& z^Lum6!D$7ZD7)y?MAI3Zyk*siGEIaTq@-)F$k5HD7|@MZlxpil6uMgG(m>^KNm;9$ zagc%;#5hJEKtrSjj9b)^!Z2rJaO{h<$T{&ei_wlYy!v3TxtUuFZGG$YmX*%WxJKya zPVxDzU&w0;sA9Eh#ry@8Yvd^?Y?jRb3}os-h=IH=oz+aSesIw!aAyejesDQ;%+bIV z0Fuf{Uuj8>9KYIjI>p3q@aa-|4DZo2tzMDx#~` zEialFF?8Kv-C=suxchF*g&B15KKRZ;MwuA@fo>I7S>tUl*?s$2Q)$s3o?vVQCcC)(9J0=fQc1MVHp%&Ke2L zSheFwzA1_uYw~=-63b931f*@OX>KMXZo&&ow6-bQ5(679!JAAM*VhW6!slp~!sg5t z8t=k4MVp(Wb-3!zRFI+Rg2om+SIP5|nx;k3ra5pOCC|k*^|%MW0vFu1pyKefG_~@| zyB4*z8Iyl~-5lH>@uVZ28Dd(~J|yC}*H1U$+-Cu@gwY3+qM z7m#=R!2w?{UW+{kmQlMH4k3Gn_a&Nm$KS>@);C6%(7`MTNVcV7Y(cqY*pW%~{c~PQ7uYC0U!K-oRRT)j-6%yHffrDE@9h3Ic#tIqGSd|oc)1ZuGS`w@= z=#1)IFpw*&(`~rKNr7n70{b%CnitGM*y}Lvm$q(Z@K!5!LwbELoR1hRl@UU`OJs+> zg0Hv+uhhv(BB+Tmi@qeRPr8J*6$!QPNRx(Tl697Ka*ls+3R&AqFykvJ(j1hgKii%w zjp7z*y$4zaPf^GhkCi~*_tKf<7n+p(5t`9K8>;u0tM{3MBNC;NAUE+-CLPbd9 zJdYA`4BWKWuYjRUBgo1O0$7dLjt6uVe!7Hm^*@#r@`lM@1Dq z!DfYgZNpU2s0r^5;O4g6jES*tpp*6PT2S+Ed7MX`7=vs|_>m%*OGnEcGXA=8YZ%N!T%*oquY zl>I`dcUV`Z*ydX2jP>DF2`pM+nv91cw!=*GdT7G$>hDhS{R_RrgEq9JfKSi+-SM7& zjt-o+x2?2GM$@=1F21fG+orDR(a6Xyffv=$wd*C z$e~eLG)jx4BD!`M7OsX`53x8id(G@8aSmbSbIqlgm;;SoLpLp}Gl6qciH+FHYS+-(NZwG(kIZt+;Pa3 z2QjA??Ircmy=RUM*-yql)(bm*Ro&{fCOWgQy9 z8m>5$B;$3unNoO92lnv)>0TeJcQG7C^)5#ni2j(?nyE*Z(35sKZ(_vO_NRobdkba` zQ-!a?Vva}q4T&{khjd2TXGmawnj!J7DFE&~h) z+%Yf~TD}XzJfPB0JX=M3QK~PFd(9af8=Cf`*ddVxxDs9v9WRU5fqqjGn=}u}Z3n9d+c|z!^ zLJI+IQ#^*36wh8SDKf)cmcg9);T`G(vUiK+rMw|Uam_qdF7uc~@IIJ!5u~|wy8a$m zCfT%-+RDdikQ}G`bX)Ul>q+}>Y8t#ZqN_A&LNKP4b}P^UV^{r2N9(K>bY(wWJ#50J zl0CX*aG^pkQ?wq~IKA)V6iV_R<(K80-sY~cTBNm6`%1Ga679EpnV`vYIkX~Mwla=y zfFyW22diziK)i(jb1H}0h~dS)h2A4E^A%SL!eCt<9=?j$z8@?c=z49t?F$?{@zPGD z#G|WqyjO&spvh1H9S{bdAW9L~y0*q#7_6({t_IpsE3wc8NsiI0ljqGWnrJu0xE0Al z(LFkq9ht$vl6X5K@&1blMV*%zw5?7l7k=Qkc*-pdwoRerI%%NA5^tLn+N|S!DltR>ovwRTf|OMPw4;yGuP&IqD-^dDF=z^*|a!r$Lvjl>2VIPwzkWfxl7|0zO(^v zb<{U3odZWF;FL`D&Viq+kYzJy4qo~U*cUa&<}a?nnQV#QCc?c4M88EEo9eOEaY3vV zn?E?I4f1Ggz&_Lkb4q;S*ajwJziE(YX|$$NHng~4!;6ey*HelM|y4K79)wuSZPG(d5f)S6nAw!kyZsFjz zb?Ks|4a=8eGZGKn(ZeoNHESy;JpBBK-4>HnoSsYZ)?3=$pOA6QJ0+5*S@znOA|VG2 zE`)|d=1$dMK#gR)yP|yqGcReAX{uzr{h(RwqSRj!m_=7L&UqUfct8y~WHztDWC7nO zX6_7u40FU-ZOGAe=w9sot=4yxuxn^9D}hJ0#q4b*Noq`>-Zp}*H#OzE&pBO#oie{n zy-kJ;t%h*tYh5~ROSM2+mvL~mjA9^>)r_<$7SGxv7^7ITkvzYCX&t_9GQYv-CzOem z<_;N@3IJV*TI;Q=E__Ezj)Lc?Q4}qa?r`Wsu$6_DKySJmBIr${lsZq}WtO!d3RxLX z$yqacTRT<7SP#;z;vC1EwX0BqjJOiQ2*VkCaF!0KO_y>fO;-cGXiwtpVY=+wVl1Lx z<2nm-jL-d9yQ4ZfBD$icmOHwmV=yb>(ivuo(NRlk>K9|K7&`*#Fky@x7hPJ1P5RMs z%Nv^NnlXcnj%i+qNhr1mrgiohv5OmU?SPS$V??&p&8x3lf$jX!xz2BF!u3M9RAWY! z6mge-%qVX5j}cMd#B+eLT4br58I-}SETR!@mU|OR=)THemDiM@nCLcbWpi#;Knvy8 z1@6S?EoO4sVUZQ9!}po8FsI%JOY~sv>gU-!_%W=4MHLUT%;%h_fvfWsTYMD8R4d#O z!gg}rg~CK}<){})WkZ6mNy&xl_k1ngWk-#kE!S4?#e>DQn09!VlOIvS!_w9znB;hD zB#$liOLz#O`5G24hLZyYmB%GGGQzRN1@hq}q+EhipPB`jH~GmVJl8D7$%%%IHMl$q zMHBkc8f3}OAd#o8j)z6iC3$R#E{@{d2FF-XA)ze61dYj9AM6;J>g#aKQ$HWxoOmP9 z9K5cHHN+a$3v&a^U3mq*@PMti9?UgZBiU^}nO3ll!-f+-pek=v=nX(^09U4xS$vuj zv!A~$hz$5C0DMt$eQ!IKFeXyZjf{YIygo0|OhvtT0bxu@%*@32G37C{6yx)Svgd`# zG&YFwjRmeKtW0L!>Wi<$rStqaeQe0ubfwxlcuz&%`!OHk;j(%xx@-)`I=Gh^@4y#v z1!1}wN5CU(qTWYZr`Y9TY2oYN?2 zwUNv5nDp3y`B>e}4G?UB!coz)Gq$}*cUM^Sm@qeJ_4Lb;z*^%l6>F^=fnh-@gLtMQ zixbdHzOd7B#VuW4k|U9fV?FXPwviNm2sU_D!!qN+t_$TBwRHt`C4$?v6fvM zYTif2r$bR)8R>+AK-+`|({!T=29bU)K-P>-SW|+m^MrdpQ#qUgDC= zd}PYEk##rqoBPSiv~WLTZDh(uqW1j2wV6&OW9*d8na>UacWzqqq`!!*pfoAN29M9k zg!@+bM2(dMEd7SXTMs)>SrIiO;)oecx zfOv>-AaPeyR9%SIi#cLR?bG)4yI? z%!+s|5woy3prWg~i^kBWU|h>TaFCwUOkKvBBk&{nZ)E80ZxwLcT=|Y0)k=SLPd3T< zjR5YG_KVTM&0lR443}EVxRj1_pkLIGpFa=_L||)?Q>GXjB?HCgXY+?L@wiGcD~+6P zb1#t%BI!7E(H!Drn*m|+GXqaoDR}VeB8kj^A)6bArpl&T38@VVNe3J-Esopjuj!!C zohf&%rGk1ZwTy$Q&roT~IkM~^;g$ri`6&5Fm0mnZ6?5kUCC5s6G}i(AvY>upQIg6d z#N?VGAjmpH9uGJ)`3^2VOBPG7PMBANsSa8~)S=&PE2vA1d4bz9zux)ih zl3^u`e}@H|_5JIu()oRtH)Eud`2LC=@_ADL?XmqpR=;`eNCh(=+Z!V0xI%ev$eNDh zzewc&Gbjh-a@#%%GZP*}GyUFcZ(FS^pdEOX>Fv*XUfqt(H8jI$o5`=o^F1j&BJ`8} z5Eni>@c*;O1J^^pS0-28_1m=z%=na-f>00KfgdXZvnyZa9TkgLh(=TK&(~maF^U;v zzPu5`{eX6S&ufk;+LHN?PT|!vkc~0mt2E@{4F!Dmyv(K(iM5w{SvKK5ny>vIDVC$i z?H|b%VLh(QEnIupVTi^l!!=wwNY}Amd3~U=ZxR zjdttCVSB;c*ubZ8@dch3odjJO-W0cPC}pyWV>DP)B1qiAH6# zwpem4!oH(diIty;#cDUvpO6o?Yp=V~s#q7iNE3%Vdh9KuN*E5LMWeVAI4QAGuLMAM zS7U_EqjvCP({&}^FiD#)${L-h(u)ErD?OR~SgnctmMncZ3^#nh-fAg6W5SH>h_ZDn zE`rAZI%f-tlIXGuTwJ8L2JFaSlUSX?OQ+H#F3wZL;$p3#Z9Qw-*5hM#a)hl_x|sLF zP*|#~qqwR{-g1W?6qjImtf?hsWkput5`F1~FFwTJfvj%BHVpP>T(o|7A>Nzqp$7^p zBvD*Bm12#_Ds}UpdcaMK4qP^{$+!!_IC%YT>Eqg>)V$ZJZ#eKYLbiRh?ZHKL{epeY z)kx{IVu&rdmaI14Cop7eJh3p4N2_FUx&AC8V@K zdqh2WJ4#+b6|Y7&bfs2e&DNEQ$6*|L17`}?v$QT_xb^TKz0emYsC!jfk*z-HTcBtu zIn%1rn%tf2!#hmfnM^;nyOQhN0IAa2%(tcWi(S5GF`@v#7>Y_ z^0q9#mCgA8>q-3M7OqQ6P%slFp&~dh;Tk5*o6b2F5tEzl+HQ194ukYZNr_f`b}g<) zx>s3CO0~-4Uy528hjIp!CHDri8=oh~NK0Oe?2Sti?;NSnT>ReLh27yHBH&0}$wIY=1p}-4F97QpF z5U)2O4dn7Kw__q^j4f{M;uRLTEv}n~AB9CdupOgEp;_}`$)!h^d0kPH;jG#py36d8 zm``h26K1y_;1&lwvYV2ZW8t}lMfgNp-|3k`wl&{CE2O0;#DFQh^wjj}<4vp(eL`0L zZKfO^zHFC6;HO3n!HS#{TG_bg0<58VARPi#TRz9!Q^z#k&>-^dAk~}P4o5-9Npg-z($N)IBPm6XPs;I zC1;AWtxg_I%B|`D?5U`@q_nKOVs>Sf;jfujTNj0-VfBD?6e*K&0U-<2dFa7TKFK_?DfB$jEpZ@&H&Ru`mz30`}{`&gg-gtBG z-{1PjzPJDRul?`5JHmdoy99ZStNuIz|0eDjf3JhazxPi>6}`X7z^_9Y3J)9m?^4jO z#NQ$KQP7viN@?DDU~z{aIHKLV?FP4eZ*$Bkn0Q1&`xh8%HVluqEEk1fPZ zk;Q${^@X^_vi9Fthy}H^a|)*|SlT+hQ2$e>g}6XlTr|6=v~XHoydB^5ErjJyVH*(} z)v(JmarlR?ek07JbclaTP9rY-)J(9>)QFyun%SOCtC{eY;!MJXWYCEJM`DnYnwL=) za~ekr=2!UNJRgrZ{PXSVV8b&PVL$&(XY-qE!jlmmYKaIx@tjiE;oqNTD=|I)P3Up> zJJUQz5b}4omlvPyza3o+On#q_&-Q2eDnKy*oMgerJe_k^B=$HFC%H0kc%O%~Gn*g?X1FE@?DOE&Zem3rKJp1zXgD+*75L!NlRyIbT{d33! z`Do7Ajx3jbOd0(AE`Az)$w>3F`PpY1`t$|n(D_`7=T6|m!NX_v_jU@2zYuEK47d)8_C6E; zAw1uJ%3qI={Ffn|hE9o__>baQ{*7~lL95CG%b$qnZv34G zO#ah=Z^vH&{`g#q(Ei9jFYmb_@A=}q=N>%Y35C2Gf6VXWdGXigJ>Qh~d~4qGck-Se z%6on+@A;Q`&%e)m{%hXzyLc``H-86prM$;L?wxqH?Ku$$P#W&(}fe+o15we<&~hOL@=V&3oRG_xxPm^MB_(@6CHY$Q$&O?})tTiAx9$Gj)GlKWs-;2A&KE)UbHmkZ`(2TsIO9sTQFKJcn+l>XX@ zi!nY9&N1}YK=IG`CVIfj%@xNq>g?RbaQUdTgB&v-PI4mB1;kO<>bQ$Bg}G;2O@Eze zK7U8Y*ySXPM*CentUV2sf7L>mz(ALZLL2{_$3QWCPg>z$*tM{;eI*!D*ssuTJgzZ| zk7whpLg|A6*#T$Y5#S%ETwZ21$*n3aWU#G-FZ#HP zud+!L>tD@uxMw2&R2rT}ainuN@WeJSD_PSBF>Wbw&x{)d*uSvtEBhkUCEWp?9q1_0 zPaMzWtcKLuHvIGArnx(~-r@JSGyT*-7RMt+*~-xcrfmW89hiV4jbk8F=d>1+yVW^V zeTbDvc4T_ccU+>Fyn-_)g%-wF?z~edOUXOQl8@ba>GIC5M6sS-XTzMjv=z%OSAzxE1-QJ#M5UWA6!5chQkEcQl2^2la6Uc z@;}6coa0%Z_(&%xbu6F3cM8JriAtSi!YUIsnfM=@@K>Z4Vjko(@_CidC&P!t+`@bc z5r$7us+7-&ujDi0V|+$@hR+|+J^`;TcN()x_^L@$VS=Ovn_5Be{s-45e;jh;;WMWPV%t ze3nvwV>lB&J7~=B7=~!CQyD^z4>6nyIT%8o^$3~%qkKj^K4qRiZ^AE;j(+Zgzrb{Z z43TaKA=7=5&!;K%W%K+E6W&ES(na9sl5e~Tk1*k}2*c>tnG7Kp*J=N`%O>1m!d)i*9}JOC)i{KwDs>S;=65NdA>S9x^W!GuVuJDCL&$dgnRz}Leohz; z`gsh?lu95ZU)qH0NJqW?+dNO1h-VCfWel+pJRE*`c(zgt7*;BE6+`T4J-~1d^bsNR z+iSviO&GyqhxtxG$oQj8c$}dhf%OCX^Av=Pui|qx^v2L%W{C2SgM-BS&t`bGQcD@0 zgY^c(b5Re3On*6_;Xn^EMEhJ%`dp=MGx2ws`1=`RTs+UPMybCuoQL%;7I$p#_c25{ zg$$AZ83>vGEIuROB@AmZt{8%^iy`>>5t1*Z>+&38dm-rl2>bne~*INwzr+fzga}2@% zJBIVIRzFhbcO1g-0@xvjNLOv>bNP&P4GfWP1w*9kHt{JFt~KFD7=Hrv#5`Zm=Y>jr zogwnQ$Iu_-GxB|mA@Y5OA@bz{nf-DiL-b1pLe_UKpHZ(ChRCPg&{y&q`K)1xd_Kky z`P^upzrhggcQ-?{-}ezRzaR4%>3+o!>0V(7zSkInZy!SPy~AhlO@S^`pJpIr|DVec z{85JBZ(uz5TlftA^$hE=e__HeFg#zWyBH$fy$q3VD?+AwjL%5-M~0~1Yli-&p^q?} zuhj9FlNf&*LZ&;D&qz0iA?i`f5PTnEi1K?)IKU9)U(XQb-^38*-;R*^e2dS>=V#{m z89rk?{e~gN%Wgw|!_fa_=o6vCOn)Ro@}IyE{4)%_)X=L8eIY~G?-moHc~uB=P@xHL zXS?HXJL`{0`XekC8)i}t_^v~`&`C-SU@i{5AM+IE-Vp5gJ;*aO3;Kpkt.generic ); @@ -309,7 +310,7 @@ parse( IOBUF inp, PACKET *pkt, int onlykeypkts, off_t *retpos, hdrlen=0; hdr[hdrlen++] = ctb; if( !(ctb & 0x80) ) { - log_error("%s: invalid packet (ctb=%02x)\n", iobuf_where(inp), ctb ); + log_error("%s: invalid packet (ctb=%02x)\n", iobuf_where(inp), ctb ); rc = G10ERR_INVALID_PACKET; goto leave; } @@ -375,12 +376,14 @@ parse( IOBUF inp, PACKET *pkt, int onlykeypkts, off_t *retpos, goto leave; } - if( do_skip + if (with_uid && pkttype == PKT_USER_ID) + ; + else if( do_skip || !pkttype || (onlykeypkts && pkttype != PKT_PUBLIC_SUBKEY && pkttype != PKT_PUBLIC_KEY && pkttype != PKT_SECRET_SUBKEY - && pkttype != PKT_SECRET_KEY ) ) { + && pkttype != PKT_SECRET_KEY ) ) { skip_rest(inp, pktlen); *skip = 1; rc = 0; @@ -866,7 +869,7 @@ dump_sig_subpkt( int hashed, int type, int critical, case SIGSUBPKT_FEATURES: fputs ( "features:", stdout ); for( i=0; i < length; i++ ) - printf(" %d", buffer[i] ); + printf(" %02x", buffer[i] ); break; case SIGSUBPKT_PRIV_VERIFY_CACHE: p = "verification cache"; diff --git a/g10/passphrase.c b/g10/passphrase.c index c6c7cb27d..7ca28e72a 100644 --- a/g10/passphrase.c +++ b/g10/passphrase.c @@ -285,7 +285,7 @@ agent_get_passphrase ( u32 *keyid, int mode ) char *atext; char buf[50]; int fd = -1; - int nread; + size_t nread; u32 reply; char *pw = NULL; PKT_public_key *pk = m_alloc_clear( sizeof *pk ); @@ -433,7 +433,7 @@ passphrase_clear_cache ( u32 *keyid, int algo ) size_t n; char buf[50]; int fd = -1; - int nread; + size_t nread; u32 reply; PKT_public_key *pk; byte fpr[MAX_FINGERPRINT_LEN]; diff --git a/g10/pkclist.c b/g10/pkclist.c index e4e0f38a3..e09d55418 100644 --- a/g10/pkclist.c +++ b/g10/pkclist.c @@ -628,7 +628,10 @@ check_signatures_trust( PKT_signature *sig ) if( opt.always_trust ) { if( !opt.quiet ) log_info(_("WARNING: Using untrusted key!\n")); - return 0; + if (opt.with_fingerprint) + fpr_info (pk); + rc = 0; + goto leave; } @@ -705,6 +708,8 @@ check_signatures_trust( PKT_signature *sig ) write_status( STATUS_TRUST_NEVER ); log_info(_("WARNING: We do NOT trust this key!\n")); log_info(_(" The signature is probably a FORGERY.\n")); + if (opt.with_fingerprint) + fpr_info (pk); rc = G10ERR_BAD_SIGN; break; @@ -721,10 +726,14 @@ check_signatures_trust( PKT_signature *sig ) case TRUST_FULLY: write_status( STATUS_TRUST_FULLY ); + if (opt.with_fingerprint) + fpr_info (pk); break; case TRUST_ULTIMATE: write_status( STATUS_TRUST_ULTIMATE ); + if (opt.with_fingerprint) + fpr_info (pk); break; default: BUG(); @@ -814,10 +823,12 @@ build_pk_list( STRLIST remusr, PK_LIST *ret_pk_list, unsigned use ) else if( (use & PUBKEY_USAGE_ENC) && !opt.no_encrypt_to ) { pk = m_alloc_clear( sizeof *pk ); pk->req_usage = use; - if( (rc = get_pubkey_byname( NULL, pk, rov->d, NULL )) ) { + if( (rc = get_pubkey_byname( pk, rov->d, NULL, NULL )) ) { free_public_key( pk ); pk = NULL; log_error(_("%s: skipped: %s\n"), rov->d, g10_errstr(rc) ); - } + write_status_text_and_buffer (STATUS_INV_RECP, "0 ", + rov->d, strlen (rov->d), -1); + } else if( !(rc=check_pubkey_algo2(pk->pubkey_algo, use )) ) { /* Skip the actual key if the key is already present * in the list */ @@ -838,6 +849,8 @@ build_pk_list( STRLIST remusr, PK_LIST *ret_pk_list, unsigned use ) else { free_public_key( pk ); pk = NULL; log_error(_("%s: skipped: %s\n"), rov->d, g10_errstr(rc) ); + write_status_text_and_buffer (STATUS_INV_RECP, "0 ", + rov->d, strlen (rov->d), -1); } } } @@ -870,7 +883,7 @@ build_pk_list( STRLIST remusr, PK_LIST *ret_pk_list, unsigned use ) free_public_key( pk ); pk = m_alloc_clear( sizeof *pk ); pk->req_usage = use; - rc = get_pubkey_byname( NULL, pk, answer, NULL ); + rc = get_pubkey_byname( pk, answer, NULL, NULL ); if( rc ) tty_printf(_("No such user ID.\n")); else if( !(rc=check_pubkey_algo2(pk->pubkey_algo, use)) ) { @@ -936,7 +949,7 @@ build_pk_list( STRLIST remusr, PK_LIST *ret_pk_list, unsigned use ) else if( !any_recipients && (def_rec = default_recipient()) ) { pk = m_alloc_clear( sizeof *pk ); pk->req_usage = use; - rc = get_pubkey_byname( NULL, pk, def_rec, NULL ); + rc = get_pubkey_byname( pk, def_rec, NULL, NULL ); if( rc ) log_error(_("unknown default recipient `%s'\n"), def_rec ); else if( !(rc=check_pubkey_algo2(pk->pubkey_algo, use)) ) { @@ -961,9 +974,12 @@ build_pk_list( STRLIST remusr, PK_LIST *ret_pk_list, unsigned use ) pk = m_alloc_clear( sizeof *pk ); pk->req_usage = use; - if( (rc = get_pubkey_byname( NULL, pk, remusr->d, NULL )) ) { + if( (rc = get_pubkey_byname( pk, remusr->d, NULL, NULL )) ) { free_public_key( pk ); pk = NULL; log_error(_("%s: skipped: %s\n"), remusr->d, g10_errstr(rc) ); + write_status_text_and_buffer (STATUS_INV_RECP, "0 ", + remusr->d, strlen (remusr->d), + -1); } else if( !(rc=check_pubkey_algo2(pk->pubkey_algo, use )) ) { int trustlevel; @@ -973,11 +989,19 @@ build_pk_list( STRLIST remusr, PK_LIST *ret_pk_list, unsigned use ) free_public_key( pk ); pk = NULL; log_error(_("%s: error checking key: %s\n"), remusr->d, g10_errstr(rc) ); + write_status_text_and_buffer (STATUS_INV_RECP, "0 ", + remusr->d, + strlen (remusr->d), + -1); } else if( (trustlevel & TRUST_FLAG_DISABLED) ) { free_public_key(pk); pk = NULL; log_info(_("%s: skipped: public key is disabled\n"), remusr->d); + write_status_text_and_buffer (STATUS_INV_RECP, "0 ", + remusr->d, + strlen (remusr->d), + -1); } else if( do_we_trust_pre( pk, trustlevel ) ) { /* note: do_we_trust may have changed the trustlevel */ @@ -1004,10 +1028,18 @@ build_pk_list( STRLIST remusr, PK_LIST *ret_pk_list, unsigned use ) } else { /* we don't trust this pk */ free_public_key( pk ); pk = NULL; + write_status_text_and_buffer (STATUS_INV_RECP, "0 ", + remusr->d, + strlen (remusr->d), + -1); } } else { free_public_key( pk ); pk = NULL; + write_status_text_and_buffer (STATUS_INV_RECP, "0 ", + remusr->d, + strlen (remusr->d), + -1); log_error(_("%s: skipped: %s\n"), remusr->d, g10_errstr(rc) ); } } diff --git a/g10/revoke.c b/g10/revoke.c index 7e25aeaa2..10db123cd 100644 --- a/g10/revoke.c +++ b/g10/revoke.c @@ -87,41 +87,46 @@ gen_revoke( const char *uname ) IOBUF out = NULL; KBNODE keyblock = NULL; KBNODE node; - KBPOS kbpos; + KEYDB_HANDLE kdbhd; struct revocation_reason_info *reason = NULL; + KEYDB_SEARCH_DESC desc; if( opt.batch ) { log_error(_("sorry, can't do this in batch mode\n")); return G10ERR_GENERAL; } - memset( &afx, 0, sizeof afx); memset( &zfx, 0, sizeof zfx); init_packet( &pkt ); - - /* search the userid */ - rc = find_secret_keyblock_byname( &kbpos, uname ); - if( rc ) { - log_error(_("secret key for user `%s' not found\n"), uname ); + /* search the userid: + * We don't want the whole getkey stuff here but the entire keyblock + */ + kdbhd = keydb_new (1); + memset (&desc, 0, sizeof desc); + desc.mode = classify_user_id (uname, + desc.u.kid, + desc.u.fpr, + &desc.u.name, + NULL); + rc = desc.mode? keydb_search (kdbhd, &desc, 1) : G10ERR_INV_USER_ID; + if (rc) { + log_error (_("secret key `%s' not found: %s\n"), + uname, g10_errstr (rc)); goto leave; } - /* read the keyblock */ - rc = read_keyblock( &kbpos, &keyblock ); + rc = keydb_get_keyblock (kdbhd, &keyblock ); if( rc ) { - log_error(_("error reading the certificate: %s\n"), g10_errstr(rc) ); + log_error (_("error reading keyblock: %s\n"), g10_errstr(rc) ); goto leave; } /* get the keyid from the keyblock */ node = find_kbnode( keyblock, PKT_SECRET_KEY ); - if( !node ) { /* maybe better to use log_bug ? */ - log_error(_("Oops; secret key not found anymore!\n")); - rc = G10ERR_GENERAL; - goto leave; - } + if( !node ) + BUG (); /* fixme: should make a function out of this stuff, * it's used all over the source */ @@ -139,6 +144,8 @@ gen_revoke( const char *uname ) tty_printf("\n"); } pk = m_alloc_clear( sizeof *pk ); + + /* FIXME: We should get the public key direct from the secret one */ rc = get_pubkey( pk, sk_keyid ); if( rc ) { log_error(_("no corresponding public key: %s\n"), g10_errstr(rc) ); @@ -224,6 +231,7 @@ gen_revoke( const char *uname ) if( sig ) free_seckey_enc( sig ); release_kbnode( keyblock ); + keydb_release (kdbhd); if( rc ) iobuf_cancel(out); else diff --git a/g10/ringedit.c b/g10/ringedit.c deleted file mode 100644 index 5e610e583..000000000 --- a/g10/ringedit.c +++ /dev/null @@ -1,1796 +0,0 @@ -/* ringedit.c - Function for key ring editing - * Copyright (C) 1998, 1999, 2000, 2001 Free Software Foundation, Inc. - * - * This file is part of GnuPG. - * - * 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. - * - * 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. - * - * 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 - */ - - -/**************** - * This module supplies function for: - * - * - Search for a key block (pubkey and all other stuff) and return a - * handle for it. - * - * - Lock/Unlock a key block - * - * - Read a key block into a tree - * - * - Update a key block - * - * - Insert a new key block - * - * - Delete a key block - * - */ - - - -#include -#include -#include -#include -#include -#include -#include -#include /* for truncate */ -#include -#ifdef HAVE_LIBGDBM - #include -#endif -#include "util.h" -#include "packet.h" -#include "memory.h" -#include "mpi.h" -#include "iobuf.h" -#include "keydb.h" -#include "host2net.h" -#include "options.h" -#include "main.h" -#include "i18n.h" - - - - -struct resource_table_struct { - int used; - int secret; /* this is a secret keyring */ - char *fname; - IOBUF iobuf; - #ifdef HAVE_LIBGDBM - GDBM_FILE dbf; - #endif - enum resource_type rt; - DOTLOCK lockhd; - int is_locked; -}; -typedef struct resource_table_struct RESTBL; - -#define MAX_RESOURCES 10 -static RESTBL resource_table[MAX_RESOURCES]; -static int default_public_resource; -static int default_secret_resource; - -static int search (PKT_public_key *req_pk, u32 *req_keyid, - KBPOS *kbpos, int secret); - - -static int keyring_search (PKT_public_key *req_pk, u32 *req_keyid, - KBPOS *kbpos, IOBUF iobuf, const char *fname ); -static int keyring_read( KBPOS *kbpos, KBNODE *ret_root ); -static int keyring_enum( KBPOS *kbpos, KBNODE *ret_root, int skipsigs ); -static int keyring_copy( KBPOS *kbpos, int mode, KBNODE root ); - -#ifdef HAVE_LIBGDBM -static int do_gdbm_store( KBPOS *kbpos, KBNODE root, int update ); -static int do_gdbm_locate( GDBM_FILE dbf, KBPOS *kbpos, - const byte *fpr, int fprlen ); -/*static int do_gdbm_locate_by_keyid( GDBM_FILE dbf, KBPOS *kbpos, u32 *keyid );*/ -static int do_gdbm_read( KBPOS *kbpos, KBNODE *ret_root ); -static int do_gdbm_enum( KBPOS *kbpos, KBNODE *ret_root ); -#endif - - -static RESTBL * -check_pos( KBPOS *kbpos ) -{ - if( kbpos->resno < 0 || kbpos->resno >= MAX_RESOURCES ) - return NULL; - if( !resource_table[kbpos->resno].used ) - return NULL; - return resource_table + kbpos->resno; -} - -#ifdef HAVE_LIBGDBM -static void -fatal_gdbm_error( const char *string ) -{ - log_fatal("gdbm failed: %s\n", string); -} - -#endif /* HAVE_LIBGDBM */ - - -/**************** - * Hmmm, how to avoid deadlock? They should not happen if everyone - * locks the key resources in the same order; but who knows. - * A solution is to use only one lock file in the gnupg homedir but - * what will happen with key resources which normally don't belong - * to the gpg homedir? - */ -static void -lock_rentry( RESTBL *rentry ) -{ - if( !rentry->lockhd ) { - rentry->lockhd = create_dotlock( rentry->fname ); - if( !rentry->lockhd ) - log_fatal("can't allocate lock for `%s'\n", rentry->fname ); - rentry->is_locked = 0; - } - if( !rentry->is_locked ) { - if( make_dotlock( rentry->lockhd, -1 ) ) - log_fatal("can't lock `%s'\n", rentry->fname ); - rentry->is_locked = 1; - } -} - -static void -unlock_rentry( RESTBL *rentry ) -{ - if( opt.lock_once ) - return; - if( !release_dotlock( rentry->lockhd ) ) - rentry->is_locked = 0; -} - - -/**************************************************************** - ****************** public functions **************************** - ****************************************************************/ - -/**************** - * Get the name of the keyrings, start with a sequence number pointing to a 0. - */ -const char * -enum_keyblock_resources( int *sequence, int secret ) -{ - int i = *sequence; - const char *name = NULL; - - for(; i < MAX_RESOURCES; i++ ) - if( resource_table[i].used && !resource_table[i].secret == !secret ) { - if( resource_table[i].fname ) { - name = resource_table[i].fname; - break; - } - } - *sequence = ++i; - return name; -} - - -/**************** - * Register a resource (which currently may only be a keyring file). - * The first keyring which is added by this function is - * created if it does not exist. - * Note: this function may be called before secure memory is - * available. - */ -int -add_keyblock_resource( const char *url, int force, int secret ) -{ - static int any_secret, any_public; - const char *resname = url; - IOBUF iobuf = NULL; - int i; - char *filename = NULL; - int rc = 0; - enum resource_type rt = rt_UNKNOWN; - const char *created_fname = NULL; - - /* Do we have an URL? - * gnupg-gdbm:filename := this is a GDBM resource - * gnupg-ring:filename := this is a plain keyring - * filename := See what is is, but create as plain keyring. - */ - if( strlen( resname ) > 11 ) { - if( !strncmp( resname, "gnupg-ring:", 11 ) ) { - rt = rt_RING; - resname += 11; - } - else if( !strncmp( resname, "gnupg-gdbm:", 11 ) ) { - rt = rt_GDBM; - resname += 11; - } - #if !defined(HAVE_DRIVE_LETTERS) && !defined(__riscos__) - else if( strchr( resname, ':' ) ) { - log_error("%s: invalid URL\n", url ); - rc = G10ERR_GENERAL; - goto leave; - } - #endif /* !HAVE_DRIVE_LETTERS && !__riscos__ */ - } - - if( *resname != DIRSEP_C ) { /* do tilde expansion etc */ - if( strchr(resname, DIRSEP_C) ) - filename = make_filename(resname, NULL); - else - filename = make_filename(opt.homedir, resname, NULL); - } - else - filename = m_strdup( resname ); - - if( !force ) - force = secret? !any_secret : !any_public; - - for(i=0; i < MAX_RESOURCES; i++ ) - if( !resource_table[i].used ) - break; - if( i == MAX_RESOURCES ) { - rc = G10ERR_RESOURCE_LIMIT; - goto leave; - } - - /* see whether we can determine the filetype */ - if( rt == rt_UNKNOWN ) { - FILE *fp = fopen( filename, "rb" ); - - if( fp ) { - u32 magic; - - if( fread( &magic, 4, 1, fp) == 1 ) { - if( magic == 0x13579ace ) - rt = rt_GDBM; - else if( magic == 0xce9a5713 ) - log_error("%s: endianess does not match\n", url ); - else - rt = rt_RING; - } - else /* maybe empty: assume ring */ - rt = rt_RING; - fclose( fp ); - } - else /* no file yet: create ring */ - rt = rt_RING; - } - - switch( rt ) { - case rt_UNKNOWN: - log_error("%s: unknown resource type\n", url ); - rc = G10ERR_GENERAL; - goto leave; - - case rt_RING: - iobuf = iobuf_open( filename ); - if( !iobuf && !force ) { - rc = G10ERR_OPEN_FILE; - goto leave; - } - - if( !iobuf ) { - char *last_slash_in_filename; - - last_slash_in_filename = strrchr(filename, DIRSEP_C); - *last_slash_in_filename = 0; - - if( access(filename, F_OK) ) { - /* on the first time we try to create the default homedir and - * in this case the process will be terminated, so that on the - * next invocation it can read the options file in on startup - */ - try_make_homedir( filename ); - rc = G10ERR_OPEN_FILE; - *last_slash_in_filename = DIRSEP_C; - goto leave; - } - - *last_slash_in_filename = DIRSEP_C; - - iobuf = iobuf_create( filename ); - if( !iobuf ) { - log_error(_("%s: can't create keyring: %s\n"), - filename, strerror(errno)); - rc = G10ERR_OPEN_FILE; - goto leave; - } - else { - #ifndef HAVE_DOSISH_SYSTEM - if( secret && !opt.preserve_permissions ) { - if( chmod( filename, S_IRUSR | S_IWUSR ) ) { - log_error("%s: chmod failed: %s\n", - filename, strerror(errno) ); - rc = G10ERR_WRITE_FILE; - goto leave; - } - } - #endif - if( !opt.quiet ) - log_info(_("%s: keyring created\n"), filename ); - created_fname = filename; - } - } - #if HAVE_DOSISH_SYSTEM || 1 - iobuf_close( iobuf ); - iobuf = NULL; - if (created_fname) /* must invalidate that ugly cache */ - iobuf_ioctl (NULL, 2, 0, (char*)created_fname); - #endif - break; - - #ifdef HAVE_LIBGDBM - case rt_GDBM: - resource_table[i].dbf = gdbm_open( filename, 0, - force? GDBM_WRCREAT : GDBM_WRITER, - S_IRUSR | S_IWUSR | - S_IRGRP | S_IWGRP | S_IROTH, - fatal_gdbm_error ); - if( !resource_table[i].dbf ) { - log_error("%s: can't open gdbm file: %s\n", - filename, gdbm_strerror(gdbm_errno)); - rc = G10ERR_OPEN_FILE; - goto leave; - } - break; - #endif - - default: - log_error("%s: unsupported resource type\n", url ); - rc = G10ERR_GENERAL; - goto leave; - } - - #ifndef HAVE_DOSISH_SYSTEM - #if 0 /* fixme: check directory permissions and print a warning */ - if( secret ) { - } - #endif - #endif - - /* fixme: avoid duplicate resources */ - resource_table[i].used = 1; - resource_table[i].secret = !!secret; - resource_table[i].fname = m_strdup(filename); - resource_table[i].iobuf = iobuf; - resource_table[i].rt = rt; - if( secret ) - default_secret_resource = i; - else - default_public_resource = i; - - leave: - if( rc ) - log_error("keyblock resource `%s': %s\n", filename, g10_errstr(rc) ); - else if( secret ) - any_secret = 1; - else - any_public = 1; - m_free( filename ); - return rc; -} - -/**************** - * Return the resource name of the keyblock associated with KBPOS. - */ -const char * -keyblock_resource_name( KBPOS *kbpos ) -{ - RESTBL *rentry; - - if( !(rentry = check_pos( kbpos )) || !rentry->fname ) - log_bug("no name for keyblock resource %d\n", kbpos->resno ); - return rentry->fname; -} - - -/**************** - * Get a keyblock handle KBPOS from a filename. This can be used - * to get a handle for insert_keyblock for a new keyblock. - * Using a filename of NULL returns the default resource - */ -int -get_keyblock_handle( const char *filename, int secret, KBPOS *kbpos ) -{ - int i = 0; - - if( !filename ) - i = secret? default_secret_resource : default_public_resource; - - for(; i < MAX_RESOURCES; i++ ) { - if( resource_table[i].used && !resource_table[i].secret == !secret ) { - /* fixme: dos needs case insensitive file compare */ - if( !filename || !strcmp( resource_table[i].fname, filename ) ) { - memset( kbpos, 0, sizeof *kbpos ); - kbpos->resno = i; - kbpos->rt = resource_table[i].rt; - return 0; - } - } - } - return -1; /* not found */ -} - - -/**************** - * Return the filename of the firstkeyblock resource which is intended - * for write access. This will either be the default resource or in - * case this is not writable one of the others. If no writable is found, - * the default filename in the homedirectory will be returned. - * Caller must free, will never return NULL. - */ -char * -get_writable_keyblock_file( int secret ) -{ - int i = secret? default_secret_resource : default_public_resource; - - if( resource_table[i].used && !resource_table[i].secret == !secret ) { - if( !access( resource_table[i].fname, R_OK|W_OK ) ) { - return m_strdup( resource_table[i].fname ); - } - } - for(i=0; i < MAX_RESOURCES; i++ ) { - if( resource_table[i].used && !resource_table[i].secret == !secret ) { - if( !access( resource_table[i].fname, R_OK|W_OK ) ) { - return m_strdup( resource_table[i].fname ); - } - } - } - /* Assume the home dir is always writable */ - return make_filename(opt.homedir, secret? "secring" EXTSEP_S "gpg" - : "pubring" EXTSEP_S "gpg", NULL ); -} - - -/**************** - * Search a keyblock which starts with the given packet and puts all - * information into KBPOS, which can be used later to access this key block. - * This function looks into all registered keyblock sources. - * PACKET must be a packet with either a secret_key or a public_key - * - * This function is intended to check whether a given certificate - * is already in a keyring or to prepare it for editing. - * - * Returns: 0 if found, -1 if not found or an errorcode. - */ -static int -search (PKT_public_key *req_pk, u32 *req_keyid, KBPOS *kbpos, int secret) -{ - int i, rc, last_rc=-1; - - for(i=0; i < MAX_RESOURCES; i++ ) { - if( resource_table[i].used && !resource_table[i].secret == !secret ) { - switch( resource_table[i].rt ) { - case rt_RING: - rc = keyring_search (req_pk, req_keyid, - kbpos, resource_table[i].iobuf, - resource_table[i].fname ); - - break; - #ifdef HAVE_LIBGDBM - case rt_GDBM: { - byte fpr[20]; - size_t fprlen; - - if (!req_pk) - rc = G10ERR_UNSUPPORTED; - else { - fingerprint_from_pk( req_pk, fpr, &fprlen ); - rc = do_gdbm_locate( resource_table[i].dbf, - kbpos, fpr, fprlen ); - } - } - break; - #endif - default: BUG(); - } - - kbpos->rt = resource_table[i].rt; - if( !rc ) { - kbpos->resno = i; - kbpos->fp = NULL; - return 0; - } - if( rc != -1 ) { - log_error("error searching resource %d: %s\n", - i, g10_errstr(rc)); - last_rc = rc; - } - } - } - return last_rc; -} - - -/**************** - * Combined function to search for a username and get the position - * of the keyblock. - */ -int -find_keyblock_byname( KBPOS *kbpos, const char *username ) -{ - PKT_public_key *pk = m_alloc_clear( sizeof *pk ); - int rc; - - rc = get_pubkey_byname( NULL, pk, username, NULL ); - if (!rc) - rc = search( pk, NULL, kbpos, 0 ); - free_public_key(pk); - return rc; -} - - -/**************** - * Combined function to search for a key and get the position - * of the keyblock. - */ -int -find_keyblock_bypk( KBPOS *kbpos, PKT_public_key *pk ) -{ - int rc; - - rc = search( pk, NULL, kbpos, 0 ); - return rc; -} - -/**************** - * Combined function to search for a secret key and get the position - * of the keyblock. - */ -int -find_secret_keyblock_bypk (KBPOS *kbpos, PKT_public_key *pk) -{ - int rc; - - rc = search (pk, NULL, kbpos, 1); - return rc; -} - - -/**************** - * Combined function to search for a username and get the position - * of the keyblock. This function does not unprotect the secret key. - */ -int -find_secret_keyblock_byname( KBPOS *kbpos, const char *username ) -{ - PKT_public_key *pk = m_alloc_clear( sizeof *pk ); - int rc; - - rc = get_pubkey_byname( NULL, pk, username, NULL ); - if (!rc) - rc = search (pk, NULL, kbpos, 1); - free_public_key (pk); - return rc; -} - -/* - * This function locates the secret keyblock without doing a public - * keyring check fist. It is useful in certain situations and much - * faster than the generic solution. - */ -int -find_secret_keyblock_direct (KBPOS *kbpos, u32 *keyid) -{ - int rc; - rc = search (NULL, keyid, kbpos, 1); - return rc; -} - - - - -/**************** - * Lock the keyblock; wait until it's available - * This function may change the internal data in kbpos, in cases - * when the keyblock to be locked has been modified. - * fixme: remove this function and add an option to search()? - */ -int -lock_keyblock( KBPOS *kbpos ) -{ - if( !check_pos(kbpos) ) - return G10ERR_GENERAL; - return 0; -} - -/**************** - * Release a lock on a keyblock - */ -void -unlock_keyblock( KBPOS *kbpos ) -{ - if( !check_pos(kbpos) ) - BUG(); -} - -/**************** - * Read a complete keyblock and return the root in ret_root. - */ -int -read_keyblock( KBPOS *kbpos, KBNODE *ret_root ) -{ - if( !check_pos(kbpos) ) - return G10ERR_GENERAL; - - switch( kbpos->rt ) { - case rt_RING: - return keyring_read( kbpos, ret_root ); - #ifdef HAVE_LIBGDBM - case rt_GDBM: - return do_gdbm_read( kbpos, ret_root ); - #endif - default: BUG(); - } -} - - -/**************** - * This functions can be used to read through a complete keyring. - * Mode is: 0 = open - * 1 = read - * 2 = close - * 5 = open secret keyrings - * 11 = read but skip signature and comment packets. - * all others are reserved! - * Note that you do not need a search prior to this function, - * only a handle is needed. - * NOTE: It is not allowed to do an insert/update/delete with this - * keyblock, if you want to do this, use search/read! - */ -int -enum_keyblocks( int mode, KBPOS *kbpos, KBNODE *ret_root ) -{ - int rc = 0; - RESTBL *rentry; - - if( !mode || mode == 5 || mode == 100 ) { - int i; - - kbpos->fp = NULL; - kbpos->rt = rt_UNKNOWN; - if( !mode ) { - kbpos->secret = 0; - i = 0; - } - else if( mode == 5 ) { - kbpos->secret = 1; - mode = 0; - i = 0; - } - else - i = kbpos->resno+1; - for(; i < MAX_RESOURCES; i++ ) - if( resource_table[i].used - && !resource_table[i].secret == !kbpos->secret ) - break; - if( i == MAX_RESOURCES ) - return -1; /* no resources */ - kbpos->resno = i; - rentry = check_pos( kbpos ); - kbpos->rt = resource_table[i].rt; - kbpos->valid = 0; - switch( kbpos->rt ) { - case rt_RING: - kbpos->fp = iobuf_open( rentry->fname ); - if( !kbpos->fp ) { - log_error("can't open `%s'\n", rentry->fname ); - return G10ERR_OPEN_FILE; - } - break; - #ifdef HAVE_LIBGDBM - case rt_GDBM: - /* FIXME: make sure that there is only one enum at a time */ - kbpos->offset = 0; - break; - #endif - default: BUG(); - } - kbpos->pkt = NULL; - } - else if( mode == 1 || mode == 11 ) { - int cont; - do { - cont = 0; - switch( kbpos->rt ) { - case rt_RING: - if( !kbpos->fp ) - return G10ERR_GENERAL; - rc = keyring_enum( kbpos, ret_root, mode == 11 ); - break; - #ifdef HAVE_LIBGDBM - case rt_GDBM: - rc = do_gdbm_enum( kbpos, ret_root ); - break; - #endif - default: BUG(); - } - - if( rc == -1 ) { - assert( !kbpos->pkt ); - rentry = check_pos( kbpos ); - assert(rentry); - /* close */ - enum_keyblocks(2, kbpos, ret_root ); - /* and open the next one */ - rc = enum_keyblocks(100, kbpos, ret_root ); - if( !rc ) - cont = 1; - } - } while(cont); - } - else { - switch( kbpos->rt ) { - case rt_RING: - if( kbpos->fp ) { - iobuf_close( kbpos->fp ); - kbpos->fp = NULL; - } - break; - case rt_GDBM: - break; - case rt_UNKNOWN: - /* this happens when we have no keyring at all */ - return rc; - - default: - BUG(); - } - /* release pending packet */ - free_packet( kbpos->pkt ); - m_free( kbpos->pkt ); - } - return rc; -} - - - - -/**************** - * Insert the keyblock described by ROOT into the keyring described - * by KBPOS. This actually appends the data to the keyfile. - */ -int -insert_keyblock( KBPOS *kbpos, KBNODE root ) -{ - int rc; - - if( !check_pos(kbpos) ) - return G10ERR_GENERAL; - - switch( kbpos->rt ) { - case rt_RING: - rc = keyring_copy( kbpos, 1, root ); - break; - #ifdef HAVE_LIBGDBM - case rt_GDBM: - rc = do_gdbm_store( kbpos, root, 0 ); - break; - #endif - default: BUG(); - } - - return rc; -} - -/**************** - * Delete the keyblock described by KBPOS. - * The current code simply changes the keyblock in the keyring - * to packet of type 0 with the correct length. To help detect errors, - * zero bytes are written. - */ -int -delete_keyblock( KBPOS *kbpos ) -{ - int rc; - - if( !check_pos(kbpos) ) - return G10ERR_GENERAL; - - switch( kbpos->rt ) { - case rt_RING: - rc = keyring_copy( kbpos, 2, NULL ); - break; - #ifdef HAVE_LIBGDBM - case rt_GDBM: - log_debug("deleting gdbm keyblock is not yet implemented\n"); - rc = 0; - break; - #endif - default: BUG(); - } - - return rc; -} - - -/**************** - * Update the keyblock at KBPOS with the one in ROOT. - */ -int -update_keyblock( KBPOS *kbpos, KBNODE root ) -{ - int rc; - - if( !check_pos(kbpos) ) - return G10ERR_GENERAL; - - switch( kbpos->rt ) { - case rt_RING: - rc = keyring_copy( kbpos, 3, root ); - break; - #ifdef HAVE_LIBGDBM - case rt_GDBM: - rc = do_gdbm_store( kbpos, root, 1 ); - break; - #endif - default: BUG(); - } - - return rc; -} - - - -/**************************************************************** - ********** Implemenation of a user ID database ************** - ****************************************************************/ -#if 0 -/**************** - * Layout of the user ID db - * - * This user ID DB provides fast lookup of user ID, but the user ids are - * not in any specific order. - * - * A string "GnuPG user db", a \n. - * user ids of one key, delimited by \t, - * a # or ^ followed by a 20 byte fingerprint, followed by an \n - * The literal characters %, \n, \t, #, ^ must be replaced by a percent sign - * and their hex value. - * - * (We use Boyer/Moore pattern matching) - */ - -/**************** - * This compiles pattern to the distance table, the table will be allocate - * here and must be freed by using free(). - * Returns: Ptr to new allocated Table - * Caller must free the table. - */ - -static size_t * -compile_bm_table( const byte *pattern, size_t len ) -{ - ushort *dist; - int i; - - dist = m_alloc_clear( 256 * sizeof *dist ); - for(i=0; i < 256; i++ ) - dist[i] = len; - for(i=0; i < len-1; i++ ) - dTbl[p[i]] = len-i-1; - return dist; -} - - - - -/**************** - * Search BUF of BUFLEN for pattern P of length PATLEN. - * dist is the Boyer/Moore distance table of 256 Elements, - * case insensitive search is done if IGNCASE is true (In this case - * the distance table has to compiled from uppercase chacaters and - * PAT must also be uppercase. - * Returns: Prt to maching string in BUF, or NULL if not found. - */ - -static const * -do_bm_search( const byte *buf, size_t buflen, - const byte *pat, size_t patlen, size_t *dist, int igncase ) -{ - int i, j, k; - - if( igncase ) { - int c, c1; - - for( i = --patlen; i < buflen; i += dist[c1] ) - for( j=patlen, k=i, c1=c=ascii_toupper(buf[k]); c == pat[j]; - j--, k--, c=ascii_toupper(buf[k]) ) { - if( !j ) - return buf+k; - } - } - else { - for( i = --patlen; i < buflen; i += dist[buf[i]] ) - for( j=patlen, k=i; buf[k] == pat[j]; j--, k-- ) { - if( !j ) - return buf+k; - } - } - return NULL; -} - - -typedef struct { - size_t dist[256]; -} *SCAN_USER_HANDLE; - -static SCAN_USER_HANDLE -scan_user_file_open( const byte *name ) -{ - SCAN_USER_HANDLE hd; - size_t *dist; - int i; - - hd = m_alloc_clear( sizeof *hd ); - dist = hd->dist; - /* compile the distance table */ - for(i=0; i < 256; i++ ) - dist[i] = len; - for(i=0; i < len-1; i++ ) - dTbl[p[i]] = len-i-1; - /* setup other things */ - - return hd; -} - -static int -scan_user_file_close( SCAN_USER_HANDLE hd ) -{ - m_free( hd ); -} - -static int -scan_user_file_read( SCAN_USER_HANDLE hd, byte *fpr ) -{ - char record[1000]; - - /* read a record */ - - -} -#endif - - - -/**************************************************************** - ********** Functions which operates on regular keyrings ******** - ****************************************************************/ - -static int -cmp_seckey( PKT_public_key *req_pk, PKT_secret_key *sk ) -{ - int n,i; - - if (req_pk->pubkey_algo != sk->pubkey_algo) - return -1; - - n = pubkey_get_npkey (req_pk->pubkey_algo); - for (i=0; i < n; i++ ) { - if( mpi_cmp (req_pk->pkey[i], sk->skey[i]) ) - return -1; - } - return 0; -} - -static int -cmp_pubkey( PKT_public_key *req_pk, PKT_public_key *pk ) -{ - int n, i; - - assert( req_pk->pubkey_algo == pk->pubkey_algo ); - - n = pubkey_get_npkey( req_pk->pubkey_algo ); - for(i=0; i < n; i++ ) { - if( mpi_cmp( req_pk->pkey[i], pk->pkey[i] ) ) - return -1; - } - return 0; -} - -/**************** - * search one keyring, return 0 if found, -1 if not found or an errorcode. - */ -static int -keyring_search( PKT_public_key *req_pk, u32 *req_keyid, - KBPOS *kbpos, IOBUF iobuf, const char *fname ) -{ - int rc; - PACKET pkt; - int save_mode; - off_t offset, main_offset; - - assert (!!req_pk ^ !!req_keyid); /* exactly one must be specified */ - init_packet(&pkt); - save_mode = set_packet_list_mode(0); - kbpos->rt = rt_RING; - kbpos->valid = 0; - - #if HAVE_DOSISH_SYSTEM || 1 - assert(!iobuf); - iobuf = iobuf_open( fname ); - if( !iobuf ) { - log_error("%s: can't open keyring file\n", fname); - rc = G10ERR_KEYRING_OPEN; - goto leave; - } - #else - if( iobuf_seek( iobuf, 0 ) ) { - log_error("can't rewind keyring file\n"); - rc = G10ERR_KEYRING_OPEN; - goto leave; - } - #endif - - main_offset = 0; - while ( !(rc=search_packet(iobuf, &pkt, &offset)) ) { - if (pkt.pkttype == PKT_PUBLIC_KEY || pkt.pkttype == PKT_SECRET_KEY) { - main_offset = offset; - } - - if (pkt.pkttype == PKT_SECRET_KEY || pkt.pkttype == PKT_SECRET_SUBKEY) { - PKT_secret_key *sk = pkt.pkt.secret_key; - - if (req_keyid) { - u32 aki[2]; - - keyid_from_sk (sk, aki); - if (aki[0] == req_keyid[0] && aki[1] == req_keyid[1]) - break; /* found */ - } - else { - /* We can't compare the timestamps here because they - might differ */ - if( !cmp_seckey (req_pk, sk) ) - break; /* found */ - } - } - else if (pkt.pkttype == PKT_PUBLIC_KEY - || pkt.pkttype == PKT_PUBLIC_SUBKEY) { - PKT_public_key *pk = pkt.pkt.public_key; - - if (req_keyid) { - u32 aki[2]; - - keyid_from_pk (pk, aki); - if (aki[0] == req_keyid[0] && aki[1] == req_keyid[1]) - break; /* found */ - } - else { - if( req_pk->timestamp == pk->timestamp - && req_pk->pubkey_algo == pk->pubkey_algo - && !cmp_pubkey( req_pk, pk ) ) - break; /* found */ - } - } - else - BUG(); - free_packet(&pkt); - } - if( !rc ) { - if (pkt.pkttype == PKT_SECRET_SUBKEY - || pkt.pkttype == PKT_PUBLIC_SUBKEY) - kbpos->offset = main_offset; - else - kbpos->offset = offset; - kbpos->valid = 1; - } - - leave: - free_packet(&pkt); - set_packet_list_mode(save_mode); - #if HAVE_DOSISH_SYSTEM || 1 - iobuf_close(iobuf); - #endif - return rc; -} - - -static int -keyring_read( KBPOS *kbpos, KBNODE *ret_root ) -{ - PACKET *pkt; - int rc; - RESTBL *rentry; - KBNODE root = NULL; - IOBUF a; - int in_cert = 0; - - if( !(rentry=check_pos(kbpos)) ) - return G10ERR_GENERAL; - - a = iobuf_open( rentry->fname ); - if( !a ) { - log_error("can't open `%s'\n", rentry->fname ); - return G10ERR_OPEN_FILE; - } - - if( !kbpos->valid ) - log_debug("kbpos not valid in keyring_read\n" ); - if( iobuf_seek( a, kbpos->offset ) ) { - log_error("can't seek\n"); - iobuf_close(a); - return G10ERR_KEYRING_OPEN; - } - - pkt = m_alloc( sizeof *pkt ); - init_packet(pkt); - kbpos->count=0; - while( (rc=parse_packet(a, pkt)) != -1 ) { - if( rc ) { /* ignore errors */ - if( rc != G10ERR_UNKNOWN_PACKET ) { - log_error("read_keyblock: read error: %s\n", g10_errstr(rc) ); - rc = G10ERR_INV_KEYRING; - goto ready; - } - kbpos->count++; - free_packet( pkt ); - init_packet( pkt ); - continue; - } - /* make a linked list of all packets */ - switch( pkt->pkttype ) { - case PKT_COMPRESSED: - log_error("skipped compressed packet in keyring\n" ); - free_packet(pkt); - init_packet(pkt); - break; - - case PKT_PUBLIC_KEY: - case PKT_SECRET_KEY: - if( in_cert ) - goto ready; - in_cert = 1; - default: - kbpos->count++; - if( !root ) - root = new_kbnode( pkt ); - else - add_kbnode( root, new_kbnode( pkt ) ); - pkt = m_alloc( sizeof *pkt ); - init_packet(pkt); - break; - } - } - ready: - kbpos->valid = 0; - if( rc == -1 && root ) - rc = 0; - - if( rc ) - release_kbnode( root ); - else - *ret_root = root; - free_packet( pkt ); - m_free( pkt ); - iobuf_close(a); - return rc; -} - - -static int -keyring_enum( KBPOS *kbpos, KBNODE *ret_root, int skipsigs ) -{ - PACKET *pkt; - int rc; - RESTBL *rentry; - KBNODE root = NULL; - - if( !(rentry=check_pos(kbpos)) ) - return G10ERR_GENERAL; - - if( kbpos->pkt ) { - root = new_kbnode( kbpos->pkt ); - kbpos->pkt = NULL; - } - - pkt = m_alloc( sizeof *pkt ); - init_packet(pkt); - while( (rc=parse_packet(kbpos->fp, pkt)) != -1 ) { - if( rc ) { /* ignore errors */ - if( rc != G10ERR_UNKNOWN_PACKET ) { - log_error("read_keyblock: read error: %s\n", g10_errstr(rc) ); - rc = G10ERR_INV_KEYRING; - goto ready; - } - free_packet( pkt ); - init_packet( pkt ); - continue; - } - /* make a linked list of all packets */ - switch( pkt->pkttype ) { - case PKT_COMPRESSED: - log_error("skipped compressed packet in keyring\n" ); - free_packet(pkt); - init_packet(pkt); - break; - - case PKT_PUBLIC_KEY: - case PKT_SECRET_KEY: - if( root ) { /* store this packet */ - kbpos->pkt = pkt; - pkt = NULL; - goto ready; - } - root = new_kbnode( pkt ); - pkt = m_alloc( sizeof *pkt ); - init_packet(pkt); - break; - - default: - /* skip pakets at the beginning of a keyring, until we find - * a start packet; issue a warning if it is not a comment */ - if( !root && pkt->pkttype != PKT_COMMENT - && pkt->pkttype != PKT_OLD_COMMENT ) { - break; - } - if( !root || (skipsigs && ( pkt->pkttype == PKT_SIGNATURE - ||pkt->pkttype == PKT_COMMENT - ||pkt->pkttype == PKT_OLD_COMMENT )) ) { - init_packet(pkt); - break; - } - add_kbnode( root, new_kbnode( pkt ) ); - pkt = m_alloc( sizeof *pkt ); - init_packet(pkt); - break; - } - } - ready: - if( rc == -1 && root ) - rc = 0; - - if( rc ) - release_kbnode( root ); - else - *ret_root = root; - free_packet( pkt ); - m_free( pkt ); - - return rc; -} - - -/**************** - * Perform insert/delete/update operation. - * mode 1 = insert - * 2 = delete - * 3 = update - */ -static int -keyring_copy( KBPOS *kbpos, int mode, KBNODE root ) -{ - RESTBL *rentry; - IOBUF fp, newfp; - int rc=0; - char *bakfname = NULL; - char *tmpfname = NULL; - - if( !(rentry = check_pos( kbpos )) ) - return G10ERR_GENERAL; - if( kbpos->fp ) - BUG(); /* not allowed with such a handle */ - - if( opt.dry_run ) - return 0; - - lock_rentry( rentry ); - - /* open the source file */ - fp = iobuf_open( rentry->fname ); - if( mode == 1 && !fp && errno == ENOENT ) { /* no file yet */ - KBNODE kbctx, node; - - /* insert: create a new file */ - newfp = iobuf_create( rentry->fname ); - if( !newfp ) { - log_error(_("%s: can't create: %s\n"), rentry->fname, strerror(errno)); - unlock_rentry( rentry ); - return G10ERR_OPEN_FILE; - } - else if( !opt.quiet ) - log_info(_("%s: keyring created\n"), rentry->fname ); - - kbctx=NULL; - while( (node = walk_kbnode( root, &kbctx, 0 )) ) { - if( (rc = build_packet( newfp, node->pkt )) ) { - log_error("build_packet(%d) failed: %s\n", - node->pkt->pkttype, g10_errstr(rc) ); - iobuf_cancel(newfp); - unlock_rentry( rentry ); - return G10ERR_WRITE_FILE; - } - } - if( iobuf_close(newfp) ) { - log_error("%s: close failed: %s\n", rentry->fname, strerror(errno)); - unlock_rentry( rentry ); - return G10ERR_CLOSE_FILE; - } - if( chmod( rentry->fname, S_IRUSR | S_IWUSR ) ) { - log_error("%s: chmod failed: %s\n", - rentry->fname, strerror(errno) ); - unlock_rentry( rentry ); - return G10ERR_WRITE_FILE; - } - return 0; - } - if( !fp ) { - log_error("%s: can't open: %s\n", rentry->fname, strerror(errno) ); - rc = G10ERR_OPEN_FILE; - goto leave; - } - - /* create the new file */ - #ifdef USE_ONLY_8DOT3 - /* Here is another Windoze bug?: - * you cant rename("pubring.gpg.tmp", "pubring.gpg"); - * but rename("pubring.gpg.tmp", "pubring.aaa"); - * works. So we replace .gpg by .bak or .tmp - */ - if( strlen(rentry->fname) > 4 - && !strcmp(rentry->fname+strlen(rentry->fname)-4, EXTSEP_S "gpg") ) { - bakfname = m_alloc( strlen( rentry->fname ) + 1 ); - strcpy(bakfname,rentry->fname); - strcpy(bakfname+strlen(rentry->fname)-4, EXTSEP_S "bak"); - tmpfname = m_alloc( strlen( rentry->fname ) + 1 ); - strcpy(tmpfname,rentry->fname); - strcpy(tmpfname+strlen(rentry->fname)-4, EXTSEP_S "tmp"); - } - else { /* file does not end with gpg; hmmm */ - bakfname = m_alloc( strlen( rentry->fname ) + 5 ); - strcpy(stpcpy(bakfname,rentry->fname), EXTSEP_S "bak"); - tmpfname = m_alloc( strlen( rentry->fname ) + 5 ); - strcpy(stpcpy(tmpfname,rentry->fname), EXTSEP_S "tmp"); - } - #else - bakfname = m_alloc( strlen( rentry->fname ) + 2 ); - strcpy(stpcpy(bakfname,rentry->fname),"~"); - tmpfname = m_alloc( strlen( rentry->fname ) + 5 ); - strcpy(stpcpy(tmpfname,rentry->fname), EXTSEP_S "tmp"); - #endif - newfp = iobuf_create( tmpfname ); - if( !newfp ) { - log_error("%s: can't create: %s\n", tmpfname, strerror(errno) ); - iobuf_close(fp); - rc = G10ERR_OPEN_FILE; - goto leave; - } - - if( mode == 1 ) { /* insert */ - /* copy everything to the new file */ - rc = copy_all_packets( fp, newfp ); - if( rc != -1 ) { - log_error("%s: copy to %s failed: %s\n", - rentry->fname, tmpfname, g10_errstr(rc) ); - iobuf_close(fp); - iobuf_cancel(newfp); - goto leave; - } - rc = 0; - } - - if( mode == 2 || mode == 3 ) { /* delete or update */ - /* copy first part to the new file */ - rc = copy_some_packets( fp, newfp, kbpos->offset ); - if( rc ) { /* should never get EOF here */ - log_error("%s: copy to %s failed: %s\n", - rentry->fname, tmpfname, g10_errstr(rc) ); - iobuf_close(fp); - iobuf_cancel(newfp); - goto leave; - } - /* skip this keyblock */ - assert( kbpos->count ); - rc = skip_some_packets( fp, kbpos->count ); - if( rc ) { - log_error("%s: skipping %u packets failed: %s\n", - rentry->fname, kbpos->count, g10_errstr(rc)); - iobuf_close(fp); - iobuf_cancel(newfp); - goto leave; - } - } - - if( mode == 1 || mode == 3 ) { /* insert or update */ - KBNODE kbctx, node; - - /* append the new data */ - kbctx=NULL; - while( (node = walk_kbnode( root, &kbctx, 0 )) ) { - if( (rc = build_packet( newfp, node->pkt )) ) { - log_error("build_packet(%d) failed: %s\n", - node->pkt->pkttype, g10_errstr(rc) ); - iobuf_close(fp); - iobuf_cancel(newfp); - rc = G10ERR_WRITE_FILE; - goto leave; - } - } - kbpos->valid = 0; - } - - if( mode == 2 || mode == 3 ) { /* delete or update */ - /* copy the rest */ - rc = copy_all_packets( fp, newfp ); - if( rc != -1 ) { - log_error("%s: copy to %s failed: %s\n", - rentry->fname, tmpfname, g10_errstr(rc) ); - iobuf_close(fp); - iobuf_cancel(newfp); - goto leave; - } - rc = 0; - } - - /* close both files */ - if( iobuf_close(fp) ) { - log_error("%s: close failed: %s\n", rentry->fname, strerror(errno) ); - rc = G10ERR_CLOSE_FILE; - goto leave; - } - if( iobuf_close(newfp) ) { - log_error("%s: close failed: %s\n", tmpfname, strerror(errno) ); - rc = G10ERR_CLOSE_FILE; - goto leave; - } - /* if the new file is a secring, restrict the permissions */ - #ifndef HAVE_DOSISH_SYSTEM - if( rentry->secret && !opt.preserve_permissions ) { - if( chmod( tmpfname, S_IRUSR | S_IWUSR ) ) { - log_error("%s: chmod failed: %s\n", - tmpfname, strerror(errno) ); - rc = G10ERR_WRITE_FILE; - goto leave; - } - } - #endif - - /* rename and make backup file */ - if( !rentry->secret ) { /* but not for secret keyrings */ - iobuf_ioctl (NULL, 2, 0, bakfname ); - iobuf_ioctl (NULL, 2, 0, rentry->fname ); - #if defined(HAVE_DOSISH_SYSTEM) || defined(__riscos__) - remove( bakfname ); - #endif - if( rename( rentry->fname, bakfname ) ) { - log_error("%s: rename to %s failed: %s\n", - rentry->fname, bakfname, strerror(errno) ); - rc = G10ERR_RENAME_FILE; - goto leave; - } - } - iobuf_ioctl (NULL, 2, 0, tmpfname ); - iobuf_ioctl (NULL, 2, 0, rentry->fname ); - #if defined(HAVE_DOSISH_SYSTEM) || defined(__riscos__) - remove( rentry->fname ); - #endif - if( rename( tmpfname, rentry->fname ) ) { - log_error("%s: rename to %s failed: %s\n", - tmpfname, rentry->fname,strerror(errno) ); - rc = G10ERR_RENAME_FILE; - if( rentry->secret ) { - log_info(_( - "WARNING: 2 files with confidential information exists.\n")); - log_info(_("%s is the unchanged one\n"), rentry->fname ); - log_info(_("%s is the new one\n"), tmpfname ); - log_info(_("Please fix this possible security flaw\n")); - } - goto leave; - } - - leave: - unlock_rentry( rentry ); - m_free(bakfname); - m_free(tmpfname); - return rc; -} - - -#ifdef HAVE_LIBGDBM -/**************************************************************** - ********** Functions which operates on GDM files *************** - ****************************************************************/ - -#if MAX_FINGERPRINT_LEN > 20 - #error A GDBM keyring assumes that fingerprints are less than 21 -#endif - -/**************** - * Insert the keyblock into the GDBM database - */ - -static int -do_gdbm_store( KBPOS *kbpos, KBNODE root, int update ) -{ - RESTBL *rentry; - PKT_public_key *pk; - KBNODE kbctx, node; - IOBUF fp = NULL; - byte fpr[20]; - byte contbuf[21]; - byte keybuf[21]; - size_t fprlen; - datum key, content; - int i, rc; - - if( !(rentry = check_pos( kbpos )) ) - return G10ERR_GENERAL; - - if( opt.dry_run ) - return 0; - - /* construct the fingerprint which is used as the primary key */ - node = find_kbnode( root, PKT_PUBLIC_KEY ); - if( !node ) - log_bug("a gdbm database can't store secret keys\n"); - pk = node->pkt->pkt.public_key; - - fingerprint_from_pk( pk, fpr, &fprlen ); - for(i=fprlen; i < DIM(fpr); i++ ) - fpr[i] = 0; - - /* build the keyblock */ - kbctx=NULL; - fp = iobuf_temp(); - iobuf_put( fp, 1 ); /* data is a keyblock */ - while( (node = walk_kbnode( root, &kbctx, 0 )) ) { - if( (rc = build_packet( fp, node->pkt )) ) { - log_error("build_packet(%d) failed: %s\n", - node->pkt->pkttype, g10_errstr(rc) ); - rc = G10ERR_WRITE_FILE; - goto leave; - } - } - /* store data and key */ - *keybuf = 1; /* key is a padded fingerprint */ - memcpy(keybuf+1, fpr, 20 ); - key.dptr = keybuf; - key.dsize = 21; - content.dptr = iobuf_get_temp_buffer( fp ); - content.dsize = iobuf_get_temp_length( fp ); - rc = gdbm_store( rentry->dbf, key, content, - update? GDBM_REPLACE : GDBM_INSERT ); - if( rc == 1 && !update ) - rc = gdbm_store( rentry->dbf, key, content, GDBM_REPLACE ); - - if( rc ) { - log_error("%s: gdbm_store failed: %s\n", rentry->fname, - rc == 1 ? "already stored" - : gdbm_strerror(gdbm_errno) ); - rc = G10ERR_WRITE_FILE; - goto leave; - } - /* now store all keyids */ - *contbuf = 2; /* data is a list of fingerprints */ - memcpy(contbuf+1, fpr, 20 ); - content.dptr = contbuf; - content.dsize= 21; - kbctx=NULL; - while( (node = walk_kbnode( root, &kbctx, 0 )) ) { - if( node->pkt->pkttype == PKT_PUBLIC_KEY - || node->pkt->pkttype == PKT_PUBLIC_SUBKEY ) { - u32 aki[2]; - - keyid_from_pk( node->pkt->pkt.public_key, aki ); - *keybuf = 2; /* key is a 8 byte keyid */ - u32tobuf( keybuf+1 , aki[0] ); - u32tobuf( keybuf+5, aki[1] ); - key.dptr = keybuf; - key.dsize= 9; - /* fixme: must be more clever when a insert failed: - * build a list of fingerprints in this case */ - rc = gdbm_store( rentry->dbf, key, content, - update? GDBM_REPLACE : GDBM_INSERT ); - if( rc ) { - log_info("%s: gdbm_store keyid failed: %s\n", rentry->fname, - rc == 1 ? "already stored" - : gdbm_strerror(gdbm_errno) ); - rc = 0; - } - } - } - - leave: - iobuf_close(fp); /* don't need a cancel because it is a temp iobuf */ - return rc; -} - - - -/**************** - * search one keybox, return 0 if found, -1 if not found or an errorcode. - */ -static int -do_gdbm_locate( GDBM_FILE dbf, KBPOS *kbpos, const byte *fpr, int fprlen ) -{ - byte *keybuf = kbpos->keybuf; - datum key; - int i; - - *keybuf = 1; - for(i=0; i < fprlen; i++ ) - keybuf[i+1] = fpr[i]; - for(; i < 20; i++ ) - keybuf[i+1] = 0; - - /* fetch the data */ - key.dptr = keybuf; - key.dsize = 21; - if( !gdbm_exists( dbf, key ) ) - return -1; /* not found */ - return 0; -} - -/**************** - * locate by keyid. - * FIXME: we must have a way to enumerate thru the list opf fingerprints - */ -#if 0 /* not used */ -static int -do_gdbm_locate_by_keyid( GDBM_FILE dbf, KBPOS *kbpos, u32 *keyid ) -{ - byte keybuf[9]; - datum key, content; - int rc; - - /* construct the fingerprint which is used as the primary key */ - *keybuf = 2; - u32tobuf( keybuf+1, keyid[0] ); - u32tobuf( keybuf+5, keyid[1] ); - - /* fetch the data */ - key.dptr = keybuf; - key.dsize = 9; - content = gdbm_fetch( dbf, key ); - if( !content.dptr ) - return -1; - - if( content.dsize < 2 ) { - log_error("gdbm_fetch did not return enough data\n" ); - free( content.dptr ); /* can't use m_free() here */ - return G10ERR_INV_KEYRING; - } - if( *content.dptr != 2 ) { - log_error("gdbm_fetch returned unexpected type %d\n", - *(byte*)content.dptr ); - free( content.dptr ); /* can't use m_free() here */ - return G10ERR_INV_KEYRING; - } - if( content.dsize < 21 ) { - log_error("gdbm_fetch did not return a complete fingerprint\n" ); - free( content.dptr ); /* can't use m_free() here */ - return G10ERR_INV_KEYRING; - } - if( content.dsize > 21 ) - log_info("gdbm_fetch: WARNING: more than one fingerprint\n" ); - - rc = do_gdbm_locate( dbf, kbpos, content.dptr+1, 20 ); - free( content.dptr ); /* can't use m_free() here */ - return rc; -} -#endif /* not used */ - - -static int -do_gdbm_read( KBPOS *kbpos, KBNODE *ret_root ) -{ - PACKET *pkt; - int rc; - RESTBL *rentry; - KBNODE root = NULL; - IOBUF a; - datum key, content; - - if( !(rentry=check_pos(kbpos)) ) - return G10ERR_GENERAL; - - key.dptr = kbpos->keybuf; - key.dsize = 21; - content = gdbm_fetch( rentry->dbf, key ); - if( !content.dptr ) { - log_error("gdbm_fetch failed: %s\n", gdbm_strerror(gdbm_errno) ); - return G10ERR_INV_KEYRING; - } - if( content.dsize < 2 ) { - log_error("gdbm_fetch did not return enough data\n" ); - free( content.dptr ); /* can't use m_free() here */ - return G10ERR_INV_KEYRING; - } - if( *content.dptr != 1 ) { - log_error("gdbm_fetch returned unexpected type %d\n", - *(byte*)content.dptr ); - free( content.dptr ); /* can't use m_free() here */ - return G10ERR_INV_KEYRING; - } - - a = iobuf_temp_with_content( content.dptr+1, content.dsize-1 ); - free( content.dptr ); /* can't use m_free() here */ - - pkt = m_alloc( sizeof *pkt ); - init_packet(pkt); - kbpos->count=0; - while( (rc=parse_packet(a, pkt)) != -1 ) { - if( rc ) { /* ignore errors */ - if( rc != G10ERR_UNKNOWN_PACKET ) { - log_error("read_keyblock: read error: %s\n", g10_errstr(rc) ); - rc = G10ERR_INV_KEYRING; - break; - } - kbpos->count++; - free_packet( pkt ); - init_packet( pkt ); - continue; - } - /* make a linked list of all packets */ - kbpos->count++; - if( !root ) - root = new_kbnode( pkt ); - else - add_kbnode( root, new_kbnode( pkt ) ); - pkt = m_alloc( sizeof *pkt ); - init_packet(pkt); - } - if( rc == -1 && root ) - rc = 0; - if( rc ) - release_kbnode( root ); - else - *ret_root = root; - free_packet( pkt ); - m_free( pkt ); - iobuf_close(a); - return rc; -} - - -/**************** - * Enum over keyblok data - */ -static int -do_gdbm_enum( KBPOS *kbpos, KBNODE *ret_root ) -{ - RESTBL *rentry; - datum key, helpkey; - - if( !(rentry=check_pos(kbpos)) ) - return G10ERR_GENERAL; - - if( !kbpos->offset ) { - kbpos->offset = 1; - key = gdbm_firstkey( rentry->dbf ); - } - else { - helpkey.dptr = kbpos->keybuf; - helpkey.dsize= 21; - key = gdbm_nextkey( rentry->dbf, helpkey ); - } - while( key.dptr && (!key.dsize || *key.dptr != 1) ) { - helpkey = key; - key = gdbm_nextkey( rentry->dbf, helpkey ); - free( helpkey.dptr ); /* free and not m_free() ! */ - } - if( !key.dptr ) - return -1; /* eof */ - - if( key.dsize < 21 ) { - free( key.dptr ); /* free and not m_free() ! */ - log_error("do_gdm_enum: key is too short\n" ); - return G10ERR_INV_KEYRING; - } - memcpy( kbpos->keybuf, key.dptr, 21 ); - free( key.dptr ); /* free and not m_free() ! */ - return do_gdbm_read( kbpos, ret_root ); -} - -#endif /*HAVE_LIBGDBM*/ diff --git a/g10/status.c b/g10/status.c index 9ada277da..7a7ddf850 100644 --- a/g10/status.c +++ b/g10/status.c @@ -140,6 +140,7 @@ get_status_string ( int no ) case STATUS_KEY_CREATED : s = "KEY_CREATED"; break; case STATUS_USERID_HINT : s = "USERID_HINT"; break; case STATUS_UNEXPECTED : s = "UNEXPECTED"; break; + case STATUS_INV_RECP : s = "INV_RECP"; break; default: s = "?"; break; } return s; diff --git a/g10/status.h b/g10/status.h index 271959fa6..840df62ec 100644 --- a/g10/status.h +++ b/g10/status.h @@ -90,6 +90,8 @@ #define STATUS_KEY_CREATED 58 #define STATUS_USERID_HINT 59 #define STATUS_UNEXPECTED 60 +#define STATUS_INV_RECP 61 + /*-- status.c --*/ void set_status_fd ( int fd ); diff --git a/g10/tdbdump.c b/g10/tdbdump.c index d1a6b3120..b44cdbd95 100644 --- a/g10/tdbdump.c +++ b/g10/tdbdump.c @@ -352,7 +352,7 @@ list_trustdb( const char *username ) PKT_public_key *pk = m_alloc_clear( sizeof *pk ); int rc; - if( (rc = get_pubkey_byname( NULL, pk, username, NULL )) ) + if( (rc = get_pubkey_byname( pk, username, NULL, NULL )) ) log_error(_("user '%s' not found: %s\n"), username, g10_errstr(rc) ); else if( (rc=tdbio_search_dir_bypk( pk, &rec )) && rc != -1 ) log_error(_("problem finding '%s' in trustdb: %s\n"), diff --git a/g10/trustdb.c b/g10/trustdb.c index b7e5bbd6c..9de778aa4 100644 --- a/g10/trustdb.c +++ b/g10/trustdb.c @@ -1697,22 +1697,28 @@ void update_trustdb() { KBNODE keyblock = NULL; - KBPOS kbpos; + KEYDB_HANDLE kdbhd; int rc; if( opt.dry_run ) return; init_trustdb(); - rc = enum_keyblocks( 0, &kbpos, &keyblock ); - if( !rc ) { + kdbhd = keydb_new (0); + rc = keydb_search_first (kdbhd); + if (!rc) { ulong count=0, err_count=0, new_count=0; - while( !(rc = enum_keyblocks( 1, &kbpos, &keyblock )) ) { - /*int modified;*/ + do { TRUSTREC drec; - PKT_public_key *pk = find_kbnode( keyblock, PKT_PUBLIC_KEY ) - ->pkt->pkt.public_key; + PKT_public_key *pk; + /*int modified;*/ + + rc = keydb_get_keyblock (kdbhd, &keyblock ); + if (rc) + break; + + pk = find_kbnode (keyblock, PKT_PUBLIC_KEY)->pkt->pkt.public_key; rc = get_dir_record( pk, &drec ); if( rc == -1 ) { /* not in trustdb: insert */ @@ -1741,7 +1747,7 @@ update_trustdb() release_kbnode( keyblock ); keyblock = NULL; if( !(++count % 100) ) log_info(_("%lu keys so far processed\n"), count); - } + } while ( !(rc = keydb_search_next (kdbhd))); log_info(_("%lu keys processed\n"), count); if( err_count ) log_info(_("\t%lu keys with errors\n"), err_count); @@ -1751,7 +1757,7 @@ update_trustdb() if( rc && rc != -1 ) log_error(_("enumerate keyblocks failed: %s\n"), g10_errstr(rc)); - enum_keyblocks( 2, &kbpos, &keyblock ); /* close */ + keydb_release (kdbhd); release_kbnode( keyblock ); } @@ -2514,7 +2520,7 @@ list_trust_path( const char *username ) PKT_public_key *pk = m_alloc_clear( sizeof *pk ); init_trustdb(); - if( (rc = get_pubkey_byname(NULL, pk, username, NULL )) ) + if( (rc = get_pubkey_byname (pk, username, NULL, NULL )) ) log_error(_("user '%s' not found: %s\n"), username, g10_errstr(rc) ); else if( (rc=tdbio_search_dir_bypk( pk, &rec )) && rc != -1 ) log_error(_("problem finding '%s' in trustdb: %s\n"), diff --git a/scripts/autogen.sh b/scripts/autogen.sh index ac4bc2dae..46744cd2e 100755 --- a/scripts/autogen.sh +++ b/scripts/autogen.sh @@ -120,7 +120,7 @@ if (gettext --version /dev/null | awk 'NR==1 { split($4,A,"\."); \ X=10000*A[1]+100*A[2]+A[3]; echo X; if( X >= 1038 ) exit 1; exit 0}') then echo "**Error**: You must have "\`gettext\'" installed to compile $PGM." - echo ' (version 0.10.35 or newer is required; get' + echo ' (version 0.10.38 or newer is required; get' echo ' ftp://alpha.gnu.org/gnu/gettext/gettext-0.10.38.tar.gz' echo ' or install the latest Debian package)' DIE="yes" diff --git a/tools/gpgsplit.c b/tools/gpgsplit.c index 3094550f1..707f17147 100644 --- a/tools/gpgsplit.c +++ b/tools/gpgsplit.c @@ -49,7 +49,6 @@ enum cmd_and_opt_values { aNull = 0, oPrefix = 'p', aTest }; -#warning Add an option to split a keyring into reasonable sized chunks. static ARGPARSE_OPTS opts[] = { diff --git a/util/ChangeLog b/util/ChangeLog index 77ffb4f30..ccbe1764b 100644 --- a/util/ChangeLog +++ b/util/ChangeLog @@ -1,3 +1,7 @@ +2001-09-03 Werner Koch + + * miscutil.c (strtimestamp,asctimestamp): Avoid trigraphs. + 2001-08-21 Stefan Bellon * riscos.c [__riscos__] (close_fds): Fixed possible endless loop. diff --git a/util/miscutil.c b/util/miscutil.c index dbe6db4ff..928ec3bc7 100644 --- a/util/miscutil.c +++ b/util/miscutil.c @@ -126,7 +126,7 @@ strtimestamp( u32 stamp ) time_t atime = stamp; if (atime < 0) { - strcpy (buffer, "????-??-??"); + strcpy (buffer, "????" "-??" "-??"); } else { tp = gmtime( &atime ); @@ -150,7 +150,7 @@ asctimestamp( u32 stamp ) time_t atime = stamp; if (atime < 0) { - strcpy (buffer, "????-??-??"); + strcpy (buffer, "????" "-??" "-??"); return buffer; }