diff --git a/NEWS b/NEWS index 8e0506502..185104f91 100644 --- a/NEWS +++ b/NEWS @@ -1,746 +1,749 @@ - * + * New options --[no-]utf8-strings. + * New edit-menu commands "enable" and "disable" for entire keys. + + * You will be asked for a filename if gpg cannot deduce one. Noteworthy changes in version 0.9.8 ----------------------------------- * New subcommand "delsig" in the edit menu. * The name of the output file is not anymore the one which is embedded in the processed message, but the used filename with the extension stripped. To revert to the old behaviour you can use the option --use-embedded-filename. * Another hack to cope with pgp2 generated detached signatures. * latin-2 character set works (--charset=iso-8859-2). * New option --with-key-data to list the public key parameters. New option -N to insert notations and a --set-policy-url. A couple of other options to allow reseting of options. * Better support for HPUX. Noteworthy changes in version 0.9.7 ----------------------------------- * Add some work arounds for a bugs in pgp 2 which led to bad signatures when used with canoncial texts in some cases. * Enhanced some status outputs. Noteworthy changes in version 0.9.6 ----------------------------------- * Twofish is now statically linked by default. The experimental 128 bit version is now disabled. Full support will be available as soon as the OpenPGP WG has decided on an interpretation of rfc2440. * Dropped support for the ancient Blowfish160 which is not OpenPGP. * Merged gpgm and gpg into one binary. * Add "revsig" and "revkey" commands to the edit menu. It is now possible to revoke signature and subkeys. Noteworthy changes in version 0.9.5 ----------------------------------- * New command "lsign" in the keyedit menu to create non-exportable signatures. Removed --trusted-keys option. * A bunch of changes to the key validation code. * --list-trust-path now has an optional --with-colons format. * New command --recv-keys to import keys from an keyserver. Noteworthy changes in version 0.9.4 ----------------------------------- * New configure option --enable-static-rnd=[egd|linux|unix|none] to select a random gathering module for static linking. * The original text is now verbatim copied to a cleartext signed message. * Bugfixes but there are still a couple of bugs. Noteworthy changes in version 0.9.3 ----------------------------------- * Changed the internal design of getkey which now allows a efficient lookup of multiple keys and add a word match mode. * New options --[no-]encrypt-to. * Some changes to the configure stuff. Switched to automake 1.4. Removed intl/ from CVS, autogen.sh now uses gettextize. * Preferences now include Twofish. Removed preference to Blowfish with a special hack to suppress the "not listed in preferences" warning; this is to allow us to switch completely to Twofish in the near future. * Changed the locking stuff. * Print all user ids of a good signature. Noteworthy changes in version 0.9.2 ----------------------------------- * add some additional time warp checks. * Option --keyserver and command --send-keys to utilize HKP servers. * Upgraded to zlib 1.1.3 and fixed an inflate bug * More cleanup on the cleartext signatures. Noteworthy changes in version 0.9.1 ----------------------------------- * Polish language support. * When querying the passphrase, the key ID of the primary key is displayed along with the one of the used secondary key. * Fixed a bug occurring when decrypting pgp 5 encrypted messages, fixed an infinite loop bug in the 3DES code and in the code which looks for trusted signatures. * Fixed a bug in the mpi library which caused signatures not to compare okay. * Rewrote the handling of cleartext signatures; the code is now better maintainable (I hope so). * New status output VALIDSIG only for valid signatures together with the fingerprint of the signer's key. Noteworthy changes in version 0.9.0 ----------------------------------- * --export does now only exports rfc2440 compatible keys; the old behaviour is available with --export-all. Generation of v3 ElGamal (sign and encrypt) keys is not longer supported. * Fixed the uncompress bug. * Rewrote the rndunix module. There are two environment variables used for debugging now: GNUPG_RNDUNIX_DBG give the file to write debugging information (use "-" for stdout) and if GNUPG_RNDUNIX_DBGALL is set, all programs which are only tried are also printed. * New option --escape-from-lines to "dash-escape" "From " lines to prevent mailers to change them to ">From ". This is not enabled by default because it is not in compliance with rfc2440 - however, you should turn it on. Noteworthy changes in version 0.4.5 ----------------------------------- * The keyrings and the trustdb is now locked, so that other GnuPG processes won't damage these files. You may want to put the option --lock-once into your options file. * The latest self-signatures are now used; this enables --import to see updated preferences etc. * Import of subkeys should now work. * Random gathering modules may now be loaded as extensions. Add such a module for most Unices but it is very experimental! * Brazilian language support. Noteworthy changes in version 0.4.4 ----------------------------------- * Fixed the way the key expiration time is stored. If you have an expiration time on your key you should fix it with --edit-key and the command "expire". I apologize for this inconvenience. * Add option --charset to support "koi8-r" encoding of user ids. (Not yet tested). * Preferences should now work again. You should run "gpgm --check-trustdb \*" to rebuild all preferences. * Checking of certificates should now work but this needs a lot of testing. Key validation values are now cached in the trustdb; they should be recalculated as needed, but you may use --check-trustdb or --update-trustdb to do this. * Spanish translation by Urko Lusa. * Patch files are from now on signed. See the man page for the new option --not-dash-escaped. * New syntax: --edit-key [] If you run it without --batch the commands are executed and then you are put into normal mode unless you use "quit" or "save" as one of the commands. When in batch mode, the program quits after the last command, so you have to use "save" if you did some changes. It does not yet work completely, but may be used to list so the keys etc. Noteworthy changes in version 0.4.3 ----------------------------------- * Fixed the gettext configure bug. * Kludge for RSA keys: keyid and length of a RSA key are correctly reported, but you get an error if you try to use this key (If you do not have the non-US version). * Experimental support for keyrings stored in a GDBM database. This is *much* faster than a standard keyring. You will notice that the import gets slower with time; the reason is that all new keys are used to verify signatures of previous inserted keys. Use "--keyring gnupg-gdbm:". This is not (yet) supported for secret keys. * A Russian language file in the distribution (alternatives are in the contrib directory of the FTP servers) * commandline option processing now works as expected for GNU programs with the exception that you can't mix options and normal arguments. * Now --list-key lists all matching keys. This is needed in some other places too. Noteworthy changes in version 0.4.2 ----------------------------------- * This is only a snapshot: There are still a few bugs. * Fixed this huge memory leak. * Redesigned the trust database: You should run "gpgm --check-trustdb". New command --update-trustdb, which adds new key from the public keyring into your trustdb * Fixed a bug in the armor code, leading to invalid packet errors. (a workaround for this was to use --no-armor). The shorten line length (64 instead of 72) fixes a problem with pgp5 and keyservers. * comment packets are not anymore generated. "--export" filters them out. One Exception: The comment packets in a secret keyring are still used because they carry the factorization of the public prime product. * --import now only looks for KEYBLOCK headers, so you can now simply remove the "- " in front of such a header if someone accidently signed such a message or the keyblock is part of a cleartext signed message. * --with-colons now lists the key expiration time and not anymore the valid period. * Some keyblocks created with old releases have a wrong sequence of packets, so that the keyservers don't accept these keys. Simply using "--edit-key" fixes the problem. * New option --force-v3-sigs to generate signed messages which are compatible to PGP 5. * Add some code to support DLD (for non ELF systems) - but this is not tested because my BSD box is currently broken. * New command "expire" in the edit-key menu. Noteworthy changes in version 0.4.1 ----------------------------------- * A secondary key is used when the primary key is specified but cannot be used for the operation (if it is a sign-only key). * GNUPG can now handle concatenated armored messages: There is still a bug if different kinds of messages are mixed. * Iterated+Salted passphrases now work. If want to be sure that PGP5 is able to handle them you may want to use the options "--s2k-mode 3 --s2k-cipher-algo cast5 --s2k-digest-algo sha1" when changing a passphrase. * doc/OpenPGP talks about OpenPGP compliance, doc/HACKING gives a few hints about the internal structure. * Checked gnupg against the August 1998 draft (07) and I believe it is in compliance with this document (except for one point). * Fixed some bugs in the import merging code and rewrote some code for the trustdb. Noteworthy changes in version 0.4.0 ----------------------------------- * Triple DES is now supported. Michael Roth did this piece of needed work. We have now all the coded needed to be OpenPGP compliant. * Added a simple rpm spec file (see INSTALL). * detached and armored signatures are now using "PGP SIGNATURE", except when --rfc1991 is used. * All times which are not in the yyy-mm-dd format are now printed in local time. Noteworthy changes in version 0.3.5 ----------------------------------- * New option --throw-keyid to create anonymous enciphered messages. If gpg detects such a message it tires all available secret keys in turn so decode it. This is a gnupg extension and not in OpenPGP but it has been discussed there and afaik some products use this scheme too (Suggested by Nimrod Zimmerman). * Fixed a bug with 5 byte length headers. * --delete-[secret-]key is now also available in gpgm. * cleartext signatures are not anymore converted to LF only. * Fixed a trustdb problem. Run "gpgm --check-trustdb" to fix old trust dbs. * Building in another directory should now work. * Weak key detection mechanism (Niklas Hernaeus). Noteworthy changes in version 0.3.4 ----------------------------------- * New options --comment and --set-filename; see g10/OPTIONS * yes/no, y/n localized. * Fixed some bugs. Noteworthy changes in version 0.3.3 ----------------------------------- * IMPORTANT: I found yet another bug in the way the secret keys are encrypted - I did it the way pgp 2.x did it, but OpenPGP and pgp 5.x specify another (in some aspects simpler) method. To convert your secret keys you have to do this: 1. Build the new release but don't install it and keep a copy of the old program. 2. Disable the network, make sure that you are the only user, be sure that there are no Trojan horses etc .... 3. Use your old gpg (version 0.3.[12]) and set the passphrases of ALL your secret keys to empty! (gpg --change-passphrase your-user-id). 4. Save your ownertrusts (see the next point) 5. rm ~/.gnupg/trustdb.gpg 6. install the new version of gpg (0.3.3) 7. For every secret key call "gpg --edit-key your-user-id", enter "passwd" at the prompt, follow the instructions and change your password back, enter "save" to store it. 8. Restore the ownertrust (see next point). * The format of the trust database has changed; you must delete the old one, so gnupg can create a new one. IMPORTANT: Use version 0.3.[12] to save your assigned ownertrusts ("gpgm --list-ownertrust >saved-trust"); then build this new version and restore the ownertrust with this new version ("gpgm --import-ownertrust saved-trust"). Please note that --list-ownertrust has been renamed to --export-ownertrust in this release and it does now only export defined ownertrusts. * The command --edit-key now provides a commandline driven menu which can be used for various tasks. --sign-key is only an an alias to --edit-key and maybe removed in future: use the command "sign" of this new menu - you can select which user ids you want to sign. * Alternate user ids can now be created an signed. * Owner trust values can now be changed with --edit-key (trust) * GNUPG can now run as a coprocess; this enables sophisticated frontends. tools/shmtest.c is a simple sample implementation. This needs some more work: all tty_xxx() are to be replaced by cpr_xxx() and some changes in the display logics is needed. * Removed options --gen-prime and --gen-random. * Removed option --add-key; use --edit-key instead. * Removed option --change-passphrase; use --edit-key instead. * Signatures are now checked even if the output file could not be created. Command "--verify" tries to find the detached data. * gpg now disables core dumps. * compress and symmetric cipher preferences are now used. Because there is no 3DES yet, this is replaced by Blowfish. * We have added the Twofish as an experimental cipher algorithm. Many thanks to Matthew Skala for doing this work. Twofish is the AES submission from Schneier et al.; see "www.counterpane.com/twofish.html" for more information. * Started with a help system: If you enter a question mark at some prompt; you should get a specific help for this prompt. * There is no more backup copy of the secret keyring. * A lot of new bugs. I think this release is not as stable as the previous one. Noteworthy changes in version 0.3.2 ----------------------------------- * Fixed some bugs when using --textmode (-seat) * Now displays the trust status of a positive verified message. * Keyrings are now scanned in the sequence they are added with --[secret-]keyring. Note that the default keyring is implicitly added as the very first one unless --no-default-keyring is used. * Fixed setuid and dlopen bug. Noteworthy changes in version 0.3.1 ----------------------------------- * Partial headers are now written in the OpenPGP format if a key in a v4 packet is used. * Removed some unused options, removed the gnupg.sig stuff. * Key lookup by name now returns a key which can be used for the desired action. * New options --list-ownertrust (gpgm) to make a backup copy of the ownertrust values you assigned. * clear signature headers are now in compliance with OpenPGP. Noteworthy changes in version 0.3.0 ----------------------------------- * New option --emulate-checksum-bug. If your passphrase does not work anymore, use this option and --change-passphrase to rewrite your passphrase. * More complete v4 key support: Preferences and expiration time is set into the self signature. * Key generation defaults to DSA/ElGamal keys, so that new keys are interoperable with pgp5 * DSA key generation is faster and key generation does not anymore remove entropy from the random generator (the primes are public parameters, so there is really no need for a cryptographic secure prime number generator which we had used). * A complete new structure for representing the key parameters. * Removed most public key knowledge into the cipher library. * Support for dynamic loading of new algorithms. * Moved tiger to an extension module. Noteworthy changes in version 0.2.19 ------------------------------------ * Replaced /dev/urandom in checks with new tool mk-tdata. * Some assembler file cleanups; some more functions for the Alpha. * Tiger has now the OpenPGP assigned number 6. Because the OID has changed, old signatures using this algorithm can't be verified. * gnupg now encrypts the compressed packed and not any longer in the reverse order; anyway it can decrypt both versions. Thanks to Tom for telling me this (not security related) bug. * --add-key works and you are now able to generate subkeys. * It is now possible to generate ElGamal keys in v4 packets to create valid OpenPGP keys. * Some new features for better integration into MUAs. Noteworthy changes in version 0.2.18 ------------------------------------ * Splitted cipher/random.c, add new option "--disable-dev-random" to configure to support the development of a random source for other systems. Prepared sourcefiles rand-unix.c, rand-w32.c and rand-dummy.c (which is used to allow compilation on systems without a random source). * Fixed a small bug in the key generation (it was possible that 48 bits of a key were not taken from the random pool) * Add key generation for DSA and v4 signatures. * Add a function trap_unaligned(), so that a SIGBUS is issued on Alphas and not the slow emulation code is used. And success: rmd160 raised a SIGBUS. * Enhanced the formatting facility of argparse and changed the use of \r,\v to @ because gettext does not like it. * New option "--compress-algo 1" to allow the creation of compressed messages which are readable by PGP and "--print-md" (gpgm) to make speed measurement easier. Noteworthy changes in version 0.2.17 ------------------------------------ * Comment packets are now of private type 61. * Passphrase code still used a 160 bit blowfish key, added a silly workaround. Please change your passphrase again - sorry. * Conventional encryption now uses a type 3 packet to describe the used algorithms. * The new algorithm number for Blowfish is 20, 16 is still used for encryption only; for signing it is only used when it is in a v3 packet, so that GNUPG keys are still valid. Noteworthy changes in version 0.2.16 ------------------------------------ * Add experimental support for the TIGER/192 message digest algorithm. (But there is only a dummy ASN OID). * Standard cipher is now Blowfish with 128 bit key in OpenPGP's CFB mode. I renamed the old cipher to Blowfish160. Because the OpenPGP group refused to assign me a number for Blowfish160, I have to drop support for this in the future. You should use "--change-passphrase" to recode your current passphrase with 128 bit Blowfish. Noteworthy changes in version 0.2.15 ------------------------------------ * Fixed a bug with the old checksum calculation for secret keys. If you run the program without --batch, a warning does inform you if your secret key needs to be converted; simply use --change-passphrase to recalculate the checksum. Please do this soon, as the compatible mode will be removed sometime in the future. * CAST5 works (using the PGP's special CFB mode). * Again somewhat more PGP 5 compatible. * Some new test cases Noteworthy changes in version 0.2.14 ------------------------------------ * Changed the internal handling of keyrings. * Add support to list PGP 5 keyrings with subkeys * Timestamps of signatures are now verified. * A expiration time can now be specified during key generation. * Some speedups for Blowfish and SHA-1, rewrote SHA-1 transform. Reduced the amount of random bytes needed for key generation in some cases. Noteworthy changes in version 0.2.13 ------------------------------------ * Verify of DSA signatures works. * Re-implemented the slower random number generator. Noteworthy changes in version 0.2.12 ------------------------------------ * --delete-key checks that there is no secret key. The new option --delete-secret-key maybe used to delete a secret key. * "-kv" now works as expected. Options "--list-{keys,sigs]" and "--check-sigs" are now working. * New options "--verify" and "--decrypt" to better support integration into MUAs (partly done for Mutt). * New option "--with-colons" to make parsing of key lists easier. Noteworthy changes in version 0.2.11 ------------------------------------ * GPG now asks for a recipient's name if option "-r" is not used. * If there is no good trust path, the program asks whether to use the public keys anyway. * "--delete-key" works for public keys. What semantics shall I use when there is a secret key too? Delete the secret key or leave him and auto-regenerate the public key, next time the secret key is used? Noteworthy changes in version 0.2.10 ------------------------------------ * Code for the alpha is much faster (about 20 times); the data was misaligned and the kernel traps this, so nearly all time was used by system to trap the misalignments and to write syslog messages. Shame on me and thanks to Ralph for pointing me at this while drinking some beer yesterday. * Changed some configure options and add an option --disable-m-guard to remove the memory checking code and to compile everything with optimization on. * New environment variable GNUPGHOME, which can be used to set another homedir than ~/.gnupg. Changed default homedir for Windoze version to c:/gnupg. * Fixed detached signatures; detached PGP signatures caused a SEGV. * The Windoze version works (as usual w/o a strong RNG). Noteworthy changes in version 0.2.9 ----------------------------------- * Fixed FreeBSD bug. * Added a simple man page. * Switched to automake1.2f and a newer gettext. Noteworthy changes in version 0.2.8 ----------------------------------- * Changed the name to GNUPG, the binaries are called gpg and gpgm. You must rename rename the directory "~/.g10" to ~/.gnupg/, rename {pub,sec}ring.g10 to {pub,sec}ring.gpg, trustdb.g10 to trustdb.gpg and g10.sig to gnupg.sig. * New or changed passphrases are now salted. Noteworthy changes in version 0.2.7 ----------------------------------- * New command "gen-revoke" to create a key revocation certificate. * New option "homedir" to set the homedir (which defaults to "~/.g10"). This directory is created if it does not exists (only the last part of the name and not the complete hierarchy) * Command "import" works. (Try: "finger gcrypt@ftp.guug.de|g10 --import") * New commands "dearmor/enarmor" for g10maint. These are mainly used for internal test purposes. * Option --version now conforming to the GNU standards and lists the available ciphers, message digests and public key algorithms. * Assembler code for m68k (not tested). * "make check" works. Noteworthy changes in version 0.2.6 ----------------------------------- * Option "--export" works. Noteworthy changes in version 0.2.5 ----------------------------------- * Added zlib for systems which don't have it. Use "./configure --with-zlib" to link with the static version. * Generalized some more functions and rewrote the encoding of message digests into MPIs. * Enhanced the checkit script Noteworthy changes in version 0.2.4 ----------------------------------- * nearly doubled the speed of the ElGamal signature verification. * backup copies of keyrings are created. * assembler stuff for Pentium; gives about 15% better performance. * fixed a lot of bugs. Noteworthy changes in version 0.2.3 ----------------------------------- * Found a bug in the calculation of ELG fingerprints. This is now fixed, but all existing fingerprints and keyids for ELG keys are not any more valid. * armor should now work; including clear signed text. * moved some options to the new program g10maint * It's now 64 bit clean and runs fine on an alpha--linux. * Key generation is much faster now. I fixed this by using not so strong random number for the primes (this was a bug because the ElGamal primes are public parameters and it does not make sense to generate them from strong random). The real secret is the x value which is still generated from strong (okay: /dev/random) random bits. * added option "--status-fd": see g10/OPTIONS * We have secure memory on systems which support mlock(). It is not complete yet, because we do not have signal handler which does a cleanup in very case. We should also check the ulimit for the user in the case that the admin does not have set a limit on locked pages. * started with internationalization support. * The logic to handle the web of trust is now implemented. It is has some bugs; but I'm going to change the algorithm anyway. It works by calculating the trustlevel on the fly. It may ask you to provide trust parameters if the calculated trust probability is too low. I will write a paper which discusses this new approach. * a couple of changes to the configure script. * New option "--quick-random" which uses a much quicker random number generator. Keys generated while this option is in effect are flags with "INSECURE!" in the user-id. This is a development only option. * Read support for new version packets (OpenPGP). * Comment packets are now of correct OpenPGP type 16. Old comment packets written by G10 are detected because they always start with a hash which is an invalid version byte. * The string "(INSECURE!)" is appended to a new user-id if this is generated on a system without a good random number generator. diff --git a/TODO b/TODO index 49203445b..34b009b03 100644 --- a/TODO +++ b/TODO @@ -1,55 +1,52 @@ - * add keylength and type to status output. - * add some status output put for signing and encryption. replace the putc in primegen with some kind of status-fd outputs. * Speed up calculation of key validity. * print a warning when a revoked/expired _secret_ key is used. * remove more "Fixmes" * when decryptiong multiple key: print a warning only if no usable pubkey encrypt package was found. Extension: display a list of all recipients. * Add NO_PUBKEY and NO_SECKEY status lines. * Solaris make has problems with the generated POTFILES - seems to be a gettext bug. * find a way to allow the import of non-self-signed keys. This is needed for the IN ENCR/SIGN hack. - * convert the given user ID to UTF-8 and add an option to suppress this. + * Not all user names are correctly converted to UTF8. Nice to have ------------ * Let take --help an option to select some topics. * Offcial test vectors for 3DES-EDE3 * use DEL and ^H for erasing the previous character (util/ttyio.c). or better readline. * Print a warning if the directory mode is wrong. * replace the keyserver stuff either by a call to a specialized utility and SOCKSify this utility. * Do a real fix for bug #7 or document that it is a PGP 5 error. * preferences of hash algorithms are not yet used. * new menu to delete signatures and list signature in menu * Replace the SIGUSR1 stuff by semaphores to avoid loss of a signal. or use POSIX.4 realtime signals. Overhaul the interface and the test program. Use it with the test suite? * add test cases for invalid data (scrambled armor or other random data) * add checking of armor trailers * Burn the buffers used by fopen(), or use read(2). Does this really make sense? And while we are at it: implement a secure deletion stuff? * Stats about used random numbers. * the pubkey encrypt functions should do some sanity checks. * dynload: implement the hint stuff. * "gpg filename.tar.gz.asc" sollte wie mit --verify funktionieren (-sab). * for messages created with "-t", it might make sense to append the verification status of the message to the output (i.e. write something to the --output file and not only to stderr. - diff --git a/cipher/ChangeLog b/cipher/ChangeLog index 30d2ddd22..77a3e4b82 100644 --- a/cipher/ChangeLog +++ b/cipher/ChangeLog @@ -1,455 +1,461 @@ +Thu Jul 1 12:47:31 CEST 1999 Werner Koch + + + * primegen.c, elgamal.c, dsa.c (progess): New and replaced all + fputc with a call to this function. + Sat Jun 26 12:15:59 CEST 1999 Werner Koch * rndegd.c (do_write): s/ssize_t/int/ due to SunOS 4.1 probs. * cipher.c (do_cbc_encrypt, do_cbc_decrypt): New. * dynload.c (HAVE_DL_SHL_LOAD): Map hpux API to dlopen (Dave Dykstra). * Makefile.am (install-exec-hook): Removed. Sun May 23 14:20:22 CEST 1999 Werner Koch * cipher.c (setup_cipher_table): Enable Twofish * random.c (fast_random_poll): Disable use of times() for mingw32. Mon May 17 21:54:43 CEST 1999 Werner Koch * dynload.c (register_internal_cipher_extension): Minor init fix. Tue May 4 15:47:53 CEST 1999 Werner Koch * primegen.c (gen_prime): Readded the Fermat test. Fixed the bug that we didn't correct for step when passing the prime to the Rabin-Miller test which led to bad performance (Stefan Keller). (check_prime): Add a first Fermat test. Sun Apr 18 10:11:28 CEST 1999 Werner Koch * cipher.c (cipher_setiv): Add ivlen arg, changed all callers. * random.c (randomize_buffer): alway use secure memory because we can't use m_is_secure() on a statically allocated buffer. * twofish.c: Replaced some macros by a loop to reduce text size. * Makefile.am (twofish): No more need for sed editing. Fri Apr 9 12:26:25 CEST 1999 Werner Koch * cipher.c (cipher_open): Reversed the changes for AUTO_CFB. * blowfish.c: Dropped the Blowfish 160 mode. * cipher.c (cipher_open): Ditto. (setup_cipher_table): Ditto. And removed support of twofish128 Wed Apr 7 20:51:39 CEST 1999 Werner Koch * random.c (get_random_bits): Can now handle requests > POOLSIZE * cipher.c (cipher_open): Now uses standard CFB for automode if the blocksize is gt 8 (according to rfc2440). * twofish.c: Applied Matthew Skala's patches for 256 bit key. Tue Apr 6 19:58:12 CEST 1999 Werner Koch * random.c (get_random_bits): Can now handle requests > POOLSIZE * cipher.c (cipher_open): Now uses standard CFB for automode if the blocksize is gt 8 (according to rfc2440). Sat Mar 20 11:44:21 CET 1999 Werner Koch * rndlinux.c (tty_printf) [IS_MODULE]: Removed. * rndegd.c (gather_random): Some fixes. Wed Mar 17 13:09:03 CET 1999 Werner Koch * rndegd.c (do_read): New. (gather_random): Changed the implementation. Mon Mar 8 20:47:17 CET 1999 Werner Koch * dynload.c (DLSYM_NEEDS_UNDERSCORE): Renamed. Fri Feb 26 17:55:41 CET 1999 Werner Koch * md.c: Nearly a total rewrote. Wed Feb 24 11:07:27 CET 1999 Werner Koch * cipher.c (context): Fixed alignment * md.c: Ditto. * rndegd.c: New Mon Feb 22 20:04:00 CET 1999 Werner Koch * rndegd.c: New. Wed Feb 10 17:15:39 CET 1999 Werner Koch * Makefile.am: Modules are now figured out by configure * construct.c: New. Generated by configure. Changed all modules to work with that. * sha1.h: Removed. * md5.h: Removed. * twofish.c: Changed interface to allow Twofish/256 * rndunix.c (start_gatherer): Die on SIGPIPE. Wed Jan 20 18:59:49 CET 1999 Werner Koch * rndunix.c (gather_random): Fix to avoid infinite loop. Sun Jan 17 11:04:33 CET 1999 Werner Koch * des.c (is_weak_key): Replace system memcmp due to bugs in SunOS's memcmp. (des_get_info): Return error on failed selftest. * twofish.c (twofish_setkey): Return error on failed selftest or invalid keylength. * cast5.c (cast_setkey): Ditto. * blowfish.c (bf_setkey): Return error on failed selftest. Tue Jan 12 11:17:18 CET 1999 Werner Koch * random.c (random_is_faked): New. * tiger.c: Only compile if we have the u64 type Sat Jan 9 16:02:23 CET 1999 Werner Koch * rndunix.c (gather_random): check for setuid. * Makefile.am: Add a way to staically link random modules Thu Jan 7 18:00:58 CET 1999 Werner Koch * md.c (md_stop_debug): Do a flush first. (md_open): size of buffer now depends on the secure parameter Sun Jan 3 15:28:44 CET 1999 Werner Koch * rndunix.c (start_gatherer): Fixed stupid ==/= bug 1998-12-31 Geoff Keating * des.c (is_weak_key): Rewrite loop end condition. Tue Dec 29 14:41:47 CET 1998 Werner Koch * random.c: add unistd.h for getpid(). (RAND_MAX): Fallback value for Sun. Wed Dec 23 17:12:24 CET 1998 Werner Koch * md.c (md_copy): Reset debug. Mon Dec 14 21:18:49 CET 1998 Werner Koch * random.c (read_random_source): Changed the interface to the random gathering function. (gather_faked): Use new interface. * dynload.c (dynload_getfnc_fast_random_poll): Ditto. (dynload_getfnc_gather_random): Ditto. * rndlinux.c (gather_random): Ditto. * rndunix.c (gather_random): Ditto. Sat Dec 12 18:40:32 CET 1998 Werner Koch * dynload.c (SYMBOL_VERSION): New to cope with system which needs underscores. * rndunix.c: Rewrote large parts Thu Dec 10 20:15:36 CET 1998 Werner Koch * dynload.c (load_extension): increased needed verbosity level. * random.c (fast_random_poll): Fallback to a default fast random poll function. (read_random_source): Always use the faked entroy gatherer if no gather module is available. * rndlinux.c (fast_poll): Removed. * rndunix.c (fast_poll): Removed. Wed Nov 25 12:33:41 1998 Werner Koch (wk@isil.d.shuttle.de) * rand-*.c: Removed. * rndlinux.c : New. * rndunix.c : New. * random.c : Restructured the interface to the gather modules. (intialize): Call constructor functions (read_radnom_source): Moved to here. * dynload.c (dynload_getfnc_gather_random): New. (dynload_getfnc_fast_random_poll): New. (register_internal_cipher_extension): New. (register_cipher_extension): Support of internal modules. Sun Nov 8 17:44:36 1998 Werner Koch (wk@isil.d.shuttle.de) * rand-unix.c (read_random_source): Removed the assert. Mon Oct 19 18:34:30 1998 me,,, (wk@tobold) * pubkey.c: Hack to allow us to give some info about RSA keys back. Thu Oct 15 11:47:57 1998 Werner Koch (wk@isil.d.shuttle.de) * dynload.c: Support for DLD Wed Oct 14 12:13:07 1998 Werner Koch (wk@isil.d.shuttle.de) * rand-unix.c: Now uses names from configure for /dev/random. 1998-10-10 SL Baur * Makefile.am: fix sed -O substitutions to catch -O6, etc. Tue Oct 6 10:06:32 1998 Werner Koch (wk@isil.d.shuttle.de) * rand-unix.c (HAVE_GETTIMEOFDAY): Fixed (was ..GETTIMEOFTIME :-) * rand-dummy.c (HAVE_GETTIMEOFDAY): Ditto. Mon Sep 28 13:23:09 1998 Werner Koch (wk@isil.d.shuttle.de) * md.c (md_digest): New. (md_reset): New. Wed Sep 23 12:27:02 1998 Werner Koch (wk@isil.d.shuttle.de) * tiger.c (TIGER_CONTEXT): moved "buf", so that it is 64 bit aligned. Mon Sep 21 06:22:53 1998 Werner Koch (wk@(none)) * des.c: Some patches from Michael. Thu Sep 17 19:00:06 1998 Werner Koch (wk@(none)) * des.c : New file from Michael Roth Mon Sep 14 11:10:55 1998 Werner Koch (wk@(none)) * blowfish.c (bf_setkey): Niklas Hernaeus patch to detect weak keys. Mon Sep 14 09:19:25 1998 Werner Koch (wk@(none)) * dynload.c (RTLD_NOW): Now defined to 1 if it is undefined. Mon Sep 7 17:04:33 1998 Werner Koch (wk@(none)) * Makefile.am: Fixes to allow a different build directory Thu Aug 6 17:25:38 1998 Werner Koch,mobil,,, (wk@tobold) * random.c (get_random_byte): Removed and changed all callers to use get_random_bits() Mon Jul 27 10:30:22 1998 Werner Koch (wk@(none)) * cipher.c : Support for other blocksizes (cipher_get_blocksize): New. * twofish.c: New. * Makefile.am: Add twofish module. Mon Jul 13 21:30:52 1998 Werner Koch (wk@isil.d.shuttle.de) * random.c (read_pool): Simple alloc if secure_alloc is not set. (get_random_bits): Ditto. Thu Jul 9 13:01:14 1998 Werner Koch (wk@isil.d.shuttle.de) * dynload.c (load_extension): Function now nbails out if the program is run setuid. Wed Jul 8 18:58:23 1998 Werner Koch (wk@isil.d.shuttle.de) * rmd160.c (rmd160_hash_buffer): New. Thu Jul 2 10:50:30 1998 Werner Koch (wk@isil.d.shuttle.de) * cipher.c (cipher_open): algos >=100 use standard CFB Thu Jun 25 11:18:25 1998 Werner Koch (wk@isil.d.shuttle.de) * Makefile.am: Support for extensions Thu Jun 18 12:09:38 1998 Werner Koch (wk@isil.d.shuttle.de) * random.c (mix_pool): simpler handling for level 0 Mon Jun 15 14:40:48 1998 Werner Koch (wk@isil.d.shuttle.de) * tiger.c: Removed from dist, will reappear as dynload module Sat Jun 13 14:16:57 1998 Werner Koch (wk@isil.d.shuttle.de) * pubkey.c: Major changes to allow extensions. Changed the inteface of all public key ciphers and added the ability to load extensions on demand. * misc.c: Removed. Wed Jun 10 07:52:08 1998 Werner Koch,mobil,,, (wk@tobold) * dynload.c: New. * cipher.c: Major changes to allow extensions. Mon Jun 8 22:43:00 1998 Werner Koch (wk@isil.d.shuttle.de) * cipher.c: Major internal chnages to support extensions. * blowfish.c (blowfish_get_info): New and made all internal functions static, changed heder. * cast5.c (cast5_get_info): Likewise. Mon Jun 8 12:27:52 1998 Werner Koch (wk@isil.d.shuttle.de) * tiger.c (transform): Fix for big endian * cipher.c (do_cfb_decrypt): Big endian fix. Fri May 22 07:30:39 1998 Werner Koch (wk@isil.d.shuttle.de) * md.c (md_get_oid): Add a new one for TIGER. Thu May 21 13:24:52 1998 Werner Koch (wk@isil.d.shuttle.de) * cipher.c: Add support for a dummy cipher Thu May 14 15:40:36 1998 Werner Koch (wk@isil.d.shuttle.de) * rmd160.c (transform): fixed sigbus - I should better add Christian von Roques's new implemenation of rmd160_write. Fri May 8 18:07:44 1998 Werner Koch (wk@isil.d.shuttle.de) * rand-internal.h, rand-unix.c, rand-w32.c, rand_dummy.c: New * random.c: Moved system specific functions to rand-****.c Fri May 8 14:01:17 1998 Werner Koch (wk@isil.d.shuttle.de) * random.c (fast_random_poll): add call to gethrtime. Tue May 5 21:28:55 1998 Werner Koch (wk@isil.d.shuttle.de) * elgamal.c (elg_generate): choosing x was not correct, could yield 6 bytes which are not from the random pool, tsss, tsss.. Tue May 5 14:09:06 1998 Werner Koch (wk@isil.d.shuttle.de) * primegen.c (generate_elg_prime): Add arg mode, changed all callers and implemented mode 1. Mon Apr 27 14:41:58 1998 Werner Koch (wk@isil.d.shuttle.de) * cipher.c (cipher_get_keylen): New. Sun Apr 26 14:44:52 1998 Werner Koch (wk@isil.d.shuttle.de) * tiger.c, tiger.h: New. Wed Apr 8 14:57:11 1998 Werner Koch (wk@isil.d.shuttle.de) * misc.c (check_pubkey_algo2): New. Tue Apr 7 18:46:49 1998 Werner Koch (wk@isil.d.shuttle.de) * cipher.c: New * misc.c (check_cipher_algo): Moved to cipher.c * cast5.c: Moved many functions to cipher.c * blowfish.c: Likewise. Sat Apr 4 19:52:08 1998 Werner Koch (wk@isil.d.shuttle.de) * cast5.c: Implemented and tested. Wed Apr 1 16:38:27 1998 Werner Koch (wk@isil.d.shuttle.de) * elgamal.c (elg_generate): Faster generation of x in some cases. Thu Mar 19 13:54:48 1998 Werner Koch (wk@isil.d.shuttle.de) * blowfish.c (blowfish_decode_cfb): changed XOR operation (blowfish_encode_cfb): Ditto. Thu Mar 12 14:04:05 1998 Werner Koch (wk@isil.d.shuttle.de) * sha1.c (transform): Rewrote * blowfish.c (encrypt): Unrolled for rounds == 16 (decrypt): Ditto. Tue Mar 10 16:32:08 1998 Werner Koch (wk@isil.d.shuttle.de) * rmd160.c (transform): Unrolled the loop. Tue Mar 10 13:05:14 1998 Werner Koch (wk@isil.d.shuttle.de) * random.c (read_pool): Add pool_balance stuff. (get_random_bits): New. * elgamal.c (elg_generate): Now uses get_random_bits to generate x. Tue Mar 10 11:33:51 1998 Werner Koch (wk@isil.d.shuttle.de) * md.c (md_digest_length): New. Tue Mar 10 11:27:41 1998 Werner Koch (wk@isil.d.shuttle.de) * dsa.c (dsa_verify): Works. Mon Mar 9 12:59:08 1998 Werner Koch (wk@isil.d.shuttle.de) * dsa.c, dsa.h: Removed some unused code. Wed Mar 4 10:39:22 1998 Werner Koch (wk@isil.d.shuttle.de) * md.c (md_open): Add call to fast_random_poll. blowfish.c (blowfish_setkey): Ditto. Tue Mar 3 13:32:54 1998 Werner Koch (wk@isil.d.shuttle.de) * rmd160.c (rmd160_mixblock): New. * random.c: Restructured to start with a new RNG implementation. * random.h: New. Mon Mar 2 19:21:46 1998 Werner Koch (wk@isil.d.shuttle.de) * gost.c, gost.h: Removed because they did only conatin trash. Sun Mar 1 16:42:29 1998 Werner Koch (wk@isil.d.shuttle.de) * random.c (fill_buffer): removed error message if n == -1. Fri Feb 27 16:39:34 1998 Werner Koch (wk@isil.d.shuttle.de) * md.c (md_enable): No init if called twice. Thu Feb 26 07:57:02 1998 Werner Koch (wk@isil.d.shuttle.de) * primegen.c (generate_elg_prime): Changed the progress printing. (gen_prime): Ditto. Tue Feb 24 12:28:42 1998 Werner Koch (wk@isil.d.shuttle.de) * md5.c, md.5 : Replaced by a modified version of md5.c from GNU textutils 1.22. Wed Feb 18 14:08:30 1998 Werner Koch (wk@isil.d.shuttle.de) * md.c, md.h : New debugging support Mon Feb 16 10:08:47 1998 Werner Koch (wk@isil.d.shuttle.de) * misc.c (cipher_algo_to_string): New (pubkey_algo_to_string): New. (digest_algo_to_string): New. diff --git a/cipher/dsa.c b/cipher/dsa.c index 14854635c..cbd90ac5f 100644 --- a/cipher/dsa.c +++ b/cipher/dsa.c @@ -1,427 +1,434 @@ /* dsa.c - DSA signature scheme * Copyright (C) 1998 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 "util.h" #include "mpi.h" #include "cipher.h" #include "dsa.h" typedef struct { MPI p; /* prime */ MPI q; /* group order */ MPI g; /* group generator */ MPI y; /* g^x mod p */ } DSA_public_key; typedef struct { MPI p; /* prime */ MPI q; /* group order */ MPI g; /* group generator */ MPI y; /* g^x mod p */ MPI x; /* secret exponent */ } DSA_secret_key; static MPI gen_k( MPI q ); static void test_keys( DSA_secret_key *sk, unsigned qbits ); static int check_secret_key( DSA_secret_key *sk ); static void generate( DSA_secret_key *sk, unsigned nbits, MPI **ret_factors ); static void sign(MPI r, MPI s, MPI input, DSA_secret_key *skey); static int verify(MPI r, MPI s, MPI input, DSA_public_key *pkey); +static void +progress( int c ) +{ + fputc( c, stderr ); +} + + /**************** * Generate a random secret exponent k less than q */ static MPI gen_k( MPI q ) { MPI k = mpi_alloc_secure( mpi_get_nlimbs(q) ); unsigned nbits = mpi_get_nbits(q); if( DBG_CIPHER ) log_debug("choosing a random k "); for(;;) { if( DBG_CIPHER ) - fputc('.', stderr); + progress('.'); { char *p = get_random_bits( nbits, 1, 1 ); mpi_set_buffer( k, p, (nbits+7)/8, 0 ); m_free(p); /* make sure that the number is of the exact lenght */ if( mpi_test_bit( k, nbits-1 ) ) mpi_set_highbit( k, nbits-1 ); else { mpi_set_highbit( k, nbits-1 ); mpi_clear_bit( k, nbits-1 ); } } if( !(mpi_cmp( k, q ) < 0) ) /* check: k < q */ continue; /* no */ if( !(mpi_cmp_ui( k, 0 ) > 0) ) /* check: k > 0 */ continue; /* no */ break; /* okay */ } if( DBG_CIPHER ) - fputc('\n', stderr); + progress('\n'); return k; } static void test_keys( DSA_secret_key *sk, unsigned qbits ) { DSA_public_key pk; MPI test = mpi_alloc( qbits / BITS_PER_MPI_LIMB ); MPI out1_a = mpi_alloc( qbits / BITS_PER_MPI_LIMB ); MPI out1_b = mpi_alloc( qbits / BITS_PER_MPI_LIMB ); pk.p = sk->p; pk.q = sk->q; pk.g = sk->g; pk.y = sk->y; /*mpi_set_bytes( test, qbits, get_random_byte, 0 );*/ { char *p = get_random_bits( qbits, 0, 0 ); mpi_set_buffer( test, p, (qbits+7)/8, 0 ); m_free(p); } sign( out1_a, out1_b, test, sk ); if( !verify( out1_a, out1_b, test, &pk ) ) log_fatal("DSA:: sign, verify failed\n"); mpi_free( test ); mpi_free( out1_a ); mpi_free( out1_b ); } /**************** * Generate a DSA key pair with a key of size NBITS * Returns: 2 structures filled with all needed values * and an array with the n-1 factors of (p-1) */ static void generate( DSA_secret_key *sk, unsigned nbits, MPI **ret_factors ) { MPI p; /* the prime */ MPI q; /* the 160 bit prime factor */ MPI g; /* the generator */ MPI y; /* g^x mod p */ MPI x; /* the secret exponent */ MPI h, e; /* helper */ unsigned qbits; byte *rndbuf; assert( nbits >= 512 && nbits <= 1024 ); qbits = 160; p = generate_elg_prime( 1, nbits, qbits, NULL, ret_factors ); /* get q out of factors */ q = mpi_copy((*ret_factors)[0]); if( mpi_get_nbits(q) != qbits ) BUG(); /* find a generator g (h and e are helpers)*/ /* e = (p-1)/q */ e = mpi_alloc( mpi_get_nlimbs(p) ); mpi_sub_ui( e, p, 1 ); mpi_fdiv_q( e, e, q ); g = mpi_alloc( mpi_get_nlimbs(p) ); h = mpi_alloc_set_ui( 1 ); /* we start with 2 */ do { mpi_add_ui( h, h, 1 ); /* g = h^e mod p */ mpi_powm( g, h, e, p ); } while( !mpi_cmp_ui( g, 1 ) ); /* continue until g != 1 */ /* select a random number which has these properties: * 0 < x < q-1 * This must be a very good random number because this * is the secret part. */ if( DBG_CIPHER ) log_debug("choosing a random x "); assert( qbits >= 160 ); x = mpi_alloc_secure( mpi_get_nlimbs(q) ); mpi_sub_ui( h, q, 1 ); /* put q-1 into h */ rndbuf = NULL; do { if( DBG_CIPHER ) - fputc('.', stderr); + progress('.'); if( !rndbuf ) rndbuf = get_random_bits( qbits, 2, 1 ); else { /* change only some of the higher bits (= 2 bytes)*/ char *r = get_random_bits( 16, 2, 1 ); memcpy(rndbuf, r, 16/8 ); m_free(r); } mpi_set_buffer( x, rndbuf, (qbits+7)/8, 0 ); mpi_clear_highbit( x, qbits+1 ); } while( !( mpi_cmp_ui( x, 0 )>0 && mpi_cmp( x, h )<0 ) ); m_free(rndbuf); mpi_free( e ); mpi_free( h ); /* y = g^x mod p */ y = mpi_alloc( mpi_get_nlimbs(p) ); mpi_powm( y, g, x, p ); if( DBG_CIPHER ) { - fputc('\n', stderr); + progress('\n'); log_mpidump("dsa p= ", p ); log_mpidump("dsa q= ", q ); log_mpidump("dsa g= ", g ); log_mpidump("dsa y= ", y ); log_mpidump("dsa x= ", x ); } /* copy the stuff to the key structures */ sk->p = p; sk->q = q; sk->g = g; sk->y = y; sk->x = x; /* now we can test our keys (this should never fail!) */ test_keys( sk, qbits ); } /**************** * Test whether the secret key is valid. * Returns: if this is a valid key. */ static int check_secret_key( DSA_secret_key *sk ) { int rc; MPI y = mpi_alloc( mpi_get_nlimbs(sk->y) ); mpi_powm( y, sk->g, sk->x, sk->p ); rc = !mpi_cmp( y, sk->y ); mpi_free( y ); return rc; } /**************** * Make a DSA signature from HASH and put it into r and s. */ static void sign(MPI r, MPI s, MPI hash, DSA_secret_key *skey ) { MPI k; MPI kinv; MPI tmp; /* select a random k with 0 < k < q */ k = gen_k( skey->q ); /* r = (a^k mod p) mod q */ mpi_powm( r, skey->g, k, skey->p ); mpi_fdiv_r( r, r, skey->q ); /* kinv = k^(-1) mod q */ kinv = mpi_alloc( mpi_get_nlimbs(k) ); mpi_invm(kinv, k, skey->q ); /* s = (kinv * ( hash + x * r)) mod q */ tmp = mpi_alloc( mpi_get_nlimbs(skey->p) ); mpi_mul( tmp, skey->x, r ); mpi_add( tmp, tmp, hash ); mpi_mulm( s , kinv, tmp, skey->q ); mpi_free(k); mpi_free(kinv); mpi_free(tmp); } /**************** * Returns true if the signature composed from R and S is valid. */ static int verify(MPI r, MPI s, MPI hash, DSA_public_key *pkey ) { int rc; MPI w, u1, u2, v; MPI base[3]; MPI exp[3]; if( !(mpi_cmp_ui( r, 0 ) > 0 && mpi_cmp( r, pkey->q ) < 0) ) return 0; /* assertion 0 < r < q failed */ if( !(mpi_cmp_ui( s, 0 ) > 0 && mpi_cmp( s, pkey->q ) < 0) ) return 0; /* assertion 0 < s < q failed */ w = mpi_alloc( mpi_get_nlimbs(pkey->q) ); u1 = mpi_alloc( mpi_get_nlimbs(pkey->q) ); u2 = mpi_alloc( mpi_get_nlimbs(pkey->q) ); v = mpi_alloc( mpi_get_nlimbs(pkey->p) ); /* w = s^(-1) mod q */ mpi_invm( w, s, pkey->q ); /* u1 = (hash * w) mod q */ mpi_mulm( u1, hash, w, pkey->q ); /* u2 = r * w mod q */ mpi_mulm( u2, r, w, pkey->q ); /* v = g^u1 * y^u2 mod p mod q */ base[0] = pkey->g; exp[0] = u1; base[1] = pkey->y; exp[1] = u2; base[2] = NULL; exp[2] = NULL; mpi_mulpowm( v, base, exp, pkey->p ); mpi_fdiv_r( v, v, pkey->q ); rc = !mpi_cmp( v, r ); mpi_free(w); mpi_free(u1); mpi_free(u2); mpi_free(v); return rc; } /********************************************* ************** interface ****************** *********************************************/ int dsa_generate( int algo, unsigned nbits, MPI *skey, MPI **retfactors ) { DSA_secret_key sk; if( algo != PUBKEY_ALGO_DSA ) return G10ERR_PUBKEY_ALGO; generate( &sk, nbits, retfactors ); skey[0] = sk.p; skey[1] = sk.q; skey[2] = sk.g; skey[3] = sk.y; skey[4] = sk.x; return 0; } int dsa_check_secret_key( int algo, MPI *skey ) { DSA_secret_key sk; if( algo != PUBKEY_ALGO_DSA ) return G10ERR_PUBKEY_ALGO; sk.p = skey[0]; sk.q = skey[1]; sk.g = skey[2]; sk.y = skey[3]; sk.x = skey[4]; if( !check_secret_key( &sk ) ) return G10ERR_BAD_SECKEY; return 0; } int dsa_sign( int algo, MPI *resarr, MPI data, MPI *skey ) { DSA_secret_key sk; if( algo != PUBKEY_ALGO_DSA ) return G10ERR_PUBKEY_ALGO; sk.p = skey[0]; sk.q = skey[1]; sk.g = skey[2]; sk.y = skey[3]; sk.x = skey[4]; resarr[0] = mpi_alloc( mpi_get_nlimbs( sk.p ) ); resarr[1] = mpi_alloc( mpi_get_nlimbs( sk.p ) ); sign( resarr[0], resarr[1], data, &sk ); return 0; } int dsa_verify( int algo, MPI hash, MPI *data, MPI *pkey, int (*cmp)(void *, MPI), void *opaquev ) { DSA_public_key pk; if( algo != PUBKEY_ALGO_DSA ) return G10ERR_PUBKEY_ALGO; pk.p = pkey[0]; pk.q = pkey[1]; pk.g = pkey[2]; pk.y = pkey[3]; if( !verify( data[0], data[1], hash, &pk ) ) return G10ERR_BAD_SIGN; return 0; } unsigned dsa_get_nbits( int algo, MPI *pkey ) { if( algo != PUBKEY_ALGO_DSA ) return 0; return mpi_get_nbits( pkey[0] ); } /**************** * Return some information about the algorithm. We need algo here to * distinguish different flavors of the algorithm. * Returns: A pointer to string describing the algorithm or NULL if * the ALGO is invalid. * Usage: Bit 0 set : allows signing * 1 set : allows encryption */ const char * dsa_get_info( int algo, int *npkey, int *nskey, int *nenc, int *nsig, int *use ) { *npkey = 4; *nskey = 5; *nenc = 0; *nsig = 2; switch( algo ) { case PUBKEY_ALGO_DSA: *use = PUBKEY_USAGE_SIG; return "DSA"; default: *use = 0; return NULL; } } diff --git a/cipher/elgamal.c b/cipher/elgamal.c index 9d9058f97..0e6b992c0 100644 --- a/cipher/elgamal.c +++ b/cipher/elgamal.c @@ -1,556 +1,563 @@ /* elgamal.c - ElGamal Public Key encryption * Copyright (C) 1998 Free Software Foundation, Inc. * * For a description of the algorithm, see: * Bruce Schneier: Applied Cryptography. John Wiley & Sons, 1996. * ISBN 0-471-11709-9. Pages 476 ff. * * 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 "util.h" #include "mpi.h" #include "cipher.h" #include "elgamal.h" typedef struct { MPI p; /* prime */ MPI g; /* group generator */ MPI y; /* g^x mod p */ } ELG_public_key; typedef struct { MPI p; /* prime */ MPI g; /* group generator */ MPI y; /* g^x mod p */ MPI x; /* secret exponent */ } ELG_secret_key; static void test_keys( ELG_secret_key *sk, unsigned nbits ); static MPI gen_k( MPI p ); static void generate( ELG_secret_key *sk, unsigned nbits, MPI **factors ); static int check_secret_key( ELG_secret_key *sk ); static void encrypt(MPI a, MPI b, MPI input, ELG_public_key *pkey ); static void decrypt(MPI output, MPI a, MPI b, ELG_secret_key *skey ); static void sign(MPI a, MPI b, MPI input, ELG_secret_key *skey); static int verify(MPI a, MPI b, MPI input, ELG_public_key *pkey); +static void +progress( int c ) +{ + fputc( c, stderr ); +} + + static void test_keys( ELG_secret_key *sk, unsigned nbits ) { ELG_public_key pk; MPI test = mpi_alloc( 0 ); MPI out1_a = mpi_alloc( nbits / BITS_PER_MPI_LIMB ); MPI out1_b = mpi_alloc( nbits / BITS_PER_MPI_LIMB ); MPI out2 = mpi_alloc( nbits / BITS_PER_MPI_LIMB ); pk.p = sk->p; pk.g = sk->g; pk.y = sk->y; /*mpi_set_bytes( test, nbits, get_random_byte, 0 );*/ { char *p = get_random_bits( nbits, 0, 0 ); mpi_set_buffer( test, p, (nbits+7)/8, 0 ); m_free(p); } encrypt( out1_a, out1_b, test, &pk ); decrypt( out2, out1_a, out1_b, sk ); if( mpi_cmp( test, out2 ) ) log_fatal("ElGamal operation: encrypt, decrypt failed\n"); sign( out1_a, out1_b, test, sk ); if( !verify( out1_a, out1_b, test, &pk ) ) log_fatal("ElGamal operation: sign, verify failed\n"); mpi_free( test ); mpi_free( out1_a ); mpi_free( out1_b ); mpi_free( out2 ); } /**************** * generate a random secret exponent k from prime p, so * that k is relatively prime to p-1 */ static MPI gen_k( MPI p ) { MPI k = mpi_alloc_secure( 0 ); MPI temp = mpi_alloc( mpi_get_nlimbs(p) ); MPI p_1 = mpi_copy(p); unsigned nbits = mpi_get_nbits(p); if( DBG_CIPHER ) log_debug("choosing a random k "); mpi_sub_ui( p_1, p, 1); for(;;) { if( DBG_CIPHER ) - fputc('.', stderr); + progress('.'); { char *pp = get_random_bits( nbits, 1, 1 ); mpi_set_buffer( k, pp, (nbits+7)/8, 0 ); m_free(pp); /* make sure that the number is of the exact lenght */ if( mpi_test_bit( k, nbits-1 ) ) mpi_set_highbit( k, nbits-1 ); else { mpi_set_highbit( k, nbits-1 ); mpi_clear_bit( k, nbits-1 ); } } if( !(mpi_cmp( k, p_1 ) < 0) ) /* check: k < (p-1) */ continue; /* no */ if( !(mpi_cmp_ui( k, 0 ) > 0) ) /* check: k > 0 */ continue; /* no */ if( mpi_gcd( temp, k, p_1 ) ) break; /* okay, k is relatively prime to (p-1) */ } if( DBG_CIPHER ) - fputc('\n', stderr); + progress('\n'); mpi_free(p_1); mpi_free(temp); return k; } /**************** * Generate a key pair with a key of size NBITS * Returns: 2 structures filles with all needed values * and an array with n-1 factors of (p-1) */ static void generate( ELG_secret_key *sk, unsigned nbits, MPI **ret_factors ) { MPI p; /* the prime */ MPI p_min1; MPI g; MPI x; /* the secret exponent */ MPI y; MPI temp; unsigned qbits; byte *rndbuf; p_min1 = mpi_alloc( (nbits+BITS_PER_MPI_LIMB-1)/BITS_PER_MPI_LIMB ); temp = mpi_alloc( (nbits+BITS_PER_MPI_LIMB-1)/BITS_PER_MPI_LIMB ); if( nbits < 512 ) qbits = 120; else if( nbits <= 1024 ) qbits = 160; else if( nbits <= 2048 ) qbits = 200; else qbits = 240; g = mpi_alloc(1); p = generate_elg_prime( 0, nbits, qbits, g, ret_factors ); mpi_sub_ui(p_min1, p, 1); /* select a random number which has these properties: * 0 < x < p-1 * This must be a very good random number because this is the * secret part. The prime is public and may be shared anyway, * so a random generator level of 1 is used for the prime. */ x = mpi_alloc_secure( nbits/BITS_PER_MPI_LIMB ); if( DBG_CIPHER ) log_debug("choosing a random x "); rndbuf = NULL; do { if( DBG_CIPHER ) - fputc('.', stderr); + progress('.'); if( rndbuf ) { /* change only some of the higher bits */ if( nbits < 16 ) {/* should never happen ... */ m_free(rndbuf); rndbuf = get_random_bits( nbits, 2, 1 ); } else { char *r = get_random_bits( 16, 2, 1 ); memcpy(rndbuf, r, 16/8 ); m_free(r); } } else rndbuf = get_random_bits( nbits, 2, 1 ); mpi_set_buffer( x, rndbuf, (nbits+7)/8, 0 ); mpi_clear_highbit( x, nbits+1 ); } while( !( mpi_cmp_ui( x, 0 )>0 && mpi_cmp( x, p_min1 )<0 ) ); m_free(rndbuf); y = mpi_alloc(nbits/BITS_PER_MPI_LIMB); mpi_powm( y, g, x, p ); if( DBG_CIPHER ) { - fputc('\n', stderr); + progress('\n'); log_mpidump("elg p= ", p ); log_mpidump("elg g= ", g ); log_mpidump("elg y= ", y ); log_mpidump("elg x= ", x ); } /* copy the stuff to the key structures */ sk->p = p; sk->g = g; sk->y = y; sk->x = x; /* now we can test our keys (this should never fail!) */ test_keys( sk, nbits - 64 ); mpi_free( p_min1 ); mpi_free( temp ); } /**************** * Test whether the secret key is valid. * Returns: if this is a valid key. */ static int check_secret_key( ELG_secret_key *sk ) { int rc; MPI y = mpi_alloc( mpi_get_nlimbs(sk->y) ); mpi_powm( y, sk->g, sk->x, sk->p ); rc = !mpi_cmp( y, sk->y ); mpi_free( y ); return rc; } static void encrypt(MPI a, MPI b, MPI input, ELG_public_key *pkey ) { MPI k; /* Note: maybe we should change the interface, so that it * is possible to check that input is < p and return an * error code. */ k = gen_k( pkey->p ); mpi_powm( a, pkey->g, k, pkey->p ); /* b = (y^k * input) mod p * = ((y^k mod p) * (input mod p)) mod p * and because input is < p * = ((y^k mod p) * input) mod p */ mpi_powm( b, pkey->y, k, pkey->p ); mpi_mulm( b, b, input, pkey->p ); #if 0 if( DBG_CIPHER ) { log_mpidump("elg encrypted y= ", pkey->y); log_mpidump("elg encrypted p= ", pkey->p); log_mpidump("elg encrypted k= ", k); log_mpidump("elg encrypted M= ", input); log_mpidump("elg encrypted a= ", a); log_mpidump("elg encrypted b= ", b); } #endif mpi_free(k); } static void decrypt(MPI output, MPI a, MPI b, ELG_secret_key *skey ) { MPI t1 = mpi_alloc_secure( mpi_get_nlimbs( skey->p ) ); /* output = b/(a^x) mod p */ mpi_powm( t1, a, skey->x, skey->p ); mpi_invm( t1, t1, skey->p ); mpi_mulm( output, b, t1, skey->p ); #if 0 if( DBG_CIPHER ) { log_mpidump("elg decrypted x= ", skey->x); log_mpidump("elg decrypted p= ", skey->p); log_mpidump("elg decrypted a= ", a); log_mpidump("elg decrypted b= ", b); log_mpidump("elg decrypted M= ", output); } #endif mpi_free(t1); } /**************** * Make an Elgamal signature out of INPUT */ static void sign(MPI a, MPI b, MPI input, ELG_secret_key *skey ) { MPI k; MPI t = mpi_alloc( mpi_get_nlimbs(a) ); MPI inv = mpi_alloc( mpi_get_nlimbs(a) ); MPI p_1 = mpi_copy(skey->p); /* * b = (t * inv) mod (p-1) * b = (t * inv(k,(p-1),(p-1)) mod (p-1) * b = (((M-x*a) mod (p-1)) * inv(k,(p-1),(p-1))) mod (p-1) * */ mpi_sub_ui(p_1, p_1, 1); k = gen_k( skey->p ); mpi_powm( a, skey->g, k, skey->p ); mpi_mul(t, skey->x, a ); mpi_subm(t, input, t, p_1 ); while( mpi_is_neg(t) ) mpi_add(t, t, p_1); mpi_invm(inv, k, p_1 ); mpi_mulm(b, t, inv, p_1 ); #if 0 if( DBG_CIPHER ) { log_mpidump("elg sign p= ", skey->p); log_mpidump("elg sign g= ", skey->g); log_mpidump("elg sign y= ", skey->y); log_mpidump("elg sign x= ", skey->x); log_mpidump("elg sign k= ", k); log_mpidump("elg sign M= ", input); log_mpidump("elg sign a= ", a); log_mpidump("elg sign b= ", b); } #endif mpi_free(k); mpi_free(t); mpi_free(inv); mpi_free(p_1); } /**************** * Returns true if the signature composed of A and B is valid. */ static int verify(MPI a, MPI b, MPI input, ELG_public_key *pkey ) { int rc; MPI t1; MPI t2; MPI base[4]; MPI exp[4]; if( !(mpi_cmp_ui( a, 0 ) > 0 && mpi_cmp( a, pkey->p ) < 0) ) return 0; /* assertion 0 < a < p failed */ t1 = mpi_alloc( mpi_get_nlimbs(a) ); t2 = mpi_alloc( mpi_get_nlimbs(a) ); #if 0 /* t1 = (y^a mod p) * (a^b mod p) mod p */ mpi_powm( t1, pkey->y, a, pkey->p ); mpi_powm( t2, a, b, pkey->p ); mpi_mulm( t1, t1, t2, pkey->p ); /* t2 = g ^ input mod p */ mpi_powm( t2, pkey->g, input, pkey->p ); rc = !mpi_cmp( t1, t2 ); #elif 0 /* t1 = (y^a mod p) * (a^b mod p) mod p */ base[0] = pkey->y; exp[0] = a; base[1] = a; exp[1] = b; base[2] = NULL; exp[2] = NULL; mpi_mulpowm( t1, base, exp, pkey->p ); /* t2 = g ^ input mod p */ mpi_powm( t2, pkey->g, input, pkey->p ); rc = !mpi_cmp( t1, t2 ); #else /* t1 = g ^ - input * y ^ a * a ^ b mod p */ mpi_invm(t2, pkey->g, pkey->p ); base[0] = t2 ; exp[0] = input; base[1] = pkey->y; exp[1] = a; base[2] = a; exp[2] = b; base[3] = NULL; exp[3] = NULL; mpi_mulpowm( t1, base, exp, pkey->p ); rc = !mpi_cmp_ui( t1, 1 ); #endif mpi_free(t1); mpi_free(t2); return rc; } /********************************************* ************** interface ****************** *********************************************/ int elg_generate( int algo, unsigned nbits, MPI *skey, MPI **retfactors ) { ELG_secret_key sk; if( !is_ELGAMAL(algo) ) return G10ERR_PUBKEY_ALGO; generate( &sk, nbits, retfactors ); skey[0] = sk.p; skey[1] = sk.g; skey[2] = sk.y; skey[3] = sk.x; return 0; } int elg_check_secret_key( int algo, MPI *skey ) { ELG_secret_key sk; if( !is_ELGAMAL(algo) ) return G10ERR_PUBKEY_ALGO; sk.p = skey[0]; sk.g = skey[1]; sk.y = skey[2]; sk.x = skey[3]; if( !check_secret_key( &sk ) ) return G10ERR_BAD_SECKEY; return 0; } int elg_encrypt( int algo, MPI *resarr, MPI data, MPI *pkey ) { ELG_public_key pk; if( !is_ELGAMAL(algo) ) return G10ERR_PUBKEY_ALGO; pk.p = pkey[0]; pk.g = pkey[1]; pk.y = pkey[2]; resarr[0] = mpi_alloc( mpi_get_nlimbs( pk.p ) ); resarr[1] = mpi_alloc( mpi_get_nlimbs( pk.p ) ); encrypt( resarr[0], resarr[1], data, &pk ); return 0; } int elg_decrypt( int algo, MPI *result, MPI *data, MPI *skey ) { ELG_secret_key sk; if( !is_ELGAMAL(algo) ) return G10ERR_PUBKEY_ALGO; sk.p = skey[0]; sk.g = skey[1]; sk.y = skey[2]; sk.x = skey[3]; *result = mpi_alloc_secure( mpi_get_nlimbs( sk.p ) ); decrypt( *result, data[0], data[1], &sk ); return 0; } int elg_sign( int algo, MPI *resarr, MPI data, MPI *skey ) { ELG_secret_key sk; if( !is_ELGAMAL(algo) ) return G10ERR_PUBKEY_ALGO; sk.p = skey[0]; sk.g = skey[1]; sk.y = skey[2]; sk.x = skey[3]; resarr[0] = mpi_alloc( mpi_get_nlimbs( sk.p ) ); resarr[1] = mpi_alloc( mpi_get_nlimbs( sk.p ) ); sign( resarr[0], resarr[1], data, &sk ); return 0; } int elg_verify( int algo, MPI hash, MPI *data, MPI *pkey, int (*cmp)(void *, MPI), void *opaquev ) { ELG_public_key pk; if( !is_ELGAMAL(algo) ) return G10ERR_PUBKEY_ALGO; pk.p = pkey[0]; pk.g = pkey[1]; pk.y = pkey[2]; if( !verify( data[0], data[1], hash, &pk ) ) return G10ERR_BAD_SIGN; return 0; } unsigned elg_get_nbits( int algo, MPI *pkey ) { if( !is_ELGAMAL(algo) ) return 0; return mpi_get_nbits( pkey[0] ); } /**************** * Return some information about the algorithm. We need algo here to * distinguish different flavors of the algorithm. * Returns: A pointer to string describing the algorithm or NULL if * the ALGO is invalid. * Usage: Bit 0 set : allows signing * 1 set : allows encryption * NOTE: This function allows signing also for ELG-E, which is not * okay but a bad hack to allow to work with old gpg keys. The real check * is done in the gnupg ocde depending on the packet version. */ const char * elg_get_info( int algo, int *npkey, int *nskey, int *nenc, int *nsig, int *use ) { *npkey = 3; *nskey = 4; *nenc = 2; *nsig = 2; switch( algo ) { case PUBKEY_ALGO_ELGAMAL: *use = PUBKEY_USAGE_SIG|PUBKEY_USAGE_ENC; return "ELG"; case PUBKEY_ALGO_ELGAMAL_E: *use = PUBKEY_USAGE_SIG|PUBKEY_USAGE_ENC; return "ELG-E"; default: *use = 0; return NULL; } } diff --git a/cipher/primegen.c b/cipher/primegen.c index 9019e2839..9bf108531 100644 --- a/cipher/primegen.c +++ b/cipher/primegen.c @@ -1,555 +1,562 @@ /* primegen.c - prime number generator * Copyright (C) 1998 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 * * *********************************************************************** * The algorithm used to generate practically save primes is due to * Lim and Lee as described in the CRYPTO '97 proceedings (ISBN3540633847) * page 260. */ #include #include #include #include #include #include "util.h" #include "mpi.h" #include "cipher.h" static int no_of_small_prime_numbers; static MPI gen_prime( unsigned nbits, int mode, int randomlevel ); static int check_prime( MPI prime, MPI val_2 ); static int is_prime( MPI n, int steps, int *count ); static void m_out_of_n( char *array, int m, int n ); +static void +progress( int c ) +{ + fputc( c, stderr ); +} + + /**************** * Generate a prime number (stored in secure memory) */ MPI generate_secret_prime( unsigned nbits ) { MPI prime; prime = gen_prime( nbits, 1, 2 ); - fputc('\n', stderr); + progress('\n'); return prime; } MPI generate_public_prime( unsigned nbits ) { MPI prime; prime = gen_prime( nbits, 0, 2 ); - fputc('\n', stderr); + progress('\n'); return prime; } /**************** * We do not need to use the strongest RNG because we gain no extra * security from it - The prime number is public and we could also * offer the factors for those who are willing to check that it is * indeed a strong prime. * * mode 0: Standard * 1: Make sure that at least one factor is of size qbits. */ MPI generate_elg_prime( int mode, unsigned pbits, unsigned qbits, MPI g, MPI **ret_factors ) { int n; /* number of factors */ int m; /* number of primes in pool */ unsigned fbits; /* length of prime factors */ MPI *factors; /* current factors */ MPI *pool; /* pool of primes */ MPI q; /* first prime factor (variable)*/ MPI prime; /* prime test value */ MPI q_factor; /* used for mode 1 */ byte *perms = NULL; int i, j; int count1, count2; unsigned nprime; unsigned req_qbits = qbits; /* the requested q bits size */ MPI val_2 = mpi_alloc_set_ui( 2 ); /* find number of needed prime factors */ for(n=1; (pbits - qbits - 1) / n >= qbits; n++ ) ; n--; if( !n || (mode==1 && n < 2) ) log_fatal("can't gen prime with pbits=%u qbits=%u\n", pbits, qbits ); if( mode == 1 ) { n--; fbits = (pbits - 2*req_qbits -1) / n; qbits = pbits - req_qbits - n*fbits; } else { fbits = (pbits - req_qbits -1) / n; qbits = pbits - n*fbits; } if( DBG_CIPHER ) log_debug("gen prime: pbits=%u qbits=%u fbits=%u/%u n=%d\n", pbits, req_qbits, qbits, fbits, n ); prime = mpi_alloc( (pbits + BITS_PER_MPI_LIMB - 1) / BITS_PER_MPI_LIMB ); q = gen_prime( qbits, 0, 1 ); q_factor = mode==1? gen_prime( req_qbits, 0, 1 ) : NULL; /* allocate an array to hold the factors + 2 for later usage */ factors = m_alloc_clear( (n+2) * sizeof *factors ); /* make a pool of 3n+5 primes (this is an arbitrary value) */ m = n*3+5; if( mode == 1 ) m += 5; /* need some more for DSA */ if( m < 25 ) m = 25; pool = m_alloc_clear( m * sizeof *pool ); /* permutate over the pool of primes */ count1=count2=0; do { next_try: if( !perms ) { /* allocate new primes */ for(i=0; i < m; i++ ) { mpi_free(pool[i]); pool[i] = NULL; } /* init m_out_of_n() */ perms = m_alloc_clear( m ); for(i=0; i < n; i++ ) { perms[i] = 1; pool[i] = gen_prime( fbits, 0, 1 ); factors[i] = pool[i]; } } else { m_out_of_n( perms, n, m ); for(i=j=0; i < m && j < n ; i++ ) if( perms[i] ) { if( !pool[i] ) pool[i] = gen_prime( fbits, 0, 1 ); factors[j++] = pool[i]; } if( i == n ) { m_free(perms); perms = NULL; - fputc('!', stderr); + progress('!'); goto next_try; /* allocate new primes */ } } mpi_set( prime, q ); mpi_mul_ui( prime, prime, 2 ); if( mode == 1 ) mpi_mul( prime, prime, q_factor ); for(i=0; i < n; i++ ) mpi_mul( prime, prime, factors[i] ); mpi_add_ui( prime, prime, 1 ); nprime = mpi_get_nbits(prime); if( nprime < pbits ) { if( ++count1 > 20 ) { count1 = 0; qbits++; - fputc('>', stderr); + progress('>'); q = gen_prime( qbits, 0, 1 ); goto next_try; } } else count1 = 0; if( nprime > pbits ) { if( ++count2 > 20 ) { count2 = 0; qbits--; - fputc('<', stderr); + progress('<'); q = gen_prime( qbits, 0, 1 ); goto next_try; } } else count2 = 0; } while( !(nprime == pbits && check_prime( prime, val_2 )) ); if( DBG_CIPHER ) { - putc('\n', stderr); + progress('\n'); log_mpidump( "prime : ", prime ); log_mpidump( "factor q: ", q ); if( mode == 1 ) log_mpidump( "factor q0: ", q_factor ); for(i=0; i < n; i++ ) log_mpidump( "factor pi: ", factors[i] ); log_debug("bit sizes: prime=%u, q=%u", mpi_get_nbits(prime), mpi_get_nbits(q) ); if( mode == 1 ) fprintf(stderr, ", q0=%u", mpi_get_nbits(q_factor) ); for(i=0; i < n; i++ ) fprintf(stderr, ", p%d=%u", i, mpi_get_nbits(factors[i]) ); - putc('\n', stderr); + progress('\n'); } if( ret_factors ) { /* caller wants the factors */ *ret_factors = m_alloc_clear( (n+2) * sizeof **ret_factors); if( mode == 1 ) { i = 0; (*ret_factors)[i++] = mpi_copy( q_factor ); for(; i <= n; i++ ) (*ret_factors)[i] = mpi_copy( factors[i] ); } else { for(; i < n; i++ ) (*ret_factors)[i] = mpi_copy( factors[i] ); } } if( g ) { /* create a generator (start with 3)*/ MPI tmp = mpi_alloc( mpi_get_nlimbs(prime) ); MPI b = mpi_alloc( mpi_get_nlimbs(prime) ); MPI pmin1 = mpi_alloc( mpi_get_nlimbs(prime) ); if( mode == 1 ) BUG(); /* not yet implemented */ factors[n] = q; factors[n+1] = mpi_alloc_set_ui(2); mpi_sub_ui( pmin1, prime, 1 ); mpi_set_ui(g,2); do { mpi_add_ui(g, g, 1); if( DBG_CIPHER ) { log_debug("checking g: "); mpi_print( stderr, g, 1 ); } else - fputc('^', stderr); + progress('^'); for(i=0; i < n+2; i++ ) { /*fputc('~', stderr);*/ mpi_fdiv_q(tmp, pmin1, factors[i] ); /* (no mpi_pow(), but it is okay to use this with mod prime) */ mpi_powm(b, g, tmp, prime ); if( !mpi_cmp_ui(b, 1) ) break; } if( DBG_CIPHER ) - fputc('\n', stderr); + progress('\n'); } while( i < n+2 ); mpi_free(factors[n+1]); mpi_free(tmp); mpi_free(b); mpi_free(pmin1); } if( !DBG_CIPHER ) - putc('\n', stderr); + progress('\n'); m_free( factors ); /* (factors are shallow copies) */ for(i=0; i < m; i++ ) mpi_free( pool[i] ); m_free( pool ); m_free(perms); mpi_free(val_2); return prime; } static MPI gen_prime( unsigned nbits, int secret, int randomlevel ) { unsigned nlimbs; MPI prime, ptest, pminus1, val_2, val_3, result; int i; unsigned x, step; unsigned count1, count2; int *mods; if( 0 && DBG_CIPHER ) log_debug("generate a prime of %u bits ", nbits ); if( !no_of_small_prime_numbers ) { for(i=0; small_prime_numbers[i]; i++ ) no_of_small_prime_numbers++; } mods = m_alloc( no_of_small_prime_numbers * sizeof *mods ); /* make nbits fit into MPI implementation */ nlimbs = (nbits + BITS_PER_MPI_LIMB - 1) / BITS_PER_MPI_LIMB; val_2 = mpi_alloc_set_ui( 2 ); val_3 = mpi_alloc_set_ui( 3); prime = secret? mpi_alloc_secure( nlimbs ): mpi_alloc( nlimbs ); result = mpi_alloc_like( prime ); pminus1= mpi_alloc_like( prime ); ptest = mpi_alloc_like( prime ); count1 = count2 = 0; for(;;) { /* try forvever */ int dotcount=0; /* generate a random number */ { char *p = get_random_bits( nbits, randomlevel, secret ); mpi_set_buffer( prime, p, (nbits+7)/8, 0 ); m_free(p); } /* set high order bit to 1, set low order bit to 1 */ mpi_set_highbit( prime, nbits-1 ); mpi_set_bit( prime, 0 ); /* calculate all remainders */ for(i=0; (x = small_prime_numbers[i]); i++ ) mods[i] = mpi_fdiv_r_ui(NULL, prime, x); /* now try some primes starting with prime */ for(step=0; step < 20000; step += 2 ) { /* check against all the small primes we have in mods */ count1++; for(i=0; (x = small_prime_numbers[i]); i++ ) { while( mods[i] + step >= x ) mods[i] -= x; if( !(mods[i] + step) ) break; } if( x ) continue; /* found a multiple of an already known prime */ mpi_add_ui( ptest, prime, step ); /* do a faster Fermat test */ count2++; mpi_sub_ui( pminus1, ptest, 1); mpi_powm( result, val_2, pminus1, ptest ); if( !mpi_cmp_ui( result, 1 ) ) { /* not composite */ /* perform stronger tests */ if( is_prime(ptest, 5, &count2 ) ) { if( !mpi_test_bit( ptest, nbits-1 ) ) { - fputc('\n', stderr); + progress('\n'); log_debug("overflow in prime generation\n"); break; /* step loop, continue with a new prime */ } mpi_free(val_2); mpi_free(val_3); mpi_free(result); mpi_free(pminus1); mpi_free(prime); m_free(mods); return ptest; } } if( ++dotcount == 10 ) { - fputc('.', stderr); + progress('.'); dotcount = 0; } } - fputc(':', stderr); /* restart with a new random value */ + progress(':'); /* restart with a new random value */ } } /**************** * Returns: true if this may be a prime */ static int check_prime( MPI prime, MPI val_2 ) { int i; unsigned x; int count=0; /* check against small primes */ for(i=0; (x = small_prime_numbers[i]); i++ ) { if( mpi_divisible_ui( prime, x ) ) return 0; } /* a quick fermat test */ { MPI result = mpi_alloc_like( prime ); MPI pminus1 = mpi_alloc_like( prime ); mpi_sub_ui( pminus1, prime, 1); mpi_powm( result, val_2, pminus1, prime ); mpi_free( pminus1 ); if( mpi_cmp_ui( result, 1 ) ) { /* if composite */ mpi_free( result ); - fputc('.', stderr); + progress('.'); return 0; } mpi_free( result ); } /* perform stronger tests */ if( is_prime(prime, 5, &count ) ) return 1; /* is probably a prime */ - fputc('.', stderr); + progress('.'); return 0; } /**************** * Return true if n is probably a prime */ static int is_prime( MPI n, int steps, int *count ) { MPI x = mpi_alloc( mpi_get_nlimbs( n ) ); MPI y = mpi_alloc( mpi_get_nlimbs( n ) ); MPI z = mpi_alloc( mpi_get_nlimbs( n ) ); MPI nminus1 = mpi_alloc( mpi_get_nlimbs( n ) ); MPI a2 = mpi_alloc_set_ui( 2 ); MPI q; unsigned i, j, k; int rc = 0; unsigned nbits = mpi_get_nbits( n ); mpi_sub_ui( nminus1, n, 1 ); /* find q and k, so that n = 1 + 2^k * q */ q = mpi_copy( nminus1 ); k = mpi_trailing_zeros( q ); mpi_tdiv_q_2exp(q, q, k); for(i=0 ; i < steps; i++ ) { ++*count; if( !i ) { mpi_set_ui( x, 2 ); } else { /*mpi_set_bytes( x, nbits-1, get_random_byte, 0 );*/ { char *p = get_random_bits( nbits, 0, 0 ); mpi_set_buffer( x, p, (nbits+7)/8, 0 ); m_free(p); } /* make sure that the number is smaller than the prime * and keep the randomness of the high bit */ if( mpi_test_bit( x, nbits-2 ) ) { mpi_set_highbit( x, nbits-2 ); /* clear all higher bits */ } else { mpi_set_highbit( x, nbits-2 ); mpi_clear_bit( x, nbits-2 ); } assert( mpi_cmp( x, nminus1 ) < 0 && mpi_cmp_ui( x, 1 ) > 0 ); } mpi_powm( y, x, q, n); if( mpi_cmp_ui(y, 1) && mpi_cmp( y, nminus1 ) ) { for( j=1; j < k && mpi_cmp( y, nminus1 ); j++ ) { mpi_powm(y, y, a2, n); if( !mpi_cmp_ui( y, 1 ) ) goto leave; /* not a prime */ } if( mpi_cmp( y, nminus1 ) ) goto leave; /* not a prime */ } - fputc('+', stderr); + progress('+'); } rc = 1; /* may be a prime */ leave: mpi_free( x ); mpi_free( y ); mpi_free( z ); mpi_free( nminus1 ); mpi_free( q ); return rc; } static void m_out_of_n( char *array, int m, int n ) { int i=0, i1=0, j=0, jp=0, j1=0, k1=0, k2=0; if( !m || m >= n ) return; if( m == 1 ) { /* special case */ for(i=0; i < n; i++ ) if( array[i] ) { array[i++] = 0; if( i >= n ) i = 0; array[i] = 1; return; } BUG(); } for(j=1; j < n; j++ ) { if( array[n-1] == array[n-j-1] ) continue; j1 = j; break; } if( m & 1 ) { /* m is odd */ if( array[n-1] ) { if( j1 & 1 ) { k1 = n - j1; k2 = k1+2; if( k2 > n ) k2 = n; goto leave; } goto scan; } k2 = n - j1 - 1; if( k2 == 0 ) { k1 = i; k2 = n - j1; } else if( array[k2] && array[k2-1] ) k1 = n; else k1 = k2 + 1; } else { /* m is even */ if( !array[n-1] ) { k1 = n - j1; k2 = k1 + 1; goto leave; } if( !(j1 & 1) ) { k1 = n - j1; k2 = k1+2; if( k2 > n ) k2 = n; goto leave; } scan: jp = n - j1 - 1; for(i=1; i <= jp; i++ ) { i1 = jp + 2 - i; if( array[i1-1] ) { if( array[i1-2] ) { k1 = i1 - 1; k2 = n - j1; } else { k1 = i1 - 1; k2 = n + 1 - j1; } goto leave; } } k1 = 1; k2 = n + 1 - m; } leave: array[k1-1] = !array[k1-1]; array[k2-1] = !array[k2-1]; } diff --git a/doc/DETAILS b/doc/DETAILS index 84b42833b..6ff7cb656 100644 --- a/doc/DETAILS +++ b/doc/DETAILS @@ -1,564 +1,570 @@ Format of "---with-colons" listings =================================== sec::1024:17:6C7EE1B8621CC013:1998-07-07:0:::Werner Koch : ssb::1536:20:5CE086B5B5A18FF4:1998-07-07:0::: 1. Field: Type of record pub = public key sub = subkey (secondary key) sec = secret key ssb = secret subkey (secondary key) uid = user id (only field 10 is used). fpr = fingerprint: (fingerprint is in field 10) pkd = public key data (special field format, see below) 2. Field: A letter describing the calculated trust, see doc/FAQ This is a single letter, but be prepared that additional information may follow in some future versions. (not used for secret keys) 3. Field: length of key in bits. 4. Field: Algorithm: 1 = RSA 16 = ElGamal (encrypt only) 17 = DSA (sometimes called DH, sign only) 20 = ElGamal (sign and encrypt) 5. Field: KeyID 6. Field: Creation Date (in UTC) 7. Field: Key expiration date or empty if none. 8. Field: Local ID: record number of the dir record in the trustdb this value is only valid as long as the trustdb is not deleted. May be later used to lookup the key: You will be able to use "# as the user id. This is needed because keyids may not be unique - a program may use this number to access keys later. 9. Field: Ownertrust (primary public keys only) This is a single letter, but be prepared that additional information may follow in some future versions. 10. Field: User-ID. The value is quoted like a C string to avoid control characters (the colon is quoted "\x3a"). More fields may be added later. If field 1 has the tag "pkd", a listing looks like this: pkd:0:1024:B665B1435F4C2 .... FF26ABB: ! ! !-- the value ! !------ for infomation number of bits in the value !--------- index (eg. DSA goes from 0 to 3: p,q,g,y) Format of the "--status-fd" output ================================== Every line is prefixed with "[GNUPG:] ", followed by a keyword with the type of the status line and a some arguments depending on the type (maybe none); an application should always be prepared to see more arguments in future versions. GOODSIG The signature with the keyid is good. BADSIG The signature with the keyid has not been verified okay. ERRSIG \ It was not possible to check the signature. This may be caused by a missing public key or an unsupported algorithm. A RC of 4 indicates unknown algorithm, a 9 indicates a missing public key. The other fields give more information about this signature. sig_class is a 2 byte hex-value. VALIDSIG The signature with the keyid is good. This is the same as GOODSIG but has the fingerprint as the argument. Both status lines ere emitted for a good signature. sig-timestamp is the signature creation time in seconds after the epoch. SIG_ID This is emitted only for signatures of class 0 or 1 which have been verified okay. The string is a signature id and may be used in applications to detect replay attacks of signed messages. Note that only DLP algorithms give unique ids - others may yield duplicated ones when they have been created in the same second. - ENC_TO + ENC_TO The message is encrypted to this keyid. + keytype is the numerical value of the public key algorithm, + kenlength is the length of the key or 0 if it is not known + (which is currently always the case). NODATA No data has been found. Codes for what are: 1 - No armored data. 2 - Expected a packet but did not found one. 3 - Invalid packet found, this may indicate a non OpenPGP message. You may see more than one of these status lines. TRUST_UNDEFINED TRUST_NEVER TRUST_MARGINAL TRUST_FULLY TRUST_ULTIMATE For good signatures one of these status lines are emitted to indicate how trustworthy the signature is. No arguments yet. SIGEXPIRED The signature key has expired. No arguments yet. KEYREVOKED The used key has been revoked by his owner. No arguments yet. BADARMOR The ASCII armor is corrupted. No arguments yet. RSA_OR_IDEA The RSA or IDEA algorithms has been used in the data. A program might want to fallback to another program to handle the data if GnuPG failed. SHM_INFO SHM_GET SHM_GET_BOOL SHM_GET_HIDDEN - NEED_PASSPHRASE + NEED_PASSPHRASE Issued whenever a passphrase is needed. + keytype is the numerical value of the public key algorithm + or 0 if this is not applicable, keylength is the length + of the key or 0 if it is not known (this is currently always the case). NEED_PASSPHRASE_SYM Issued whenever a passphrase for symmetric encryption is needed. MISSING_PASSPHRASE BAD_PASSPHRASE The supplied passphrase was wrong GOOD_PASSPHRASE The supplied passphrase was good and the secret key material is therefore usuable. DECRYPTION_FAILED The symmetric decryption failed - one reason could be a wrong passphrase for a symmetrical encrypted message. DECRYPTION_OKAY The decryption process succeeded. This means, that either the correct secret key has been used or the correct passphrase for a conventional encrypted message was given. The program itself may return an errorcode becuase it may not be possible to verify a signature for some reasons. NO_PUBKEY NO_SECKEY The key is not available Key generation ============== Key generation shows progress by printing different characters to stderr: "." Last 10 Miller-Rabin tests failed "+" Miller-Rabin test succeeded "!" Reloading the pool with fresh prime numbers "^" Checking a new value for the generator "<" Size of one factor decreased ">" Size of one factor increased The prime number for ElGamal is generated this way: 1) Make a prime number q of 160, 200, 240 bits (depending on the keysize) 2) Select the length of the other prime factors to be at least the size of q and calculate the number of prime factors needed 3) Make a pool of prime numbers, each of the length determined in step 2 4) Get a new permutation out of the pool or continue with step 3 if we have tested all permutations. 5) Calculate a candidate prime p = 2 * q * p[1] * ... * p[n] + 1 6) Check that this prime has the correct length (this may change q if it seems not to be possible to make a prime of the desired length) 7) Check whether this is a prime using trial divisions and the Miller-Rabin test. 8) Continue with step 4 if we did not find a prime in step 7. 9) Find a generator for that prime. This algorithm is based on Lim and Lee's suggestion from the Crypto '97 proceedings p. 260. Layout of the TrustDB ===================== The TrustDB is built from fixed length records, where the first byte describes the record type. All numeric values are stored in network byte order. The length of each record is 40 bytes. The first record of the DB is always of type 2 and this is the only record of this type. Record type 0: -------------- Unused record, can be reused for any purpose. Record type 1: -------------- Version information for this TrustDB. This is always the first record of the DB and the only one with type 1. 1 byte value 1 3 bytes 'gpg' magic value 1 byte Version of the TrustDB (2) 1 byte marginals needed 1 byte completes needed 1 byte max_cert_depth The three items are used to check whether the cached validity value from the dir record can be used. 1 u32 locked flags 1 u32 timestamp of trustdb creation 1 u32 timestamp of last modification which may affect the validity of keys in the trustdb. This value is checked against the validity timestamp in the dir records. 1 u32 timestamp of last validation (Used to keep track of the time, when this TrustDB was checked against the pubring) 1 u32 record number of keyhashtable 1 u32 first free record 1 u32 record number of shadow directory hash table It does not make sense to combine this table with the key table because the keyid is not in every case a part of the fingerprint. 4 bytes reserved for version extension record Record type 2: (directory record) -------------- Informations about a public key certificate. These are static values which are never changed without user interaction. 1 byte value 2 1 byte reserved 1 u32 LID . (This is simply the record number of this record.) 1 u32 List of key-records (the first one is the primary key) 1 u32 List of uid-records 1 u32 cache record 1 byte ownertrust 1 byte dirflag 1 byte maximum validity of all the user ids 1 u32 time of last validity check. 1 u32 Must check when this time has been reached. (0 = no check required) Record type 3: (key record) -------------- Informations about a primary public key. (This is mainly used to lookup a trust record) 1 byte value 3 1 byte reserved 1 u32 LID 1 u32 next - next key record 7 bytes reserved 1 byte keyflags 1 byte pubkey algorithm 1 byte length of the fingerprint (in bytes) 20 bytes fingerprint of the public key (This is the value we use to identify a key) Record type 4: (uid record) -------------- Informations about a userid We do not store the userid but the hash value of the userid because that is sufficient. 1 byte value 4 1 byte reserved 1 u32 LID points to the directory record. 1 u32 next next userid 1 u32 pointer to preference record 1 u32 siglist list of valid signatures 1 byte uidflags 1 byte validity of the key calculated over this user id 20 bytes ripemd160 hash of the username. Record type 5: (pref record) -------------- Informations about preferences 1 byte value 5 1 byte reserved 1 u32 LID; points to the directory record (and not to the uid record!). (or 0 for standard preference record) 1 u32 next 30 byte preference data Record type 6 (sigrec) ------------- Used to keep track of key signatures. Self-signatures are not stored. If a public key is not in the DB, the signature points to a shadow dir record, which in turn has a list of records which might be interested in this key (and the signature record here is one). 1 byte value 6 1 byte reserved 1 u32 LID points back to the dir record 1 u32 next next sigrec of this uid or 0 to indicate the last sigrec. 6 times 1 u32 Local_id of signators dir or shadow dir record 1 byte Flag: Bit 0 = checked: Bit 1 is valid (we have a real directory record for this) 1 = valid is set (but my be revoked) Record type 8: (shadow directory record) -------------- This record is used to reserved a LID for a public key. We need this to create the sig records of other keys, even if we do not yet have the public key of the signature. This record (the record number to be more precise) will be reused as the dir record when we import the real public key. 1 byte value 8 1 byte reserved 1 u32 LID (This is simply the record number of this record.) 2 u32 keyid 1 byte pubkey algorithm 3 byte reserved 1 u32 hintlist A list of records which have references to this key. This is used for fast access to signature records which are not yet checked. Note, that this is only a hint and the actual records may not anymore hold signature records for that key but that the code cares about this. 18 byte reserved Record Type 10 (hash table) -------------- Due to the fact that we use fingerprints to lookup keys, we can implement quick access by some simple hash methods, and avoid the overhead of gdbm. A property of fingerprints is that they can be used directly as hash values. (They can be considered as strong random numbers.) What we use is a dynamic multilevel architecture, which combines hashtables, record lists, and linked lists. This record is a hashtable of 256 entries; a special property is that all these records are stored consecutively to make one big table. The hash value is simple the 1st, 2nd, ... byte of the fingerprint (depending on the indirection level). When used to hash shadow directory records, a different table is used and indexed by the keyid. 1 byte value 10 1 byte reserved n u32 recnum; n depends on the record length: n = (reclen-2)/4 which yields 9 for the current record length of 40 bytes. the total number of such record which makes up the table is: m = (256+n-1) / n which is 29 for a record length of 40. To look up a key we use the first byte of the fingerprint to get the recnum from this hashtable and look up the addressed record: - If this record is another hashtable, we use 2nd byte to index this hash table and so on. - if this record is a hashlist, we walk all entries until we found one a matching one. - if this record is a key record, we compare the fingerprint and to decide whether it is the requested key; Record type 11 (hash list) -------------- see hash table for an explanation. This is also used for other purposes. 1 byte value 11 1 byte reserved 1 u32 next next hash list record n times n = (reclen-5)/5 1 u32 recnum For the current record length of 40, n is 7 Record type 254 (free record) --------------- All these records form a linked list of unused records. 1 byte value 254 1 byte reserved (0) 1 u32 next_free Packet Headers =============== GNUPG uses PGP 2 packet headers and also understands OpenPGP packet header. There is one enhancement used with the old style packet headers: CTB bits 10, the "packet-length length bits", have values listed in the following table: 00 - 1-byte packet-length field 01 - 2-byte packet-length field 10 - 4-byte packet-length field 11 - no packet length supplied, unknown packet length As indicated in this table, depending on the packet-length length bits, the remaining 1, 2, 4, or 0 bytes of the packet structure field are a "packet-length field". The packet-length field is a whole number field. The value of the packet-length field is defined to be the value of the whole number field. A value of 11 is currently used in one place: on compressed data. That is, a compressed data block currently looks like , where , binary 10 1000 11, is an indefinite-length packet. The proper interpretation is "until the end of the enclosing structure", although it should never appear outermost (where the enclosing structure is a file). + This will be changed with another version, where the new meaning of + the value 11 (see below) will also take place. + + A value of 11 for other packets enables a special length encoding, + which is used in case, where the length of the following packet can + not be determined prior to writing the packet; especially this will + be used if large amounts of data are processed in filter mode. + + It works like this: After the CTB (with a length field of 11) a + marker field is used, which gives the length of the following datablock. + This is a simple 2 byte field (MSB first) containing the amount of data + following this field, not including this length field. After this datablock + another length field follows, which gives the size of the next datablock. + A value of 0 indicates the end of the packet. The maximum size of a + data block is limited to 65534, thereby reserving a value of 0xffff for + future extensions. These length markers must be inserted into the data + stream just before writing the data out. + + This 2 byte filed is large enough, because the application must buffer + this amount of data to prepend the length marker before writing it out. + Data block sizes larger than about 32k doesn't make any sense. Note + that this may also be used for compressed data streams, but we must use + another packet version to tell the application that it can not assume, + that this is the last packet. Usage of gdbm files for keyrings ================================ The key to store the keyblock is it's fingerprint, other records are used for secondary keys. fingerprints are always 20 bytes where 16 bit fingerprints are appended with zero. The first byte of the key gives some information on the type of the key. 1 = key is a 20 bit fingerprint (16 bytes fpr are padded with zeroes) data is the keyblock 2 = key is the complete 8 byte keyid data is a list of 20 byte fingerprints 3 = key is the short 4 byte keyid data is a list of 20 byte fingerprints 4 = key is the email address data is a list of 20 byte fingerprints Data is prepended with a type byte: 1 = keyblock 2 = list of 20 byte padded fingerprints 3 = list of list fingerprints (but how to we key them?) Other Notes =========== * For packet version 3 we calculate the keyids this way: RSA := low 64 bits of n ELGAMAL := build a v3 pubkey packet (with CTB 0x99) and calculate a rmd160 hash value from it. This is used as the fingerprint and the low 64 bits are the keyid. * Revocation certificates consist only of the signature packet; "import" knows how to handle this. The rationale behind it is to keep them small. Keyserver Message Format ========================= The keyserver may be contacted by a Unix Domain socket or via TCP. The format of a request is: ==== command-tag "Content-length:" digits CRLF ======= Where command-tag is NOOP GET PUT DELETE The format of a response is: ====== "GNUPG/1.0" status-code status-text "Content-length:" digits CRLF ============ followed by bytes of data Status codes are: o 1xx: Informational - Request received, continuing process o 2xx: Success - The action was successfully received, understood, and accepted o 4xx: Client Error - The request contains bad syntax or cannot be fulfilled o 5xx: Server Error - The server failed to fulfill an apparently valid request Ich werde jetzt doch das HKP Protokoll implementieren: Naja, die Doku ist so gut wie nichtexistent, da gebe ich Dir recht. In kurzen Worten: (Minimal-)HTTP-Server auf Port 11371, versteht ein GET auf /pks/lookup, wobei die Query-Parameter (Key-Value-Paare mit = zwischen Key und Value; die Paare sind hinter ? und durch & getrennt). Gültige Operationen sind: - - op (Operation) mit den Möglichkeiten index (gleich wie -kv bei PGP), vindex (-kvv) und get (-kxa) - - search: Liste der Worte, die im Key vorkommen müssen. Worte sind mit Worttrennzeichen wie Space, Punkt, @, ... getrennt, Worttrennzeichen werden nicht betrachtet, die Reihenfolge der Worte ist egal. - - exact: (on=aktiv, alles andere inaktiv) Nur die Schlüssel zurückgeben, die auch den "search"-String beinhalten (d.h. Wortreihenfolge und Sonderzeichen sind wichtig) - - fingerprint (Bei [v]index auch den Fingerprint ausgeben), "on" für aktiv, alles andere inaktiv Neu (wird von GNUPG benutzt): /pks/lookup/?op= Zusätzlich versteht der Keyserver auch ein POST auf /pks/add, womit man Keys hochladen kann. diff --git a/doc/gpg.sgml b/doc/gpg.sgml index 82e069b56..21d74fc83 100644 --- a/doc/gpg.sgml +++ b/doc/gpg.sgml @@ -1,1214 +1,1233 @@ directory"> file"> &ParmFile;"> files"> &ParmFiles;"> names"> &ParmNames;"> name"> &ParmName;"> key IDs"> n"> flags"> string"> value"> name=value"> ]> gpg 1 GNU Tools encryption and signing tool gpg --homedir --options command DESCRIPTION COMMANDS -s, --sign Make a signature. This command may be combined with --encrypt. --clearsign Make a clear text signature. -b, --detach-sign Make a detached signature. -e, --encrypt Encrypt data. This option may be combined with --sign. -c, --symmetric Encrypt with symmetric cipher only This command asks for a passphrase. --store Store only (make a simple RFC1991 packet). --decrypt &OptParmFile; Decrypt &ParmFile; (or stdin if no file is specified) and write it to stdout (or the file specified with --output). If the decrypted file is signed, the signature is also verified. This command differs from the default operation, as it never writes to the filename which is included in the file and it rejects files which don't begin with an encrypted message. --verify Assume that --list-keys &OptParmNames; --list-public-keys &OptParmNames; List all keys from the public keyrings, or just the ones given on the command line. --list-secret-keys &OptParmNames; List all keys from the secret keyrings, or just the ones given on the command line. --list-sigs &OptParmNames; Same as --list-keys, but the signatures are listed too. --check-sigs &OptParmNames; Same as --list-sigs, but the signatures are verified. --fingerprint &OptParmNames; List all keys with their fingerprints. This is the same output as --list-keys but with the additional output of a line with the fingerprint. May also be combined with --list-sigs or --check-sigs. If this command is given twice, the fingerprints of all secondary keys are listed too. --list-packets List only the sequence of packets. This is mainly useful for debugging. --gen-key Generate a new key pair. This command can only be used interactive. --edit-key &ParmName; Present a menu which enables you to do all key related tasks: sign Make a signature on key of user &ParmName; If the key is not yet signed by the default user (or the users given with -u), the program displays the information of the key again, together with its fingerprint and asks whether it should be signed. This question is repeated for all users specified with -u. lsign Same as --sign but the signature is marked as non-exportbale and will therefore never be used by others. This may be used to make keys valid only in the local environment. revsig Revoke a signature. GnuPG asks for every every signature which has been done by one of the secret keys, whether a revocation certificate should be generated. trust Change the owner trust value. This updates the trust-db immediately and no save is required. + + disable + enable + +Disable or enable an entire key. A disabled key can normally not be used +for encryption. adduid Create an alternate user id. deluid Delete an user id. addkey Add a subkey to this key. delkey Remove a subkey. revkey Revoke a subkey. expire Change the key expiration time. If a key is selected, the time of this key will be changed. With no selection the key expiration of the primary key is changed. passwd Change the passphrase of the secret key. uid &ParmN; Toggle selection of user id with index &ParmN;. Use 0 to deselect all. key &ParmN; Toggle selection of subkey with index &ParmN;. Use 0 to deselect all. check Check all selected user ids. pref List preferences. toggle Toggle between public and secret key listing. save Save all changes to the key rings and quit. quit Quit the program without updating the key rings. The listing shows you the key with its secondary keys and all user ids. Selected keys or user ids are indicated by an asterisk. The trust value is displayed with the primary key: the first is the assigned owner trust and the second is the calculated trust value. Letters are used for the values: -No ownertrust assigned / not yet calculated. eTrust calculation has failed. qNot enough information for calculation. nNever trust this key. mMarginally trusted. fFully trusted. uUltimately trusted. --delete-key &ParmName; Remove key from the public keyring --delete-secret-key &ParmName; Remove key from the secret and public keyring --gen-revoke Generate a revocation certificate for the complete key. To revoke a subkey or a signature, use the --edit command. --export &OptParmNames; Either export all keys from all keyrings (default keyrings and those registered via option --keyring), or if at least one name is given, those of the given name. The new keyring is written to stdout or to the file given with option "output". Use together with --armor to mail those keys. --send-keys &OptParmNames; Same as --export but sends the keys to a keyserver. Option --keyserver must be used to give the name of this keyserver. Don't send your complete keyring to a keyserver - select only those keys which are new or changed by you. --export-all &OptParmNames; Same as --export, but does also export keys which are not compatible to OpenPGP. --export-secret-keys &OptParmNames; Same as --export, but does export the secret keys. This is normally not very useful and a security risk. --import &OptParmFiles; --fast-import &OptParmFiles; Import/merge keys. The fast version does not build the trustdb; this can be done at any time with the command --update-trustdb. --recv-keys &ParmKeyIDs; Import the keys with the given key IDs from a HKP keyserver. Option --keyserver must be used to give the name of this keyserver. --export-ownertrust List the assigned ownertrust values in ASCII format for backup purposes --import-ownertrust &OptParmFiles; Update the trustdb with the ownertrust values stored in &ParmFiles; (or stdin if not given); existing values will be overwritten. --version Print version information along with a list of supported algorithms. --warranty Print warranty information. -h, --help Print usage information. This is a really long list even it does list not all options. OPTIONS Long options can be put in an options file (default "~/.gnupg/options"). Do not write the 2 dashes, but simply the name of the option and any required arguments. Lines with a hash as the first non-white-space character are ignored. Commands may be put in this file too, but that does not make sense. -a, --armor Create ASCII armored output. -o, --output &ParmFile; Write output to &ParmFile;. -u, --local-user &ParmName; Use &ParmName as the user ID to sign. This option is silently ignored for the list commands, so that it can be used in an options file. --default-key &ParmName; Use &ParmName; as default user ID for signatures. If this is not used the default user ID is the first user ID found in the secret keyring. -r, --recipient &ParmName; Encrypt for user id &ParmName;. If this option is not specified, GnuPG asks for the user id. --encrypt-to &ParmName; Same as --recipient but this one is intended for in the options file and may be used together with an own user-id as an "encrypt-to-self". These keys are only used when there are other recipients given either by use of --recipient or by the asked user id. -No trust checking is performed for these user ids. +No trust checking is performed for these user ids and +even disabled keys can be used. --no-encrypt-to Disable the use of all --encrypt-to keys. -v, --verbose Give more information during processing. If used twice, the input data is listed in detail. -q, --quiet Try to be as quiet as possible. -z &ParmN; Set compression level to &ParmN;. A value of 0 for &ParmN; disables compression. Default is to use the default compression level of zlib (normally 6). -t, --textmode Use canonical text mode. If -t (but not --textmode) is used together with armoring and signing, this enables clearsigned messages. This kludge is needed for PGP compatibility; normally you would use --sign or --clearsign to selected the type of the signature. -n, --dry-run Don't make any changes (this is not completely implemented). -i, --interactive Prompt before overwriting any files. --batch Use batch mode. Never ask, do not allow interactive commands. --no-batch Disable batch mode. This may be of use if --batch is enabled from an options file. --yes Assume "yes" on most questions. --no Assume "no" on most questions. --keyserver &ParmName; Use &ParmName to lookup keys which are not yet in your keyring. This is only done while verifying messages with signatures. The option is also required for the command --send-keys to specify the keyserver to where the keys should be send. All keyservers synchronize with each other - so there is no need to send keys to more than one server. Using the command "host -l pgp.net | grep wwwkeys" gives you a list of keyservers. Because there is load balancing using round-robin DNS you may notice that you get different key servers. --keyring &ParmFile; Add &ParmFile to the list of keyrings. If &ParmFile begins with a tilde and a slash, these are replaced by the HOME directory. If the filename 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. It might make sense to use it together with --no-default-keyring. --secret-keyring &ParmFile; Same as --keyring but for the secret keyrings. --homedir &ParmDir; Set the name of the home directory to &ParmDir; If this option is not used it defaults to "~/.gnupg". It does not make sense to use this in a options file. This also overrides the environment variable "GNUPGHOME". --charset &ParmName; Set the name of the native character set. This is used to convert some strings to proper UTF-8 encoding. Valid values for &ParmName; are: iso-8859-1This is the default Latin 1 set. iso-8859-2The Latin 2 set. koi8-rThe usual Russian set (rfc1489). + +--utf8-strings +--no-utf8-strings + +Assume that the arguments are already given as UTF8 strings. The default +(--no-utf8-strings) +is to assume that arguments are encoded in the character set as specified +by --charset. These options effects all following arguments. Both options may +used multiple times. + + + --options &ParmFile; Read options from &ParmFile; and do not try to read them from the default options file in the homedir (see --homedir). This option is ignored if used in an options file. --no-options Shortcut for "--options /dev/null". This option is detected before an attempt to open an option file. --load-extension &ParmName; Load an extension module. If &ParmName; does not contain a slash it is searched in "/usr/local/lib/gnupg" See the manual for more information about extensions. --debug &ParmFlags; Set debugging flags. All flags are or-ed and &ParmFlags; may be given in C syntax (e.g. 0x0042). --debug-all Set all useful debugging flags. --status-fd &ParmN; Write special status strings to the file descriptor &ParmN;. See the file DETAILS in the documentation for a listing of them. --logger-fd &ParmN; Write log output to file descriptor &ParmN; and not to stderr. --no-comment Do not write comment packets. This option affects only the generation of secret keys. Output of option packets is disabled since version 0.4.2. --comment &ParmString; Use &ParmString; as comment string in clear text signatures. --default-comment Force to write the standard comment string in clear text signatures. Use this to overwrite a --comment from a config file. --no-version Omit the version string in clear text signatures. --emit-version Force to write the version string in clear text signatures. Use this to overwrite a previous --no-version from a config file. -N, --notation-data &ParmNameValue; Put the name value pair into the signature as notation data. &ParmName; must consists only of alphanumeric characters, digits or the underscore; the first character must not be a digit. &ParmValue; may be any printable string; it will encoded in UTF8, so sou should have check that your --charset is set right. If you prefix &ParmName; with an exclamation mark, the notation data will be flagged as critical (rfc2440:5.2.3.15). --set-policy-url &ParmString; Use &ParmString; as Policy URL for signatures (rfc2440:5.2.3.19). If you prefix it with an exclamation mark, the policy URL packet will be flagged as critical. --set-filename &ParmString; Use &ParmString; as the name of file which is stored in messages. --completes-needed &ParmN; Number of completely trusted users to introduce a new key signer (defaults to 1). --marginals-needed &ParmN; Number of marginally trusted users to introduce a new key signer (defaults to 3) --max-cert-depth &ParmN; Maximum depth of a certification chain (default is 5). --cipher-algo &ParmName; Use &ParmName; as cipher algorithm. Running the program with the command --version yields a list of supported algorithms. If this is not used the cipher algorithm is selected from the preferences stored with the key. --digest-algo &ParmName; Use &ParmName; as message digest algorithm. Running the program with the command --version yields a list of supported algorithms. Please note that using this option may violate the OpenPGP requirement, that a 160 bit hash is to be used for DSA. --s2k-cipher-algo &ParmName; Use &ParmName; as the cipher algorithm used to protect secret keys. The default cipher is BLOWFISH. This cipher is also used for conventional encryption if --cipher-algo is not given. --s2k-digest-algo &ParmName; Use &ParmName; as the digest algorithm used to mangle the passphrases. The default algorithm is RIPE-MD-160. This digest algorithm is also used for conventional encryption if --digest-algo is not given. --s2k-mode &ParmN; Selects how passphrases are mangled. If &ParmN; is 0 a plain passphrase (which is not recommended) will be used, a 1 (default) adds a salt to the passphrase and a 3 iterates the whole process a couple of times. Unless --rfc1991 is used, this mode is also used for conventional encryption. --compress-algo &ParmN; Use compress algorithm &ParmN;. Default is 2 which is RFC1950 compression. You may use 1 to use the old zlib version which is used by PGP. The default algorithm may give better results because the window size is not limited to 8K. If this is not used the OpenPGP behavior is used, i.e. the compression algorithm is selected from the preferences; note, that this can't be done if you do not encrypt the data. --throw-keyid Do not put the keyid into encrypted packets. This option hides the receiver of the message and is a countermeasure against traffic analysis. It may slow down the decryption process because all available secret keys are tried. --not-dash-escaped This option changes the behavior of cleartext signatures so that they can be used for patch files. You should not send such an armored file via email because all spaces and line endings are hashed too. You can not use this option for data which has 5 dashes at the beginning of a line, patch files don't have this. A special armor header line tells GnuPG about this cleartext signature option. --escape-from-lines Because some mailers change lines starting with "From " to "<From " it is good to handle such lines in a special way when creating cleartext signatures. All other PGP versions do it this way too. This option is not enabled by default because it would violate rfc2440. --passphrase-fd &ParmN; Read the passphrase from file descriptor &ParmN;. If you use 0 for &ParmN;, the passphrase will be read from stdin. This can only be used if only one passphrase is supplied. Don't use this option if you can avoid it. --rfc1991 Try to be more RFC1991 (PGP 2.x) compliant. --openpgp Reset all packet, cipher and digest options to OpenPGP behavior. Use this option to reset all previous options like --rfc1991, --force-v3-sigs, --s2k-*, --cipher-algo, --digest-algo and --compress-algo to OpenPGP compliant values. --force-v3-sigs OpenPGP states that an implementation should generate v4 signatures but PGP 5.x recognizes v4 signatures only on key material. This options forces v3 signatures for signatures on data. --force-mdc Force the use of encryption with appended manipulation code. This is always used with the newer cipher (those with a blocksize greater than 64 bit). --lock-once Lock the databases the first time a lock is requested and do not release the lock until the process terminates. --lock-multiple Release the locks every time a lock is no longer needed. Use this to override a previous --lock-once from a config file. --no-verbose Reset verbose level to 0. --no-greeting Suppress the initial copyright message but do not enter batch mode. --no-armor Assume the input data is not in ASCII armored format. --no-default-keyring Do not add the default keyrings to the list of keyrings. --skip-verify Skip the signature verification step. This may be used to make the encryption faster if the signature verification is not needed. --with-colons Print key listings delimited by colons. --with-key-data Print key listings delimited by colons and print the public key data. RETURN VALUE The program returns 0 if everything was fine, 1 if at least a signature was bad, and other error codes for fatal errors. EXAMPLES gpg -se -r sign and encrypt for user Bob gpg --clearsign &ParmFile; make a clear text signature gpg -sb &ParmFile; make a detached signature gpg --list-keys show keys gpg --fingerprint show fingerprint ENVIRONMENT HOME Used to locate the default home directory. GNUPGHOME If set directory used instead of "~/.gnupg". FILES ~/.gnupg/secring.gpg The secret keyring ~/.gnupg/secring.gpg.lock and the lock file ~/.gnupg/pubring.gpg The public keyring ~/.gnupg/pubring.gpg.lock and the lock file ~/.gnupg/trustdb.gpg The trust database ~/.gnupg/trustdb.gpg.lock and the lock file ~/.gnupg/options May contain options /usr[/local]/share/gnupg/options.skel Skeleton options file /usr[/local]/lib/gnupg/ Default location for extensions WARNINGS Use a *good* password for your user account and a *good* passphrase to protect your secret key. This passphrase is the weakest part of the whole system. Programs to do dictionary attacks on your secret keyring are very easy to write and so you should protect your "~/.gnupg/" directory very well. Keep in mind that, if this program is used over a network (telnet), it is *very* easy to spy out your passphrase! BUGS On many systems this program should be installed as setuid(root). This is necessary to lock memory pages. Locking memory pages prevents the operating system from writing memory pages to disk. If you get no warning message about insecure memory your operating system supports locking without being root. The program drops root privileges as soon as locked memory is allocated. diff --git a/g10/ChangeLog b/g10/ChangeLog index 9db45bb4a..ca68a142a 100644 --- a/g10/ChangeLog +++ b/g10/ChangeLog @@ -1,2091 +1,2116 @@ +Thu Jul 1 12:47:31 CEST 1999 Werner Koch + + + * keyedit.c (show_key_with_all_names): Print a notice for disabled keys. + (enable_disable_keys): Add functionality + * pkclist.c (edit_ownertrust): preserve disabled state. + (build_pk_list): Skip disabled keys. + * trustdb.c (upd_one_ownertrust): Ditto. + (build_cert_tree): Mask the ownertrust. + (trust_letter): Mask the value. + (do_check): Take disabled flag into account. + + * passphrase.c (passphrase_to_dek): Add a pubkey_alfo arg and changed + all callers. + + * g10.c (utf8_strings): 2 new options. + + * trustdb.c (insert_trust_record_by_pk): New, replaces the next one. + (insert_trust_record): Now takes a keyblock as arg. Changed all + callers to use the appropritae function. + + * openfile.c (ask_outfile_name): New. + * plaintext.c (handle_plaintext): Ask for filename if there is + no valid syntax. Don't use fname varbatim but filter it. + Tue Jun 29 21:44:25 CEST 1999 Werner Koch * trustdb.h (TRUST_FLAG_DISABLED): New. * status.c (USE_CAPABILITIES): Capabilities support (Remi). * tdbio.c : Added new fields to the DIR record. (tdbio_write_record): Fixed the update of the hash tables. (tdbio_delete_record): Drop the record from the hash tables. (drop_from_hashtbl): New. * status.c (cpr_get): Special online help mode. * helptext.c ("keyedit.cmd"): Removed. * keyedit.c (keyedit_menu): Use only help system. (enable_disable_key): New bit doies not yet work. Sat Jun 26 12:15:59 CEST 1999 Werner Koch * dearmor.c (enarmor_file): Fixed comment string. * tdbdump.c (export_ownertrust): Text fix. * tbio.c (tdbio_invalid): Ditto. * parse-packet.c (parse_key): Made temp buffer larger. * Makefile.am (install-data-local): Add missing backslashes Tue Jun 15 12:21:08 CEST 1999 Werner Koch * g10.c (main): Made iterated+salted the default S2K method. * Makefile.am (install-data-local): Use DESTDIR. * passphrase.c (passphrase_to_dek): Emit missing-passphrase while in batchmode. * parse-packet.c (parse_pubkeyenc): Fixed a SEGV. Mon Jun 14 21:18:54 CEST 1999 Michael Roth * g10.c: New options --openpgp, --no-tty, --emit-version, --default-comment and --lock-multiple Thu Jun 10 14:18:23 CEST 1999 Werner Koch * free-packet.c (free_encrypted): Fixed EOF case (Remi). (free_plaintext): Ditto. * helptext.c (keyedit.delsig.unknown): New (Remi). * keyedit.c (print_and_check_one_sig): Add arg print_without_key and changed all callers to make use of it (Remi): Tue Jun 8 13:36:25 CEST 1999 Werner Koch * keylist.c (print_key_data): New and called elsewhere. * g10.c: New option --with-key-data Wed Jun 2 14:17:19 CEST 1999 Werner Koch * mainproc.c (proc_tree): Yet another bad hack to cope with broken pgp2 created detached messages in textmode. Tue Jun 1 16:01:46 CEST 1999 Werner Koch * openfile.c (make_outfile_name): New. * plaintext.c (handle_plaintext): Outputfile is now the inputfile without the suffix. * g10.c: New option --use-embedded-filename Mon May 31 19:41:10 CEST 1999 Werner Koch * g10.c (main): Fix for SHM init (Michael). * compress.c, encr-data.c, mdfilter.c, plaintext.c, free-packet.c: Speed patches (Rémi). Thu May 27 09:40:55 CEST 1999 Werner Koch * status.c (cpr_get_answer_yes_no_quit): New. * keyedit.c (menu_delsig): New. (check_all_keysigs): Splitted. (print_and_check_one_sig): New. Wed May 26 14:36:29 CEST 1999 Werner Koch * build-packet.c (build_sig_subpkt): Support large packets. * parse-packet.c (enum_sig_subpkt): Replaces parse_sig_subpkt. * mainproc.c (print_notation_data): Print all notation packets. * g10.c (add_notation_data): Add a way to specify the critical flag. (main): Add option --set-policy-url. (check_policy_url): Basic checks. * sign.c (mk_notation_and_policy): Replaces mk_notation. * parse-packet.c (can_handle_critical): Moved decision whether we can handle critical subpacket to an extra function. Tue May 25 19:50:32 CEST 1999 Werner Koch * sign.c (sign_file): Always use compression algo 1 for signed onyl file becuase we can´ be sure the the verifier supports other algorithms. * build-packet.c (build_sig_subpkt): Support for notation data. * sign.c (sign_file,clearsign_file,make_keysig_packet): Ditto. (mk_notation): New. * g10.c (add_notation_data): New and add option -N * mainproc.c (print_notation_data): New. (check_sig_and_print): Print any notation data of the signed text. Sun May 23 14:20:22 CEST 1999 Werner Koch * pkclist.c (check_signatures_trust): Print a warning and return immediateley if opt.always_trust is true. * g10.c (main): Corrected handling of no-default-keyring * pkclist.c (algo_available): Disable Twofish until we have settled how to do the MDC. * hkp.c: Disable everything for mingw32 Sat May 22 22:47:26 CEST 1999 Werner Koch * mainproc.c (check_sig_and_print): Add sig creation time to the VALIDSIG status output. Add more info to the ERRSIG output. * sig-check.c (signature_check): Add sig time after epoch to SIG_ID. * import.c (import_one): Merge duplicate user IDs. (collapse_uids): New. * kbnode.c (move_kbnode): New. (remove_kbnode): New. * keyedit.c (keyedit_menu): Call collapse_uids. * g10.c: new option --logger-fd. * import.c: s/log_*_f/log_*/ Thu May 20 14:04:08 CEST 1999 Werner Koch * misc.c (pull_in_libs): do the volatile only for gcc * sig-check (signature_check): Emit SIG_iD only for classes 0 and 1. * armor.c (armor_filter): Add detection of PGP2 created clearsigs. (fake_packet): A tab is not a WS for pgp2 - handle this. * textfilter.c (len_without_trailing_chars): New. (copy_clearsig_text): Add pgp2mode arg. * sign.c (clearsign_file): pass old_style to the above fnc. Wed May 19 16:04:30 CEST 1999 Werner Koch * g10.c: New option --interactive. * mainproc.c (proc_plaintext): Add workaround for pgp2 bug (do_check_sig): Ditto. (proc_tree): Ditto. * plaintext.c (do_hash): Ditto. (hash_datafiles): Ditto, add an arg, changed all callers. * mdfilter.c (md_filter): Add support for the alternate hash context. Mon May 17 21:54:43 CEST 1999 Werner Koch * parse-packet.c (parse_encrypted): Support for PKT_ENCRYPTED_MDC. * build-packet.c (do_encrypted_mdc): Ditto. * cipher.c (write_header): Add mdc hashing. (cipher_filter): write out the hash. * mainproc.c (do_proc_packets): Add PKT_ENCRYPTED_MDC. * encr-data.c (decrypt_data): Add mdc hashing. (mdc_decode_filter): New. * parse-packet.c (parse_sig_subpkt): Fixed stupid bug for subpkt length calculation (parse_signature): Fixed even more stupid bug. Sat May 8 19:28:08 CEST 1999 Werner Koch * build-packet.c (do_signature): Removed MDC hack. * encode.c (encode_crypt_mdc): Removed. * mainproc.c (do_check_sig): Removed MDC hack. (check_sig_and_print): Ditto. * parse-packet.c (parse_signature): Ditto. * sig-check.c (mdc_kludge_check): Ditto. * free-packte.c (copy_signature, free_seckey_enc): Ditto. * parse-packet.c (parse_signature,parse_key): Store data of unknown algorithms with mpi_set_opaque inseatd of the old faked data stuff. (read_rest): Removed. (read_rest2): Renamed to read_rest * build-packet.c (write_fake_data): Use mpi_get_opaque. * free-packet.c (cp_fake_data): Removed and cahnged all callers to use mpi_copy. (free_pubkey_enc,free_seckey_enc,release_public_key_parts, release_secret_key_parts): Use mpi_free for opaque data. Thu May 6 14:18:17 CEST 1999 Werner Koch * trustdb.c (check_trust): Check for revoked subkeys. * pkclist.c (do_we_trust): Handled revoked subkeys. (do_we_trust_pre): Ditto. (check_signatures_trust): Ditto. * build-packet.c (hash_public_key): Fix for ancient g10 keys. * mainproc.c (do_proc_packets): Return EOF if no data has been read. * g10.c (main): Catch errors for default operation. Thu Apr 29 12:29:22 CEST 1999 Werner Koch * sign.c (sign_file): Fixed hashing in case of no subpackets. (clearsign_file): Ditto. (make_keysig_packet): Ditto. Wed Apr 28 13:03:03 CEST 1999 Werner Koch * keyedit.c (keyedit_menu): Add new command revkey. * (menu_revkey): New. Mon Apr 26 17:48:15 CEST 1999 Werner Koch * parse-packet.c (parse_signature): Add the MDC hack. * build-packet.c (do_signature): Ditto. * free-packet.c (free_seckey_enc,copy_signature,cmp_signatures): Ditto. * mainproc.c (do_check_sig): Ditto. * sig-check.c (mdc_kludge_check): New. * encode.c (encrypt_mdc_file): New. * keyedit.c (check_all_keysigs): List revocations. * (menu_revsig): New. * sign (make_keysig_packet): Support for class 0x30. Sun Apr 18 20:48:15 CEST 1999 Werner Koch * pkclist.c (select_algo_from_prefs): Fixed the case that one key has no preferences (Remi Guyomarch). keylist.c (list_keyblock): ulti_hack to propagate trust to all uids. Sun Apr 18 10:11:28 CEST 1999 Werner Koch * seckey-cert.c (do_check): Use real IV instead of a 0 one, so that it works even if the length of the IV doesn't match the blocksize. Removed the save_iv stuff. (protect_secret_key): Likewise. Create the IV here. * packet.h (PKT_secret_key): Increased size of IV field and add a ivlen field. * parse-packet.c (parse_key): Use the len protect.ivlen. * build-packet.c (do_secret_key). Ditto. * getkey.c (key_byname): Close keyblocks. * Makefile.am (gpgm): Removed this * g10.c: Merged gpg and gpgm * import.c (import): Utilize option quiet. * tdbio.c (tdbio_set_dbname): Ditto. * ringedit.c (add_keyblock_resource,keyring_copy): Ditto. * keyedit.c (sign_uids): Add some batch support. * g10.c (main): add call to tty_batchmode. Fri Apr 9 12:26:25 CEST 1999 Werner Koch * status.c (write_status_text): Some more status codes. * passphrase_to_dek (passphrase_to_dek): add a status code. * seckey_cert.c (check_secret_key): Likewise. * encr-data.c (decrypt_data): Reverse the last changes * cipher.c (write_header): Ditto. * parse-packet.c (parse_key): Dropped kludge for ancient blowfish mode. Thu Apr 8 09:35:53 CEST 1999 Werner Koch * mainproc.c (proc_encrypted): Add a new status output * passphrase.c (passphrase_to_dek): Ditto. * status.h status.c: Add new status tokens. Wed Apr 7 20:51:39 CEST 1999 Werner Koch * encr-data.c (decrypt_data): Fixes for 128 bit blocksize * cipher.c (write_header): Ditto. * seckey-cert.c (do_check): Ditto. (protect_secret_key). Ditto. * misc.c (print_cipher_algo_note): Twofish is now a standard algo. * keygen.c (do_create): Fixed spelling (Gaël Quéri) (ask_keysize): Only allow keysizes up to 4096 * ringedit.c (add_keyblock_resource): chmod newly created secrings. * import.c (delete_inv_parts): Fixed accidently deleted subkeys. Tue Apr 6 19:58:12 CEST 1999 Werner Koch * armor.c: Removed duped include (John Bley) * mainproc.c: Ditto. * build-packet.c (hash_public_key): Fixed hashing of the header. * import.c (delete_inv_parts): Allow import of own non-exportable sigs. Sat Mar 20 13:59:47 CET 1999 Werner Koch * armor.c (fake_packet): Fix for not not-dash-escaped Sat Mar 20 11:44:21 CET 1999 Werner Koch * g10.c (main): Added command --recv-keys * hkp.c (hkp_import): New. Wed Mar 17 13:09:03 CET 1999 Werner Koch * trustdb.c (check_trust): add new arg add_fnc and changed all callers. (do_check): Ditto. (verify_key): Ditto. (propagate_validity): Use the new add_fnc arg. (print_user_id): Add the FILE arg. (propagate_ownertrust): New. * pkclist.c (add_ownertrust_cb): New and changed the add_ownertrust logic. * getkey.c (get_keyblock_bylid): New. * trustdb.c (print_uid_from_keyblock): New. (dump_tn_tree_with_colons): New. (list_trust_path): Add colon print mode. * trustdb.c (insert_trust_record): Always use the primary key. * encode.c (encode_simple): Added text_mode filter (Rémi Guyomarch) (encode_crypt): Ditto. * mainproc.c (proc_pubkey_enc): Added status ENC_TO. * armor.c (armor_filter): Added status NODATA. * passphrase.c (passphrase_to_dek): Always print NEED_PASSPHRASE * seckey_cert.c (check_secret_key): Added BAD_PASS status. * g10.c (main): Set g10_opt_homedir. Sun Mar 14 19:34:36 CET 1999 Werner Koch * keygen.c (do_create): Changed wording of the note (Hugh Daniel) Thu Mar 11 16:39:46 CET 1999 Werner Koch * tdbdump.c: New * trustdb.c (walk_sigrecs,do_list_sigs,list_sigs, list_records,list_trustdb,export_ownertrust,import_ownertrust): Moved to tdbdump.c (init_trustdb): renamed to setup_trustdb. Changed all callers. (do_init_trustdb): renamed to init_trustdb(). * trustdb.c (die_invalid_db): replaced by tdbio_invalid. * tdbio.c (tdbio_invalid): New. * import.c (delete_inv_parts): Skip non exportable signatures. * keyedit.c (sign_uid_mk_attrib): New. (sign_uids): Add the local argument. (keyedit_menu): New "lsign" command. * trustdb.c (register_trusted_key): Removed this and all related stuff. * g10.c (oTrustedKey): Removed option. * tdbio.h (dir.valcheck): New trustdb field. * tdbio.c: Add support for this field (tdbio_read_modify_stamp): New. (tdbio_write_modify_stamp): New. * trustdb.c (do_check): Check against this field. Removed cache update. (verify_key): Add cache update. (upd_uid_record): Some functional changes. (upd_cert_record): Ditto Wed Mar 10 11:26:18 CET 1999 Werner Koch * keylist.c (list_keyblock): Fixed segv in uid. Print 'u' as validity of sks. Mon Mar 8 20:47:17 CET 1999 Werner Koch * getkey.c (classify_user_id): Add new mode 12 (#). * seckey-cert.c (check_secret_key): replaced error by info. * trustdb.c (query_trust_info): Add another arg, changed all callers. (check_trust): Ditto. (do_check): Ditto. (verify_key): Handle namehash. * keylist.c (list_keyblock): print trust info for user ids. * sig-check.c (signature_check): Add sig-created to status output. Tue Mar 2 16:44:57 CET 1999 Werner Koch * textfilter.c (copy_clearsig_text): New. (clearsign): Removed. * sign.c (clearsign_file): does not use textfiler anymore. * keygen.c (ask_user_id): print a note about the used charset. Tue Mar 2 10:38:42 CET 1999 Werner Koch * sig-check.c (signature_check): sig-id now works for all algos. * armor.c (armor_filter): Fixed armor bypassing. Sun Feb 28 19:11:00 CET 1999 Werner Koch * keygen.c (ask_user_id): Don't change the case of email addresses. (has_invalid_email_chars): Adjusted. * keylist.c (list_one): Really list serect keys (Remi Guyomarch) * keyedit.c (menu_select_uid): Add some braces to make egcs happy. (menu_select_key): Ditto. * mainproc.c (do_proc_packets): List sym-enc packets (Remi Guyomarch) Fri Feb 26 17:55:41 CET 1999 Werner Koch * pkclist.c (build_pk_list): Return error if there are no recipients. * sig-check.c (signature_check): New signature id feature. * armor.c (make_radic64_string): New. * mainproc.c (proc_pubkey_enc): early check for seckey availability. * pkclist.c (do_we_trust_pre): print user id before asking. * ringedit.c (add_keyblock_resource,get_keyblock_handle): Cleaner handling of default resource. Thu Feb 25 18:47:39 CET 1999 Werner Koch * pkclist.c (algo_available): New. (select_algo_from_prefs): Check whether algo is available. * ringedit.c (keyring_copy): Take care of opt.dry_run. (do_gdbm_store): Ditto. * openfile.c (open_outfile). Ditto. (copy_options_file): Ditto. * trustdb.c (update_trustdb): Ditto. (clear_trust_checked_flag): Ditto. (update_trust_record): Ditto. (insert_trust_record): Ditto. Wed Feb 24 11:07:27 CET 1999 Werner Koch * keylist.c (secret_key_list): Now really list the secret key. * trustdb.c (do_init_trustdb): New. Init is now deferred. Mon Feb 22 20:04:00 CET 1999 Werner Koch * getkey.c (lookup_sk): Return G10ERR_NO_SECKEY and not x_PUBKEY. Fri Feb 19 15:49:15 CET 1999 Werner Koch * pkclist.c (select_algo_from_prefs): retrieve LID if not there. * armor.c (fake_packet): Replaced ugly lineending handling. * g10.c (oNoEncryptTo): New. * pkclist.c (build_pk_list): Implemented this option. * g10.c (main): Greeting is now printed to stderr and not to tty. Use add_to_strlist() instead of direct coding. * import.c (import): Use iobuf_push_filter2. * mainproc.c (check_sig_and_print): Print all user ids for good signatures. * getkey.c (get_pubkeyblock): New. * import.c (chk_self_sigs): Fixed SEGV for unbounded class 0x18 keys. (delete_inv_parts): Delete special marked packets. Tue Feb 16 14:10:02 CET 1999 Werner Koch * g10.c (main): New option --encrypt-to * pkclist.c (build_pk_list): Implemented encrypt-to. * parse-packet.c (parse_user_id): Removed the hack to work with utf-8 strings. * g10.c (main): Install lockfile cleanup handler. * tdbio.c (cleanup): Removed: this is now handled by dotlock. Sat Feb 13 14:13:04 CET 1999 Werner Koch * tdbio.c (tdbio_set_dbname): Init lockhandle for a new trustdb Wed Feb 10 17:15:39 CET 1999 Werner Koch * g10.c (main): check for development version now in configure * tdbio.c (tdbio_write_record): Add uid.validity (tdbio_read_record) : Ditto. (tdbio_dump_record) : Ditto. * keygen.c (keygen_add_std_prefs): Replaced Blowfish by Twofish, removed MD5 and Tiger. * pubkey-enc.c (get_it): Suppress warning about missing Blowfish in preferences in certain cases. * ringedit.c (lock_rentry,unlock_rentry): New. * getkey.c (key_byname): Pass ret_kb down to lookup_xx. * armor.c (armor_filter): No output of of empty comment lines. Add option --no-version to suppress the output of the version string. * getkey.c: Release the getkey context for auto context variables. Sun Jan 24 18:16:26 CET 1999 Werner Koch * getkey.c: Changed the internal design to allow simultaneous lookup of multible user ids (get_pubkey_bynames): New. (get_seckey_bynames): New. (get_seckey_next): New. (get_seckey_end): New. * keylist.c (list_one): Use the new functions. * keylist.c (list_keyblock): add a newline for normal listings. * g10.c (--recipient): New option name to replace --remote-user Wed Jan 20 18:59:49 CET 1999 Werner Koch * textfilter.c: Mostly rewritten * plaintext.c (handle_plaintext): Use now text_filter semantics. Tue Jan 19 19:34:58 CET 1999 Werner Koch * export.c (export_pubkeys_stream): New. (do_export_stream): New. * g10.c (aSendKeys): New command. * hkp.c (hkp_export): New. * compress.c (do_uncompress): Hack for algo 1 and 1.1.3 Sun Jan 17 11:04:33 CET 1999 Werner Koch * textfilter.c (text_filter): Now uses iobuf_read_line(). (read_line): Removed. * armor.c (trim_trailing_spaces): Removed and replaced by trim_trailing_ws from libutil Sat Jan 16 12:03:27 CET 1999 Werner Koch * hkp.c (hkp_ask_import): Use only the short keyid Sat Jan 16 09:27:30 CET 1999 Werner Koch * import.c (import_key_stream): New (import): New, moved most of import_keys here. * g10.c: New option --keyserver * mainproc.c (check_sig_and_print): Hook to import a pubkey. * pref.c pref.h : Removed * hkp.c hkp.h: New Wed Jan 13 14:10:15 CET 1999 Werner Koch * armor.c (radix64_read): Print an error if a bad armor was detected. Wed Jan 13 12:49:36 CET 1999 Werner Koch * armor.c (radix64_read): Now handles malformed armors produced by some buggy MUAs. Tue Jan 12 11:17:18 CET 1999 Werner Koch * ringedit.c (find_keyblock_bysk): New. * skc_list.c (is_insecure): New. (build_sk_list): usage check for insecure keys. * import.c (chk_self_sigs): Add handling for subkeys. (delete_inv_parts): Skip unsigned subkeys * sig-check.c (do_check): Print info if the signature is older than the key. * keygen.c (generate_subkeypair): Fail on time warp. * sign.c (do_sign): Ditto. Sun Jan 10 15:10:02 CET 1999 Werner Koch * armor.c (fake_packet): Fixed not-dash-escaped bug. Sat Jan 9 16:02:23 CET 1999 Werner Koch * sig-check.c (do_check): Output time diff on error * status.c (STATUS_VALIDSIG): New. (is_status_enabled): New. * mainproc.c (check_sig_and_print): Issue that status message. * plaintext.c (special_md_putc): Removed * armor.c (armor_filter): print error for truncated lines. * free-packet.c (free_encrypted): Revomed call to set_block_mode. (free_plaintext): Ditto. Thu Jan 7 18:00:58 CET 1999 Werner Koch * pkclist.c (add_ownertrust): Fixed return value. * encr-data.c (decrypt_data): Disabled iobuf_set_limit and iobuf_pop_filter stuff. * compress.c (handle_compressed): Disabled iobuf_pop_filter. * packet.h (PKT_secret_key): Add is_primary flag. * parse-packet.c (parse_key): Set this flag. * passphrase.c (passphrase_to_dek): Kludge to print the primary keyid - changed the API: keyid must now hold 2 keyids. * getkey.c (get_primary_seckey): New. * seckey-cert.c (do_check): pass primary keyid to passphrase query * tbdio.c (open_db): removed the atexit (tdbio_set_dbname): and moved it to here. * armor.c: Rewrote large parts. Tue Dec 29 19:55:38 CET 1998 Werner Koch * revoke.c (gen_revoke): Removed compression. * pkclist.c (do_we_trust_pre): special check for revoked keys * trustdb.c (update_trust_record): Fixed revoke flag. Tue Dec 29 14:41:47 CET 1998 Werner Koch * misc.c (disable_core_dumps): Check for EINVAL (Atari) * getkey (merge_one_pk_and_selfsig): Fixed search of expiredate. (merge_keys_and_selfsig): Ditto. * free-packet.c (cmp_public_keys): cmp expire only for v3 packets (cmp_secret_keys): Ditto. (cmp_public_secret_key): Ditto. Wed Dec 23 17:12:24 CET 1998 Werner Koch * armor.c (find_header): Reset not_dashed at every header Wed Dec 23 13:18:14 CET 1998 Werner Koch * pkclist.c (add_ownertrust): Refresh validity values. * trustdb.c (enum_cert_paths_print): New arg refresh. * ringedit.c: Fixed problems fix keyrings * parse-packet.c (dbg_parse_packet): New debug functions. * getkey.c (getkey_disable_caches): New. * import.c (import_keys): Disable caches. Thu Dec 17 18:31:15 CET 1998 Werner Koch * misc.c (trap_unaligned): Only for glibc 1 * sign.c (write_dash_escaped): Now escapes "From " lines * g10.c: New option --escape-from-lines * trustdb.c (sort_tsl_list): New (list_trust_path): Now prints sorted list. (enum_cert_paths): Likewise. (enum_cert_paths_print): New. (print_paths): New printing format. * pkclist.c (add_ownertrust): New arg quit. (edit_ownertrust): New quit selection and does not query the recipients ownertrust anymore. (add_ownertrust): Print the ceritficate path. Mon Dec 14 21:18:49 CET 1998 Werner Koch * parse-packet.c (parse_signature): Now checks for critical bit (parse_sig_subpkt): Splitted. (parse_one_sig_subpkt): New. * sig-check.c (do_check): handle critical bit. Sun Dec 13 14:10:56 CET 1998 Werner Koch * pcklist.c (select_algo_from_prefs): Preferences should now work (lost the != ? ) Thu Dec 10 20:15:36 CET 1998 Werner Koch * ringedit.c (gdbm_store): Fix for inserts * g10.c (main): New option --export-all * export.c (export_pubkeys): New arg. (do_export): Now may skip old keys. * status.c: Minor patches for Sun's cc * keygen.c (ask_algo): Disabled v3 ElGamal choice, rearranged the numbers. Add a warning question when a sign+encrypt key is selected. * g10.c (do_not_use_RSA): Removed. * misc.c (print_pubkey_algo_note): New as replacement for the do_not_use_RSA() and chnaged all callers. (print_cipher_algo_note): New. (print_hash_algo_note): New. * cipher.c (write_header): Add a call to print_cipher_algo_note. * seckey-cert.c (protect_secret_key): Ditto * sign.c (do_sign): Add a call to print_digest_algo_note. * getkey.c (get_long_user_id_string): New. * mainproc.c (check_sig_and_print): Changed the format of the status output. * encrypt.c (write_pubkey_enc_from_list): print used symmetric cipher. * pkclist.c (do_we_trust): Changed a message. Wed Dec 9 13:41:06 CET 1998 Werner Koch * misc.c (trap_unaligned) [ALPHA]: Only if UAC_SIGBUS is defined. * sign.c (write_dash_escaped): Add the forgotten patch by Brian Moore. * compress.c (do_uncompress): Fixed the inflating bug. Tue Dec 8 13:15:16 CET 1998 Werner Koch * trustdb.c (upd_uid_record): Now uses the newest self-signature (insert_trust_record): Now calls update with recheck set to true. (register_trusted_key): New. (verify_own_keys): Enhanced by list of trusted keys. * g10.c (main): Print a warning when a devel version is used. (main): New option --trusted-key * import.c (merge_blocks): Fixed merging of new user ids and added merging of subkeys. (append_uid): Ditto. (merge_keysig): New. (append_key): New. * getkey.c (merge_one_pk_and_selfsig): Get the expiration time from the newest self-signature. (merge_keys_and_selfsig): Ditto. * free-packet.c (cmp_secret_key): New. Fri Nov 27 21:37:41 CET 1998 Werner Koch * g10.c: New option --lock-once * tdbio.c (open_db): Add an atexit (cleanup): New. (tdbio_sync): Add locking. (tdbio_end_transaction): Ditto. (put_record_into_cache): Ditto. * ringedit.c (keyring_copy): Ditto. (cleanup): New. (add_keyblock_resource): Add an atexit. Fri Nov 27 15:30:24 CET 1998 Werner Koch * armor.c (find_header): Another fix for clearsigs. Fri Nov 27 12:39:29 CET 1998 Werner Koch * status.c (display_help): Removed. * helptext.c: New and removed the N_() from all cpr_gets. Fri Nov 20 16:54:52 1998 Werner Koch (wk@isil.d.shuttle.de) * g10.c (main): New option --not-dash-escaped * sign.c (write_dashed_escaped): Ditto. * armor.c (find_header): Support for NotDashEscaped header. * getkey.c: print "disabled cache.." only if verbose is used. Thu Nov 19 07:17:31 1998 Werner Koch * parse-packet.c (dump_sig_subpkt): Fixed expire listing * getkey.c (merge_keys_and_selfsig): Fixed expire calculation. (merge_one_pk_and_selfsig): Ditto. * keyedit.c (menu_expire). Ditto. * keygen.c (keygen_add_key_expire): Ditto. (ask_expire_interval): New and changed all local function to use this instead. (keygen_add_key_expire): Opaque should now be a public key; changed all callers. * parse.packet.c (parse): use skip_rest to skip packets. * keyedit.c (keyedit_menu): New arg for cmdline cmds. Wed Nov 18 20:33:50 1998 Werner Koch (wk@isil.d.shuttle.de) * trustdb.c (check_trustdb): Now rechecks all gived userids. (collect_paths): Some fixes. (upd_pref_records): Skips empty items, evaluate all items. * parse-packet.c (dump_sig_subpkt): Better listing of prefs. (skip_packet): Now knows about marker packet * g10.c: removed cmd "--edit-sig". * pubring.asc: Updated. Sat Nov 14 14:01:29 1998 Werner Koch (wk@isil.d.shuttle.de) * g10.c (main): Changed syntax of --list-trust-path * trustdb.c (list_trust_path): Replaced max_depth by opt.max_cert_depth Fri Nov 13 07:39:58 1998 Werner Koch * trustdb.c (collect_paths): Removed a warning message. (enum_trust_web): Removed. (enum_cert_paths): New. * pkclist.c (add_ownertrust): Changed to use enum_cert_paths. (edit_ownertrust): Now list ceritficates on request. (show_paths): New. Wed Nov 11 18:05:44 1998 Werner Koch * g10.c (main): New option --max-cert-depth * tdbio.h: add new fields to ver and dir record. * tdbio.c: read/write/dump of these fields. (tdbio_db_matches_options): New. * trustdb.c: replaced MAC_CERT_DEPTH by opt.max_cert_depth. (do_check): cache validity and changed other functions to reset the cached value. * keylist.c (list_one): Now lists the ownertrust. * mainproc.c (list_node): Ditto. Tue Nov 10 10:08:59 1998 Werner Koch (wk@isil.d.shuttle.de) * g10.c (g10_exit): Now looks at the new g10_errors_seen. * mainproc.c (check_sig_and_print): Sets g10_errors_seen. * *.c : i18n many more strings. * ringedit.c (locate_keyblock_by_keyid): Add HAVE_LIBGDBM (locate_keyblock_by_fpr): Ditto. * g10.c (main): removed unsused "int errors". (main): Add new option --charset. * g10.c (main): special message for the unix newbie. Mon Nov 9 07:17:42 1998 Werner Koch * getkey.c (finish_lookup): Kludge to prefere algo 16. * trustdb.c (new_lid_table): Clear cached item. * status.c (cpr_get_utf8): New. * pkclist.c (build_pk_list): Uses this. Sun Nov 8 17:20:39 1998 Werner Koch (wk@isil.d.shuttle.de) * mainproc.c (check_sig_and_print): Why did I use strlen()-1 in the printf? - This truncated the TZ. Sat Nov 7 15:57:28 1998 me,,, (wk@tobold) * getkey.c (lookup): Changes to support a read_next. (get_pubkey): Fixed a memory leak. * keylist.c (list_one): Now lists all matching user IDs. Tue Nov 3 16:19:21 1998 Werner Koch (wk@isil.d.shuttle.de) * keygen.c (ask_user_id): Now converted to UTF-8 * g10.c (main): Kludge for pgp clearsigs and textmode. Fri Oct 30 16:40:39 1998 me,,, (wk@tobold) * signal.c (block_all_signals): New. (unblock_all_signals): New * tdbio.c (tdbio_end_transaction): Now blocks all signals. * trustdb.c (new_lid_table): Changed the representation of the former local_lid_info stuff. * trustdb.c (update_trust_record): Reorganized the whole thing. * sig-check.c (check_key_signature): Now handles class 0x28 Wed Oct 28 18:56:33 1998 me,,, (wk@tobold) * export.c (do_export): Takes care of the exportable sig flag. Tue Oct 27 14:53:04 1998 Werner Koch (wk@isil.d.shuttle.de) * trustdb.c (update_trust_record): New "fast" parameter. Sun Oct 25 19:32:05 1998 Werner Koch (wk@isil.d.shuttle.de) * openfile.c (copy_options_File): New. * ringedit.c (add_keyblock_resource): Creates options file * tdbio.c (tdbio_set_dbname): Ditto. Sat Oct 24 14:10:53 1998 brian moore * mainproc.c (proc_pubkey_enc): Don't release the DEK (do_proc_packets): Ditto. Fri Oct 23 06:49:38 1998 me,,, (wk@tobold) * keyedit.c (keyedit_menu): Comments are now allowed * trustdb.c: Rewrote large parts. Thu Oct 22 15:56:45 1998 Michael Roth (mroth@nessie.de) * encode.c: (encode_simple): Only the plain filename without a given directory is stored in generated packets. (encode_crypt): Ditto. * sign.c: (sign_file) Ditto. Thu Oct 22 10:53:41 1998 Werner Koch (wk@isil.d.shuttle.de) * trustdb.c (update_trust_record): Add new optional arg. * import.c (import_keys): Add statistics output * trustdb.c (update_trustdb): Ditto. (insert_trustdb): Ditto. * tdbio.c (tdbio_begin_transaction): New. (tdbio_end_transaction): New. (tdbio_cancel_transaction): New. * g10.c (main): New option --quit. * trustdb.c (check_hint_sig): No tests for user-id w/o sig. This caused an assert while checking the sigs. * trustdb.c (upd_sig_record): Splitted into several functions. * import.c (import_keys): New arg "fast". * g10.c (main): New command --fast-import. Wed Oct 21 18:19:36 1998 Michael Roth * ringedit.c (add_keyblock_resource): Directory is now created. * tdbio.c (tdbio_set_dbname): New info message. Wed Oct 21 11:52:04 1998 Werner Koch (wk@isil.d.shuttle.de) * trustdb.c (update_trustdb): released keyblock in loop. * keylist.c (list_block): New. (list_all): Changed to use list_block. * trustdb.c: Completed support for GDBM * sign.c (only_old_style): Changed the way force_v3 is handled (sign_file): Ditto. (clearsign_file): Ditto. * keygen.c (has_invalid_email_chars): Splitted into mailbox and host part. * keylist.c (list_one): Add a merge_keys_and_selfsig. * mainproc.c (proc_tree): Ditto. Sun Oct 18 11:49:03 1998 Werner Koch (wk@isil.d.shuttle.de) * sign.c (only_old_style): Add option force_v3_sigs (sign_file): Fixed a bug in sig->version (clearsign_file): Ditto. * parse-packet.c (dump_sig_subpkt): New * keyedit.c (menu_expire): New. * free-packet.c (cmp_signatures): New Sat Oct 17 10:22:39 1998 Werner Koch (wk@isil.d.shuttle.de) * armor.c: changed output line length from 72 to 64. * keyedit.c (fix_keyblock): New. Fri Oct 16 10:24:47 1998 Werner Koch (wk@isil.d.shuttle.de) * trustdb.c: Rewrote most. * tdbio.c: Add cache and generalized hash tables. * options.h (ENABLE_COMMENT_PACKETS): New but undef'ed. * encode.c, sign.c, keygen.c: Disabled comment packets. * export.c (do_export): Comment packets are never exported, except for those in the secret keyring. * g10.c (main): Removed option do-no-export-rsa; should be be replaced by a secpial tool. * export.c (do_export): Removed the code for the above option. * armor.c (find_header): Support for new only_keyblocks. * import.c (import_keys): Only looks for keyblock armors. * packet.h: replaced valid_days by expiredate and changed all users. * build-packet.c (do_public_key): calculates valid-days (do_secret_key): Ditto. * parse-packet.c (parse_key): expiredate is calucated from the valid_period in v3 packets. * keyid.c (do_fingerprint_md): calculates valid_dates. * keygen.c (add_key_expire): fixed key expiration time for v4 packets. * armor.c (find_header): A LF in the first 28 bytes was skipped for non-armored data. Thu Oct 8 11:35:51 1998 Werner Koch (wk@isil.d.shuttle.de) * armor.c (is_armored): Add test on old comment packets. * tdbio.c (tdbio_search_dir_bypk): fixed memory leak. * getkey.c: Changed the caching algorithms. Wed Oct 7 19:33:28 1998 Werner Koch (wk@isil.d.shuttle.de) * kbnodes.c (unused_nodes): New. Wed Oct 7 11:15:36 1998 Werner Koch (wk@isil.d.shuttle.de) * keyedit.c (sign_uids): Fixed a problem with SK which could caused a save of an unprotected key. (menu_adduid): Ditto. * keyedit.c (keyedit_menu): Prefs are now correctly listed for new user ids. * trustdb.c (update_trust_record): New. (insert_trust_record): Now makes use of update_trust_record. Tue Oct 6 16:18:03 1998 Werner Koch (wk@isil.d.shuttle.de) * trustdb.c (read_record): replaces most of the tdbio_read_records. (write_record): Ditto. Sat Oct 3 11:01:21 1998 Werner Koch (wk@isil.d.shuttle.de) * keygen.c (ask_alogo): enable ElGamal enc-only only for addmode. Wed Sep 30 10:15:33 1998 Werner Koch (wk@isil.d.shuttle.de) * import.c (import_one): Fixed update of wrong keyblock. Tue Sep 29 08:32:08 1998 me,,, (wk@tobold) * mainproc.c (proc_plaintext): Display note for special filename. * plaintext.c (handle_plaintext): Suppress output of special file. Mon Sep 28 12:57:12 1998 Werner Koch (wk@isil.d.shuttle.de) * g10.c (verify_own_keys): Add warning if a key is not protected. * passphrase (hash_passphrase): Fixed iterated+salted mode and setup for keysizes > hashsize. * g10.c (main): New options: --s2k-{cipher,digest,mode}. Fri Sep 25 09:34:23 1998 Werner Koch (wk@isil.d.shuttle.de) * g10.c: Chnaged some help texts. Tue Sep 22 19:34:39 1998 Werner Koch (wk@isil.d.shuttle.de) * passphrase.c (read_passphrase_from_fd): fixed bug for long passphrases. Mon Sep 21 11:28:05 1998 Werner Koch (wk@(none)) * getkey.c (lookup): Add code to use the sub key if the primary one does not match the usage. * armor.c (armor_filter): New error message: no valid data found. (radix64_read): Changes to support multiple messages. (i18n.h): New. * mainproc.c (add_onepass_sig): bug fix. Mon Sep 21 08:03:16 1998 Werner Koch (wk@isil.d.shuttle.de) * pkclist.c (do_we_trust): Add keyid to most messages. * passphrase.c (read_passphrase_from_fd): New. (have_static_passphrase): New (get_passphrase_fd): Removed. (set_passphrase_fd): Removed. * g10.c (main): passphrase is now read here. * keyedit.c (keyedit_menu): "help" texts should now translate fine. Mon Sep 21 06:40:02 1998 Werner Koch (wk@isil.d.shuttle.de) * encode.c (encode_simple): Now disables compression when --rfc1991 is used. (encode_crypt): Ditto. Fri Sep 18 16:50:32 1998 Werner Koch (wk@isil.d.shuttle.de) * getkey.c (merge_key_and_selfsig): New. Fri Sep 18 10:20:11 1998 Werner Koch (wk@isil.d.shuttle.de) * pkclist.c (select_algo_from_prefs): Removed 3DES kludge. * seskey.c (make_session_key): Fixed SERIOUS bug introduced by adding the weak key detection code. * sign.c (sign_file): Changed aremor header in certain cases. Tue Sep 15 17:52:55 1998 Werner Koch (wk@isil.d.shuttle.de) * mainproc.c (check_sig_and_print): Replaced ascime by asctimestamp. Mon Sep 14 11:40:52 1998 Werner Koch (wk@isil.d.shuttle.de) * seskey.c (make_session_key): Now detects weak keys. * trustdb (clear_trust_checked_flag): New. * plaintext.c (handle_plaintext): Does no anymore suppress CR from cleartext signed messages. Sun Sep 13 12:54:29 1998 Werner Koch (wk@isil.d.shuttle.de) * trustdb.c (insert_trust_record): Fixed a stupid bug in the free liunked list loops. Sat Sep 12 15:49:16 1998 Werner Koch (wk@isil.d.shuttle.de) * status.c (remove_shmid): New. (init_shm_comprocess): Now sets permission to the real uid. Wed Sep 9 11:15:03 1998 Werner Koch (wk@isil.d.shuttle.de) * packet.h (PKT_pubkey_enc): New flah throw_keyid, and add logic to implement it. * g10.c (main): New Option --throw-keyid * getkey.c (enum_secret_keys): Add new ar and changed all callers. Tue Sep 8 20:04:09 1998 Werner Koch (wk@isil.d.shuttle.de) * delkey.c (delete_key): Moved from keyedit.c. Mon Sep 7 16:37:52 1998 Werner Koch (wk@isil.d.shuttle.de) * build-packet.c (calc_length_header): New arg new_ctb to correctly calculate the length of new style packets. * armor.c (is_armored): Checks for symkey_enc packets. * pkclist.c (select_algo_from_prefs): 3DEs substitute is now CAST5. Tue Aug 11 17:54:50 1998 Werner Koch (wk@isil.d.shuttle.de) * build-packet.c (do_secret_key): Fixed handling of old keys. * getkey.c (compare_name): Fixed exact and email matching * openfile.c (open_outfile): Changed arguments and all callers. Tue Aug 11 09:14:35 1998 Werner Koch (wk@isil.d.shuttle.de) * encode.c (encode_simple): Applied option set-filename and comment. (encode_crypt): Ditto. * sign.c (sign_file): Ditto. * armor.c (armor_filter): Applied option comment. * encode.c (encode_crypt): Moved init_packet to the begin. (encode_simple): add an init_packet(). * comment (write_comment): Now enforces a hash sign as the 1st byte. * import.c (import_one): Add explanation for "no user ids". * compress.c (do_uncompress): Applied Brian Warner's patch to support zlib 1.1.3 etc. * trustdb.c (check_trust): Fixed a problem after inserting new keys. * getkey (lookup): do not return the primary key if usage is given (lookup_sk): Ditto and take usage into account. * status.c (cpr_get_answer_is_yes): add display_help. Mon Aug 10 10:11:28 1998 Werner Koch (wk@isil.d.shuttle.de) * getkey.c (lookup_sk): Now always returns the primary if arg primary is true. (lookup): Likewise. (get_pubkey_byname): Now returns the primary key (get_seckey_byname): Ditto. Mon Aug 10 08:34:03 1998 Werner Koch (wk@isil.d.shuttle.de) * keyid.c (pubkey_letter): ELG_E is now a small g. Sat Aug 8 17:26:12 1998 Werner Koch (wk@isil.d.shuttle.de) * openfile (overwrite_filep): Changed semantics and all callers. Sat Aug 8 12:17:07 1998 Werner Koch (wk@isil.d.shuttle.de) * status.c (display_help): New. Thu Aug 6 16:30:41 1998 Werner Koch,mobil,,, (wk@tobold) * seskey.c (encode_session_key): Now uses get_random_bits(). Thu Aug 6 07:34:56 1998 Werner Koch,mobil,,, (wk@tobold) * ringedit.c (keyring_copy): No more backupfiles for secret keyrings and add additional warning in case of a failed secret keyring operation. Wed Aug 5 11:54:37 1998 Werner Koch (wk@isil.d.shuttle.de) * g10.c (check_opts): Moved to main. Changed def_cipher_algo semantics and chnaged all users. * pubkey-enc.c (get_sssion_key): New informational output about preferences. * parse-packet.c (parse_symkeyenc): Fixed salted+iterated S2K (parse_key): Ditto. * build-packet.c (do_secret_key): Ditto. (do_symkey_enc): Ditto. Tue Aug 4 08:59:10 1998 Werner Koch (wk@isil.d.shuttle.de) * getkey.c (enum_secret_keys): Now returns only primary keys. * getkey (lookup): Now sets the new namehash field. * parse-packet.c (parse_sig_subpkt2): New. * sign.c (sign_file): one-pass sigs are now emiited reverse. Preference data is considered when selecting the compress algo. Wed Jul 29 12:53:03 1998 Werner Koch (wk@isil.d.shuttle.de) * free-packet.c (copy_signature): New. * keygen.c (generate_subkeypair): rewritten * g10.c (aKeyadd): Removed option --add-key Mon Jul 27 10:37:28 1998 Werner Koch (wk@isil.d.shuttle.de) * seckey-cert.c (do_check): Additional check on cipher blocksize. (protect_secret_key): Ditto. * encr-data.c: Support for other blocksizes. * cipher.c (write_header): Ditto. Fri Jul 24 16:47:59 1998 Werner Koch (wk@isil.d.shuttle.de) * kbnode.c (insert_kbnode): Changed semantics and all callers. * keyedit.c : More or less a complete rewrite Wed Jul 22 17:10:04 1998 Werner Koch (wk@isil.d.shuttle.de) * build-packet.c (write_sign_packet_header): New. Tue Jul 21 14:37:09 1998 Werner Koch (wk@isil.d.shuttle.de) * import.c (import_one): Now creates a trustdb record. * g10.c (main): New command --check-trustdb Mon Jul 20 11:15:07 1998 Werner Koch (wk@isil.d.shuttle.de) * genkey.c (generate_keypair): Default key is now DSA with encryption only ElGamal subkey. Thu Jul 16 10:58:33 1998 Werner Koch (wk@isil.d.shuttle.de) * keyid.c (keyid_from_fingerprint): New. * getkey.c (get_pubkey_byfprint): New. Tue Jul 14 18:09:51 1998 Werner Koch (wk@isil.d.shuttle.de) * keyid.c (fingerprint_from_pk): Add argument and changed all callers. (fingerprint_from_sk): Ditto. Tue Jul 14 10:10:03 1998 Werner Koch (wk@isil.d.shuttle.de) * plaintext.c (handle_plaintext): Now returns create error if the file could not be created or the user responded not to overwrite the file. * mainproc.c (proc_plaintext): Tries again if the file could not be created to check the signature without output. * misc.c (disable_core_dumps): New. * g10.c (main): disable coredumps for gpg * g10.c (MAINTAINER_OPTIONS): New to disable some options Mon Jul 13 16:47:54 1998 Werner Koch (wk@isil.d.shuttle.de) * plaintext.c (hash_datafiles): New arg for better support of detached sigs. Changed all callers. * mainproc.c (proc_signature_packets): Ditto. * g10.c (main): New option "compress-sigs" * sig.c (sign_file): detached signatures are not anymore compressed unless the option --compress-sigs is used. Thu Jul 9 19:54:54 1998 Werner Koch (wk@isil.d.shuttle.de) * armor.c: Fixes to allow zero length cleartext signatures Thu Jul 9 14:52:47 1998 Werner Koch (wk@isil.d.shuttle.de) * g10.c (build_list): Now drops setuid. (main): Changed the way keyrings and algorithms are registered . Wed Jul 8 14:17:30 1998 Werner Koch (wk@isil.d.shuttle.de) * packet.h (PKT_public_key): Add field keyid. * parse-packet.c (parse_key): Reset the above field. * keyid.c (keyid_from_pk): Use above field as cache. * tdbio.c, tdbio.h: New * trustdb.c: Moved some functions to tdbio.c. (print_keyid): New. * pkclist.c (check_signatures_trust): New. Wed Jul 8 10:45:28 1998 Werner Koch (wk@isil.d.shuttle.de) * plaintext.c (special_md_putc): New. (handle_plaintext): add clearsig argument * mainproc.c (proc_plaintext): detection of clearsig * sign.c (write_dased_escaped): Changed clearsig format Tue Jul 7 18:56:19 1998 Werner Koch (wk@isil.d.shuttle.de) * armor.c (find_header): Now makes sure that there is only one empty line for clearsigs, as this is what OP now says. Mon Jul 6 13:09:07 1998 Werner Koch (wk@isil.d.shuttle.de) * g10.c (main): New option default-secret-key * getkey.c (get_seckey_byname): support for this option. Mon Jul 6 09:03:49 1998 Werner Koch (wk@isil.d.shuttle.de) * getkey.c (add_keyring): Keyrings are now added to end of the list of keyrings. The first added keyringwill be created. (add_secret_keyring): Likewise. * ringedit.c (add_keyblock_resource): Files are created here. * g10.c (aNOP): Removed * getkey.c (lookup): Add checking of usage for name lookups * packet.h (pubkey_usage): Add a field which may be used to store usage capabilities. * pkclist.c (build_pk_list): getkey now called with usage arg. * skclist.c (build_sk_list): Ditto. * sign.c (clearsign_file): Fixed "Hash:" headers Sat Jul 4 13:33:31 1998 Werner Koch (wk@isil.d.shuttle.de) * trustdb.c (list_ownertrust): New. * g10.c (aListOwnerTrust): New. * g10.c (def_pubkey_algo): Removed. * trustdb.c (verify_private_data): Removed and also the call to it. (sign_private_data): Removed. Fri Jul 3 13:26:10 1998 Werner Koch (wk@isil.d.shuttle.de) * g10.c (aEditKey): was aEditSig. Changed usage msg. * keyedit.c: Done some i18n stuff. * g10.c (do_not_use_RSA): New. * sign.c (do_sign): Add call to above function. * encode.c (write_pubkey_enc_from_list): Ditto. Thu Jul 2 21:01:25 1998 Werner Koch (wk@isil.d.shuttle.de) * parse-packet.c: Now is able sto store data of unknown algorithms. * free-packet.c: Support for this. * build-packet.c: Can write data of packet with unknown algos. Thu Jul 2 11:46:36 1998 Werner Koch (wk@isil.d.shuttle.de) * parse-packet.c (parse): fixed 4 byte length header Wed Jul 1 12:36:55 1998 Werner Koch (wk@isil.d.shuttle.de) * packet.h (new_ctb): New field for some packets * build-packet.c (build_packet): Support for new_ctb * parse-packet.c (parse): Ditto. Mon Jun 29 12:54:45 1998 Werner Koch (wk@isil.d.shuttle.de) * packet.h: changed all "_cert" to "_key", "subcert" to "subkey". * free-packet.c (free_packet): Removed memory leak for subkeys. Sun Jun 28 18:32:27 1998 Werner Koch (wk@isil.d.shuttle.de) * import.c (import_keys): Renamed from import_pubkeys. (import_secret_one): New. * g10.c (aExportSecret): New. * export.c (export_seckeys): New. * parse-packet.c (parse_certificate): Cleaned up. (parse_packet): Trust packets are now considered as unknown. (parse_pubkey_warning): New. Fri Jun 26 10:37:35 1998 Werner Koch (wk@isil.d.shuttle.de) * keygen.c (has_invalid_email_chars): New. Wed Jun 24 16:40:22 1998 Werner Koch (wk@isil.d.shuttle.de) * armor.c (armor_filter): Now creates valid onepass_sig packets with all detected hash algorithms. * mainproc.c (proc_plaintext): Now uses the hash algos as specified in the onepass_sig packets (if there are any) Mon Jun 22 11:54:08 1998 Werner Koch (wk@isil.d.shuttle.de) * plaintext.c (handle_plaintext): add arg to disable outout * mainproc.c (proc_plaintext): disable output when in sigs_only mode. Thu Jun 18 13:17:27 1998 Werner Koch (wk@isil.d.shuttle.de) * keygen.c: Removed all rsa packet stuff, chnaged defaults for key generation. Sun Jun 14 21:28:31 1998 Werner Koch (wk@isil.d.shuttle.de) * misc.c (checksum_u16): Fixed a stupid bug which caused a wrong checksum calculation for the secret key protection and add a backward compatibility option. * g10.c (main): Add option --emulate-checksum-bug. Thu Jun 11 13:26:44 1998 Werner Koch (wk@isil.d.shuttle.de) * packet.h: Major changes to the structure of public key material which is now stored in an array and not anaymore in a union of algorithm specific structures. These is needed to make the system more extendable and makes a lot of stuff much simpler. Changed all over the system. * dsa.c, rsa.c, elg.c: Removed. Wed Jun 10 07:22:02 1998 Werner Koch,mobil,,, (wk@tobold) * g10.c ("load-extension"): New option. Mon Jun 8 22:23:37 1998 Werner Koch (wk@isil.d.shuttle.de) * seckey-cert.c (do_check): Removed cipher constants (protect_secret_key): Ditto. Fri May 29 10:00:28 1998 Werner Koch (wk@isil.d.shuttle.de) * trustdb.c (query_trust_info): New. * keylist.c (list_one): Add output of trust info * mainproc (list_node): ditto. * g10.c (main): full trustdb init if -with-colons and any of the key list modes. Thu May 28 10:34:42 1998 Werner Koch (wk@isil.d.shuttle.de) * status.c (STATUS_RSA_OR_IDEA): New. * sig-check.c (check_signature): Output special status message. * pubkey-enc.c (get_session_key): Ditto. * mainproc.c (check_sig_and_print): Changed format of output. * passpharse.c (passphrase_to_dek): Likewise. Wed May 27 13:46:48 1998 Werner Koch (wk@isil.d.shuttle.de) * g10.c (aListSecretKeys): New option --list-secret-keys * keylist.c (std_key_list): Renamed to public_key_list. (secret_key_list): New (list_one, list_all): Add support for secret keys. * getkey.c (get_secret_keyring): New. * mainproc.c (list_node): Add option --with-colons for secret keys * sig-check.c (check_key_signature): detection of selfsigs * mainproc.c (list_node): fixed listing. * g10.c (aListSecretKeys): New option --always-trust * pkclist.c (do_we_trust): Override per option added * status.c (write_status_text): Add a prefix to every output line. Wed May 27 07:49:21 1998 Werner Koch (wk@isil.d.shuttle.de) * g10 (--compress-keys): New. * options.h (compress_keys): New. * export.c (export_pubkeys): Only compresses with the new option. Tue May 26 11:24:33 1998 Werner Koch (wk@isil.d.shuttle.de) * passphrase.c (get_last_passphrase): New (set_next_passphrase): New. (passphrase_to_dek): add support for the above functions. * keyedit.c (make_keysig_packet): Add sigclass 0x18, changed all callers due to a new argument. * keygen.c (write_keybinding): New (generate_subkeypair): Add functionality (ask_algo, ask_keysize, ask_valid_days): Broke out of generate_keypair (ask_user_id, ask_passphrase): Ditto. Thu May 21 11:26:13 1998 Werner Koch (wk@isil.d.shuttle.de) * g10.c,gpgd.c (main): Does now return an int, so that egcs does not complain. * armor.c (fake_packet): Removed erro message and add a noticed that this part should be fixed. * sign.c (sign_file): Compression now comes in front of encryption. * encode.c (encode_simple): Ditto. (encode_crypt): Ditto. Tue May 19 16:18:19 1998 Werner Koch (wk@isil.d.shuttle.de) * armor.c (fake_packet): Changed assertion to log_error Sat May 16 16:02:06 1998 Werner Koch (wk@isil.d.shuttle.de) * build-packet.c (build_packet): Add SUBKEY packets. Fri May 15 17:57:23 1998 Werner Koch (wk@isil.d.shuttle.de) * sign.c (hash_for): New and used in all places here. * main.h (DEFAULT_): new macros. * g10.c (opt.def_digest_algo): Now set to 0 * compress.c (init_compress): Add support for algo 1 * options.h (def_compress_algo): New * g10.c (main): New option --compress-algo Fri May 15 13:23:59 1998 Werner Koch (wk@isil.d.shuttle.de) * g10.c (print_mds): New feature to print only one hash, chnaged formatting. Thu May 14 15:36:24 1998 Werner Koch (wk@isil.d.shuttle.de) * misc.c (trap_unaligned) [__alpha__]: New * g10.c (trap_unaligned): Add call to this to track down SIGBUS on Alphas (to avoid the slow emulation code). Wed May 13 11:48:27 1998 Werner Koch (wk@isil.d.shuttle.de) * build-packet.c (do_signature): Support for v4 pakets. * keyedit.c (make_keysig_packet): Ditto. * build-packet.c (build_sig_subpkt_from_sig): New. (build_sig_subpkt): New. * elg.c (g10_elg_sign): removed keyid_from_skc. * dsa.c (g10_dsa_sign): Ditto. * rsa.c (g10_rsa_sign): Ditto. * keyedit.c (make_keysig_packet): Add call to keyid_from_skc * sign.c (clearsign_file): Support for v4 signatures. (sign_file): Ditto. Wed May 6 09:31:24 1998 Werner Koch (wk@isil.d.shuttle.de) * parse-packet.c (do_parse): add support for 5 byte length leader. (parse_subpkt): Ditto. * build-packet.c (write_new_header): Ditto. * packet.h (SIGSUBPKT_): New constants. * parse-packet.c (parse_sig_subpkt): Changed name, made global, and arg to return packet length, chnaged all callers Tue May 5 22:11:59 1998 Werner Koch (wk@isil.d.shuttle.de) * keygen.c (gen_dsa): New. * build_packet.c (do_secret_cert): Support for DSA Mon May 4 19:01:25 1998 Werner Koch (wk@isil.d.shuttle.de) * compress.c: doubled buffer sizes * parse-packet.c (do_plaintext): now uses iobuf_read/write. Mon May 4 09:35:53 1998 Werner Koch (wk@isil.d.shuttle.de) * seskey.c (encode_md_value): Add optional argument hash_algo, changed all callers. * passphrase.c (make_dek_from_passphrase): Removed * (get_passhrase_hash): Changed name to passphrase_to_dek, add arg, changed all callers. * all: Introduced the new ELG identifier and added support for the encryption only one (which is okay to use by GNUPG for signatures). Sun May 3 17:50:26 1998 Werner Koch (wk@isil.d.shuttle.de) * packet.h (PKT_OLD_COMMENT): New name for type 16. * parse-packet.c (parse_comment): Now uses type 61 Fri May 1 12:44:39 1998 Werner Koch,mobil,,, (wk@tobold) * packet.h (count): Chnaged s2k count from byte to u32. * seckey-cert.c (do_check): Changed s2k algo 3 to 4, changed reading of count. * build-packet.c (do_secret_cert): ditto. * parse-packet.c (parse_certificate): ditto. * parse-packet.c (parse_symkeyenc): New. * build-packet.c (do_symkey_enc): New. Thu Apr 30 16:33:34 1998 Werner Koch (wk@isil.d.shuttle.de) * sign.c (clearsign_file): Fixed "Hash: " armor line. Tue Apr 28 14:27:42 1998 Werner Koch (wk@isil.d.shuttle.de) * parse-packet.c (parse_subpkt): Some new types. Mon Apr 27 12:53:59 1998 Werner Koch (wk@isil.d.shuttle.de) * g10.c (main): Add option --skip-verify. * mainproc.c (check_sig_and_print): Ditto. * g10.c (print_mds): Add output for Tiger. * sign.c (sign_file): Now uses partial length headers if used in canonical textmode (kludge to fix a bug). * parse-packet.c (parse_certificate): Changed BLOWFISH id. * pubkey-enc.c (get_session_key): Ditto. * seskey.c (make_session_key): Ditto. * seckey-cert.c (protect_secret_key,do_check): Add BLOWFISH160. Fri Apr 24 17:38:48 1998 Werner Koch,mobil,,, (wk@tobold) * sig-check.c (check_key_signature): Add sig-class 0x14..0x17 * keyedit.c (sign-key): Some changes to start with support of the above new sig-classes. Wed Apr 22 09:01:57 1998 Werner Koch,mobil,,, (wk@tobold) * getkey.c (compare_name): add email matching Tue Apr 21 16:17:12 1998 Werner Koch,mobil,,, (wk@tobold) * armor.c (armor_filter): fixed missing last LF before CSUM. Thu Apr 9 11:35:22 1998 Werner Koch (wk@isil.d.shuttle.de) * seckey-cert.c (do_check): New; combines all the check functions into one. * sign.c: removed all key management functions * keyedit.c: New. Thu Apr 9 09:49:36 1998 Werner Koch (wk@isil.d.shuttle.de) * import.c (chk_self_sigs): Changed an error message. Wed Apr 8 16:19:39 1998 Werner Koch (wk@isil.d.shuttle.de) * packet.h: packet structs now uses structs from the pubkey, removed all copy operations from packet to pubkey structs. Wed Apr 8 13:40:33 1998 Werner Koch (wk@isil.d.shuttle.de) * trustdb.c (verify_own_certs): Fixed "public key not found". * getkey.c (key_byname): New, combines public and secret key search. * pkclist.c (build_pkc_list): Add new arg usage, changed all callers. * skclist.c (build_skc_list): Likewise. * ringedit.c (find_keyblock, keyring_search2): Removed. Wed Apr 8 09:47:21 1998 Werner Koch (wk@isil.d.shuttle.de) * sig-check.c (do_check): Applied small fix from Ulf Möller. Tue Apr 7 19:28:07 1998 Werner Koch (wk@isil.d.shuttle.de) * cipher.c, encr-data.c, seckey-cert.c: Now uses cipher_xxxx functions instead of blowfish_xxx or cast_xxx Tue Apr 7 11:04:02 1998 Werner Koch (wk@isil.d.shuttle.de) * Makefile.am (g10maint.o): Changed the way it is created. Mon Apr 6 11:17:08 1998 Werner Koch (wk@isil.d.shuttle.de) * misc.c: New. * keygen.c (checksum,checksum_u16,checksum_mpi): Moved to misc.c * seckey-cert.c: Kludge for wrong ELG checksum implementation. Sat Apr 4 20:07:01 1998 Werner Koch (wk@isil.d.shuttle.de) * cipher.c (cipher_filter): Support for CAST5 * encr-data.c (decode_filter): Ditto. (decrypt_data): Ditto. * seskey.c (make_session_key): Ditto. * seckey-cert.c (check_elg, check_dsa): Ditto, (protect_secret_key): Ditto. * pubkey-enc.c (get_session_key): Ditto. * passphrase.c (hash_passphrase): Ditto. Thu Apr 2 20:22:35 1998 Werner Koch (wk@isil.d.shuttle.de) * gpgd.c: New Thu Apr 2 10:38:16 1998 Werner Koch (wk@isil.d.shuttle.de) * keygen.c (generate_keypair): Add valid_days stuff. * trustdb.c (check_trust): Add check for valid_days. Wed Apr 1 16:15:58 1998 Werner Koch (wk@isil.d.shuttle.de) * keygen.c (generate_keypair): Addional question whether the selected large keysize is really needed. Wed Apr 1 15:56:33 1998 Werner Koch (wk@isil.d.shuttle.de) * seckey-cert.c (protect_secret_key): merged protect_xxx to here. Wed Apr 1 10:34:46 1998 Werner Koch (wk@isil.d.shuttle.de) * Makefile.am (g10maint.c): Changed creation rule, so that it works on FreeBSD (missing CFLAGS). * parse-packet.c (parse_subkey): Removed. Thu Mar 19 15:22:36 1998 Werner Koch (wk@isil.d.shuttle.de) * ringedit.c (keyring_enum): Fixed problem with reading too many packets. Add support to read secret keyrings. * getkey.c (scan_keyring): Removed (lookup): New to replace scan_keyring. (scan_secret_keyring): Removed. (lookup_skc): New. Wed Mar 18 11:47:34 1998 Werner Koch (wk@isil.d.shuttle.de) * ringedit.c (enum_keyblocks): New read mode 11. * keyid.c (elg_fingerprint_md): New and changed all other functions to call this if the packet version is 4 or above. Tue Mar 17 20:46:16 1998 Werner Koch (wk@isil.d.shuttle.de) * parse-packet.c (parse_certificate): Add listing support for subkeys. Tue Mar 17 20:32:22 1998 Werner Koch (wk@isil.d.shuttle.de) * armor.c (is_armored): Allow marker packet. Thu Mar 12 13:36:49 1998 Werner Koch (wk@isil.d.shuttle.de) * trustdb.c (check_trust): Checks timestamp of pubkey. * sig-check. (do_check): Compares timestamps. Tue Mar 10 17:01:56 1998 Werner Koch (wk@isil.d.shuttle.de) * g10.c (main): Add call to init_signals. * signal.c: New. Mon Mar 9 12:43:42 1998 Werner Koch (wk@isil.d.shuttle.de) * dsa.c: New * packet.h, free-packet.c, parse-packet.c : Add support for DSA * sig-check.c, getkey.c, keyid.c, ringedit.c: Ditto. * seckey-cert.c: Ditto. * packet.h : Moved .digest_algo of signature packets to outer structure. Changed all references Sun Mar 8 13:06:42 1998 Werner Koch (wk@isil.d.shuttle.de) * openfile.c : Support for stdout filename "-". * mainproc.c (check_sig_and_print): Enhanced status output: * status.c (write_status_text): New. Fri Mar 6 16:10:54 1998 Werner Koch (wk@isil.d.shuttle.de) * kbnode.c (clone_kbnode): Fixed private_flag. * mainproc.c (list_node): Output of string "Revoked" as user-id. Fri Mar 6 14:26:39 1998 Werner Koch (wk@isil.d.shuttle.de) * g10.c (main): Add userids to "-kv" and cleaned up this stuff. Fri Mar 6 12:45:58 1998 Werner Koch (wk@isil.d.shuttle.de) * g10.c (main): Changed semantics of the list-... commands and added a new one. Removed option "-d" * decrypt.c: New. * trustdb.c (init_trustdb): Autocreate directory only if it ends in "/.gnupg". Thu Mar 5 12:12:11 1998 Werner Koch (wk@isil.d.shuttle.de) * mainproc.c (do_proc_packets): New. Common part of proc_packet. (proc_signature_packets): special version to handle signature data. * verify.c: New. * g10.c (aVerify): New. * plaintext.c (hash_datafiles): New. * compress.c (handle_compressed): Add callback arg, changed caller. Thu Mar 5 10:20:06 1998 Werner Koch (wk@isil.d.shuttle.de) * g10.c: Is nom the common source for gpg and gpgm * g10maint.c: Removed * Makefile.am: Add rule to build g10maint.c Thu Mar 5 08:43:59 1998 Werner Koch (wk@isil.d.shuttle.de) * g10.c (main): Changed the way clear text sigs are faked. Wed Mar 4 19:47:37 1998 Werner Koch (wk@isil.d.shuttle.de) * g10maint.c (aMuttKeyList): New * keylist.c: New. Wed Mar 4 17:20:33 1998 Werner Koch (wk@isil.d.shuttle.de) * getkey.c (get_pubkey_byname): Kludge to allow 0x prefix. Tue Mar 3 13:46:55 1998 Werner Koch (wk@isil.d.shuttle.de) * g10maint.c (main): New option --gen-random. Tue Mar 3 09:50:08 1998 Werner Koch (wk@isil.d.shuttle.de) * g10.c (aDeleteSecretKey): New. (aEditSig): Add option "--edit-key" as synonym for "--edit-sig". (aDeleteSecretKey): New. * getkey.c (seckey_available): New. * sign.c (delete_key): Enhanced to delete secret keys, changed all callers. Mon Mar 2 21:23:48 1998 Werner Koch (wk@isil.d.shuttle.de) * pkc_list.c (build_pkc_list): Add interactive input of user ID. Mon Mar 2 20:54:05 1998 Werner Koch (wk@isil.d.shuttle.de) * pkclist.c (do_we_trust_pre): New. (add_ownertrust): Add message. * trustdb.c (enum_trust_web): Quick fix. Mon Mar 2 13:50:53 1998 Werner Koch (wk@isil.d.shuttle.de) * g10.c (main): New action aDeleteKey * sign.c (delete_key): New. Sun Mar 1 16:38:58 1998 Werner Koch (wk@isil.d.shuttle.de) * trustdb.c (do_check): No returns TRUST_UNDEFINED instead of eof error. Fri Feb 27 18:14:03 1998 Werner Koch (wk@isil.d.shuttle.de) * armor.c (find_header): Removed trailing CR on headers. Fri Feb 27 18:02:48 1998 Werner Koch (wk@isil.d.shuttle.de) * ringedit.c (keyring_search) [MINGW32]: Open and close file here because rename does not work on open files. Chnaged callers. Fri Feb 27 16:43:11 1998 Werner Koch (wk@isil.d.shuttle.de) * sig-check.c (do_check): Add an md_enable. * mainproc.c (do_check_sig): Use md_open in case of detached sig (proc_tree): Take detached sigs into account. Fri Feb 27 15:22:46 1998 Werner Koch (wk@isil.d.shuttle.de) * g10.c (main): Make use of GNUPGHOME envvar. * g10main.c (main): Ditto. Wed Feb 25 11:40:04 1998 Werner Koch (wk@isil.d.shuttle.de) * plaintext.c (ask_for_detached_datafile): add opt.verbose to info output. * openfile.c (open_sigfile): Try also name ending in ".asc" Wed Feb 25 08:41:00 1998 Werner Koch (wk@isil.d.shuttle.de) * keygen.c (generate_keypair): Fixed memory overflow. Tue Feb 24 15:51:55 1998 Werner Koch (wk@isil.d.shuttle.de) * parse-packet.c (parse_certificate): Support for S2K. * build-packet.c (do_secret_cert): Ditto. * keygen.c (gen_elg): Ditto. * seckey-cert.c (check_elg): Ditto (protect_elg): Ditto. * sign.c (chnage_passphrase): Ditto. * passphrase.c (get_passphrase_hash): Support for a salt and changed all callers. (make_dek_from_passphrase): Ditto. Tue Feb 24 12:30:56 1998 Werner Koch (wk@isil.d.shuttle.de) * build-packet.c (hash_public_cert): Disabled debug output. Fri Feb 20 17:22:28 1998 Werner Koch (wk@isil.d.shuttle.de) * trustdb.c (init_trustdb) [MINGW32]: Removed 2nd mkdir arg. (keyring_copy) [MINGW32]: Add a remove prior to the renames. Wed Feb 18 18:39:02 1998 Werner Koch (wk@isil.d.shuttle.de) * Makefile.am (OMIT_DEPENDENCIES): New. * rsa.c: Replaced log_bug by BUG. Wed Feb 18 13:35:58 1998 Werner Koch (wk@isil.d.shuttle.de) * mainproc.c (do_check_sig): Now uses hash_public_cert. * parse-packet.c (parse_certificate): Removed hashing. * packet.h (public_cert): Removed hash variable. * free-packet.c (copy_public_cert, free_public_cert): Likewise. * sig-check.c (check_key_signatures): Changed semantics. Wed Feb 18 12:11:28 1998 Werner Koch (wk@isil.d.shuttle.de) * trustdb.c (do_check): Add handling for revocation certificates. (build_sigrecs): Ditto. (check_sigs): Ditto. Wed Feb 18 09:31:04 1998 Werner Koch (wk@isil.d.shuttle.de) * armor.c (armor_filter): Add afx->hdrlines. * revoke.c (gen_revoke): Add comment line. * dearmor.c (enarmor_file): Ditto. * sig-check.c (check_key_signature): Add handling for class 0x20. * mainproc.c : Ditto. Tue Feb 17 21:24:17 1998 Werner Koch (wk@isil.d.shuttle.de) * armor.c : Add header lines "...ARMORED FILE .." * dearmor.c (enarmor_file): New. * g10maint.c (main): New option "--enarmor" Tue Feb 17 19:03:33 1998 Werner Koch (wk@isil.d.shuttle.de) * mainproc.c : Changed a lot, because the packets are now stored a simple linlked list and not anymore in a complicatd tree structure. Tue Feb 17 10:14:48 1998 Werner Koch (wk@isil.d.shuttle.de) * free_packet.c (cmp_public_certs): New. (cmp_user_ids): New. * kbnode.c (clone_kbnode): New. (release_kbnode): Add clone support. * ringedit.c (find_keyblock_bypkc): New. * sign.c (remove_keysigs): Self signatures are now skipped, changed arguments and all callers. * import.c : Add functionality. Tue Feb 17 09:31:40 1998 Werner Koch (wk@isil.d.shuttle.de) * options.h (homedir): New option. * g10.c, g10maint.c, getkey.c, keygen.c, trustdb.c (opt.homedir): New. * trustdb.c (init_trustdb): mkdir for hoem directory (sign_private_data): Renamed "sig" to "g10.sig" Mon Feb 16 20:02:03 1998 Werner Koch (wk@isil.d.shuttle.de) * kbnode.c (commit_kbnode): New. (delete_kbnode): removed unused first arg. Changed all Callers. * ringedit.c (keyblock_resource_name): New. (get_keyblock_handle): NULL for filename returns default resource. Mon Feb 16 19:38:48 1998 Werner Koch (wk@isil.d.shuttle.de) * sig-check.s (check_key_signature): Now uses the supplied public key to check the signature and not any more the one from the getkey.c (do_check): New. (check_signature): Most work moved to do_check. Mon Feb 16 14:48:57 1998 Werner Koch (wk@isil.d.shuttle.de) * armor.c (find_header): Fixed another bug. Mon Feb 16 12:18:34 1998 Werner Koch (wk@isil.d.shuttle.de) * getkey.c (scan_keyring): Add handling of compressed keyrings. Mon Feb 16 10:44:51 1998 Werner Koch (wk@isil.d.shuttle.de) * g10.c, g10maint.c (strusage): Rewrote. (build_list): New Mon Feb 16 08:58:41 1998 Werner Koch (wk@isil.d.shuttle.de) * armor.c (use_armor): New. Sat Feb 14 14:30:57 1998 Werner Koch (wk@isil.d.shuttle.de) * mainproc.c (proc_tree): Sigclass fix. Sat Feb 14 14:16:33 1998 Werner Koch (wk@isil.d.shuttle.de) * armor.c (armor_filter): Changed version and comment string. * encode.c, sign.c, keygen.c: Changed all comment packet strings. Sat Feb 14 12:39:24 1998 Werner Koch (wk@isil.d.shuttle.de) * g10.c (aGenRevoke): New command. * revoke.c: New. * sign.c (make_keysig_packet): Add support for sigclass 0x20. Fri Feb 13 20:18:14 1998 Werner Koch (wk@isil.d.shuttle.de) * ringedit.c (enum_keyblocks, keyring_enum): New. Fri Feb 13 19:33:40 1998 Werner Koch (wk@isil.d.shuttle.de) * export.c: Add functionality. * keygen.c (generate_keypair): Moved the leading comment behind the key packet. * kbnode.c (walk_kbnode): Fixed. * g10.c (main): listing armored keys now work. Fri Feb 13 16:17:43 1998 Werner Koch (wk@isil.d.shuttle.de) * parse-packet.c (parse_publickey, parse_signature): Fixed calls to mpi_read used for ELG b. Fri Feb 13 15:13:23 1998 Werner Koch (wk@isil.d.shuttle.de) * g10.c (main): changed formatting of help output. Thu Feb 12 22:24:42 1998 Werner Koch (wk@frodo) * pubkey-enc.c (get_session_key): rewritten diff --git a/g10/encode.c b/g10/encode.c index 7daec6c64..147607b31 100644 --- a/g10/encode.c +++ b/g10/encode.c @@ -1,450 +1,450 @@ /* encode.c - encode data * Copyright (C) 1998 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 "options.h" #include "packet.h" #include "errors.h" #include "iobuf.h" #include "keydb.h" #include "memory.h" #include "util.h" #include "main.h" #include "filter.h" #include "trustdb.h" #include "i18n.h" static int encode_simple( const char *filename, int mode ); static int write_pubkey_enc_from_list( PK_LIST pk_list, DEK *dek, IOBUF out ); /**************** * Encode FILENAME with only the symmetric cipher. Take input from * stdin if FILENAME is NULL. */ int encode_symmetric( const char *filename ) { return encode_simple( filename, 1 ); } /**************** * Encode FILENAME as a literal data packet only. Take input from * stdin if FILENAME is NULL. */ int encode_store( const char *filename ) { return encode_simple( filename, 0 ); } static int encode_simple( const char *filename, int mode ) { IOBUF inp, out; PACKET pkt; PKT_plaintext *pt; STRING2KEY *s2k = NULL; int rc = 0; u32 filesize; cipher_filter_context_t cfx; armor_filter_context_t afx; compress_filter_context_t zfx; text_filter_context_t tfx; int do_compress = opt.compress && !opt.rfc1991; memset( &cfx, 0, sizeof cfx); memset( &afx, 0, sizeof afx); memset( &zfx, 0, sizeof zfx); memset( &tfx, 0, sizeof tfx); init_packet(&pkt); /* prepare iobufs */ if( !(inp = iobuf_open(filename)) ) { log_error(_("%s: can't open: %s\n"), filename? filename: "[stdin]", strerror(errno) ); return G10ERR_OPEN_FILE; } if( opt.textmode ) iobuf_push_filter( inp, text_filter, &tfx ); cfx.dek = NULL; if( mode ) { s2k = m_alloc_clear( sizeof *s2k ); s2k->mode = opt.rfc1991? 0:opt.s2k_mode; s2k->hash_algo = opt.def_digest_algo ? opt.def_digest_algo : opt.s2k_digest_algo; - cfx.dek = passphrase_to_dek( NULL, + cfx.dek = passphrase_to_dek( NULL, 0, opt.def_cipher_algo ? opt.def_cipher_algo : opt.s2k_cipher_algo , s2k, 2 ); if( !cfx.dek || !cfx.dek->keylen ) { rc = G10ERR_PASSPHRASE; m_free(cfx.dek); m_free(s2k); iobuf_close(inp); log_error(_("error creating passphrase: %s\n"), g10_errstr(rc) ); return rc; } } if( (rc = open_outfile( filename, opt.armor? 1:0, &out )) ) { iobuf_close(inp); m_free(cfx.dek); m_free(s2k); return rc; } if( opt.armor ) iobuf_push_filter( out, armor_filter, &afx ); #ifdef ENABLE_COMMENT_PACKETS else { write_comment( out, "#created by GNUPG v" VERSION " (" PRINTABLE_OS_NAME ")"); if( opt.comment_string ) write_comment( out, opt.comment_string ); } #endif if( s2k && !opt.rfc1991 ) { PKT_symkey_enc *enc = m_alloc_clear( sizeof *enc ); enc->version = 4; enc->cipher_algo = cfx.dek->algo; enc->s2k = *s2k; pkt.pkttype = PKT_SYMKEY_ENC; pkt.pkt.symkey_enc = enc; if( (rc = build_packet( out, &pkt )) ) log_error("build symkey packet failed: %s\n", g10_errstr(rc) ); m_free(enc); } /* setup the inner packet */ if( filename || opt.set_filename ) { char *s = make_basename( opt.set_filename ? opt.set_filename : filename ); pt = m_alloc( sizeof *pt + strlen(s) - 1 ); pt->namelen = strlen(s); memcpy(pt->name, s, pt->namelen ); m_free(s); } else { /* no filename */ pt = m_alloc( sizeof *pt - 1 ); pt->namelen = 0; } /* pgp5 has problems to decrypt symmetrically encrypted data from * GnuPG if the filelength is in the inner packet. It works * when only partial length headers are use. Until we have * tracked this problem down. We use this temporary fix * (fixme: remove the && !mode ) */ if( filename && !opt.textmode && !mode ) { if( !(filesize = iobuf_get_filelength(inp)) ) log_info(_("%s: WARNING: empty file\n"), filename ); } else filesize = 0; /* stdin */ pt->timestamp = make_timestamp(); pt->mode = opt.textmode? 't' : 'b'; pt->len = filesize; pt->buf = inp; pkt.pkttype = PKT_PLAINTEXT; pkt.pkt.plaintext = pt; cfx.datalen = filesize && !do_compress ? calc_packet_length( &pkt ) : 0; /* register the cipher filter */ if( mode ) iobuf_push_filter( out, cipher_filter, &cfx ); /* register the compress filter */ if( do_compress ) iobuf_push_filter( out, compress_filter, &zfx ); /* do the work */ if( (rc = build_packet( out, &pkt )) ) log_error("build_packet failed: %s\n", g10_errstr(rc) ); /* finish the stuff */ iobuf_close(inp); iobuf_close(out); /* fixme: check returncode */ pt->buf = NULL; free_packet(&pkt); m_free(cfx.dek); m_free(s2k); return rc; } /**************** * Encrypt the file with the given userids (or ask if none * is supplied). */ int encode_crypt( const char *filename, STRLIST remusr ) { IOBUF inp = NULL, out = NULL; PACKET pkt; PKT_plaintext *pt = NULL; int rc = 0; u32 filesize; cipher_filter_context_t cfx; armor_filter_context_t afx; compress_filter_context_t zfx; text_filter_context_t tfx; PK_LIST pk_list; int do_compress = opt.compress && !opt.rfc1991; memset( &cfx, 0, sizeof cfx); memset( &afx, 0, sizeof afx); memset( &zfx, 0, sizeof zfx); memset( &tfx, 0, sizeof tfx); init_packet(&pkt); if( (rc=build_pk_list( remusr, &pk_list, PUBKEY_USAGE_ENC)) ) return rc; /* prepare iobufs */ if( !(inp = iobuf_open(filename)) ) { log_error(_("can't open %s: %s\n"), filename? filename: "[stdin]", strerror(errno) ); rc = G10ERR_OPEN_FILE; goto leave; } else if( opt.verbose ) log_info(_("reading from `%s'\n"), filename? filename: "[stdin]"); if( opt.textmode ) iobuf_push_filter( inp, text_filter, &tfx ); if( (rc = open_outfile( filename, opt.armor? 1:0, &out )) ) goto leave; if( opt.armor ) iobuf_push_filter( out, armor_filter, &afx ); #ifdef ENABLE_COMMENT_PACKETS else { write_comment( out, "#created by GNUPG v" VERSION " (" PRINTABLE_OS_NAME ")"); if( opt.comment_string ) write_comment( out, opt.comment_string ); } #endif /* create a session key */ cfx.dek = m_alloc_secure( sizeof *cfx.dek ); if( !opt.def_cipher_algo ) { /* try to get it from the prefs */ cfx.dek->algo = select_algo_from_prefs( pk_list, PREFTYPE_SYM ); if( cfx.dek->algo == -1 ) cfx.dek->algo = DEFAULT_CIPHER_ALGO; } else cfx.dek->algo = opt.def_cipher_algo; make_session_key( cfx.dek ); if( DBG_CIPHER ) log_hexdump("DEK is: ", cfx.dek->key, cfx.dek->keylen ); rc = write_pubkey_enc_from_list( pk_list, cfx.dek, out ); if( rc ) goto leave; /* setup the inner packet */ if( filename || opt.set_filename ) { char *s = make_basename( opt.set_filename ? opt.set_filename : filename ); pt = m_alloc( sizeof *pt + strlen(s) - 1 ); pt->namelen = strlen(s); memcpy(pt->name, s, pt->namelen ); m_free(s); } else { /* no filename */ pt = m_alloc( sizeof *pt - 1 ); pt->namelen = 0; } if( filename && !opt.textmode ) { if( !(filesize = iobuf_get_filelength(inp)) ) log_info(_("%s: WARNING: empty file\n"), filename ); } else filesize = 0; /* stdin */ pt->timestamp = make_timestamp(); pt->mode = opt.textmode ? 't' : 'b'; pt->len = filesize; pt->new_ctb = !pt->len && !opt.rfc1991; pt->buf = inp; pkt.pkttype = PKT_PLAINTEXT; pkt.pkt.plaintext = pt; cfx.datalen = filesize && !do_compress? calc_packet_length( &pkt ) : 0; /* register the cipher filter */ iobuf_push_filter( out, cipher_filter, &cfx ); /* register the compress filter */ if( do_compress ) { int compr_algo = select_algo_from_prefs( pk_list, PREFTYPE_COMPR ); if( !compr_algo ) ; /* don't use compression */ else { if( compr_algo == 1 ) zfx.algo = 1; /* default is 2 */ iobuf_push_filter( out, compress_filter, &zfx ); } } /* do the work */ if( (rc = build_packet( out, &pkt )) ) log_error("build_packet failed: %s\n", g10_errstr(rc) ); /* finish the stuff */ leave: iobuf_close(inp); if( rc ) iobuf_cancel(out); else iobuf_close(out); /* fixme: check returncode */ if( pt ) pt->buf = NULL; free_packet(&pkt); m_free(cfx.dek); release_pk_list( pk_list ); return rc; } /**************** * Filter to do a complete public key encryption. */ int encrypt_filter( void *opaque, int control, IOBUF a, byte *buf, size_t *ret_len) { size_t size = *ret_len; encrypt_filter_context_t *efx = opaque; int rc=0; if( control == IOBUFCTRL_UNDERFLOW ) { /* decrypt */ BUG(); /* not used */ } else if( control == IOBUFCTRL_FLUSH ) { /* encrypt */ if( !efx->header_okay ) { efx->cfx.dek = m_alloc_secure( sizeof *efx->cfx.dek ); if( !opt.def_cipher_algo ) { /* try to get it from the prefs */ efx->cfx.dek->algo = select_algo_from_prefs( efx->pk_list, PREFTYPE_SYM ); if( efx->cfx.dek->algo == -1 ) efx->cfx.dek->algo = DEFAULT_CIPHER_ALGO; } else efx->cfx.dek->algo = opt.def_cipher_algo; make_session_key( efx->cfx.dek ); if( DBG_CIPHER ) log_hexdump("DEK is: ", efx->cfx.dek->key, efx->cfx.dek->keylen ); rc = write_pubkey_enc_from_list( efx->pk_list, efx->cfx.dek, a ); if( rc ) return rc; iobuf_push_filter( a, cipher_filter, &efx->cfx ); efx->header_okay = 1; } rc = iobuf_write( a, buf, size ); } else if( control == IOBUFCTRL_FREE ) { } else if( control == IOBUFCTRL_DESC ) { *(char**)buf = "encrypt_filter"; } return rc; } /**************** * Write pubkey-enc packets from the list of PKs to OUT. */ static int write_pubkey_enc_from_list( PK_LIST pk_list, DEK *dek, IOBUF out ) { PACKET pkt; PKT_public_key *pk; PKT_pubkey_enc *enc; int rc; for( ; pk_list; pk_list = pk_list->next ) { MPI frame; pk = pk_list->pk; print_pubkey_algo_note( pk->pubkey_algo ); enc = m_alloc_clear( sizeof *enc ); enc->pubkey_algo = pk->pubkey_algo; keyid_from_pk( pk, enc->keyid ); enc->throw_keyid = opt.throw_keyid; /* Okay, what's going on: We have the session key somewhere in * the structure DEK and want to encode this session key in * an integer value of n bits. pubkey_nbits gives us the * number of bits we have to use. We then encode the session * key in some way and we get it back in the big intger value * FRAME. Then we use FRAME, the public key PK->PKEY and the * algorithm number PK->PUBKEY_ALGO and pass it to pubkey_encrypt * which returns the encrypted value in the array ENC->DATA. * This array has a size which depends on the used algorithm * (e.g. 2 for ElGamal). We don't need frame anymore because we * have everything now in enc->data which is the passed to * build_packet() */ frame = encode_session_key( dek, pubkey_nbits( pk->pubkey_algo, pk->pkey ) ); rc = pubkey_encrypt( pk->pubkey_algo, enc->data, frame, pk->pkey ); mpi_free( frame ); if( rc ) log_error("pubkey_encrypt failed: %s\n", g10_errstr(rc) ); else { if( opt.verbose ) { char *ustr = get_user_id_string( enc->keyid ); log_info(_("%s/%s encrypted for: %s\n"), pubkey_algo_to_string(enc->pubkey_algo), cipher_algo_to_string(dek->algo), ustr ); m_free(ustr); } /* and write it */ init_packet(&pkt); pkt.pkttype = PKT_PUBKEY_ENC; pkt.pkt.pubkey_enc = enc; rc = build_packet( out, &pkt ); if( rc ) log_error("build_packet(pubkey_enc) failed: %s\n", g10_errstr(rc)); } free_pubkey_enc(enc); if( rc ) return rc; } return 0; } diff --git a/g10/g10.c b/g10/g10.c index 94aa2808f..66a91bf94 100644 --- a/g10/g10.c +++ b/g10/g10.c @@ -1,1511 +1,1515 @@ /* g10.c - The GnuPG utility (main for gpg) * Copyright (C) 1998, 1999 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 #define MAINTAINER_OPTIONS #include "packet.h" #include "iobuf.h" #include "memory.h" #include "util.h" #include "main.h" #include "options.h" #include "keydb.h" #include "trustdb.h" #include "mpi.h" #include "cipher.h" #include "filter.h" #include "ttyio.h" #include "i18n.h" #include "status.h" #include "g10defs.h" #include "hkp.h" enum cmd_and_opt_values { aNull = 0, oArmor = 'a', aDetachedSign = 'b', aSym = 'c', aDecrypt = 'd', aEncr = 'e', oInteractive = 'i', oKOption = 'k', oDryRun = 'n', oOutput = 'o', oQuiet = 'q', oRecipient = 'r', aSign = 's', oTextmodeShort= 't', oUser = 'u', oVerbose = 'v', oCompress = 'z', oNotation = 'N', oBatch = 500, aClearsign, aStore, aKeygen, aSignEncr, aSignKey, aListPackets, aEditKey, aDeleteKey, aDeleteSecretKey, aKMode, aKModeC, aImport, aFastImport, aVerify, aListKeys, aListSigs, aListSecretKeys, aSendKeys, aRecvKeys, aExport, aExportAll, aExportSecret, aCheckKeys, aGenRevoke, aPrimegen, aPrintMD, aPrintMDs, aCheckTrustDB, aUpdateTrustDB, aFixTrustDB, aListTrustDB, aListTrustPath, aExportOwnerTrust, aImportOwnerTrust, aDeArmor, aEnArmor, aGenRandom, oTextmode, oFingerprint, oAnswerYes, oAnswerNo, oKeyring, oSecretKeyring, oDefaultKey, oOptions, oDebug, oDebugAll, oStatusFD, oNoComment, oNoVersion, oEmitVersion, oCompletesNeeded, oMarginalsNeeded, oMaxCertDepth, oLoadExtension, oRFC1991, oOpenPGP, oCipherAlgo, oDigestAlgo, oCompressAlgo, oPasswdFD, oQuickRandom, oNoVerbose, oTrustDBName, oNoSecmemWarn, oNoArmor, oNoDefKeyring, oNoGreeting, oNoTTY, oNoOptions, oNoBatch, oHomedir, oWithColons, oWithKeyData, oSkipVerify, oCompressKeys, oCompressSigs, oAlwaysTrust, oEmuChecksumBug, oRunAsShmCP, oSetFilename, oSetPolicyURL, oUseEmbeddedFilename, oComment, oDefaultComment, oThrowKeyid, oForceV3Sigs, oForceMDC, oS2KMode, oS2KDigest, oS2KCipher, oCharset, oNotDashEscaped, oEscapeFrom, oLockOnce, oLockMultiple, oKeyServer, oEncryptTo, oNoEncryptTo, oLoggerFD, + oUtf8Strings, + oNoUtf8Strings, aTest }; static ARGPARSE_OPTS opts[] = { { 300, NULL, 0, N_("@Commands:\n ") }, { aSign, "sign", 256, N_("|[file]|make a signature")}, { aClearsign, "clearsign", 256, N_("|[file]|make a clear text signature") }, { aDetachedSign, "detach-sign", 256, N_("make a detached signature")}, { aEncr, "encrypt", 256, N_("encrypt data")}, { aSym, "symmetric", 256, N_("encryption only with symmetric cipher")}, { aStore, "store", 256, N_("store only")}, { aDecrypt, "decrypt", 256, N_("decrypt data (default)")}, { aVerify, "verify" , 256, N_("verify a signature")}, { aListKeys, "list-keys", 256, N_("list keys")}, { aListKeys, "list-public-keys", 256, "@" }, { aListSigs, "list-sigs", 256, N_("list keys and signatures")}, { aCheckKeys, "check-sigs",256, N_("check key signatures")}, { oFingerprint, "fingerprint", 256, N_("list keys and fingerprints")}, { aListSecretKeys, "list-secret-keys", 256, N_("list secret keys")}, { aKeygen, "gen-key", 256, N_("generate a new key pair")}, { aDeleteKey, "delete-key",256, N_("remove key from the public keyring")}, { aEditKey, "edit-key" ,256, N_("sign or edit a key")}, { aGenRevoke, "gen-revoke",256, N_("generate a revocation certificate")}, { aExport, "export" , 256, N_("export keys") }, { aSendKeys, "send-keys" , 256, N_("export keys to a key server") }, { aRecvKeys, "recv-keys" , 256, N_("import keys from a key server") }, { aExportAll, "export-all" , 256, "@" }, { aExportSecret, "export-secret-keys" , 256, "@" }, { aImport, "import", 256 , N_("import/merge keys")}, { aFastImport, "fast-import", 256 , "@"}, { aListPackets, "list-packets",256,N_("list only the sequence of packets")}, { aExportOwnerTrust, "export-ownertrust", 256, N_("export the ownertrust values")}, { aImportOwnerTrust, "import-ownertrust", 256 , N_("import ownertrust values")}, { aUpdateTrustDB, "update-trustdb",0 , N_("|[NAMES]|update the trust database")}, { aCheckTrustDB, "check-trustdb",0 , N_("|[NAMES]|check the trust database")}, { aFixTrustDB, "fix-trustdb",0 , N_("fix a corrupted trust database")}, { aDeArmor, "dearmor", 256, N_("De-Armor a file or stdin") }, { aEnArmor, "enarmor", 256, N_("En-Armor a file or stdin") }, { aPrintMD, "print-md" , 256, N_("|algo [files]|print message digests")}, { aPrintMDs, "print-mds" , 256, N_("print all message digests")}, #ifdef MAINTAINER_OPTIONS { aPrimegen, "gen-prime" , 256, "@" }, { aGenRandom, "gen-random" , 256, "@" }, #endif { 301, NULL, 0, N_("@\nOptions:\n ") }, { oArmor, "armor", 0, N_("create ascii armored output")}, { oRecipient, "recipient", 2, N_("|NAME|encrypt for NAME")}, { oRecipient, "remote-user", 2, "@"}, /* old option name */ { oEncryptTo, "encrypt-to", 2, "@" }, { oNoEncryptTo, "no-encrypt-to", 0, "@" }, { oUser, "local-user",2, N_("use this user-id to sign or decrypt")}, { oCompress, NULL, 1, N_("|N|set compress level N (0 disables)") }, { oTextmodeShort, NULL, 0, "@"}, { oTextmode, "textmode", 0, N_("use canonical text mode")}, { oOutput, "output", 2, N_("use as output file")}, { oVerbose, "verbose", 0, N_("verbose") }, { oQuiet, "quiet", 0, N_("be somewhat more quiet") }, { oNoTTY, "no-tty", 0, N_("don't use the terminal at all") }, { oForceV3Sigs, "force-v3-sigs", 0, N_("force v3 signatures") }, { oForceMDC, "force-mdc", 0, N_("always use a MDC for encryption") }, { oDryRun, "dry-run", 0, N_("do not make any changes") }, /*{ oInteractive, "interactive", 0, N_("prompt before overwriting") }, */ { oBatch, "batch", 0, N_("batch mode: never ask")}, { oAnswerYes, "yes", 0, N_("assume yes on most questions")}, { oAnswerNo, "no", 0, N_("assume no on most questions")}, { oKeyring, "keyring" ,2, N_("add this keyring to the list of keyrings")}, { oSecretKeyring, "secret-keyring" ,2, N_("add this secret keyring to the list")}, { oDefaultKey, "default-key" ,2, N_("|NAME|use NAME as default secret key")}, { oKeyServer, "keyserver",2, N_("|HOST|use this keyserver to lookup keys")}, { oCharset, "charset" , 2, N_("|NAME|set terminal charset to NAME") }, { oOptions, "options" , 2, N_("read options from file")}, { oDebug, "debug" ,4|16, N_("set debugging flags")}, { oDebugAll, "debug-all" ,0, N_("enable full debugging")}, { oStatusFD, "status-fd" ,1, N_("|FD|write status info to this FD") }, { oNoComment, "no-comment", 0, N_("do not write comment packets")}, { oCompletesNeeded, "completes-needed", 1, N_("(default is 1)")}, { oMarginalsNeeded, "marginals-needed", 1, N_("(default is 3)")}, { oMaxCertDepth, "max-cert-depth", 1, "@" }, { oLoadExtension, "load-extension" ,2, N_("|FILE|load extension module FILE")}, { oRFC1991, "rfc1991", 0, N_("emulate the mode described in RFC1991")}, { oOpenPGP, "openpgp", 0, N_("set all packet, cipher and digest options to OpenPGP behavior")}, { oS2KMode, "s2k-mode", 1, N_("|N|use passphrase mode N")}, { oS2KDigest, "s2k-digest-algo",2, N_("|NAME|use message digest algorithm NAME for passphrases")}, { oS2KCipher, "s2k-cipher-algo",2, N_("|NAME|use cipher algorithm NAME for passphrases")}, { oCipherAlgo, "cipher-algo", 2 , N_("|NAME|use cipher algorithm NAME")}, { oDigestAlgo, "digest-algo", 2 , N_("|NAME|use message digest algorithm NAME")}, { oCompressAlgo, "compress-algo", 1 , N_("|N|use compress algorithm N")}, { oThrowKeyid, "throw-keyid", 0, N_("throw keyid field of encrypted packets")}, { oNotation, "notation-data", 2, N_("|NAME=VALUE|use this notation data")}, { 302, NULL, 0, N_("@\nExamples:\n\n" " -se -r Bob [file] sign and encrypt for user Bob\n" " --clearsign [file] make a clear text signature\n" " --detach-sign [file] make a detached signature\n" " --list-keys [names] show keys\n" " --fingerprint [names] show fingerprints\n" ) }, /* hidden options */ { aExportOwnerTrust, "list-ownertrust",0 , "@"}, /* alias */ { aListTrustDB, "list-trustdb",0 , "@"}, { aListTrustPath, "list-trust-path",0, "@"}, { oKOption, NULL, 0, "@"}, { oPasswdFD, "passphrase-fd",1, "@" }, { aSignKey, "sign-key" ,256, "@" }, /* alias for edit-key */ { aDeleteSecretKey, "delete-secret-key",0, "@" }, { oQuickRandom, "quick-random", 0, "@"}, { oNoVerbose, "no-verbose", 0, "@"}, { oTrustDBName, "trustdb-name", 2, "@" }, { oNoSecmemWarn, "no-secmem-warning", 0, "@" }, /* used only by regression tests */ { oNoArmor, "no-armor", 0, "@"}, { oNoDefKeyring, "no-default-keyring", 0, "@" }, { oNoGreeting, "no-greeting", 0, "@" }, { oNoOptions, "no-options", 0, "@" }, /* shortcut for --options /dev/null */ { oHomedir, "homedir", 2, "@" }, /* defaults to "~/.gnupg" */ { oNoBatch, "no-batch", 0, "@" }, { oWithColons, "with-colons", 0, "@"}, { oWithKeyData,"with-key-data", 0, "@"}, { aListKeys, "list-key", 0, "@" }, /* alias */ { aListSigs, "list-sig", 0, "@" }, /* alias */ { aCheckKeys, "check-sig",0, "@" }, /* alias */ { oSkipVerify, "skip-verify",0, "@" }, { oCompressKeys, "compress-keys",0, "@"}, { oCompressSigs, "compress-sigs",0, "@"}, { oAlwaysTrust, "always-trust", 0, "@"}, { oEmuChecksumBug, "emulate-checksum-bug", 0, "@"}, { oRunAsShmCP, "run-as-shm-coprocess", 4, "@" }, { oSetFilename, "set-filename", 2, "@" }, { oSetPolicyURL, "set-policy-url", 2, "@" }, { oComment, "comment", 2, "@" }, { oDefaultComment, "default-comment", 0, "@" }, { oNoVersion, "no-version", 0, "@"}, { oEmitVersion, "emit-version", 0, "@"}, { oNotDashEscaped, "not-dash-escaped", 0, "@" }, { oEscapeFrom, "escape-from-lines", 0, "@" }, { oLockOnce, "lock-once", 0, "@" }, { oLockMultiple, "lock-multiple", 0, "@" }, { oLoggerFD, "logger-fd",1, "@" }, { oUseEmbeddedFilename, "use-embedded-filename", 0, "@" }, + { oUtf8Strings, "utf8-strings", 0, "@" }, + { oNoUtf8Strings, "no-utf8-strings", 0, "@" }, {0} }; int g10_errors_seen = 0; - +static int utf8_strings = 0; static int maybe_setuid = 1; static char *build_list( const char *text, const char *(*mapf)(int), int (*chkf)(int) ); static void set_cmd( enum cmd_and_opt_values *ret_cmd, enum cmd_and_opt_values new_cmd ); static void print_hex( byte *p, size_t n ); static void print_mds( const char *fname, int algo ); static void add_notation_data( const char *string ); static int check_policy_url( const char *s ); const char * strusage( int level ) { static char *digests, *pubkeys, *ciphers; const char *p; switch( level ) { case 11: p = "gpg (GnuPG)"; break; case 13: p = VERSION; break; case 17: p = PRINTABLE_OS_NAME; break; case 19: p = _("Please report bugs to .\n"); break; case 1: case 40: p = _("Usage: gpg [options] [files] (-h for help)"); break; case 41: p = _("Syntax: gpg [options] [files]\n" "sign, check, encrypt or decrypt\n" "default operation depends on the input data\n"); break; case 31: p = _("\nSupported algorithms:\n"); break; case 32: if( !ciphers ) ciphers = build_list("Cipher: ", cipher_algo_to_string, check_cipher_algo ); p = ciphers; break; case 33: if( !pubkeys ) pubkeys = build_list("Pubkey: ", pubkey_algo_to_string, check_pubkey_algo ); p = pubkeys; break; case 34: if( !digests ) digests = build_list("Hash: ", digest_algo_to_string, check_digest_algo ); p = digests; break; default: p = default_strusage(level); } return p; } static char * build_list( const char *text, const char * (*mapf)(int), int (*chkf)(int) ) { int i; const char *s; size_t n=strlen(text)+2; char *list, *p; if( maybe_setuid ) secmem_init( 0 ); /* drop setuid */ for(i=1; i < 110; i++ ) if( !chkf(i) && (s=mapf(i)) ) n += strlen(s) + 2; list = m_alloc( 21 + n ); *list = 0; for(p=NULL, i=1; i < 110; i++ ) { if( !chkf(i) && (s=mapf(i)) ) { if( !p ) p = stpcpy( list, text ); else p = stpcpy( p, ", "); p = stpcpy(p, s ); } } if( p ) p = stpcpy(p, "\n" ); return list; } static void i18n_init(void) { #ifdef ENABLE_NLS #ifdef HAVE_LC_MESSAGES setlocale( LC_TIME, "" ); setlocale( LC_MESSAGES, "" ); #else setlocale( LC_ALL, "" ); #endif bindtextdomain( PACKAGE, G10_LOCALEDIR ); textdomain( PACKAGE ); #endif } static void wrong_args( const char *text) { fputs(_("usage: gpg [options] "),stderr); fputs(text,stderr); putc('\n',stderr); g10_exit(2); } static void set_debug(void) { if( opt.debug & DBG_MEMORY_VALUE ) memory_debug_mode = 1; if( opt.debug & DBG_MEMSTAT_VALUE ) memory_stat_debug_mode = 1; if( opt.debug & DBG_MPI_VALUE ) mpi_debug_mode = 1; if( opt.debug & DBG_CIPHER_VALUE ) g10c_debug_mode = 1; if( opt.debug & DBG_IOBUF_VALUE ) iobuf_debug_mode = 1; } static void set_cmd( enum cmd_and_opt_values *ret_cmd, enum cmd_and_opt_values new_cmd ) { enum cmd_and_opt_values cmd = *ret_cmd; if( !cmd || cmd == new_cmd ) cmd = new_cmd; else if( cmd == aSign && new_cmd == aEncr ) cmd = aSignEncr; else if( cmd == aEncr && new_cmd == aSign ) cmd = aSignEncr; else if( cmd == aKMode && new_cmd == aSym ) cmd = aKModeC; else if( ( cmd == aSign && new_cmd == aClearsign ) || ( cmd == aClearsign && new_cmd == aSign ) ) cmd = aClearsign; else { log_error(_("conflicting commands\n")); g10_exit(2); } *ret_cmd = cmd; } int main( int argc, char **argv ) { ARGPARSE_ARGS pargs; IOBUF a; int rc=0; int orig_argc; char **orig_argv; const char *fname; STRLIST sl, remusr= NULL, locusr=NULL; STRLIST nrings=NULL, sec_nrings=NULL; armor_filter_context_t afx; int detached_sig = 0; FILE *configfp = NULL; char *configname = NULL; unsigned configlineno; int parse_debug = 0; int default_config =1; int default_keyring = 1; int greeting = 1; enum cmd_and_opt_values cmd = 0; const char *trustdb_name = NULL; char *def_cipher_string = NULL; char *def_digest_string = NULL; char *s2k_cipher_string = NULL; char *s2k_digest_string = NULL; int pwfd = -1; #ifdef USE_SHM_COPROCESSING ulong requested_shm_size=0; #endif trap_unaligned(); secmem_set_flags( secmem_get_flags() | 2 ); /* suspend warnings */ /* Please note that we may running SUID(ROOT), so be very CAREFUL * when adding any stuff between here and the call to * secmem_init() somewhere after the option parsing */ log_set_name("gpg"); secure_random_alloc(); /* put random number into secure memory */ disable_core_dumps(); init_signals(); create_dotlock(NULL); /* register locking cleanup */ i18n_init(); opt.compress = -1; /* defaults to standard compress level */ /* note: if you change these lines, look at oOpenPGP */ opt.def_cipher_algo = 0; opt.def_digest_algo = 0; opt.def_compress_algo = 2; opt.s2k_mode = 3; /* iterated+salted */ opt.s2k_digest_algo = DIGEST_ALGO_RMD160; opt.s2k_cipher_algo = CIPHER_ALGO_BLOWFISH; opt.completes_needed = 1; opt.marginals_needed = 3; opt.max_cert_depth = 5; opt.homedir = getenv("GNUPGHOME"); if( !opt.homedir || !*opt.homedir ) { #ifdef HAVE_DRIVE_LETTERS opt.homedir = "c:/gnupg"; #else opt.homedir = "~/.gnupg"; #endif } /* check whether we have a config file on the commandline */ orig_argc = argc; orig_argv = argv; pargs.argc = &argc; pargs.argv = &argv; pargs.flags= 1|(1<<6); /* do not remove the args, ignore version */ while( arg_parse( &pargs, opts) ) { if( pargs.r_opt == oDebug || pargs.r_opt == oDebugAll ) parse_debug++; else if( pargs.r_opt == oOptions ) { /* yes there is one, so we do not try the default one, but * read the option file when it is encountered at the commandline */ default_config = 0; } else if( pargs.r_opt == oNoOptions ) default_config = 0; /* --no-options */ else if( pargs.r_opt == oHomedir ) opt.homedir = pargs.r.ret_str; #ifdef USE_SHM_COPROCESSING else if( pargs.r_opt == oRunAsShmCP ) { /* does not make sense in a options file, we do it here, * so that we are the able to drop setuid as soon as possible */ opt.shm_coprocess = 1; requested_shm_size = pargs.r.ret_ulong; } else if ( pargs.r_opt == oStatusFD ) { /* this is needed to ensure that the status-fd filedescriptor is * initialized when init_shm_coprocessing() is called */ set_status_fd( pargs.r.ret_int ); } #endif } #ifdef USE_SHM_COPROCESSING if( opt.shm_coprocess ) { init_shm_coprocessing(requested_shm_size, 1 ); } #endif /* initialize the secure memory. */ secmem_init( 16384 ); maybe_setuid = 0; /* Okay, we are now working under our real uid */ if( default_config ) configname = make_filename(opt.homedir, "options", NULL ); argc = orig_argc; argv = orig_argv; pargs.argc = &argc; pargs.argv = &argv; pargs.flags= 1; /* do not remove the args */ next_pass: if( configname ) { configlineno = 0; configfp = fopen( configname, "r" ); if( !configfp ) { if( default_config ) { if( parse_debug ) log_info(_("NOTE: no default option file `%s'\n"), configname ); } else { log_error(_("option file `%s': %s\n"), configname, strerror(errno) ); g10_exit(2); } m_free(configname); configname = NULL; } if( parse_debug && configname ) log_info(_("reading options from `%s'\n"), configname ); default_config = 0; } while( optfile_parse( configfp, configname, &configlineno, &pargs, opts) ) { switch( pargs.r_opt ) { case aCheckKeys: set_cmd( &cmd, aCheckKeys); break; case aListPackets: set_cmd( &cmd, aListPackets); break; case aImport: set_cmd( &cmd, aImport); break; case aFastImport: set_cmd( &cmd, aFastImport); break; case aSendKeys: set_cmd( &cmd, aSendKeys); break; case aRecvKeys: set_cmd( &cmd, aRecvKeys); break; case aExport: set_cmd( &cmd, aExport); break; case aExportAll: set_cmd( &cmd, aExportAll); break; case aListKeys: set_cmd( &cmd, aListKeys); break; case aListSigs: set_cmd( &cmd, aListSigs); break; case aExportSecret: set_cmd( &cmd, aExportSecret); break; case aDeleteSecretKey: set_cmd( &cmd, aDeleteSecretKey); break; case aDeleteKey: set_cmd( &cmd, aDeleteKey); break; case aDetachedSign: detached_sig = 1; set_cmd( &cmd, aSign ); break; case aSym: set_cmd( &cmd, aSym); break; case aDecrypt: set_cmd( &cmd, aDecrypt); break; case aEncr: set_cmd( &cmd, aEncr); break; case aSign: set_cmd( &cmd, aSign ); break; case aKeygen: set_cmd( &cmd, aKeygen); break; case aSignKey: set_cmd( &cmd, aSignKey); break; case aStore: set_cmd( &cmd, aStore); break; case aEditKey: set_cmd( &cmd, aEditKey); break; case aClearsign: set_cmd( &cmd, aClearsign); break; case aGenRevoke: set_cmd( &cmd, aGenRevoke); break; case aVerify: set_cmd( &cmd, aVerify); break; #ifdef MAINTAINER_OPTIONS case aPrimegen: set_cmd( &cmd, aPrimegen); break; case aGenRandom: set_cmd( &cmd, aGenRandom); break; #endif case aPrintMD: set_cmd( &cmd, aPrintMD); break; case aPrintMDs: set_cmd( &cmd, aPrintMDs); break; case aListTrustDB: set_cmd( &cmd, aListTrustDB); break; case aCheckTrustDB: set_cmd( &cmd, aCheckTrustDB); break; case aUpdateTrustDB: set_cmd( &cmd, aUpdateTrustDB); break; case aFixTrustDB: set_cmd( &cmd, aFixTrustDB); break; case aListTrustPath: set_cmd( &cmd, aListTrustPath); break; case aDeArmor: set_cmd( &cmd, aDeArmor); greeting = 0; break; case aEnArmor: set_cmd( &cmd, aEnArmor); greeting = 0; break; case aExportOwnerTrust: set_cmd( &cmd, aExportOwnerTrust); break; case aImportOwnerTrust: set_cmd( &cmd, aImportOwnerTrust); break; case oArmor: opt.armor = 1; opt.no_armor=0; break; case oOutput: opt.outfile = pargs.r.ret_str; break; case oQuiet: opt.quiet = 1; break; case oNoTTY: opt.quiet = 1; tty_no_terminal(1); break; case oDryRun: opt.dry_run = 1; break; case oInteractive: opt.interactive = 1; break; case oVerbose: g10_opt_verbose++; opt.verbose++; opt.list_sigs=1; break; case oKOption: set_cmd( &cmd, aKMode ); break; case oBatch: opt.batch = 1; greeting = 0; break; case oAnswerYes: opt.answer_yes = 1; break; case oAnswerNo: opt.answer_no = 1; break; case oKeyring: append_to_strlist( &nrings, pargs.r.ret_str); break; case oDebug: opt.debug |= pargs.r.ret_ulong; break; case oDebugAll: opt.debug = ~0; break; case oStatusFD: set_status_fd( pargs.r.ret_int ); break; case oLoggerFD: log_set_logfile( NULL, pargs.r.ret_int ); break; case oFingerprint: opt.fingerprint++; break; case oSecretKeyring: append_to_strlist( &sec_nrings, pargs.r.ret_str); break; case oOptions: /* config files may not be nested (silently ignore them) */ if( !configfp ) { m_free(configname); configname = m_strdup(pargs.r.ret_str); goto next_pass; } break; case oNoArmor: opt.no_armor=1; opt.armor=0; break; case oNoDefKeyring: default_keyring = 0; break; case oNoGreeting: greeting = 0; break; case oNoVerbose: g10_opt_verbose = 0; opt.verbose = 0; opt.list_sigs=0; break; case oQuickRandom: quick_random_gen(1); break; case oNoComment: opt.no_comment=1; break; case oNoVersion: opt.no_version=1; break; case oEmitVersion: opt.no_version=0; break; case oCompletesNeeded: opt.completes_needed = pargs.r.ret_int; break; case oMarginalsNeeded: opt.marginals_needed = pargs.r.ret_int; break; case oMaxCertDepth: opt.max_cert_depth = pargs.r.ret_int; break; case oTrustDBName: trustdb_name = pargs.r.ret_str; break; case oDefaultKey: opt.def_secret_key = pargs.r.ret_str; break; case oNoOptions: break; /* no-options */ case oHomedir: opt.homedir = pargs.r.ret_str; break; case oNoBatch: opt.batch = 0; break; case oWithKeyData: opt.with_key_data=1; /* fall thru */ case oWithColons: opt.with_colons=':'; break; case oSkipVerify: opt.skip_verify=1; break; case oCompressAlgo: opt.def_compress_algo = pargs.r.ret_int; break; case oCompressKeys: opt.compress_keys = 1; break; case aListSecretKeys: set_cmd( &cmd, aListSecretKeys); break; case oAlwaysTrust: opt.always_trust = 1; break; case oLoadExtension: register_cipher_extension(orig_argc? *orig_argv:NULL, pargs.r.ret_str); break; case oRFC1991: opt.rfc1991 = 1; opt.no_comment = 1; opt.escape_from = 1; break; case oOpenPGP: opt.rfc1991 = 0; opt.escape_from = 0; opt.force_v3_sigs = 0; opt.compress_keys = 0; /* not mandated but we do it */ opt.compress_sigs = 0; /* ditto. */ opt.not_dash_escaped = 0; opt.def_cipher_algo = 0; opt.def_digest_algo = 0; opt.def_compress_algo = 2; opt.s2k_mode = 3; /* iterated+salted */ opt.s2k_digest_algo = DIGEST_ALGO_RMD160; opt.s2k_cipher_algo = CIPHER_ALGO_BLOWFISH; break; case oEmuChecksumBug: opt.emulate_bugs |= EMUBUG_GPGCHKSUM; break; case oCompressSigs: opt.compress_sigs = 1; break; case oRunAsShmCP: #ifndef USE_SHM_COPROCESSING /* not possible in the option file, * but we print the warning here anyway */ log_error("shared memory coprocessing is not available\n"); #endif break; case oSetFilename: opt.set_filename = pargs.r.ret_str; break; case oSetPolicyURL: opt.set_policy_url = pargs.r.ret_str; break; case oUseEmbeddedFilename: opt.use_embedded_filename = 1; break; case oComment: opt.comment_string = pargs.r.ret_str; break; case oDefaultComment: opt.comment_string = NULL; break; case oThrowKeyid: opt.throw_keyid = 1; break; case oForceV3Sigs: opt.force_v3_sigs = 1; break; case oForceMDC: opt.force_mdc = 1; break; case oS2KMode: opt.s2k_mode = pargs.r.ret_int; break; case oS2KDigest: s2k_digest_string = m_strdup(pargs.r.ret_str); break; case oS2KCipher: s2k_cipher_string = m_strdup(pargs.r.ret_str); break; case oNoEncryptTo: opt.no_encrypt_to = 1; break; case oEncryptTo: /* store the recipient in the second list */ - sl = add_to_strlist( &remusr, pargs.r.ret_str ); + sl = add_to_strlist2( &remusr, pargs.r.ret_str, utf8_strings ); sl->flags = 1; break; case oRecipient: /* store the recipient */ - add_to_strlist( &remusr, pargs.r.ret_str ); + add_to_strlist2( &remusr, pargs.r.ret_str, utf8_strings ); break; case oTextmodeShort: opt.textmode = 2; break; case oTextmode: opt.textmode=1; break; case oUser: /* store the local users */ - add_to_strlist( &locusr, pargs.r.ret_str ); + add_to_strlist2( &locusr, pargs.r.ret_str, utf8_strings ); break; case oCompress: opt.compress = pargs.r.ret_int; break; case oPasswdFD: pwfd = pargs.r.ret_int; break; case oCipherAlgo: def_cipher_string = m_strdup(pargs.r.ret_str); break; case oDigestAlgo: def_digest_string = m_strdup(pargs.r.ret_str); break; case oNoSecmemWarn: secmem_set_flags( secmem_get_flags() | 1 ); break; case oCharset: if( set_native_charset( pargs.r.ret_str ) ) log_error(_("%s is not a valid character set\n"), pargs.r.ret_str); break; case oNotDashEscaped: opt.not_dash_escaped = 1; break; case oEscapeFrom: opt.escape_from = 1; break; case oLockOnce: opt.lock_once = 1; break; case oLockMultiple: opt.lock_once = 0; break; case oKeyServer: opt.keyserver_name = pargs.r.ret_str; break; case oNotation: add_notation_data( pargs.r.ret_str ); break; + case oUtf8Strings: utf8_strings = 1; break; + case oNoUtf8Strings: utf8_strings = 0; break; default : pargs.err = configfp? 1:2; break; } } if( configfp ) { fclose( configfp ); configfp = NULL; m_free(configname); configname = NULL; goto next_pass; } m_free( configname ); configname = NULL; if( log_get_errorcount(0) ) g10_exit(2); if( greeting ) { fprintf(stderr, "%s %s; %s\n", strusage(11), strusage(13), strusage(14) ); fprintf(stderr, "%s\n", strusage(15) ); #ifdef IS_DEVELOPMENT_VERSION log_info("NOTE: this is a development version!\n"); #endif } if( opt.batch ) tty_batchmode( 1 ); secmem_set_flags( secmem_get_flags() & ~2 ); /* resume warnings */ set_debug(); g10_opt_homedir = opt.homedir; /* must do this after dropping setuid, because string_to... * may try to load an module */ if( def_cipher_string ) { opt.def_cipher_algo = string_to_cipher_algo(def_cipher_string); m_free(def_cipher_string); def_cipher_string = NULL; if( check_cipher_algo(opt.def_cipher_algo) ) log_error(_("selected cipher algorithm is invalid\n")); } if( def_digest_string ) { opt.def_digest_algo = string_to_digest_algo(def_digest_string); m_free(def_digest_string); def_digest_string = NULL; if( check_digest_algo(opt.def_digest_algo) ) log_error(_("selected digest algorithm is invalid\n")); } if( s2k_cipher_string ) { opt.s2k_cipher_algo = string_to_cipher_algo(s2k_cipher_string); m_free(s2k_cipher_string); s2k_cipher_string = NULL; if( check_cipher_algo(opt.s2k_cipher_algo) ) log_error(_("selected cipher algorithm is invalid\n")); } if( s2k_digest_string ) { opt.s2k_digest_algo = string_to_digest_algo(s2k_digest_string); m_free(s2k_digest_string); s2k_digest_string = NULL; if( check_digest_algo(opt.s2k_digest_algo) ) log_error(_("selected digest algorithm is invalid\n")); } if( opt.set_policy_url ) { if( check_policy_url( opt.set_policy_url ) ) log_error(_("the given policy URL is invalid\n")); } if( opt.def_compress_algo < 1 || opt.def_compress_algo > 2 ) log_error(_("compress algorithm must be in range %d..%d\n"), 1, 2); if( opt.completes_needed < 1 ) log_error(_("completes-needed must be greater than 0\n")); if( opt.marginals_needed < 2 ) log_error(_("marginals-needed must be greater than 1\n")); if( opt.max_cert_depth < 1 || opt.max_cert_depth > 255 ) log_error(_("max-cert-depth must be in range 1 to 255\n")); switch( opt.s2k_mode ) { case 0: log_info(_("NOTE: simple S2K mode (0) is strongly discouraged\n")); break; case 1: case 3: break; default: log_error(_("invalid S2K mode; must be 0, 1 or 3\n")); } if( log_get_errorcount(0) ) g10_exit(2); if( !cmd && opt.fingerprint ) set_cmd( &cmd, aListKeys); if( cmd == aKMode || cmd == aKModeC ) { /* kludge to be compatible to pgp */ if( cmd == aKModeC ) { opt.fingerprint = 1; cmd = aKMode; } opt.list_sigs = 0; if( opt.verbose > 2 ) opt.check_sigs++; if( opt.verbose > 1 ) opt.list_sigs++; opt.verbose = opt.verbose > 1; g10_opt_verbose = opt.verbose; } /* kludge to let -sat generate a clear text signature */ if( opt.textmode == 2 && !detached_sig && opt.armor && cmd == aSign ) cmd = aClearsign; if( opt.verbose > 1 ) set_packet_list_mode(1); /* add the keyrings, but not for some special commands and * not in case of "-kvv userid keyring" */ if( cmd != aDeArmor && cmd != aEnArmor && !(cmd == aKMode && argc == 2 ) ) { if( !sec_nrings && default_keyring ) /* add default secret rings */ add_keyblock_resource("secring.gpg", 0, 1); for(sl = sec_nrings; sl; sl = sl->next ) add_keyblock_resource( sl->d, 0, 1 ); if( !nrings && default_keyring ) /* add default ring */ add_keyblock_resource("pubring.gpg", 0, 0); for(sl = nrings; sl; sl = sl->next ) add_keyblock_resource( sl->d, 0, 0 ); } FREE_STRLIST(nrings); FREE_STRLIST(sec_nrings); if( pwfd != -1 ) /* read the passphrase now. */ read_passphrase_from_fd( pwfd ); fname = argc? *argv : NULL; switch( cmd ) { case aPrimegen: case aPrintMD: case aPrintMDs: case aGenRandom: case aDeArmor: case aEnArmor: case aFixTrustDB: break; case aKMode: case aListKeys: case aListSecretKeys: case aCheckKeys: if( opt.with_colons ) /* need this to list the trust */ rc = setup_trustdb(1, trustdb_name ); break; case aExportOwnerTrust: rc = setup_trustdb( 0, trustdb_name ); break; case aListTrustDB: rc = setup_trustdb( argc? 1:0, trustdb_name ); break; default: rc = setup_trustdb(1, trustdb_name ); break; } if( rc ) log_error(_("failed to initialize the TrustDB: %s\n"), g10_errstr(rc)); switch( cmd ) { case aStore: /* only store the file */ if( argc > 1 ) wrong_args(_("--store [filename]")); if( (rc = encode_store(fname)) ) log_error_f( print_fname_stdin(fname), "store failed: %s\n", g10_errstr(rc) ); break; case aSym: /* encrypt the given file only with the symmetric cipher */ if( argc > 1 ) wrong_args(_("--symmetric [filename]")); if( (rc = encode_symmetric(fname)) ) log_error_f(print_fname_stdin(fname), "symmetric encryption failed: %s\n",g10_errstr(rc) ); break; case aEncr: /* encrypt the given file */ if( argc > 1 ) wrong_args(_("--encrypt [filename]")); if( (rc = encode_crypt(fname,remusr)) ) log_error("%s: encryption failed: %s\n", print_fname_stdin(fname), g10_errstr(rc) ); break; case aSign: /* sign the given file */ sl = NULL; if( detached_sig ) { /* sign all files */ for( ; argc; argc--, argv++ ) add_to_strlist( &sl, *argv ); } else { if( argc > 1 ) wrong_args(_("--sign [filename]")); if( argc ) { sl = m_alloc_clear( sizeof *sl + strlen(fname)); strcpy(sl->d, fname); } } if( (rc = sign_file( sl, detached_sig, locusr, 0, NULL, NULL)) ) log_error("signing failed: %s\n", g10_errstr(rc) ); free_strlist(sl); break; case aSignEncr: /* sign and encrypt the given file */ if( argc > 1 ) wrong_args(_("--sign --encrypt [filename]")); if( argc ) { sl = m_alloc_clear( sizeof *sl + strlen(fname)); strcpy(sl->d, fname); } else sl = NULL; if( (rc = sign_file(sl, detached_sig, locusr, 1, remusr, NULL)) ) log_error("%s: sign+encrypt failed: %s\n", print_fname_stdin(fname), g10_errstr(rc) ); free_strlist(sl); break; case aClearsign: /* make a clearsig */ if( argc > 1 ) wrong_args(_("--clearsign [filename]")); if( (rc = clearsign_file(fname, locusr, NULL)) ) log_error("%s: clearsign failed: %s\n", print_fname_stdin(fname), g10_errstr(rc) ); break; case aVerify: if( (rc = verify_signatures( argc, argv ) )) log_error("verify signatures failed: %s\n", g10_errstr(rc) ); break; case aDecrypt: if( argc > 1 ) wrong_args(_("--decrypt [filename]")); if( (rc = decrypt_message( fname ) )) log_error("decrypt_message failed: %s\n", g10_errstr(rc) ); break; case aSignKey: /* sign the key given as argument */ case aEditKey: /* Edit a key signature */ if( !argc ) wrong_args(_("--edit-key username [commands]")); if( argc > 1 ) { sl = NULL; for( argc--, argv++ ; argc; argc--, argv++ ) - append_to_strlist( &sl, *argv ); + append_to_strlist2( &sl, *argv, utf8_strings ); keyedit_menu( fname, locusr, sl ); free_strlist(sl); } else keyedit_menu(fname, locusr, NULL ); break; case aDeleteSecretKey: if( argc != 1 ) wrong_args(_("--delete-secret-key username")); case aDeleteKey: if( argc != 1 ) wrong_args(_("--delete-key username")); /* note: fname is the user id! */ + /* fixme: do utf8 conversion */ if( (rc = delete_key(fname, cmd==aDeleteSecretKey)) ) log_error("%s: delete key failed: %s\n", print_fname_stdin(fname), g10_errstr(rc) ); break; case aCheckKeys: opt.check_sigs = 1; case aListSigs: opt.list_sigs = 1; case aListKeys: public_key_list( argc, argv ); break; case aListSecretKeys: secret_key_list( argc, argv ); break; case aKMode: /* list keyring */ if( argc < 2 ) /* -kv [userid] */ public_key_list( (argc && **argv)? 1:0, argv ); else if( argc == 2 ) { /* -kv userid keyring */ if( access( argv[1], R_OK ) ) { log_error(_("can't open %s: %s\n"), print_fname_stdin(argv[1]), strerror(errno)); } else { /* add keyring (default keyrings are not registered in this * special case */ add_keyblock_resource( argv[1], 0, 0 ); public_key_list( **argv?1:0, argv ); } } else wrong_args(_("-k[v][v][v][c] [userid] [keyring]") ); break; case aKeygen: /* generate a key (interactive) */ if( argc ) wrong_args("--gen-key"); generate_keypair(); break; case aFastImport: case aImport: if( !argc ) { rc = import_keys( NULL, (cmd == aFastImport) ); if( rc ) log_error("import failed: %s\n", g10_errstr(rc) ); } for( ; argc; argc--, argv++ ) { rc = import_keys( *argv, (cmd == aFastImport) ); if( rc ) log_error("import from `%s' failed: %s\n", *argv, g10_errstr(rc) ); } break; case aExport: case aExportAll: case aSendKeys: case aRecvKeys: sl = NULL; for( ; argc; argc--, argv++ ) add_to_strlist( &sl, *argv ); if( cmd == aSendKeys ) hkp_export( sl ); else if( cmd == aRecvKeys ) hkp_import( sl ); else export_pubkeys( sl, (cmd == aExport) ); free_strlist(sl); break; case aExportSecret: sl = NULL; for( ; argc; argc--, argv++ ) add_to_strlist( &sl, *argv ); export_seckeys( sl ); free_strlist(sl); break; case aGenRevoke: if( argc != 1 ) wrong_args("--gen-revoke user-id"); gen_revoke( *argv ); break; case aDeArmor: if( argc > 1 ) wrong_args("--dearmor [file]"); rc = dearmor_file( argc? *argv: NULL ); if( rc ) log_error(_("dearmoring failed: %s\n"), g10_errstr(rc)); break; case aEnArmor: if( argc > 1 ) wrong_args("--enarmor [file]"); rc = enarmor_file( argc? *argv: NULL ); if( rc ) log_error(_("enarmoring failed: %s\n"), g10_errstr(rc)); break; #ifdef MAINTAINER_OPTIONS case aPrimegen: if( argc == 1 ) { mpi_print( stdout, generate_public_prime( atoi(argv[0]) ), 1); putchar('\n'); } else if( argc == 2 ) { mpi_print( stdout, generate_elg_prime( 0, atoi(argv[0]), atoi(argv[1]), NULL,NULL ), 1); putchar('\n'); } else if( argc == 3 ) { MPI g = mpi_alloc(1); mpi_print( stdout, generate_elg_prime( 0, atoi(argv[0]), atoi(argv[1]), g, NULL ), 1); printf("\nGenerator: "); mpi_print( stdout, g, 1 ); putchar('\n'); mpi_free(g); } else if( argc == 4 ) { mpi_print( stdout, generate_elg_prime( 1, atoi(argv[0]), atoi(argv[1]), NULL,NULL ), 1); putchar('\n'); } else usage(1); break; #endif /* MAINTAINER OPTIONS */ #ifdef MAINTAINER_OPTIONS case aGenRandom: if( argc < 1 || argc > 2 ) wrong_args("--gen-random level [hex]"); { int c; int level = atoi(*argv); for(;;) { byte *p; if( argc == 2 ) { p = get_random_bits( 8, level, 0); printf("%02x", *p ); fflush(stdout); } else { p = get_random_bits( 800, level, 0); for(c=0; c < 100; c++ ) putchar( p[c] ); } m_free(p); } } break; #endif /* MAINTAINER OPTIONS */ case aPrintMD: if( argc < 1) wrong_args("--print-md algo [file]"); else { int algo = string_to_digest_algo(*argv); if( !algo ) log_error(_("invalid hash algorithm `%s'\n"), *argv ); else { argc--; argv++; if( !argc ) print_mds(NULL, algo); else { for(; argc; argc--, argv++ ) print_mds(*argv, algo); } } } break; case aPrintMDs: if( !argc ) print_mds(NULL,0); else { for(; argc; argc--, argv++ ) print_mds(*argv,0); } break; case aListTrustDB: if( !argc ) list_trustdb(NULL); else { for( ; argc; argc--, argv++ ) list_trustdb( *argv ); } break; case aUpdateTrustDB: if( argc ) wrong_args("--update-trustdb"); update_trustdb(); break; case aCheckTrustDB: if( !argc ) check_trustdb(NULL); else { for( ; argc; argc--, argv++ ) check_trustdb( *argv ); } break; case aFixTrustDB: log_error("this command ist not yet implemented.\"\n"); log_error("A workaround is to use \"--export-ownertrust\", remove\n"); log_error("the trustdb file and do an \"--import-ownertrust\".\n" ); break; case aListTrustPath: if( !argc ) wrong_args("--list-trust-path "); for( ; argc; argc--, argv++ ) list_trust_path( *argv ); break; case aExportOwnerTrust: if( argc ) wrong_args("--export-ownertrust"); export_ownertrust(); break; case aImportOwnerTrust: if( argc > 1 ) wrong_args("--import-ownertrust [file]"); import_ownertrust( argc? *argv:NULL ); break; case aListPackets: opt.list_packets=1; default: if( argc > 1 ) wrong_args(_("[filename]")); /* Issue some output for the unix newbie */ if( !fname && !opt.outfile && isatty( fileno(stdin) ) && isatty( fileno(stdout) ) && isatty( fileno(stderr) ) ) log_info(_("Go ahead and type your message ...\n")); if( !(a = iobuf_open(fname)) ) log_error(_("can't open `%s'\n"), print_fname_stdin(fname)); else { if( !opt.no_armor ) { if( use_armor_filter( a ) ) { memset( &afx, 0, sizeof afx); iobuf_push_filter( a, armor_filter, &afx ); } } if( cmd == aListPackets ) { set_packet_list_mode(1); opt.list_packets=1; } rc = proc_packets(NULL, a ); if( rc ) log_error("processing message failed: %s\n", g10_errstr(rc) ); iobuf_close(a); } break; } /* cleanup */ FREE_STRLIST(remusr); FREE_STRLIST(locusr); g10_exit(0); return 8; /*NEVER REACHED*/ } void g10_exit( int rc ) { if( opt.debug & DBG_MEMSTAT_VALUE ) m_print_stats("on exit"); if( opt.debug ) secmem_dump_stats(); secmem_term(); rc = rc? rc : log_get_errorcount(0)? 2 : g10_errors_seen? 1 : 0; /*write_status( STATUS_LEAVE );*/ exit(rc ); } static void print_hex( byte *p, size_t n ) { int i; if( n == 20 ) { for(i=0; i < n ; i++, i++, p += 2 ) { if( i ) putchar(' '); if( i == 10 ) putchar(' '); printf("%02X%02X", *p, p[1] ); } } else if( n == 24 ) { for(i=0; i < n ; i += 4, p += 4 ) { if( i ) putchar(' '); if( i == 12 ) putchar(' '); printf("%02X%02X%02X%02X", *p, p[1], p[2], p[3] ); } } else { for(i=0; i < n ; i++, p++ ) { if( i ) putchar(' '); if( i && !(i%8) ) putchar(' '); printf("%02X", *p ); } } } static void print_mds( const char *fname, int algo ) { FILE *fp; char buf[1024]; size_t n; MD_HANDLE md; char *pname; if( !fname ) { fp = stdin; pname = m_strdup("[stdin]: "); } else { pname = m_alloc(strlen(fname)+3); strcpy(stpcpy(pname,fname),": "); fp = fopen( fname, "rb" ); } if( !fp ) { log_error("%s%s\n", pname, strerror(errno) ); m_free(pname); return; } md = md_open( 0, 0 ); if( algo ) md_enable( md, algo ); else { md_enable( md, DIGEST_ALGO_MD5 ); md_enable( md, DIGEST_ALGO_SHA1 ); md_enable( md, DIGEST_ALGO_RMD160 ); if( !check_digest_algo(DIGEST_ALGO_TIGER) ) md_enable( md, DIGEST_ALGO_TIGER ); } while( (n=fread( buf, 1, DIM(buf), fp )) ) md_write( md, buf, n ); if( ferror(fp) ) log_error("%s%s\n", pname, strerror(errno) ); else { md_final(md); if( algo ) { if( fname ) fputs( pname, stdout ); print_hex(md_read(md, algo), md_digest_length(algo) ); } else { printf( "%s MD5 = ", fname?pname:"" ); print_hex(md_read(md, DIGEST_ALGO_MD5), 16 ); printf("\n%s SHA1 = ", fname?pname:"" ); print_hex(md_read(md, DIGEST_ALGO_SHA1), 20 ); printf("\n%sRMD160 = ", fname?pname:"" ); print_hex(md_read(md, DIGEST_ALGO_RMD160), 20 ); if( !check_digest_algo(DIGEST_ALGO_TIGER) ) { printf("\n%s TIGER = ", fname?pname:"" ); print_hex(md_read(md, DIGEST_ALGO_TIGER), 24 ); } } putchar('\n'); } md_close(md); if( fp != stdin ) fclose(fp); } /**************** * Check the supplied name,value string and add it to the notation * data to be used for signatures. */ static void add_notation_data( const char *string ) { const char *s; const char *s2; STRLIST sl; int critical=0; int highbit=0; if( *string == '!' ) { critical = 1; string++; } s = string; if( !*s || (*s & 0x80) || (!isalpha(*s) && *s != '_') ) { log_error(_("the first character of a notation name " "must be a letter or an underscore\n") ); return; } for(s++; *s != '='; s++ ) { if( !*s || (*s & 0x80) || (!isalnum(*s) && *s != '_' && *s != '.' ) ) { log_error(_("a notation name must have only letters, " "digits, dots or underscores and end with an '='\n") ); return; } } if( s[-1] == '.' || ((s2=strstr(string, "..")) && s2 < s ) ) { log_error(_("dots in a notation name must be surrounded " "by other characters\n") ); return; } /* we do only support printabe text - therefore we enforce the use * of only printable characters (an empty value is valid) */ for( s++; *s ; s++ ) { if( iscntrl(*s) ) { log_error(_("a notation value must not use " "any control characters\n") ); return; } else if( *s & 0x80 ) highbit = 1; } - if( highbit ) { /* must use UTF8 encoding */ - char *p = native_to_utf8( string ); - sl = add_to_strlist( &opt.notation_data, p ); - m_free( p ); - } + if( highbit ) /* must use UTF8 encoding */ + sl = add_to_strlist2( &opt.notation_data, string, utf8_strings ); else sl = add_to_strlist( &opt.notation_data, string ); if( critical ) sl->flags |= 1; } static int check_policy_url( const char *s ) { if( *s == '!' ) s++; if( !*s ) return -1; for(; *s ; s++ ) { if( (*s & 0x80) || iscntrl(*s) ) return -1; } return 0; } diff --git a/g10/helptext.c b/g10/helptext.c index 521df534d..5fb800784 100644 --- a/g10/helptext.c +++ b/g10/helptext.c @@ -1,245 +1,252 @@ /* helptext.c - English help texts * Copyright (C) 1998 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 "util.h" #include "ttyio.h" #include "main.h" #include "i18n.h" /**************** * These helptexts are used for the "online" help feature. We use * a key consisting of words and dots. Because the lookup is only * done in an interactive mode on a user request (when she enters a "?" * as response to a prompt) we can use a simple search through the list. * Translators should use the key as msgid, this is to keep the msgid short * and to allow for easy changing of the helptexts. * * Mini glossary: * * "user ID", "trustdb", "NOTE" and "WARNING". */ static struct helptexts { const char *key; const char *help; } helptexts[] = { /* begin of list */ { N_("edit_ownertrust.value"), "It's up to you to assign a value here; this value will never be exported\n" "to any 3rd party. We need it to implement the web-of-trust; it has nothing\n" "to do with the (implicitly created) web-of-certificates." }, { N_("revoked_key.override"), "If you want to use this revoked key anyway, answer \"yes\"." }, { N_("untrusted_key.override"), "If you want to use this untrusted key anyway, answer \"yes\"." }, { N_("pklist.user_id.enter"), "Enter the user id of the addressee to whom you want to send the message." }, { N_("keygen.algo"), "Select the algorithm to use.\n" "DSA (aka DSS) is the digital signature algorithm which can only be used\n" "for signatures. This is the suggested algorithm because verification of\n" "DSA signatures are much faster than those of ElGamal.\n" "ElGamal is a algorithm which can be used for signatures and encryption.\n" "OpenPGP distunguishs between two flavors of this algorithms: a encrypt only\n" "and a sign+encrypt; actually it is the same, but some parameters must be\n" "selected in a special way to create a safe key for signatures: this program\n" "does this but other OpenPGP implemenations are not required to understand\n" "the signature+encryption flavor.\n" "The first (primary) key must always be a key which is capable of signing;\n" "this is the reason why the encryption only ElGamal key is disabled in this." }, { N_("keygen.algo.elg_se"), "Although these keys are defined in RFC2440 they are not suggested\n" "because they are not supported by all programs and signatures created\n" "with them are quite large and very slow to verify." }, { N_("keygen.size"), "Enter the size of the key" }, { N_("keygen.size.huge.okay"), "Answer \"yes\" or \"no\"" }, { N_("keygen.size.large.okay"), "Answer \"yes\" or \"no\"" }, { N_("keygen.valid"), "Enter the required value" }, { N_("keygen.valid.okay"), "Answer \"yes\" or \"no\"" }, { N_("keygen.name"), "Enter the name of the key holder" }, { N_("keygen.email"), "please enter an optional but highly suggested email address" }, { N_("keygen.comment"), "Please enter an optional comment" }, { N_("keygen.userid.cmd"), "" "N to change the name.\n" "C to change the comment.\n" "E to change the email address.\n" "O to continue with key generation.\n" "Q to to quit the key generation." }, { N_("keygen.sub.okay"), "Answer \"yes\" (or just \"y\") if it is okay to generate the sub key." }, { N_("sign_uid.okay"), "Answer \"yes\" or \"no\"" }, { N_("change_passwd.empty.okay"), "Answer \"yes\" or \"no\"" }, { N_("keyedit.save.okay"), "Answer \"yes\" or \"no\"" }, { N_("keyedit.cancel.okay"), "Answer \"yes\" or \"no\"" }, { N_("keyedit.sign_all.okay"), "Answer \"yes\" is you want to sign ALL the user IDs" }, { N_("keyedit.remove.uid.okay"), "Answer \"yes\" if you really want to delete this user ID.\n" "All certificates are then also lost!" }, { N_("keyedit.remove.subkey.okay"), "Answer \"yes\" if it is okay to delete the subkey" }, { N_("keyedit.delsig.valid"), "This is a valid signature on the key; you normally don't want\n" "to delete this signature may be important to establish a trust\n" "connection to the key or another key certified by this key." }, { N_("keyedit.delsig.unknown"), "This signature can't be checked because you don't have the\n" "corresponding key. You should postpone its deletion until you\n" "know which key was used because this signing key might establish" "a trust connection through another already certified key." }, { N_("keyedit.delsig.invalid"), "The signature is not valid. It does make sense to remove it from\n" "your keyring." }, { N_("keyedit.delsig.selfsig"), "This is a signature which binds the user ID to the key. It is\n" "usually not a good idea to remove such a signature. Actually\n" "GnuPG might not be able to use this key anymore. So do this\n" "only if this self-signature is for some reason not valid and\n" "a second one is available." }, { N_("passphrase.enter"), "" "Please enter the passhrase; this is a secret sentence \n" " Blurb, blurb,.... " }, { N_("passphrase.repeat"), "Please repeat the last passphrase, so you are sure what you typed in." }, { N_("detached_signature.filename"), "Give the name fo the file to which the signature applies" }, +/* openfile.c (overwrite_filep) */ { N_("openfile.overwrite.okay"), "Answer \"yes\" if it is okay to overwrite the file" }, +/* openfile.c (ask_outfile_name) */ +{ N_("openfile.askoutname"), + "Please enter a new filename. If you just hit RETURN the default\n" + "file (which is shown in brackets) will be used." +}, + /* end of list */ { NULL, NULL } }; void display_online_help( const char *keyword ) { tty_kill_prompt(); if( !keyword ) tty_printf(_("No help available") ); else { const char *p = _(keyword); if( strcmp( p, keyword ) ) tty_printf("%s", p ); else { int i; for(i=0; (p=helptexts[i].key) && strcmp( p, keyword ); i++ ) ; if( !p || !*helptexts[i].help ) tty_printf(_("No help available for `%s'"), keyword ); else tty_printf("%s", helptexts[i].help ); } } tty_printf("\n"); } diff --git a/g10/import.c b/g10/import.c index 060990e36..a81c7edbf 100644 --- a/g10/import.c +++ b/g10/import.c @@ -1,1255 +1,1252 @@ /* import.c * Copyright (C) 1998, 1999 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 "options.h" #include "packet.h" #include "errors.h" #include "keydb.h" #include "memory.h" #include "util.h" #include "trustdb.h" #include "main.h" #include "i18n.h" static struct { ulong no_user_id; ulong imported; ulong imported_rsa; ulong n_uids; ulong n_sigs; ulong n_subk; ulong unchanged; ulong n_revoc; ulong secret_read; ulong secret_imported; ulong secret_dups; } stats; static int import( IOBUF inp, int fast, const char* fname ); static int read_block( IOBUF a, PACKET **pending_pkt, KBNODE *ret_root ); static int import_one( const char *fname, KBNODE keyblock, int fast ); static int import_secret_one( const char *fname, KBNODE keyblock ); static int import_revoke_cert( const char *fname, KBNODE node ); static int chk_self_sigs( const char *fname, KBNODE keyblock, PKT_public_key *pk, u32 *keyid ); static int delete_inv_parts( const char *fname, KBNODE keyblock, u32 *keyid ); static int merge_blocks( const char *fname, KBNODE keyblock_orig, KBNODE keyblock, u32 *keyid, int *n_uids, int *n_sigs, int *n_subk ); static int append_uid( KBNODE keyblock, KBNODE node, int *n_sigs, const char *fname, u32 *keyid ); static int append_key( KBNODE keyblock, KBNODE node, int *n_sigs, const char *fname, u32 *keyid ); static int merge_sigs( KBNODE dst, KBNODE src, int *n_sigs, const char *fname, u32 *keyid ); static int merge_keysigs( KBNODE dst, KBNODE src, int *n_sigs, const char *fname, u32 *keyid ); /**************** * Import the public keys from the given filename. Input may be armored. * This function rejects all keys which are not validly self signed on at * least one userid. Only user ids which are self signed will be imported. * Other signatures are not checked. * * Actually this function does a merge. It works like this: * * - get the keyblock * - check self-signatures and remove all userids and their signatures * without/invalid self-signatures. * - reject the keyblock, if we have no valid userid. * - See whether we have this key already in one of our pubrings. * If not, simply add it to the default keyring. * - Compare the key and the self-signatures of the new and the one in * our keyring. If they are different something weird is going on; * ask what to do. * - See whether we have only non-self-signature on one user id; if not * ask the user what to do. * - compare the signatures: If we already have this signature, check * that they compare okay; if not, issue a warning and ask the user. * (consider looking at the timestamp and use the newest?) * - Simply add the signature. Can't verify here because we may not have * the signature's public key yet; verification is done when putting it * into the trustdb, which is done automagically as soon as this pubkey * is used. * - Proceed with next signature. * * Key revocation certificates have special handling. * */ int import_keys( const char *fname, int fast ) { IOBUF inp = NULL; int rc; inp = iobuf_open(fname); if( !fname ) fname = "[stdin]"; if( !inp ) { log_error(_("can't open `%s': %s\n"), fname, strerror(errno) ); return G10ERR_OPEN_FILE; } rc = import( inp, fast, fname ); iobuf_close(inp); return rc; } int import_keys_stream( IOBUF inp, int fast ) { return import( inp, fast, "[stream]" ); } static int import( IOBUF inp, int fast, const char* fname ) { PACKET *pending_pkt = NULL; KBNODE keyblock; int rc = 0; ulong count=0; /* fixme: don't use static variables */ memset( &stats, 0, sizeof( stats ) ); getkey_disable_caches(); if( !opt.no_armor ) { /* armored reading is not disabled */ armor_filter_context_t *afx = m_alloc_clear( sizeof *afx ); afx->only_keyblocks = 1; iobuf_push_filter2( inp, armor_filter, afx, 1 ); } while( !(rc = read_block( inp, &pending_pkt, &keyblock) )) { if( keyblock->pkt->pkttype == PKT_PUBLIC_KEY ) rc = import_one( fname, keyblock, fast ); else if( keyblock->pkt->pkttype == PKT_SECRET_KEY ) rc = import_secret_one( fname, keyblock ); else if( keyblock->pkt->pkttype == PKT_SIGNATURE && keyblock->pkt->pkt.signature->sig_class == 0x20 ) rc = import_revoke_cert( fname, keyblock ); else { log_info( _("skipping block of type %d\n"), keyblock->pkt->pkttype ); } release_kbnode(keyblock); if( rc ) break; if( !(++count % 100) && !opt.quiet ) log_info(_("%lu keys so far processed\n"), count ); } if( rc == -1 ) rc = 0; else if( rc && rc != G10ERR_INV_KEYRING ) log_error( _("error reading `%s': %s\n"), fname, g10_errstr(rc)); if( !opt.quiet ) { log_info(_("Total number processed: %lu\n"), count ); if( stats.no_user_id ) log_info(_(" w/o user IDs: %lu\n"), stats.no_user_id ); if( stats.imported || stats.imported_rsa ) { log_info(_(" imported: %lu"), stats.imported ); if( stats.imported_rsa ) fprintf(stderr, " (RSA: %lu)", stats.imported_rsa ); putc('\n', stderr); } if( stats.unchanged ) log_info(_(" unchanged: %lu\n"), stats.unchanged ); if( stats.n_uids ) log_info(_(" new user IDs: %lu\n"), stats.n_uids ); if( stats.n_subk ) log_info(_(" new subkeys: %lu\n"), stats.n_subk ); if( stats.n_sigs ) log_info(_(" new signatures: %lu\n"), stats.n_sigs ); if( stats.n_revoc ) log_info(_(" new key revocations: %lu\n"), stats.n_revoc ); if( stats.secret_read ) log_info(_(" secret keys read: %lu\n"), stats.secret_read ); if( stats.secret_imported ) log_info(_(" secret keys imported: %lu\n"), stats.secret_imported ); if( stats.secret_dups ) log_info(_(" secret keys unchanged: %lu\n"), stats.secret_dups ); } return rc; } /**************** * Read the next keyblock from stream A. * PENDING_PKT should be initialzed to NULL * and not chnaged form the caller. * Retunr: 0 = okay, -1 no more blocks or another errorcode. */ static int read_block( IOBUF a, PACKET **pending_pkt, KBNODE *ret_root ) { int rc; PACKET *pkt; KBNODE root = NULL; int in_cert; if( *pending_pkt ) { root = new_kbnode( *pending_pkt ); *pending_pkt = NULL; in_cert = 1; } else in_cert = 0; pkt = m_alloc( sizeof *pkt ); init_packet(pkt); while( (rc=parse_packet(a, pkt)) != -1 ) { if( rc ) { /* ignore errors */ if( rc != G10ERR_UNKNOWN_PACKET ) { log_error("read_block: read error: %s\n", g10_errstr(rc) ); rc = G10ERR_INV_KEYRING; goto ready; } free_packet( pkt ); init_packet(pkt); continue; } if( !root && pkt->pkttype == PKT_SIGNATURE && pkt->pkt.signature->sig_class == 0x20 ) { /* this is a revocation certificate which is handled * in a special way */ root = new_kbnode( pkt ); pkt = NULL; goto ready; } /* make a linked list of all packets */ switch( pkt->pkttype ) { case PKT_COMPRESSED: if( pkt->pkt.compressed->algorithm < 1 || pkt->pkt.compressed->algorithm > 2 ) { rc = G10ERR_COMPR_ALGO; goto ready; } { compress_filter_context_t *cfx = m_alloc_clear( sizeof *cfx ); cfx->algo = pkt->pkt.compressed->algorithm; pkt->pkt.compressed->buf = NULL; iobuf_push_filter2( a, compress_filter, cfx, 1 ); } free_packet( pkt ); init_packet(pkt); break; case PKT_PUBLIC_KEY: case PKT_SECRET_KEY: if( in_cert ) { /* store this packet */ *pending_pkt = pkt; pkt = NULL; goto ready; } in_cert = 1; default: if( in_cert ) { if( !root ) root = new_kbnode( pkt ); else 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; } /**************** * Try to import one keyblock. Return an error only in serious cases, but * never for an invalid keyblock. It uses log_error to increase the * internal errorcount, so that invalid input can be detected by programs * which called g10. */ static int import_one( const char *fname, KBNODE keyblock, int fast ) { PKT_public_key *pk; PKT_public_key *pk_orig; KBNODE node, uidnode; KBNODE keyblock_orig = NULL; KBPOS kbpos; u32 keyid[2]; int rc = 0; int new_key = 0; int mod_key = 0; /* get the key and print some info about it */ node = find_kbnode( keyblock, PKT_PUBLIC_KEY ); if( !node ) BUG(); pk = node->pkt->pkt.public_key; keyid_from_pk( pk, keyid ); uidnode = find_next_kbnode( keyblock, PKT_USER_ID ); if( opt.verbose ) { log_info( "pub %4u%c/%08lX %s ", nbits_from_pk( pk ), pubkey_letter( pk->pubkey_algo ), (ulong)keyid[1], datestr_from_pk(pk) ); if( uidnode ) print_string( stderr, uidnode->pkt->pkt.user_id->name, uidnode->pkt->pkt.user_id->len, 0 ); putc('\n', stderr); } if( !uidnode ) { log_error( _("key %08lX: no user id\n"), (ulong)keyid[1]); return 0; } clear_kbnode_flags( keyblock ); rc = chk_self_sigs( fname, keyblock , pk, keyid ); if( rc ) return rc== -1? 0:rc; if( !delete_inv_parts( fname, keyblock, keyid ) ) { if( !opt.quiet ) { log_info( _("key %08lX: no valid user ids\n"), (ulong)keyid[1]); log_info(_("this may be caused by a missing self-signature\n")); } stats.no_user_id++; return 0; } /* do we have this key already in one of our pubrings ? */ pk_orig = m_alloc_clear( sizeof *pk_orig ); rc = get_pubkey( pk_orig, keyid ); if( rc && rc != G10ERR_NO_PUBKEY ) { log_error( _("key %08lX: public key not found: %s\n"), (ulong)keyid[1], g10_errstr(rc)); } else if( rc ) { /* insert this key */ /* get default resource */ if( get_keyblock_handle( NULL, 0, &kbpos ) ) { log_error(_("no default public keyring\n")); 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"), + 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"), + log_error( _("error writing keyring `%s': %s\n"), keyblock_resource_name(&kbpos), g10_errstr(rc) ); unlock_keyblock( &kbpos ); /* we are ready */ if( !opt.quiet ) log_info( _("key %08lX: public key imported\n"), (ulong)keyid[1]); stats.imported++; if( is_RSA( pk->pubkey_algo ) ) stats.imported_rsa++; new_key = 1; } else { /* merge */ 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 ) ) { log_error( _("key %08lX: doesn't match our copy\n"), (ulong)keyid[1]); rc = G10ERR_GENERAL; goto leave; } - /* See whether we have only non-self-signature on one user id; if not - * ask the user what to do. <--- fixme */ - /* now read the original keyblock */ rc = find_keyblock_bypk( &kbpos, pk_orig ); 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_orig ); if( rc ) { log_error( _("key %08lX: can't read original keyblock: %s\n"), (ulong)keyid[1], g10_errstr(rc)); goto leave; } collapse_uids( &keyblock ); /* and try to merge the block */ clear_kbnode_flags( keyblock_orig ); clear_kbnode_flags( keyblock ); n_uids = n_sigs = n_subk = 0; rc = merge_blocks( fname, keyblock_orig, keyblock, keyid, &n_uids, &n_sigs, &n_subk ); if( rc ) 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"), + 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 ); /* we are ready */ if( !opt.quiet ) { if( n_uids == 1 ) log_info( _("key %08lX: 1 new user-id\n"), (ulong)keyid[1]); else if( n_uids ) log_info( _("key %08lX: %d new user-ids\n"), (ulong)keyid[1], n_uids ); if( n_sigs == 1 ) log_info( _("key %08lX: 1 new signature\n"), (ulong)keyid[1]); else if( n_sigs ) log_info( _("key %08lX: %d new signatures\n"), (ulong)keyid[1], n_sigs ); if( n_subk == 1 ) log_info( _("key %08lX: 1 new subkey\n"), (ulong)keyid[1]); else if( n_subk ) log_info( _("key %08lX: %d new subkeys\n"), (ulong)keyid[1], n_subk ); } stats.n_uids +=n_uids; stats.n_sigs +=n_sigs; stats.n_subk +=n_subk; } else { if( !opt.quiet ) log_info( _("key %08lX: not changed\n"), (ulong)keyid[1] ); stats.unchanged++; } } if( !rc && !fast ) { rc = query_trust_record( new_key? pk : pk_orig ); if( rc && rc != -1 ) log_error("trustdb error: %s\n", g10_errstr(rc) ); else if( rc == -1 ) { /* not found trustdb */ - rc = insert_trust_record( new_key? pk : pk_orig ); + rc = insert_trust_record( new_key? keyblock : keyblock_orig ); if( rc ) log_error("key %08lX: trustdb insert failed: %s\n", (ulong)keyid[1], g10_errstr(rc) ); } else if( mod_key ) rc = update_trust_record( keyblock_orig, 1, NULL ); else rc = clear_trust_checked_flag( new_key? pk : pk_orig ); } leave: release_kbnode( keyblock_orig ); free_public_key( pk_orig ); return rc; } /**************** * Ditto for secret keys. Handling is simpler than for public keys. */ static int import_secret_one( const char *fname, KBNODE keyblock ) { PKT_secret_key *sk; KBNODE node, uidnode; KBPOS kbpos; u32 keyid[2]; int rc = 0; /* get the key and print some info about it */ node = find_kbnode( keyblock, PKT_SECRET_KEY ); if( !node ) BUG(); sk = node->pkt->pkt.secret_key; keyid_from_sk( sk, keyid ); uidnode = find_next_kbnode( keyblock, PKT_USER_ID ); if( opt.verbose ) { log_info( "sec %4u%c/%08lX %s ", nbits_from_sk( sk ), pubkey_letter( sk->pubkey_algo ), (ulong)keyid[1], datestr_from_sk(sk) ); if( uidnode ) print_string( stderr, uidnode->pkt->pkt.user_id->name, uidnode->pkt->pkt.user_id->len, 0 ); putc('\n', stderr); } stats.secret_read++; if( !uidnode ) { log_error( _("key %08lX: no user id\n"), (ulong)keyid[1]); return 0; } clear_kbnode_flags( keyblock ); /* do we have this key already in one of our secrings ? */ rc = seckey_available( keyid ); if( rc == G10ERR_NO_SECKEY ) { /* simply insert this key */ /* get default resource */ if( get_keyblock_handle( NULL, 1, &kbpos ) ) { log_error("no default secret keyring\n"); 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 ); /* we are ready */ if( !opt.quiet ) log_info( _("key %08lX: secret key imported\n"), (ulong)keyid[1]); stats.secret_imported++; } else if( !rc ) { /* we can't merge secret keys */ log_error( _("key %08lX: already in secret keyring\n"), (ulong)keyid[1]); stats.secret_dups++; } else log_error( _("key %08lX: secret key not found: %s\n"), (ulong)keyid[1], g10_errstr(rc)); return rc; } /**************** * Import a revocation certificate; this is a single signature packet. */ static int import_revoke_cert( const char *fname, KBNODE node ) { PKT_public_key *pk=NULL; KBNODE onode, keyblock = NULL; KBPOS kbpos; u32 keyid[2]; int rc = 0; assert( !node->next ); assert( node->pkt->pkttype == PKT_SIGNATURE ); assert( node->pkt->pkt.signature->sig_class == 0x20 ); keyid[0] = node->pkt->pkt.signature->keyid[0]; keyid[1] = node->pkt->pkt.signature->keyid[1]; pk = m_alloc_clear( sizeof *pk ); rc = get_pubkey( pk, keyid ); if( rc == G10ERR_NO_PUBKEY ) { log_info( _("key %08lX: no public key - " "can't apply revocation certificate\n"), (ulong)keyid[1]); rc = 0; goto leave; } else if( rc ) { log_error( _("key %08lX: public key not found: %s\n"), (ulong)keyid[1], g10_errstr(rc)); goto leave; } /* 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)); 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)); goto leave; } /* it is okay, that node is not in keyblock because * check_key_signature works fine for sig_class 0x20 in this * special case. */ rc = check_key_signature( keyblock, node, NULL); if( rc ) { log_error( _("key %08lX: invalid revocation certificate" ": %s - rejected\n"), (ulong)keyid[1], g10_errstr(rc)); } /* check whether we already have this */ for(onode=keyblock->next; onode; onode=onode->next ) { if( onode->pkt->pkttype == PKT_USER_ID ) break; else if( onode->pkt->pkttype == PKT_SIGNATURE && onode->pkt->pkt.signature->sig_class == 0x20 && keyid[0] == onode->pkt->pkt.signature->keyid[0] && keyid[1] == onode->pkt->pkt.signature->keyid[1] ) { rc = 0; goto leave; /* yes, we already know about it */ } } /* insert it */ 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 ); /* we are ready */ if( !opt.quiet ) log_info( _("key %08lX: revocation certificate imported\n"), (ulong)keyid[1]); stats.n_revoc++; leave: release_kbnode( keyblock ); free_public_key( pk ); return rc; } /**************** * loop over the keyblock and check all self signatures. * Mark all user-ids with a self-signature by setting flag bit 0. * Mark all user-ids with an invalid self-signature by setting bit 1. * This works allso for subkeys, here the subkey is marked. */ static int chk_self_sigs( const char *fname, KBNODE keyblock, PKT_public_key *pk, u32 *keyid ) { KBNODE n; PKT_signature *sig; int rc; for( n=keyblock; (n = find_next_kbnode(n, 0)); ) { if( n->pkt->pkttype != PKT_SIGNATURE ) continue; sig = n->pkt->pkt.signature; if( keyid[0] == sig->keyid[0] && keyid[1] == sig->keyid[1] ) { if( (sig->sig_class&~3) == 0x10 ) { KBNODE unode = find_prev_kbnode( keyblock, n, PKT_USER_ID ); if( !unode ) { log_error( _("key %08lX: no user-id for signature\n"), (ulong)keyid[1]); return -1; /* the complete keyblock is invalid */ } rc = check_key_signature( keyblock, n, NULL); if( rc ) { log_error( rc == G10ERR_PUBKEY_ALGO ? _("key %08lX: unsupported public key algorithm\n"): _("key %08lX: invalid self-signature\n"), (ulong)keyid[1]); unode->flag |= 2; /* mark as invalid */ } unode->flag |= 1; /* mark that signature checked */ } else if( sig->sig_class == 0x18 ) { KBNODE knode = find_prev_kbnode( keyblock, n, PKT_PUBLIC_SUBKEY ); if( !knode ) knode = find_prev_kbnode( keyblock, n, PKT_SECRET_SUBKEY ); if( !knode ) { log_error( _("key %08lX: no subkey for key binding\n"), (ulong)keyid[1]); n->flag |= 4; /* delete this */ } else { rc = check_key_signature( keyblock, n, NULL); if( rc ) { log_error( rc == G10ERR_PUBKEY_ALGO ? _("key %08lX: unsupported public key algorithm\n"): _("key %08lX: invalid subkey binding\n"), (ulong)keyid[1]); knode->flag |= 2; /* mark as invalid */ } knode->flag |= 1; /* mark that signature checked */ } } } } return 0; } /**************** * delete all parts which are invalid and those signatures whose * public key algorithm is not available in this implemenation; * but consider RSA as valid, because parse/build_packets knows * about it. * returns: true if at least one valid user-id is left over. */ static int delete_inv_parts( const char *fname, KBNODE keyblock, u32 *keyid ) { KBNODE node; int nvalid=0, uid_seen=0; const char *p; for(node=keyblock->next; node; node = node->next ) { if( node->pkt->pkttype == PKT_USER_ID ) { uid_seen = 1; if( (node->flag & 2) || !(node->flag & 1) ) { if( opt.verbose ) { log_info( _("key %08lX: skipped userid '"), (ulong)keyid[1]); print_string( stderr, node->pkt->pkt.user_id->name, node->pkt->pkt.user_id->len, 0 ); fputs("'\n", stderr ); } delete_kbnode( node ); /* the user-id */ /* and all following packets up to the next user-id */ while( node->next && node->next->pkt->pkttype != PKT_USER_ID && node->next->pkt->pkttype != PKT_PUBLIC_SUBKEY && node->next->pkt->pkttype != PKT_SECRET_SUBKEY ){ delete_kbnode( node->next ); node = node->next; } } else nvalid++; } else if( node->pkt->pkttype == PKT_PUBLIC_SUBKEY || node->pkt->pkttype == PKT_SECRET_SUBKEY ) { if( (node->flag & 2) || !(node->flag & 1) ) { if( opt.verbose ) { log_info( _("key %08lX: skipped subkey\n"), (ulong)keyid[1]); } delete_kbnode( node ); /* the subkey */ /* and all following signature packets */ while( node->next && node->next->pkt->pkttype == PKT_SIGNATURE ) { delete_kbnode( node->next ); node = node->next; } } } else if( node->pkt->pkttype == PKT_SIGNATURE && check_pubkey_algo( node->pkt->pkt.signature->pubkey_algo) && node->pkt->pkt.signature->pubkey_algo != PUBKEY_ALGO_RSA ) delete_kbnode( node ); /* build_packet() can't handle this */ else if( node->pkt->pkttype == PKT_SIGNATURE && (p = parse_sig_subpkt2( node->pkt->pkt.signature, SIGSUBPKT_EXPORTABLE, NULL )) && !*p && seckey_available( node->pkt->pkt.signature->keyid ) ) { /* here we violate the rfc a bit by still allowing * to import non-exportable signature when we have the * the secret key used to create this signature - it * seems that this makes sense */ log_info( _("key %08lX: non exportable signature " "(class %02x) - skipped\n"), (ulong)keyid[1], node->pkt->pkt.signature->sig_class ); delete_kbnode( node ); } else if( node->pkt->pkttype == PKT_SIGNATURE && node->pkt->pkt.signature->sig_class == 0x20 ) { if( uid_seen ) { log_error( _("key %08lX: revocation certificate " "at wrong place - skipped\n"), (ulong)keyid[1]); delete_kbnode( node ); } else { int rc = check_key_signature( keyblock, node, NULL); if( rc ) { log_error( _("key %08lX: invalid revocation " "certificate: %s - skipped\n"), (ulong)keyid[1], g10_errstr(rc)); delete_kbnode( node ); } } } else if( (node->flag & 4) ) /* marked for deletion */ delete_kbnode( node ); } /* note: because keyblock is the public key, it is never marked * for deletion and so keyblock cannot change */ commit_kbnode( &keyblock ); return nvalid; } /**************** * It may happen that the imported keyblock has duplicated user IDs. * We check this here and collapse those user IDs together with their * sigs into one. * Returns: True if the keyblock hash changed. */ int collapse_uids( KBNODE *keyblock ) { KBNODE n, n2; int in_uid; int any=0; u32 kid1; restart: for( n = *keyblock; n; n = n->next ) { if( n->pkt->pkttype != PKT_USER_ID ) continue; for( n2 = n->next; n2; n2 = n2->next ) { if( n2->pkt->pkttype == PKT_USER_ID && !cmp_user_ids( n->pkt->pkt.user_id, n2->pkt->pkt.user_id ) ) { /* found a duplicate */ any = 1; if( !n2->next || n2->next->pkt->pkttype == PKT_USER_ID || n2->next->pkt->pkttype == PKT_PUBLIC_SUBKEY || n2->next->pkt->pkttype == PKT_SECRET_SUBKEY ) { /* no more signatures: delete the user ID * and start over */ remove_kbnode( keyblock, n2 ); } else { /* The simple approach: Move one signature and * then start over to delete the next one :-( */ move_kbnode( keyblock, n2->next, n->next ); } goto restart; } } } if( !any ) return 0; restart_sig: /* now we may have duplicate signatures on one user ID: fix this */ for( in_uid = 0, n = *keyblock; n; n = n->next ) { if( n->pkt->pkttype == PKT_USER_ID ) in_uid = 1; else if( n->pkt->pkttype == PKT_PUBLIC_SUBKEY || n->pkt->pkttype == PKT_SECRET_SUBKEY ) in_uid = 0; else if( in_uid ) { n2 = n; do { KBNODE ncmp = NULL; for( ; n2; n2 = n2->next ) { if( n2->pkt->pkttype == PKT_USER_ID || n2->pkt->pkttype == PKT_PUBLIC_SUBKEY || n2->pkt->pkttype == PKT_SECRET_SUBKEY ) break; if( n2->pkt->pkttype != PKT_SIGNATURE ) ; else if( !ncmp ) ncmp = n2; else if( !cmp_signatures( ncmp->pkt->pkt.signature, n2->pkt->pkt.signature )) { remove_kbnode( keyblock, n2 ); goto restart_sig; } } n2 = ncmp? ncmp->next : NULL; } while( n2 ); } } if( (n = find_kbnode( *keyblock, PKT_PUBLIC_KEY )) ) kid1 = keyid_from_pk( n->pkt->pkt.public_key, NULL ); else if( (n = find_kbnode( *keyblock, PKT_SECRET_KEY )) ) kid1 = keyid_from_sk( n->pkt->pkt.secret_key, NULL ); else kid1 = 0; log_info(_("key %08lX: duplicated user ID detected - merged\n"), (ulong)kid1); return 1; } /**************** * compare and merge the blocks * * o compare the signatures: If we already have this signature, check * that they compare okay; if not, issue a warning and ask the user. * o Simply add the signature. Can't verify here because we may not have * the signature's public key yet; verification is done when putting it * into the trustdb, which is done automagically as soon as this pubkey * is used. * Note: We indicate newly inserted packets with flag bit 0 */ static int merge_blocks( const char *fname, KBNODE keyblock_orig, KBNODE keyblock, u32 *keyid, int *n_uids, int *n_sigs, int *n_subk ) { KBNODE onode, node; int rc, found; /* 1st: handle revocation certificates */ for(node=keyblock->next; node; node=node->next ) { if( node->pkt->pkttype == PKT_USER_ID ) break; else if( node->pkt->pkttype == PKT_SIGNATURE && node->pkt->pkt.signature->sig_class == 0x20 ) { /* check whether we already have this */ found = 0; for(onode=keyblock_orig->next; onode; onode=onode->next ) { if( onode->pkt->pkttype == PKT_USER_ID ) break; else if( onode->pkt->pkttype == PKT_SIGNATURE && onode->pkt->pkt.signature->sig_class == 0x20 && node->pkt->pkt.signature->keyid[0] == onode->pkt->pkt.signature->keyid[0] && node->pkt->pkt.signature->keyid[1] == onode->pkt->pkt.signature->keyid[1] ) { found = 1; break; } } if( !found ) { KBNODE n2 = clone_kbnode(node); insert_kbnode( keyblock_orig, n2, 0 ); n2->flag |= 1; log_info( _("key %08lX: revocation certificate added\n"), (ulong)keyid[1]); } } } /* 2nd: try to merge new certificates in */ for(onode=keyblock_orig->next; onode; onode=onode->next ) { if( !(onode->flag & 1) && onode->pkt->pkttype == PKT_USER_ID) { /* find the user id in the imported keyblock */ for(node=keyblock->next; node; node=node->next ) if( node->pkt->pkttype == PKT_USER_ID && !cmp_user_ids( onode->pkt->pkt.user_id, node->pkt->pkt.user_id ) ) break; if( node ) { /* found: merge */ rc = merge_sigs( onode, node, n_sigs, fname, keyid ); if( rc ) return rc; } } } /* 3rd: add new user-ids */ for(node=keyblock->next; node; node=node->next ) { if( node->pkt->pkttype == PKT_USER_ID) { /* do we have this in the original keyblock */ for(onode=keyblock_orig->next; onode; onode=onode->next ) if( onode->pkt->pkttype == PKT_USER_ID && !cmp_user_ids( onode->pkt->pkt.user_id, node->pkt->pkt.user_id ) ) break; if( !onode ) { /* this is a new user id: append */ rc = append_uid( keyblock_orig, node, n_sigs, fname, keyid); if( rc ) return rc; ++*n_uids; } } } /* merge subkey certificates */ for(onode=keyblock_orig->next; onode; onode=onode->next ) { if( !(onode->flag & 1) && ( onode->pkt->pkttype == PKT_PUBLIC_SUBKEY || onode->pkt->pkttype == PKT_SECRET_SUBKEY) ) { /* find the subkey in the imported keyblock */ for(node=keyblock->next; node; node=node->next ) { if( node->pkt->pkttype == PKT_PUBLIC_SUBKEY && !cmp_public_keys( onode->pkt->pkt.public_key, node->pkt->pkt.public_key ) ) break; else if( node->pkt->pkttype == PKT_SECRET_SUBKEY && !cmp_secret_keys( onode->pkt->pkt.secret_key, node->pkt->pkt.secret_key ) ) break; } if( node ) { /* found: merge */ rc = merge_keysigs( onode, node, n_sigs, fname, keyid ); if( rc ) return rc; } } } /* add new subkeys */ for(node=keyblock->next; node; node=node->next ) { onode = NULL; if( node->pkt->pkttype == PKT_PUBLIC_SUBKEY ) { /* do we have this in the original keyblock? */ for(onode=keyblock_orig->next; onode; onode=onode->next ) if( onode->pkt->pkttype == PKT_PUBLIC_SUBKEY && !cmp_public_keys( onode->pkt->pkt.public_key, node->pkt->pkt.public_key ) ) break; if( !onode ) { /* this is a new subkey: append */ rc = append_key( keyblock_orig, node, n_sigs, fname, keyid); if( rc ) return rc; ++*n_subk; } } else if( node->pkt->pkttype == PKT_SECRET_SUBKEY ) { /* do we have this in the original keyblock? */ for(onode=keyblock_orig->next; onode; onode=onode->next ) if( onode->pkt->pkttype == PKT_SECRET_SUBKEY && !cmp_secret_keys( onode->pkt->pkt.secret_key, node->pkt->pkt.secret_key ) ) break; if( !onode ) { /* this is a new subkey: append */ rc = append_key( keyblock_orig, node, n_sigs, fname, keyid); if( rc ) return rc; ++*n_subk; } } } return 0; } /**************** * append the userid starting with NODE and all signatures to KEYBLOCK. */ static int append_uid( KBNODE keyblock, KBNODE node, int *n_sigs, const char *fname, u32 *keyid ) { KBNODE n, n_where=NULL; assert(node->pkt->pkttype == PKT_USER_ID ); if( node->next->pkt->pkttype == PKT_USER_ID ) { log_error( _("key %08lX: our copy has no self-signature\n"), (ulong)keyid[1]); return G10ERR_GENERAL; } /* find the position */ for( n = keyblock; n; n_where = n, n = n->next ) { if( n->pkt->pkttype == PKT_PUBLIC_SUBKEY || n->pkt->pkttype == PKT_SECRET_SUBKEY ) break; } if( !n ) n_where = NULL; /* and append/insert */ while( node ) { /* we add a clone to the original keyblock, because this * one is released first */ n = clone_kbnode(node); if( n_where ) { insert_kbnode( n_where, n, 0 ); n_where = n; } else add_kbnode( keyblock, n ); n->flag |= 1; node->flag |= 1; if( n->pkt->pkttype == PKT_SIGNATURE ) ++*n_sigs; node = node->next; if( node && node->pkt->pkttype != PKT_SIGNATURE ) break; } return 0; } /**************** * Merge the sigs from SRC onto DST. SRC and DST are both a PKT_USER_ID. * (how should we handle comment packets here?) */ static int merge_sigs( KBNODE dst, KBNODE src, int *n_sigs, const char *fname, u32 *keyid ) { KBNODE n, n2; int found=0; assert(dst->pkt->pkttype == PKT_USER_ID ); assert(src->pkt->pkttype == PKT_USER_ID ); /* at least a self signature comes next to the user-ids */ assert(src->next->pkt->pkttype != PKT_USER_ID ); if( dst->next->pkt->pkttype == PKT_USER_ID ) { log_error( _("key %08lX: our copy has no self-signature\n"), (ulong)keyid[1]); return 0; } for(n=src->next; n && n->pkt->pkttype != PKT_USER_ID; n = n->next ) { if( n->pkt->pkttype != PKT_SIGNATURE ) continue; found = 0; for(n2=dst->next; n2 && n2->pkt->pkttype != PKT_USER_ID; n2 = n2->next){ if( n2->pkt->pkttype == PKT_SIGNATURE && n->pkt->pkt.signature->keyid[0] == n2->pkt->pkt.signature->keyid[0] && n->pkt->pkt.signature->keyid[1] == n2->pkt->pkt.signature->keyid[1] && n->pkt->pkt.signature->timestamp <= n2->pkt->pkt.signature->timestamp && n->pkt->pkt.signature->sig_class == n2->pkt->pkt.signature->sig_class ) { found++; break; } } if( !found ) { /* This signature is new or newer, append N to DST. * We add a clone to the original keyblock, because this * one is released first */ n2 = clone_kbnode(n); insert_kbnode( dst, n2, PKT_SIGNATURE ); n2->flag |= 1; n->flag |= 1; ++*n_sigs; } } return 0; } /**************** * Merge the sigs from SRC onto DST. SRC and DST are both a PKT_xxx_SUBKEY. */ static int merge_keysigs( KBNODE dst, KBNODE src, int *n_sigs, const char *fname, u32 *keyid ) { KBNODE n, n2; int found=0; assert( dst->pkt->pkttype == PKT_PUBLIC_SUBKEY || dst->pkt->pkttype == PKT_SECRET_SUBKEY ); for(n=src->next; n ; n = n->next ) { if( n->pkt->pkttype == PKT_PUBLIC_SUBKEY || n->pkt->pkttype == PKT_PUBLIC_KEY ) break; if( n->pkt->pkttype != PKT_SIGNATURE ) continue; found = 0; for(n2=dst->next; n2; n2 = n2->next){ if( n2->pkt->pkttype == PKT_PUBLIC_SUBKEY || n2->pkt->pkttype == PKT_PUBLIC_KEY ) break; if( n2->pkt->pkttype == PKT_SIGNATURE && n->pkt->pkt.signature->keyid[0] == n2->pkt->pkt.signature->keyid[0] && n->pkt->pkt.signature->keyid[1] == n2->pkt->pkt.signature->keyid[1] && n->pkt->pkt.signature->timestamp <= n2->pkt->pkt.signature->timestamp && n->pkt->pkt.signature->sig_class == n2->pkt->pkt.signature->sig_class ) { found++; break; } } if( !found ) { /* This signature is new or newer, append N to DST. * We add a clone to the original keyblock, because this * one is released first */ n2 = clone_kbnode(n); insert_kbnode( dst, n2, PKT_SIGNATURE ); n2->flag |= 1; n->flag |= 1; ++*n_sigs; } } return 0; } /**************** * append the subkey starting with NODE and all signatures to KEYBLOCK. * Mark all new and copied packets by setting flag bit 0. */ static int append_key( KBNODE keyblock, KBNODE node, int *n_sigs, const char *fname, u32 *keyid ) { KBNODE n; assert( node->pkt->pkttype == PKT_PUBLIC_SUBKEY || node->pkt->pkttype == PKT_SECRET_SUBKEY ); while( node ) { /* we add a clone to the original keyblock, because this * one is released first */ n = clone_kbnode(node); add_kbnode( keyblock, n ); n->flag |= 1; node->flag |= 1; if( n->pkt->pkttype == PKT_SIGNATURE ) ++*n_sigs; node = node->next; if( node && node->pkt->pkttype != PKT_SIGNATURE ) break; } return 0; } diff --git a/g10/keydb.h b/g10/keydb.h index 236967511..20a8a6325 100644 --- a/g10/keydb.h +++ b/g10/keydb.h @@ -1,219 +1,220 @@ /* keydb.h - Key database * Copyright (C) 1998 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 G10_KEYDB_H #define G10_KEYDB_H #ifdef HAVE_LIBGDBM #include #endif #include "types.h" #include "packet.h" #include "cipher.h" #define MAX_FINGERPRINT_LEN 20 struct getkey_ctx_s; typedef struct getkey_ctx_s *GETKEY_CTX; /**************** * A Keyblock is all packets which form an entire certificate; * i.e. the public key, certificate, trust packets, user ids, * signatures, and subkey. * * This structure is also used to bind arbitrary packets together. */ typedef struct kbnode_struct *KBNODE; struct kbnode_struct { KBNODE next; PACKET *pkt; int flag; int private_flag; ulong recno; /* used while updating the trustdb */ }; #define is_deleted_kbnode(a) ((a)->private_flag & 1) #define is_cloned_kbnode(a) ((a)->private_flag & 2) enum resource_type { rt_UNKNOWN = 0, rt_RING = 1, rt_GDBM = 2 }; /**************** * A data structre to hold information about the external position * of a keyblock. */ struct keyblock_pos_struct { int resno; /* resource number */ enum resource_type rt; ulong offset; /* position information */ 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; }; typedef struct keyblock_pos_struct KBPOS; /* structure to hold a couple of public key certificates */ typedef struct pk_list *PK_LIST; struct pk_list { PK_LIST next; PKT_public_key *pk; int mark; }; /* structure to hold a couple of secret key certificates */ typedef struct sk_list *SK_LIST; struct sk_list { SK_LIST next; PKT_secret_key *sk; int mark; }; /* structure to collect all information which can be used to * identify a public key */ typedef struct pubkey_find_info *PUBKEY_FIND_INFO; struct pubkey_find_info { u32 keyid[2]; unsigned nbits; byte pubkey_algo; byte fingerprint[MAX_FINGERPRINT_LEN]; char userid[1]; }; /*-- pkclist.c --*/ int check_signatures_trust( PKT_signature *sig ); void release_pk_list( PK_LIST pk_list ); int build_pk_list( STRLIST remusr, PK_LIST *ret_pk_list, unsigned use ); int select_algo_from_prefs( PK_LIST pk_list, int preftype ); /*-- skclist.c --*/ void release_sk_list( SK_LIST sk_list ); int build_sk_list( STRLIST locusr, SK_LIST *ret_sk_list, int unlock, unsigned use ); /*-- passphrase.h --*/ int have_static_passphrase(void); void read_passphrase_from_fd( int fd ); -DEK *passphrase_to_dek( u32 *keyid, int cipher_algo, STRING2KEY *s2k, int mode); +DEK *passphrase_to_dek( u32 *keyid, int pubkey_algo, + int cipher_algo, STRING2KEY *s2k, int mode); void set_next_passphrase( const char *s ); char *get_last_passphrase(void); /*-- getkey.c --*/ int classify_user_id( const char *name, u32 *keyid, byte *fprint, const char **retstr, size_t *retlen ); 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_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 ); void get_pubkey_end( GETKEY_CTX ctx ); int get_seckey( PKT_secret_key *sk, u32 *keyid ); int get_primary_seckey( PKT_secret_key *sk, u32 *keyid ); int get_pubkey_byfprint( PKT_public_key *pk, const byte *fprint, size_t fprint_len ); int get_keyblock_byfprint( KBNODE *ret_keyblock, const byte *fprint, size_t fprint_len ); int get_keyblock_bylid( KBNODE *ret_keyblock, ulong lid ); int seckey_available( u32 *keyid ); int get_seckey_byname( PKT_secret_key *sk, const char *name, int unlock ); int get_seckey_bynames( GETKEY_CTX *rx, PKT_secret_key *sk, STRLIST names, KBNODE *ret_keyblock ); int get_seckey_next( GETKEY_CTX ctx, PKT_secret_key *sk, KBNODE *ret_keyblock ); void get_seckey_end( GETKEY_CTX ctx ); int enum_secret_keys( void **context, PKT_secret_key *sk, int with_subkeys ); void merge_keys_and_selfsig( KBNODE keyblock ); char*get_user_id_string( u32 *keyid ); char*get_long_user_id_string( u32 *keyid ); char*get_user_id( u32 *keyid, size_t *rn ); /*-- keyid.c --*/ int pubkey_letter( int algo ); u32 keyid_from_sk( PKT_secret_key *sk, u32 *keyid ); u32 keyid_from_pk( PKT_public_key *pk, u32 *keyid ); u32 keyid_from_sig( PKT_signature *sig, u32 *keyid ); u32 keyid_from_fingerprint( const byte *fprint, size_t fprint_len, u32 *keyid ); unsigned nbits_from_pk( PKT_public_key *pk ); unsigned nbits_from_sk( PKT_secret_key *sk ); const char *datestr_from_pk( PKT_public_key *pk ); const char *datestr_from_sk( PKT_secret_key *sk ); const char *datestr_from_sig( PKT_signature *sig ); const char *expirestr_from_pk( PKT_public_key *pk ); const char *expirestr_from_sk( PKT_secret_key *sk ); byte *fingerprint_from_sk( PKT_secret_key *sk, byte *buf, size_t *ret_len ); byte *fingerprint_from_pk( PKT_public_key *pk, byte *buf, size_t *ret_len ); /*-- kbnode.c --*/ KBNODE new_kbnode( PACKET *pkt ); KBNODE clone_kbnode( KBNODE node ); void release_kbnode( KBNODE n ); void delete_kbnode( KBNODE node ); void add_kbnode( KBNODE root, KBNODE node ); void insert_kbnode( KBNODE root, KBNODE node, int pkttype ); void move_kbnode( KBNODE *root, KBNODE node, KBNODE where ); void remove_kbnode( KBNODE *root, KBNODE node ); KBNODE find_prev_kbnode( KBNODE root, KBNODE node, int pkttype ); KBNODE find_next_kbnode( KBNODE node, int pkttype ); KBNODE find_kbnode( KBNODE node, int pkttype ); KBNODE walk_kbnode( KBNODE root, KBNODE *context, int all ); 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 ); int locate_keyblock_by_fpr( KBPOS *kbpos, const byte *fpr, int fprlen, int secret ); int locate_keyblock_by_keyid( KBPOS *kbpos, u32 *keyid, int shortkid, 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_keyblock_bysk( KBPOS *kbpos, PKT_secret_key *sk ); int find_secret_keyblock_byname( KBPOS *kbpos, const char *username ); 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 df14f13e1..e93634034 100644 --- a/g10/keyedit.c +++ b/g10/keyedit.c @@ -1,1883 +1,1897 @@ /* keyedit.c - keyedit stuff * Copyright (C) 1998, 1999 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 "options.h" #include "packet.h" #include "errors.h" #include "iobuf.h" #include "keydb.h" #include "memory.h" #include "util.h" #include "main.h" #include "trustdb.h" #include "filter.h" #include "ttyio.h" #include "status.h" #include "i18n.h" static void show_prefs( KBNODE keyblock, PKT_user_id *uid ); static void show_key_with_all_names( KBNODE keyblock, int only_marked, int with_fpr, int with_subkeys, int with_prefs ); static void show_key_and_fingerprint( KBNODE keyblock ); static void show_fingerprint( PKT_public_key *pk ); static int menu_adduid( KBNODE keyblock, KBNODE sec_keyblock ); static void menu_deluid( KBNODE pub_keyblock, KBNODE sec_keyblock ); static int menu_delsig( KBNODE pub_keyblock ); static void menu_delkey( KBNODE pub_keyblock, KBNODE sec_keyblock ); static int menu_expire( KBNODE pub_keyblock, KBNODE sec_keyblock ); static int menu_select_uid( KBNODE keyblock, int idx ); static int menu_select_key( KBNODE keyblock, int idx ); static int count_uids( KBNODE keyblock ); static int count_uids_with_flag( KBNODE keyblock, unsigned flag ); static int count_keys_with_flag( KBNODE keyblock, unsigned flag ); static int count_selected_uids( KBNODE keyblock ); static int count_selected_keys( KBNODE keyblock ); static int menu_revsig( KBNODE keyblock ); static int menu_revkey( KBNODE pub_keyblock, KBNODE sec_keyblock ); static int enable_disable_key( KBNODE keyblock, int disable ); #define CONTROL_D ('D' - 'A' + 1) #define NODFLG_BADSIG (1<<0) /* bad signature */ #define NODFLG_NOKEY (1<<1) /* no public key */ #define NODFLG_SIGERR (1<<2) /* other sig error */ #define NODFLG_MARK_A (1<<4) /* temporary mark */ #define NODFLG_SELUID (1<<8) /* indicate the selected userid */ #define NODFLG_SELKEY (1<<9) /* indicate the selected key */ #define NODFLG_SELSIG (1<<10) /* indicate a selected signature */ struct sign_uid_attrib { int non_exportable; }; 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, chek it and return true * if the signature is okay. NODE must be a signature packet. */ static int print_and_check_one_sig( KBNODE keyblock, KBNODE node, int *inv_sigs, int *no_key, int *oth_err, int *is_selfsig, int print_without_key ) { PKT_signature *sig = node->pkt->pkt.signature; int rc, sigrc; int is_rev = sig->sig_class == 0x30; switch( (rc = check_key_signature( keyblock, node, is_selfsig)) ) { case 0: node->flag &= ~(NODFLG_BADSIG|NODFLG_NOKEY|NODFLG_SIGERR); sigrc = '!'; break; case G10ERR_BAD_SIGN: node->flag = NODFLG_BADSIG; sigrc = '-'; if( inv_sigs ) ++*inv_sigs; break; case G10ERR_NO_PUBKEY: node->flag = NODFLG_NOKEY; sigrc = '?'; if( no_key ) ++*no_key; break; default: node->flag = NODFLG_SIGERR; sigrc = '%'; if( oth_err ) ++*oth_err; break; } if( sigrc != '?' || print_without_key ) { tty_printf("%s%c %08lX %s ", is_rev? "rev":"sig", sigrc, sig->keyid[1], datestr_from_sig(sig)); if( sigrc == '%' ) tty_printf("[%s] ", g10_errstr(rc) ); else if( sigrc == '?' ) ; else if( *is_selfsig ) { tty_printf( is_rev? _("[revocation]") : _("[self-signature]") ); } else { size_t n; char *p = get_user_id( sig->keyid, &n ); tty_print_string( p, n > 40? 40 : n ); m_free(p); } tty_printf("\n"); } return (sigrc == '!'); } /**************** * Check the keysigs and set the flags to indicate errors. * Returns true if error found. */ static int check_all_keysigs( KBNODE keyblock, int only_selected ) { KBNODE kbctx; KBNODE node; int inv_sigs = 0; int no_key = 0; int oth_err = 0; int has_selfsig = 0; int mis_selfsig = 0; int selected = !only_selected; int anyuid = 0; for( kbctx=NULL; (node=walk_kbnode( keyblock, &kbctx, 0)) ; ) { if( node->pkt->pkttype == PKT_USER_ID ) { PKT_user_id *uid = node->pkt->pkt.user_id; if( only_selected ) selected = (node->flag & NODFLG_SELUID); if( selected ) { tty_printf("uid "); tty_print_string( uid->name, uid->len ); tty_printf("\n"); if( anyuid && !has_selfsig ) mis_selfsig++; has_selfsig = 0; anyuid = 1; } } else if( selected && node->pkt->pkttype == PKT_SIGNATURE && ( (node->pkt->pkt.signature->sig_class&~3) == 0x10 || node->pkt->pkt.signature->sig_class == 0x30 ) ) { int selfsig; if( print_and_check_one_sig( keyblock, node, &inv_sigs, &no_key, &oth_err, &selfsig, 0 ) ) { if( selfsig ) has_selfsig = 1; } /* Hmmm: should we update the trustdb here? */ } } if( !has_selfsig ) mis_selfsig++; if( inv_sigs == 1 ) tty_printf(_("1 bad signature\n"), inv_sigs ); else if( inv_sigs ) tty_printf(_("%d bad signatures\n"), inv_sigs ); if( no_key == 1 ) tty_printf(_("1 signature not checked due to a missing key\n") ); else if( no_key ) tty_printf(_("%d signatures not checked due to missing keys\n"), no_key ); if( oth_err == 1 ) tty_printf(_("1 signature not checked due to an error\n") ); else if( oth_err ) tty_printf(_("%d signatures not checked due to errors\n"), oth_err ); if( mis_selfsig == 1 ) tty_printf(_("1 user id without valid self-signature detected\n")); else if( mis_selfsig ) tty_printf(_("%d user ids without valid self-signatures detected\n"), mis_selfsig); return inv_sigs || no_key || oth_err || mis_selfsig; } int sign_uid_mk_attrib( PKT_signature *sig, void *opaque ) { struct sign_uid_attrib *attrib = opaque; byte buf[8]; if( attrib->non_exportable ) { buf[0] = 0; /* not exportable */ build_sig_subpkt( sig, SIGSUBPKT_EXPORTABLE, buf, 1 ); } return 0; } /**************** * Loop over all locusr and and sign the uids after asking. * If no user id is marked, all user ids will be signed; * if some user_ids are marked those will be signed. */ static int sign_uids( KBNODE keyblock, STRLIST locusr, int *ret_modified, int local ) { int rc = 0; SK_LIST sk_list = NULL; SK_LIST sk_rover = NULL; PKT_secret_key *sk = NULL; KBNODE node, uidnode; PKT_public_key *primary_pk=NULL; int select_all = !count_selected_uids(keyblock); int upd_trust = 0; /* build a list of all signators */ rc=build_sk_list( locusr, &sk_list, 0, 1 ); if( rc ) goto leave; /* loop over all signaturs */ for( sk_rover = sk_list; sk_rover; sk_rover = sk_rover->next ) { u32 sk_keyid[2]; size_t n; char *p; /* we have to use a copy of the sk, because make_keysig_packet * may remove the protection from sk and if we did other * changes to the secret key, we would save the unprotected * version */ if( sk ) free_secret_key(sk); sk = copy_secret_key( NULL, sk_rover->sk ); keyid_from_sk( sk, sk_keyid ); /* set mark A for all selected user ids */ for( node=keyblock; node; node = node->next ) { if( select_all || (node->flag & NODFLG_SELUID) ) node->flag |= NODFLG_MARK_A; else node->flag &= ~NODFLG_MARK_A; } /* reset mark for uids which are already signed */ uidnode = NULL; for( node=keyblock; node; node = node->next ) { if( node->pkt->pkttype == PKT_USER_ID ) { uidnode = (node->flag & NODFLG_MARK_A)? node : NULL; } else if( uidnode && node->pkt->pkttype == PKT_SIGNATURE && (node->pkt->pkt.signature->sig_class&~3) == 0x10 ) { if( sk_keyid[0] == node->pkt->pkt.signature->keyid[0] && sk_keyid[1] == node->pkt->pkt.signature->keyid[1] ) { /* Fixme: see whether there is a revocation in which * case we should allow to sign it again. */ tty_printf(_("Already signed by key %08lX\n"), (ulong)sk_keyid[1] ); uidnode->flag &= ~NODFLG_MARK_A; /* remove mark */ } } } /* check whether any uids are left for signing */ if( !count_uids_with_flag(keyblock, NODFLG_MARK_A) ) { tty_printf(_("Nothing to sign with key %08lX\n"), (ulong)sk_keyid[1] ); continue; } /* Ask whether we really should sign these user id(s) */ tty_printf("\n"); show_key_with_all_names( keyblock, 1, 1, 0, 0 ); tty_printf("\n"); tty_printf(_( "Are you really sure that you want to sign this key\n" "with your key: \"")); p = get_user_id( sk_keyid, &n ); tty_print_string( p, n ); m_free(p); p = NULL; tty_printf("\"\n\n"); if( local ) tty_printf( _("The signature will be marked as non-exportable.\n\n")); if( opt.batch && opt.answer_yes ) ; else if( !cpr_get_answer_is_yes("sign_uid.okay", _("Really sign? ")) ) continue; /* now we can sign the user ids */ reloop: /* (must use this, because we are modifing the list) */ primary_pk = NULL; for( node=keyblock; node; node = node->next ) { if( node->pkt->pkttype == PKT_PUBLIC_KEY ) primary_pk = node->pkt->pkt.public_key; else if( node->pkt->pkttype == PKT_USER_ID && (node->flag & NODFLG_MARK_A) ) { PACKET *pkt; PKT_signature *sig; struct sign_uid_attrib attrib; assert( primary_pk ); memset( &attrib, 0, sizeof attrib ); attrib.non_exportable = local; node->flag &= ~NODFLG_MARK_A; rc = make_keysig_packet( &sig, primary_pk, node->pkt->pkt.user_id, NULL, sk, 0x10, 0, sign_uid_mk_attrib, &attrib ); if( rc ) { log_error(_("signing failed: %s\n"), g10_errstr(rc)); goto leave; } *ret_modified = 1; /* we changed the keyblock */ upd_trust = 1; pkt = m_alloc_clear( sizeof *pkt ); pkt->pkttype = PKT_SIGNATURE; pkt->pkt.signature = sig; insert_kbnode( node, new_kbnode(pkt), PKT_SIGNATURE ); goto reloop; } } } /* end loop over signators */ if( upd_trust && primary_pk ) { rc = clear_trust_checked_flag( primary_pk ); } leave: release_sk_list( sk_list ); if( sk ) free_secret_key(sk); return rc; } /**************** * Change the passphrase of the primary and all secondary keys. * We use only one passphrase for all keys. */ static int change_passphrase( KBNODE keyblock ) { int rc = 0; int changed=0; KBNODE node; PKT_secret_key *sk; char *passphrase = NULL; node = find_kbnode( keyblock, PKT_SECRET_KEY ); if( !node ) { log_error("Oops; secret key not found anymore!\n"); goto leave; } sk = node->pkt->pkt.secret_key; switch( is_secret_key_protected( sk ) ) { case -1: rc = G10ERR_PUBKEY_ALGO; break; case 0: tty_printf(_("This key is not protected.\n")); break; default: tty_printf(_("Key is protected.\n")); rc = check_secret_key( sk, 0 ); if( !rc ) passphrase = get_last_passphrase(); break; } /* unprotect all subkeys (use the supplied passphrase or ask)*/ for(node=keyblock; !rc && node; node = node->next ) { if( node->pkt->pkttype == PKT_SECRET_SUBKEY ) { PKT_secret_key *subsk = node->pkt->pkt.secret_key; set_next_passphrase( passphrase ); rc = check_secret_key( subsk, 0 ); } } if( rc ) tty_printf(_("Can't edit this key: %s\n"), g10_errstr(rc)); else { DEK *dek = NULL; STRING2KEY *s2k = m_alloc_secure( sizeof *s2k ); tty_printf(_("Enter the new passphrase for this secret key.\n\n") ); set_next_passphrase( NULL ); for(;;) { s2k->mode = opt.s2k_mode; s2k->hash_algo = opt.s2k_digest_algo; - dek = passphrase_to_dek( NULL, opt.s2k_cipher_algo, s2k, 2 ); + dek = passphrase_to_dek( NULL, 0, opt.s2k_cipher_algo, s2k, 2 ); if( !dek ) { tty_printf(_("passphrase not correctly repeated; try again.\n")); } else if( !dek->keylen ) { rc = 0; tty_printf(_( "You don't want a passphrase -" " this is probably a *bad* idea!\n\n")); if( cpr_get_answer_is_yes("change_passwd.empty.okay", _("Do you really want to do this? "))) changed++; break; } else { /* okay */ sk->protect.algo = dek->algo; sk->protect.s2k = *s2k; rc = protect_secret_key( sk, dek ); for(node=keyblock; !rc && node; node = node->next ) { if( node->pkt->pkttype == PKT_SECRET_SUBKEY ) { PKT_secret_key *subsk = node->pkt->pkt.secret_key; subsk->protect.algo = dek->algo; subsk->protect.s2k = *s2k; rc = protect_secret_key( subsk, dek ); } } if( rc ) log_error("protect_secret_key failed: %s\n", g10_errstr(rc) ); else changed++; break; } } m_free(s2k); m_free(dek); } leave: m_free( passphrase ); set_next_passphrase( NULL ); return changed && !rc; } /**************** * There are some keys out (due to a bug in gnupg), where the sequence * of the packets is wrong. This function fixes that. * Returns: true if the keyblock has been fixed. * * Note: This function does not work if there is more than one user ID. */ static int fix_keyblock( KBNODE keyblock ) { KBNODE node, last, subkey; int fixed=0; /* locate key signatures of class 0x10..0x13 behind sub key packets */ for( subkey=last=NULL, node = keyblock; node; last=node, node = node->next ) { switch( node->pkt->pkttype ) { case PKT_PUBLIC_SUBKEY: case PKT_SECRET_SUBKEY: if( !subkey ) subkey = last; /* actually it is the one before the subkey */ break; case PKT_SIGNATURE: if( subkey ) { PKT_signature *sig = node->pkt->pkt.signature; if( sig->sig_class >= 0x10 && sig->sig_class <= 0x13 ) { log_info(_( "moving a key signature to the correct place\n")); last->next = node->next; node->next = subkey->next; subkey->next = node; node = last; fixed=1; } } break; default: break; } } return fixed; } /**************** * Menu driven key editor * * Note: to keep track of some selection we use node->mark MARKBIT_xxxx. */ void keyedit_menu( const char *username, STRLIST locusr, STRLIST commands ) { enum cmdids { cmdNONE = 0, cmdQUIT, cmdHELP, cmdFPR, cmdLIST, cmdSELUID, cmdCHECK, cmdSIGN, cmdLSIGN, cmdREVSIG, cmdREVKEY, cmdDELSIG, cmdDEBUG, cmdSAVE, cmdADDUID, cmdDELUID, cmdADDKEY, cmdDELKEY, cmdTOGGLE, cmdSELKEY, cmdPASSWD, cmdTRUST, cmdPREF, cmdEXPIRE, cmdENABLEKEY, cmdDISABLEKEY, cmdNOP }; static struct { const char *name; enum cmdids id; int need_sk; const char *desc; } cmds[] = { { N_("quit") , cmdQUIT , 0, N_("quit this menu") }, { N_("q") , cmdQUIT , 0, NULL }, { N_("save") , cmdSAVE , 0, N_("save and quit") }, { N_("help") , cmdHELP , 0, N_("show this help") }, { "?" , cmdHELP , 0, NULL }, { N_("fpr") , cmdFPR , 0, N_("show fingerprint") }, { N_("list") , cmdLIST , 0, N_("list key and user ids") }, { N_("l") , cmdLIST , 0, NULL }, { N_("uid") , cmdSELUID , 0, N_("select user id N") }, { N_("key") , cmdSELKEY , 0, N_("select secondary key N") }, { N_("check") , cmdCHECK , 0, N_("list signatures") }, { N_("c") , cmdCHECK , 0, NULL }, { N_("sign") , cmdSIGN , 0, N_("sign the key") }, { N_("s") , cmdSIGN , 0, NULL }, { N_("lsign") , cmdLSIGN , 0, N_("sign the key locally") }, { N_("debug") , cmdDEBUG , 0, NULL }, { N_("adduid") , cmdADDUID , 1, N_("add a user id") }, { N_("deluid") , cmdDELUID , 0, N_("delete user id") }, { N_("addkey") , cmdADDKEY , 1, N_("add a secondary key") }, { N_("delkey") , cmdDELKEY , 0, N_("delete a secondary key") }, { N_("delsig") , cmdDELSIG , 0, N_("delete signatures") }, { N_("expire") , cmdEXPIRE , 1, N_("change the expire date") }, { N_("toggle") , cmdTOGGLE , 1, N_("toggle between secret " "and public key listing") }, { N_("t" ) , cmdTOGGLE , 1, NULL }, { N_("pref") , cmdPREF , 0, N_("list preferences") }, { N_("passwd") , cmdPASSWD , 1, N_("change the passphrase") }, { N_("trust") , cmdTRUST , 0, N_("change the ownertrust") }, { N_("revsig") , cmdREVSIG , 0, N_("revoke signatures") }, { N_("revkey") , cmdREVKEY , 1, N_("revoke a secondary key") }, { N_("disable") , cmdDISABLEKEY , 0, N_("disable a key") }, { N_("enable") , cmdENABLEKEY , 0, N_("enable a key") }, { NULL, cmdNONE } }; enum cmdids cmd; int rc = 0; KBNODE keyblock = NULL; KBPOS keyblockpos; KBNODE sec_keyblock = NULL; KBPOS sec_keyblockpos; KBNODE cur_keyblock; char *answer = NULL; int redisplay = 1; int modified = 0; int sec_modified = 0; int toggle; int have_commands = !!commands; if( opt.batch && !have_commands ) { log_error(_("can't do that in batchmode\n")); goto leave; } /* first try to locate it as secret key */ rc = find_secret_keyblock_byname( &sec_keyblockpos, username ); if( !rc ) { rc = read_keyblock( &sec_keyblockpos, &sec_keyblock ); if( rc ) { log_error("%s: secret keyblock read problem: %s\n", username, g10_errstr(rc)); goto leave; } merge_keys_and_selfsig( sec_keyblock ); if( fix_keyblock( sec_keyblock ) ) sec_modified++; } /* and now get the public key */ rc = get_keyblock_byname( &keyblock, &keyblockpos, username ); if( rc ) goto leave; if( fix_keyblock( keyblock ) ) modified++; if( collapse_uids( &keyblock ) ) modified++; if( sec_keyblock ) { /* check that they match */ /* FIXME: check that they both match */ tty_printf(_("Secret key is available.\n")); } toggle = 0; cur_keyblock = keyblock; for(;;) { /* main loop */ int i, arg_number; char *p; tty_printf("\n"); if( redisplay ) { show_key_with_all_names( cur_keyblock, 0, 0, 1, 0 ); tty_printf("\n"); redisplay = 0; } do { m_free(answer); if( have_commands ) { if( commands ) { answer = m_strdup( commands->d ); commands = commands->next; } else if( opt.batch ) { answer = m_strdup("quit"); } else have_commands = 0; } if( !have_commands ) { answer = cpr_get("", _("Command> ")); cpr_kill_prompt(); } trim_spaces(answer); } while( *answer == '#' ); arg_number = 0; if( !*answer ) cmd = cmdLIST; else if( *answer == CONTROL_D ) cmd = cmdQUIT; else if( isdigit( *answer ) ) { cmd = cmdSELUID; arg_number = atoi(answer); } else { if( (p=strchr(answer,' ')) ) { *p++ = 0; trim_spaces(answer); trim_spaces(p); arg_number = atoi(p); } for(i=0; cmds[i].name; i++ ) if( !stricmp( answer, cmds[i].name ) ) break; if( cmds[i].need_sk && !sec_keyblock ) { tty_printf(_("Need the secret key to do this.\n")); cmd = cmdNOP; } else cmd = cmds[i].id; } switch( cmd ) { case cmdHELP: for(i=0; cmds[i].name; i++ ) { if( cmds[i].need_sk && !sec_keyblock ) ; /* skip if we do not have the secret key */ else if( cmds[i].desc ) tty_printf("%-10s %s\n", cmds[i].name, _(cmds[i].desc) ); } break; case cmdQUIT: if( have_commands ) goto leave; if( !modified && !sec_modified ) goto leave; if( !cpr_get_answer_is_yes("keyedit.save.okay", _("Save changes? ")) ) { if( cpr_enabled() || cpr_get_answer_is_yes("keyedit.cancel.okay", _("Quit without saving? ")) ) goto leave; break; } /* fall thru */ case cmdSAVE: if( modified || sec_modified ) { if( modified ) { rc = update_keyblock( &keyblockpos, keyblock ); if( rc ) { log_error(_("update failed: %s\n"), g10_errstr(rc) ); break; } } if( sec_modified ) { rc = update_keyblock( &sec_keyblockpos, sec_keyblock ); if( rc ) { log_error(_("update secret failed: %s\n"), g10_errstr(rc) ); break; } } } else tty_printf(_("Key not changed so no update needed.\n")); rc = update_trust_record( keyblock, 0, NULL ); if( rc ) log_error(_("update of trustdb failed: %s\n"), g10_errstr(rc) ); goto leave; case cmdLIST: redisplay = 1; break; case cmdFPR: show_key_and_fingerprint( keyblock ); break; case cmdSELUID: if( menu_select_uid( cur_keyblock, arg_number ) ) redisplay = 1; break; case cmdSELKEY: if( menu_select_key( cur_keyblock, arg_number ) ) redisplay = 1; break; case cmdCHECK: /* we can only do this with the public key becuase the * check functions can't cope with secret keys and it * is questionable whether this would make sense at all */ check_all_keysigs( keyblock, count_selected_uids(keyblock) ); break; case cmdSIGN: /* sign (only the public key) */ case cmdLSIGN: /* sign (only the public key) */ if( count_uids(keyblock) > 1 && !count_selected_uids(keyblock) ) { if( !cpr_get_answer_is_yes("keyedit.sign_all.okay", _("Really sign all user ids? ")) ) { tty_printf(_("Hint: Select the user ids to sign\n")); break; } } sign_uids( keyblock, locusr, &modified, cmd == cmdLSIGN ); break; case cmdDEBUG: dump_kbnode( cur_keyblock ); break; case cmdTOGGLE: toggle = !toggle; cur_keyblock = toggle? sec_keyblock : keyblock; redisplay = 1; break; case cmdADDUID: if( menu_adduid( keyblock, sec_keyblock ) ) { redisplay = 1; sec_modified = modified = 1; /* must update the trustdb already here, so that preferences * get listed correctly */ rc = update_trust_record( keyblock, 0, NULL ); if( rc ) { log_error(_("update of trustdb failed: %s\n"), g10_errstr(rc) ); rc = 0; } } break; case cmdDELUID: { int n1; if( !(n1=count_selected_uids(keyblock)) ) tty_printf(_("You must select at least one user id.\n")); else if( count_uids(keyblock) - n1 < 1 ) tty_printf(_("You can't delete the last user id!\n")); else if( cpr_get_answer_is_yes( "keyedit.remove.uid.okay", n1 > 1? _("Really remove all selected user ids? ") : _("Really remove this user id? ") ) ) { menu_deluid( keyblock, sec_keyblock ); redisplay = 1; modified = 1; if( sec_keyblock ) sec_modified = 1; } } break; case cmdDELSIG: { int n1; if( !(n1=count_selected_uids(keyblock)) ) tty_printf(_("You must select at least one user id.\n")); else if( menu_delsig( keyblock ) ) { /* no redisplay here, because it may scroll away some * status output of delsig */ modified = 1; } } break; case cmdADDKEY: if( generate_subkeypair( keyblock, sec_keyblock ) ) { redisplay = 1; sec_modified = modified = 1; } break; case cmdDELKEY: { int n1; if( !(n1=count_selected_keys( keyblock )) ) tty_printf(_("You must select at least one key.\n")); else if( sec_keyblock && !cpr_get_answer_is_yes( "keyedit.remove.subkey.okay", n1 > 1? _("Do you really want to delete the selected keys? "): _("Do you really want to delete this key? ") )) ; else { menu_delkey( keyblock, sec_keyblock ); redisplay = 1; modified = 1; if( sec_keyblock ) sec_modified = 1; } } break; case cmdREVKEY: { int n1; if( !(n1=count_selected_keys( keyblock )) ) tty_printf(_("You must select at least one key.\n")); else if( sec_keyblock && !cpr_get_answer_is_yes( "keyedit.revoke.subkey.okay", n1 > 1? _("Do you really want to revoke the selected keys? "): _("Do you really want to revoke this key? ") )) ; else { if( menu_revkey( keyblock, sec_keyblock ) ) { modified = 1; /*sec_modified = 1;*/ } redisplay = 1; } } break; case cmdEXPIRE: if( menu_expire( keyblock, sec_keyblock ) ) { merge_keys_and_selfsig( sec_keyblock ); merge_keys_and_selfsig( keyblock ); sec_modified = 1; modified = 1; redisplay = 1; } break; case cmdPASSWD: if( change_passphrase( sec_keyblock ) ) sec_modified = 1; break; case cmdTRUST: show_key_with_all_names( keyblock, 0, 0, 1, 0 ); tty_printf("\n"); if( edit_ownertrust( find_kbnode( keyblock, PKT_PUBLIC_KEY )->pkt->pkt.public_key->local_id, 1 ) ) redisplay = 1; /* we don't need to set modified here, as the trustvalues * are updated immediately */ break; case cmdPREF: show_key_with_all_names( keyblock, 0, 0, 0, 1 ); break; case cmdNOP: break; case cmdREVSIG: if( menu_revsig( keyblock ) ) { redisplay = 1; modified = 1; } break; case cmdENABLEKEY: case cmdDISABLEKEY: if( enable_disable_key( keyblock, cmd == cmdDISABLEKEY ) ) { redisplay = 1; modified = 1; } break; default: tty_printf("\n"); tty_printf(_("Invalid command (try \"help\")\n")); break; } } /* end main loop */ leave: release_kbnode( keyblock ); release_kbnode( sec_keyblock ); m_free(answer); } /**************** * show preferences of a public keyblock. */ static void show_prefs( KBNODE keyblock, PKT_user_id *uid ) { KBNODE node = find_kbnode( keyblock, PKT_PUBLIC_KEY ); PKT_public_key *pk; byte *p; int i; size_t n; byte namehash[20]; if( !node ) return; /* is a secret keyblock */ pk = node->pkt->pkt.public_key; if( !pk->local_id ) { log_error("oops: no LID\n"); return; } rmd160_hash_buffer( namehash, uid->name, uid->len ); p = get_pref_data( pk->local_id, namehash, &n ); if( !p ) return; tty_printf(" "); for(i=0; i < n; i+=2 ) { if( p[i] ) tty_printf( " %c%d", p[i] == PREFTYPE_SYM ? 'S' : p[i] == PREFTYPE_HASH ? 'H' : p[i] == PREFTYPE_COMPR ? 'Z' : '?', p[i+1]); } tty_printf("\n"); m_free(p); } /**************** * Display the key a the user ids, if only_marked is true, do only * so for user ids with mark A flag set and dont display the index number */ static void show_key_with_all_names( KBNODE keyblock, int only_marked, int with_fpr, int with_subkeys, int with_prefs ) { KBNODE node; int i, rc; /* the keys */ for( node = keyblock; node; node = node->next ) { if( node->pkt->pkttype == PKT_PUBLIC_KEY || (with_subkeys && node->pkt->pkttype == PKT_PUBLIC_SUBKEY) ) { PKT_public_key *pk = node->pkt->pkt.public_key; int otrust=0, trust=0; if( node->pkt->pkttype == PKT_PUBLIC_KEY ) { /* do it here, so that debug messages don't clutter the * output */ trust = query_trust_info(pk, NULL); otrust = get_ownertrust_info( pk->local_id ); } tty_printf("%s%c %4u%c/%08lX created: %s expires: %s", node->pkt->pkttype == PKT_PUBLIC_KEY? "pub":"sub", (node->flag & NODFLG_SELKEY)? '*':' ', nbits_from_pk( pk ), pubkey_letter( pk->pubkey_algo ), (ulong)keyid_from_pk(pk,NULL), datestr_from_pk(pk), expirestr_from_pk(pk) ); if( node->pkt->pkttype == PKT_PUBLIC_KEY ) { tty_printf(" trust: %c/%c", otrust, trust ); + if( node->pkt->pkttype == PKT_PUBLIC_KEY + && (get_ownertrust( pk->local_id )&TRUST_FLAG_DISABLED)) { + tty_printf("\n*** "); + tty_printf(_("This key has been disabled")); + } + if( with_fpr ) { tty_printf("\n"); show_fingerprint( pk ); } } tty_printf("\n"); } else if( node->pkt->pkttype == PKT_SECRET_KEY || (with_subkeys && node->pkt->pkttype == PKT_SECRET_SUBKEY) ) { PKT_secret_key *sk = node->pkt->pkt.secret_key; tty_printf("%s%c %4u%c/%08lX created: %s expires: %s\n", node->pkt->pkttype == PKT_SECRET_KEY? "sec":"sbb", (node->flag & NODFLG_SELKEY)? '*':' ', nbits_from_sk( sk ), pubkey_letter( sk->pubkey_algo ), (ulong)keyid_from_sk(sk,NULL), datestr_from_sk(sk), expirestr_from_sk(sk) ); } else if( with_subkeys && node->pkt->pkttype == PKT_SIGNATURE && node->pkt->pkt.signature->sig_class == 0x28 ) { PKT_signature *sig = node->pkt->pkt.signature; rc = check_key_signature( keyblock, node, NULL ); if( !rc ) tty_printf( "rev! subkey has been revoked: %s\n", datestr_from_sig( sig ) ); else if( rc == G10ERR_BAD_SIGN ) tty_printf( "rev- faked revocation found\n" ); else if( rc ) tty_printf( "rev? problem checking revocation: %s\n", g10_errstr(rc) ); } } /* the user ids */ i = 0; for( node = keyblock; node; node = node->next ) { if( node->pkt->pkttype == PKT_USER_ID ) { PKT_user_id *uid = node->pkt->pkt.user_id; ++i; if( !only_marked || (only_marked && (node->flag & NODFLG_MARK_A))){ if( only_marked ) tty_printf(" "); else if( node->flag & NODFLG_SELUID ) tty_printf("(%d)* ", i); else tty_printf("(%d) ", i); tty_print_string( uid->name, uid->len ); tty_printf("\n"); if( with_prefs ) show_prefs( keyblock, uid ); } } } } static void show_key_and_fingerprint( KBNODE keyblock ) { KBNODE node; PKT_public_key *pk = NULL; for( node = keyblock; node; node = node->next ) { if( node->pkt->pkttype == PKT_PUBLIC_KEY ) { pk = node->pkt->pkt.public_key; tty_printf("pub %4u%c/%08lX %s ", nbits_from_pk( pk ), pubkey_letter( pk->pubkey_algo ), (ulong)keyid_from_pk(pk,NULL), datestr_from_pk(pk) ); } else if( node->pkt->pkttype == PKT_USER_ID ) { PKT_user_id *uid = node->pkt->pkt.user_id; tty_print_string( uid->name, uid->len ); break; } } tty_printf("\n"); if( pk ) show_fingerprint( pk ); } static void show_fingerprint( PKT_public_key *pk ) { byte array[MAX_FINGERPRINT_LEN], *p; size_t i, n; fingerprint_from_pk( pk, array, &n ); p = array; tty_printf(" Fingerprint:"); if( n == 20 ) { for(i=0; i < n ; i++, i++, p += 2 ) { if( i == 10 ) tty_printf(" "); tty_printf(" %02X%02X", *p, p[1] ); } } else { for(i=0; i < n ; i++, p++ ) { if( i && !(i%8) ) tty_printf(" "); tty_printf(" %02X", *p ); } } tty_printf("\n"); } /**************** * Ask for a new user id , do the selfsignature and put it into * both keyblocks. * Return true if there is a new user id */ static int menu_adduid( KBNODE pub_keyblock, KBNODE sec_keyblock ) { PKT_user_id *uid; PKT_public_key *pk=NULL; PKT_secret_key *sk=NULL; PKT_signature *sig=NULL; PACKET *pkt; KBNODE node; KBNODE pub_where=NULL, sec_where=NULL; int rc; uid = generate_user_id(); if( !uid ) return 0; for( node = pub_keyblock; node; pub_where = node, node = node->next ) { if( node->pkt->pkttype == PKT_PUBLIC_KEY ) pk = node->pkt->pkt.public_key; else if( node->pkt->pkttype == PKT_PUBLIC_SUBKEY ) break; } if( !node ) /* no subkey */ pub_where = NULL; for( node = sec_keyblock; node; sec_where = node, node = node->next ) { if( node->pkt->pkttype == PKT_SECRET_KEY ) sk = copy_secret_key( NULL, node->pkt->pkt.secret_key); else if( node->pkt->pkttype == PKT_SECRET_SUBKEY ) break; } if( !node ) /* no subkey */ sec_where = NULL; assert(pk && sk ); rc = make_keysig_packet( &sig, pk, uid, NULL, sk, 0x13, 0, keygen_add_std_prefs, pk ); free_secret_key( sk ); if( rc ) { log_error("signing failed: %s\n", g10_errstr(rc) ); free_user_id(uid); return 0; } /* insert/append to secret keyblock */ pkt = m_alloc_clear( sizeof *pkt ); pkt->pkttype = PKT_USER_ID; pkt->pkt.user_id = copy_user_id(NULL, uid); node = new_kbnode(pkt); if( sec_where ) insert_kbnode( sec_where, node, 0 ); else add_kbnode( sec_keyblock, node ); pkt = m_alloc_clear( sizeof *pkt ); pkt->pkttype = PKT_SIGNATURE; pkt->pkt.signature = copy_signature(NULL, sig); if( sec_where ) insert_kbnode( node, new_kbnode(pkt), 0 ); else add_kbnode( sec_keyblock, new_kbnode(pkt) ); /* insert/append to public keyblock */ pkt = m_alloc_clear( sizeof *pkt ); pkt->pkttype = PKT_USER_ID; pkt->pkt.user_id = uid; node = new_kbnode(pkt); if( pub_where ) insert_kbnode( pub_where, node, 0 ); else add_kbnode( pub_keyblock, node ); pkt = m_alloc_clear( sizeof *pkt ); pkt->pkttype = PKT_SIGNATURE; pkt->pkt.signature = copy_signature(NULL, sig); if( pub_where ) insert_kbnode( node, new_kbnode(pkt), 0 ); else add_kbnode( pub_keyblock, new_kbnode(pkt) ); return 1; } /**************** * Remove all selceted userids from the keyrings */ static void menu_deluid( KBNODE pub_keyblock, KBNODE sec_keyblock ) { KBNODE node; int selected=0; for( node = pub_keyblock; node; node = node->next ) { if( node->pkt->pkttype == PKT_USER_ID ) { selected = node->flag & NODFLG_SELUID; if( selected ) { delete_kbnode( node ); if( sec_keyblock ) { KBNODE snode; int s_selected = 0; PKT_user_id *uid = node->pkt->pkt.user_id; for( snode = sec_keyblock; snode; snode = snode->next ) { if( snode->pkt->pkttype == PKT_USER_ID ) { PKT_user_id *suid = snode->pkt->pkt.user_id; s_selected = (uid->len == suid->len && !memcmp( uid->name, suid->name, uid->len)); if( s_selected ) delete_kbnode( snode ); } else if( s_selected && snode->pkt->pkttype == PKT_SIGNATURE ) delete_kbnode( snode ); else if( snode->pkt->pkttype == PKT_SECRET_SUBKEY ) s_selected = 0; } } } } else if( selected && node->pkt->pkttype == PKT_SIGNATURE ) delete_kbnode( node ); else if( node->pkt->pkttype == PKT_PUBLIC_SUBKEY ) selected = 0; } commit_kbnode( &pub_keyblock ); if( sec_keyblock ) commit_kbnode( &sec_keyblock ); } static int menu_delsig( KBNODE pub_keyblock ) { KBNODE node; PKT_user_id *uid = NULL; int changed=0; for( node = pub_keyblock; node; node = node->next ) { if( node->pkt->pkttype == PKT_USER_ID ) { uid = (node->flag & NODFLG_SELUID)? node->pkt->pkt.user_id : NULL; } else if( uid && node->pkt->pkttype == PKT_SIGNATURE ) { int okay, valid, selfsig, inv_sig, no_key, other_err; tty_printf("uid "); tty_print_string( uid->name, uid->len ); tty_printf("\n"); okay = inv_sig = no_key = other_err = 0; valid = print_and_check_one_sig( pub_keyblock, node, &inv_sig, &no_key, &other_err, &selfsig, 1 ); if( valid ) okay = cpr_get_answer_yes_no_quit( "keyedit.delsig.valid", _("Delete this good signature? (y/N/q)")); else if( inv_sig || other_err ) okay = cpr_get_answer_yes_no_quit( "keyedit.delsig.invalid", _("Delete this invalid signature? (y/N/q)")); else if( no_key ) okay = cpr_get_answer_yes_no_quit( "keyedit.delsig.unknown", _("Delete this unknown signature? (y/N/q)")); if( okay == -1 ) break; if( okay && selfsig && !cpr_get_answer_is_yes( "keyedit.delsig.selfsig", _("Really delete this self-signature? (y/N)") )) okay = 0; if( okay ) { delete_kbnode( node ); changed++; } } else if( node->pkt->pkttype == PKT_PUBLIC_SUBKEY ) uid = NULL; } if( changed ) { commit_kbnode( &pub_keyblock ); tty_printf( changed == 1? _("Deleted %d signature.\n") : _("Deleted %d signatures.\n"), changed ); } else tty_printf( _("Nothing deleted.\n") ); return changed; } /**************** * Remove some of the secondary keys */ static void menu_delkey( KBNODE pub_keyblock, KBNODE sec_keyblock ) { KBNODE node; int selected=0; for( node = pub_keyblock; node; node = node->next ) { if( node->pkt->pkttype == PKT_PUBLIC_SUBKEY ) { selected = node->flag & NODFLG_SELKEY; if( selected ) { delete_kbnode( node ); if( sec_keyblock ) { KBNODE snode; int s_selected = 0; u32 ki[2]; keyid_from_pk( node->pkt->pkt.public_key, ki ); for( snode = sec_keyblock; snode; snode = snode->next ) { if( snode->pkt->pkttype == PKT_SECRET_SUBKEY ) { u32 ki2[2]; keyid_from_sk( snode->pkt->pkt.secret_key, ki2 ); s_selected = (ki[0] == ki2[0] && ki[1] == ki2[1]); if( s_selected ) delete_kbnode( snode ); } else if( s_selected && snode->pkt->pkttype == PKT_SIGNATURE ) delete_kbnode( snode ); else s_selected = 0; } } } } else if( selected && node->pkt->pkttype == PKT_SIGNATURE ) delete_kbnode( node ); else selected = 0; } commit_kbnode( &pub_keyblock ); if( sec_keyblock ) commit_kbnode( &sec_keyblock ); } static int menu_expire( KBNODE pub_keyblock, KBNODE sec_keyblock ) { int n1, signumber, rc; u32 expiredate; int mainkey=0; PKT_secret_key *sk; /* copy of the main sk */ PKT_public_key *main_pk, *sub_pk; PKT_user_id *uid; KBNODE node; u32 keyid[2]; if( count_selected_keys( sec_keyblock ) ) { tty_printf(_("Please remove selections from the secret keys.\n")); return 0; } n1 = count_selected_keys( pub_keyblock ); if( n1 > 1 ) { tty_printf(_("Please select at most one secondary key.\n")); return 0; } else if( n1 ) tty_printf(_("Changing expiration time for a secondary key.\n")); else { tty_printf(_("Changing expiration time for the primary key.\n")); mainkey=1; } expiredate = ask_expiredate(); node = find_kbnode( sec_keyblock, PKT_SECRET_KEY ); sk = copy_secret_key( NULL, node->pkt->pkt.secret_key); /* Now we can actually change the self signature(s) */ main_pk = sub_pk = NULL; uid = NULL; signumber = 0; for( node=pub_keyblock; node; node = node->next ) { if( node->pkt->pkttype == PKT_PUBLIC_KEY ) { main_pk = node->pkt->pkt.public_key; keyid_from_pk( main_pk, keyid ); main_pk->expiredate = expiredate; } else if( node->pkt->pkttype == PKT_PUBLIC_SUBKEY && (node->flag & NODFLG_SELKEY ) ) { sub_pk = node->pkt->pkt.public_key; sub_pk->expiredate = expiredate; } else if( node->pkt->pkttype == PKT_USER_ID ) uid = node->pkt->pkt.user_id; else if( main_pk && node->pkt->pkttype == PKT_SIGNATURE ) { PKT_signature *sig = node->pkt->pkt.signature; if( keyid[0] == sig->keyid[0] && keyid[1] == sig->keyid[1] && ( (mainkey && uid && (sig->sig_class&~3) == 0x10) || (!mainkey && sig->sig_class == 0x18) ) ) { /* this is a selfsignature which is to be replaced */ PKT_signature *newsig; PACKET *newpkt; KBNODE sn; int signumber2 = 0; signumber++; if( (mainkey && main_pk->version < 4) || (!mainkey && sub_pk->version < 4 ) ) { log_info(_( "You can't change the expiration date of a v3 key\n")); free_secret_key( sk ); return 0; } /* find the corresponding secret self-signature */ for( sn=sec_keyblock; sn; sn = sn->next ) { if( sn->pkt->pkttype == PKT_SIGNATURE ) { PKT_signature *b = sn->pkt->pkt.signature; if( keyid[0] == b->keyid[0] && keyid[1] == b->keyid[1] && sig->sig_class == b->sig_class && ++signumber2 == signumber ) break; } } if( !sn ) log_info(_("No corresponding signature in secret ring\n")); /* create new self signature */ if( mainkey ) rc = make_keysig_packet( &newsig, main_pk, uid, NULL, sk, 0x13, 0, keygen_add_std_prefs, main_pk ); else rc = make_keysig_packet( &newsig, main_pk, NULL, sub_pk, sk, 0x18, 0, keygen_add_key_expire, sub_pk ); if( rc ) { log_error("make_keysig_packet failed: %s\n", g10_errstr(rc)); free_secret_key( sk ); return 0; } /* replace the packet */ newpkt = m_alloc_clear( sizeof *newpkt ); newpkt->pkttype = PKT_SIGNATURE; newpkt->pkt.signature = newsig; free_packet( node->pkt ); m_free( node->pkt ); node->pkt = newpkt; if( sn ) { newpkt = m_alloc_clear( sizeof *newpkt ); newpkt->pkttype = PKT_SIGNATURE; newpkt->pkt.signature = copy_signature( NULL, newsig ); free_packet( sn->pkt ); m_free( sn->pkt ); sn->pkt = newpkt; } } } } free_secret_key( sk ); return 1; } /**************** * Select one user id or remove all selection if index is 0. * Returns: True if the selection changed; */ static int menu_select_uid( KBNODE keyblock, int idx ) { KBNODE node; int i; /* first check that the index is valid */ if( idx ) { for( i=0, node = keyblock; node; node = node->next ) { if( node->pkt->pkttype == PKT_USER_ID ) { if( ++i == idx ) break; } } if( !node ) { tty_printf(_("No user id with index %d\n"), idx ); return 0; } } else { /* reset all */ for( i=0, node = keyblock; node; node = node->next ) { if( node->pkt->pkttype == PKT_USER_ID ) node->flag &= ~NODFLG_SELUID; } return 1; } /* and toggle the new index */ for( i=0, node = keyblock; node; node = node->next ) { if( node->pkt->pkttype == PKT_USER_ID ) { if( ++i == idx ) { if( (node->flag & NODFLG_SELUID) ) node->flag &= ~NODFLG_SELUID; else node->flag |= NODFLG_SELUID; } } } return 1; } /**************** * Select secondary keys * Returns: True if the selection changed; */ static int menu_select_key( KBNODE keyblock, int idx ) { KBNODE node; int i; /* first check that the index is valid */ if( idx ) { for( i=0, node = keyblock; node; node = node->next ) { if( node->pkt->pkttype == PKT_PUBLIC_SUBKEY || node->pkt->pkttype == PKT_SECRET_SUBKEY ) { if( ++i == idx ) break; } } if( !node ) { tty_printf(_("No secondary key with index %d\n"), idx ); return 0; } } else { /* reset all */ for( i=0, node = keyblock; node; node = node->next ) { if( node->pkt->pkttype == PKT_PUBLIC_SUBKEY || node->pkt->pkttype == PKT_SECRET_SUBKEY ) node->flag &= ~NODFLG_SELKEY; } return 1; } /* and set the new index */ for( i=0, node = keyblock; node; node = node->next ) { if( node->pkt->pkttype == PKT_PUBLIC_SUBKEY || node->pkt->pkttype == PKT_SECRET_SUBKEY ) { if( ++i == idx ) { if( (node->flag & NODFLG_SELKEY) ) node->flag &= ~NODFLG_SELKEY; else node->flag |= NODFLG_SELKEY; } } } return 1; } static int count_uids_with_flag( KBNODE keyblock, unsigned flag ) { KBNODE node; int i=0; for( node = keyblock; node; node = node->next ) if( node->pkt->pkttype == PKT_USER_ID && (node->flag & flag) ) i++; return i; } static int count_keys_with_flag( KBNODE keyblock, unsigned flag ) { KBNODE node; int i=0; for( node = keyblock; node; node = node->next ) if( ( node->pkt->pkttype == PKT_PUBLIC_SUBKEY || node->pkt->pkttype == PKT_SECRET_SUBKEY) && (node->flag & flag) ) i++; return i; } static int count_uids( KBNODE keyblock ) { KBNODE node; int i=0; for( node = keyblock; node; node = node->next ) if( node->pkt->pkttype == PKT_USER_ID ) i++; return i; } /**************** * Returns true if there is at least one selected user id */ static int count_selected_uids( KBNODE keyblock ) { return count_uids_with_flag( keyblock, NODFLG_SELUID); } static int count_selected_keys( KBNODE keyblock ) { return count_keys_with_flag( keyblock, NODFLG_SELKEY); } /* * Ask whether the signature should be revoked. If the user commits this, * flag bit MARK_A is set on the signature and the user ID. */ static void ask_revoke_sig( KBNODE keyblock, KBNODE node ) { PKT_signature *sig = node->pkt->pkt.signature; KBNODE unode = find_prev_kbnode( keyblock, node, PKT_USER_ID ); if( !unode ) { log_error("Oops: no user ID for signature\n"); return; } tty_printf(_("user ID: \"")); tty_print_string( unode->pkt->pkt.user_id->name, unode->pkt->pkt.user_id->len ); tty_printf(_("\"\nsigned with your key %08lX at %s\n"), sig->keyid[1], datestr_from_sig(sig) ); if( cpr_get_answer_is_yes("ask_revoke_sig.one", _("Create a revocation certificate for this signature? (y/N)")) ) { node->flag |= NODFLG_MARK_A; unode->flag |= NODFLG_MARK_A; } } /**************** * Display all user ids of the current public key together with signatures * done by one of our keys. Then walk over all this sigs and ask the user * whether he wants to revoke this signature. * Return: True when the keyblock has changed. */ static int menu_revsig( KBNODE keyblock ) { PKT_signature *sig; PKT_public_key *primary_pk; KBNODE node; int changed = 0; int upd_trust = 0; int rc, any; /* FIXME: detect duplicates here */ tty_printf("You have signed these user IDs:\n"); for( node = keyblock; node; node = node->next ) { node->flag &= ~(NODFLG_SELSIG | NODFLG_MARK_A); if( node->pkt->pkttype == PKT_USER_ID ) { PKT_user_id *uid = node->pkt->pkt.user_id; /* Hmmm: Should we show only UIDs with a signature? */ tty_printf(" "); tty_print_string( uid->name, uid->len ); tty_printf("\n"); } else if( node->pkt->pkttype == PKT_SIGNATURE && ((sig = node->pkt->pkt.signature), !seckey_available( sig->keyid ) ) ) { if( (sig->sig_class&~3) == 0x10 ) { tty_printf(" signed by %08lX at %s\n", sig->keyid[1], datestr_from_sig(sig) ); node->flag |= NODFLG_SELSIG; } else if( sig->sig_class == 0x30 ) { tty_printf(" revoked by %08lX at %s\n", sig->keyid[1], datestr_from_sig(sig) ); } } } /* ask */ for( node = keyblock; node; node = node->next ) { if( !(node->flag & NODFLG_SELSIG) ) continue; ask_revoke_sig( keyblock, node ); } /* present selected */ any = 0; for( node = keyblock; node; node = node->next ) { if( !(node->flag & NODFLG_MARK_A) ) continue; if( !any ) { any = 1; tty_printf("You are about to revoke these signatures:\n"); } if( node->pkt->pkttype == PKT_USER_ID ) { PKT_user_id *uid = node->pkt->pkt.user_id; tty_printf(" "); tty_print_string( uid->name, uid->len ); tty_printf("\n"); } else if( node->pkt->pkttype == PKT_SIGNATURE ) { sig = node->pkt->pkt.signature; tty_printf(" signed by %08lX at %s\n", sig->keyid[1], datestr_from_sig(sig) ); } } if( !any ) return 0; /* none selected */ if( !cpr_get_answer_is_yes("ask_revoke_sig.okay", _("Really create the revocation certificates? (y/N)")) ) return 0; /* forget it */ /* now we can sign the user ids */ reloop: /* (must use this, because we are modifing the list) */ primary_pk = keyblock->pkt->pkt.public_key; for( node=keyblock; node; node = node->next ) { KBNODE unode; PACKET *pkt; struct sign_uid_attrib attrib; PKT_secret_key *sk; if( !(node->flag & NODFLG_MARK_A) || node->pkt->pkttype != PKT_SIGNATURE ) continue; unode = find_prev_kbnode( keyblock, node, PKT_USER_ID ); assert( unode ); /* we already checked this */ memset( &attrib, 0, sizeof attrib ); node->flag &= ~NODFLG_MARK_A; sk = m_alloc_secure_clear( sizeof *sk ); if( get_seckey( sk, node->pkt->pkt.signature->keyid ) ) { log_info(_("no secret key\n")); continue; } rc = make_keysig_packet( &sig, primary_pk, unode->pkt->pkt.user_id, NULL, sk, 0x30, 0, sign_uid_mk_attrib, &attrib ); free_secret_key(sk); if( rc ) { log_error(_("signing failed: %s\n"), g10_errstr(rc)); return changed; } changed = 1; /* we changed the keyblock */ upd_trust = 1; pkt = m_alloc_clear( sizeof *pkt ); pkt->pkttype = PKT_SIGNATURE; pkt->pkt.signature = sig; insert_kbnode( unode, new_kbnode(pkt), 0 ); goto reloop; } if( upd_trust ) clear_trust_checked_flag( primary_pk ); return changed; } /**************** * Revoke some of the secondary keys. * Hmmm: Should we add a revocation to the secret keyring too? * Does its all make sense to duplicate most of the information? */ static int menu_revkey( KBNODE pub_keyblock, KBNODE sec_keyblock ) { PKT_public_key *mainpk; KBNODE node; int changed = 0; int upd_trust = 0; int rc; reloop: /* (better this way because we are modifing the keyring) */ mainpk = pub_keyblock->pkt->pkt.public_key; for( node = pub_keyblock; node; node = node->next ) { if( node->pkt->pkttype == PKT_PUBLIC_SUBKEY && (node->flag & NODFLG_SELKEY) ) { PACKET *pkt; PKT_signature *sig; PKT_secret_key *sk; PKT_public_key *subpk = node->pkt->pkt.public_key; node->flag &= ~NODFLG_SELKEY; sk = copy_secret_key( NULL, sec_keyblock->pkt->pkt.secret_key ); rc = make_keysig_packet( &sig, mainpk, NULL, subpk, sk, 0x28, 0, NULL, NULL ); free_secret_key(sk); if( rc ) { log_error(_("signing failed: %s\n"), g10_errstr(rc)); return changed; } changed = 1; /* we changed the keyblock */ upd_trust = 1; pkt = m_alloc_clear( sizeof *pkt ); pkt->pkttype = PKT_SIGNATURE; pkt->pkt.signature = sig; insert_kbnode( node, new_kbnode(pkt), 0 ); goto reloop; } } commit_kbnode( &pub_keyblock ); /*commit_kbnode( &sec_keyblock );*/ if( upd_trust ) clear_trust_checked_flag( mainpk ); return changed; } static int enable_disable_key( KBNODE keyblock, int disable ) { - int entire; - int changed = 0; - - - entire = !count_selected_keys( keyblock ); - - return changed; + ulong lid = find_kbnode( keyblock, PKT_PUBLIC_KEY ) + ->pkt->pkt.public_key->local_id; + unsigned int trust, newtrust; + + /* Note: Because the keys have beed displayed, we have + * ensured that local_id has been set */ + trust = newtrust = get_ownertrust( lid ); + newtrust &= ~TRUST_FLAG_DISABLED; + if( disable ) + newtrust |= TRUST_FLAG_DISABLED; + if( trust == newtrust ) + return 0; /* already in that state */ + if( !update_ownertrust( lid, newtrust ) ) + return 1; + return 0; } diff --git a/g10/keygen.c b/g10/keygen.c index 1621d9445..74840e8c6 100644 --- a/g10/keygen.c +++ b/g10/keygen.c @@ -1,1071 +1,1071 @@ /* keygen.c - generate a key pair * Copyright (C) 1998, 1999 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 "util.h" #include "main.h" #include "packet.h" #include "cipher.h" #include "ttyio.h" #include "options.h" #include "keydb.h" #include "status.h" #include "i18n.h" static void write_uid( KBNODE root, const char *s ) { PACKET *pkt = m_alloc_clear(sizeof *pkt ); size_t n = strlen(s); pkt->pkttype = PKT_USER_ID; pkt->pkt.user_id = m_alloc( sizeof *pkt->pkt.user_id + n - 1 ); pkt->pkt.user_id->len = n; strcpy(pkt->pkt.user_id->name, s); add_kbnode( root, new_kbnode( pkt ) ); } int keygen_add_key_expire( PKT_signature *sig, void *opaque ) { PKT_public_key *pk = opaque; byte buf[8]; u32 u; if( pk->expiredate ) { u = pk->expiredate > pk->timestamp? pk->expiredate - pk->timestamp : pk->timestamp; buf[0] = (u >> 24) & 0xff; buf[1] = (u >> 16) & 0xff; buf[2] = (u >> 8) & 0xff; buf[3] = u & 0xff; build_sig_subpkt( sig, SIGSUBPKT_KEY_EXPIRE, buf, 4 ); } return 0; } /**************** * Add preference to the self signature packet. * This is only called for packets with version > 3. */ int keygen_add_std_prefs( PKT_signature *sig, void *opaque ) { byte buf[8]; keygen_add_key_expire( sig, opaque ); buf[0] = CIPHER_ALGO_TWOFISH; buf[1] = CIPHER_ALGO_CAST5; build_sig_subpkt( sig, SIGSUBPKT_PREF_SYM, buf, 2 ); buf[0] = DIGEST_ALGO_RMD160; buf[1] = DIGEST_ALGO_SHA1; build_sig_subpkt( sig, SIGSUBPKT_PREF_HASH, buf, 2 ); buf[0] = 2; buf[1] = 1; build_sig_subpkt( sig, SIGSUBPKT_PREF_COMPR, buf, 2 ); buf[0] = 0x80; /* no modify - It is reasonable that a key holder * has the possibility to reject signatures from users * who are known to sign everything without any * validation - so a signed key should be send * to the holder who in turn can put it on a keyserver */ build_sig_subpkt( sig, SIGSUBPKT_KS_FLAGS, buf, 1 ); return 0; } static int write_selfsig( KBNODE root, KBNODE pub_root, PKT_secret_key *sk ) { PACKET *pkt; PKT_signature *sig; PKT_user_id *uid; int rc=0; KBNODE node; PKT_public_key *pk; if( opt.verbose ) log_info(_("writing self signature\n")); /* get the uid packet from the list */ node = find_kbnode( root, PKT_USER_ID ); if( !node ) BUG(); /* no user id packet in tree */ uid = node->pkt->pkt.user_id; /* get the pk packet from the pub_tree */ node = find_kbnode( pub_root, PKT_PUBLIC_KEY ); if( !node ) BUG(); pk = node->pkt->pkt.public_key; /* and make the signature */ rc = make_keysig_packet( &sig, pk, uid, NULL, sk, 0x13, 0, keygen_add_std_prefs, pk ); if( rc ) { log_error("make_keysig_packet failed: %s\n", g10_errstr(rc) ); return rc; } pkt = m_alloc_clear( sizeof *pkt ); pkt->pkttype = PKT_SIGNATURE; pkt->pkt.signature = sig; add_kbnode( root, new_kbnode( pkt ) ); return rc; } static int write_keybinding( KBNODE root, KBNODE pub_root, PKT_secret_key *sk ) { PACKET *pkt; PKT_signature *sig; int rc=0; KBNODE node; PKT_public_key *pk, *subpk; if( opt.verbose ) log_info(_("writing key binding signature\n")); /* get the pk packet from the pub_tree */ node = find_kbnode( pub_root, PKT_PUBLIC_KEY ); if( !node ) BUG(); pk = node->pkt->pkt.public_key; /* find the last subkey */ subpk = NULL; for(node=pub_root; node; node = node->next ) { if( node->pkt->pkttype == PKT_PUBLIC_SUBKEY ) subpk = node->pkt->pkt.public_key; } if( !subpk ) BUG(); /* and make the signature */ rc = make_keysig_packet( &sig, pk, NULL, subpk, sk, 0x18, 0, keygen_add_key_expire, subpk ); if( rc ) { log_error("make_keysig_packet failed: %s\n", g10_errstr(rc) ); return rc; } pkt = m_alloc_clear( sizeof *pkt ); pkt->pkttype = PKT_SIGNATURE; pkt->pkt.signature = sig; add_kbnode( root, new_kbnode( pkt ) ); return rc; } static int gen_elg(int algo, unsigned nbits, KBNODE pub_root, KBNODE sec_root, DEK *dek, STRING2KEY *s2k, PKT_secret_key **ret_sk, u32 expireval, int version ) { int rc; int i; PACKET *pkt; PKT_secret_key *sk; PKT_public_key *pk; MPI skey[4]; MPI *factors; assert( is_ELGAMAL(algo) ); rc = pubkey_generate( algo, nbits, skey, &factors ); if( rc ) { log_error("pubkey_generate failed: %s\n", g10_errstr(rc) ); return rc; } sk = m_alloc_clear( sizeof *sk ); pk = m_alloc_clear( sizeof *pk ); sk->timestamp = pk->timestamp = make_timestamp(); sk->version = pk->version = version; if( expireval ) { sk->expiredate = pk->expiredate = sk->timestamp + expireval; } sk->pubkey_algo = pk->pubkey_algo = algo; pk->pkey[0] = mpi_copy( skey[0] ); pk->pkey[1] = mpi_copy( skey[1] ); pk->pkey[2] = mpi_copy( skey[2] ); sk->skey[0] = skey[0]; sk->skey[1] = skey[1]; sk->skey[2] = skey[2]; sk->skey[3] = skey[3]; sk->is_protected = 0; sk->protect.algo = 0; sk->csum = checksum_mpi_counted_nbits( sk->skey[3] ); if( ret_sk ) /* not a subkey: return an unprotected version of the sk */ *ret_sk = copy_secret_key( NULL, sk ); if( dek ) { sk->protect.algo = dek->algo; sk->protect.s2k = *s2k; rc = protect_secret_key( sk, dek ); if( rc ) { log_error("protect_secret_key failed: %s\n", g10_errstr(rc) ); free_public_key(pk); free_secret_key(sk); return rc; } } pkt = m_alloc_clear(sizeof *pkt); pkt->pkttype = ret_sk ? PKT_PUBLIC_KEY : PKT_PUBLIC_SUBKEY; pkt->pkt.public_key = pk; add_kbnode(pub_root, new_kbnode( pkt )); /* don't know whether it makes sense to have the factors, so for now * we store them in the secret keyring (but they are not secret) */ pkt = m_alloc_clear(sizeof *pkt); pkt->pkttype = ret_sk ? PKT_SECRET_KEY : PKT_SECRET_SUBKEY; pkt->pkt.secret_key = sk; add_kbnode(sec_root, new_kbnode( pkt )); for(i=0; factors[i]; i++ ) add_kbnode( sec_root, make_mpi_comment_node("#:ELG_factor:", factors[i] )); return 0; } /**************** * Generate a DSA key */ static int gen_dsa(unsigned nbits, KBNODE pub_root, KBNODE sec_root, DEK *dek, STRING2KEY *s2k, PKT_secret_key **ret_sk, u32 expireval ) { int rc; int i; PACKET *pkt; PKT_secret_key *sk; PKT_public_key *pk; MPI skey[5]; MPI *factors; if( nbits > 1024 ) nbits = 1024; rc = pubkey_generate( PUBKEY_ALGO_DSA, nbits, skey, &factors ); if( rc ) { log_error("pubkey_generate failed: %s\n", g10_errstr(rc) ); return rc; } sk = m_alloc_clear( sizeof *sk ); pk = m_alloc_clear( sizeof *pk ); sk->timestamp = pk->timestamp = make_timestamp(); sk->version = pk->version = 4; if( expireval ) { sk->expiredate = pk->expiredate = sk->timestamp + expireval; } sk->pubkey_algo = pk->pubkey_algo = PUBKEY_ALGO_DSA; pk->pkey[0] = mpi_copy( skey[0] ); pk->pkey[1] = mpi_copy( skey[1] ); pk->pkey[2] = mpi_copy( skey[2] ); pk->pkey[3] = mpi_copy( skey[3] ); sk->skey[0] = skey[0]; sk->skey[1] = skey[1]; sk->skey[2] = skey[2]; sk->skey[3] = skey[3]; sk->skey[4] = skey[4]; sk->is_protected = 0; sk->protect.algo = 0; sk->csum = checksum_mpi_counted_nbits( sk->skey[4] ); if( ret_sk ) /* not a subkey: return an unprotected version of the sk */ *ret_sk = copy_secret_key( NULL, sk ); if( dek ) { sk->protect.algo = dek->algo; sk->protect.s2k = *s2k; rc = protect_secret_key( sk, dek ); if( rc ) { log_error("protect_secret_key failed: %s\n", g10_errstr(rc) ); free_public_key(pk); free_secret_key(sk); return rc; } } pkt = m_alloc_clear(sizeof *pkt); pkt->pkttype = ret_sk ? PKT_PUBLIC_KEY : PKT_PUBLIC_SUBKEY; pkt->pkt.public_key = pk; add_kbnode(pub_root, new_kbnode( pkt )); /* don't know whether it makes sense to have the factors, so for now * we store them in the secret keyring (but they are not secret) * p = 2 * q * f1 * f2 * ... * fn * We store only f1 to f_n-1; fn can be calculated because p and q * are known. */ pkt = m_alloc_clear(sizeof *pkt); pkt->pkttype = ret_sk ? PKT_SECRET_KEY : PKT_SECRET_SUBKEY; pkt->pkt.secret_key = sk; add_kbnode(sec_root, new_kbnode( pkt )); for(i=1; factors[i]; i++ ) /* the first one is q */ add_kbnode( sec_root, make_mpi_comment_node("#:DSA_factor:", factors[i] )); return 0; } /**************** * check valid days: * return 0 on error or the multiplier */ static int check_valid_days( const char *s ) { if( !isdigit(*s) ) return 0; for( s++; *s; s++) if( !isdigit(*s) ) break; if( !*s ) return 1; if( s[1] ) return 0; /* e.g. "2323wc" */ if( *s == 'd' || *s == 'D' ) return 1; if( *s == 'w' || *s == 'W' ) return 7; if( *s == 'm' || *s == 'M' ) return 30; if( *s == 'y' || *s == 'Y' ) return 365; return 0; } /**************** * Returns: 0 to create both a DSA and a ElGamal key. */ static int ask_algo( int *ret_v4, int addmode ) { char *answer; int algo; tty_printf(_("Please select what kind of key you want:\n")); if( !addmode ) tty_printf(_(" (%d) DSA and ElGamal (default)\n"), 1 ); tty_printf( _(" (%d) DSA (sign only)\n"), 2 ); if( addmode ) tty_printf( _(" (%d) ElGamal (encrypt only)\n"), 3 ); tty_printf( _(" (%d) ElGamal (sign and encrypt)\n"), 4 ); #if 0 tty_printf( _(" (%d) ElGamal in a v3 packet\n"), 5 ); #endif *ret_v4 = 1; for(;;) { answer = cpr_get("keygen.algo",_("Your selection? ")); cpr_kill_prompt(); algo = *answer? atoi(answer): 1; m_free(answer); if( algo == 1 && !addmode ) { algo = 0; /* create both keys */ break; } else if( algo == 4 ) { if( cpr_get_answer_is_yes("keygen.algo.elg_se",_( "Do you really want to create a sign and encrypt key? "))) { algo = PUBKEY_ALGO_ELGAMAL; break; } } else if( algo == 3 && addmode ) { algo = PUBKEY_ALGO_ELGAMAL_E; break; } else if( algo == 2 ) { algo = PUBKEY_ALGO_DSA; break; } #if 0 else if( algo == 5 ) { algo = PUBKEY_ALGO_ELGAMAL_E; *ret_v4 = 0; break; } #endif else tty_printf(_("Invalid selection.\n")); } return algo; } static unsigned ask_keysize( int algo ) { char *answer; unsigned nbits; tty_printf(_("About to generate a new %s keypair.\n" " minimum keysize is 768 bits\n" " default keysize is 1024 bits\n" " highest suggested keysize is 2048 bits\n"), pubkey_algo_to_string(algo) ); for(;;) { answer = cpr_get("keygen.size", _("What keysize do you want? (1024) ")); cpr_kill_prompt(); nbits = *answer? atoi(answer): 1024; m_free(answer); if( algo == PUBKEY_ALGO_DSA && (nbits < 512 || nbits > 1024) ) tty_printf(_("DSA only allows keysizes from 512 to 1024\n")); else if( nbits < 768 ) tty_printf(_("keysize too small; 768 is smallest value allowed.\n")); else if( nbits > 4096 ) { /* It is ridiculous and an annoyance to use larger key sizes! * GnuPG can handle much larger sizes; but it takes an eternity * to create such a key (but less than the time the Sirius * Computer Corporation needs to process one of the usual * complaints) and {de,en}cryption although needs some time. * So, before you complain about this limitation, I suggest that * you start a discussion with Marvin about this theme and then * do whatever you want. */ tty_printf(_("keysize too large; %d is largest value allowed.\n"), 4096); } else if( nbits > 2048 && !cpr_enabled() ) { tty_printf( _("Keysizes larger than 2048 are not suggested because\n" "computations take REALLY long!\n")); if( cpr_get_answer_is_yes("keygen.size.huge.okay",_( "Are you sure that you want this keysize? ")) ) { tty_printf(_("Okay, but keep in mind that your monitor " "and keyboard radiation is also very vulnerable " "to attacks!\n")); break; } } else if( nbits > 1536 && !cpr_enabled() ) { if( cpr_get_answer_is_yes("keygen.size.large.okay",_( "Do you really need such a large keysize? ")) ) break; } else break; } tty_printf(_("Requested keysize is %u bits\n"), nbits ); if( algo == PUBKEY_ALGO_DSA && (nbits % 64) ) { nbits = ((nbits + 63) / 64) * 64; tty_printf(_("rounded up to %u bits\n"), nbits ); } else if( (nbits % 32) ) { nbits = ((nbits + 31) / 32) * 32; tty_printf(_("rounded up to %u bits\n"), nbits ); } return nbits; } static u32 ask_expire_interval(void) { char *answer; int valid_days=0; u32 interval = 0; tty_printf(_("Please specify how long the key should be valid.\n" " 0 = key does not expire\n" " = key expires in n days\n" " w = key expires in n weeks\n" " m = key expires in n months\n" " y = key expires in n years\n")); /* Note: The elgamal subkey for DSA has no expiration date because * it must be signed with the DSA key and this one has the expiration * date */ answer = NULL; for(;;) { int mult; m_free(answer); answer = cpr_get("keygen.valid",_("Key is valid for? (0) ")); cpr_kill_prompt(); trim_spaces(answer); if( !*answer ) valid_days = 0; else if( (mult=check_valid_days(answer)) ) { valid_days = atoi(answer) * mult; if( valid_days < 0 || valid_days > 32767 ) valid_days = 0; } else { tty_printf(_("invalid value\n")); continue; } if( !valid_days ) { tty_printf(_("Key does not expire at all\n")); interval = 0; } else { interval = valid_days * 86400L; /* print the date when the key expires */ tty_printf(_("Key expires at %s\n"), asctimestamp(make_timestamp() + interval ) ); } if( !cpr_enabled() && cpr_get_answer_is_yes("keygen.valid.okay", _("Is this correct (y/n)? ")) ) break; } m_free(answer); return interval; } u32 ask_expiredate() { u32 x = ask_expire_interval(); return x? make_timestamp() + x : 0; } static int has_invalid_email_chars( const char *s ) { int at_seen=0; static char valid_chars[] = "01234567890_-." "abcdefghijklmnopqrstuvwxyz" "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; for( ; *s; s++ ) { if( *s & 0x80 ) return 1; if( *s == '@' ) at_seen=1; else if( !at_seen && !( !!strchr( valid_chars, *s ) || *s == '+' ) ) return 1; else if( at_seen && !strchr( valid_chars, *s ) ) return 1; } return 0; } static char * ask_user_id( int mode ) { char *answer; char *aname, *acomment, *amail, *uid; if( !mode ) tty_printf( _("\n" "You need a User-ID to identify your key; the software constructs the user id\n" "from Real Name, Comment and Email Address in this form:\n" " \"Heinrich Heine (Der Dichter) \"\n\n") ); uid = aname = acomment = amail = NULL; for(;;) { char *p; if( !aname ) { for(;;) { m_free(aname); aname = cpr_get("keygen.name",_("Real name: ")); trim_spaces(aname); cpr_kill_prompt(); if( strpbrk( aname, "<([])>" ) ) tty_printf(_("Invalid character in name\n")); else if( isdigit(*aname) ) tty_printf(_("Name may not start with a digit\n")); else if( strlen(aname) < 5 ) tty_printf(_("Name must be at least 5 characters long\n")); else break; } } if( !amail ) { for(;;) { m_free(amail); amail = cpr_get("keygen.email",_("Email address: ")); trim_spaces(amail); cpr_kill_prompt(); if( !*amail ) break; /* no email address is okay */ else if( has_invalid_email_chars(amail) || string_count_chr(amail,'@') != 1 || *amail == '@' || amail[strlen(amail)-1] == '@' || amail[strlen(amail)-1] == '.' || strstr(amail, "..") ) tty_printf(_("Not a valid email address\n")); else break; } } if( !acomment ) { for(;;) { m_free(acomment); acomment = cpr_get("keygen.comment",_("Comment: ")); trim_spaces(acomment); cpr_kill_prompt(); if( !*acomment ) break; /* no comment is okay */ else if( strpbrk( acomment, "()" ) ) tty_printf(_("Invalid character in comment\n")); else break; } } m_free(uid); uid = p = m_alloc(strlen(aname)+strlen(amail)+strlen(acomment)+12+10); p = stpcpy(p, aname ); if( *acomment ) p = stpcpy(stpcpy(stpcpy(p," ("), acomment),")"); if( *amail ) p = stpcpy(stpcpy(stpcpy(p," <"), amail),">"); /* append a warning if we do not have dev/random * or it is switched into quick testmode */ if( quick_random_gen(-1) ) strcpy(p, " (INSECURE!)" ); /* print a note in case that UTF8 mapping has to be done */ for(p=uid; *p; p++ ) { if( *p & 0x80 ) { tty_printf(_("You are using the `%s' character set.\n"), get_native_charset() ); break; } } tty_printf(_("You selected this USER-ID:\n \"%s\"\n\n"), uid); /* fixme: add a warning if this user-id already exists */ for(;;) { char *ansstr = _("NnCcEeOoQq"); if( strlen(ansstr) != 10 ) BUG(); if( cpr_enabled() ) { answer = m_strdup(ansstr+6); answer[1] = 0; } else { answer = cpr_get("keygen.userid.cmd",_( "Change (N)ame, (C)omment, (E)mail or (O)kay/(Q)uit? ")); cpr_kill_prompt(); } if( strlen(answer) > 1 ) ; else if( *answer == ansstr[0] || *answer == ansstr[1] ) { m_free(aname); aname = NULL; break; } else if( *answer == ansstr[2] || *answer == ansstr[3] ) { m_free(acomment); acomment = NULL; break; } else if( *answer == ansstr[4] || *answer == ansstr[5] ) { m_free(amail); amail = NULL; break; } else if( *answer == ansstr[6] || *answer == ansstr[7] ) { m_free(aname); aname = NULL; m_free(acomment); acomment = NULL; m_free(amail); amail = NULL; break; } else if( *answer == ansstr[8] || *answer == ansstr[9] ) { m_free(aname); aname = NULL; m_free(acomment); acomment = NULL; m_free(amail); amail = NULL; m_free(uid); uid = NULL; break; } m_free(answer); } m_free(answer); if( !amail && !acomment && !amail ) break; m_free(uid); uid = NULL; } if( uid ) { char *p = native_to_utf8( uid ); m_free( uid ); uid = p; } return uid; } static DEK * ask_passphrase( STRING2KEY **ret_s2k ) { DEK *dek = NULL; STRING2KEY *s2k; tty_printf(_("You need a Passphrase to protect your secret key.\n\n") ); s2k = m_alloc_secure( sizeof *s2k ); for(;;) { s2k->mode = opt.s2k_mode; s2k->hash_algo = opt.s2k_digest_algo; - dek = passphrase_to_dek( NULL, opt.s2k_cipher_algo, s2k, 2 ); + dek = passphrase_to_dek( NULL, 0, opt.s2k_cipher_algo, s2k, 2 ); if( !dek ) { tty_printf(_("passphrase not correctly repeated; try again.\n")); } else if( !dek->keylen ) { m_free(dek); dek = NULL; m_free(s2k); s2k = NULL; tty_printf(_( "You don't want a passphrase - this is probably a *bad* idea!\n" "I will do it anyway. You can change your passphrase at any time,\n" "using this program with the option \"--edit-key\".\n\n")); break; } else break; /* okay */ } *ret_s2k = s2k; return dek; } static int do_create( int algo, unsigned nbits, KBNODE pub_root, KBNODE sec_root, DEK *dek, STRING2KEY *s2k, PKT_secret_key **sk, u32 expiredate, int v4_packet ) { int rc=0; tty_printf(_( "We need to generate a lot of random bytes. It is a good idea to perform\n" "some other action (type on the keyboard, move the mouse, utilize the\n" "disks) during the prime generation; this gives the random number\n" "generator a better chance to gain enough entropy.\n") ); if( algo == PUBKEY_ALGO_ELGAMAL || algo == PUBKEY_ALGO_ELGAMAL_E ) rc = gen_elg(algo, nbits, pub_root, sec_root, dek, s2k, sk, expiredate, v4_packet? 4:3 ); else if( algo == PUBKEY_ALGO_DSA ) rc = gen_dsa(nbits, pub_root, sec_root, dek, s2k, sk, expiredate); else BUG(); #ifdef ENABLE_COMMENT_PACKETS if( !rc ) { add_kbnode( pub_root, make_comment_node("#created by GNUPG v" VERSION " (" PRINTABLE_OS_NAME ")")); add_kbnode( sec_root, make_comment_node("#created by GNUPG v" VERSION " (" PRINTABLE_OS_NAME ")")); } #endif return rc; } /**************** * Generate a new user id packet, or return NULL if canceled */ PKT_user_id * generate_user_id() { PKT_user_id *uid; char *p; size_t n; p = ask_user_id( 1 ); if( !p ) return NULL; n = strlen(p); uid = m_alloc( sizeof *uid + n - 1 ); uid->len = n; strcpy(uid->name, p); return uid; } /**************** * Generate a keypair */ void generate_keypair() { unsigned nbits; char *pub_fname = NULL; char *sec_fname = NULL; char *uid = NULL; KBNODE pub_root = NULL; KBNODE sec_root = NULL; PKT_secret_key *sk = NULL; DEK *dek; STRING2KEY *s2k; int rc; int algo; u32 expire; int v4; int both = 0; if( opt.batch || opt.answer_yes || opt.answer_no ) { log_error(_("Key generation can only be used in interactive mode\n")); return; } algo = ask_algo( &v4, 0 ); if( !algo ) { algo = PUBKEY_ALGO_ELGAMAL_E; both = 1; tty_printf(_("DSA keypair will have 1024 bits.\n")); } nbits = ask_keysize( algo ); expire = ask_expire_interval(); uid = ask_user_id(0); if( !uid ) { log_error(_("Key generation canceled.\n")); return; } dek = ask_passphrase( &s2k ); /* now check whether we are allowed to write to the keyrings */ pub_fname = make_filename(opt.homedir, "pubring.gpg", NULL ); sec_fname = make_filename(opt.homedir, "secring.gpg", NULL ); if( opt.verbose ) { tty_printf(_("writing public certificate to `%s'\n"), pub_fname ); tty_printf(_("writing secret certificate 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 * The first packet is a dummy comment packet which we flag * as deleted. The very first packet must always be a KEY packet. */ pub_root = make_comment_node("#"); delete_kbnode(pub_root); sec_root = make_comment_node("#"); delete_kbnode(sec_root); if( both ) rc = do_create( PUBKEY_ALGO_DSA, 1024, pub_root, sec_root, dek, s2k, &sk, expire, 1); else rc = do_create( algo, nbits, pub_root, sec_root, dek, s2k, &sk, expire, v4); if( !rc ) write_uid(pub_root, uid ); if( !rc ) write_uid(sec_root, uid ); if( !rc ) rc = write_selfsig(pub_root, pub_root, sk); if( !rc ) rc = write_selfsig(sec_root, pub_root, sk); if( both ) { rc = do_create( algo, nbits, pub_root, sec_root, dek, s2k, NULL, expire, 1 ); if( !rc ) rc = write_keybinding(pub_root, pub_root, sk); if( !rc ) rc = write_keybinding(sec_root, pub_root, sk); } if( !rc ) { KBPOS pub_kbpos; KBPOS sec_kbpos; int rc1 = -1; int rc2 = -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; } } 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 { tty_printf(_("public and secret key created and signed.\n") ); if( algo == PUBKEY_ALGO_DSA ) tty_printf(_("Note that this key cannot be used for " "encryption. You may want to use\n" "the command \"--edit-key\" to generate a " "secondary key for this purpose.\n") ); } if( !rc1 ) unlock_keyblock( &pub_kbpos ); if( !rc2 ) unlock_keyblock( &sec_kbpos ); } if( rc ) tty_printf(_("Key generation failed: %s\n"), g10_errstr(rc) ); release_kbnode( pub_root ); release_kbnode( sec_root ); if( sk ) /* the unprotected secret key */ free_secret_key(sk); m_free(uid); m_free(dek); m_free(s2k); m_free(pub_fname); m_free(sec_fname); } /**************** * add a new subkey to an existing key. * Returns true if a new key has been generated and put into the keyblocks. */ int generate_subkeypair( KBNODE pub_keyblock, KBNODE sec_keyblock ) { int okay=0, rc=0; KBNODE node; PKT_secret_key *sk = NULL; /* this is the primary sk */ int v4, algo; u32 expire; unsigned nbits; char *passphrase = NULL; DEK *dek = NULL; STRING2KEY *s2k = NULL; u32 cur_time; /* break out the primary secret key */ node = find_kbnode( sec_keyblock, PKT_SECRET_KEY ); if( !node ) { log_error("Oops; secret key not found anymore!\n"); goto leave; } /* make a copy of the sk to keep the protected one in the keyblock */ sk = copy_secret_key( NULL, node->pkt->pkt.secret_key ); cur_time = make_timestamp(); if( sk->timestamp > cur_time ) { ulong d = sk->timestamp - cur_time; log_info( d==1 ? _("key has been created %lu second " "in future (time warp or clock problem)\n") : _("key has been created %lu seconds " "in future (time warp or clock problem)\n"), d ); rc = G10ERR_TIME_CONFLICT; goto leave; } /* unprotect to get the passphrase */ switch( is_secret_key_protected( sk ) ) { case -1: rc = G10ERR_PUBKEY_ALGO; break; case 0: tty_printf("This key is not protected.\n"); break; default: tty_printf("Key is protected.\n"); rc = check_secret_key( sk, 0 ); if( !rc ) passphrase = get_last_passphrase(); break; } if( rc ) goto leave; algo = ask_algo( &v4, 1 ); assert(algo); nbits = ask_keysize( algo ); expire = ask_expire_interval(); if( !cpr_enabled() && !cpr_get_answer_is_yes("keygen.sub.okay", _("Really create? ") ) ) goto leave; if( passphrase ) { s2k = m_alloc_secure( sizeof *s2k ); s2k->mode = opt.s2k_mode; s2k->hash_algo = opt.s2k_digest_algo; set_next_passphrase( passphrase ); - dek = passphrase_to_dek( NULL, opt.s2k_cipher_algo, s2k, 2 ); + dek = passphrase_to_dek( NULL, 0, opt.s2k_cipher_algo, s2k, 2 ); } rc = do_create( algo, nbits, pub_keyblock, sec_keyblock, dek, s2k, NULL, expire, v4 ); if( !rc ) rc = write_keybinding(pub_keyblock, pub_keyblock, sk); if( !rc ) rc = write_keybinding(sec_keyblock, pub_keyblock, sk); if( !rc ) okay = 1; leave: if( rc ) log_error(_("Key generation failed: %s\n"), g10_errstr(rc) ); m_free( passphrase ); m_free( dek ); m_free( s2k ); if( sk ) /* release the copy of the (now unprotected) secret key */ free_secret_key(sk); set_next_passphrase( NULL ); return okay; } diff --git a/g10/main.h b/g10/main.h index 5889ccf71..f2c059b33 100644 --- a/g10/main.h +++ b/g10/main.h @@ -1,151 +1,152 @@ /* main.h * Copyright (C) 1998 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 G10_MAIN_H #define G10_MAIN_H #include "types.h" #include "iobuf.h" #include "mpi.h" #include "cipher.h" #include "keydb.h" #define DEFAULT_CIPHER_ALGO CIPHER_ALGO_BLOWFISH #define DEFAULT_PUBKEY_ALGO PUBKEY_ALGO_ELGAMAL #define DEFAULT_DIGEST_ALGO DIGEST_ALGO_RMD160 typedef struct { int header_okay; PK_LIST pk_list; cipher_filter_context_t cfx; } encrypt_filter_context_t; /*-- g10.c --*/ extern int g10_errors_seen; #if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 5 ) void g10_exit(int rc) __attribute__ ((noreturn)); #else void g10_exit(int rc); #endif void print_pubkey_algo_note( int algo ); void print_cipher_algo_note( int algo ); void print_digest_algo_note( int algo ); /*-- armor.c --*/ char *make_radix64_string( const byte *data, size_t len ); /*-- misc.c --*/ void trap_unaligned(void); void disable_core_dumps(void); u16 checksum_u16( unsigned n ); u16 checksum( byte *p, unsigned n ); u16 checksum_mpi( MPI a ); u16 checksum_mpi_counted_nbits( MPI a ); u32 buffer_to_u32( const byte *buffer ); /*-- helptext.c --*/ void display_online_help( const char *keyword ); /*-- encode.c --*/ int encode_symmetric( const char *filename ); int encode_store( const char *filename ); int encode_crypt( const char *filename, STRLIST remusr ); int encrypt_filter( void *opaque, int control, IOBUF a, byte *buf, size_t *ret_len); /*-- sign.c --*/ int complete_sig( PKT_signature *sig, PKT_secret_key *sk, MD_HANDLE md ); int sign_file( STRLIST filenames, int detached, STRLIST locusr, int do_encrypt, STRLIST remusr, const char *outfile ); int clearsign_file( const char *fname, STRLIST locusr, const char *outfile ); /*-- sig-check.c --*/ int check_key_signature( KBNODE root, KBNODE node, int *is_selfsig ); /*-- delkey.c --*/ int delete_key( const char *username, int secure ); /*-- keyedit.c --*/ void keyedit_menu( const char *username, STRLIST locusr, STRLIST cmds ); /*-- keygen.c --*/ u32 ask_expiredate(void); void generate_keypair(void); int keygen_add_key_expire( PKT_signature *sig, void *opaque ); int keygen_add_std_prefs( PKT_signature *sig, void *opaque ); int generate_subkeypair( KBNODE pub_keyblock, KBNODE sec_keyblock ); /*-- openfile.c --*/ int overwrite_filep( const char *fname ); char *make_outfile_name( const char *iname ); +char *ask_outfile_name( const char *name, size_t namelen ); int open_outfile( const char *iname, int mode, IOBUF *a ); IOBUF open_sigfile( const char *iname ); void copy_options_file( const char *destdir ); /*-- seskey.c --*/ void make_session_key( DEK *dek ); MPI encode_session_key( DEK *dek, unsigned nbits ); MPI encode_md_value( int pubkey_algo, MD_HANDLE md, int hash_algo, unsigned nbits ); /*-- comment.c --*/ KBNODE make_comment_node( const char *s ); KBNODE make_mpi_comment_node( const char *s, MPI a ); /*-- import.c --*/ int import_keys( const char *filename, int fast ); int import_keys_stream( IOBUF inp, int fast ); int collapse_uids( KBNODE *keyblock ); /*-- export.c --*/ int export_pubkeys( STRLIST users, int onlyrfc ); int export_pubkeys_stream( IOBUF out, STRLIST users, int onlyrfc ); int export_seckeys( STRLIST users ); /* dearmor.c --*/ int dearmor_file( const char *fname ); int enarmor_file( const char *fname ); /*-- revoke.c --*/ int gen_revoke( const char *uname ); /*-- keylist.c --*/ void public_key_list( int nnames, char **names ); void secret_key_list( int nnames, char **names ); /*-- verify.c --*/ int verify_signatures( int nfiles, char **files ); /*-- decrypt.c --*/ int decrypt_message( const char *filename ); /*-- plaintext.c --*/ int hash_datafiles( MD_HANDLE md, MD_HANDLE md2, STRLIST files, const char *sigfilename, int textmode ); /*-- signal.c --*/ void init_signals(void); void pause_on_sigusr( int which ); void block_all_signals(void); void unblock_all_signals(void); #endif /*G10_MAIN_H*/ diff --git a/g10/mainproc.c b/g10/mainproc.c index cb2388f06..51d436127 100644 --- a/g10/mainproc.c +++ b/g10/mainproc.c @@ -1,1119 +1,1120 @@ /* maPPPPinproc.c - handle packets * Copyright (C) 1998, 1999 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 "packet.h" #include "iobuf.h" #include "memory.h" #include "options.h" #include "util.h" #include "cipher.h" #include "keydb.h" #include "filter.h" #include "main.h" #include "status.h" #include "i18n.h" #include "trustdb.h" #include "hkp.h" /**************** * Structure to hold the context */ typedef struct mainproc_context *CTX; struct mainproc_context { struct mainproc_context *anchor; /* may be useful in the future */ PKT_public_key *last_pubkey; PKT_secret_key *last_seckey; PKT_user_id *last_user_id; md_filter_context_t mfx; int sigs_only; /* process only signatures and reject all other stuff */ int encrypt_only; /* process only encrytion messages */ STRLIST signed_data; const char *sigfilename; DEK *dek; int last_was_session_key; KBNODE list; /* the current list of packets */ int have_data; IOBUF iobuf; /* used to get the filename etc. */ int trustletter; /* temp usage in list_node */ ulong local_id; /* ditto */ }; static int do_proc_packets( CTX c, IOBUF a ); static void list_node( CTX c, KBNODE node ); static void proc_tree( CTX c, KBNODE node ); static void release_list( CTX c ) { if( !c->list ) return; proc_tree(c, c->list ); release_kbnode( c->list ); c->list = NULL; } static int add_onepass_sig( CTX c, PACKET *pkt ) { KBNODE node; if( c->list ) { /* add another packet */ if( c->list->pkt->pkttype != PKT_ONEPASS_SIG ) { log_error("add_onepass_sig: another packet is in the way\n"); release_list( c ); c->list = new_kbnode( pkt ); } else add_kbnode( c->list, new_kbnode( pkt )); } else /* insert the first one */ c->list = node = new_kbnode( pkt ); return 1; } static int add_user_id( CTX c, PACKET *pkt ) { if( !c->list ) { log_error("orphaned user id\n" ); return 0; } add_kbnode( c->list, new_kbnode( pkt ) ); return 1; } static int add_subkey( CTX c, PACKET *pkt ) { if( !c->list ) { log_error("subkey w/o mainkey\n" ); return 0; } add_kbnode( c->list, new_kbnode( pkt ) ); return 1; } static int add_signature( CTX c, PACKET *pkt ) { KBNODE node; if( pkt->pkttype == PKT_SIGNATURE && !c->list ) { /* This is the first signature for the following datafile. * G10 does not write such packets; instead it always uses * onepass-sig packets. The drawback of PGP's method * of prepending the signature to the data is * that it is not possible to make a signature from data read * from stdin. (G10 is able to read PGP stuff anyway.) */ node = new_kbnode( pkt ); c->list = node; return 1; } else if( !c->list ) return 0; /* oops (invalid packet sequence)*/ else if( !c->list->pkt ) BUG(); /* so nicht */ /* add a new signature node id at the end */ node = new_kbnode( pkt ); add_kbnode( c->list, node ); return 1; } static void proc_symkey_enc( CTX c, PACKET *pkt ) { PKT_symkey_enc *enc; enc = pkt->pkt.symkey_enc; if( enc->seskeylen ) log_error( "symkey_enc packet with session keys are not supported!\n"); else { c->last_was_session_key = 2; - c->dek = passphrase_to_dek( NULL, enc->cipher_algo, &enc->s2k, 0 ); + c->dek = passphrase_to_dek( NULL, 0, enc->cipher_algo, &enc->s2k, 0 ); } free_packet(pkt); } static void proc_pubkey_enc( CTX c, PACKET *pkt ) { PKT_pubkey_enc *enc; int result = 0; /* check whether the secret key is available and store in this case */ c->last_was_session_key = 1; enc = pkt->pkt.pubkey_enc; /*printf("enc: encrypted by a pubkey with keyid %08lX\n", enc->keyid[1] );*/ /* Hmmm: why do I have this algo check here - anyway there is * function to check it. */ if( opt.verbose ) log_info(_("public key is %08lX\n"), (ulong)enc->keyid[1] ); if( is_status_enabled() ) { char buf[50]; - sprintf(buf, "%08lX%08lX", (ulong)enc->keyid[0], (ulong)enc->keyid[1]); + sprintf(buf, "%08lX%08lX %d 0", + (ulong)enc->keyid[0], (ulong)enc->keyid[1], enc->pubkey_algo ); write_status_text( STATUS_ENC_TO, buf ); } if( is_ELGAMAL(enc->pubkey_algo) || enc->pubkey_algo == PUBKEY_ALGO_DSA || is_RSA(enc->pubkey_algo) ) { if ( !c->dek && ((!enc->keyid[0] && !enc->keyid[1]) || !seckey_available( enc->keyid )) ) { c->dek = m_alloc_secure( sizeof *c->dek ); if( (result = get_session_key( enc, c->dek )) ) { /* error: delete the DEK */ m_free(c->dek); c->dek = NULL; } } } else result = G10ERR_PUBKEY_ALGO; if( result == -1 ) ; else if( !result ) { if( opt.verbose > 1 ) log_info( _("public key encrypted data: good DEK\n") ); } else { /* fixme: defer this message until we have parsed all packets of * this type - do this by building a list of keys with their stati * and store it with the context. do_proc_packets can then use * this list to display some information */ log_error(_("public key decryption failed: %s\n"), g10_errstr(result)); } free_packet(pkt); } static void proc_encrypted( CTX c, PACKET *pkt ) { int result = 0; /*log_debug("dat: %sencrypted data\n", c->dek?"":"conventional ");*/ if( !c->dek && !c->last_was_session_key ) { /* assume this is old conventional encrypted data */ - c->dek = passphrase_to_dek( NULL, + c->dek = passphrase_to_dek( NULL, 0, opt.def_cipher_algo ? opt.def_cipher_algo : DEFAULT_CIPHER_ALGO, NULL, 0 ); } else if( !c->dek ) result = G10ERR_NO_SECKEY; if( !result ) result = decrypt_data( c, pkt->pkt.encrypted, c->dek ); m_free(c->dek); c->dek = NULL; if( result == -1 ) ; else if( !result ) { write_status( STATUS_DECRYPTION_OKAY ); if( opt.verbose > 1 ) log_info(_("decryption okay\n")); if( pkt->pkt.encrypted->mdc_method ) write_status( STATUS_GOODMDC ); } else if( result == G10ERR_BAD_SIGN ) { log_error(_("WARNING: encrypted message has been manipulated!\n")); write_status( STATUS_BADMDC ); } else { write_status( STATUS_DECRYPTION_FAILED ); log_error(_("decryption failed: %s\n"), g10_errstr(result)); /* FIXME: if this is secret key not available, try with * other keys */ } free_packet(pkt); c->last_was_session_key = 0; } static void proc_plaintext( CTX c, PACKET *pkt ) { PKT_plaintext *pt = pkt->pkt.plaintext; int any, clearsig, only_md5, rc; KBNODE n; if( pt->namelen == 8 && !memcmp( pt->name, "_CONSOLE", 8 ) ) log_info(_("NOTE: sender requested \"for-your-eyes-only\"\n")); else if( opt.verbose ) log_info(_("original file name='%.*s'\n"), pt->namelen, pt->name); free_md_filter_context( &c->mfx ); c->mfx.md = md_open( 0, 0); /* fixme: we may need to push the textfilter if we have sigclass 1 * and no armoring - Not yet tested * Hmmm, why don't we need it at all if we have sigclass 1 * Should we assume that plaintext in mode 't' has always sigclass 1?? * See: Russ Allbery's mail 1999-02-09 */ any = clearsig = only_md5 = 0; for(n=c->list; n; n = n->next ) { if( n->pkt->pkttype == PKT_ONEPASS_SIG ) { if( n->pkt->pkt.onepass_sig->digest_algo ) { md_enable( c->mfx.md, n->pkt->pkt.onepass_sig->digest_algo ); if( !any && n->pkt->pkt.onepass_sig->digest_algo == DIGEST_ALGO_MD5 ) only_md5 = 1; else only_md5 = 0; any = 1; } if( n->pkt->pkt.onepass_sig->sig_class != 0x01 ) only_md5 = 0; /* Check whether this is a cleartext signature. We assume that * we have one if the sig_class is 1 and the keyid is 0, that * are the faked packets produced by armor.c. There is a * possibility that this fails, but there is no other easy way * to do it. (We could use a special packet type to indicate * this, but this may also be faked - it simply can't be verified * and is _no_ security issue) */ if( n->pkt->pkt.onepass_sig->sig_class == 0x01 && !n->pkt->pkt.onepass_sig->keyid[0] && !n->pkt->pkt.onepass_sig->keyid[1] ) clearsig = 1; } } if( !any ) { /* no onepass sig packet: enable all standard algos */ md_enable( c->mfx.md, DIGEST_ALGO_RMD160 ); md_enable( c->mfx.md, DIGEST_ALGO_SHA1 ); md_enable( c->mfx.md, DIGEST_ALGO_MD5 ); } if( only_md5 ) { /* This is a kludge to work around a bug in pgp2. It does only * catch those mails which are armored. To catch the non-armored * pgp mails we could see whether there is the signature packet * in front of the plaintext. If someone needs this, send me a patch. */ c->mfx.md2 = md_open( DIGEST_ALGO_MD5, 0); } #if 0 #warning md_start_debug is enabled md_start_debug( c->mfx.md, "verify" ); #endif rc = handle_plaintext( pt, &c->mfx, c->sigs_only, clearsig ); if( rc == G10ERR_CREATE_FILE && !c->sigs_only) { /* can't write output but we hash it anyway to * check the signature */ rc = handle_plaintext( pt, &c->mfx, 1, clearsig ); } if( rc ) log_error( "handle plaintext failed: %s\n", g10_errstr(rc)); free_packet(pkt); c->last_was_session_key = 0; } static int proc_compressed_cb( IOBUF a, void *info ) { return proc_signature_packets( info, a, ((CTX)info)->signed_data, ((CTX)info)->sigfilename ); } static int proc_encrypt_cb( IOBUF a, void *info ) { return proc_encryption_packets( info, a ); } static void proc_compressed( CTX c, PACKET *pkt ) { PKT_compressed *zd = pkt->pkt.compressed; int rc; /*printf("zip: compressed data packet\n");*/ if( c->sigs_only ) rc = handle_compressed( c, zd, proc_compressed_cb, c ); else if( c->encrypt_only ) rc = handle_compressed( c, zd, proc_encrypt_cb, c ); else rc = handle_compressed( c, zd, NULL, NULL ); if( rc ) log_error("uncompressing failed: %s\n", g10_errstr(rc)); free_packet(pkt); c->last_was_session_key = 0; } /**************** * check the signature * Returns: 0 = valid signature or an error code */ static int do_check_sig( CTX c, KBNODE node, int *is_selfsig ) { PKT_signature *sig; MD_HANDLE md = NULL, md2 = NULL; int algo, rc; assert( node->pkt->pkttype == PKT_SIGNATURE ); if( is_selfsig ) *is_selfsig = 0; sig = node->pkt->pkt.signature; algo = sig->digest_algo; if( (rc=check_digest_algo(algo)) ) return rc; if( sig->sig_class == 0x00 ) { if( c->mfx.md ) md = md_copy( c->mfx.md ); else /* detached signature */ md = md_open( 0, 0 ); /* signature_check() will enable the md*/ } else if( sig->sig_class == 0x01 ) { /* how do we know that we have to hash the (already hashed) text * in canonical mode ??? (calculating both modes???) */ if( c->mfx.md ) { md = md_copy( c->mfx.md ); if( c->mfx.md2 ) md2 = md_copy( c->mfx.md2 ); } else { /* detached signature */ log_debug("Do we really need this here?"); md = md_open( 0, 0 ); /* signature_check() will enable the md*/ md2 = md_open( 0, 0 ); } } else if( (sig->sig_class&~3) == 0x10 || sig->sig_class == 0x18 || sig->sig_class == 0x20 || sig->sig_class == 0x30 ) { /* classes 0x10..0x17,0x20,0x30 */ if( c->list->pkt->pkttype == PKT_PUBLIC_KEY || c->list->pkt->pkttype == PKT_PUBLIC_SUBKEY ) { return check_key_signature( c->list, node, is_selfsig ); } else { log_error("invalid root packet for sigclass %02x\n", sig->sig_class); return G10ERR_SIG_CLASS; } } else return G10ERR_SIG_CLASS; rc = signature_check( sig, md ); if( rc == G10ERR_BAD_SIGN && md2 ) rc = signature_check( sig, md2 ); md_close(md); md_close(md2); return rc; } static void print_userid( PACKET *pkt ) { if( !pkt ) BUG(); if( pkt->pkttype != PKT_USER_ID ) { printf("ERROR: unexpected packet type %d", pkt->pkttype ); return; } print_string( stdout, pkt->pkt.user_id->name, pkt->pkt.user_id->len, opt.with_colons ); } static void print_fingerprint( PKT_public_key *pk, PKT_secret_key *sk ) { byte array[MAX_FINGERPRINT_LEN], *p; size_t i, n; if( sk ) fingerprint_from_sk( sk, array, &n ); else fingerprint_from_pk( pk, array, &n ); p = array; if( opt.with_colons ) { printf("fpr:::::::::"); for(i=0; i < n ; i++, p++ ) printf("%02X", *p ); putchar(':'); } else { printf(" Key fingerprint ="); if( n == 20 ) { for(i=0; i < n ; i++, i++, p += 2 ) { if( i == 10 ) putchar(' '); printf(" %02X%02X", *p, p[1] ); } } else { for(i=0; i < n ; i++, p++ ) { if( i && !(i%8) ) putchar(' '); printf(" %02X", *p ); } } } putchar('\n'); } static void print_notation_data( PKT_signature *sig ) { size_t n, n1, n2; const byte *p; int seq = 0; while( (p = enum_sig_subpkt( sig->hashed_data, SIGSUBPKT_NOTATION, &n, &seq )) ) { if( n < 8 ) { log_info(_("WARNING: invalid notation data found\n")); return; } if( !(*p & 0x80) ) return; /* not human readable */ n1 = (p[4] << 8) | p[5]; n2 = (p[6] << 8) | p[7]; p += 8; if( 8+n1+n2 != n ) { log_info(_("WARNING: invalid notation data found\n")); return; } log_info(_("Notation: ") ); print_string( log_stream(), p, n1, 0 ); putc( '=', log_stream() ); print_string( log_stream(), p+n1, n2, 0 ); putc( '\n', log_stream() ); } if( (p = parse_sig_subpkt( sig->hashed_data, SIGSUBPKT_POLICY, &n ) )) { log_info(_("Policy: ") ); print_string( log_stream(), p, n, 0 ); putc( '\n', log_stream() ); } /* Now check wheter the key of this signature has some * notation data */ /* TODO */ } /**************** * List the certificate in a user friendly way */ static void list_node( CTX c, KBNODE node ) { int any=0; int mainkey; if( !node ) ; else if( (mainkey = (node->pkt->pkttype == PKT_PUBLIC_KEY) ) || node->pkt->pkttype == PKT_PUBLIC_SUBKEY ) { PKT_public_key *pk = node->pkt->pkt.public_key; if( opt.with_colons ) { u32 keyid[2]; keyid_from_pk( pk, keyid ); if( mainkey ) { c->local_id = pk->local_id; c->trustletter = query_trust_info( pk, NULL ); } printf("%s:%c:%u:%d:%08lX%08lX:%s:%s:", mainkey? "pub":"sub", c->trustletter, nbits_from_pk( pk ), pk->pubkey_algo, (ulong)keyid[0],(ulong)keyid[1], datestr_from_pk( pk ), pk->expiredate? strtimestamp(pk->expiredate):"" ); if( c->local_id ) printf("%lu", c->local_id ); putchar(':'); if( c->local_id ) putchar( get_ownertrust_info( c->local_id ) ); putchar(':'); } else printf("%s %4u%c/%08lX %s ", mainkey? "pub":"sub", nbits_from_pk( pk ), pubkey_letter( pk->pubkey_algo ), (ulong)keyid_from_pk( pk, NULL ), datestr_from_pk( pk ) ); if( mainkey ) { /* and now list all userids with their signatures */ for( node = node->next; node; node = node->next ) { if( node->pkt->pkttype == PKT_SIGNATURE ) { if( !any ) { if( node->pkt->pkt.signature->sig_class == 0x20 ) puts("[revoked]"); else putchar('\n'); any = 1; } list_node(c, node ); } else if( node->pkt->pkttype == PKT_USER_ID ) { if( any ) { if( opt.with_colons ) printf("uid:::::::::"); else printf( "uid%*s", 28, "" ); } print_userid( node->pkt ); if( opt.with_colons ) putchar(':'); putchar('\n'); if( opt.fingerprint && !any ) print_fingerprint( pk, NULL ); any=1; } else if( node->pkt->pkttype == PKT_PUBLIC_SUBKEY ) { if( !any ) { putchar('\n'); any = 1; } list_node(c, node ); } } } if( !any ) putchar('\n'); if( !mainkey && opt.fingerprint > 1 ) print_fingerprint( pk, NULL ); } else if( (mainkey = (node->pkt->pkttype == PKT_SECRET_KEY) ) || node->pkt->pkttype == PKT_SECRET_SUBKEY ) { PKT_secret_key *sk = node->pkt->pkt.secret_key; if( opt.with_colons ) { u32 keyid[2]; keyid_from_sk( sk, keyid ); printf("%s::%u:%d:%08lX%08lX:%s:%s:::", mainkey? "sec":"ssb", nbits_from_sk( sk ), sk->pubkey_algo, (ulong)keyid[0],(ulong)keyid[1], datestr_from_sk( sk ), sk->expiredate? strtimestamp(sk->expiredate):"" /* fixme: add LID */ ); } else printf("%s %4u%c/%08lX %s ", mainkey? "sec":"ssb", nbits_from_sk( sk ), pubkey_letter( sk->pubkey_algo ), (ulong)keyid_from_sk( sk, NULL ), datestr_from_sk( sk ) ); if( mainkey ) { /* and now list all userids with their signatures */ for( node = node->next; node; node = node->next ) { if( node->pkt->pkttype == PKT_SIGNATURE ) { if( !any ) { if( node->pkt->pkt.signature->sig_class == 0x20 ) puts("[revoked]"); else putchar('\n'); any = 1; } list_node(c, node ); } else if( node->pkt->pkttype == PKT_USER_ID ) { if( any ) { if( opt.with_colons ) printf("uid:::::::::"); else printf( "uid%*s", 28, "" ); } print_userid( node->pkt ); if( opt.with_colons ) putchar(':'); putchar('\n'); if( opt.fingerprint && !any ) print_fingerprint( NULL, sk ); any=1; } else if( node->pkt->pkttype == PKT_SECRET_SUBKEY ) { if( !any ) { putchar('\n'); any = 1; } list_node(c, node ); } } } if( !any ) putchar('\n'); if( !mainkey && opt.fingerprint > 1 ) print_fingerprint( NULL, sk ); } else if( node->pkt->pkttype == PKT_SIGNATURE ) { PKT_signature *sig = node->pkt->pkt.signature; int is_selfsig = 0; int rc2=0; size_t n; char *p; int sigrc = ' '; if( !opt.list_sigs ) return; if( sig->sig_class == 0x20 || sig->sig_class == 0x30 ) fputs("rev", stdout); else fputs("sig", stdout); if( opt.check_sigs ) { fflush(stdout); switch( (rc2=do_check_sig( c, node, &is_selfsig )) ) { case 0: sigrc = '!'; break; case G10ERR_BAD_SIGN: sigrc = '-'; break; case G10ERR_NO_PUBKEY: sigrc = '?'; break; default: sigrc = '%'; break; } } else { /* check whether this is a self signature */ u32 keyid[2]; if( c->list->pkt->pkttype == PKT_PUBLIC_KEY || c->list->pkt->pkttype == PKT_SECRET_KEY ) { if( c->list->pkt->pkttype == PKT_PUBLIC_KEY ) keyid_from_pk( c->list->pkt->pkt.public_key, keyid ); else keyid_from_sk( c->list->pkt->pkt.secret_key, keyid ); if( keyid[0] == sig->keyid[0] && keyid[1] == sig->keyid[1] ) is_selfsig = 1; } } if( opt.with_colons ) { putchar(':'); if( sigrc != ' ' ) putchar(sigrc); printf(":::%08lX%08lX:%s::::", (ulong)sig->keyid[0], (ulong)sig->keyid[1], datestr_from_sig(sig)); } else printf("%c %08lX %s ", sigrc, (ulong)sig->keyid[1], datestr_from_sig(sig)); if( sigrc == '%' ) printf("[%s] ", g10_errstr(rc2) ); else if( sigrc == '?' ) ; else if( is_selfsig ) { if( opt.with_colons ) putchar(':'); fputs( sig->sig_class == 0x18? "[keybind]":"[selfsig]", stdout); if( opt.with_colons ) putchar(':'); } else { p = get_user_id( sig->keyid, &n ); print_string( stdout, p, n, opt.with_colons ); m_free(p); } if( opt.with_colons ) printf(":%02x:", sig->sig_class ); putchar('\n'); } else log_error("invalid node with packet of type %d\n", node->pkt->pkttype); } int proc_packets( void *anchor, IOBUF a ) { int rc; CTX c = m_alloc_clear( sizeof *c ); c->anchor = anchor; rc = do_proc_packets( c, a ); m_free( c ); return rc; } int proc_signature_packets( void *anchor, IOBUF a, STRLIST signedfiles, const char *sigfilename ) { CTX c = m_alloc_clear( sizeof *c ); int rc; c->anchor = anchor; c->sigs_only = 1; c->signed_data = signedfiles; c->sigfilename = sigfilename; rc = do_proc_packets( c, a ); m_free( c ); return rc; } int proc_encryption_packets( void *anchor, IOBUF a ) { CTX c = m_alloc_clear( sizeof *c ); int rc; c->anchor = anchor; c->encrypt_only = 1; rc = do_proc_packets( c, a ); m_free( c ); return rc; } int do_proc_packets( CTX c, IOBUF a ) { PACKET *pkt = m_alloc( sizeof *pkt ); int rc=0; int any_data=0; int newpkt; c->iobuf = a; init_packet(pkt); while( (rc=parse_packet(a, pkt)) != -1 ) { any_data = 1; if( rc ) { free_packet(pkt); if( rc == G10ERR_INVALID_PACKET ) break; continue; } newpkt = -1; if( opt.list_packets ) { switch( pkt->pkttype ) { case PKT_PUBKEY_ENC: proc_pubkey_enc( c, pkt ); break; case PKT_SYMKEY_ENC: proc_symkey_enc( c, pkt ); break; case PKT_ENCRYPTED: case PKT_ENCRYPTED_MDC: proc_encrypted( c, pkt ); break; case PKT_COMPRESSED: proc_compressed( c, pkt ); break; default: newpkt = 0; break; } } else if( c->sigs_only ) { switch( pkt->pkttype ) { case PKT_PUBLIC_KEY: case PKT_SECRET_KEY: case PKT_USER_ID: case PKT_SYMKEY_ENC: case PKT_PUBKEY_ENC: case PKT_ENCRYPTED: case PKT_ENCRYPTED_MDC: rc = G10ERR_UNEXPECTED; goto leave; case PKT_SIGNATURE: newpkt = add_signature( c, pkt ); break; case PKT_PLAINTEXT: proc_plaintext( c, pkt ); break; case PKT_COMPRESSED: proc_compressed( c, pkt ); break; case PKT_ONEPASS_SIG: newpkt = add_onepass_sig( c, pkt ); break; default: newpkt = 0; break; } } else if( c->encrypt_only ) { switch( pkt->pkttype ) { case PKT_PUBLIC_KEY: case PKT_SECRET_KEY: case PKT_USER_ID: rc = G10ERR_UNEXPECTED; goto leave; case PKT_SIGNATURE: newpkt = add_signature( c, pkt ); break; case PKT_SYMKEY_ENC: proc_symkey_enc( c, pkt ); break; case PKT_PUBKEY_ENC: proc_pubkey_enc( c, pkt ); break; case PKT_ENCRYPTED: case PKT_ENCRYPTED_MDC: proc_encrypted( c, pkt ); break; case PKT_PLAINTEXT: proc_plaintext( c, pkt ); break; case PKT_COMPRESSED: proc_compressed( c, pkt ); break; case PKT_ONEPASS_SIG: newpkt = add_onepass_sig( c, pkt ); break; default: newpkt = 0; break; } } else { switch( pkt->pkttype ) { case PKT_PUBLIC_KEY: case PKT_SECRET_KEY: release_list( c ); c->list = new_kbnode( pkt ); newpkt = 1; break; case PKT_PUBLIC_SUBKEY: case PKT_SECRET_SUBKEY: newpkt = add_subkey( c, pkt ); break; case PKT_USER_ID: newpkt = add_user_id( c, pkt ); break; case PKT_SIGNATURE: newpkt = add_signature( c, pkt ); break; case PKT_PUBKEY_ENC: proc_pubkey_enc( c, pkt ); break; case PKT_SYMKEY_ENC: proc_symkey_enc( c, pkt ); break; case PKT_ENCRYPTED: case PKT_ENCRYPTED_MDC: proc_encrypted( c, pkt ); break; case PKT_PLAINTEXT: proc_plaintext( c, pkt ); break; case PKT_COMPRESSED: proc_compressed( c, pkt ); break; case PKT_ONEPASS_SIG: newpkt = add_onepass_sig( c, pkt ); break; default: newpkt = 0; break; } } if( pkt->pkttype != PKT_SIGNATURE ) c->have_data = pkt->pkttype == PKT_PLAINTEXT; if( newpkt == -1 ) ; else if( newpkt ) { pkt = m_alloc( sizeof *pkt ); init_packet(pkt); } else free_packet(pkt); } if( rc == G10ERR_INVALID_PACKET ) write_status_text( STATUS_NODATA, "3" ); if( any_data ) rc = 0; else if( rc == -1 ) write_status_text( STATUS_NODATA, "2" ); leave: release_list( c ); m_free(c->dek); free_packet( pkt ); m_free( pkt ); free_md_filter_context( &c->mfx ); return rc; } static int check_sig_and_print( CTX c, KBNODE node ) { PKT_signature *sig = node->pkt->pkt.signature; const char *astr, *tstr; int rc; if( opt.skip_verify ) { log_info(_("signature verification suppressed\n")); return 0; } tstr = asctimestamp(sig->timestamp); astr = pubkey_algo_to_string( sig->pubkey_algo ); log_info(_("Signature made %.*s using %s key ID %08lX\n"), (int)strlen(tstr), tstr, astr? astr: "?", (ulong)sig->keyid[1] ); rc = do_check_sig(c, node, NULL ); if( rc == G10ERR_NO_PUBKEY && opt.keyserver_name ) { if( !hkp_ask_import( sig->keyid ) ) rc = do_check_sig(c, node, NULL ); } if( !rc || rc == G10ERR_BAD_SIGN ) { KBNODE un, keyblock; char *us; int count=0; keyblock = get_pubkeyblock( sig->keyid ); us = get_long_user_id_string( sig->keyid ); write_status_text( rc? STATUS_BADSIG : STATUS_GOODSIG, us ); m_free(us); /* fixme: list only user ids which are valid and add information * about the trustworthiness of each user id, sort them. * Integrate this with check_signatures_trust(). */ for( un=keyblock; un; un = un->next ) { if( un->pkt->pkttype != PKT_USER_ID ) continue; if( !count++ ) log_info(rc? _("BAD signature from \"") : _("Good signature from \"")); else log_info( _(" aka \"")); print_string( log_stream(), un->pkt->pkt.user_id->name, un->pkt->pkt.user_id->len, '\"' ); fputs("\"\n", log_stream() ); if( rc ) break; /* print only one id in this case */ } if( !count ) { /* just in case that we have no userid */ log_info(rc? _("BAD signature from \"") : _("Good signature from \"")); fputs("[?]\"\n", log_stream() ); } release_kbnode( keyblock ); if( !rc ) print_notation_data( sig ); if( !rc && is_status_enabled() ) { /* print a status response with the fingerprint */ PKT_public_key *pk = m_alloc_clear( sizeof *pk ); if( !get_pubkey( pk, sig->keyid ) ) { byte array[MAX_FINGERPRINT_LEN], *p; char buf[MAX_FINGERPRINT_LEN*2+61]; size_t i, n; fingerprint_from_pk( pk, array, &n ); p = array; for(i=0; i < n ; i++, p++ ) sprintf(buf+2*i, "%02X", *p ); sprintf(buf+strlen(buf), " %s %lu", strtimestamp( sig->timestamp ), (ulong)sig->timestamp ); write_status_text( STATUS_VALIDSIG, buf ); } free_public_key( pk ); } if( !rc ) rc = check_signatures_trust( sig ); if( rc ) g10_errors_seen = 1; if( opt.batch && rc ) g10_exit(1); } else { char buf[50]; sprintf(buf, "%08lX%08lX %d %d %02x %lu %d", (ulong)sig->keyid[0], (ulong)sig->keyid[1], sig->pubkey_algo, sig->digest_algo, sig->sig_class, (ulong)sig->timestamp, rc ); write_status_text( STATUS_ERRSIG, buf ); log_error(_("Can't check signature: %s\n"), g10_errstr(rc) ); } return rc; } /**************** * Process the tree which starts at node */ static void proc_tree( CTX c, KBNODE node ) { KBNODE n1; int rc; if( opt.list_packets ) return; c->local_id = 0; c->trustletter = ' '; if( node->pkt->pkttype == PKT_PUBLIC_KEY || node->pkt->pkttype == PKT_PUBLIC_SUBKEY ) { merge_keys_and_selfsig( node ); list_node( c, node ); } else if( node->pkt->pkttype == PKT_SECRET_KEY ) { merge_keys_and_selfsig( node ); list_node( c, node ); } else if( node->pkt->pkttype == PKT_ONEPASS_SIG ) { /* check all signatures */ if( !c->have_data ) { free_md_filter_context( &c->mfx ); /* prepare to create all requested message digests */ c->mfx.md = md_open(0, 0); /* fixme: why looking for the signature packet and not 1passpacket*/ for( n1 = node; (n1 = find_next_kbnode(n1, PKT_SIGNATURE )); ) { md_enable( c->mfx.md, n1->pkt->pkt.signature->digest_algo); } /* ask for file and hash it */ if( c->sigs_only ) rc = hash_datafiles( c->mfx.md, NULL, c->signed_data, c->sigfilename, n1? (n1->pkt->pkt.onepass_sig->sig_class == 0x01):0 ); else rc = ask_for_detached_datafile( &c->mfx, iobuf_get_fname(c->iobuf)); if( rc ) { log_error("can't hash datafile: %s\n", g10_errstr(rc)); return; } } for( n1 = node; (n1 = find_next_kbnode(n1, PKT_SIGNATURE )); ) check_sig_and_print( c, n1 ); } else if( node->pkt->pkttype == PKT_SIGNATURE ) { PKT_signature *sig = node->pkt->pkt.signature; if( !c->have_data ) { free_md_filter_context( &c->mfx ); c->mfx.md = md_open(sig->digest_algo, 0); if( sig->digest_algo == DIGEST_ALGO_MD5 && is_RSA( sig->pubkey_algo ) ) { /* enable a workaround for a pgp2 bug */ c->mfx.md2 = md_open( DIGEST_ALGO_MD5, 0 ); } #if 0 #warning md_start_debug enabled md_start_debug( c->mfx.md, "det1" ); if( c->mfx.md2 ) md_start_debug( c->mfx.md2, "det2" ); #endif /* Here we have another hack to work around a pgp 2 bug * It works by not using the textmode for detached signatures; * this will let the first signazure check (on md) fail * but the second one (on md2) which adds an extra CR should * then produce the "correct" hash. This is very, very ugly * hack but it may help in some cases (and break others) */ if( c->sigs_only ) rc = hash_datafiles( c->mfx.md, c->mfx.md2, c->signed_data, c->sigfilename, c->mfx.md2? 0 :(sig->sig_class == 0x01) ); else rc = ask_for_detached_datafile( &c->mfx, iobuf_get_fname(c->iobuf)); if( rc ) { log_error("can't hash datafile: %s\n", g10_errstr(rc)); return; } } else log_info(_("old style (PGP 2.x) signature\n")); check_sig_and_print( c, node ); } else log_error(_("invalid root packet detected in proc_tree()\n")); } diff --git a/g10/openfile.c b/g10/openfile.c index af0ab3c1d..ebf954843 100644 --- a/g10/openfile.c +++ b/g10/openfile.c @@ -1,234 +1,272 @@ /* openfile.c * Copyright (C) 1998 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 "util.h" #include "memory.h" #include "ttyio.h" #include "options.h" #include "main.h" #include "status.h" #include "i18n.h" #ifdef USE_ONLY_8DOT3 #define SKELEXT ".skl" #else #define SKELEXT ".skel" #endif /* FIXME: Implement opt.interactive. */ /**************** * Check whether FNAME exists and ask if it's okay to overwrite an * existing one. * Returns: True: it's okay to overwrite or the file does not exist * False: Do not overwrite */ int overwrite_filep( const char *fname ) { if( !fname || (*fname == '-' && !fname[1]) ) return 1; /* writing to stdout is always okay */ if( access( fname, F_OK ) ) return 1; /* does not exist */ /* fixme: add some backup stuff in case of overwrite */ if( opt.answer_yes ) return 1; if( opt.answer_no || opt.batch ) return 0; /* do not overwrite */ tty_printf(_("File `%s' exists. "), fname); if( cpr_get_answer_is_yes("openfile.overwrite.okay", _("Overwrite (y/N)? ")) ) return 1; return 0; } /**************** * Strip know extensions from iname and return a newly allocated * filename. Return NULL if we can't do that. */ char * make_outfile_name( const char *iname ) { size_t n; if( (!iname || (*iname=='-' && !iname[1]) )) return m_strdup("-"); #ifdef HAVE_DRIVE_LETTERS #warning add case insensitive compare #endif n = strlen(iname); if( n > 4 && ( !strcmp(iname+n-4,".gpg") || !strcmp(iname+n-4,".sig") || !strcmp(iname+n-4,".asc") ) ) { char *buf = m_strdup( iname ); buf[n-4] = 0; return buf; } log_error(_("%s: unknown suffix\n"), iname ); return NULL; } +/**************** + * Ask for a outputfilename and use the given one as default. + * Return NULL if no file has been given or it is not possible to + * ask the user. + */ +char * +ask_outfile_name( const char *name, size_t namelen ) +{ + size_t n; + const char *s; + char *prompt; + char *fname; + char *defname; + + if( opt.batch ) + return NULL; + + s = _("Enter new filename"); + + n = strlen(s) + namelen + 10; + defname = name && namelen? make_printable_string( name, namelen, 0): NULL; + prompt = m_alloc(n); + if( defname ) + sprintf(prompt, "%s [%s]: ", s, defname ); + else + sprintf(prompt, "%s: ", s ); + fname = cpr_get("openfile.askoutname", prompt ); + cpr_kill_prompt(); + m_free(prompt); + if( !*fname ) { + m_free( fname ); fname = NULL; + fname = defname; defname = NULL; + } + m_free(defname); + return fname; +} + + /**************** * Make an output filename for the inputfile INAME. * Returns an IOBUF and an errorcode * Mode 0 = use ".gpg" * 1 = use ".asc" * 2 = use ".sig" */ int open_outfile( const char *iname, int mode, IOBUF *a ) { int rc = 0; *a = NULL; if( (!iname || (*iname=='-' && !iname[1])) && !opt.outfile ) { if( !(*a = iobuf_create(NULL)) ) { log_error(_("%s: can't open: %s\n"), "[stdout]", strerror(errno) ); rc = G10ERR_CREATE_FILE; } else if( opt.verbose ) log_info(_("writing to stdout\n")); } else { char *buf=NULL; const char *name; if( opt.dry_run ) name = "/dev/null"; else if( opt.outfile ) name = opt.outfile; else { #ifdef USE_ONLY_8DOT3 #warning please implement 8.3 files #endif buf = m_alloc(strlen(iname)+4+1); strcpy(stpcpy(buf,iname), mode==1 ? ".asc" : mode==2 ? ".sig" : ".gpg"); name = buf; } if( overwrite_filep( name ) ) { if( !(*a = iobuf_create( name )) ) { log_error(_("%s: can't create: %s\n"), name, strerror(errno) ); rc = G10ERR_CREATE_FILE; } else if( opt.verbose ) log_info(_("writing to `%s'\n"), name ); } else rc = G10ERR_FILE_EXISTS; m_free(buf); } return rc; } /**************** * Try to open a file without the extension ".sig" or ".asc" * Return NULL if such a file is not available. */ IOBUF open_sigfile( const char *iname ) { IOBUF a = NULL; size_t len; #ifdef USE_ONLY_8DOT3 #warning please implement 8.3 files #endif if( iname && !(*iname == '-' && !iname[1]) ) { len = strlen(iname); if( len > 4 && ( !strcmp(iname + len - 4, ".sig") || !strcmp(iname + len - 4, ".asc")) ) { char *buf; buf = m_strdup(iname); buf[len-4] = 0 ; a = iobuf_open( buf ); if( opt.verbose ) log_info(_("assuming signed data in `%s'\n"), buf ); m_free(buf); } } return a; } /**************** * Copy the option file skeleton to the given directory. */ void copy_options_file( const char *destdir ) { const char *datadir = GNUPG_DATADIR; char *fname; FILE *src, *dst; int linefeeds=0; int c; if( opt.dry_run ) return; fname = m_alloc( strlen(datadir) + strlen(destdir) + 15 ); strcpy(stpcpy(fname, datadir), "/options" SKELEXT ); src = fopen( fname, "r" ); if( !src ) { log_error(_("%s: can't open: %s\n"), fname, strerror(errno) ); m_free(fname); return; } strcpy(stpcpy(fname, destdir), "/options" ); dst = fopen( fname, "w" ); if( !dst ) { log_error(_("%s: can't create: %s\n"), fname, strerror(errno) ); fclose( src ); m_free(fname); return; } while( (c=getc(src)) != EOF ) { if( linefeeds < 3 ) { if( c == '\n' ) linefeeds++; } else putc( c, dst ); } fclose( dst ); fclose( src ); log_info(_("%s: new options file created\n"), fname ); m_free(fname); } diff --git a/g10/passphrase.c b/g10/passphrase.c index 9581246f2..80c6fa33f 100644 --- a/g10/passphrase.c +++ b/g10/passphrase.c @@ -1,289 +1,291 @@ /* passphrase.c - Get a passphrase * Copyright (C) 1998 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 "util.h" #include "memory.h" #include "options.h" #include "ttyio.h" #include "cipher.h" #include "keydb.h" #include "main.h" #include "i18n.h" #include "status.h" static char *fd_passwd = NULL; static char *next_pw = NULL; static char *last_pw = NULL; static void hash_passphrase( DEK *dek, char *pw, STRING2KEY *s2k, int create ); int have_static_passphrase() { return !!fd_passwd; } /**************** * Set the passphrase to be used for the next query and only for the next * one. */ void set_next_passphrase( const char *s ) { m_free(next_pw); next_pw = NULL; if( s ) { next_pw = m_alloc_secure( strlen(s)+1 ); strcpy(next_pw, s ); } } /**************** * Get the last passphrase used in passphrase_to_dek. * Note: This removes the passphrase from this modules and * the caller must free the result. May return NULL: */ char * get_last_passphrase() { char *p = last_pw; last_pw = NULL; return p; } void read_passphrase_from_fd( int fd ) { int i, len; char *pw; if( !opt.batch ) tty_printf("Reading passphrase from file descriptor %d ...", fd ); for( pw = NULL, i = len = 100; ; i++ ) { if( i >= len-1 ) { char *pw2 = pw; len += 100; pw = m_alloc_secure( len ); if( pw2 ) memcpy(pw, pw2, i ); else i=0; } if( read( fd, pw+i, 1) != 1 || pw[i] == '\n' ) break; } pw[i] = 0; if( !opt.batch ) tty_printf("\b\b\b \n" ); m_free( fd_passwd ); fd_passwd = pw; } /**************** * Get a passphrase for the secret key with KEYID, display TEXT * if the user needs to enter the passphrase. * mode 0 = standard, 2 = create new passphrase * Returns: a DEK with a session key; caller must free * or NULL if the passphrase was not correctly repeated. * (only for mode 2) * a dek->keylen of 0 means: no passphrase entered. * (only for mode 2) + * pubkey_algo is only informational. */ DEK * -passphrase_to_dek( u32 *keyid, int cipher_algo, STRING2KEY *s2k, int mode ) +passphrase_to_dek( u32 *keyid, int pubkey_algo, + int cipher_algo, STRING2KEY *s2k, int mode ) { char *pw = NULL; DEK *dek; STRING2KEY help_s2k; if( !s2k ) { s2k = &help_s2k; s2k->mode = 0; /* this should be MD5 if cipher is IDEA, but because we do * not have IDEA, we use the default one, the user * can select it from the commandline */ s2k->hash_algo = opt.def_digest_algo?opt.def_digest_algo :DEFAULT_DIGEST_ALGO; } if( !next_pw && is_status_enabled() ) { char buf[50]; if( keyid ) { sprintf( buf, "%08lX%08lX", (ulong)keyid[0], (ulong)keyid[1] ); if( keyid[2] && keyid[3] && keyid[0] != keyid[2] && keyid[1] != keyid[3] ) - sprintf( buf+strlen(buf), " %08lX%08lX", - (ulong)keyid[2], (ulong)keyid[3] ); + sprintf( buf+strlen(buf), " %08lX%08lX %d 0", + (ulong)keyid[2], (ulong)keyid[3], pubkey_algo ); write_status_text( STATUS_NEED_PASSPHRASE, buf ); } else { sprintf( buf, "%d %d %d", cipher_algo, s2k->mode, s2k->hash_algo ); write_status_text( STATUS_NEED_PASSPHRASE_SYM, buf ); } } if( keyid && !opt.batch && !next_pw ) { PKT_public_key *pk = m_alloc_clear( sizeof *pk ); size_t n; char *p; tty_printf(_("\nYou need a passphrase to unlock the secret key for\n" "user: \"") ); p = get_user_id( keyid, &n ); tty_print_string( p, n ); m_free(p); tty_printf("\"\n"); if( !get_pubkey( pk, keyid ) ) { const char *s = pubkey_algo_to_string( pk->pubkey_algo ); tty_printf( _("%u-bit %s key, ID %08lX, created %s"), nbits_from_pk( pk ), s?s:"?", (ulong)keyid[1], strtimestamp(pk->timestamp) ); if( keyid[2] && keyid[3] && keyid[0] != keyid[2] && keyid[1] != keyid[3] ) tty_printf( _(" (main key ID %08lX)"), (ulong)keyid[3] ); tty_printf("\n"); } tty_printf("\n"); free_public_key( pk ); } if( next_pw ) { pw = next_pw; next_pw = NULL; } else if( fd_passwd ) { pw = m_alloc_secure( strlen(fd_passwd)+1 ); strcpy( pw, fd_passwd ); } else if( opt.batch ) { write_status( STATUS_MISSING_PASSPHRASE ); log_fatal("Can't query password in batchmode\n"); } else { pw = cpr_get_hidden("passphrase.enter", _("Enter passphrase: ") ); tty_kill_prompt(); if( mode == 2 && !cpr_enabled() ) { char *pw2 = cpr_get_hidden("passphrase.repeat", _("Repeat passphrase: ") ); tty_kill_prompt(); if( strcmp(pw, pw2) ) { m_free(pw2); m_free(pw); return NULL; } m_free(pw2); } } if( !pw || !*pw ) write_status( STATUS_MISSING_PASSPHRASE ); dek = m_alloc_secure( sizeof *dek ); dek->algo = cipher_algo; if( !*pw && mode == 2 ) dek->keylen = 0; else hash_passphrase( dek, pw, s2k, mode==2 ); m_free(last_pw); last_pw = pw; return dek; } /**************** * Hash a passphrase using the supplied s2k. If create is true, create * a new salt or what else must be filled into the s2k for a new key. * always needs: dek->algo, s2k->mode, s2k->hash_algo. */ static void hash_passphrase( DEK *dek, char *pw, STRING2KEY *s2k, int create ) { MD_HANDLE md; int pass, i; int used = 0; int pwlen = strlen(pw); assert( s2k->hash_algo ); dek->keylen = cipher_get_keylen( dek->algo ) / 8; if( !(dek->keylen > 0 && dek->keylen <= DIM(dek->key)) ) BUG(); md = md_open( s2k->hash_algo, 1); for(pass=0; used < dek->keylen ; pass++ ) { if( pass ) { md_reset(md); for(i=0; i < pass; i++ ) /* preset the hash context */ md_putc(md, 0 ); } if( s2k->mode == 1 || s2k->mode == 3 ) { int len2 = pwlen + 8; ulong count = len2; if( create && !pass ) { randomize_buffer(s2k->salt, 8, 1); if( s2k->mode == 3 ) s2k->count = 96; /* 65536 iterations */ } if( s2k->mode == 3 ) { count = (16ul + (s2k->count & 15)) << ((s2k->count >> 4) + 6); if( count < len2 ) count = len2; } /* a little bit complicated because we need a ulong for count */ while( count > len2 ) { /* maybe iterated+salted */ md_write( md, s2k->salt, 8 ); md_write( md, pw, pwlen ); count -= len2; } if( count < 8 ) md_write( md, s2k->salt, count ); else { md_write( md, s2k->salt, 8 ); count -= 8; assert( count >= 0 ); md_write( md, pw, count ); } } else md_write( md, pw, pwlen ); md_final( md ); i = md_digest_length( s2k->hash_algo ); if( i > dek->keylen - used ) i = dek->keylen - used; memcpy( dek->key+used, md_read(md, s2k->hash_algo), i ); used += i; } md_close(md); } diff --git a/g10/pkclist.c b/g10/pkclist.c index 39ed0e5f2..d8c25ebba 100644 --- a/g10/pkclist.c +++ b/g10/pkclist.c @@ -1,850 +1,860 @@ /* pkclist.c * Copyright (C) 1998 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 "options.h" #include "packet.h" #include "errors.h" #include "keydb.h" #include "memory.h" #include "util.h" #include "trustdb.h" #include "ttyio.h" #include "status.h" #include "i18n.h" #define CONTROL_D ('D' - 'A' + 1) static void show_paths( ulong lid, int only_first ) { void *context = NULL; unsigned otrust, validity; int last_level, level; last_level = 0; while( (level=enum_cert_paths( &context, &lid, &otrust, &validity)) != -1){ char *p; int c, rc; size_t n; u32 keyid[2]; PKT_public_key *pk ; if( level < last_level && only_first ) break; last_level = level; rc = keyid_from_lid( lid, keyid ); if( rc ) { log_error("ooops: can't get keyid for lid %lu\n", lid); return; } pk = m_alloc_clear( sizeof *pk ); rc = get_pubkey( pk, keyid ); if( rc ) { log_error("key %08lX: public key not found: %s\n", (ulong)keyid[1], g10_errstr(rc) ); return; } tty_printf("%*s%4u%c/%08lX.%lu %s \"", level*2, "", nbits_from_pk( pk ), pubkey_letter( pk->pubkey_algo ), (ulong)keyid[1], lid, datestr_from_pk( pk ) ); c = trust_letter(otrust); if( c ) putchar( c ); else printf( "%02x", otrust ); putchar('/'); c = trust_letter(validity); if( c ) putchar( c ); else printf( "%02x", validity ); putchar(' '); p = get_user_id( keyid, &n ); tty_print_string( p, n ), m_free(p); tty_printf("\"\n"); free_public_key( pk ); } enum_cert_paths( &context, NULL, NULL, NULL ); /* release context */ tty_printf("\n"); } /**************** * Returns true if an ownertrust has changed. */ static int do_edit_ownertrust( ulong lid, int mode, unsigned *new_trust ) { char *p; int rc; size_t n; u32 keyid[2]; PKT_public_key *pk ; int changed=0; int quit=0; int show=0; rc = keyid_from_lid( lid, keyid ); if( rc ) { log_error("ooops: can't get keyid for lid %lu\n", lid); return 0; } pk = m_alloc_clear( sizeof *pk ); rc = get_pubkey( pk, keyid ); if( rc ) { log_error("key %08lX: public key not found: %s\n", (ulong)keyid[1], g10_errstr(rc) ); return 0; } if( !mode ) { tty_printf(_("No trust value assigned to %lu:\n" "%4u%c/%08lX %s \""), lid, nbits_from_pk( pk ), pubkey_letter( pk->pubkey_algo ), (ulong)keyid[1], datestr_from_pk( pk ) ); p = get_user_id( keyid, &n ); tty_print_string( p, n ), m_free(p); tty_printf("\"\n\n"); } tty_printf(_( "Please decide how far you trust this user to correctly\n" "verify other users' keys (by looking at passports,\n" "checking fingerprints from different sources...)?\n\n" " 1 = Don't know\n" " 2 = I do NOT trust\n" " 3 = I trust marginally\n" " 4 = I trust fully\n" " s = please show me more information\n") ); if( mode ) tty_printf(_(" m = back to the main menu\n")); else tty_printf(_(" q = quit\n")); tty_printf("\n"); for(;;) { /* a string with valid answers */ char *ans = _("sSmMqQ"); if( strlen(ans) != 6 ) BUG(); p = cpr_get("edit_ownertrust.value",_("Your decision? ")); trim_spaces(p); cpr_kill_prompt(); if( *p && p[1] ) ; else if( !p[1] && (*p >= '1' && *p <= '4') ) { unsigned trust; switch( *p ) { case '1': trust = TRUST_UNDEFINED; break; case '2': trust = TRUST_NEVER ; break; case '3': trust = TRUST_MARGINAL ; break; case '4': trust = TRUST_FULLY ; break; default: BUG(); } *new_trust = trust; changed = 1; break; } else if( *p == ans[0] || *p == ans[1] ) { tty_printf(_( "Certificates leading to an ultimately trusted key:\n")); show = 1; break; } else if( mode && (*p == ans[2] || *p == ans[3] || *p == CONTROL_D ) ) { break ; /* back to the menu */ } else if( !mode && (*p == ans[4] || *p == ans[5] ) ) { quit = 1; break ; /* back to the menu */ } m_free(p); p = NULL; } m_free(p); m_free(pk); return show? -2: quit? -1 : changed; } int edit_ownertrust( ulong lid, int mode ) { - unsigned trust; + unsigned int trust; for(;;) { switch( do_edit_ownertrust( lid, mode, &trust ) ) { case -1: return 0; case -2: show_paths( lid, 1 ); break; case 1: + trust &= ~TRUST_FLAG_DISABLED; + trust |= get_ownertrust( lid ) & TRUST_FLAG_DISABLED; if( !update_ownertrust( lid, trust ) ) return 1; return 0; default: return 0; } } } static int add_ownertrust_cb( ulong lid ) { unsigned trust; int rc = do_edit_ownertrust( lid, 0, &trust ); if( rc == 1 ) return trust & TRUST_MASK; return rc > 0? 0 : rc; } /**************** * Try to add some more owner trusts (interactive) * This function presents all the signator in a certificate * chain who have no ownertrust value assigned. * Returns: -1 if no ownertrust were added. */ static int add_ownertrust( PKT_public_key *pk, int *quit, unsigned *trustlevel ) { int rc; unsigned flags = 0; *quit = 0; *trustlevel = 0; tty_printf( _("Could not find a valid trust path to the key. Let's see whether we\n" "can assign some missing owner trust values.\n\n")); rc = check_trust( pk, trustlevel, NULL, add_ownertrust_cb, &flags ); if( !(flags & 1) ) tty_printf(_("No path leading to one of our keys found.\n\n") ); else if( !(flags & 2) ) tty_printf(_("No certificates with undefined trust found.\n\n") ); else if( !(flags & 4) ) tty_printf(_("No trust values changed.\n\n") ); return (flags & 4)? 0:-1; } /**************** * Check whether we can trust this pk which has a trustlevel of TRUSTLEVEL * Returns: true if we trust. */ static int do_we_trust( PKT_public_key *pk, int trustlevel ) { int rc; int did_add = 0; retry: if( (trustlevel & TRUST_FLAG_REVOKED) ) { log_info(_("key %08lX: key has been revoked!\n"), (ulong)keyid_from_pk( pk, NULL) ); if( opt.batch ) return 0; if( !cpr_get_answer_is_yes("revoked_key.override", _("Use this key anyway? ")) ) return 0; } else if( (trustlevel & TRUST_FLAG_SUB_REVOKED) ) { log_info(_("key %08lX: subkey has been revoked!\n"), (ulong)keyid_from_pk( pk, NULL) ); if( opt.batch ) return 0; if( !cpr_get_answer_is_yes("revoked_key.override", _("Use this key anyway? ")) ) return 0; } switch( (trustlevel & TRUST_MASK) ) { case TRUST_UNKNOWN: /* No pubkey in trustDB: Insert and check again */ - rc = insert_trust_record( pk ); + rc = insert_trust_record_by_pk( pk ); if( rc ) { log_error("failed to insert it into the trustdb: %s\n", g10_errstr(rc) ); return 0; /* no */ } rc = check_trust( pk, &trustlevel, NULL, NULL, NULL ); if( rc ) log_fatal("trust check after insert failed: %s\n", g10_errstr(rc) ); if( trustlevel == TRUST_UNKNOWN || trustlevel == TRUST_EXPIRED ) { log_debug("do_we_trust: oops at %d\n", __LINE__ ); return 0; } return do_we_trust( pk, trustlevel ); case TRUST_EXPIRED: log_info(_("%08lX: key has expired\n"), (ulong)keyid_from_pk( pk, NULL) ); return 0; /* no */ case TRUST_UNDEFINED: if( opt.batch || opt.answer_no ) log_info(_("%08lX: no info to calculate a trust probability\n"), (ulong)keyid_from_pk( pk, NULL) ); else { int quit; rc = add_ownertrust( pk, &quit, &trustlevel ); if( !rc && !did_add && !quit ) { did_add = 1; goto retry; } } return 0; case TRUST_NEVER: log_info(_("%08lX: We do NOT trust this key\n"), (ulong)keyid_from_pk( pk, NULL) ); return 0; /* no */ case TRUST_MARGINAL: log_info( _("%08lX: It is not sure that this key really belongs to the owner\n" "but it is accepted anyway\n"), (ulong)keyid_from_pk( pk, NULL) ); return 1; /* yes */ case TRUST_FULLY: if( opt.verbose ) log_info(_("This key probably belongs to the owner\n")); return 1; /* yes */ case TRUST_ULTIMATE: if( opt.verbose ) log_info(_("This key belongs to us\n")); return 1; /* yes */ default: BUG(); } return 1; /* yes */ } /**************** * wrapper around do_we_trust, so we can ask whether to use the * key anyway. */ static int do_we_trust_pre( PKT_public_key *pk, int trustlevel ) { int rc; rc = do_we_trust( pk, trustlevel ); if( (trustlevel & TRUST_FLAG_REVOKED) && !rc ) return 0; if( (trustlevel & TRUST_FLAG_SUB_REVOKED) && !rc ) return 0; else if( !opt.batch && !rc ) { char *p; u32 keyid[2]; size_t n; keyid_from_pk( pk, keyid); tty_printf( "%4u%c/%08lX %s \"", nbits_from_pk( pk ), pubkey_letter( pk->pubkey_algo ), (ulong)keyid[1], datestr_from_pk( pk ) ); p = get_user_id( keyid, &n ); tty_print_string( p, n ), m_free(p); tty_printf("\"\n\n"); tty_printf(_( "It is NOT certain that the key belongs to its owner.\n" "If you *really* know what you are doing, you may answer\n" "the next question with yes\n\n") ); if( cpr_get_answer_is_yes("untrusted_key.override", _("Use this key anyway? ")) ) rc = 1; /* Hmmm: Should we set a flag to tell the user the user about * his decision the next time he encrypts for this recipient? */ } else if( opt.always_trust && !rc ) { log_info(_("WARNING: Using untrusted key!\n")); rc = 1; } return rc; } /**************** * Check whether we can trust this signature. * Returns: Error if we shall not trust this signatures. */ int check_signatures_trust( PKT_signature *sig ) { PKT_public_key *pk = m_alloc_clear( sizeof *pk ); int trustlevel; int did_add = 0; int rc=0; if( opt.always_trust ) { log_info(_("WARNING: Using untrusted key!\n")); return 0; } rc = get_pubkey( pk, sig->keyid ); if( rc ) { /* this should not happen */ log_error("Ooops; the key vanished - can't check the trust\n"); rc = G10ERR_NO_PUBKEY; goto leave; } rc = check_trust( pk, &trustlevel, NULL, NULL, NULL ); if( rc ) { log_error("check trust failed: %s\n", g10_errstr(rc)); goto leave; } retry: if( (trustlevel & TRUST_FLAG_REVOKED) ) { write_status( STATUS_KEYREVOKED ); log_info(_("WARNING: This key has been revoked by its owner!\n")); log_info(_(" This could mean that the signature is forgery.\n")); } else if( (trustlevel & TRUST_FLAG_SUB_REVOKED) ) { write_status( STATUS_KEYREVOKED ); log_info(_("WARNING: This subkey has been revoked by its owner!\n")); } switch( (trustlevel & TRUST_MASK) ) { case TRUST_UNKNOWN: /* No pubkey in trustDB: Insert and check again */ - rc = insert_trust_record( pk ); + rc = insert_trust_record_by_pk( pk ); if( rc ) { log_error("failed to insert it into the trustdb: %s\n", g10_errstr(rc) ); goto leave; } rc = check_trust( pk, &trustlevel, NULL, NULL, NULL ); if( rc ) log_fatal("trust check after insert failed: %s\n", g10_errstr(rc) ); if( trustlevel == TRUST_UNKNOWN || trustlevel == TRUST_EXPIRED ) BUG(); goto retry; case TRUST_EXPIRED: log_info(_("Note: This key has expired!\n")); break; case TRUST_UNDEFINED: if( did_add || opt.batch || opt.answer_no ) { write_status( STATUS_TRUST_UNDEFINED ); log_info(_( "WARNING: This key is not certified with a trusted signature!\n")); log_info(_( " There is no indication that the " "signature belongs to the owner.\n" )); } else { int quit; rc = add_ownertrust( pk, &quit, &trustlevel ); if( rc || quit ) { did_add = 1; rc = 0; } goto retry; } break; case TRUST_NEVER: write_status( STATUS_TRUST_NEVER ); log_info(_("WARNING: We do NOT trust this key!\n")); log_info(_(" The signature is probably a FORGERY.\n")); rc = G10ERR_BAD_SIGN; break; case TRUST_MARGINAL: write_status( STATUS_TRUST_MARGINAL ); log_info(_( "WARNING: This key is not certified with sufficiently trusted signatures!\n" )); log_info(_( " It is not certain that the signature belongs to the owner.\n" )); break; case TRUST_FULLY: write_status( STATUS_TRUST_FULLY ); break; case TRUST_ULTIMATE: write_status( STATUS_TRUST_ULTIMATE ); break; default: BUG(); } leave: free_public_key( pk ); return rc; } void release_pk_list( PK_LIST pk_list ) { PK_LIST pk_rover; for( ; pk_list; pk_list = pk_rover ) { pk_rover = pk_list->next; free_public_key( pk_list->pk ); m_free( pk_list ); } } static int key_present_in_pk_list(PK_LIST pk_list, PKT_public_key *pk) { for( ; pk_list; pk_list = pk_list->next) if (cmp_public_keys(pk_list->pk, pk) == 0) return 0; return -1; } int build_pk_list( STRLIST remusr, PK_LIST *ret_pk_list, unsigned use ) { PK_LIST pk_list = NULL; PKT_public_key *pk=NULL; int rc=0; int any_recipients=0; STRLIST rov; /* check whether there are any recipients in the list and build the * list of the encrypt-to ones (we always trust them) */ for( rov = remusr; rov; rov = rov->next ) { if( !(rov->flags & 1) ) any_recipients = 1; else if( (use & PUBKEY_USAGE_ENC) && !opt.no_encrypt_to ) { pk = m_alloc_clear( sizeof *pk ); pk->pubkey_usage = use; if( (rc = get_pubkey_byname( NULL, pk, rov->d, NULL )) ) { free_public_key( pk ); pk = NULL; log_error(_("%s: skipped: %s\n"), rov->d, g10_errstr(rc) ); } else if( !(rc=check_pubkey_algo2(pk->pubkey_algo, use )) ) { /* Skip the actual key if the key is already present * in the list */ if (key_present_in_pk_list(pk_list, pk) == 0) { free_public_key(pk); pk = NULL; log_info(_("%s: skipped: public key already present\n"), rov->d); } else { PK_LIST r; r = m_alloc( sizeof *r ); r->pk = pk; pk = NULL; r->next = pk_list; r->mark = 0; pk_list = r; } } else { free_public_key( pk ); pk = NULL; log_error(_("%s: skipped: %s\n"), rov->d, g10_errstr(rc) ); } } } if( !any_recipients && !opt.batch ) { /* ask */ char *answer=NULL; tty_printf(_( "You did not specify a user ID. (you may use \"-r\")\n\n")); for(;;) { rc = 0; m_free(answer); answer = cpr_get_utf8("pklist.user_id.enter", _("Enter the user ID: ")); trim_spaces(answer); cpr_kill_prompt(); if( !*answer ) break; if( pk ) free_public_key( pk ); pk = m_alloc_clear( sizeof *pk ); pk->pubkey_usage = use; rc = get_pubkey_byname( NULL, pk, answer, NULL ); if( rc ) tty_printf(_("No such user ID.\n")); else if( !(rc=check_pubkey_algo2(pk->pubkey_algo, use)) ) { int trustlevel; rc = check_trust( pk, &trustlevel, NULL, NULL, NULL ); if( rc ) { log_error("error checking pk of `%s': %s\n", answer, g10_errstr(rc) ); } + else if( (trustlevel & TRUST_FLAG_DISABLED) ) { + tty_printf(_("Public key is disabled.\n") ); + } else if( do_we_trust_pre( pk, trustlevel ) ) { PK_LIST r; r = m_alloc( sizeof *r ); r->pk = pk; pk = NULL; r->next = pk_list; r->mark = 0; pk_list = r; any_recipients = 1; break; } } } m_free(answer); if( pk ) { free_public_key( pk ); pk = NULL; } } else { any_recipients = 0; for(; remusr; remusr = remusr->next ) { if( (remusr->flags & 1) ) continue; /* encrypt-to keys are already handled */ pk = m_alloc_clear( sizeof *pk ); pk->pubkey_usage = use; if( (rc = get_pubkey_byname( NULL, pk, remusr->d, NULL )) ) { free_public_key( pk ); pk = NULL; log_error(_("%s: skipped: %s\n"), remusr->d, g10_errstr(rc) ); } else if( !(rc=check_pubkey_algo2(pk->pubkey_algo, use )) ) { int trustlevel; rc = check_trust( pk, &trustlevel, NULL, NULL, NULL ); if( rc ) { free_public_key( pk ); pk = NULL; log_error(_("%s: error checking key: %s\n"), remusr->d, g10_errstr(rc) ); } + else if( (trustlevel & TRUST_FLAG_DISABLED) ) { + free_public_key(pk); pk = NULL; + log_info(_("%s: skipped: public key is disabled\n"), + remusr->d); + } else if( do_we_trust_pre( pk, trustlevel ) ) { /* note: do_we_trust may have changed the trustlevel */ /* We have at least one valid recipient. It doesn't matters * if this recipient is already present. */ any_recipients = 1; /* Skip the actual key if the key is already present * in the list */ if (key_present_in_pk_list(pk_list, pk) == 0) { free_public_key(pk); pk = NULL; log_info(_("%s: skipped: public key already present\n"), remusr->d); } else { PK_LIST r; r = m_alloc( sizeof *r ); r->pk = pk; pk = NULL; r->next = pk_list; r->mark = 0; pk_list = r; } } else { /* we don't trust this pk */ free_public_key( pk ); pk = NULL; } } else { free_public_key( pk ); pk = NULL; log_error(_("%s: skipped: %s\n"), remusr->d, g10_errstr(rc) ); } } } if( !rc && !any_recipients ) { log_error(_("no valid addressees\n")); rc = G10ERR_NO_USER_ID; } if( rc ) release_pk_list( pk_list ); else *ret_pk_list = pk_list; return rc; } static int algo_available( int preftype, int algo ) { if( preftype == PREFTYPE_SYM ) { if( algo == CIPHER_ALGO_TWOFISH ) return 0; /* we don't want to generate Twofish messages for now*/ return algo && !check_cipher_algo( algo ); } else if( preftype == PREFTYPE_HASH ) { return algo && !check_digest_algo( algo ); } else if( preftype == PREFTYPE_COMPR ) { return !algo || algo == 1 || algo == 2; } else return 0; } /**************** * Return -1 if we could not find an algorithm. */ int select_algo_from_prefs( PK_LIST pk_list, int preftype ) { PK_LIST pkr; u32 bits[8]; byte *pref = NULL; size_t npref; int i, j; int compr_hack=0; int any; if( !pk_list ) return -1; memset( bits, ~0, 8 * sizeof *bits ); for( pkr = pk_list; pkr; pkr = pkr->next ) { u32 mask[8]; memset( mask, 0, 8 * sizeof *mask ); if( !pkr->pk->local_id ) { /* try to set the local id */ query_trust_info( pkr->pk, NULL ); if( !pkr->pk->local_id ) { log_debug("select_algo_from_prefs: can't get LID\n"); continue; } } if( preftype == PREFTYPE_SYM ) mask[0] |= (1<<2); /* 3DES is implicitly there */ m_free(pref); pref = get_pref_data( pkr->pk->local_id, pkr->pk->namehash, &npref); any = 0; if( pref ) { #if 0 log_hexdump("raw: ", pref, npref ); #endif for(i=0; i+1 < npref; i+=2 ) { if( pref[i] == preftype ) { mask[pref[i+1]/32] |= 1 << (pref[i+1]%32); any = 1; } } } if( (!pref || !any) && preftype == PREFTYPE_COMPR ) { mask[0] |= 3; /* asume no_compression and old pgp */ compr_hack = 1; } #if 0 log_debug("mask=%08lX%08lX%08lX%08lX%08lX%08lX%08lX%08lX\n", (ulong)mask[7], (ulong)mask[6], (ulong)mask[5], (ulong)mask[4], (ulong)mask[3], (ulong)mask[2], (ulong)mask[1], (ulong)mask[0]); #endif for(i=0; i < 8; i++ ) bits[i] &= mask[i]; #if 0 log_debug("bits=%08lX%08lX%08lX%08lX%08lX%08lX%08lX%08lX\n", (ulong)bits[7], (ulong)bits[6], (ulong)bits[5], (ulong)bits[4], (ulong)bits[3], (ulong)bits[2], (ulong)bits[1], (ulong)bits[0]); #endif } /* usable algorithms are now in bits * We now use the last key from pk_list to select * the algorithm we want to use. there are no * preferences for the last key, we select the one * corresponding to first set bit. */ i = -1; any = 0; if( pref ) { for(j=0; j+1 < npref; j+=2 ) { if( pref[j] == preftype ) { if( (bits[pref[j+1]/32] & (1<<(pref[j+1]%32))) ) { if( algo_available( preftype, pref[j+1] ) ) { any = 1; i = pref[j+1]; break; } } } } } if( !pref || !any ) { for(j=0; j < 256; j++ ) if( (bits[j/32] & (1<<(j%32))) ) { if( algo_available( preftype, j ) ) { i = j; break; } } } #if 0 log_debug("prefs of type %d: selected %d\n", preftype, i ); #endif if( compr_hack && !i ) { /* selected no compression, but we should check whether * algorithm 1 is also available (the ordering is not relevant * in this case). */ if( bits[0] & (1<<1) ) i = 1; /* yep; we can use compression algo 1 */ } m_free(pref); return i; } diff --git a/g10/plaintext.c b/g10/plaintext.c index 1edca16e5..878bdc46b 100644 --- a/g10/plaintext.c +++ b/g10/plaintext.c @@ -1,375 +1,375 @@ /* plaintext.c - process an plaintext packet * Copyright (C) 1998, 1999 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 "util.h" #include "memory.h" #include "options.h" #include "packet.h" #include "ttyio.h" #include "filter.h" #include "main.h" #include "status.h" #include "i18n.h" /**************** * Handle a plaintext packet. If MFX is not NULL, update the MDs * Note: we should use the filter stuff here, but we have to add some * easy mimic to set a read limit, so we calculate only the * bytes from the plaintext. */ int handle_plaintext( PKT_plaintext *pt, md_filter_context_t *mfx, int nooutput, int clearsig ) { char *fname = NULL; FILE *fp = NULL; int rc = 0; int c; int convert = pt->mode == 't'; /* create the filename as C string */ if( nooutput ) ; else if( opt.outfile ) { fname = m_alloc( strlen( opt.outfile ) + 1); strcpy(fname, opt.outfile ); } else if( pt->namelen == 8 && !memcmp( pt->name, "_CONSOLE", 8 ) ) { log_info(_("data not saved; use option \"--output\" to save it\n")); nooutput = 1; } else if( !opt.use_embedded_filename ) { fname = make_outfile_name( iobuf_get_real_fname(pt->buf) ); + if( !fname ) + fname = ask_outfile_name( pt->name, pt->namelen ); if( !fname ) { rc = G10ERR_CREATE_FILE; goto leave; } } else { - fname = m_alloc( pt->namelen +1 ); - memcpy( fname, pt->name, pt->namelen ); - fname[pt->namelen] = 0; + fname = make_printable_string( pt->name, pt->namelen, 0 ); } if( nooutput ) ; else if( !*fname || (*fname=='-' && !fname[1])) { /* no filename or "-" given; write to stdout */ fp = stdout; } else if( !overwrite_filep( fname ) ) { rc = G10ERR_CREATE_FILE; goto leave; } if( fp || nooutput ) ; else if( !(fp = fopen(fname,"wb")) ) { log_error("Error creating `%s': %s\n", fname, strerror(errno) ); rc = G10ERR_CREATE_FILE; goto leave; } if( pt->len ) { assert( !clearsig ); if( convert ) { /* text mode */ for( ; pt->len; pt->len-- ) { if( (c = iobuf_get(pt->buf)) == -1 ) { log_error("Problem reading source (%u bytes remaining)\n", (unsigned)pt->len); rc = G10ERR_READ_FILE; goto leave; } if( mfx->md ) md_putc(mfx->md, c ); if( c == '\r' ) continue; /* fixme: this hack might be too simple */ if( fp ) { if( putc( c, fp ) == EOF ) { log_error("Error writing to `%s': %s\n", fname, strerror(errno) ); rc = G10ERR_WRITE_FILE; goto leave; } } } } else { /* binary mode */ byte *buffer = m_alloc( 32768 ); while( pt->len ) { int len = pt->len > 32768 ? 32768 : pt->len; len = iobuf_read( pt->buf, buffer, len ); if( len == -1 ) { log_error("Problem reading source (%u bytes remaining)\n", (unsigned)pt->len); rc = G10ERR_READ_FILE; m_free( buffer ); goto leave; } if( mfx->md ) md_write( mfx->md, buffer, len ); if( fp ) { if( fwrite( buffer, 1, len, fp ) != len ) { log_error("Error writing to `%s': %s\n", fname, strerror(errno) ); rc = G10ERR_WRITE_FILE; m_free( buffer ); goto leave; } } pt->len -= len; } m_free( buffer ); } } else if( !clearsig ) { if( convert ) { /* text mode */ while( (c = iobuf_get(pt->buf)) != -1 ) { if( mfx->md ) md_putc(mfx->md, c ); if( convert && c == '\r' ) continue; /* fixme: this hack might be too simple */ if( fp ) { if( putc( c, fp ) == EOF ) { log_error("Error writing to `%s': %s\n", fname, strerror(errno) ); rc = G10ERR_WRITE_FILE; goto leave; } } } } else { /* binary mode */ byte *buffer = m_alloc( 32768 ); for( ;; ) { int len = iobuf_read( pt->buf, buffer, 32768 ); if( len == -1 ) break; if( mfx->md ) md_write( mfx->md, buffer, len ); if( fp ) { if( fwrite( buffer, 1, len, fp ) != len ) { log_error("Error writing to `%s': %s\n", fname, strerror(errno) ); rc = G10ERR_WRITE_FILE; m_free( buffer ); goto leave; } } } m_free( buffer ); } pt->buf = NULL; } else { /* clear text signature - don't hash the last cr,lf */ int state = 0; while( (c = iobuf_get(pt->buf)) != -1 ) { if( fp ) { if( putc( c, fp ) == EOF ) { log_error("Error writing to `%s': %s\n", fname, strerror(errno) ); rc = G10ERR_WRITE_FILE; goto leave; } } if( !mfx->md ) continue; if( state == 2 ) { md_putc(mfx->md, '\r' ); md_putc(mfx->md, '\n' ); state = 0; } if( !state ) { if( c == '\r' ) state = 1; else md_putc(mfx->md, c ); } else if( state == 1 ) { if( c == '\n' ) state = 2; else { md_putc(mfx->md, '\r' ); if( c == '\r' ) state = 1; else { state = 0; md_putc(mfx->md, c ); } } } } pt->buf = NULL; } if( fp && fp != stdout && fclose(fp) ) { log_error("Error closing `%s': %s\n", fname, strerror(errno) ); fp = NULL; rc = G10ERR_WRITE_FILE; goto leave; } fp = NULL; leave: if( fp && fp != stdout ) fclose(fp); m_free(fname); return rc; } /**************** * Ask for the detached datafile and calculate the digest from it. * INFILE is the name of the input file. */ int ask_for_detached_datafile( md_filter_context_t *mfx, const char *inname ) { char *answer = NULL; IOBUF fp; int rc = 0; int c; fp = open_sigfile( inname ); /* open default file */ if( !fp && !opt.batch ) { int any=0; tty_printf("Detached signature.\n"); do { m_free(answer); answer = cpr_get("detached_signature.filename", _("Please enter name of data file: ")); cpr_kill_prompt(); if( any && !*answer ) { rc = G10ERR_READ_FILE; goto leave; } fp = iobuf_open(answer); if( !fp && errno == ENOENT ) { tty_printf("No such file, try again or hit enter to quit.\n"); any++; } else if( !fp ) { log_error("can't open `%s': %s\n", answer, strerror(errno) ); rc = G10ERR_READ_FILE; goto leave; } } while( !fp ); } if( !fp ) { if( opt.verbose ) log_info(_("reading stdin ...\n")); while( (c = getchar()) != EOF ) { if( mfx->md ) md_putc(mfx->md, c ); } } else { while( (c = iobuf_get(fp)) != -1 ) { if( mfx->md ) md_putc(mfx->md, c ); } iobuf_close(fp); } leave: m_free(answer); return rc; } static void do_hash( MD_HANDLE md, MD_HANDLE md2, IOBUF fp, int textmode ) { text_filter_context_t tfx; int c; if( textmode ) { memset( &tfx, 0, sizeof tfx); iobuf_push_filter( fp, text_filter, &tfx ); } if( md2 ) { /* work around a strange behaviour in pgp2 */ while( (c = iobuf_get(fp)) != -1 ) { if( c == '\n' ) md_putc(md2, '\r' ); md_putc(md, c ); md_putc(md2, c ); } } else { while( (c = iobuf_get(fp)) != -1 ) md_putc(md, c ); } } /**************** * Hash the given files and append the hash to hash context md. * If FILES is NULL, hash stdin. */ int hash_datafiles( MD_HANDLE md, MD_HANDLE md2, STRLIST files, const char *sigfilename, int textmode ) { IOBUF fp; STRLIST sl=NULL; if( !files ) { /* check whether we can open the signed material */ fp = open_sigfile( sigfilename ); if( fp ) { do_hash( md, md2, fp, textmode ); iobuf_close(fp); return 0; } /* no we can't (no sigfile) - read signed stuff from stdin */ add_to_strlist( &sl, "-"); } else sl = files; for( ; sl; sl = sl->next ) { fp = iobuf_open( sl->d ); if( !fp ) { log_error(_("can't open signed data `%s'\n"), print_fname_stdin(sl->d)); if( !files ) free_strlist(sl); return G10ERR_OPEN_FILE; } do_hash( md, md2, fp, textmode ); iobuf_close(fp); } if( !files ) free_strlist(sl); return 0; } diff --git a/g10/seckey-cert.c b/g10/seckey-cert.c index 03cf3f2bf..95af40b78 100644 --- a/g10/seckey-cert.c +++ b/g10/seckey-cert.c @@ -1,302 +1,302 @@ /* seckey-cert.c - secret key certificate packet handling * Copyright (C) 1998, 1999 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 "util.h" #include "memory.h" #include "packet.h" #include "mpi.h" #include "keydb.h" #include "cipher.h" #include "main.h" #include "options.h" #include "i18n.h" #include "status.h" static int do_check( PKT_secret_key *sk ) { byte *buffer; u16 csum=0; int i, res; unsigned nbytes; if( sk->is_protected ) { /* remove the protection */ DEK *dek = NULL; u32 keyid[4]; /* 4! because we need two of them */ CIPHER_HANDLE cipher_hd=NULL; PKT_secret_key *save_sk; if( sk->protect.algo == CIPHER_ALGO_NONE ) BUG(); if( check_cipher_algo( sk->protect.algo ) ) { log_info(_("protection algorithm %d is not supported\n"), sk->protect.algo ); return G10ERR_CIPHER_ALGO; } keyid_from_sk( sk, keyid ); keyid[2] = keyid[3] = 0; if( !sk->is_primary ) { PKT_secret_key *sk2 = m_alloc_clear( sizeof *sk2 ); if( !get_primary_seckey( sk2, keyid ) ) keyid_from_sk( sk2, keyid+2 ); free_secret_key( sk2 ); } - dek = passphrase_to_dek( keyid, sk->protect.algo, + dek = passphrase_to_dek( keyid, sk->pubkey_algo, sk->protect.algo, &sk->protect.s2k, 0 ); cipher_hd = cipher_open( sk->protect.algo, CIPHER_MODE_AUTO_CFB, 1); cipher_setkey( cipher_hd, dek->key, dek->keylen ); m_free(dek); save_sk = copy_secret_key( NULL, sk ); cipher_setiv( cipher_hd, sk->protect.iv, sk->protect.ivlen ); csum = 0; if( sk->version >= 4 ) { int ndata; byte *p, *data; i = pubkey_get_npkey(sk->pubkey_algo); assert( mpi_is_opaque( sk->skey[i] ) ); p = mpi_get_opaque( sk->skey[i], &ndata ); data = m_alloc_secure( ndata ); cipher_decrypt( cipher_hd, data, p, ndata ); mpi_free( sk->skey[i] ); sk->skey[i] = NULL ; p = data; if( ndata < 2 ) { log_error("not enough bytes for checksum\n"); sk->csum = 0; csum = 1; } else { csum = checksum( data, ndata-2); sk->csum = data[ndata-2] << 8 | data[ndata-1]; } /* must check it here otherwise the mpi_read_xx would fail * because the length das an abritary value */ if( sk->csum == csum ) { for( ; i < pubkey_get_nskey(sk->pubkey_algo); i++ ) { nbytes = ndata; sk->skey[i] = mpi_read_from_buffer(p, &nbytes, 1 ); ndata -= nbytes; p += nbytes; } } m_free(data); } else { for(i=pubkey_get_npkey(sk->pubkey_algo); i < pubkey_get_nskey(sk->pubkey_algo); i++ ) { buffer = mpi_get_secure_buffer( sk->skey[i], &nbytes, NULL ); cipher_sync( cipher_hd ); assert( mpi_is_protected(sk->skey[i]) ); cipher_decrypt( cipher_hd, buffer, buffer, nbytes ); mpi_set_buffer( sk->skey[i], buffer, nbytes, 0 ); mpi_clear_protect_flag( sk->skey[i] ); csum += checksum_mpi( sk->skey[i] ); m_free( buffer ); } if( opt.emulate_bugs & EMUBUG_GPGCHKSUM ) { csum = sk->csum; } } cipher_close( cipher_hd ); /* now let's see whether we have used the right passphrase */ if( csum != sk->csum ) { copy_secret_key( sk, save_sk ); free_secret_key( save_sk ); return G10ERR_BAD_PASS; } /* the checksum may fail, so we also check the key itself */ res = pubkey_check_secret_key( sk->pubkey_algo, sk->skey ); if( res ) { copy_secret_key( sk, save_sk ); free_secret_key( save_sk ); return G10ERR_BAD_PASS; } free_secret_key( save_sk ); sk->is_protected = 0; } else { /* not protected, assume it is okay if the checksum is okay */ csum = 0; for(i=pubkey_get_npkey(sk->pubkey_algo); i < pubkey_get_nskey(sk->pubkey_algo); i++ ) { csum += checksum_mpi( sk->skey[i] ); } if( csum != sk->csum ) return G10ERR_CHECKSUM; } return 0; } /**************** * Check the secret key * Ask up to 3 (or n) times for a correct passphrase */ int check_secret_key( PKT_secret_key *sk, int n ) { int rc = G10ERR_BAD_PASS; int i; if( n < 1 ) n = 3; /* use the default value */ for(i=0; i < n && rc == G10ERR_BAD_PASS; i++ ) { if( i ) log_info(_("Invalid passphrase; please try again ...\n")); rc = do_check( sk ); if( rc == G10ERR_BAD_PASS && is_status_enabled() ) { u32 kid[2]; char buf[50]; keyid_from_sk( sk, kid ); sprintf(buf, "%08lX%08lX", (ulong)kid[0], (ulong)kid[1]); write_status_text( STATUS_BAD_PASSPHRASE, buf ); } if( have_static_passphrase() ) break; } if( !rc ) write_status( STATUS_GOOD_PASSPHRASE ); return rc; } /**************** * check whether the secret key is protected. * Returns: 0 not protected, -1 on error or the protection algorithm */ int is_secret_key_protected( PKT_secret_key *sk ) { return sk->is_protected? sk->protect.algo : 0; } /**************** * Protect the secret key with the passphrase from DEK */ int protect_secret_key( PKT_secret_key *sk, DEK *dek ) { int i,j, rc = 0; byte *buffer; unsigned nbytes; u16 csum; if( !dek ) return 0; if( !sk->is_protected ) { /* okay, apply the protection */ CIPHER_HANDLE cipher_hd=NULL; if( check_cipher_algo( sk->protect.algo ) ) rc = G10ERR_CIPHER_ALGO; /* unsupport protection algorithm */ else { print_cipher_algo_note( sk->protect.algo ); cipher_hd = cipher_open( sk->protect.algo, CIPHER_MODE_AUTO_CFB, 1 ); if( cipher_setkey( cipher_hd, dek->key, dek->keylen ) ) log_info(_("WARNING: Weak key detected" " - please change passphrase again.\n")); sk->protect.ivlen = cipher_get_blocksize( sk->protect.algo ); assert( sk->protect.ivlen <= DIM(sk->protect.iv) ); if( sk->protect.ivlen != 8 && sk->protect.ivlen != 16 ) BUG(); /* yes, we are very careful */ randomize_buffer(sk->protect.iv, sk->protect.ivlen, 1); cipher_setiv( cipher_hd, sk->protect.iv, sk->protect.ivlen ); if( sk->version >= 4 ) { #define NMPIS (PUBKEY_MAX_NSKEY - PUBKEY_MAX_NPKEY) byte *bufarr[NMPIS]; unsigned narr[NMPIS]; unsigned nbits[NMPIS]; int ndata=0; byte *p, *data; for(j=0, i = pubkey_get_npkey(sk->pubkey_algo); i < pubkey_get_nskey(sk->pubkey_algo); i++, j++ ) { assert( !mpi_is_opaque( sk->skey[i] ) ); bufarr[j] = mpi_get_buffer( sk->skey[i], &narr[j], NULL ); nbits[j] = mpi_get_nbits( sk->skey[i] ); ndata += narr[j] + 2; } for( ; j < NMPIS; j++ ) bufarr[j] = NULL; ndata += 2; /* for checksum */ data = m_alloc_secure( ndata ); p = data; for(j=0; j < NMPIS && bufarr[j]; j++ ) { p[0] = nbits[j] >> 8 ; p[1] = nbits[j]; p += 2; memcpy(p, bufarr[j], narr[j] ); p += narr[j]; m_free(bufarr[j]); } #undef NMPIS csum = checksum( data, ndata-2); sk->csum = csum; *p++ = csum >> 8; *p++ = csum; assert( p == data+ndata ); cipher_encrypt( cipher_hd, data, data, ndata ); for(i = pubkey_get_npkey(sk->pubkey_algo); i < pubkey_get_nskey(sk->pubkey_algo); i++ ) { mpi_free( sk->skey[i] ); sk->skey[i] = NULL; } i = pubkey_get_npkey(sk->pubkey_algo); sk->skey[i] = mpi_set_opaque(NULL, data, ndata ); } else { /* NOTE: we always recalculate the checksum because there * are some test releases which calculated it wrong */ csum = 0; for(i=pubkey_get_npkey(sk->pubkey_algo); i < pubkey_get_nskey(sk->pubkey_algo); i++ ) { csum += checksum_mpi_counted_nbits( sk->skey[i] ); buffer = mpi_get_buffer( sk->skey[i], &nbytes, NULL ); cipher_sync( cipher_hd ); assert( !mpi_is_protected(sk->skey[i]) ); cipher_encrypt( cipher_hd, buffer, buffer, nbytes ); mpi_set_buffer( sk->skey[i], buffer, nbytes, 0 ); mpi_set_protect_flag( sk->skey[i] ); m_free( buffer ); } sk->csum = csum; } sk->is_protected = 1; cipher_close( cipher_hd ); } } return rc; } diff --git a/g10/sig-check.c b/g10/sig-check.c index 48734370c..e57ae8019 100644 --- a/g10/sig-check.c +++ b/g10/sig-check.c @@ -1,489 +1,494 @@ /* sig-check.c - Check a signature * Copyright (C) 1998 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 "util.h" #include "packet.h" #include "memory.h" #include "mpi.h" #include "keydb.h" #include "cipher.h" #include "main.h" #include "status.h" #include "i18n.h" struct cmp_help_context_s { PKT_signature *sig; MD_HANDLE md; }; static int do_check( PKT_public_key *pk, PKT_signature *sig, MD_HANDLE digest ); /**************** * Check the signature which is contained in SIG. * The MD_HANDLE should be currently open, so that this function * is able to append some data, before finalizing the digest. */ int signature_check( PKT_signature *sig, MD_HANDLE digest ) { PKT_public_key *pk = m_alloc_clear( sizeof *pk ); int rc=0; if( is_RSA(sig->pubkey_algo) ) write_status(STATUS_RSA_OR_IDEA); if( get_pubkey( pk, sig->keyid ) ) rc = G10ERR_NO_PUBKEY; else rc = do_check( pk, sig, digest ); free_public_key( pk ); if( !rc && sig->sig_class < 2 && is_status_enabled() ) { /* This signature id works best with DLP algorithms because * they use a random parameter for every signature. Instead of * this sig-id we could have also used the hash of the document * and the timestamp, but the drawback of this is, that it is * not possible to sign more than one identical document within * one second. Some remote bacth processing applications might * like this feature here */ MD_HANDLE md; u32 a = sig->timestamp; int i, nsig = pubkey_get_nsig( sig->pubkey_algo ); byte *p, *buffer; md = md_open( DIGEST_ALGO_RMD160, 0); md_putc( digest, sig->pubkey_algo ); md_putc( digest, sig->digest_algo ); md_putc( digest, (a >> 24) & 0xff ); md_putc( digest, (a >> 16) & 0xff ); md_putc( digest, (a >> 8) & 0xff ); md_putc( digest, a & 0xff ); for(i=0; i < nsig; i++ ) { unsigned n = mpi_get_nbits( sig->data[i]); md_putc( md, n>>8); md_putc( md, n ); p = mpi_get_buffer( sig->data[i], &n, NULL ); md_write( md, p, n ); m_free(p); } md_final( md ); p = make_radix64_string( md_read( md, 0 ), 20 ); buffer = m_alloc( strlen(p) + 60 ); sprintf( buffer, "%s %s %lu", p, strtimestamp( sig->timestamp ), (ulong)sig->timestamp ); write_status_text( STATUS_SIG_ID, buffer ); m_free(buffer); m_free(p); md_close(md); } return rc; } #if 0 /* not anymore used */ /**************** * Check the MDC which is contained in SIG. * The MD_HANDLE should be currently open, so that this function * is able to append some data, before finalizing the digest. */ int mdc_kludge_check( PKT_signature *sig, MD_HANDLE digest ) { int rc=0; if( (rc=check_digest_algo(sig->digest_algo)) ) return rc; /* make sure the digest algo is enabled (in case of a detached mdc??) */ md_enable( digest, sig->digest_algo ); /* complete the digest */ if( sig->version >= 4 ) md_putc( digest, sig->version ); md_putc( digest, sig->sig_class ); if( sig->version < 4 ) { u32 a = sig->timestamp; md_putc( digest, (a >> 24) & 0xff ); md_putc( digest, (a >> 16) & 0xff ); md_putc( digest, (a >> 8) & 0xff ); md_putc( digest, a & 0xff ); } else { byte buf[6]; size_t n; md_putc( digest, sig->pubkey_algo ); md_putc( digest, sig->digest_algo ); if( sig->hashed_data ) { n = (sig->hashed_data[0] << 8) | sig->hashed_data[1]; md_write( digest, sig->hashed_data, n+2 ); n += 6; } else n = 6; /* add some magic */ buf[0] = sig->version; buf[1] = 0xff; buf[2] = n >> 24; buf[3] = n >> 16; buf[4] = n >> 8; buf[5] = n; md_write( digest, buf, 6 ); } md_final( digest ); rc = G10ERR_BAD_SIGN; { const byte *s1 = md_read( digest, sig->digest_algo ); int s1len = md_digest_length( sig->digest_algo ); log_hexdump( "MDC calculated", s1, s1len ); if( !sig->data[0] ) log_debug("sig_data[0] is NULL\n"); else { unsigned s2len; byte *s2; s2 = mpi_get_buffer( sig->data[0], &s2len, NULL ); log_hexdump( "MDC stored ", s2, s2len ); if( s2len != s1len ) log_debug("MDC check: len differ: %d/%d\n", s1len, s2len); else if( memcmp( s1, s2, s1len ) ) log_debug("MDC check: hashs differ\n"); else rc = 0; m_free(s2); } } if( !rc && sig->flags.unknown_critical ) { log_info(_("assuming bad MDC due to an unknown critical bit\n")); rc = G10ERR_BAD_SIGN; } sig->flags.checked = 1; sig->flags.valid = !rc; /* FIXME: check that we are actually in an encrypted packet */ return rc; } #endif /**************** * This function gets called by pubkey_verify() if the algorithm needs it. */ static int cmp_help( void *opaque, MPI result ) { #if 0 /* we do not use this anymore */ int rc=0, i, j, c, old_enc; byte *dp; const byte *asn; size_t mdlen, asnlen; struct cmp_help_context_s *ctx = opaque; PKT_signature *sig = ctx->sig; MD_HANDLE digest = ctx->md; old_enc = 0; for(i=j=0; (c=mpi_getbyte(result, i)) != -1; i++ ) { if( !j ) { if( !i && c != 1 ) break; else if( i && c == 0xff ) ; /* skip the padding */ else if( i && !c ) j++; else break; } else if( ++j == 18 && c != 1 ) break; else if( j == 19 && c == 0 ) { old_enc++; break; } } if( old_enc ) { log_error("old encoding scheme is not supported\n"); return G10ERR_GENERAL; } if( (rc=check_digest_algo(sig->digest_algo)) ) return rc; /* unsupported algo */ asn = md_asn_oid( sig->digest_algo, &asnlen, &mdlen ); for(i=mdlen,j=asnlen-1; (c=mpi_getbyte(result, i)) != -1 && j >= 0; i++, j-- ) if( asn[j] != c ) break; if( j != -1 || mpi_getbyte(result, i) ) return G10ERR_BAD_PUBKEY; /* ASN is wrong */ for(i++; (c=mpi_getbyte(result, i)) != -1; i++ ) if( c != 0xff ) break; i++; if( c != sig->digest_algo || mpi_getbyte(result, i) ) { /* Padding or leading bytes in signature is wrong */ return G10ERR_BAD_PUBKEY; } if( mpi_getbyte(result, mdlen-1) != sig->digest_start[0] || mpi_getbyte(result, mdlen-2) != sig->digest_start[1] ) { /* Wrong key used to check the signature */ return G10ERR_BAD_PUBKEY; } dp = md_read( digest, sig->digest_algo ); for(i=mdlen-1; i >= 0; i--, dp++ ) { if( mpi_getbyte( result, i ) != *dp ) return G10ERR_BAD_SIGN; } return 0; #else return -1; #endif } static int do_check( PKT_public_key *pk, PKT_signature *sig, MD_HANDLE digest ) { MPI result = NULL; int rc=0; struct cmp_help_context_s ctx; u32 cur_time; if( pk->version == 4 && pk->pubkey_algo == PUBKEY_ALGO_ELGAMAL_E ) { log_info(_("this is a PGP generated " "ElGamal key which is NOT secure for signatures!\n")); return G10ERR_PUBKEY_ALGO; } if( pk->timestamp > sig->timestamp ) { ulong d = pk->timestamp - sig->timestamp; log_info( d==1 ? _("public key is %lu second newer than the signature\n") : _("public key is %lu seconds newer than the signature\n"), d ); return G10ERR_TIME_CONFLICT; /* pubkey newer than signature */ } cur_time = make_timestamp(); if( pk->timestamp > cur_time ) { ulong d = pk->timestamp - cur_time; log_info( d==1 ? _("key has been created %lu second " "in future (time warp or clock problem)\n") : _("key has been created %lu seconds " "in future (time warp or clock problem)\n"), d ); return G10ERR_TIME_CONFLICT; } if( pk->expiredate && pk->expiredate < cur_time ) { log_info(_("NOTE: signature key expired %s\n"), asctimestamp( pk->expiredate ) ); write_status(STATUS_SIGEXPIRED); } if( (rc=check_digest_algo(sig->digest_algo)) ) return rc; if( (rc=check_pubkey_algo(sig->pubkey_algo)) ) return rc; /* make sure the digest algo is enabled (in case of a detached signature)*/ md_enable( digest, sig->digest_algo ); /* complete the digest */ if( sig->version >= 4 ) md_putc( digest, sig->version ); md_putc( digest, sig->sig_class ); if( sig->version < 4 ) { u32 a = sig->timestamp; md_putc( digest, (a >> 24) & 0xff ); md_putc( digest, (a >> 16) & 0xff ); md_putc( digest, (a >> 8) & 0xff ); md_putc( digest, a & 0xff ); } else { byte buf[6]; size_t n; md_putc( digest, sig->pubkey_algo ); md_putc( digest, sig->digest_algo ); if( sig->hashed_data ) { n = (sig->hashed_data[0] << 8) | sig->hashed_data[1]; md_write( digest, sig->hashed_data, n+2 ); n += 6; } else n = 6; /* add some magic */ buf[0] = sig->version; buf[1] = 0xff; buf[2] = n >> 24; buf[3] = n >> 16; buf[4] = n >> 8; buf[5] = n; md_write( digest, buf, 6 ); } md_final( digest ); result = encode_md_value( pk->pubkey_algo, digest, sig->digest_algo, mpi_get_nbits(pk->pkey[0])); ctx.sig = sig; ctx.md = digest; rc = pubkey_verify( pk->pubkey_algo, result, sig->data, pk->pkey, cmp_help, &ctx ); mpi_free( result ); if( !rc && sig->flags.unknown_critical ) { log_info(_("assuming bad signature due to an unknown critical bit\n")); rc = G10ERR_BAD_SIGN; } sig->flags.checked = 1; sig->flags.valid = !rc; return rc; } static void hash_uid_node( KBNODE unode, MD_HANDLE md, PKT_signature *sig ) { PKT_user_id *uid = unode->pkt->pkt.user_id; assert( unode->pkt->pkttype == PKT_USER_ID ); if( sig->version >=4 ) { byte buf[5]; buf[0] = 0xb4; /* indicates a userid packet */ buf[1] = uid->len >> 24; /* always use 4 length bytes */ buf[2] = uid->len >> 16; buf[3] = uid->len >> 8; buf[4] = uid->len; md_write( md, buf, 5 ); } md_write( md, uid->name, uid->len ); } /**************** * check the signature pointed to by NODE. This is a key signature. * If the function detects a self-signature, it uses the PK from * ROOT and does not read any public key. */ int check_key_signature( KBNODE root, KBNODE node, int *is_selfsig ) { MD_HANDLE md; PKT_public_key *pk; PKT_signature *sig; int algo; int rc; if( is_selfsig ) *is_selfsig = 0; assert( node->pkt->pkttype == PKT_SIGNATURE ); assert( root->pkt->pkttype == PKT_PUBLIC_KEY ); pk = root->pkt->pkt.public_key; sig = node->pkt->pkt.signature; algo = sig->digest_algo; + + if( sig->flags.checked ) + log_debug("check_key_signature: already checked: %s\n", + sig->flags.valid? "good":"bad" ); + if( (rc=check_digest_algo(algo)) ) return rc; if( sig->sig_class == 0x20 ) { md = md_open( algo, 0 ); hash_public_key( md, pk ); rc = do_check( pk, sig, md ); md_close(md); } else if( sig->sig_class == 0x28 ) { /* subkey revocation */ KBNODE snode = find_prev_kbnode( root, node, PKT_PUBLIC_SUBKEY ); if( snode ) { md = md_open( algo, 0 ); hash_public_key( md, pk ); hash_public_key( md, snode->pkt->pkt.public_key ); rc = do_check( pk, sig, md ); md_close(md); } else { log_error("no subkey for subkey revocation packet\n"); rc = G10ERR_SIG_CLASS; } } else if( sig->sig_class == 0x18 ) { KBNODE snode = find_prev_kbnode( root, node, PKT_PUBLIC_SUBKEY ); if( snode ) { if( is_selfsig ) { /* does this make sense????? */ u32 keyid[2]; /* it should always be a selfsig */ keyid_from_pk( pk, keyid ); if( keyid[0] == sig->keyid[0] && keyid[1] == sig->keyid[1] ) *is_selfsig = 1; } md = md_open( algo, 0 ); hash_public_key( md, pk ); hash_public_key( md, snode->pkt->pkt.public_key ); rc = do_check( pk, sig, md ); md_close(md); } else { log_error("no subkey for key signature packet\n"); rc = G10ERR_SIG_CLASS; } } else { KBNODE unode = find_prev_kbnode( root, node, PKT_USER_ID ); if( unode ) { u32 keyid[2]; keyid_from_pk( pk, keyid ); md = md_open( algo, 0 ); hash_public_key( md, pk ); hash_uid_node( unode, md, sig ); if( keyid[0] == sig->keyid[0] && keyid[1] == sig->keyid[1] ) { if( is_selfsig ) *is_selfsig = 1; rc = do_check( pk, sig, md ); } else rc = signature_check( sig, md ); md_close(md); } else { log_error("no user id for key signature packet\n"); rc = G10ERR_SIG_CLASS; } } return rc; } diff --git a/g10/tdbdump.c b/g10/tdbdump.c index 2d3502caf..799309e05 100644 --- a/g10/tdbdump.c +++ b/g10/tdbdump.c @@ -1,524 +1,524 @@ /* tdbdump.c * Copyright (C) 1999 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 #include #include "errors.h" #include "iobuf.h" #include "keydb.h" #include "memory.h" #include "util.h" #include "trustdb.h" #include "options.h" #include "packet.h" #include "main.h" #include "i18n.h" #include "tdbio.h" #define HEXTOBIN(a) ( (a) >= '0' && (a) <= '9' ? ((a)-'0') : \ (a) >= 'A' && (a) <= 'F' ? ((a)-'A'+10) : ((a)-'a'+10)) /**************** * Read a record but die if it does not exist * fixme: duplicate: remove it */ #if 0 static void read_record( ulong recno, TRUSTREC *rec, int rectype ) { int rc = tdbio_read_record( recno, rec, rectype ); if( !rc ) return; log_error(_("trust record %lu, req type %d: read failed: %s\n"), recno, rectype, g10_errstr(rc) ); tdbio_invalid(); } #endif /**************** * Wirte a record but die on error */ static void write_record( TRUSTREC *rec ) { int rc = tdbio_write_record( rec ); if( !rc ) return; log_error(_("trust record %lu, type %d: write failed: %s\n"), rec->recnum, rec->rectype, g10_errstr(rc) ); tdbio_invalid(); } /**************** * sync the db */ static void do_sync(void) { int rc = tdbio_sync(); if( !rc ) return; log_error(_("trustdb: sync failed: %s\n"), g10_errstr(rc) ); g10_exit(2); } #if 0 static int print_sigflags( FILE *fp, unsigned flags ) { if( flags & SIGF_CHECKED ) { fprintf(fp,"%c%c%c", (flags & SIGF_VALID) ? 'V':'-', (flags & SIGF_EXPIRED) ? 'E':'-', (flags & SIGF_REVOKED) ? 'R':'-'); } else if( flags & SIGF_NOPUBKEY) fputs("?--", fp); else fputs("---", fp); return 3; } #endif /**************** * Walk through the signatures of a public key. * The caller must provide a context structure, with all fields set * to zero, but the local_id field set to the requested key; * This function does not change this field. On return the context * is filled with the local-id of the signature and the signature flag. * No fields should be changed (clearing all fields and setting * pubkeyid is okay to continue with an other pubkey) * Returns: 0 - okay, -1 for eof (no more sigs) or any other errorcode * FIXME: Do we really need this large and complicated function? */ #if 0 static int walk_sigrecs( SIGREC_CONTEXT *c ) { TRUSTREC *r; ulong rnum; if( c->ctl.eof ) return -1; r = &c->ctl.rec; if( !c->ctl.init_done ) { c->ctl.init_done = 1; read_record( c->lid, r, 0 ); if( r->rectype != RECTYPE_DIR ) { c->ctl.eof = 1; return -1; /* return eof */ } c->ctl.nextuid = r->r.dir.uidlist; /* force a read */ c->ctl.index = SIGS_PER_RECORD; r->r.sig.next = 0; } /* need a loop to skip over deleted sigs */ do { if( c->ctl.index >= SIGS_PER_RECORD ) { /* read the record */ rnum = r->r.sig.next; if( !rnum && c->ctl.nextuid ) { /* read next uid record */ read_record( c->ctl.nextuid, r, RECTYPE_UID ); c->ctl.nextuid = r->r.uid.next; rnum = r->r.uid.siglist; } if( !rnum ) { c->ctl.eof = 1; return -1; /* return eof */ } read_record( rnum, r, RECTYPE_SIG ); if( r->r.sig.lid != c->lid ) { log_error(_("chained sigrec %lu has a wrong owner\n"), rnum ); c->ctl.eof = 1; tdbio_invalid(); } c->ctl.index = 0; } } while( !r->r.sig.sig[c->ctl.index++].lid ); c->sig_lid = r->r.sig.sig[c->ctl.index-1].lid; c->sig_flag = r->r.sig.sig[c->ctl.index-1].flag; return 0; } #endif #if 0 static int do_list_sigs( ulong root, ulong pk_lid, int depth, LOCAL_ID_TABLE lids, unsigned *lineno ) { SIGREC_CONTEXT sx; int rc; u32 keyid[2]; memset( &sx, 0, sizeof sx ); sx.lid = pk_lid; for(;;) { rc = walk_sigrecs( &sx ); /* should we replace it and use */ if( rc ) break; rc = keyid_from_lid( sx.sig_lid, keyid ); if( rc ) { printf("%6u: %*s????????.%lu:", *lineno, depth*4, "", sx.sig_lid ); print_sigflags( stdout, sx.sig_flag ); putchar('\n'); ++*lineno; } else { printf("%6u: %*s%08lX.%lu:", *lineno, depth*4, "", (ulong)keyid[1], sx.sig_lid ); print_sigflags( stdout, sx.sig_flag ); putchar(' '); /* check whether we already checked this pk_lid */ if( !qry_lid_table_flag( ultikey_table, sx.sig_lid, NULL ) ) { print_user_id("[ultimately trusted]", keyid); ++*lineno; } else if( sx.sig_lid == pk_lid ) { printf("[self-signature]\n"); ++*lineno; } else if( sx.sig_lid == root ) { printf("[closed]\n"); ++*lineno; } else if( ins_lid_table_item( lids, sx.sig_lid, *lineno ) ) { unsigned refline; qry_lid_table_flag( lids, sx.sig_lid, &refline ); printf("[see line %u]\n", refline); ++*lineno; } else if( depth+1 >= MAX_LIST_SIGS_DEPTH ) { print_user_id( "[too deeply nested]", keyid ); ++*lineno; } else { print_user_id( "", keyid ); ++*lineno; rc = do_list_sigs( root, sx.sig_lid, depth+1, lids, lineno ); if( rc ) break; } } } return rc==-1? 0 : rc; } #endif /**************** * List all signatures of a public key */ static int list_sigs( ulong pubkey_id ) { int rc=0; #if 0 u32 keyid[2]; LOCAL_ID_TABLE lids; unsigned lineno = 1; rc = keyid_from_lid( pubkey_id, keyid ); if( rc ) return rc; printf("Signatures of %08lX.%lu ", (ulong)keyid[1], pubkey_id ); print_user_id("", keyid); printf("----------------------\n"); lids = new_lid_table(); rc = do_list_sigs( pubkey_id, pubkey_id, 0, lids, &lineno ); putchar('\n'); release_lid_table(lids); #endif return rc; } /**************** * List all records of a public key */ static int list_records( ulong lid ) { int rc; TRUSTREC dr, ur, rec; ulong recno; rc = tdbio_read_record( lid, &dr, RECTYPE_DIR ); if( rc ) { log_error(_("lid %lu: read dir record failed: %s\n"), lid, g10_errstr(rc)); return rc; } tdbio_dump_record( &dr, stdout ); for( recno=dr.r.dir.keylist; recno; recno = rec.r.key.next ) { rc = tdbio_read_record( recno, &rec, 0 ); if( rc ) { log_error(_("lid %lu: read key record failed: %s\n"), lid, g10_errstr(rc)); return rc; } tdbio_dump_record( &rec, stdout ); } for( recno=dr.r.dir.uidlist; recno; recno = ur.r.uid.next ) { rc = tdbio_read_record( recno, &ur, RECTYPE_UID ); if( rc ) { log_error(_("lid %lu: read uid record failed: %s\n"), lid, g10_errstr(rc)); return rc; } tdbio_dump_record( &ur, stdout ); /* preference records */ for(recno=ur.r.uid.prefrec; recno; recno = rec.r.pref.next ) { rc = tdbio_read_record( recno, &rec, RECTYPE_PREF ); if( rc ) { log_error(_("lid %lu: read pref record failed: %s\n"), lid, g10_errstr(rc)); return rc; } tdbio_dump_record( &rec, stdout ); } /* sig records */ for(recno=ur.r.uid.siglist; recno; recno = rec.r.sig.next ) { rc = tdbio_read_record( recno, &rec, RECTYPE_SIG ); if( rc ) { log_error(_("lid %lu: read sig record failed: %s\n"), lid, g10_errstr(rc)); return rc; } tdbio_dump_record( &rec, stdout ); } } /* add cache record dump here */ return rc; } /**************** * Dump the complte trustdb or only the entries of one key. */ void list_trustdb( const char *username ) { TRUSTREC rec; init_trustdb(); if( username && *username == '#' ) { int rc; ulong lid = atoi(username+1); if( (rc = list_records( lid)) ) log_error(_("user '%s' read problem: %s\n"), username, g10_errstr(rc)); else if( (rc = list_sigs( lid )) ) log_error(_("user '%s' list problem: %s\n"), username, g10_errstr(rc)); } else if( username ) { PKT_public_key *pk = m_alloc_clear( sizeof *pk ); int rc; if( (rc = get_pubkey_byname( NULL, pk, username, 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"), username, g10_errstr(rc)); else if( rc == -1 ) log_error(_("user '%s' not in trustdb\n"), username); else if( (rc = list_records( pk->local_id)) ) log_error(_("user '%s' read problem: %s\n"), username, g10_errstr(rc)); else if( (rc = list_sigs( pk->local_id )) ) log_error(_("user '%s' list problem: %s\n"), username, g10_errstr(rc)); free_public_key( pk ); } else { ulong recnum; int i; printf("TrustDB: %s\n", tdbio_get_dbname() ); for(i=9+strlen(tdbio_get_dbname()); i > 0; i-- ) putchar('-'); putchar('\n'); for(recnum=0; !tdbio_read_record( recnum, &rec, 0); recnum++ ) tdbio_dump_record( &rec, stdout ); } } /**************** * Print a list of all defined owner trust value. */ void export_ownertrust() { TRUSTREC rec; TRUSTREC rec2; ulong recnum; int i; byte *p; int rc; init_trustdb(); printf(_("# List of assigned trustvalues, created %s\n" "# (Use \"gpg --import-ownertrust\" to restore them)\n"), asctimestamp( make_timestamp() ) ); for(recnum=0; !tdbio_read_record( recnum, &rec, 0); recnum++ ) { if( rec.rectype == RECTYPE_DIR ) { if( !rec.r.dir.keylist ) { log_error(_("directory record w/o primary key\n")); continue; } if( !rec.r.dir.ownertrust ) continue; rc = tdbio_read_record( rec.r.dir.keylist, &rec2, RECTYPE_KEY); if( rc ) { log_error(_("error reading key record: %s\n"), g10_errstr(rc)); continue; } p = rec2.r.key.fingerprint; for(i=0; i < rec2.r.key.fingerprint_len; i++, p++ ) printf("%02X", *p ); printf(":%u:\n", (unsigned)rec.r.dir.ownertrust ); } } } void import_ownertrust( const char *fname ) { FILE *fp; int is_stdin=0; char line[256]; char *p; size_t n, fprlen; unsigned otrust; init_trustdb(); if( !fname || (*fname == '-' && !fname[1]) ) { fp = stdin; fname = "[stdin]"; is_stdin = 1; } else if( !(fp = fopen( fname, "r" )) ) { log_error_f(fname, _("can't open file: %s\n"), strerror(errno) ); return; } while( fgets( line, DIM(line)-1, fp ) ) { TRUSTREC rec; int rc; if( !*line || *line == '#' ) continue; n = strlen(line); if( line[n-1] != '\n' ) { log_error_f(fname, _("line too long\n") ); /* ... or last line does not have a LF */ break; /* can't continue */ } for(p = line; *p && *p != ':' ; p++ ) if( !isxdigit(*p) ) break; if( *p != ':' ) { log_error_f(fname, _("error: missing colon\n") ); continue; } fprlen = p - line; if( fprlen != 32 && fprlen != 40 ) { log_error_f(fname, _("error: invalid fingerprint\n") ); continue; } if( sscanf(p, ":%u:", &otrust ) != 1 ) { log_error_f(fname, _("error: no ownertrust value\n") ); continue; } if( !otrust ) continue; /* no otrust defined - no need to update or insert */ /* convert the ascii fingerprint to binary */ for(p=line, fprlen=0; *p != ':'; p += 2 ) line[fprlen++] = HEXTOBIN(p[0]) * 16 + HEXTOBIN(p[1]); line[fprlen] = 0; repeat: rc = tdbio_search_dir_byfpr( line, fprlen, 0, &rec ); if( !rc ) { /* found: update */ if( rec.r.dir.ownertrust ) log_info("LID %lu: changing trust from %u to %u\n", rec.r.dir.lid, rec.r.dir.ownertrust, otrust ); else log_info("LID %lu: setting trust to %u\n", rec.r.dir.lid, otrust ); rec.r.dir.ownertrust = otrust; write_record( &rec ); } else if( rc == -1 ) { /* not found; get the key from the ring */ PKT_public_key *pk = m_alloc_clear( sizeof *pk ); log_info_f(fname, _("key not in trustdb, searching ring.\n")); rc = get_pubkey_byfprint( pk, line, fprlen ); if( rc ) log_info_f(fname, _("key not in ring: %s\n"), g10_errstr(rc)); else { rc = query_trust_record( pk ); /* only as assertion */ if( rc != -1 ) log_error_f(fname, _("Oops: key is now in trustdb???\n")); else { - rc = insert_trust_record( pk ); + rc = insert_trust_record_by_pk( pk ); if( !rc ) goto repeat; /* update the ownertrust */ log_error_f(fname, _("insert trust record failed: %s\n"), g10_errstr(rc) ); } } } else /* error */ log_error_f(fname, _("error finding dir record: %s\n"), g10_errstr(rc)); } if( ferror(fp) ) log_error_f(fname, _("read error: %s\n"), strerror(errno) ); if( !is_stdin ) fclose(fp); do_sync(); } diff --git a/g10/trustdb.c b/g10/trustdb.c index 1e804d704..fa1e43a56 100644 --- a/g10/trustdb.c +++ b/g10/trustdb.c @@ -1,2595 +1,2619 @@ /* trustdb.c * Copyright (C) 1998, 1999 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 #include #include "errors.h" #include "iobuf.h" #include "keydb.h" #include "memory.h" #include "util.h" #include "trustdb.h" #include "options.h" #include "packet.h" #include "main.h" #include "i18n.h" #include "tdbio.h" #include "ttyio.h" #if MAX_FINGERPRINT_LEN > 20 #error Must change structure of trustdb #endif struct keyid_list { struct keyid_list *next; u32 keyid[2]; }; struct local_id_item { struct local_id_item *next; ulong lid; unsigned flag; }; struct local_id_table { struct local_id_table *next; /* only used to keep a list of unused tables */ struct local_id_item *items[16]; }; typedef struct local_id_table *LOCAL_ID_TABLE; struct enum_cert_paths_ctx { int init; int idx; }; struct recno_list_struct { struct recno_list_struct *next; ulong recno; int type; }; typedef struct recno_list_struct *RECNO_LIST; typedef struct trust_node *TN; struct trust_node { TN back; /* parent */ TN list; /* list of other node (should all be of the same type)*/ TN next; /* used to build the list */ int is_uid; /* set if this is an uid node */ ulong lid; /* key or uid recordnumber */ union { struct { int ownertrust; int validity; /* helper */ int buckstop; } k; struct { int marginal_count; int fully_count; int validity; } u; } n; }; static TN used_tns; static int alloced_tns; static int max_alloced_tns; static LOCAL_ID_TABLE new_lid_table(void); static int ins_lid_table_item( LOCAL_ID_TABLE tbl, ulong lid, unsigned flag ); static int qry_lid_table_flag( LOCAL_ID_TABLE tbl, ulong lid, unsigned *flag ); static int propagate_validity( TN root, TN node, int (*add_fnc)(ulong), unsigned *retflgs ); static void print_user_id( FILE *fp, const char *text, u32 *keyid ); static int do_check( TRUSTREC *drec, unsigned *trustlevel, const char *nhash, int (*add_fnc)(ulong), unsigned *retflgs); static int get_dir_record( PKT_public_key *pk, TRUSTREC *rec ); /* a table used to keep track of ultimately trusted keys * which are the ones from our secrings and the trusted keys */ static LOCAL_ID_TABLE ultikey_table; /* list of unused lid items and tables */ static LOCAL_ID_TABLE unused_lid_tables; static struct local_id_item *unused_lid_items; static struct { int init; int level; char *dbname; } trustdb_args; /********************************************** *********** record read write ************** **********************************************/ /**************** * Read a record but die if it does not exist */ static void read_record( ulong recno, TRUSTREC *rec, int rectype ) { int rc = tdbio_read_record( recno, rec, rectype ); if( !rc ) return; log_error(_("trust record %lu, req type %d: read failed: %s\n"), recno, rectype, g10_errstr(rc) ); tdbio_invalid(); } /**************** * Wirte a record but die on error */ static void write_record( TRUSTREC *rec ) { int rc = tdbio_write_record( rec ); if( !rc ) return; log_error(_("trust record %lu, type %d: write failed: %s\n"), rec->recnum, rec->rectype, g10_errstr(rc) ); tdbio_invalid(); } /**************** * Delete a record but die on error */ static void delete_record( ulong recno ) { int rc = tdbio_delete_record( recno ); if( !rc ) return; log_error(_("trust record %lu: delete failed: %s\n"), recno, g10_errstr(rc) ); tdbio_invalid(); } /**************** * sync the db */ static void do_sync(void) { int rc = tdbio_sync(); if( !rc ) return; log_error(_("trustdb: sync failed: %s\n"), g10_errstr(rc) ); g10_exit(2); } /********************************************** ***************** helpers ****************** **********************************************/ static LOCAL_ID_TABLE new_lid_table(void) { LOCAL_ID_TABLE a; a = unused_lid_tables; if( a ) { unused_lid_tables = a->next; memset( a, 0, sizeof *a ); } else a = m_alloc_clear( sizeof *a ); return a; } #if 0 static void release_lid_table( LOCAL_ID_TABLE tbl ) { struct local_id_item *a, *a2; int i; for(i=0; i < 16; i++ ) { for(a=tbl->items[i]; a; a = a2 ) { a2 = a->next; a->next = unused_lid_items; unused_lid_items = a; } } tbl->next = unused_lid_tables; unused_lid_tables = tbl; } #endif /**************** * Add a new item to the table or return 1 if we already have this item */ static int ins_lid_table_item( LOCAL_ID_TABLE tbl, ulong lid, unsigned flag ) { struct local_id_item *a; for( a = tbl->items[lid & 0x0f]; a; a = a->next ) if( a->lid == lid ) return 1; a = unused_lid_items; if( a ) unused_lid_items = a->next; else a = m_alloc( sizeof *a ); a->lid = lid; a->flag = flag; a->next = tbl->items[lid & 0x0f]; tbl->items[lid & 0x0f] = a; return 0; } static int qry_lid_table_flag( LOCAL_ID_TABLE tbl, ulong lid, unsigned *flag ) { struct local_id_item *a; for( a = tbl->items[lid & 0x0f]; a; a = a->next ) if( a->lid == lid ) { if( flag ) *flag = a->flag; return 0; } return -1; } static TN new_tn(void) { TN t; if( used_tns ) { t = used_tns; used_tns = t->next; memset( t, 0, sizeof *t ); } else t = m_alloc_clear( sizeof *t ); if( ++alloced_tns > max_alloced_tns ) max_alloced_tns = alloced_tns; return t; } static void release_tn( TN t ) { if( t ) { t->next = used_tns; used_tns = t; alloced_tns--; } } static void release_tn_tree( TN kr ) { TN kr2; for( ; kr; kr = kr2 ) { release_tn_tree( kr->list ); kr2 = kr->next; release_tn( kr ); } } /********************************************** ****** access by LID and other helpers ******* **********************************************/ /**************** * Return the keyid from the primary key identified by LID. */ int keyid_from_lid( ulong lid, u32 *keyid ) { TRUSTREC rec; int rc; init_trustdb(); keyid[0] = keyid[1] = 0; rc = tdbio_read_record( lid, &rec, 0 ); if( rc ) { log_error(_("error reading dir record for LID %lu: %s\n"), lid, g10_errstr(rc)); return G10ERR_TRUSTDB; } if( rec.rectype == RECTYPE_SDIR ) return 0; if( rec.rectype != RECTYPE_DIR ) { log_error(_("lid %lu: expected dir record, got type %d\n"), lid, rec.rectype ); return G10ERR_TRUSTDB; } if( !rec.r.dir.keylist ) { log_error(_("no primary key for LID %lu\n"), lid ); return G10ERR_TRUSTDB; } rc = tdbio_read_record( rec.r.dir.keylist, &rec, RECTYPE_KEY ); if( rc ) { log_error(_("error reading primary key for LID %lu: %s\n"), lid, g10_errstr(rc)); return G10ERR_TRUSTDB; } keyid_from_fingerprint( rec.r.key.fingerprint, rec.r.key.fingerprint_len, keyid ); return 0; } ulong lid_from_keyblock( KBNODE keyblock ) { KBNODE node = find_kbnode( keyblock, PKT_PUBLIC_KEY ); PKT_public_key *pk; if( !node ) BUG(); pk = node->pkt->pkt.public_key; if( !pk->local_id ) { TRUSTREC rec; init_trustdb(); get_dir_record( pk, &rec ); } return pk->local_id; } static int get_dir_record( PKT_public_key *pk, TRUSTREC *rec ) { int rc=0; if( pk->local_id ) { read_record( pk->local_id, rec, RECTYPE_DIR ); } else { /* no local_id: scan the trustdb */ if( (rc=tdbio_search_dir_bypk( pk, rec )) && rc != -1 ) log_error(_("get_dir_record: search_record failed: %s\n"), g10_errstr(rc)); } return rc; } /**************** * Get the LID of a public key. * Returns: The LID of the key (note, that this may be a shadow dir) * or 0 if not available. * fixme: make this ftser by putting entries into the sdir hash table */ #if 0 static ulong lid_from_keyid( u32 *keyid ) { PKT_public_key *pk = m_alloc_clear( sizeof *pk ); TRUSTREC rec; ulong lid = 0; int rc; rc = get_pubkey( pk, keyid ); if( !rc ) { if( pk->local_id ) lid = pk->local_id; else { rc = tdbio_search_dir_bypk( pk, &rec ); if( !rc ) lid = rec.recnum; else if( rc == -1 ) { /* see whether there is a sdir instead */ u32 akid[2]; keyid_from_pk( pk, akid ); rc = tdbio_search_sdir( akid, pk->pubkey_algo, &rec ); if( !rc ) lid = rec.recnum; } } } free_public_key( pk ); return lid; } #endif static ulong lid_from_keyid_no_sdir( u32 *keyid ) { PKT_public_key *pk = m_alloc_clear( sizeof *pk ); TRUSTREC rec; ulong lid = 0; int rc; rc = get_pubkey( pk, keyid ); if( !rc ) { if( pk->local_id ) lid = pk->local_id; else { rc = tdbio_search_dir_bypk( pk, &rec ); if( !rc ) lid = rec.recnum; } } free_public_key( pk ); return lid; } /*********************************************** ************* Initialization **************** ***********************************************/ /**************** * Verify that all our public keys are in the trustdb. */ static int verify_own_keys(void) { int rc; void *enum_context = NULL; PKT_secret_key *sk = m_alloc_clear( sizeof *sk ); PKT_public_key *pk = m_alloc_clear( sizeof *pk ); u32 keyid[2]; while( !(rc=enum_secret_keys( &enum_context, sk, 0 ) ) ) { int have_pk = 0; keyid_from_sk( sk, keyid ); if( DBG_TRUST ) log_debug("key %08lX: checking secret key\n", (ulong)keyid[1] ); if( is_secret_key_protected( sk ) < 1 ) log_info(_("NOTE: secret key %08lX is NOT protected.\n"), (ulong)keyid[1] ); /* see whether we can access the public key of this secret key */ memset( pk, 0, sizeof *pk ); rc = get_pubkey( pk, keyid ); if( rc ) { log_info(_("key %08lX: secret key without public key - skipped\n"), (ulong)keyid[1] ); goto skip; } have_pk=1; if( cmp_public_secret_key( pk, sk ) ) { log_info(_("key %08lX: secret and public key don't match\n"), (ulong)keyid[1] ); goto skip; } /* make sure that the pubkey is in the trustdb */ rc = query_trust_record( pk ); if( rc == -1 ) { /* put it into the trustdb */ - rc = insert_trust_record( pk ); + rc = insert_trust_record_by_pk( pk ); if( rc ) { log_error(_("key %08lX: can't put it into the trustdb\n"), (ulong)keyid[1] ); goto skip; } } else if( rc ) { log_error(_("key %08lX: query record failed\n"), (ulong)keyid[1] ); goto skip; } if( DBG_TRUST ) log_debug("key %08lX.%lu: stored into ultikey_table\n", (ulong)keyid[1], pk->local_id ); if( ins_lid_table_item( ultikey_table, pk->local_id, 0 ) ) log_error(_("key %08lX: already in trusted key table\n"), (ulong)keyid[1]); else if( opt.verbose > 1 ) log_info(_("key %08lX: accepted as trusted key.\n"), (ulong)keyid[1]); skip: release_secret_key_parts( sk ); if( have_pk ) release_public_key_parts( pk ); } if( rc != -1 ) log_error(_("enumerate secret keys failed: %s\n"), g10_errstr(rc) ); else rc = 0; enum_secret_keys( &enum_context, NULL, 0 ); /* free context */ free_secret_key( sk ); free_public_key( pk ); return rc; } /**************** * Perform some checks over the trustdb * level 0: only open the db * 1: used for initial program startup */ int setup_trustdb( int level, const char *dbname ) { /* just store the args */ if( trustdb_args.init ) return 0; trustdb_args.level = level; trustdb_args.dbname = dbname? m_strdup(dbname): NULL; return 0; } void init_trustdb() { int rc=0; int level = trustdb_args.level; const char* dbname = trustdb_args.dbname; if( trustdb_args.init ) return; trustdb_args.init = 1; if( !ultikey_table ) ultikey_table = new_lid_table(); if( !level || level==1 ) { rc = tdbio_set_dbname( dbname, !!level ); if( !rc ) { if( !level ) return; /* verify that our own keys are in the trustDB * or move them to the trustdb. */ rc = verify_own_keys(); /* should we check whether there is no other ultimately trusted * key in the database? */ } } else BUG(); if( rc ) log_fatal("can't init trustdb: %s\n", g10_errstr(rc) ); } /*********************************************** ************* Print helpers **************** ***********************************************/ static void print_user_id( FILE *fp, const char *text, u32 *keyid ) { char *p; size_t n; p = get_user_id( keyid, &n ); if( fp ) { fprintf( fp, "%s \"", text ); print_string( fp, p, n, 0 ); putc('\"', fp); putc('\n', fp); } else { tty_printf( "%s \"", text ); tty_print_string( p, n ); tty_printf( "\"\n" ); } m_free(p); } +/**************** + * This function returns a letter for a trustvalue Trust flags + * are ignore. + */ int trust_letter( unsigned value ) { - switch( value ) { + switch( (value & TRUST_MASK) ) { case TRUST_UNKNOWN: return '-'; case TRUST_EXPIRED: return 'e'; case TRUST_UNDEFINED: return 'q'; case TRUST_NEVER: return 'n'; case TRUST_MARGINAL: return 'm'; case TRUST_FULLY: return 'f'; case TRUST_ULTIMATE: return 'u'; default: return 0 ; } } #if 0 static void print_path( int pathlen, TN ME .........., FILE *fp, ulong highlight ) { int rc, c, i; u32 keyid[2]; char *p; size_t n; for( i = 0; i < pathlen; i++ ) { if( highlight ) fputs(highlight == path[i].lid? "* ":" ", fp ); rc = keyid_from_lid( path[i].lid, keyid ); if( rc ) fprintf(fp, "????????.%lu:", path[i].lid ); else fprintf(fp,"%08lX.%lu:", (ulong)keyid[1], path[i].lid ); c = trust_letter(path[i].otrust); if( c ) putc( c, fp ); else fprintf( fp, "%02x", path[i].otrust ); putc('/', fp); c = trust_letter(path[i].trust); if( c ) putc( c, fp ); else fprintf( fp, "%02x", path[i].trust ); putc(' ', fp); p = get_user_id( keyid, &n ); putc(' ', fp); putc('\"', fp); print_string( fp, p, n > 40? 40:n, 0 ); putc('\"', fp); m_free(p); putc('\n', fp ); } } #endif static void print_default_uid( FILE *fp, ulong lid ) { u32 keyid[2]; if( !keyid_from_lid( lid, keyid ) ) print_user_id( fp, "", keyid ); } static void print_uid_from_keyblock( FILE *fp, KBNODE keyblock, ulong urecno ) { TRUSTREC urec; KBNODE node; byte uhash[20]; read_record( urecno, &urec, RECTYPE_UID ); for( node=keyblock; node; node = node->next ) { if( node->pkt->pkttype == PKT_USER_ID ) { PKT_user_id *uidpkt = node->pkt->pkt.user_id; rmd160_hash_buffer( uhash, uidpkt->name, uidpkt->len ); if( !memcmp( uhash, urec.r.uid.namehash, 20 ) ) { print_string( fp, uidpkt->name, uidpkt->len, ':' ); return; } } } fputs("[?]", fp ); } static void dump_tn_tree( FILE *fp, int level, TN tree ) { TN kr, ur; for( kr=tree; kr; kr = kr->next ) { if( fp ) { fprintf( fp, "%*s", level*4, "" ); fprintf( fp, "K%lu(ot=%d,val=%d) ", kr->lid, kr->n.k.ownertrust, kr->n.k.validity ); } else { tty_printf("%*s", level*4, "" ); tty_printf("K%lu(ot=%d,val=%d) ", kr->lid, kr->n.k.ownertrust, kr->n.k.validity ); } print_default_uid( fp, kr->lid ); for( ur=kr->list; ur; ur = ur->next ) { if( fp ) { fprintf(fp, "%*s ", level*4, "" ); fprintf(fp, "U%lu(mc=%d,fc=%d,val=%d)\n", ur->lid, ur->n.u.marginal_count, ur->n.u.fully_count, ur->n.u.validity ); } else { tty_printf("%*s ", level*4, "" ); tty_printf("U%lu(mc=%d,fc=%d,val=%d)\n", ur->lid, ur->n.u.marginal_count, ur->n.u.fully_count, ur->n.u.validity ); } dump_tn_tree( fp, level+1, ur->list ); } } } /**************** * Special version of dump_tn_tree, which prints it colon delimited. * Format: * level:keyid:type:recno:ot:val:mc:cc:name: * With TYPE = U for a user ID * K for a key * The RECNO is either the one of the dir record or the one of the uid record. * OT is the the usual trust letter and only availabel on K lines. * VAL is the calcualted validity * MC is the marginal trust counter and only available on U lines * CC is the same for the complete count * NAME ist the username and only printed on U lines */ static void dump_tn_tree_with_colons( int level, TN tree ) { TN kr, ur; for( kr=tree; kr; kr = kr->next ) { KBNODE kb = NULL; u32 kid[2]; keyid_from_lid( kr->lid, kid ); get_keyblock_bylid( &kb, kr->lid ); printf( "%d:%08lX%08lX:K:%lu:%c:%c::::\n", level, (ulong)kid[0], (ulong)kid[1], kr->lid, trust_letter( kr->n.k.ownertrust ), trust_letter( kr->n.k.validity ) ); for( ur=kr->list; ur; ur = ur->next ) { printf( "%d:%08lX%08lX:U:%lu::%c:%d:%d:", level, (ulong)kid[0], (ulong)kid[1], ur->lid, trust_letter( kr->n.u.validity ), ur->n.u.marginal_count, ur->n.u.fully_count ); print_uid_from_keyblock( stdout, kb, ur->lid ); putchar(':'); putchar('\n'); dump_tn_tree_with_colons( level+1, ur->list ); } release_kbnode( kb ); } } /*********************************************** ************* trustdb maintenance *********** ***********************************************/ /**************** * Create or update shadow dir record and return the LID of the record */ static ulong create_shadow_dir( PKT_signature *sig ) { TRUSTREC sdir; int rc; /* first see whether we already have such a record */ rc = tdbio_search_sdir( sig->keyid, sig->pubkey_algo, &sdir ); if( rc && rc != -1 ) { log_error(_("tdbio_search_sdir failed: %s\n"), g10_errstr(rc)); tdbio_invalid(); } if( rc == -1 ) { /* not found: create */ memset( &sdir, 0, sizeof sdir ); sdir.recnum = tdbio_new_recnum(); sdir.rectype= RECTYPE_SDIR; sdir.r.sdir.lid = sdir.recnum; sdir.r.sdir.keyid[0] = sig->keyid[0]; sdir.r.sdir.keyid[1] = sig->keyid[1]; sdir.r.sdir.pubkey_algo = sig->pubkey_algo; write_record( &sdir ); } return sdir.recnum; } static ulong find_or_create_lid( PKT_signature *sig ) { ulong lid; lid = lid_from_keyid_no_sdir( sig->keyid ); if( !lid ) lid = create_shadow_dir( sig ); return lid; } #if 0 static void upd_pref_record( TRUSTREC *urec, u32 *keyid, PKT_signature *sig ) { static struct { sigsubpkttype_t subpkttype; int preftype; } ptable[] = { { SIGSUBPKT_PREF_SYM, PREFTYPE_SYM }, { SIGSUBPKT_PREF_HASH, PREFTYPE_HASH }, { SIGSUBPKT_PREF_COMPR, PREFTYPE_COMPR }, { 0, 0 } }; TRUSTREC prec; ulong lid = urec->r.uid.lid ; const byte *uidhash = urec->r.uid.namehash; const byte *s; size_t n; int k, i; ulong recno; byte prefs_sig[200]; int n_prefs_sig = 0; byte prefs_rec[200]; int n_prefs_rec = 0; if( DBG_TRUST ) log_debug("upd_pref_record for %08lX.%lu/%02X%02X\n", (ulong)keyid[1], lid, uidhash[18], uidhash[19] ); /* check for changed preferences */ for(k=0; ptable[k].subpkttype; k++ ) { s = parse_sig_subpkt2( sig, ptable[k].subpkttype, &n ); if( s ) { for( ; n; n--, s++ ) { if( n_prefs_sig >= DIM(prefs_sig)-1 ) { log_info("uid %08lX.%lu/%02X%02X: %s\n", (ulong)keyid[1], lid, uidhash[18], uidhash[19], _("Too many preferences") ); break; } prefs_sig[n_prefs_sig++] = ptable[k].preftype; prefs_sig[n_prefs_sig++] = *s; } } } for( recno=urec->r.uid.prefrec; recno; recno = prec.r.pref.next ) { read_record( recno, &prec, RECTYPE_PREF ); for(i = 0; i < ITEMS_PER_PREF_RECORD; i +=2 ) { if( n_prefs_rec >= DIM(prefs_rec)-1 ) { log_info("uid %08lX.%lu/%02X%02X: %s\n", (ulong)keyid[1], lid, uidhash[18], uidhash[19], _("Too many preference items") ); break; } if( prec.r.pref.data[i] ) { prefs_rec[n_prefs_rec++] = prec.r.pref.data[i]; prefs_rec[n_prefs_rec++] = prec.r.pref.data[i+1]; } } } if( n_prefs_sig == n_prefs_rec && !memcmp( prefs_sig, prefs_rec, n_prefs_sig ) ) return; /* not changed */ /* Preferences have changed: Delete all pref records * This is much simpler than checking whether we have to * do update the record at all - the record cache may care about it */ for( recno=urec->r.uid.prefrec; recno; recno = prec.r.pref.next ) { read_record( recno, &prec, RECTYPE_PREF ); delete_record( recno ); } if( n_prefs_sig > ITEMS_PER_PREF_RECORD ) log_info(_("WARNING: can't yet handle long pref records\n")); memset( &prec, 0, sizeof prec ); prec.recnum = tdbio_new_recnum(); prec.rectype = RECTYPE_PREF; prec.r.pref.lid = lid; if( n_prefs_sig <= ITEMS_PER_PREF_RECORD ) memcpy( prec.r.pref.data, prefs_sig, n_prefs_sig ); else { /* need more than one pref record */ TRUSTREC tmp; ulong nextrn; byte *pp = prefs_sig; n = n_prefs_sig; memcpy( prec.r.pref.data, pp, ITEMS_PER_PREF_RECORD ); n -= ITEMS_PER_PREF_RECORD; pp += ITEMS_PER_PREF_RECORD; nextrn = prec.r.pref.next = tdbio_new_recnum(); do { memset( &tmp, 0, sizeof tmp ); tmp.recnum = nextrn; tmp.rectype = RECTYPE_PREF; tmp.r.pref.lid = lid; if( n <= ITEMS_PER_PREF_RECORD ) { memcpy( tmp.r.pref.data, pp, n ); n = 0; } else { memcpy( tmp.r.pref.data, pp, ITEMS_PER_PREF_RECORD ); n -= ITEMS_PER_PREF_RECORD; pp += ITEMS_PER_PREF_RECORD; nextrn = tmp.r.pref.next = tdbio_new_recnum(); } write_record( &tmp ); } while( n ); } write_record( &prec ); urec->r.uid.prefrec = prec.recnum; urec->dirty = 1; } #endif /**************** * Check the validity of a key and calculate the keyflags * keynode points to * a node with a [sub]key. mainkid has the key ID of the primary key * keyblock is the complete keyblock which is needed for signature * checking. LID and PK is only used in verbose mode. */ static unsigned int check_keybinding( KBNODE keyblock, KBNODE keynode, u32 *mainkid, ulong lid, PKT_public_key *pk ) { KBNODE node; int keybind_seen = 0; int revoke_seen = 0; unsigned int keyflags=0; int is_main = (keynode->pkt->pkttype == PKT_PUBLIC_KEY); int rc; + if( DBG_TRUST ) + log_debug("check_keybinding: %08lX.%lu\n", + (ulong)mainkid[1], lid ); + if( is_main ) { /* a primary key is always valid (user IDs are handled elsewhere)*/ keyflags = KEYF_CHECKED | KEYF_VALID; } for( node=keynode->next; node; node = node->next ) { PKT_signature *sig; if( node->pkt->pkttype == PKT_PUBLIC_SUBKEY ) break; /* ready */ if( node->pkt->pkttype != PKT_SIGNATURE ) continue; /* don't care about other packets */ sig = node->pkt->pkt.signature; if( mainkid[0] != sig->keyid[0] || mainkid[1] != sig->keyid[1] ) continue; /* we only care about self-signatures */ if( sig->sig_class == 0x18 && !keybind_seen && !is_main ) { /* check until we find a valid keybinding */ rc = check_key_signature( keyblock, node, NULL ); if( !rc ) { if( opt.verbose ) log_info(_("key %08lX.%lu: Good subkey binding\n"), (ulong)keyid_from_pk(pk,NULL), lid ); keyflags |= KEYF_CHECKED | KEYF_VALID; } else { log_info(_( "key %08lX.%lu: Invalid subkey binding: %s\n"), (ulong)keyid_from_pk(pk,NULL), lid, g10_errstr(rc) ); keyflags |= KEYF_CHECKED; keyflags &= ~KEYF_VALID; } keybind_seen = 1; } else if( sig->sig_class == 0x20 && !revoke_seen ) { /* this is a key revocation certificate: check it */ rc = check_key_signature( keyblock, node, NULL ); if( !rc ) { if( opt.verbose ) log_info(_("key %08lX.%lu: Valid key revocation\n"), (ulong)keyid_from_pk(pk, NULL), lid ); keyflags |= KEYF_REVOKED; /* fixme: revoke the main key too*/ } else { log_info(_( "key %08lX.%lu: Invalid key revocation: %s\n"), (ulong)keyid_from_pk(pk,NULL), lid, g10_errstr(rc) ); } revoke_seen = 1; } else if( sig->sig_class == 0x28 && !revoke_seen && !is_main ) { /* this is a subkey revocation certificate: check it */ /* fixme: we should also check that the revocation * is newer than the key (OpenPGP) */ rc = check_key_signature( keyblock, node, NULL ); if( !rc ) { if( opt.verbose ) log_info(_( "key %08lX.%lu: Valid subkey revocation\n"), (ulong)keyid_from_pk(pk,NULL), lid ); keyflags |= KEYF_REVOKED; } else { log_info(_( "key %08lX.%lu: Invalid subkey binding: %s\n"), (ulong)keyid_from_pk(pk,NULL), lid, g10_errstr(rc) ); } revoke_seen = 1; } /* Hmmm: should we handle direct key signatures here? */ } return keyflags; } static ulong make_key_records( KBNODE keyblock, ulong lid, u32 *keyid ) { TRUSTREC *krecs, **kend, *k, *k2; KBNODE node; PKT_public_key *pk; byte fpr[MAX_FINGERPRINT_LEN]; size_t fprlen; ulong keyrecno; krecs = NULL; kend = &krecs; for( node=keyblock; node; node = node->next ) { if( node->pkt->pkttype != PKT_PUBLIC_KEY && node->pkt->pkttype != PKT_PUBLIC_SUBKEY ) continue; pk = node->pkt->pkt.public_key; fingerprint_from_pk( pk, fpr, &fprlen ); /* create the key record */ k = m_alloc_clear( sizeof *k ); k->rectype = RECTYPE_KEY; k->r.key.lid = lid; k->r.key.pubkey_algo = pk->pubkey_algo; k->r.key.fingerprint_len = fprlen; memcpy(k->r.key.fingerprint, fpr, fprlen ); k->recnum = tdbio_new_recnum(); *kend = k; kend = &k->next; k->r.key.keyflags = check_keybinding( keyblock, node, keyid, lid, pk ); } keyrecno = krecs? krecs->recnum : 0; /* write the keylist and release the memory */ for( k = krecs; k ; k = k2 ) { if( k->next ) k->r.key.next = k->next->recnum; write_record( k ); k2 = k->next; m_free( k ); } return keyrecno; } /**************** * Check the validity of a user ID and calculate the uidflags * keynode points to * a node with a user ID. mainkid has the key ID of the primary key * keyblock is the complete keyblock which is needed for signature * checking. */ static unsigned int check_uidsigs( KBNODE keyblock, KBNODE keynode, u32 *mainkid, ulong lid ) { KBNODE node; unsigned int uidflags = 0; PKT_signature *sig; PKT_signature *selfsig = NULL; /* the latest valid self signature */ int rc; + if( DBG_TRUST ) + log_debug("check_uidsigs: %08lX.%lu\n", + (ulong)mainkid[1], lid ); + /* first we check only the selfsignatures */ for( node=keynode->next; node; node = node->next ) { if( node->pkt->pkttype == PKT_USER_ID || node->pkt->pkttype == PKT_PUBLIC_SUBKEY ) break; /* ready */ if( node->pkt->pkttype != PKT_SIGNATURE ) continue; /* don't care about other packets */ sig = node->pkt->pkt.signature; if( mainkid[0] != sig->keyid[0] || mainkid[1] != sig->keyid[1] ) continue; /* we only care about self-signatures for now */ if( (sig->sig_class&~3) == 0x10 ) { /* regular self signature */ rc = check_key_signature( keyblock, node, NULL ); if( !rc ) { if( opt.verbose ) log_info( "uid %08lX.%lu: %s\n", (ulong)mainkid[1], lid, _("Good self-signature") ); uidflags |= UIDF_CHECKED | UIDF_VALID; if( !selfsig ) selfsig = sig; /* use the first valid sig */ else if( sig->timestamp > selfsig->timestamp && sig->sig_class >= selfsig->sig_class ) selfsig = sig; /* but this one is newer */ } else { log_info( "uid %08lX: %s: %s\n", (ulong)mainkid[1], _("Invalid self-signature"), g10_errstr(rc) ); uidflags |= UIDF_CHECKED; } } } /* and now check for revocations - we must do this after the * self signature check because a self-signature which is newer * than a revocation makes the revocation invalid. * Fixme: Is this correct - check with rfc2440 */ for( node=keynode->next; node; node = node->next ) { if( node->pkt->pkttype == PKT_USER_ID || node->pkt->pkttype == PKT_PUBLIC_SUBKEY ) break; /* ready */ if( node->pkt->pkttype != PKT_SIGNATURE ) continue; /* don't care about other packets */ sig = node->pkt->pkt.signature; if( mainkid[0] != sig->keyid[0] || mainkid[1] != sig->keyid[1] ) continue; /* we only care about self-signatures for now */ if( sig->sig_class == 0x30 ) { /* cert revocation */ rc = check_key_signature( keyblock, node, NULL ); if( !rc && selfsig && selfsig->timestamp > sig->timestamp ) { log_info( "uid %08lX.%lu: %s\n", (ulong)mainkid[1], lid, _("Valid user ID revocation skipped " "due to a newer self signature") ); } else if( !rc ) { if( opt.verbose ) log_info( "uid %08lX.%lu: %s\n", (ulong)mainkid[1], lid, _("Valid user ID revocation") ); uidflags |= UIDF_CHECKED | UIDF_VALID | UIDF_REVOKED; } else { log_info("uid %08lX: %s: %s\n", (ulong)mainkid[1], _("Invalid user ID revocation"), g10_errstr(rc) ); } } } return uidflags; } static unsigned int check_sig_record( KBNODE keyblock, KBNODE signode, ulong siglid, int sigidx, u32 *keyid, ulong lid ) { PKT_signature *sig = signode->pkt->pkt.signature; unsigned int sigflag = 0; TRUSTREC tmp; int revocation=0, rc; + if( DBG_TRUST ) + log_debug("check_sig_record: %08lX.%lu %lu[%d]\n", + (ulong)keyid[1], lid, siglid, sigidx ); + if( (sig->sig_class&~3) == 0x10 ) /* regular certification */ ; else if( sig->sig_class == 0x30 ) /* cert revocation */ revocation = 1; else return SIGF_CHECKED | SIGF_IGNORED; read_record( siglid, &tmp, 0 ); if( tmp.rectype == RECTYPE_DIR ) { /* the public key is in the trustdb: check sig */ rc = check_key_signature( keyblock, signode, NULL ); if( !rc ) { /* valid signature */ if( opt.verbose ) log_info("sig %08lX.%lu/%lu[%d]/%08lX: %s\n", (ulong)keyid[1], lid, siglid, sigidx, (ulong)sig->keyid[1], revocation? _("Valid certificate revocation") : _("Good certificate") ); sigflag |= SIGF_CHECKED | SIGF_VALID; if( revocation ) { sigflag |= SIGF_REVOKED; /**mod_down = 1;*/ } else /**mod_up = 1*/; } else if( rc == G10ERR_NO_PUBKEY ) { /* This may happen if the key is still in the trustdb * but not available in the keystorage */ sigflag |= SIGF_NOPUBKEY; /**mod_down = 1;*/ if( revocation ) sigflag |= SIGF_REVOKED; } else { log_info("sig %08lX.%lu/%lu[%d]/%08lX: %s: %s\n", (ulong)keyid[1], lid, siglid, sigidx, (ulong)sig->keyid[1], revocation? _("Invalid certificate revocation") : _("Invalid certificate"), g10_errstr(rc)); sigflag |= SIGF_CHECKED; if( revocation ) { sigflag |= SIGF_REVOKED; /**mod_down = 1;*/ } } } else if( tmp.rectype == RECTYPE_SDIR ) { /* better check that it is the right one */ if( tmp.r.sdir.keyid[0] == sig->keyid[0] && tmp.r.sdir.keyid[1] == sig->keyid[1] && (!tmp.r.sdir.pubkey_algo || tmp.r.sdir.pubkey_algo == sig->pubkey_algo )) sigflag |= SIGF_NOPUBKEY; else log_error(_("sig record %lu[%d] points to wrong record.\n"), siglid, sigidx ); } else { log_error(_("sig record %lu[%d] points to wrong record.\n"), siglid, sigidx ); tdbio_invalid(); } return sigflag; } /**************** * Make the sig records for the given uid record * We don't set flags here or even check the signatures; this will * happen latter. */ static ulong make_sig_records( KBNODE keyblock, KBNODE uidnode, ulong lid, u32 *mainkid ) { TRUSTREC *srecs, **s_end, *s=NULL, *s2; KBNODE node; PKT_signature *sig; ulong sigrecno, siglid; int i, sigidx = 0; srecs = NULL; s_end = &srecs; for( node=uidnode->next; node; node = node->next ) { if( node->pkt->pkttype == PKT_USER_ID || node->pkt->pkttype == PKT_PUBLIC_SUBKEY ) break; /* ready */ if( node->pkt->pkttype != PKT_SIGNATURE ) continue; /* don't care about other packets */ sig = node->pkt->pkt.signature; if( mainkid[0] == sig->keyid[0] && mainkid[1] == sig->keyid[1] ) continue; /* we don't care about self-signatures here */ siglid = find_or_create_lid( sig ); /* smash dups */ for( s2 = s; s2 ; s2 = s2->next ) { for(i=0; i < sigidx; i++ ) { if( s2->r.sig.sig[i].lid == siglid ) goto leaveduptest; } } for( s2 = srecs; s2 ; s2 = s2->next ) { for(i=0; i < SIGS_PER_RECORD; i++ ) { if( s2->r.sig.sig[i].lid == siglid ) goto leaveduptest; } } leaveduptest: if( s2 ) { log_info( "sig %08lX.%lu: %s\n", (ulong)mainkid[1], lid, _("duplicated certificate - deleted") ); continue; } /* create the sig record */ if( !sigidx ) { s = m_alloc_clear( sizeof *s ); s->rectype = RECTYPE_SIG; s->r.sig.lid = lid; } s->r.sig.sig[sigidx].lid = siglid; s->r.sig.sig[sigidx].flag= check_sig_record( keyblock, node, siglid, sigidx, mainkid, lid ); sigidx++; if( sigidx == SIGS_PER_RECORD ) { s->recnum = tdbio_new_recnum(); *s_end = s; s_end = &s->next; sigidx = 0; } } if( sigidx ) { s->recnum = tdbio_new_recnum(); *s_end = s; s_end = &s->next; } sigrecno = srecs? srecs->recnum : 0; /* write the keylist and release the memory */ for( s = srecs; s ; s = s2 ) { if( s->next ) s->r.sig.next = s->next->recnum; write_record( s ); s2 = s->next; m_free( s ); } return sigrecno; } static ulong make_uid_records( KBNODE keyblock, ulong lid, u32 *keyid ) { TRUSTREC *urecs, **uend, *u, *u2; KBNODE node; PKT_user_id *uid; byte uidhash[20]; ulong uidrecno; urecs = NULL; uend = &urecs; for( node=keyblock; node; node = node->next ) { if( node->pkt->pkttype != PKT_USER_ID ) continue; uid = node->pkt->pkt.user_id; rmd160_hash_buffer( uidhash, uid->name, uid->len ); /* create the uid record */ u = m_alloc_clear( sizeof *u ); u->rectype = RECTYPE_UID; u->r.uid.lid = lid; memcpy(u->r.uid.namehash, uidhash, 20 ); u->recnum = tdbio_new_recnum(); *uend = u; uend = &u->next; u->r.uid.uidflags = check_uidsigs( keyblock, node, keyid, lid ); if( (u->r.uid.uidflags & UIDF_CHECKED) && (u->r.uid.uidflags & UIDF_VALID) ) /*make_pref_record( &urec, keyid, selfsig )*/; /* create the list of signatures */ u->r.uid.siglist = make_sig_records( keyblock, node, lid, keyid ); } uidrecno = urecs? urecs->recnum : 0; /* write the uidlist and release the memory */ for( u = urecs; u ; u = u2 ) { if( u->next ) u->r.uid.next = u->next->recnum; write_record( u ); u2 = u->next; m_free( u ); } return uidrecno; } /**************** * Update all the info from the public keyblock. * The key must already exist in the keydb. */ int update_trust_record( KBNODE keyblock, int recheck, int *modified ) { PKT_public_key *primary_pk; KBNODE node; TRUSTREC drec, krec, urec, prec, helprec; int rc = 0; u32 keyid[2]; /* keyid of primary key */ int mod_up = 0; int mod_down = 0; ulong recno, r2; if( opt.dry_run ) return 0; init_trustdb(); if( modified ) *modified = 0; node = find_kbnode( keyblock, PKT_PUBLIC_KEY ); primary_pk = node->pkt->pkt.public_key; rc = get_dir_record( primary_pk, &drec ); if( rc ) return rc; if( !primary_pk->local_id ) primary_pk->local_id = drec.recnum; keyid_from_pk( primary_pk, keyid ); + if( DBG_TRUST ) + log_debug("update_trust_record: %08lX.%lu\n", (ulong)keyid[1], drec.recnum ); rc = tdbio_begin_transaction(); if( rc ) return rc; /* delete the old stuff */ for( recno=drec.r.dir.keylist; recno; recno = krec.r.key.next ) { read_record( recno, &krec, RECTYPE_KEY ); delete_record( recno ); } drec.r.dir.keylist = 0; for( recno=drec.r.dir.uidlist; recno; recno = urec.r.uid.next ) { read_record( recno, &urec, RECTYPE_UID ); for(r2=urec.r.uid.prefrec ; r2; r2 = prec.r.pref.next ) { read_record( r2, &prec, RECTYPE_PREF ); delete_record( r2 ); } for(r2=urec.r.uid.siglist ; r2; r2 = helprec.r.sig.next ) { read_record( r2, &helprec, RECTYPE_SIG ); delete_record( r2 ); } delete_record( recno ); } drec.r.dir.uidlist = 0; /* insert new stuff */ drec.r.dir.dirflags &= ~DIRF_REVOKED; drec.r.dir.keylist = make_key_records( keyblock, drec.recnum, keyid ); drec.r.dir.uidlist = make_uid_records( keyblock, drec.recnum, keyid ); #if 0 if( orig_uidflags != urec.r.uid.uidflags ) { write_record( &urec ); if( !( urec.r.uid.uidflags & UIDF_VALID ) || ( urec.r.uid.uidflags & UIDF_REVOKED ) ) *mod_down=1; else *mod_up=1; /*(maybe a new user id)*/ #endif /* FIXME: if the primary key has been revoked, we should set the revoked flag in the dir records */ if( rc ) rc = tdbio_cancel_transaction(); else { if( modified && tdbio_is_dirty() ) *modified = 1; drec.r.dir.dirflags |= DIRF_CHECKED; drec.r.dir.valcheck = 0; drec.r.dir.checkat = make_timestamp(); write_record( &drec ); /*tdbio_write_modify_stamp( mod_up, mod_down );*/ rc = tdbio_end_transaction(); } return rc; } /**************** * Insert a trust record into the TrustDB * This function assumes that the record does not yet exist. */ int -insert_trust_record( PKT_public_key *orig_pk ) +insert_trust_record( KBNODE keyblock ) { TRUSTREC dirrec; TRUSTREC shadow; - KBNODE keyblock = NULL; KBNODE node; - byte fingerprint[MAX_FINGERPRINT_LEN]; - size_t fingerlen; int rc = 0; PKT_public_key *pk; if( opt.dry_run ) return 0; init_trustdb(); - fingerprint_from_pk( orig_pk, fingerprint, &fingerlen ); - - /* fixme: assert that we do not have this record. - * we can do this by searching for the primary keyid - * - * fixme: If there is no such key we should look whether one - * of the subkeys has been used to sign another key and in this case - * we got the key anyway - this is because a secondary key can't be used - * without a primary key (it is needed to bind the secondary one - * to the primary one which has the user ids etc.) - */ - - if( orig_pk->local_id ) - log_debug("insert_trust_record with pk->local_id=%lu (1)\n", - orig_pk->local_id ); - - /* get the keyblock which has the key */ - rc = get_keyblock_byfprint( &keyblock, fingerprint, fingerlen ); - if( rc ) { /* that should never happen */ - log_error( _("insert_trust_record: keyblock not found: %s\n"), - g10_errstr(rc) ); - goto leave; - } - - /* make sure that we use the primary key */ pk = find_kbnode( keyblock, PKT_PUBLIC_KEY )->pkt->pkt.public_key; - if( pk->local_id ) { - orig_pk->local_id = pk->local_id; log_debug("insert_trust_record with pk->local_id=%lu (2)\n", pk->local_id ); rc = update_trust_record( keyblock, 1, NULL ); - release_kbnode( keyblock ); return rc; } /* We have to look for a shadow dir record which must be reused * as the dir record. */ rc = tdbio_search_sdir( pk->keyid, pk->pubkey_algo, &shadow ); if( rc && rc != -1 ) { log_error(_("tdbio_search_dir failed: %s\n"), g10_errstr(rc)); tdbio_invalid(); } memset( &dirrec, 0, sizeof dirrec ); dirrec.rectype = RECTYPE_DIR; if( !rc ) /* we have a shadow dir record - convert to dir record */ dirrec.recnum = shadow.recnum; else dirrec.recnum = tdbio_new_recnum(); dirrec.r.dir.lid = dirrec.recnum; write_record( &dirrec ); - /* put the LID into the in-memory copy of the keyblock */ + /* put the LID into the keyblock */ pk->local_id = dirrec.r.dir.lid; - orig_pk->local_id = dirrec.r.dir.lid; for( node=keyblock; node; node = node->next ) { if( node->pkt->pkttype == PKT_PUBLIC_KEY || node->pkt->pkttype == PKT_PUBLIC_SUBKEY ) { PKT_public_key *a_pk = node->pkt->pkt.public_key; a_pk->local_id = dirrec.r.dir.lid; } else if( node->pkt->pkttype == PKT_SIGNATURE ) { PKT_signature *a_sig = node->pkt->pkt.signature; a_sig->local_id = dirrec.r.dir.lid; } } /* mark tdb as modified upwards */ tdbio_write_modify_stamp( 1, 0 ); /* and put all the other stuff into the keydb */ rc = update_trust_record( keyblock, 1, NULL ); - leave: - release_kbnode( keyblock ); do_sync(); return rc; } +/**************** + * Insert a trust record indentified by a PK into the TrustDB + */ +int +insert_trust_record_by_pk( PKT_public_key *pk ) +{ + KBNODE keyblock = NULL; + byte fingerprint[MAX_FINGERPRINT_LEN]; + size_t fingerlen; + int rc; + + /* get the keyblock */ + fingerprint_from_pk( pk, fingerprint, &fingerlen ); + rc = get_keyblock_byfprint( &keyblock, fingerprint, fingerlen ); + if( rc ) { /* that should never happen */ + log_debug( "insert_trust_record_by_pk: keyblock not found: %s\n", + g10_errstr(rc) ); + } + else { + rc = insert_trust_record( keyblock ); + if( !rc ) /* copy the LID into the PK */ + pk->local_id = find_kbnode( keyblock, PKT_PUBLIC_KEY ) + ->pkt->pkt.public_key->local_id; + } + + release_kbnode( keyblock ); + return rc; +} + + /**************** * Walk over the keyrings and create trustdb records for all keys * It is intended to be used after a fast-import operation. */ void update_trustdb() { KBNODE keyblock = NULL; KBPOS kbpos; int rc; if( opt.dry_run ) return; init_trustdb(); rc = enum_keyblocks( 0, &kbpos, &keyblock ); if( !rc ) { ulong count=0, upd_count=0, err_count=0, new_count=0; while( !(rc = enum_keyblocks( 1, &kbpos, &keyblock )) ) { int modified; rc = update_trust_record( keyblock, 1, &modified ); if( rc == -1 ) { /* not yet in trustdb: insert */ - PKT_public_key *pk = keyblock->pkt->pkt.public_key; - rc = insert_trust_record( pk ); + PKT_public_key *pk; + rc = insert_trust_record( keyblock ); + pk = keyblock->pkt->pkt.public_key; if( rc && !pk->local_id ) { log_error(_("lid ?: insert failed: %s\n"), g10_errstr(rc) ); err_count++; } else if( rc ) { log_error(_("lid %lu: insert failed: %s\n"), pk->local_id, g10_errstr(rc) ); err_count++; } else { if( opt.verbose ) log_info(_("lid %lu: inserted\n"), pk->local_id ); new_count++; } } else if( rc ) { log_error(_("lid %lu: update failed: %s\n"), lid_from_keyblock(keyblock), g10_errstr(rc) ); err_count++; } else if( modified ) { if( opt.verbose ) log_info(_("lid %lu: updated\n"), lid_from_keyblock(keyblock)); upd_count++; } else if( opt.verbose > 1 ) log_info(_("lid %lu: okay\n"), lid_from_keyblock(keyblock) ); release_kbnode( keyblock ); keyblock = NULL; if( !(++count % 100) ) log_info(_("%lu keys so far processed\n"), count); } log_info(_("%lu keys processed\n"), count); if( err_count ) log_info(_("\t%lu keys with errors\n"), err_count); if( upd_count ) log_info(_("\t%lu keys updated\n"), upd_count); if( new_count ) log_info(_("\t%lu keys inserted\n"), new_count); } if( rc && rc != -1 ) log_error(_("enumerate keyblocks failed: %s\n"), g10_errstr(rc)); enum_keyblocks( 2, &kbpos, &keyblock ); /* close */ release_kbnode( keyblock ); } /**************** * Do all required check in the trustdb. This function walks over all * records in the trustdb and does scheduled processing. */ void check_trustdb( const char *username ) { TRUSTREC rec; ulong recnum; ulong count=0, upd_count=0, err_count=0, skip_count=0; ulong current_time = make_timestamp(); if( username ) log_info("given user IDs ignored in check_trustdb\n"); init_trustdb(); for(recnum=0; !tdbio_read_record( recnum, &rec, 0); recnum++ ) { if( rec.rectype != RECTYPE_DIR ) continue; /* we only want the dir records */ if( count && !(count % 100) && !opt.quiet ) log_info(_("%lu keys so far processed\n"), count); count++; if( !rec.r.dir.checkat || rec.r.dir.checkat > current_time ) { skip_count++; continue; /* not scheduled for checking */ } if( !rec.r.dir.keylist ) { log_info(_("lid %lu: dir record w/o key - skipped\n"), recnum); skip_count++; continue; } } log_info(_("%lu keys processed\n"), count); if( skip_count ) log_info(_("\t%lu keys skipped\n"), skip_count); if( err_count ) log_info(_("\t%lu keys with errors\n"), err_count); if( upd_count ) log_info(_("\t%lu keys updated\n"), upd_count); } /*********************************************** ********* Trust calculation ***************** ***********************************************/ /**************** * Find all certification paths of a given LID. * Limit the search to MAX_DEPTH. stack is a helper variable which * should have been allocated with size max_depth, stack[0] should * be setup to the key we are investigating, so the minimal depth * we should ever see in this function is 1. * Returns: a new tree * certchain_set must be a valid set or point to NULL; this function * may modifiy it. * * Fixme: add a fastscan mode which stops ad valid validity nodes. */ static TN build_cert_tree( ulong lid, int depth, int max_depth, TN helproot ) { TRUSTREC dirrec; TRUSTREC uidrec; ulong uidrno; TN keynode; if( depth >= max_depth ) return NULL; keynode = new_tn(); if( !helproot ) helproot = keynode; keynode->lid = lid; if( !qry_lid_table_flag( ultikey_table, lid, NULL ) ) { /* this is an ultimately trusted key; * which means that we have found the end of the chain: * We do this here prior to reading the dir record * because we don't really need the info from that record */ keynode->n.k.ownertrust = TRUST_ULTIMATE; keynode->n.k.buckstop = 1; return keynode; } read_record( lid, &dirrec, 0 ); if( dirrec.rectype != RECTYPE_DIR ) { if( dirrec.rectype != RECTYPE_SDIR ) log_debug("lid %lu, has rectype %d" " - skipped\n", lid, dirrec.rectype ); m_free(keynode); return NULL; } - keynode->n.k.ownertrust = dirrec.r.dir.ownertrust; + keynode->n.k.ownertrust = dirrec.r.dir.ownertrust & TRUST_MASK; /* loop over all user ids */ for( uidrno = dirrec.r.dir.uidlist; uidrno; uidrno = uidrec.r.uid.next ) { TRUSTREC sigrec; ulong sigrno; TN uidnode = NULL; read_record( uidrno, &uidrec, RECTYPE_UID ); if( !(uidrec.r.uid.uidflags & UIDF_CHECKED) ) continue; /* user id has not been checked */ if( !(uidrec.r.uid.uidflags & UIDF_VALID) ) continue; /* user id is not valid */ if( (uidrec.r.uid.uidflags & UIDF_REVOKED) ) continue; /* user id has been revoked */ /* loop over all signature records */ for(sigrno=uidrec.r.uid.siglist; sigrno; sigrno = sigrec.r.sig.next ) { int i; TN tn; read_record( sigrno, &sigrec, RECTYPE_SIG ); for(i=0; i < SIGS_PER_RECORD; i++ ) { if( !sigrec.r.sig.sig[i].lid ) continue; /* skip deleted sigs */ if( !(sigrec.r.sig.sig[i].flag & SIGF_CHECKED) ) continue; /* skip unchecked signatures */ if( !(sigrec.r.sig.sig[i].flag & SIGF_VALID) ) continue; /* skip invalid signatures */ if( (sigrec.r.sig.sig[i].flag & SIGF_EXPIRED) ) continue; /* skip expired signatures */ if( (sigrec.r.sig.sig[i].flag & SIGF_REVOKED) ) continue; /* skip revoked signatures */ /* check for cycles */ for( tn=keynode; tn && tn->lid != sigrec.r.sig.sig[i].lid; tn = tn->back ) ; if( tn ) continue; /* cycle found */ tn = build_cert_tree( sigrec.r.sig.sig[i].lid, depth+1, max_depth, helproot ); if( !tn ) continue; /* cert chain too deep or error */ if( !uidnode ) { uidnode = new_tn(); uidnode->back = keynode; uidnode->lid = uidrno; uidnode->is_uid = 1; uidnode->next = keynode->list; keynode->list = uidnode; } tn->back = uidnode; tn->next = uidnode->list; uidnode->list = tn; #if 0 /* optimazation - fixme: reenable this later */ if( tn->n.k.buckstop ) { /* ultimately trusted key found: * no need to check more signatures of this uid */ sigrec.r.sig.next = 0; break; } #endif } } /* end loop over sig recs */ } /* end loop over user ids */ if( !keynode->list ) { release_tn_tree( keynode ); keynode = NULL; } return keynode; } static void upd_one_ownertrust( ulong lid, unsigned new_trust, unsigned *retflgs ) { TRUSTREC rec; read_record( lid, &rec, RECTYPE_DIR ); if( DBG_TRUST ) log_debug("upd_one_ownertrust of %lu from %u to %u\n", lid, (unsigned)rec.r.dir.ownertrust, new_trust ); if( retflgs ) { - if( new_trust > rec.r.dir.ownertrust ) + if( (new_trust & TRUST_MASK) > (rec.r.dir.ownertrust & TRUST_MASK) ) *retflgs |= 16; /* modified up */ else *retflgs |= 32; /* modified down */ } - rec.r.dir.ownertrust = new_trust; + + /* we preserve the disabled state here */ + if( (rec.r.dir.ownertrust & TRUST_FLAG_DISABLED) ) + rec.r.dir.ownertrust = new_trust | TRUST_FLAG_DISABLED; + else + rec.r.dir.ownertrust = new_trust & ~TRUST_FLAG_DISABLED; write_record( &rec ); } /**************** * Update the ownertrust in the complete tree. */ static void propagate_ownertrust( TN kr, ulong lid, unsigned trust ) { TN ur; for( ; kr; kr = kr->next ) { if( kr->lid == lid ) kr->n.k.ownertrust = trust; for( ur=kr->list; ur; ur = ur->next ) propagate_ownertrust( ur->list, lid, trust ); } } /**************** * Calculate the validity of all keys in the tree and especially * the one of the top key. If add_fnc is not NULL, it is used to * ask for missing ownertrust values (but only if this will help * us to increase the validity. * add_fnc is expected to take the LID of the key under question * and return a ownertrust value or an error: positive values * are assumed to be the new ownertrust value; a 0 does mean no change, * a -1 is a request to cancel this validation procedure, a -2 requests * a listing of the sub-tree using the tty functions. * * * Returns: 0 = okay */ static int propagate_validity( TN root, TN node, int (*add_fnc)(ulong), unsigned *retflgs ) { TN kr, ur; int max_validity = 0; assert( !node->is_uid ); if( node->n.k.ownertrust == TRUST_ULTIMATE ) { /* this is one of our keys */ assert( !node->list ); /* it should be a leaf */ node->n.k.validity = TRUST_ULTIMATE; if( retflgs ) *retflgs |= 1; /* found a path to an ultimately trusted key */ return 0; } /* loop over all user ids */ for( ur=node->list; ur; ur = ur->next ) { assert( ur->is_uid ); /* loop over all signators */ for(kr=ur->list; kr; kr = kr->next ) { if( propagate_validity( root, kr, add_fnc, retflgs ) ) return -1; /* quit */ if( kr->n.k.validity == TRUST_ULTIMATE ) { ur->n.u.fully_count = opt.completes_needed; } else if( kr->n.k.validity == TRUST_FULLY ) { if( add_fnc && !kr->n.k.ownertrust ) { int rc; if( retflgs ) *retflgs |= 2; /* found key with undefined ownertrust*/ do { rc = add_fnc( kr->lid ); switch( rc ) { case TRUST_NEVER: case TRUST_MARGINAL: case TRUST_FULLY: propagate_ownertrust( root, kr->lid, rc ); upd_one_ownertrust( kr->lid, rc, retflgs ); if( retflgs ) *retflgs |= 4; /* changed */ break; case -1: return -1; /* cancel */ case -2: dump_tn_tree( NULL, 0, kr ); tty_printf("\n"); break; default: break; } } while( rc == -2 ); } if( kr->n.k.ownertrust == TRUST_FULLY ) ur->n.u.fully_count++; else if( kr->n.k.ownertrust == TRUST_MARGINAL ) ur->n.u.marginal_count++; } } /* fixme: We can move this test into the loop to stop as soon as * we have a level of FULLY and return from this function * We dont do this now to get better debug output */ if( ur->n.u.fully_count >= opt.completes_needed || ur->n.u.marginal_count >= opt.marginals_needed ) ur->n.u.validity = TRUST_FULLY; else if( ur->n.u.fully_count || ur->n.u.marginal_count ) ur->n.u.validity = TRUST_MARGINAL; if( ur->n.u.validity >= max_validity ) max_validity = ur->n.u.validity; } node->n.k.validity = max_validity; return 0; } /**************** * Given the directory record of a key, check whether we can * find a path to an ultimately trusted key. We do this by * checking all key signatures up to a some depth. */ static int verify_key( int max_depth, TRUSTREC *drec, const char *namehash, int (*add_fnc)(ulong), unsigned *retflgs ) { TN tree; int keytrust; int pv_result; tree = build_cert_tree( drec->r.dir.lid, 0, opt.max_cert_depth, NULL ); if( !tree ) return TRUST_UNDEFINED; pv_result = propagate_validity( tree, tree, add_fnc, retflgs ); if( namehash ) { /* find the matching user id. * fixme: the way we handle this is too inefficient */ TN ur; TRUSTREC rec; keytrust = 0; for( ur=tree->list; ur; ur = ur->next ) { read_record( ur->lid, &rec, RECTYPE_UID ); if( !memcmp( namehash, rec.r.uid.namehash, 20 ) ) { keytrust = ur->n.u.validity; break; } } } else keytrust = tree->n.k.validity; /* update the cached validity values */ if( !pv_result && keytrust >= TRUST_UNDEFINED && tdbio_db_matches_options() && ( !drec->r.dir.valcheck || drec->r.dir.validity != keytrust ) ) { TN ur; TRUSTREC rec; for( ur=tree->list; ur; ur = ur->next ) { read_record( ur->lid, &rec, RECTYPE_UID ); if( rec.r.uid.validity != ur->n.u.validity ) { rec.r.uid.validity = ur->n.u.validity; write_record( &rec ); } } drec->r.dir.validity = tree->n.k.validity; drec->r.dir.valcheck = make_timestamp(); write_record( drec ); do_sync(); } release_tn_tree( tree ); return keytrust; } /**************** * we have the pubkey record and all needed informations are in the trustdb * but nothing more is known. */ static int do_check( TRUSTREC *dr, unsigned *validity, const char *namehash, int (*add_fnc)(ulong), unsigned *retflgs ) { if( !dr->r.dir.keylist ) { log_error(_("Ooops, no keys\n")); return G10ERR_TRUSTDB; } if( !dr->r.dir.uidlist ) { log_error(_("Ooops, no user ids\n")); return G10ERR_TRUSTDB; } if( retflgs ) *retflgs &= ~(16|32); /* reset the 2 special flags */ - - if( namehash ) { + if( (dr->r.dir.ownertrust & TRUST_FLAG_DISABLED) ) + *validity = 0; /* no need to check further */ + else if( namehash ) { /* Fixme: use the cache */ *validity = verify_key( opt.max_cert_depth, dr, namehash, add_fnc, retflgs ); } else if( !add_fnc && tdbio_db_matches_options() && dr->r.dir.valcheck > tdbio_read_modify_stamp( (dr->r.dir.validity < TRUST_FULLY) ) && dr->r.dir.validity ) *validity = dr->r.dir.validity; else *validity = verify_key( opt.max_cert_depth, dr, NULL, add_fnc, retflgs ); if( !(*validity & TRUST_MASK) ) *validity = TRUST_UNDEFINED; + if( (dr->r.dir.ownertrust & TRUST_FLAG_DISABLED) ) + *validity |= TRUST_FLAG_DISABLED; + if( dr->r.dir.dirflags & DIRF_REVOKED ) *validity |= TRUST_FLAG_REVOKED; /* If we have changed some ownertrusts, set the trustdb timestamps * and do a sync */ if( retflgs && (*retflgs & (16|32)) ) { tdbio_write_modify_stamp( (*retflgs & 16), (*retflgs & 32) ); do_sync(); } return 0; } /*********************************************** ********* Change trustdb values ************** ***********************************************/ int update_ownertrust( ulong lid, unsigned new_trust ) { TRUSTREC rec; init_trustdb(); read_record( lid, &rec, RECTYPE_DIR ); if( DBG_TRUST ) log_debug("update_ownertrust of %lu from %u to %u\n", lid, (unsigned)rec.r.dir.ownertrust, new_trust ); rec.r.dir.ownertrust = new_trust; write_record( &rec ); do_sync(); return 0; } int clear_trust_checked_flag( PKT_public_key *pk ) { TRUSTREC rec; int rc; if( opt.dry_run ) return 0; init_trustdb(); rc = get_dir_record( pk, &rec ); if( rc ) return rc; /* check whether they are already reset */ if( !(rec.r.dir.dirflags & DIRF_CHECKED) && !rec.r.dir.valcheck ) return 0; /* reset the flag */ rec.r.dir.dirflags &= ~DIRF_CHECKED; rec.r.dir.valcheck = 0; write_record( &rec ); do_sync(); return 0; } /*********************************************** ********* Query trustdb values ************** ***********************************************/ /**************** * This function simply looks for the key in the trustdb * and makes sure that pk->local_id is set to the correct value. * Return: 0 = found * -1 = not found * other = error */ int query_trust_record( PKT_public_key *pk ) { TRUSTREC rec; init_trustdb(); return get_dir_record( pk, &rec ); } /**************** * Get the trustlevel for this PK. * Note: This does not ask any questions * Returns: 0 okay of an errorcode * * It operates this way: * locate the pk in the trustdb * found: * Do we have a valid cache record for it? * yes: return trustlevel from cache * no: make a cache record and all the other stuff * not found: * try to insert the pubkey into the trustdb and check again * * Problems: How do we get the complete keyblock to check that the * cache record is actually valid? Think we need a clever * cache in getkey.c to keep track of this stuff. Maybe it * is not necessary to check this if we use a local pubring. Hmmmm. */ int check_trust( PKT_public_key *pk, unsigned *r_trustlevel, const byte *namehash, int (*add_fnc)(ulong), unsigned *retflgs ) { TRUSTREC rec; unsigned trustlevel = TRUST_UNKNOWN; int rc=0; u32 cur_time; u32 keyid[2]; init_trustdb(); keyid_from_pk( pk, keyid ); /* get the pubkey record */ if( pk->local_id ) { read_record( pk->local_id, &rec, RECTYPE_DIR ); } else { /* no local_id: scan the trustdb */ if( (rc=tdbio_search_dir_bypk( pk, &rec )) && rc != -1 ) { log_error(_("check_trust: search dir record failed: %s\n"), g10_errstr(rc)); return rc; } else if( rc == -1 ) { /* not found - insert */ - rc = insert_trust_record( pk ); + rc = insert_trust_record_by_pk( pk ); if( rc ) { log_error(_("key %08lX: insert trust record failed: %s\n"), (ulong)keyid[1], g10_errstr(rc)); goto leave; } log_info(_("key %08lX.%lu: inserted into trustdb\n"), (ulong)keyid[1], pk->local_id ); /* and re-read the dir record */ read_record( pk->local_id, &rec, RECTYPE_DIR ); } } cur_time = make_timestamp(); if( pk->timestamp > cur_time ) { log_info(_("key %08lX.%lu: created in future " "(time warp or clock problem)\n"), (ulong)keyid[1], pk->local_id ); return G10ERR_TIME_CONFLICT; } if( pk->expiredate && pk->expiredate <= cur_time ) { log_info(_("key %08lX.%lu: expired at %s\n"), (ulong)keyid[1], pk->local_id, asctimestamp( pk->expiredate) ); - trustlevel = TRUST_EXPIRED; + trustlevel = TRUST_EXPIRED; } else { rc = do_check( &rec, &trustlevel, namehash, add_fnc, retflgs ); if( rc ) { log_error(_("key %08lX.%lu: trust check failed: %s\n"), (ulong)keyid[1], pk->local_id, g10_errstr(rc)); return rc; } } /* is a subkey has been requested, we have to check its keyflags */ if( !rc ) { TRUSTREC krec; byte fpr[MAX_FINGERPRINT_LEN] = {0}; /* to avoid compiler warnings */ size_t fprlen = 0; ulong recno; int kcount=0; for( recno = rec.r.dir.keylist; recno; recno = krec.r.key.next ) { read_record( recno, &krec, RECTYPE_KEY ); if( ++kcount == 1 ) continue; /* skip the primary key */ if( kcount == 2 ) /* now we need the fingerprint */ fingerprint_from_pk( pk, fpr, &fprlen ); if( krec.r.key.fingerprint_len == fprlen && !memcmp( krec.r.key.fingerprint, fpr, fprlen ) ) { /* found the subkey */ if( (krec.r.key.keyflags & KEYF_REVOKED) ) trustlevel |= TRUST_FLAG_SUB_REVOKED; /* should we check for keybinding here??? */ /* Hmmm: Maybe this whole checking stuff should not go * into the trustdb, but be done direct from the keyblock. * Chnage this all when we add an abstarction layer around * the way certificates are handled by different standards */ break; } } } leave: if( DBG_TRUST ) log_debug("check_trust() returns trustlevel %04x.\n", trustlevel); *r_trustlevel = trustlevel; return 0; } int query_trust_info( PKT_public_key *pk, const byte *namehash ) { unsigned trustlevel; int c; init_trustdb(); if( check_trust( pk, &trustlevel, namehash, NULL, NULL ) ) return '?'; if( trustlevel & TRUST_FLAG_DISABLED ) return 'd'; if( trustlevel & TRUST_FLAG_REVOKED ) return 'r'; c = trust_letter( (trustlevel & TRUST_MASK) ); if( !c ) c = '?'; return c; } /**************** * Return the assigned ownertrust value for the given LID */ unsigned get_ownertrust( ulong lid ) { TRUSTREC rec; init_trustdb(); read_record( lid, &rec, RECTYPE_DIR ); return rec.r.dir.ownertrust; } int get_ownertrust_info( ulong lid ) { unsigned otrust; int c; init_trustdb(); otrust = get_ownertrust( lid ); c = trust_letter( (otrust & TRUST_MASK) ); if( !c ) c = '?'; return c; } void list_trust_path( const char *username ) { int rc; ulong lid; TRUSTREC rec; TN tree; PKT_public_key *pk = m_alloc_clear( sizeof *pk ); init_trustdb(); if( (rc = get_pubkey_byname(NULL, pk, username, 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"), username, g10_errstr(rc)); else if( rc == -1 ) { log_info(_("user '%s' not in trustdb - inserting\n"), username); - rc = insert_trust_record( pk ); + rc = insert_trust_record_by_pk( pk ); if( rc ) log_error(_("failed to put '%s' into trustdb: %s\n"), username, g10_errstr(rc)); else { assert( pk->local_id ); } } lid = pk->local_id; tree = build_cert_tree( lid, 0, opt.max_cert_depth, NULL ); if( tree ) propagate_validity( tree, tree, NULL, NULL ); if( opt.with_colons ) dump_tn_tree_with_colons( 0, tree ); else dump_tn_tree( stdout, 0, tree ); /*printf("(alloced tns=%d max=%d)\n", alloced_tns, max_alloced_tns );*/ release_tn_tree( tree ); /*printf("Ownertrust=%c Validity=%c\n", get_ownertrust_info( lid ), query_trust_info( pk, NULL ) ); */ free_public_key( pk ); } /**************** * Enumerate all keys, which are needed to build all trust paths for * the given key. This function does not return the key itself or * the ultimate key (the last point in cerificate chain). Only * certificate chains which ends up at an ultimately trusted key * are listed. If ownertrust or validity is not NULL, the corresponding * value for the returned LID is also returned in these variable(s). * * 1) create a void pointer and initialize it to NULL * 2) pass this void pointer by reference to this function. * Set lid to the key you want to enumerate and pass it by reference. * 3) call this function as long as it does not return -1 * to indicate EOF. LID does contain the next key used to build the web * 4) Always call this function a last time with LID set to NULL, * so that it can free its context. * * Returns: -1 on EOF or the level of the returned LID */ int enum_cert_paths( void **context, ulong *lid, unsigned *ownertrust, unsigned *validity ) { return -1; #if 0 struct enum_cert_paths_ctx *ctx; fixme: ..... tsl; init_trustdb(); if( !lid ) { /* release the context */ if( *context ) { FIXME: ........tsl2; ctx = *context; for(tsl = ctx->tsl_head; tsl; tsl = tsl2 ) { tsl2 = tsl->next; m_free( tsl ); } *context = NULL; } return -1; } if( !*context ) { FIXME .... *tmppath; TRUSTREC rec; if( !*lid ) return -1; ctx = m_alloc_clear( sizeof *ctx ); *context = ctx; /* collect the paths */ #if 0 read_record( *lid, &rec, RECTYPE_DIR ); tmppath = m_alloc_clear( (opt.max_cert_depth+1)* sizeof *tmppath ); tsl = NULL; collect_paths( 0, opt.max_cert_depth, 1, &rec, tmppath, &tsl ); m_free( tmppath ); sort_tsl_list( &tsl ); #endif /* setup the context */ ctx->tsl_head = tsl; ctx->tsl = ctx->tsl_head; ctx->idx = 0; } else ctx = *context; while( ctx->tsl && ctx->idx >= ctx->tsl->pathlen ) { ctx->tsl = ctx->tsl->next; ctx->idx = 0; } tsl = ctx->tsl; if( !tsl ) return -1; /* eof */ if( ownertrust ) *ownertrust = tsl->path[ctx->idx].otrust; if( validity ) *validity = tsl->path[ctx->idx].trust; *lid = tsl->path[ctx->idx].lid; ctx->idx++; return ctx->idx-1; #endif } /**************** * Print the current path */ void enum_cert_paths_print( void **context, FILE *fp, int refresh, ulong selected_lid ) { return; #if 0 struct enum_cert_paths_ctx *ctx; FIXME......... tsl; if( !*context ) return; init_trustdb(); ctx = *context; if( !ctx->tsl ) return; tsl = ctx->tsl; if( !fp ) fp = stderr; if( refresh ) { /* update the ownertrust and if possible the validity */ int i; int match = tdbio_db_matches_options(); for( i = 0; i < tsl->pathlen; i++ ) { TRUSTREC rec; read_record( tsl->path[i].lid, &rec, RECTYPE_DIR ); tsl->path[i].otrust = rec.r.dir.ownertrust; /* update validity only if we have it in the cache * calculation is too time consuming */ if( match && rec.r.dir.valcheck && rec.r.dir.validity ) { tsl->path[i].trust = rec.r.dir.validity; if( rec.r.dir.dirflags & DIRF_REVOKED ) tsl->path[i].trust = TRUST_FLAG_REVOKED; } } } print_path( tsl->pathlen, tsl->path, fp, selected_lid ); #endif } /* * Return an allocated buffer with the preference values for * the key with LID and the userid which is identified by the * HAMEHASH or the firstone if namehash is NULL. ret_n receives * the length of the allocated buffer. Structure of the buffer is * a repeated sequences of 2 bytes; where the first byte describes the * type of the preference and the second one the value. The constants * PREFTYPE_xxxx should be used to reference a type. */ byte * get_pref_data( ulong lid, const byte *namehash, size_t *ret_n ) { TRUSTREC rec; ulong recno; init_trustdb(); read_record( lid, &rec, RECTYPE_DIR ); for( recno=rec.r.dir.uidlist; recno; recno = rec.r.uid.next ) { read_record( recno, &rec, RECTYPE_UID ); if( rec.r.uid.prefrec && ( !namehash || !memcmp(namehash, rec.r.uid.namehash, 20) )) { byte *buf; /* found the correct one or the first one */ read_record( rec.r.uid.prefrec, &rec, RECTYPE_PREF ); if( rec.r.pref.next ) log_info(_("WARNING: can't yet handle long pref records\n")); buf = m_alloc( ITEMS_PER_PREF_RECORD ); memcpy( buf, rec.r.pref.data, ITEMS_PER_PREF_RECORD ); *ret_n = ITEMS_PER_PREF_RECORD; return buf; } } return NULL; } /**************** * Check whether the algorithm is in one of the pref records */ int is_algo_in_prefs( ulong lid, int preftype, int algo ) { TRUSTREC rec; ulong recno; int i; byte *pref; init_trustdb(); read_record( lid, &rec, RECTYPE_DIR ); for( recno=rec.r.dir.uidlist; recno; recno = rec.r.uid.next ) { read_record( recno, &rec, RECTYPE_UID ); if( rec.r.uid.prefrec ) { read_record( rec.r.uid.prefrec, &rec, RECTYPE_PREF ); if( rec.r.pref.next ) log_info(_("WARNING: can't yet handle long pref records\n")); pref = rec.r.pref.data; for(i=0; i+1 < ITEMS_PER_PREF_RECORD; i+=2 ) { if( pref[i] == preftype && pref[i+1] == algo ) return 1; } } } return 0; } diff --git a/g10/trustdb.h b/g10/trustdb.h index 9f3e4c35f..6396fde45 100644 --- a/g10/trustdb.h +++ b/g10/trustdb.h @@ -1,80 +1,81 @@ /* trustdb.h - Trust database * Copyright (C) 1998 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 G10_TRUSTDB_H #define G10_TRUSTDB_H /* Trust values must be sorted in ascending order */ #define TRUST_MASK 15 #define TRUST_UNKNOWN 0 /* o: not yet calculated */ #define TRUST_EXPIRED 1 /* e: calculation may be invalid */ #define TRUST_UNDEFINED 2 /* q: not enough information for calculation */ #define TRUST_NEVER 3 /* n: never trust this pubkey */ #define TRUST_MARGINAL 4 /* m: marginally trusted */ #define TRUST_FULLY 5 /* f: fully trusted */ #define TRUST_ULTIMATE 6 /* u: ultimately trusted */ /* trust values not covered by the mask */ #define TRUST_FLAG_REVOKED 32 /* r: revoked */ #define TRUST_FLAG_SUB_REVOKED 64 #define TRUST_FLAG_DISABLED 128 /* d: key/uid disabled */ #define PREFTYPE_SYM 1 #define PREFTYPE_HASH 2 #define PREFTYPE_COMPR 3 /*-- trustdb.c --*/ void list_trust_path( const char *username ); void register_trusted_key( const char *string ); void check_trustdb( const char *username ); void update_trustdb( void ); int setup_trustdb( int level, const char *dbname ); void init_trustdb( void ); int check_trust( PKT_public_key *pk, unsigned *r_trustlevel, const byte* nh, int (*add_fnc)(ulong), unsigned *retflgs ); int query_trust_info( PKT_public_key *pk, const byte *nh ); int enum_cert_paths( void **context, ulong *lid, unsigned *ownertrust, unsigned *validity ); void enum_cert_paths_print( void **context, FILE *fp, int refresh, ulong selected_lid ); unsigned get_ownertrust( ulong lid ); int get_ownertrust_info( ulong lid ); byte *get_pref_data( ulong lid, const byte *namehash, size_t *ret_n ); int is_algo_in_prefs( ulong lid, int preftype, int algo ); int keyid_from_lid( ulong lid, u32 *keyid ); ulong lid_from_keyblock( KBNODE keyblock ); int query_trust_record( PKT_public_key *pk ); int clear_trust_checked_flag( PKT_public_key *pk ); -int insert_trust_record( PKT_public_key *pk ); int update_trust_record( KBNODE keyblock, int fast, int *modified ); +int insert_trust_record( KBNODE keyblock ); +int insert_trust_record_by_pk( PKT_public_key *pk ); int update_ownertrust( ulong lid, unsigned new_trust ); int trust_letter( unsigned value ); /*-- tdbdump.c --*/ void list_trustdb(const char *username); void export_ownertrust(void); void import_ownertrust(const char *fname); /*-- pkclist.c --*/ int edit_ownertrust( ulong lid, int mode ); #endif /*G10_TRUSTDB_H*/ diff --git a/include/util.h b/include/util.h index d266af412..b36e11bb2 100644 --- a/include/util.h +++ b/include/util.h @@ -1,214 +1,217 @@ /* util.h * Copyright (C) 1998 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 G10_UTIL_H #define G10_UTIL_H #include "types.h" #include "errors.h" #include "types.h" #include "mpi.h" typedef struct { int *argc; /* pointer to argc (value subject to change) */ char ***argv; /* pointer to argv (value subject to change) */ unsigned flags; /* Global flags (DO NOT CHANGE) */ int err; /* print error about last option */ /* 1 = warning, 2 = abort */ int r_opt; /* return option */ int r_type; /* type of return value (0 = no argument found)*/ union { int ret_int; long ret_long; ulong ret_ulong; char *ret_str; } r; /* Return values */ struct { int idx; int inarg; int stopped; const char *last; void *aliases; const void *cur_alias; } internal; /* DO NOT CHANGE */ } ARGPARSE_ARGS; typedef struct { int short_opt; const char *long_opt; unsigned flags; const char *description; /* optional option description */ } ARGPARSE_OPTS; /*-- logger.c --*/ void log_set_logfile( const char *name, int fd ); FILE *log_stream(void); void log_set_name( const char *name ); const char *log_get_name(void); void log_set_pid( int pid ); int log_get_errorcount( int clear ); void g10_log_hexdump( const char *text, const char *buf, size_t len ); void g10_log_mpidump( const char *text, MPI a ); #if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 5 ) void g10_log_bug( const char *fmt, ... ) __attribute__ ((noreturn, format (printf,1,2))); void g10_log_bug0( const char *, int, const char * ) __attribute__ ((noreturn)); void g10_log_fatal( const char *fmt, ... ) __attribute__ ((noreturn, format (printf,1,2))); void g10_log_error( const char *fmt, ... ) __attribute__ ((format (printf,1,2))); void g10_log_info( const char *fmt, ... ) __attribute__ ((format (printf,1,2))); void g10_log_debug( const char *fmt, ... ) __attribute__ ((format (printf,1,2))); void g10_log_fatal_f( const char *fname, const char *fmt, ... ) __attribute__ ((noreturn, format (printf,2,3))); void g10_log_error_f( const char *fname, const char *fmt, ... ) __attribute__ ((format (printf,2,3))); void g10_log_info_f( const char *fname, const char *fmt, ... ) __attribute__ ((format (printf,2,3))); void g10_log_debug_f( const char *fname, const char *fmt, ... ) __attribute__ ((format (printf,2,3))); #define BUG() g10_log_bug0( __FILE__ , __LINE__, __FUNCTION__ ) #else void g10_log_bug( const char *fmt, ... ); void g10_log_bug0( const char *, int ); void g10_log_fatal( const char *fmt, ... ); void g10_log_error( const char *fmt, ... ); void g10_log_info( const char *fmt, ... ); void g10_log_debug( const char *fmt, ... ); void g10_log_fatal_f( const char *fname, const char *fmt, ... ); void g10_log_error_f( const char *fname, const char *fmt, ... ); void g10_log_info_f( const char *fname, const char *fmt, ... ); void g10_log_debug_f( const char *fname, const char *fmt, ... ); #define BUG() g10_log_bug0( __FILE__ , __LINE__ ) #endif #define log_hexdump g10_log_hexdump #define log_mpidump g10_log_mpidump #define log_bug g10_log_bug #define log_bug0 g10_log_bug0 #define log_fatal g10_log_fatal #define log_error g10_log_error #define log_info g10_log_info #define log_debug g10_log_debug #define log_fatal_f g10_log_fatal_f #define log_error_f g10_log_error_f #define log_info_f g10_log_info_f #define log_debug_f g10_log_debug_f /*-- errors.c --*/ const char * g10_errstr( int no ); /*-- argparse.c --*/ int arg_parse( ARGPARSE_ARGS *arg, ARGPARSE_OPTS *opts); int optfile_parse( FILE *fp, const char *filename, unsigned *lineno, ARGPARSE_ARGS *arg, ARGPARSE_OPTS *opts); void usage( int level ); const char *default_strusage( int level ); /*-- (main program) --*/ const char *strusage( int level ); /*-- dotlock.c --*/ struct dotlock_handle; typedef struct dotlock_handle *DOTLOCK; DOTLOCK create_dotlock( const char *file_to_lock ); int make_dotlock( DOTLOCK h, long timeout ); int release_dotlock( DOTLOCK h ); /*-- fileutil.c --*/ char * make_basename(const char *filepath); char * make_dirname(const char *filepath); char *make_filename( const char *first_part, ... ); int compare_filenames( const char *a, const char *b ); const char *print_fname_stdin( const char *s ); const char *print_fname_stdout( const char *s ); /*-- miscutil.c --*/ u32 make_timestamp(void); u32 add_days_to_timestamp( u32 stamp, u16 days ); const char *strtimevalue( u32 stamp ); const char *strtimestamp( u32 stamp ); /* GMT */ const char *asctimestamp( u32 stamp ); /* localized */ void print_string( FILE *fp, const byte *p, size_t n, int delim ); +char *make_printable_string( const byte *p, size_t n, int delim ); int answer_is_yes( const char *s ); int answer_is_yes_no_quit( const char *s ); /*-- strgutil.c --*/ void free_strlist( STRLIST sl ); #define FREE_STRLIST(a) do { free_strlist((a)); (a) = NULL ; } while(0) STRLIST add_to_strlist( STRLIST *list, const char *string ); +STRLIST add_to_strlist2( STRLIST *list, const char *string, int is_utf8 ); STRLIST append_to_strlist( STRLIST *list, const char *string ); +STRLIST append_to_strlist2( STRLIST *list, const char *string, int is_utf8 ); STRLIST strlist_prev( STRLIST head, STRLIST node ); STRLIST strlist_last( STRLIST node ); const char *memistr( const char *buf, size_t buflen, const char *sub ); char *mem2str( char *, const void *, size_t); char *trim_spaces( char *string ); unsigned trim_trailing_chars( byte *line, unsigned len, const char *trimchars); unsigned trim_trailing_ws( byte *line, unsigned len ); int string_count_chr( const char *string, int c ); int set_native_charset( const char *newset ); const char* get_native_charset(void); char *native_to_utf8( const char *string ); char *utf8_to_native( const char *string ); int check_utf8_string( const char *string ); #ifndef HAVE_MEMICMP int memicmp( const char *a, const char *b, size_t n ); #endif #ifndef HAVE_STPCPY char *stpcpy(char *a,const char *b); #endif #ifndef HAVE_STRLWR char *strlwr(char *a); #endif #ifndef HAVE_STRTOUL #define strtoul(a,b,c) ((unsigned long)strtol((a),(b),(c))) #endif #ifndef HAVE_MEMMOVE #define memmove(d, s, n) bcopy((s), (d), (n)) #endif #ifndef HAVE_STRICMP #define stricmp(a,b) strcasecmp( (a), (b) ) #endif /**** other missing stuff ****/ #ifndef HAVE_ATEXIT /* For SunOS */ #define atexit(a) (on_exit((a),0)) #endif #ifndef HAVE_RAISE #define raise(a) kill(getpid(), (a)) #endif /******** some macros ************/ #ifndef STR #define STR(v) #v #endif #define STR2(v) STR(v) #define DIM(v) (sizeof(v)/sizeof((v)[0])) #define DIMof(type,member) DIM(((type *)0)->member) #endif /*G10_UTIL_H*/ diff --git a/util/ChangeLog b/util/ChangeLog index 35e69fb31..e74bf851e 100644 --- a/util/ChangeLog +++ b/util/ChangeLog @@ -1,389 +1,396 @@ +Thu Jul 1 12:47:31 CEST 1999 Werner Koch + + + * miscutil.c (make_printable_string): New. + + * strgutil.c (add_to_strlist2,append_to_strlist2): New. + Tue Jun 29 21:44:25 CEST 1999 Werner Koch * secmem.c (USE_CAPABILITIES): Capabilities support (Remi). Sat Jun 26 12:15:59 CEST 1999 Werner Koch * dotlock.c (create_dotlock): s/uts/utsbuf/ cause there an Amdahl system with the name UTS (Dave Dykstra). * secmem.c (DEFAULT_POOLSIZE): Doubled the size. Fri Jun 18 00:18:02 CEST 1999 Michael Roth * iobuf.c: file_filter() Detection of EOF on terminals improved/fixed (see Bug #21). Mon Jun 14 21:18:54 CEST 1999 Michael Roth * ttyio.c: tty_no_terminal() new. Sat Jun 5 15:30:33 CEST 1999 Werner Koch * strgutil.c (set_native_charset): Support Latin-2 Tue Jun 1 16:01:46 CEST 1999 Werner Koch * iobuf.c (iobuf_get_real_fname): Made global and now keep a copy of the name in the iobuf struct. Mon May 31 19:41:10 CEST 1999 Werner Koch * iobuf.c (file_filter,block_filter): Speed patches (Rémi). Thu May 27 09:40:55 CEST 1999 Werner Koch * miscutil.c (answer_is_yes_no_quit): New. Sun May 23 14:20:22 CEST 1999 Werner Koch * dotlock.c: Tweaked to make it compile under mingw32 * http.c: Disabled for mingw32. Sat May 22 22:47:26 CEST 1999 Werner Koch * logger.c (log_set_logfile): New. Thu May 20 14:04:08 CEST 1999 Werner Koch * memory.c (membug): Nanu, there was a const instead of a static. * strgutil.c (trim_trailing_chars): New. Mon May 17 21:54:43 CEST 1999 Werner Koch * logger.c (g10_log_hexdump): Made 2nd arg a const. Wed Apr 28 13:03:03 CEST 1999 Werner Koch * miscutil.c (asctimestamp): Use nl_langinfo (Gaël Quéri). Sun Apr 18 10:11:28 CEST 1999 Werner Koch * argparse.c (store_alias): Disabled becuase it is not used. * ttyio.c (tty_batchmode): New Sat Mar 20 11:44:21 CET 1999 Werner Koch * http.c: Swapped to includes. Tue Mar 2 16:44:57 CET 1999 Werner Koch * strgutil.c (get_native_charset): New. Fri Feb 26 17:55:41 CET 1999 Werner Koch * secmem.c (memblock_struct): Force align (Rémi Guyomarch) Wed Feb 24 11:07:27 CET 1999 Werner Koch * iobuf.c (block_filter): Fixed the oscillating partial packet chunks. Fri Feb 19 15:49:15 CET 1999 Werner Koch * iobuf.c (iobuf_push_filter2): New to allow transer of context ownership to the iobuf. Released the context where needed. Tue Feb 16 14:10:02 CET 1999 Werner Koch * strgutil.c (add_to_strglist): Clear the new flags field (append_to_strglist): Ditto. * dotlock.c (read_lockfile): terminate pidstr (Michael). Wed Feb 10 17:15:39 CET 1999 Werner Koch * dotlock.c (remove_lockfiles): Add cleanup function. (make_dotlock): Add deadlock check. * secmem.c (secmem_malloc): Changed error message. Wed Jan 20 21:40:21 CET 1999 Werner Koch * http.c (http_wait_response): Moved the shutdown behind the dup Wed Jan 20 18:59:49 CET 1999 Werner Koch * http.c (send_request): Removed double LF Tue Jan 19 19:34:58 CET 1999 Werner Koch * * iobuf.c (iobuf_push_filter): Allow filters for temp streams (iobuf_write_temp): Ditto. (iobuf_flush_temp): New. (iobuf_unget_and_close_temp): Removed. * http.c (close_http_document): Renamed to http_close(). (open_http_document): Renamed to http_open_document(). (http_open): New. (http_start_data): New. (http_wait_response): New. Sun Jan 17 11:04:33 CET 1999 Werner Koch * strgutil.c (trim_trailing_ws): New. Sat Jan 16 12:03:27 CET 1999 Werner Koch * http.c (connect_server): Fixed stupid bug. Sat Jan 16 09:27:30 CET 1999 Werner Koch * http.c: New Wed Jan 13 14:10:15 CET 1999 Werner Koch * iobuf.c (iobuf_fdopen): New. Sat Jan 9 16:02:23 CET 1999 Werner Koch * secmem.c (lock_pool): add another check that setuid() worked. (secmem_init): Ditto. Thu Jan 7 18:00:58 CET 1999 Werner Koch * iobuf.c (iobuf_clear_eof): Removed. (underflow): Changed the eof handling. (iobuf_pop_filter): Made static and renamed to pop_filter. * iobuf.c (iobuf_read_line): New. Sun Jan 3 15:28:44 CET 1999 Werner Koch * dotlock.c (make_dotlock): print another informal message. (make_dotlock): Removed the cpp checks. Tue Dec 29 14:41:47 CET 1998 Werner Koch * secmem.c: Moved unistd.h out of the #ifdef * dotlock.c (make_dotlock): Sun has no SYS_NMLN * iobuf.c (iobuf_unget_and_close_temp): Reset .start Sat Dec 12 18:40:32 CET 1998 Werner Koch * argparse.c (arg_pars): fixed opts[i] with negative index. Fri Nov 27 21:37:41 CET 1998 Werner Koch * dotlock.c: Implemented Wed Nov 25 11:30:07 1998 Werner Koch (wk@isil.d.shuttle.de) * iobuf.c (iobuf_pop_filter): Fixed sigsegv after error. Thu Nov 19 07:09:55 1998 Werner Koch * miscutil.c (strtimevalue): New. Tue Nov 10 10:01:53 1998 Werner Koch (wk@isil.d.shuttle.de) * strgutil.c (set_native_charset): New. (native_to_utf8): Now handles koi8-r. Tue Nov 3 16:17:56 1998 Werner Koch (wk@isil.d.shuttle.de) * strgutil.c (native_to_utf8): New. (utf8_to_native): New, but only as a stub. * argparse.c (optfile_parse): Trimmed spaces from args. Wed Oct 28 08:01:49 1998 me,,, (wk@tobold) * argparse.c (find_long_option): New. (arg_parse): option=value is now allowed. Add a new internal option "--dump-options". Thu Oct 22 16:25:49 1998 Michael Roth (mroth@nessie.de) * fileutil.c (make_basename): New. (make_dirname): New. Wed Oct 21 12:20:29 1998 Werner Koch (wk@isil.d.shuttle.de) * util.c (iobuf_flush): autoincreasing of a temp. iobuf (iobuf_temp_with_content): New. Tue Oct 13 12:40:13 1998 Werner Koch (wk@isil.d.shuttle.de) * util.c (.nofast): set this variable Wed Oct 7 19:27:50 1998 Werner Koch (wk@isil.d.shuttle.de) * memory.c (m_print_stats): New. Tue Oct 6 09:53:56 1998 Werner Koch (wk@isil.d.shuttle.de) * strgutil.c (memicmp): Add HAVE_MEMICMP. Mon Sep 21 19:45:01 1998 Werner Koch (wk@(none)) * secmem.c: New flags to allow suspend/resume of warnings. Fri Sep 18 16:25:47 1998 Werner Koch (wk@(none)) * secmem.c (lock_pool): Kludge for broken mlock on HPUX 10.20 Tue Sep 15 17:52:21 1998 Werner Koch (wk@(none)) * miscutil.c (asctimestamp): New. Mon Sep 14 09:38:18 1998 Werner Koch (wk@(none)) * secmem.c (init_pool): Now mmaps /dev/zero if we do not have MAP_ANON. Wed Sep 9 13:52:28 1998 Werner Koch (wk@(none)) * ttyio.c (do_get): Ctrl-D is now a valid but special character Mon Sep 7 13:52:41 1998 Werner Koch (wk@(none)) * iobuf.c (get_real_fname): New and changed file_filter datastructures and their initialization. Tue Aug 11 15:12:35 1998 Werner Koch (wk@(none)) * miscutil.c (answer_is_yes): i18ned Sat Aug 8 18:35:00 1998 Werner Koch (wk@(none)) * ttyio.c (cleanup): New. Mon Aug 3 17:06:00 1998 Werner Koch (wk@(none)) * secmem.c (MAP_ANON): Add a macro test Wed Jul 29 14:53:34 1998 Werner Koch (wk@(none)) * ttyio.c (tty_get_answer_is_yes): New. Tue Jul 21 10:35:48 1998 Werner Koch (wk@(none)) * argparse.c: New option flag to distinguish options and commands. Sat Jul 18 19:49:30 1998 Werner Koch (wk@(none)) * argparse.c (arg_parse): Added -? as alias for -h Thu Jul 9 14:47:20 1998 Werner Koch (wk@isil.d.shuttle.de) * secmem.c (secmem_init): Drops setuid if called with 0. Tue Jul 7 11:49:25 1998 Werner Koch (wk@isil.d.shuttle.de) * logger.c (log_set_filename): New. Mon Jul 6 09:03:49 1998 Werner Koch (wk@isil.d.shuttle.de) * strgutil.c (append_to_strlist): New. Thu Jul 2 15:55:44 1998 Werner Koch (wk@isil.d.shuttle.de) * iobuf.c (block_filter): Add writing of OP partial length headers. Fri Jun 26 10:38:35 1998 Werner Koch (wk@isil.d.shuttle.de) * ttyio.c (do_get): all iso8859-1 characters are now allowed. Thu Jun 25 15:57:21 1998 Werner Koch (wk@isil.d.shuttle.de) * secmem.c (lock_pool): Removed left over test code. Wed Jun 10 07:39:41 1998 Werner Koch,mobil,,, (wk@tobold) * fileutil.c (compare_filenames): New. * argparse.c (arg_parse): New flag bit 6 to ignore --version Thu May 14 16:45:13 1998 Werner Koch (wk@isil.d.shuttle.de) * argparse.c (show_help): Add some formatting stuff Fri May 8 17:06:49 1998 Werner Koch (wk@isil.d.shuttle.de) * errors.c (strerror): New if !HAVE_STRERROR Mon May 4 19:48:03 1998 Werner Koch (wk@isil.d.shuttle.de) * iobuf.c (iobuf_read): Code is now faster. * (iobuf_write): ditto. Mon Apr 27 11:01:32 1998 Werner Koch (wk@isil.d.shuttle.de) * strgutil.c (memicmp): New. Thu Mar 19 11:29:03 1998 Werner Koch (wk@isil.d.shuttle.de) * strgutil.c (memistr): Add const to return and first arg. Sat Mar 7 11:54:35 1998 Werner Koch (wk@isil.d.shuttle.de) * miscutil.c (print_string): New arg delim; changed all callers. Thu Mar 5 12:19:30 1998 Werner Koch (wk@isil.d.shuttle.de) * errors.c: New strings. Thu Mar 5 12:06:31 1998 Werner Koch (wk@isil.d.shuttle.de) * iobuf.c (iobuf_open): A name of "-" now opens stdin. * fileutil.c (print_fname_stdout, print_fname_stdin): New. Fri Feb 27 10:20:03 1998 Werner Koch (wk@isil.d.shuttle.de) * memory.c (m_is_secure): Removed. * secmem.c (m_is_secure): Moved to here. * secmem.c (secmem_realloc): New. * memory.c (M_GUARD,EXTRA_ALIGN): New (all functions). Thu Feb 26 14:36:51 1998 Werner Koch (wk@isil.d.shuttle.de) * secmem.c (lock_pool): No error if EAGAIN is returned instead of EPERM. Fri Feb 20 17:43:05 1998 Werner Koch (wk@isil.d.shuttle.de) * ttyio.c [MINGW32]: Add support for mingw32. Tue Feb 17 19:43:44 1998 Werner Koch (wk@isil.d.shuttle.de) * memory.c (dump_table_at_exit): New. Mon Feb 16 10:07:28 1998 Werner Koch (wk@isil.d.shuttle.de) * argparse.c (show_version, show_help, default_strusage): Changed according to GNU standards. Mon Feb 16 08:58:25 1998 Werner Koch (wk@isil.d.shuttle.de) * iobuf.c (iobuf_peek): New Fri Feb 13 19:34:59 1998 Werner Koch (wk@isil.d.shuttle.de) * iobuf.c (iobuf_seek): Set counters to new offset. Fri Feb 13 17:13:04 1998 Werner Koch (wk@isil.d.shuttle.de) * logger.c (log_set_name, log_get_name): New. (print_prefix, pgm_name): New, changed all function to make use it. (log_mpidump): Removed the "DBG" prefix. (log_hexdump): Ditto. * logger.c (printstr): Removed. Fri Feb 13 15:14:13 1998 Werner Koch (wk@isil.d.shuttle.de) * argparse.c (show_help): New '\v' kludge. diff --git a/util/miscutil.c b/util/miscutil.c index d982e64af..4244ccce0 100644 --- a/util/miscutil.c +++ b/util/miscutil.c @@ -1,189 +1,243 @@ /* miscutil.c - miscellaneous utilities * Copyright (C) 1998, 1999 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 #ifdef HAVE_LANGINFO_H #include #endif #include "types.h" #include "util.h" #include "i18n.h" u32 make_timestamp() { return time(NULL); } u32 add_days_to_timestamp( u32 stamp, u16 days ) { return stamp + days*86400L; } /**************** * Return a string with a time value in the form: x Y, n D, n H */ const char * strtimevalue( u32 value ) { static char buffer[30]; unsigned int years, days, hours, minutes; value /= 60; minutes = value % 60; value /= 60; hours = value % 24; value /= 24; days = value % 365; value /= 365; years = value; sprintf(buffer,"%uy%ud%uh%um", years, days, hours, minutes ); if( years ) return buffer; if( days ) return strchr( buffer, 'y' ) + 1; return strchr( buffer, 'd' ) + 1; } /**************** * Note: this function returns GMT */ const char * strtimestamp( u32 stamp ) { static char buffer[11+5]; struct tm *tp; time_t atime = stamp; tp = gmtime( &atime ); sprintf(buffer,"%04d-%02d-%02d", 1900+tp->tm_year, tp->tm_mon+1, tp->tm_mday ); return buffer; } /**************** * Note: this function returns local time */ const char * asctimestamp( u32 stamp ) { static char buffer[50]; #if defined (HAVE_STRFTIME) && defined (HAVE_NL_LANGINFO) static char fmt[50]; #endif struct tm *tp; time_t atime = stamp; tp = localtime( &atime ); #ifdef HAVE_STRFTIME #if defined(HAVE_NL_LANGINFO) mem2str( fmt, nl_langinfo(D_T_FMT), DIM(fmt) ); if( strstr( fmt, "%Z" ) == NULL ) strcat( fmt, " %Z"); strftime( buffer, DIM(buffer)-1, fmt, tp ); #else /* fixme: we should check whether the locale appends a " %Z" * These locales from glibc don't put the " %Z": * fi_FI hr_HR ja_JP lt_LT lv_LV POSIX ru_RU ru_SU sv_FI sv_SE zh_CN */ strftime( buffer, DIM(buffer)-1, "%c %Z", tp ); #endif buffer[DIM(buffer)-1] = 0; #else mem2str( buffer, asctime(tp), DIM(buffer) ); #endif return buffer; } /**************** * Print a string to FP, but filter all control characters out. */ void print_string( FILE *fp, const byte *p, size_t n, int delim ) { for( ; n; n--, p++ ) if( iscntrl( *p ) || *p == delim ) { putc('\\', fp); if( *p == '\n' ) putc('n', fp); else if( *p == '\r' ) putc('r', fp); else if( *p == '\f' ) putc('f', fp); else if( *p == '\v' ) putc('v', fp); else if( *p == '\b' ) putc('b', fp); else if( !*p ) putc('0', fp); else fprintf(fp, "x%02x", *p ); } else putc(*p, fp); } +/**************** + * This function returns a string which is suitable for printing + * Caller must release it with m_free() + */ +char * +make_printable_string( const byte *p, size_t n, int delim ) +{ + size_t save_n, buflen; + const byte *save_p; + char *buffer, *d; + + /* first count length */ + for(save_n = n, save_p = p, buflen=1 ; n; n--, p++ ) { + if( iscntrl( *p ) || *p == delim ) { + if( *p=='\n' || *p=='\r' || *p=='\f' + || *p=='\v' || *p=='\b' || !*p ) + buflen += 2; + else + buflen += 3; + } + else + buflen++; + } + p = save_p; + n = save_n; + /* and now make the string */ + d = buffer = m_alloc( buflen ); + for( ; n; n--, p++ ) { + if( iscntrl( *p ) || *p == delim ) { + *d++ = '\\'; + if( *p == '\n' ) + *d++ = 'n'; + else if( *p == '\r' ) + *d++ = 'r'; + else if( *p == '\f' ) + *d++ = 'f'; + else if( *p == '\v' ) + *d++ = 'v'; + else if( *p == '\b' ) + *d++ = 'b'; + else if( !*p ) + *d++ = '0'; + else { + sprintf(d, "x%02x", *p ); + d += 2; + } + } + else + *d++ = *p; + } + *d = 0; + return buffer; +} + int answer_is_yes( const char *s ) { char *long_yes = _("yes"); char *short_yes = _("yY"); if( !stricmp(s, long_yes ) ) return 1; if( strchr( short_yes, *s ) && !s[1] ) return 1; return 0; } /**************** * Return 1 for yes, -1 for quit, or 0 for no */ int answer_is_yes_no_quit( const char *s ) { char *long_yes = _("yes"); char *long_quit = _("quit"); char *short_yes = _("yY"); char *short_quit = _("qQ"); if( !stricmp(s, long_yes ) ) return 1; if( !stricmp(s, long_quit ) ) return -1; if( strchr( short_yes, *s ) && !s[1] ) return 1; if( strchr( short_quit, *s ) && !s[1] ) return -1; return 0; } diff --git a/util/strgutil.c b/util/strgutil.c index 94f8a5d6c..b27f9b7b6 100644 --- a/util/strgutil.c +++ b/util/strgutil.c @@ -1,459 +1,492 @@ /* strgutil.c - miscellaneous utilities * Copyright (C) 1998 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 "types.h" #include "util.h" #include "memory.h" static ushort koi8_unicode[128] = { 0x2500,0x2502,0x250c,0x2510,0x2514,0x2518,0x251c,0x2524, 0x252c,0x2534,0x253c,0x2580,0x2584,0x2588,0x258c,0x2590, 0x2591,0x2592,0x2593,0x2320,0x25a0,0x2219,0x221a,0x2248, 0x2264,0x2265,0x00a0,0x2321,0x00b0,0x00b2,0x00b7,0x00f7, 0x2550,0x2551,0x2552,0x0451,0x2553,0x2554,0x2555,0x2556, 0x2557,0x2558,0x2559,0x255a,0x255b,0x255c,0x255d,0x255e, 0x255f,0x2560,0x2561,0x0401,0x2562,0x2563,0x2564,0x2565, 0x2566,0x2567,0x2568,0x2569,0x256a,0x256b,0x256c,0x00a9, 0x044e,0x0430,0x0431,0x0446,0x0434,0x0435,0x0444,0x0433, 0x0445,0x0438,0x0439,0x043a,0x043b,0x043c,0x043d,0x043e, 0x043f,0x044f,0x0440,0x0441,0x0442,0x0443,0x0436,0x0432, 0x044c,0x044b,0x0437,0x0448,0x044d,0x0449,0x0447,0x044a, 0x042e,0x0410,0x0411,0x0426,0x0414,0x0415,0x0424,0x0413, 0x0425,0x0418,0x0419,0x041a,0x041b,0x041c,0x041d,0x041e, 0x041f,0x042f,0x0420,0x0421,0x0422,0x0423,0x0416,0x0412, 0x042c,0x042b,0x0417,0x0428,0x042d,0x0429,0x0427,0x042a }; static ushort latin2_unicode[128] = { 0x0080,0x0081,0x0082,0x0083,0x0084,0x0085,0x0086,0x0087, 0x0088,0x0089,0x008A,0x008B,0x008C,0x008D,0x008E,0x008F, 0x0090,0x0091,0x0092,0x0093,0x0094,0x0095,0x0096,0x0097, 0x0098,0x0099,0x009A,0x009B,0x009C,0x009D,0x009E,0x009F, 0x00A0,0x0104,0x02D8,0x0141,0x00A4,0x013D,0x015A,0x00A7, 0x00A8,0x0160,0x015E,0x0164,0x0179,0x00AD,0x017D,0x017B, 0x00B0,0x0105,0x02DB,0x0142,0x00B4,0x013E,0x015B,0x02C7, 0x00B8,0x0161,0x015F,0x0165,0x017A,0x02DD,0x017E,0x017C, 0x0154,0x00C1,0x00C2,0x0102,0x00C4,0x0139,0x0106,0x00C7, 0x010C,0x00C9,0x0118,0x00CB,0x011A,0x00CD,0x00CE,0x010E, 0x0110,0x0143,0x0147,0x00D3,0x00D4,0x0150,0x00D6,0x00D7, 0x0158,0x016E,0x00DA,0x0170,0x00DC,0x00DD,0x0162,0x00DF, 0x0155,0x00E1,0x00E2,0x0103,0x00E4,0x013A,0x0107,0x00E7, 0x010D,0x00E9,0x0119,0x00EB,0x011B,0x00ED,0x00EE,0x010F, 0x0111,0x0144,0x0148,0x00F3,0x00F4,0x0151,0x00F6,0x00F7, 0x0159,0x016F,0x00FA,0x0171,0x00FC,0x00FD,0x0163,0x02D9 }; static const char *active_charset_name = "iso-8859-1"; static ushort *active_charset = NULL; void free_strlist( STRLIST sl ) { STRLIST sl2; for(; sl; sl = sl2 ) { sl2 = sl->next; m_free(sl); } } STRLIST add_to_strlist( STRLIST *list, const char *string ) { STRLIST sl; sl = m_alloc( sizeof *sl + strlen(string)); sl->flags = 0; strcpy(sl->d, string); sl->next = *list; *list = sl; return sl; } +/**************** + * ame as add_to_strlist() but if is_utf8 is *not* set a conversion + * to UTF8 is done + */ +STRLIST +add_to_strlist2( STRLIST *list, const char *string, int is_utf8 ) +{ + STRLIST sl; + + if( is_utf8 ) + sl = add_to_strlist( list, string ); + else { + char *p = native_to_utf8( string ); + sl = add_to_strlist( list, p ); + m_free( p ); + } + return sl; +} + STRLIST append_to_strlist( STRLIST *list, const char *string ) { STRLIST r, sl; sl = m_alloc( sizeof *sl + strlen(string)); sl->flags = 0; strcpy(sl->d, string); sl->next = NULL; if( !*list ) *list = sl; else { for( r = *list; r->next; r = r->next ) ; r->next = sl; } return sl; } +STRLIST +append_to_strlist2( STRLIST *list, const char *string, int is_utf8 ) +{ + STRLIST sl; + + if( is_utf8 ) + sl = append_to_strlist( list, string ); + else { + char *p = native_to_utf8( string ); + sl = append_to_strlist( list, p ); + m_free( p ); + } + return sl; +} STRLIST strlist_prev( STRLIST head, STRLIST node ) { STRLIST n; for(n=NULL; head && head != node; head = head->next ) n = head; return n; } STRLIST strlist_last( STRLIST node ) { if( node ) for( ; node->next ; node = node->next ) ; return node; } /**************** * look for the substring SUB in buffer and return a pointer to that * substring in BUF or NULL if not found. * Comparison is case-insensitive. */ const char * memistr( const char *buf, size_t buflen, const char *sub ) { const byte *t, *s ; size_t n; for( t=buf, n=buflen, s=sub ; n ; t++, n-- ) if( toupper(*t) == toupper(*s) ) { for( buf=t++, buflen = n--, s++; n && toupper(*t) == toupper(*s); t++, s++, n-- ) ; if( !*s ) return buf; t = buf; n = buflen; s = sub ; } return NULL ; } /**************** * Wie strncpy(), aber es werden maximal n-1 zeichen kopiert und ein * '\0' angehängt. Ist n = 0, so geschieht nichts, ist Destination * gleich NULL, so wird via m_alloc Speicher besorgt, ist dann nicht * genügend Speicher vorhanden, so bricht die funktion ab. */ char * mem2str( char *dest , const void *src , size_t n ) { char *d; const char *s; if( n ) { if( !dest ) dest = m_alloc( n ) ; d = dest; s = src ; for(n--; n && *s; n-- ) *d++ = *s++; *d = '\0' ; } return dest ; } /**************** * remove leading and trailing white spaces */ char * trim_spaces( char *str ) { char *string, *p, *mark; string = str; /* find first non space character */ for( p=string; *p && isspace( *(byte*)p ) ; p++ ) ; /* move characters */ for( (mark = NULL); (*string = *p); string++, p++ ) if( isspace( *(byte*)p ) ) { if( !mark ) mark = string ; } else mark = NULL ; if( mark ) *mark = '\0' ; /* remove trailing spaces */ return str ; } unsigned trim_trailing_chars( byte *line, unsigned len, const char *trimchars ) { byte *p, *mark; unsigned n; for(mark=NULL, p=line, n=0; n < len; n++, p++ ) { if( strchr(trimchars, *p ) ) { if( !mark ) mark = p; } else mark = NULL; } if( mark ) { *mark = 0; return mark - line; } return len; } /**************** * remove trailing white spaces and return the length of the buffer */ unsigned trim_trailing_ws( byte *line, unsigned len ) { return trim_trailing_chars( line, len, " \t\r\n" ); } int string_count_chr( const char *string, int c ) { int count; for(count=0; *string; string++ ) if( *string == c ) count++; return count; } int set_native_charset( const char *newset ) { if( !stricmp( newset, "iso-8859-1" ) ) { active_charset_name = "iso-8859-1"; active_charset = NULL; } else if( !stricmp( newset, "iso-8859-2" ) ) { active_charset_name = "iso-8859-2"; active_charset = latin2_unicode; } else if( !stricmp( newset, "koi8-r" ) ) { active_charset_name = "koi8-r"; active_charset = koi8_unicode; } else return G10ERR_GENERAL; return 0; } const char* get_native_charset() { return active_charset_name; } /**************** * Convert string, which is in native encoding to UTF8 and return the * new allocated UTF8 string. */ char * native_to_utf8( const char *string ) { const byte *s; char *buffer; byte *p; size_t length=0; if( active_charset ) { for(s=string; *s; s++ ) { length++; if( *s & 0x80 ) length += 2; /* we may need 3 bytes */ } buffer = m_alloc( length + 1 ); for(p=buffer, s=string; *s; s++ ) { if( *s & 0x80 ) { ushort val = active_charset[ *s & 0x7f ]; if( val < 0x0800 ) { *p++ = 0xc0 | ( (val >> 6) & 0x1f ); *p++ = 0x80 | ( val & 0x3f ); } else { *p++ = 0xe0 | ( (val >> 12) & 0x0f ); *p++ = 0x80 | ( (val >> 6) & 0x3f ); *p++ = 0x80 | ( val & 0x3f ); } } else *p++ = *s; } *p = 0; } else { for(s=string; *s; s++ ) { length++; if( *s & 0x80 ) length++; } buffer = m_alloc( length + 1 ); for(p=buffer, s=string; *s; s++ ) { if( *s & 0x80 ) { *p++ = 0xc0 | ((*s >> 6) & 3); *p++ = 0x80 | ( *s & 0x3f ); } else *p++ = *s; } *p = 0; } return buffer; } /**************** * Convert string, which is in UTF8 to native encoding. Replace * illegal encodings by some "\xnn". */ char * utf8_to_native( const char *string ) { #if 0 const byte *s; size_t n; byte *buffer, *p; /* quick check whether we actually have characters with bit 8 set */ for( s=string; *s; s++ ) if( *s & 0x80 ) break; if( !*s ) /* that is easy */ return m_strdup(string); /* count the extended utf-8 characters */ 110x xxxx 1110 xxxx 1111 0xxx for( n=1, s=string; *s; s++ ) { if( !(*s & 0x80) ) n++; else if( (*s & 0xe0) == 0xc0 ) n += 2; else if( (*s & 0xf0) == 0xe0 ) n += 3; else if( (*s & 0xf8) == 0xf0 ) n += 4; else n++; /* invalid encoding */ } buffer = p = m_alloc( n ); for( s=string; *s; ) { if( !(*s & 0x80) ) *p++ = *s++; else if( (*s & 0xe0) == 0xc0 ) { u32 val; if( (s[1] & 0xc0) != 0x80 ) ; val = (*s << 6) | (s[1] & 0x3f); } else if( (*s & 0xf0) == 0xe0 ) n += 3; else if( (*s & 0xf8) == 0xf0 ) n += 4; else n++; /* invalid encoding */ } #endif return m_strdup(string); } /**************** * check whether string is a valid UTF8 string. * Returns 0 = Okay * 1 = Too short * 2 = invalid encoding */ int check_utf8_string( const char *string ) { /*fixme */ return 0; } /********************************************* ********** missing string functions ********* *********************************************/ #ifndef HAVE_STPCPY char * stpcpy(char *a,const char *b) { while( *b ) *a++ = *b++; *a = 0; return (char*)a; } #endif #ifndef HAVE_STRLWR char * strlwr(char *s) { char *p; for(p=s; *p; p++ ) *p = tolower(*p); return s; } #endif /**************** * mingw32/cpd has a memicmp() */ #ifndef HAVE_MEMICMP int memicmp( const char *a, const char *b, size_t n ) { for( ; n; n--, a++, b++ ) if( *a != *b && toupper(*(const byte*)a) != toupper(*(const byte*)b) ) return *(const byte *)a - *(const byte*)b; return 0; } #endif